Add quote and order HTTP APIs on manifest documents.

Expose store-backed CRUD for quotes and sales orders with shared ManifestLine shapes, entityId customers, and documented date semantics.
This commit is contained in:
seb
2026-08-06 19:58:25 +02:00
parent 74269cd3ef
commit cdae7b6eab
19 changed files with 2668 additions and 0 deletions

View File

@@ -90,6 +90,11 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
| [`routes/events.md`](routes/events.md) | `GET /events` |
| [`routes/health.md`](routes/health.md) | `GET /health` |
| [`routes/image/get.md`](routes/image/get.md) | `GET /image/{fullHash}` |
| [`routes/order/list.md`](routes/order/list.md) | `GET /order` |
| [`routes/order/create.md`](routes/order/create.md) | `POST /order` |
| [`routes/order/delete.md`](routes/order/delete.md) | `DELETE /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/posterminal/list.md`](routes/posterminal/list.md) | `GET /posterminal` |
| [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | `PUT /posterminal` |
| [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | `DELETE /posterminal/{id}` |
@@ -97,6 +102,11 @@ Generated from `@openapi` annotations in [`src/api/routes/`](../../src/api/route
| [`routes/posterminal/setOnAll.md`](routes/posterminal/setOnAll.md) | `PUT /posterminal/{id}/all` |
| [`routes/posterminal/setOff.md`](routes/posterminal/setOff.md) | `DELETE /posterminal/{id}/category/{categoryId}` |
| [`routes/posterminal/setOn.md`](routes/posterminal/setOn.md) | `PUT /posterminal/{id}/category/{categoryId}` |
| [`routes/quote/list.md`](routes/quote/list.md) | `GET /quote` |
| [`routes/quote/create.md`](routes/quote/create.md) | `POST /quote` |
| [`routes/quote/delete.md`](routes/quote/delete.md) | `DELETE /quote/{id}` |
| [`routes/quote/get.md`](routes/quote/get.md) | `GET /quote/{id}` |
| [`routes/quote/update.md`](routes/quote/update.md) | `PATCH /quote/{id}` |
| [`routes/supplier/list.md`](routes/supplier/list.md) | `GET /supplier` |
| [`routes/supplier/create.md`](routes/supplier/create.md) | `POST /supplier` |
| [`routes/supplier/delete.md`](routes/supplier/delete.md) | `DELETE /supplier/{id}` |

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,70 @@
# `POST /order`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Create order
Creates a sales order with header and lines in one transaction.
`date` is the document date (expected delivery).
`entityId` must be a company customer (not Lieferanten/POS), or null.
Each line requires `sku`, `amount`, `net`, and `tax` (rate); `rebate` defaults to `0`.
## Body (JSON)
```json
{
"number": "O-2026-001",
"date": "2026-09-15",
"entityId": 1,
"lines": [
{
"sku": "1",
"amount": 10,
"net": 99.5,
"tax": 19
}
]
}
```
## Response `201`
Created order
```json
{
"order": {
"id": 1,
"number": "…",
"type": "order",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z",
"lines": [
{
"id": 1,
"articleId": 1,
"sku": "…",
"amount": 1,
"net": 1,
"rebate": 1,
"tax": 1,
"comment": "…"
}
]
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid order, invalid lines, or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Company, entity, or article not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,32 @@
# `DELETE /order/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Delete order
Deletes the sales order (`manifestarticle` rows cascade).
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | |
## Response `204`
Deleted
_No content_
## Errors
| Status | Description |
|--------|-------------|
| 400 | No company selected or invalid id — `{"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`._

View File

@@ -0,0 +1,54 @@
# `GET /order/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Get order
Returns a sales order with lines for the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | |
## Response `200`
Order
```json
{
"order": {
"id": 1,
"number": "…",
"type": "order",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z",
"lines": [
{
"id": 1,
"articleId": 1,
"sku": "…",
"amount": 1,
"net": 1,
"rebate": 1,
"tax": 1,
"comment": "…"
}
]
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | No company selected or invalid id — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Order or company not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,40 @@
# `GET /order`
Protected. Requires a valid session cookie (`credentials: 'include'`).
List orders
Lists sales orders (`manifest` rows with `type = 'order'`)
for the authenticated user's selected company.
## Response `200`
Order list
```json
{
"orders": [
{
"id": 1,
"number": "…",
"type": "order",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z"
}
]
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | No company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Company not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,66 @@
# `PATCH /order/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Update order
Patches header fields that are present. If `lines` is present, replaces
all lines; if omitted, leaves lines unchanged. Returns the full order.
`date` is the document date (expected delivery).
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | |
## Body (JSON)
```json
{
"number": "O-2026-001b",
"lines": []
}
```
## Response `200`
Updated order
```json
{
"order": {
"id": 1,
"number": "…",
"type": "order",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z",
"lines": [
{
"id": 1,
"articleId": 1,
"sku": "…",
"amount": 1,
"net": 1,
"rebate": 1,
"tax": 1,
"comment": "…"
}
]
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid order, invalid lines, invalid id, or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Order, entity, article, or company not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,70 @@
# `POST /quote`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Create quote
Creates a quote with header and lines in one transaction.
`date` is the document date (valid-until).
`entityId` must be a company customer (not Lieferanten/POS), or null.
Each line requires `sku`, `amount`, `net`, and `tax` (rate); `rebate` defaults to `0`.
## Body (JSON)
```json
{
"number": "Q-2026-001",
"date": "2026-08-31",
"entityId": 1,
"lines": [
{
"sku": "1",
"amount": 10,
"net": 99.5,
"tax": 19
}
]
}
```
## Response `201`
Created quote
```json
{
"quote": {
"id": 1,
"number": "…",
"type": "quote",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z",
"lines": [
{
"id": 1,
"articleId": 1,
"sku": "…",
"amount": 1,
"net": 1,
"rebate": 1,
"tax": 1,
"comment": "…"
}
]
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid quote, invalid lines, or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Company, entity, or article not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,32 @@
# `DELETE /quote/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Delete quote
Deletes the quote (`manifestarticle` rows cascade).
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | |
## Response `204`
Deleted
_No content_
## Errors
| Status | Description |
|--------|-------------|
| 400 | No company selected or invalid id — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Quote not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,54 @@
# `GET /quote/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Get quote
Returns a quote with lines for the selected company.
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | |
## Response `200`
Quote
```json
{
"quote": {
"id": 1,
"number": "…",
"type": "quote",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z",
"lines": [
{
"id": 1,
"articleId": 1,
"sku": "…",
"amount": 1,
"net": 1,
"rebate": 1,
"tax": 1,
"comment": "…"
}
]
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | No company selected or invalid id — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Quote or company not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,40 @@
# `GET /quote`
Protected. Requires a valid session cookie (`credentials: 'include'`).
List quotes
Lists quotes (`manifest` rows with `type = 'quote'`)
for the authenticated user's selected company.
## Response `200`
Quote list
```json
{
"quotes": [
{
"id": 1,
"number": "…",
"type": "quote",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z"
}
]
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | No company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Company not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._

View File

@@ -0,0 +1,66 @@
# `PATCH /quote/{id}`
Protected. Requires a valid session cookie (`credentials: 'include'`).
Update quote
Patches header fields that are present. If `lines` is present, replaces
all lines; if omitted, leaves lines unchanged. Returns the full quote.
`date` is the document date (valid-until).
## Path parameters
| Param | Required | Type | Description |
|-------|----------|------|-------------|
| `id` | yes | integer | |
## Body (JSON)
```json
{
"number": "Q-2026-001b",
"lines": []
}
```
## Response `200`
Updated quote
```json
{
"quote": {
"id": 1,
"number": "…",
"type": "quote",
"entityId": 1,
"date": "2026-08-10T00:00:00.000Z",
"lines": [
{
"id": 1,
"articleId": 1,
"sku": "…",
"amount": 1,
"net": 1,
"rebate": 1,
"tax": 1,
"comment": "…"
}
]
}
}
```
## Errors
| Status | Description |
|--------|-------------|
| 400 | Invalid quote, invalid lines, invalid id, or no company selected — `{"error":"…"}` |
| 401 | Unauthorized — `{"error":"…"}` |
| 403 | Not authorized — `{"error":"…"}` |
| 404 | Quote, entity, article, or company not found — `{"error":"…"}` |
---
_Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._