u
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -3,4 +3,5 @@ node_modules/
|
|||||||
data/
|
data/
|
||||||
logs/
|
logs/
|
||||||
print_spool/
|
print_spool/
|
||||||
certs/
|
certs/
|
||||||
|
output/
|
||||||
|
|||||||
10
README.md
10
README.md
@@ -31,6 +31,16 @@ npm run docs:api
|
|||||||
|
|
||||||
That writes `docs/api/routes.md`, `docs/api/routes-all.md`, and per-route Markdown under `docs/api/routes/`. There is no generated `openapi.json` on disk — the interactive Swagger UI at `/docs/` builds the spec from the same `@openapi` JSDoc at runtime (`src/api/openapiSpec.js`).
|
That writes `docs/api/routes.md`, `docs/api/routes-all.md`, and per-route Markdown under `docs/api/routes/`. There is no generated `openapi.json` on disk — the interactive Swagger UI at `/docs/` builds the spec from the same `@openapi` JSDoc at runtime (`src/api/openapiSpec.js`).
|
||||||
|
|
||||||
|
### Function review files
|
||||||
|
|
||||||
|
Create one JavaScript file per function in the project:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run review:functions
|
||||||
|
```
|
||||||
|
|
||||||
|
The generated files are written to `output/` and include each function's source filename and line numbers. The same directory also contains `functions.txt`, an index of source filenames, line numbers, and function signatures. Existing generated function files and the index are replaced each time the command runs. Dependencies, runtime data, and the output directory itself are excluded from the scan.
|
||||||
|
|
||||||
### Brands (company-scoped, `selectedCompanyId` aus der Session)
|
### Brands (company-scoped, `selectedCompanyId` aus der Session)
|
||||||
|
|
||||||
| Methode | Route | Store-Methode |
|
| Methode | Route | Store-Methode |
|
||||||
|
|||||||
@@ -32,6 +32,9 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
|
|
||||||
| File | Endpoint |
|
| File | Endpoint |
|
||||||
|------|----------|
|
|------|----------|
|
||||||
|
| [`routes/account.get.md`](routes/account.get.md) | `GET /account` |
|
||||||
|
| [`routes/account.post.md`](routes/account.post.md) | `POST /account` |
|
||||||
|
| [`routes/account/_id.md`](routes/account/_id.md) | `DELETE /account/{id}` |
|
||||||
| [`routes/article/create.md`](routes/article/create.md) | `POST /article` |
|
| [`routes/article/create.md`](routes/article/create.md) | `POST /article` |
|
||||||
| [`routes/article/export.zip.md`](routes/article/export.zip.md) | `GET /article/export.zip` |
|
| [`routes/article/export.zip.md`](routes/article/export.zip.md) | `GET /article/export.zip` |
|
||||||
| [`routes/article/delete.md`](routes/article/delete.md) | `DELETE /article/{sku}` |
|
| [`routes/article/delete.md`](routes/article/delete.md) | `DELETE /article/{sku}` |
|
||||||
@@ -163,6 +166,12 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/order/delete.md`](routes/order/delete.md) | `DELETE /order/{id}` |
|
| [`routes/order/delete.md`](routes/order/delete.md) | `DELETE /order/{id}` |
|
||||||
| [`routes/order/get.md`](routes/order/get.md) | `GET /order/{id}` |
|
| [`routes/order/get.md`](routes/order/get.md) | `GET /order/{id}` |
|
||||||
| [`routes/order/update.md`](routes/order/update.md) | `PATCH /order/{id}` |
|
| [`routes/order/update.md`](routes/order/update.md) | `PATCH /order/{id}` |
|
||||||
|
| [`routes/order/_id/payment.get.md`](routes/order/_id/payment.get.md) | `GET /order/{id}/payment` |
|
||||||
|
| [`routes/order/_id/payment.post.md`](routes/order/_id/payment.post.md) | `POST /order/{id}/payment` |
|
||||||
|
| [`routes/order/_id/shipping.get.md`](routes/order/_id/shipping.get.md) | `GET /order/{id}/shipping` |
|
||||||
|
| [`routes/order/_id/shipping.post.md`](routes/order/_id/shipping.post.md) | `POST /order/{id}/shipping` |
|
||||||
|
| [`routes/package/_id.md`](routes/package/_id.md) | `DELETE /package/{id}` |
|
||||||
|
| [`routes/payment/_id.md`](routes/payment/_id.md) | `DELETE /payment/{id}` |
|
||||||
| [`routes/posterminal/list.md`](routes/posterminal/list.md) | `GET /posterminal` |
|
| [`routes/posterminal/list.md`](routes/posterminal/list.md) | `GET /posterminal` |
|
||||||
| [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | `PUT /posterminal` |
|
| [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | `PUT /posterminal` |
|
||||||
| [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | `DELETE /posterminal/{id}` |
|
| [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | `DELETE /posterminal/{id}` |
|
||||||
@@ -194,6 +203,9 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
|
|||||||
| [`routes/quote/update.md`](routes/quote/update.md) | `PATCH /quote/{id}` |
|
| [`routes/quote/update.md`](routes/quote/update.md) | `PATCH /quote/{id}` |
|
||||||
| [`routes/serper/image.md`](routes/serper/image.md) | `POST /serper/image` |
|
| [`routes/serper/image.md`](routes/serper/image.md) | `POST /serper/image` |
|
||||||
| [`routes/serper/images.md`](routes/serper/images.md) | `POST /serper/images` |
|
| [`routes/serper/images.md`](routes/serper/images.md) | `POST /serper/images` |
|
||||||
|
| [`routes/shipping/_id.md`](routes/shipping/_id.md) | `DELETE /shipping/{id}` |
|
||||||
|
| [`routes/shipping/_id/package.get.md`](routes/shipping/_id/package.get.md) | `GET /shipping/{id}/package` |
|
||||||
|
| [`routes/shipping/_id/package.post.md`](routes/shipping/_id/package.post.md) | `POST /shipping/{id}/package` |
|
||||||
| [`routes/stock/adjust.md`](routes/stock/adjust.md) | `POST /stock/adjust` |
|
| [`routes/stock/adjust.md`](routes/stock/adjust.md) | `POST /stock/adjust` |
|
||||||
| [`routes/supplier/list.md`](routes/supplier/list.md) | `GET /supplier` |
|
| [`routes/supplier/list.md`](routes/supplier/list.md) | `GET /supplier` |
|
||||||
| [`routes/supplier/create.md`](routes/supplier/create.md) | `POST /supplier` |
|
| [`routes/supplier/create.md`](routes/supplier/create.md) | `POST /supplier` |
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Dash API Routes Overview (All)
|
# Dash API Routes Overview (All)
|
||||||
|
|
||||||
Overview of all 192 API endpoints across all modules.
|
Overview of all 204 API endpoints across all modules.
|
||||||
|
|
||||||
### Notation
|
### Notation
|
||||||
|
|
||||||
@@ -57,6 +57,15 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
|
|
||||||
### Routes
|
### Routes
|
||||||
|
|
||||||
|
#### List accounts — `GET /account`
|
||||||
|
_none_
|
||||||
|
- Returns: `200 { accounts: { id: int, companyId: int, type: str, name: str }[] }`
|
||||||
|
#### Create account — `POST /account`
|
||||||
|
- Params: `body: { type: str, name: str }`
|
||||||
|
- Returns: `201 { account: { id: int, companyId: int, type: str, name: str } }`
|
||||||
|
#### Delete account — `DELETE /account/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
#### Create article in category — `POST /article`
|
#### Create article in category — `POST /article`
|
||||||
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
||||||
- Returns: `201 T31`
|
- Returns: `201 T31`
|
||||||
@@ -450,6 +459,24 @@ _none_
|
|||||||
#### Update order — `PATCH /order/{id}`
|
#### Update order — `PATCH /order/{id}`
|
||||||
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T25[] }`
|
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T25[] }`
|
||||||
- Returns: `200 T7`
|
- Returns: `200 T7`
|
||||||
|
#### List payments of an order — `GET /order/{id}/payment`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { payments: { id: int, accountId: int, manifestId: int, type: str, amount: num }[] }`
|
||||||
|
#### Add payment to an order — `POST /order/{id}/payment`
|
||||||
|
- Params: `id: int, body: { accountId: int, type: str, amount: num }`
|
||||||
|
- Returns: `201 { payment: { id: int, accountId: int, manifestId: int, type: str, amount: num } }`
|
||||||
|
#### List shippings of an order — `GET /order/{id}/shipping`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { shippings: { id: int, manifestId: int, number: str, type: str, state: str }[] }`
|
||||||
|
#### Create shipping for an order — `POST /order/{id}/shipping`
|
||||||
|
- Params: `id: int, body: { type: str, state: str }`
|
||||||
|
- Returns: `201 { shipping: { id: int, manifestId: int, number: str, type: str, state: str } }`
|
||||||
|
#### Delete package — `DELETE /package/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Delete payment — `DELETE /payment/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
#### List POS terminals — `GET /posterminal`
|
#### List POS terminals — `GET /posterminal`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { posterminals: T10[] }`
|
- Returns: `200 { posterminals: T10[] }`
|
||||||
@@ -543,6 +570,15 @@ _none_
|
|||||||
#### Search images via Serper.dev and stream WebP thumbnails (SSE) — `POST /serper/images`
|
#### Search images via Serper.dev and stream WebP thumbnails (SSE) — `POST /serper/images`
|
||||||
- Params: `body: { q: str, gl?: str, hl?: str, num?: int, preferSource?: bool }`
|
- Params: `body: { q: str, gl?: str, hl?: str, num?: int, preferSource?: bool }`
|
||||||
- Returns: `200 SSE`
|
- Returns: `200 SSE`
|
||||||
|
#### Delete shipping — `DELETE /shipping/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### List packages of a shipping — `GET /shipping/{id}/package`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }`
|
||||||
|
#### Create package for a shipping — `POST /shipping/{id}/package`
|
||||||
|
- Params: `id: int, body: { entityaddressId: int, type: str, state: str, details?: str }`
|
||||||
|
- Returns: `201 { package: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null } }`
|
||||||
#### Adjust stock manually — `POST /stock/adjust`
|
#### Adjust stock manually — `POST /stock/adjust`
|
||||||
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
|
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
|
||||||
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T15[] }`
|
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T15[] }`
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Dash API Routes Overview
|
# Dash API Routes Overview
|
||||||
|
|
||||||
Overview of all 146 API endpoints across all modules.
|
Overview of all 158 API endpoints across all modules.
|
||||||
|
|
||||||
### Notation
|
### Notation
|
||||||
|
|
||||||
@@ -52,6 +52,15 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
|
|
||||||
### Routes
|
### Routes
|
||||||
|
|
||||||
|
#### List accounts — `GET /account`
|
||||||
|
_none_
|
||||||
|
- Returns: `200 { accounts: { id: int, companyId: int, type: str, name: str }[] }`
|
||||||
|
#### Create account — `POST /account`
|
||||||
|
- Params: `body: { type: str, name: str }`
|
||||||
|
- Returns: `201 { account: { id: int, companyId: int, type: str, name: str } }`
|
||||||
|
#### Delete account — `DELETE /account/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
#### Create article in category — `POST /article`
|
#### Create article in category — `POST /article`
|
||||||
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
||||||
- Returns: `201 T29`
|
- Returns: `201 T29`
|
||||||
@@ -388,6 +397,24 @@ _none_
|
|||||||
#### Update order — `PATCH /order/{id}`
|
#### Update order — `PATCH /order/{id}`
|
||||||
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
|
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
|
||||||
- Returns: `200 T7`
|
- Returns: `200 T7`
|
||||||
|
#### List payments of an order — `GET /order/{id}/payment`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { payments: { id: int, accountId: int, manifestId: int, type: str, amount: num }[] }`
|
||||||
|
#### Add payment to an order — `POST /order/{id}/payment`
|
||||||
|
- Params: `id: int, body: { accountId: int, type: str, amount: num }`
|
||||||
|
- Returns: `201 { payment: { id: int, accountId: int, manifestId: int, type: str, amount: num } }`
|
||||||
|
#### List shippings of an order — `GET /order/{id}/shipping`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { shippings: { id: int, manifestId: int, number: str, type: str, state: str }[] }`
|
||||||
|
#### Create shipping for an order — `POST /order/{id}/shipping`
|
||||||
|
- Params: `id: int, body: { type: str, state: str }`
|
||||||
|
- Returns: `201 { shipping: { id: int, manifestId: int, number: str, type: str, state: str } }`
|
||||||
|
#### Delete package — `DELETE /package/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Delete payment — `DELETE /payment/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
#### List printers — `GET /printer`
|
#### List printers — `GET /printer`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { printers: T22[] }`
|
- Returns: `200 { printers: T22[] }`
|
||||||
@@ -439,6 +466,15 @@ _none_
|
|||||||
#### Update quote — `PATCH /quote/{id}`
|
#### Update quote — `PATCH /quote/{id}`
|
||||||
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
|
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }`
|
||||||
- Returns: `200 T8`
|
- Returns: `200 T8`
|
||||||
|
#### Delete shipping — `DELETE /shipping/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### List packages of a shipping — `GET /shipping/{id}/package`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }`
|
||||||
|
#### Create package for a shipping — `POST /shipping/{id}/package`
|
||||||
|
- Params: `id: int, body: { entityaddressId: int, type: str, state: str, details?: str }`
|
||||||
|
- Returns: `201 { package: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null } }`
|
||||||
#### Adjust stock manually — `POST /stock/adjust`
|
#### Adjust stock manually — `POST /stock/adjust`
|
||||||
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
|
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }`
|
||||||
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T14[] }`
|
- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T14[] }`
|
||||||
|
|||||||
35
docs/api/routes/account.get.md
Normal file
35
docs/api/routes/account.get.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
# `GET /account`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List accounts
|
||||||
|
|
||||||
|
Lists payment accounts for the selected company.
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of accounts
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"accounts": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"companyId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"name": "…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | No company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
48
docs/api/routes/account.post.md
Normal file
48
docs/api/routes/account.post.md
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
# `POST /account`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Create account
|
||||||
|
|
||||||
|
Creates a payment account for the selected company.
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `type` | yes | string | |
|
||||||
|
| `name` | yes | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "…",
|
||||||
|
"name": "…"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created account
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"id": 1,
|
||||||
|
"companyId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"name": "…"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Validation error or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
32
docs/api/routes/account/_id.md
Normal file
32
docs/api/routes/account/_id.md
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
# `DELETE /account/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete account
|
||||||
|
|
||||||
|
Deletes an account of the selected company. Its payments are removed as well.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Account ID |
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Account deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Account not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
43
docs/api/routes/order/_id/payment.get.md
Normal file
43
docs/api/routes/order/_id/payment.get.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# `GET /order/{id}/payment`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List payments of an order
|
||||||
|
|
||||||
|
Lists payments of the order (manifest) for the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Order ID |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of payments
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"payments": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"accountId": 1,
|
||||||
|
"manifestId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"amount": 1
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 404 | Order not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
58
docs/api/routes/order/_id/payment.post.md
Normal file
58
docs/api/routes/order/_id/payment.post.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# `POST /order/{id}/payment`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Add payment to an order
|
||||||
|
|
||||||
|
Adds a payment to the order using an account of the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Order ID |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `accountId` | yes | integer | |
|
||||||
|
| `type` | yes | string | |
|
||||||
|
| `amount` | yes | number | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"accountId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"amount": 1
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created payment
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"payment": {
|
||||||
|
"id": 1,
|
||||||
|
"accountId": 1,
|
||||||
|
"manifestId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"amount": 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Validation error or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Order or account not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
43
docs/api/routes/order/_id/shipping.get.md
Normal file
43
docs/api/routes/order/_id/shipping.get.md
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# `GET /order/{id}/shipping`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List shippings of an order
|
||||||
|
|
||||||
|
Lists shippings of the order (manifest) for the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Order ID |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of shippings
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"shippings": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"manifestId": 1,
|
||||||
|
"number": "…",
|
||||||
|
"type": "…",
|
||||||
|
"state": "…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 404 | Order not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
58
docs/api/routes/order/_id/shipping.post.md
Normal file
58
docs/api/routes/order/_id/shipping.post.md
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
# `POST /order/{id}/shipping`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Create shipping for an order
|
||||||
|
|
||||||
|
Creates a shipping for the order. The shipping number is allocated from the
|
||||||
|
`shipping` number sequence (prefix and counter from the numbers API).
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Order ID |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `type` | yes | string | |
|
||||||
|
| `state` | yes | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "…",
|
||||||
|
"state": "…"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created shipping
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"shipping": {
|
||||||
|
"id": 1,
|
||||||
|
"manifestId": 1,
|
||||||
|
"number": "…",
|
||||||
|
"type": "…",
|
||||||
|
"state": "…"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Validation error or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Order not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
31
docs/api/routes/package/_id.md
Normal file
31
docs/api/routes/package/_id.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# `DELETE /package/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete package
|
||||||
|
|
||||||
|
Deletes a package of the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Package ID |
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Package deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
31
docs/api/routes/payment/_id.md
Normal file
31
docs/api/routes/payment/_id.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# `DELETE /payment/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete payment
|
||||||
|
|
||||||
|
Deletes a payment of an order in the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Payment ID |
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Payment deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
31
docs/api/routes/shipping/_id.md
Normal file
31
docs/api/routes/shipping/_id.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# `DELETE /shipping/{id}`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Delete shipping
|
||||||
|
|
||||||
|
Deletes a shipping of the selected company. Its packages are removed as well.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Shipping ID |
|
||||||
|
|
||||||
|
## Response `204`
|
||||||
|
|
||||||
|
Shipping deleted
|
||||||
|
|
||||||
|
_No content_
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
45
docs/api/routes/shipping/_id/package.get.md
Normal file
45
docs/api/routes/shipping/_id/package.get.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
# `GET /shipping/{id}/package`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
List packages of a shipping
|
||||||
|
|
||||||
|
Lists packages of the shipping for the selected company.
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Shipping ID |
|
||||||
|
|
||||||
|
## Response `200`
|
||||||
|
|
||||||
|
List of packages
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"packages": [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"shippingId": 1,
|
||||||
|
"entityaddressId": 1,
|
||||||
|
"number": "…",
|
||||||
|
"type": "…",
|
||||||
|
"state": "…",
|
||||||
|
"details": "…"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Invalid ID or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 404 | Shipping not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
64
docs/api/routes/shipping/_id/package.post.md
Normal file
64
docs/api/routes/shipping/_id/package.post.md
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# `POST /shipping/{id}/package`
|
||||||
|
|
||||||
|
Protected. Requires a valid session cookie (`credentials: 'include'`).
|
||||||
|
|
||||||
|
Create package for a shipping
|
||||||
|
|
||||||
|
Creates a package for the shipping. The package number is allocated from the
|
||||||
|
`package` number sequence. `entityaddressId` is required (target address).
|
||||||
|
|
||||||
|
|
||||||
|
## Path parameters
|
||||||
|
|
||||||
|
| Param | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `id` | yes | integer | Shipping ID |
|
||||||
|
|
||||||
|
## Body (JSON)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `entityaddressId` | yes | integer | |
|
||||||
|
| `type` | yes | string | |
|
||||||
|
| `state` | yes | string | |
|
||||||
|
| `details` | no | string | |
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"entityaddressId": 1,
|
||||||
|
"type": "…",
|
||||||
|
"state": "…",
|
||||||
|
"details": "…"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Response `201`
|
||||||
|
|
||||||
|
Created package
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"package": {
|
||||||
|
"id": 1,
|
||||||
|
"shippingId": 1,
|
||||||
|
"entityaddressId": 1,
|
||||||
|
"number": "…",
|
||||||
|
"type": "…",
|
||||||
|
"state": "…",
|
||||||
|
"details": "…"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Errors
|
||||||
|
|
||||||
|
| Status | Description |
|
||||||
|
|--------|-------------|
|
||||||
|
| 400 | Validation error or no company selected — `{"error":"…"}` |
|
||||||
|
| 401 | Unauthorized — `{"error":"…"}` |
|
||||||
|
| 403 | Not authorized — `{"error":"…"}` |
|
||||||
|
| 404 | Shipping or entity address not found — `{"error":"…"}` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._
|
||||||
1
package-lock.json
generated
1
package-lock.json
generated
@@ -34,6 +34,7 @@
|
|||||||
"vm2": "^3.12.2"
|
"vm2": "^3.12.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"acorn": "^8.19.0",
|
||||||
"swagger-jsdoc": "^6.3.0"
|
"swagger-jsdoc": "^6.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
"manage-users": "node scripts/manageUsers.js",
|
"manage-users": "node scripts/manageUsers.js",
|
||||||
"wipe-company": "node scripts/wipeCompany.js",
|
"wipe-company": "node scripts/wipeCompany.js",
|
||||||
"docs:api": "node scripts/generateApiDocs.js",
|
"docs:api": "node scripts/generateApiDocs.js",
|
||||||
|
"review:functions": "node scripts/extractFunctions.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@@ -40,6 +41,7 @@
|
|||||||
"vm2": "^3.12.2"
|
"vm2": "^3.12.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"acorn": "^8.19.0",
|
||||||
"swagger-jsdoc": "^6.3.0"
|
"swagger-jsdoc": "^6.3.0"
|
||||||
},
|
},
|
||||||
"allowScripts": {
|
"allowScripts": {
|
||||||
|
|||||||
133
scripts/extractFunctions.js
Normal file
133
scripts/extractFunctions.js
Normal file
@@ -0,0 +1,133 @@
|
|||||||
|
import fs from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { parse } from 'acorn';
|
||||||
|
|
||||||
|
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
||||||
|
const rootDir = path.resolve(scriptDir, '..');
|
||||||
|
const outputDir = path.join(rootDir, 'output');
|
||||||
|
const excludedDirs = new Set(['.git', 'data', 'node_modules', 'output']);
|
||||||
|
const functions = [];
|
||||||
|
|
||||||
|
function collectJavaScriptFiles(dir) {
|
||||||
|
const files = [];
|
||||||
|
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||||
|
if (entry.isDirectory() && excludedDirs.has(entry.name)) continue;
|
||||||
|
|
||||||
|
const fullPath = path.join(dir, entry.name);
|
||||||
|
if (entry.isDirectory()) {
|
||||||
|
files.push(...collectJavaScriptFiles(fullPath));
|
||||||
|
} else if (entry.isFile() && entry.name.endsWith('.js')) {
|
||||||
|
files.push(fullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files;
|
||||||
|
}
|
||||||
|
|
||||||
|
function inferredName(node, parent) {
|
||||||
|
if (node.id?.name) return node.id.name;
|
||||||
|
if (parent?.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
|
||||||
|
return parent.id.name;
|
||||||
|
}
|
||||||
|
if (parent?.type === 'AssignmentExpression') {
|
||||||
|
return parent.left.name ?? parent.left.property?.name ?? 'assigned';
|
||||||
|
}
|
||||||
|
if (parent?.type === 'Property' || parent?.type === 'MethodDefinition') {
|
||||||
|
return parent.key.name ?? parent.key.value ?? 'method';
|
||||||
|
}
|
||||||
|
if (parent?.type === 'ExportDefaultDeclaration') return 'default';
|
||||||
|
return 'anonymous';
|
||||||
|
}
|
||||||
|
|
||||||
|
function visit(node, parent, source, relativePath) {
|
||||||
|
if (!node || typeof node !== 'object') return;
|
||||||
|
|
||||||
|
if (node.type === 'FunctionDeclaration'
|
||||||
|
|| node.type === 'FunctionExpression'
|
||||||
|
|| node.type === 'ArrowFunctionExpression') {
|
||||||
|
const method = (parent?.type === 'MethodDefinition'
|
||||||
|
|| (parent?.type === 'Property' && parent.method))
|
||||||
|
? parent
|
||||||
|
: node;
|
||||||
|
const start = method.start;
|
||||||
|
const end = method.end;
|
||||||
|
const startLine = source.slice(0, start).split('\n').length;
|
||||||
|
const endLine = source.slice(0, end).split('\n').length;
|
||||||
|
const name = inferredName(node, parent);
|
||||||
|
let signature = source.slice(start, node.body.start).replace(/\s+/g, ' ').trim();
|
||||||
|
if (method === node
|
||||||
|
&& name !== 'anonymous'
|
||||||
|
&& node.type !== 'FunctionDeclaration'
|
||||||
|
&& parent?.type !== 'ExportDefaultDeclaration') {
|
||||||
|
signature = `${name} = ${signature}`;
|
||||||
|
}
|
||||||
|
functions.push({
|
||||||
|
source: relativePath,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
startLine,
|
||||||
|
endLine,
|
||||||
|
name,
|
||||||
|
signature,
|
||||||
|
code: source.slice(start, end),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const [key, value] of Object.entries(node)) {
|
||||||
|
if (key === 'start' || key === 'end' || key === 'loc') continue;
|
||||||
|
if (Array.isArray(value)) {
|
||||||
|
for (const child of value) visit(child, node, source, relativePath);
|
||||||
|
} else if (value && typeof value === 'object') {
|
||||||
|
visit(value, node, source, relativePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const sourceFiles = collectJavaScriptFiles(rootDir).sort();
|
||||||
|
for (const filePath of sourceFiles) {
|
||||||
|
const source = fs.readFileSync(filePath, 'utf8');
|
||||||
|
const relativePath = path.relative(rootDir, filePath).split(path.sep).join('/');
|
||||||
|
let ast;
|
||||||
|
try {
|
||||||
|
ast = parse(source, {
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module',
|
||||||
|
allowHashBang: true,
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(`Could not parse ${relativePath}: ${error.message}`, { cause: error });
|
||||||
|
}
|
||||||
|
visit(ast, null, source, relativePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.mkdirSync(outputDir, { recursive: true });
|
||||||
|
for (const entry of fs.readdirSync(outputDir, { withFileTypes: true })) {
|
||||||
|
if (entry.isFile() && /^function-.*\.js$/.test(entry.name)) {
|
||||||
|
fs.unlinkSync(path.join(outputDir, entry.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
functions.sort((a, b) => a.source.localeCompare(b.source) || a.start - b.start);
|
||||||
|
const nameCounts = new Map();
|
||||||
|
for (const fn of functions) {
|
||||||
|
const sourceSlug = fn.source.replace(/\.js$/, '').replace(/[^a-zA-Z0-9_-]+/g, '-');
|
||||||
|
const nameSlug = fn.name.replace(/[^a-zA-Z0-9_-]+/g, '-');
|
||||||
|
const baseName = `function-${sourceSlug}-L${fn.startLine}-${nameSlug}`;
|
||||||
|
const sequence = (nameCounts.get(baseName) ?? 0) + 1;
|
||||||
|
nameCounts.set(baseName, sequence);
|
||||||
|
|
||||||
|
const outputName = `${baseName}-${sequence}.js`;
|
||||||
|
const contents = [
|
||||||
|
`// Source: ${fn.source} (lines ${fn.startLine}-${fn.endLine})`,
|
||||||
|
fn.code,
|
||||||
|
'',
|
||||||
|
].join('\n');
|
||||||
|
fs.writeFileSync(path.join(outputDir, outputName), contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = functions
|
||||||
|
.map(fn => `${fn.source}:${fn.startLine}\t${fn.signature}`)
|
||||||
|
.join('\n');
|
||||||
|
fs.writeFileSync(path.join(outputDir, 'functions.txt'), `${index}\n`);
|
||||||
|
|
||||||
|
console.log(`Extracted ${functions.length} functions from ${sourceFiles.length} JavaScript files into ${path.relative(rootDir, outputDir)}/`);
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import env from '../../config/env.js';
|
import env from '../../config/env.js';
|
||||||
|
|
||||||
export function meJson(res, status, body) {
|
export function respondWithAuthenticatedUser(res, status, body) {
|
||||||
return res.status(status).json({ ...body, googleClientId: env.googleClientId });
|
return res.status(status).json({ ...body, googleClientId: env.googleClientId });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1096,6 +1096,67 @@
|
|||||||
* description: Tool response content payload
|
* description: Tool response content payload
|
||||||
* answer:
|
* answer:
|
||||||
* description: Shorthand answer for ask_user response
|
* description: Shorthand answer for ask_user response
|
||||||
|
* Account:
|
||||||
|
* type: object
|
||||||
|
* required: [id, companyId, type, name]
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* type: integer
|
||||||
|
* companyId:
|
||||||
|
* type: integer
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* Payment:
|
||||||
|
* type: object
|
||||||
|
* required: [id, accountId, manifestId, type, amount]
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* type: integer
|
||||||
|
* accountId:
|
||||||
|
* type: integer
|
||||||
|
* manifestId:
|
||||||
|
* type: integer
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* amount:
|
||||||
|
* type: number
|
||||||
|
* Shipping:
|
||||||
|
* type: object
|
||||||
|
* required: [id, manifestId, number, type, state]
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* type: integer
|
||||||
|
* manifestId:
|
||||||
|
* type: integer
|
||||||
|
* number:
|
||||||
|
* type: string
|
||||||
|
* description: Shipping number from the `shipping` number sequence
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* state:
|
||||||
|
* type: string
|
||||||
|
* Package:
|
||||||
|
* type: object
|
||||||
|
* required: [id, shippingId, entityaddressId, number, type, state]
|
||||||
|
* properties:
|
||||||
|
* id:
|
||||||
|
* type: integer
|
||||||
|
* shippingId:
|
||||||
|
* type: integer
|
||||||
|
* entityaddressId:
|
||||||
|
* type: integer
|
||||||
|
* number:
|
||||||
|
* type: string
|
||||||
|
* description: Package number from the `package` number sequence
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* state:
|
||||||
|
* type: string
|
||||||
|
* details:
|
||||||
|
* type: string
|
||||||
|
* nullable: true
|
||||||
* Printer:
|
* Printer:
|
||||||
* type: object
|
* type: object
|
||||||
* required: [id, company_id, address, type, sort]
|
* required: [id, company_id, address, type, sort]
|
||||||
|
|||||||
213
src/api/routes/account.js
Normal file
213
src/api/routes/account.js
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import * as db from '../../db/account.js';
|
||||||
|
|
||||||
|
function requireSelectedCompany(req) {
|
||||||
|
const companyId = req.user?.selectedCompanyId;
|
||||||
|
if (companyId == null) {
|
||||||
|
return { error: 'No company selected' };
|
||||||
|
}
|
||||||
|
return { companyId };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePositiveInt(value, field) {
|
||||||
|
const id = Number(value);
|
||||||
|
if (!Number.isInteger(id) || id <= 0) {
|
||||||
|
return { error: `${field} is required` };
|
||||||
|
}
|
||||||
|
return { id };
|
||||||
|
}
|
||||||
|
|
||||||
|
function accountErrorStatus(error) {
|
||||||
|
if (error.message === 'No company selected') return 400;
|
||||||
|
if (error.message === 'Not authorized') return 403;
|
||||||
|
if (error.message?.endsWith(' not found')) return 404;
|
||||||
|
if (error.message?.endsWith(' required')) return 400;
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers account routes for the authenticated user's selected company.
|
||||||
|
* @param {import('express').Application} app
|
||||||
|
* @param {string} [basePath='']
|
||||||
|
*/
|
||||||
|
export function register(app, basePath = '') {
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /account:
|
||||||
|
* get:
|
||||||
|
* tags: [Account]
|
||||||
|
* summary: List accounts
|
||||||
|
* description: Lists payment accounts for the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: List of accounts
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* accounts:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/components/schemas/Account'
|
||||||
|
* 400:
|
||||||
|
* description: No company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.get(`${basePath}/account`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const accounts = await db.list(req.user.id);
|
||||||
|
res.status(200).json({ accounts });
|
||||||
|
} catch (error) {
|
||||||
|
const status = accountErrorStatus(error);
|
||||||
|
if (status === 500) console.error('List accounts failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /account:
|
||||||
|
* post:
|
||||||
|
* tags: [Account]
|
||||||
|
* summary: Create account
|
||||||
|
* description: Creates a payment account for the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* required: [type, name]
|
||||||
|
* properties:
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* name:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 201:
|
||||||
|
* description: Created account
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* account:
|
||||||
|
* $ref: '#/components/schemas/Account'
|
||||||
|
* 400:
|
||||||
|
* description: Validation error or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.post(`${basePath}/account`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const account = await db.create(req.user.id, req.body ?? {});
|
||||||
|
res.status(201).json({ account });
|
||||||
|
} catch (error) {
|
||||||
|
const status = accountErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Create account failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /account/{id}:
|
||||||
|
* delete:
|
||||||
|
* tags: [Account]
|
||||||
|
* summary: Delete account
|
||||||
|
* description: Deletes an account of the selected company. Its payments are removed as well.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Account ID
|
||||||
|
* responses:
|
||||||
|
* 204:
|
||||||
|
* description: Account deleted
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 404:
|
||||||
|
* description: Account not found
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.delete(`${basePath}/account/:id`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.remove(req.user.id, parsedId.id);
|
||||||
|
res.status(204).end();
|
||||||
|
} catch (error) {
|
||||||
|
const status = accountErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Delete account failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
252
src/api/routes/package.js
Normal file
252
src/api/routes/package.js
Normal file
@@ -0,0 +1,252 @@
|
|||||||
|
import * as db from '../../db/package.js';
|
||||||
|
|
||||||
|
function requireSelectedCompany(req) {
|
||||||
|
const companyId = req.user?.selectedCompanyId;
|
||||||
|
if (companyId == null) {
|
||||||
|
return { error: 'No company selected' };
|
||||||
|
}
|
||||||
|
return { companyId };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePositiveInt(value, field) {
|
||||||
|
const id = Number(value);
|
||||||
|
if (!Number.isInteger(id) || id <= 0) {
|
||||||
|
return { error: `${field} is required` };
|
||||||
|
}
|
||||||
|
return { id };
|
||||||
|
}
|
||||||
|
|
||||||
|
function packageErrorStatus(error) {
|
||||||
|
if (error.message === 'No company selected') return 400;
|
||||||
|
if (error.message === 'Not authorized') return 403;
|
||||||
|
if (error.message?.endsWith(' not found')) return 404;
|
||||||
|
if (error.message?.endsWith(' required')) return 400;
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers package routes (packages belong to a shipping and target an entity address).
|
||||||
|
* @param {import('express').Application} app
|
||||||
|
* @param {string} [basePath='']
|
||||||
|
*/
|
||||||
|
export function register(app, basePath = '') {
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /shipping/{id}/package:
|
||||||
|
* get:
|
||||||
|
* tags: [Package]
|
||||||
|
* summary: List packages of a shipping
|
||||||
|
* description: Lists packages of the shipping for the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Shipping ID
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: List of packages
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* packages:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/components/schemas/Package'
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 404:
|
||||||
|
* description: Shipping not found
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.get(`${basePath}/shipping/:id/package`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const packages = await db.listByShippingId(req.user.id, parsedId.id);
|
||||||
|
res.status(200).json({ packages });
|
||||||
|
} catch (error) {
|
||||||
|
const status = packageErrorStatus(error);
|
||||||
|
if (status === 500) console.error('List packages failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /shipping/{id}/package:
|
||||||
|
* post:
|
||||||
|
* tags: [Package]
|
||||||
|
* summary: Create package for a shipping
|
||||||
|
* description: |
|
||||||
|
* Creates a package for the shipping. The package number is allocated from the
|
||||||
|
* `package` number sequence. `entityaddressId` is required (target address).
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Shipping ID
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* required: [entityaddressId, type, state]
|
||||||
|
* properties:
|
||||||
|
* entityaddressId:
|
||||||
|
* type: integer
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* state:
|
||||||
|
* type: string
|
||||||
|
* details:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 201:
|
||||||
|
* description: Created package
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* package:
|
||||||
|
* $ref: '#/components/schemas/Package'
|
||||||
|
* 400:
|
||||||
|
* description: Validation error or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 404:
|
||||||
|
* description: Shipping or entity address not found
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.post(`${basePath}/shipping/:id/package`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const pkg = await db.create(req.user.id, {
|
||||||
|
...(req.body ?? {}),
|
||||||
|
shippingId: parsedId.id,
|
||||||
|
});
|
||||||
|
res.status(201).json({ package: pkg });
|
||||||
|
} catch (error) {
|
||||||
|
const status = packageErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Create package failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /package/{id}:
|
||||||
|
* delete:
|
||||||
|
* tags: [Package]
|
||||||
|
* summary: Delete package
|
||||||
|
* description: Deletes a package of the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Package ID
|
||||||
|
* responses:
|
||||||
|
* 204:
|
||||||
|
* description: Package deleted
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.delete(`${basePath}/package/:id`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.remove(req.user.id, parsedId.id);
|
||||||
|
res.status(204).end();
|
||||||
|
} catch (error) {
|
||||||
|
const status = packageErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Delete package failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
249
src/api/routes/payment.js
Normal file
249
src/api/routes/payment.js
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
import * as db from '../../db/payment.js';
|
||||||
|
|
||||||
|
function requireSelectedCompany(req) {
|
||||||
|
const companyId = req.user?.selectedCompanyId;
|
||||||
|
if (companyId == null) {
|
||||||
|
return { error: 'No company selected' };
|
||||||
|
}
|
||||||
|
return { companyId };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePositiveInt(value, field) {
|
||||||
|
const id = Number(value);
|
||||||
|
if (!Number.isInteger(id) || id <= 0) {
|
||||||
|
return { error: `${field} is required` };
|
||||||
|
}
|
||||||
|
return { id };
|
||||||
|
}
|
||||||
|
|
||||||
|
function paymentErrorStatus(error) {
|
||||||
|
if (error.message === 'No company selected') return 400;
|
||||||
|
if (error.message === 'Not authorized') return 403;
|
||||||
|
if (error.message?.endsWith(' not found')) return 404;
|
||||||
|
if (error.message?.endsWith(' required')) return 400;
|
||||||
|
if (error.message === 'Invalid payment amount') return 400;
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers payment routes (payments belong to an order / manifest).
|
||||||
|
* @param {import('express').Application} app
|
||||||
|
* @param {string} [basePath='']
|
||||||
|
*/
|
||||||
|
export function register(app, basePath = '') {
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /order/{id}/payment:
|
||||||
|
* get:
|
||||||
|
* tags: [Payment]
|
||||||
|
* summary: List payments of an order
|
||||||
|
* description: Lists payments of the order (manifest) for the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Order ID
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: List of payments
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* payments:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/components/schemas/Payment'
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 404:
|
||||||
|
* description: Order not found
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.get(`${basePath}/order/:id/payment`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payments = await db.listByManifestId(req.user.id, parsedId.id);
|
||||||
|
res.status(200).json({ payments });
|
||||||
|
} catch (error) {
|
||||||
|
const status = paymentErrorStatus(error);
|
||||||
|
if (status === 500) console.error('List payments failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /order/{id}/payment:
|
||||||
|
* post:
|
||||||
|
* tags: [Payment]
|
||||||
|
* summary: Add payment to an order
|
||||||
|
* description: Adds a payment to the order using an account of the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Order ID
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* required: [accountId, type, amount]
|
||||||
|
* properties:
|
||||||
|
* accountId:
|
||||||
|
* type: integer
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* amount:
|
||||||
|
* type: number
|
||||||
|
* responses:
|
||||||
|
* 201:
|
||||||
|
* description: Created payment
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* payment:
|
||||||
|
* $ref: '#/components/schemas/Payment'
|
||||||
|
* 400:
|
||||||
|
* description: Validation error or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 404:
|
||||||
|
* description: Order or account not found
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.post(`${basePath}/order/:id/payment`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const payment = await db.create(req.user.id, {
|
||||||
|
...(req.body ?? {}),
|
||||||
|
manifestId: parsedId.id,
|
||||||
|
});
|
||||||
|
res.status(201).json({ payment });
|
||||||
|
} catch (error) {
|
||||||
|
const status = paymentErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Create payment failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /payment/{id}:
|
||||||
|
* delete:
|
||||||
|
* tags: [Payment]
|
||||||
|
* summary: Delete payment
|
||||||
|
* description: Deletes a payment of an order in the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Payment ID
|
||||||
|
* responses:
|
||||||
|
* 204:
|
||||||
|
* description: Payment deleted
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.delete(`${basePath}/payment/:id`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.remove(req.user.id, parsedId.id);
|
||||||
|
res.status(204).end();
|
||||||
|
} catch (error) {
|
||||||
|
const status = paymentErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Delete payment failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
248
src/api/routes/shipping.js
Normal file
248
src/api/routes/shipping.js
Normal file
@@ -0,0 +1,248 @@
|
|||||||
|
import * as db from '../../db/shipping.js';
|
||||||
|
|
||||||
|
function requireSelectedCompany(req) {
|
||||||
|
const companyId = req.user?.selectedCompanyId;
|
||||||
|
if (companyId == null) {
|
||||||
|
return { error: 'No company selected' };
|
||||||
|
}
|
||||||
|
return { companyId };
|
||||||
|
}
|
||||||
|
|
||||||
|
function parsePositiveInt(value, field) {
|
||||||
|
const id = Number(value);
|
||||||
|
if (!Number.isInteger(id) || id <= 0) {
|
||||||
|
return { error: `${field} is required` };
|
||||||
|
}
|
||||||
|
return { id };
|
||||||
|
}
|
||||||
|
|
||||||
|
function shippingErrorStatus(error) {
|
||||||
|
if (error.message === 'No company selected') return 400;
|
||||||
|
if (error.message === 'Not authorized') return 403;
|
||||||
|
if (error.message?.endsWith(' not found')) return 404;
|
||||||
|
if (error.message?.endsWith(' required')) return 400;
|
||||||
|
return 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers shipping routes (shippings belong to an order / manifest).
|
||||||
|
* @param {import('express').Application} app
|
||||||
|
* @param {string} [basePath='']
|
||||||
|
*/
|
||||||
|
export function register(app, basePath = '') {
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /order/{id}/shipping:
|
||||||
|
* get:
|
||||||
|
* tags: [Shipping]
|
||||||
|
* summary: List shippings of an order
|
||||||
|
* description: Lists shippings of the order (manifest) for the selected company.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Order ID
|
||||||
|
* responses:
|
||||||
|
* 200:
|
||||||
|
* description: List of shippings
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* shippings:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* $ref: '#/components/schemas/Shipping'
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 404:
|
||||||
|
* description: Order not found
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.get(`${basePath}/order/:id/shipping`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const shippings = await db.listByManifestId(req.user.id, parsedId.id);
|
||||||
|
res.status(200).json({ shippings });
|
||||||
|
} catch (error) {
|
||||||
|
const status = shippingErrorStatus(error);
|
||||||
|
if (status === 500) console.error('List shippings failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /order/{id}/shipping:
|
||||||
|
* post:
|
||||||
|
* tags: [Shipping]
|
||||||
|
* summary: Create shipping for an order
|
||||||
|
* description: |
|
||||||
|
* Creates a shipping for the order. The shipping number is allocated from the
|
||||||
|
* `shipping` number sequence (prefix and counter from the numbers API).
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Order ID
|
||||||
|
* requestBody:
|
||||||
|
* required: true
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* required: [type, state]
|
||||||
|
* properties:
|
||||||
|
* type:
|
||||||
|
* type: string
|
||||||
|
* state:
|
||||||
|
* type: string
|
||||||
|
* responses:
|
||||||
|
* 201:
|
||||||
|
* description: Created shipping
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* shipping:
|
||||||
|
* $ref: '#/components/schemas/Shipping'
|
||||||
|
* 400:
|
||||||
|
* description: Validation error or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 404:
|
||||||
|
* description: Order not found
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.post(`${basePath}/order/:id/shipping`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const shipping = await db.create(req.user.id, {
|
||||||
|
...(req.body ?? {}),
|
||||||
|
manifestId: parsedId.id,
|
||||||
|
});
|
||||||
|
res.status(201).json({ shipping });
|
||||||
|
} catch (error) {
|
||||||
|
const status = shippingErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Create shipping failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @openapi
|
||||||
|
* /shipping/{id}:
|
||||||
|
* delete:
|
||||||
|
* tags: [Shipping]
|
||||||
|
* summary: Delete shipping
|
||||||
|
* description: Deletes a shipping of the selected company. Its packages are removed as well.
|
||||||
|
* security:
|
||||||
|
* - cookieAuth: []
|
||||||
|
* parameters:
|
||||||
|
* - in: path
|
||||||
|
* name: id
|
||||||
|
* required: true
|
||||||
|
* schema:
|
||||||
|
* type: integer
|
||||||
|
* description: Shipping ID
|
||||||
|
* responses:
|
||||||
|
* 204:
|
||||||
|
* description: Shipping deleted
|
||||||
|
* 400:
|
||||||
|
* description: Invalid ID or no company selected
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 401:
|
||||||
|
* description: Unauthorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
* 403:
|
||||||
|
* description: Not authorized
|
||||||
|
* content:
|
||||||
|
* application/json:
|
||||||
|
* schema:
|
||||||
|
* $ref: '#/components/schemas/Error'
|
||||||
|
*/
|
||||||
|
app.delete(`${basePath}/shipping/:id`, async (req, res) => {
|
||||||
|
const selected = requireSelectedCompany(req);
|
||||||
|
if (selected.error) {
|
||||||
|
return res.status(400).json({ error: selected.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
const parsedId = parsePositiveInt(req.params.id, 'id');
|
||||||
|
if (parsedId.error) {
|
||||||
|
return res.status(400).json({ error: parsedId.error });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await db.remove(req.user.id, parsedId.id);
|
||||||
|
res.status(204).end();
|
||||||
|
} catch (error) {
|
||||||
|
const status = shippingErrorStatus(error);
|
||||||
|
if (status === 500) console.error('Delete shipping failed:', error);
|
||||||
|
res.status(status).json({ error: error.message });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
16
src/db/account.js
Normal file
16
src/db/account.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { getStore } from './store.js';
|
||||||
|
|
||||||
|
export async function list(googleAuthId) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.account.list(googleAuthId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(googleAuthId, data) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.account.create(googleAuthId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(googleAuthId, id) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.account.delete(googleAuthId, id);
|
||||||
|
}
|
||||||
16
src/db/package.js
Normal file
16
src/db/package.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { getStore } from './store.js';
|
||||||
|
|
||||||
|
export async function listByShippingId(googleAuthId, shippingId) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.package.listByShippingId(googleAuthId, shippingId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(googleAuthId, data) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.package.create(googleAuthId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(googleAuthId, id) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.package.delete(googleAuthId, id);
|
||||||
|
}
|
||||||
16
src/db/payment.js
Normal file
16
src/db/payment.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { getStore } from './store.js';
|
||||||
|
|
||||||
|
export async function listByManifestId(googleAuthId, manifestId) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.payment.listByManifestId(googleAuthId, manifestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(googleAuthId, data) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.payment.create(googleAuthId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(googleAuthId, id) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.payment.delete(googleAuthId, id);
|
||||||
|
}
|
||||||
16
src/db/shipping.js
Normal file
16
src/db/shipping.js
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
import { getStore } from './store.js';
|
||||||
|
|
||||||
|
export async function listByManifestId(googleAuthId, manifestId) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.shipping.listByManifestId(googleAuthId, manifestId);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function create(googleAuthId, data) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.shipping.create(googleAuthId, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function remove(googleAuthId, id) {
|
||||||
|
const store = await getStore();
|
||||||
|
return store.shipping.delete(googleAuthId, id);
|
||||||
|
}
|
||||||
@@ -48,6 +48,10 @@ const STORE_NAMESPACES = [
|
|||||||
'categoryattributevalue',
|
'categoryattributevalue',
|
||||||
'modelconversation',
|
'modelconversation',
|
||||||
'modelmessage',
|
'modelmessage',
|
||||||
|
'account',
|
||||||
|
'payment',
|
||||||
|
'shipping',
|
||||||
|
'package',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user