diff --git a/.env.example b/.env.example index b1b052a..17ba03e 100644 --- a/.env.example +++ b/.env.example @@ -30,3 +30,5 @@ KAT_LISTS=0 # Tax zone name for VAT rates (tSteuerzone.cName) STEUER_ZONE=Zone-EU + +OPENROUTER_FREE_KEY=sk-or-v1-3d3c4e diff --git a/docs/api/openapi.json b/docs/api/openapi.json index d658d68..9320f84 100644 --- a/docs/api/openapi.json +++ b/docs/api/openapi.json @@ -8173,6 +8173,16 @@ } } } + }, + "409": { + "description": "SKU already exists", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } } } } @@ -10444,6 +10454,24 @@ "date": { "type": "string", "format": "date-time" + }, + "createdAt": { + "type": "string", + "format": "date-time" + }, + "ledger": { + "type": "object", + "description": "Ledger booking summary for this delivery", + "properties": { + "totalPositions": { + "type": "integer", + "description": "Total number of position lines on this manifest" + }, + "completelyOnLedgerPositions": { + "type": "integer", + "description": "Number of positions whose quantity is fully transacted in the ledger" + } + } } } }, @@ -10818,6 +10846,10 @@ "type": "string", "format": "date-time", "description": "Document date / valid-until" + }, + "createdAt": { + "type": "string", + "format": "date-time" } } }, @@ -10873,6 +10905,10 @@ "type": "string", "format": "date-time", "description": "Document date / expected delivery" + }, + "createdAt": { + "type": "string", + "format": "date-time" } } }, diff --git a/docs/api/routes/article/create.md b/docs/api/routes/article/create.md index 2cf45fd..8296c40 100644 --- a/docs/api/routes/article/create.md +++ b/docs/api/routes/article/create.md @@ -42,6 +42,7 @@ Created | 401 | Unauthorized — `{"error":"…"}` | | 403 | Not authorized for parent category — `{"error":"…"}` | | 404 | Parent not found — `{"error":"…"}` | +| 409 | SKU already exists — `{"error":"…"}` | --- diff --git a/docs/api/routes/delivery/create.md b/docs/api/routes/delivery/create.md index fdda471..6b71e7b 100644 --- a/docs/api/routes/delivery/create.md +++ b/docs/api/routes/delivery/create.md @@ -59,6 +59,11 @@ Created delivery "supplierId": 1, "externalNumber": "…", "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", + "ledger": { + "totalPositions": 1, + "completelyOnLedgerPositions": 1 + }, "lines": [ { "id": 1, diff --git a/docs/api/routes/delivery/get.md b/docs/api/routes/delivery/get.md index 9c819d6..81d199a 100644 --- a/docs/api/routes/delivery/get.md +++ b/docs/api/routes/delivery/get.md @@ -25,6 +25,11 @@ Delivery "supplierId": 1, "externalNumber": "…", "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", + "ledger": { + "totalPositions": 1, + "completelyOnLedgerPositions": 1 + }, "lines": [ { "id": 1, diff --git a/docs/api/routes/delivery/list.md b/docs/api/routes/delivery/list.md index 7e8cc2e..23f2c32 100644 --- a/docs/api/routes/delivery/list.md +++ b/docs/api/routes/delivery/list.md @@ -21,7 +21,12 @@ Delivery list "type": "delivery", "supplierId": 1, "externalNumber": "…", - "date": "2026-08-10T00:00:00.000Z" + "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", + "ledger": { + "totalPositions": 1, + "completelyOnLedgerPositions": 1 + } } ] } diff --git a/docs/api/routes/delivery/update.md b/docs/api/routes/delivery/update.md index 8bc20c0..d746fd7 100644 --- a/docs/api/routes/delivery/update.md +++ b/docs/api/routes/delivery/update.md @@ -46,6 +46,11 @@ Updated delivery "supplierId": 1, "externalNumber": "…", "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", + "ledger": { + "totalPositions": 1, + "completelyOnLedgerPositions": 1 + }, "lines": [ { "id": 1, diff --git a/docs/api/routes/order/create.md b/docs/api/routes/order/create.md index 9a260a5..569bca0 100644 --- a/docs/api/routes/order/create.md +++ b/docs/api/routes/order/create.md @@ -42,6 +42,7 @@ Created order "type": "order", "entityId": 1, "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", "lines": [ { "id": 1, diff --git a/docs/api/routes/order/get.md b/docs/api/routes/order/get.md index aa03d9d..88214e4 100644 --- a/docs/api/routes/order/get.md +++ b/docs/api/routes/order/get.md @@ -24,6 +24,7 @@ Order "type": "order", "entityId": 1, "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", "lines": [ { "id": 1, diff --git a/docs/api/routes/order/list.md b/docs/api/routes/order/list.md index df4e208..2c2f55c 100644 --- a/docs/api/routes/order/list.md +++ b/docs/api/routes/order/list.md @@ -20,7 +20,8 @@ Order list "number": "…", "type": "order", "entityId": 1, - "date": "2026-08-10T00:00:00.000Z" + "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z" } ] } diff --git a/docs/api/routes/order/update.md b/docs/api/routes/order/update.md index a8dafcd..d2f595a 100644 --- a/docs/api/routes/order/update.md +++ b/docs/api/routes/order/update.md @@ -36,6 +36,7 @@ Updated order "type": "order", "entityId": 1, "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", "lines": [ { "id": 1, diff --git a/docs/api/routes/quote/create.md b/docs/api/routes/quote/create.md index aba0aa4..92b33ed 100644 --- a/docs/api/routes/quote/create.md +++ b/docs/api/routes/quote/create.md @@ -42,6 +42,7 @@ Created quote "type": "quote", "entityId": 1, "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", "lines": [ { "id": 1, diff --git a/docs/api/routes/quote/get.md b/docs/api/routes/quote/get.md index dd17cff..39c3aa0 100644 --- a/docs/api/routes/quote/get.md +++ b/docs/api/routes/quote/get.md @@ -24,6 +24,7 @@ Quote "type": "quote", "entityId": 1, "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", "lines": [ { "id": 1, diff --git a/docs/api/routes/quote/list.md b/docs/api/routes/quote/list.md index ea40831..f24cdf2 100644 --- a/docs/api/routes/quote/list.md +++ b/docs/api/routes/quote/list.md @@ -20,7 +20,8 @@ Quote list "number": "…", "type": "quote", "entityId": 1, - "date": "2026-08-10T00:00:00.000Z" + "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z" } ] } diff --git a/docs/api/routes/quote/update.md b/docs/api/routes/quote/update.md index 8670553..87a3f16 100644 --- a/docs/api/routes/quote/update.md +++ b/docs/api/routes/quote/update.md @@ -36,6 +36,7 @@ Updated quote "type": "quote", "entityId": 1, "date": "2026-08-10T00:00:00.000Z", + "createdAt": "2026-08-10T00:00:00.000Z", "lines": [ { "id": 1, diff --git a/src/api/openapi.js b/src/api/openapi.js index 5d3b230..4b6345d 100644 --- a/src/api/openapi.js +++ b/src/api/openapi.js @@ -361,6 +361,19 @@ * date: * type: string * format: date-time + * createdAt: + * type: string + * format: date-time + * ledger: + * type: object + * description: Ledger booking summary for this delivery + * properties: + * totalPositions: + * type: integer + * description: Total number of position lines on this manifest + * completelyOnLedgerPositions: + * type: integer + * description: Number of positions whose quantity is fully transacted in the ledger * DeliveryLine: * type: object * required: [id, sku, name, pu, amount, net, rebate, taxrate, comment] @@ -591,6 +604,9 @@ * type: string * format: date-time * description: Document date / valid-until + * createdAt: + * type: string + * format: date-time * Quote: * allOf: * - $ref: '#/components/schemas/QuoteSummary' @@ -620,6 +636,9 @@ * type: string * format: date-time * description: Document date / expected delivery + * createdAt: + * type: string + * format: date-time * Order: * allOf: * - $ref: '#/components/schemas/OrderSummary' diff --git a/src/api/routes/delivery.js b/src/api/routes/delivery.js index 5e09c4f..8035907 100644 --- a/src/api/routes/delivery.js +++ b/src/api/routes/delivery.js @@ -47,7 +47,7 @@ function parseDeliveryPayload(req) { throw new Error('Invalid delivery'); } - const { externalNumber, date, supplierId, lines } = payload; + const { externalNumber, date, createdAt, supplierId, lines } = payload; let files = payload.files; // If binary files were uploaded via multer: if (Array.isArray(req.files) && req.files.length > 0) { @@ -96,7 +96,7 @@ function parseDeliveryPayload(req) { }; }); } - return { payload, externalNumber, date, supplierId, lines, files }; + return { payload, externalNumber, date, createdAt, supplierId, lines, files }; } function requireSelectedCompany(req) { @@ -354,11 +354,12 @@ export function register(app, basePath = '') { } try { - const { externalNumber, date, supplierId, lines, files } = parseDeliveryPayload(req); + const { externalNumber, date, createdAt, supplierId, lines, files } = parseDeliveryPayload(req); logDeliveryFiles(files, 'create'); const delivery = await db.create(req.user.id, { externalNumber, date, + createdAt, supplierId, lines, files, @@ -628,6 +629,9 @@ export function register(app, basePath = '') { if (Object.prototype.hasOwnProperty.call(parsed.payload, 'date')) { patch.date = parsed.date; } + if (Object.prototype.hasOwnProperty.call(parsed.payload, 'createdAt')) { + patch.createdAt = parsed.createdAt; + } if (Object.prototype.hasOwnProperty.call(parsed.payload, 'supplierId')) { patch.supplierId = parsed.supplierId; } diff --git a/src/api/routes/order.js b/src/api/routes/order.js index 241a9d0..2facfef 100644 --- a/src/api/routes/order.js +++ b/src/api/routes/order.js @@ -175,12 +175,13 @@ export function register(app, basePath = '') { return res.status(400).json({ error: selected.error }); } - const { number, date, entityId, lines } = req.body ?? {}; + const { number, date, createdAt, entityId, lines } = req.body ?? {}; try { const order = await db.create(req.user.id, { number, date, + createdAt, entityId, lines, }); @@ -396,6 +397,9 @@ export function register(app, basePath = '') { if (Object.prototype.hasOwnProperty.call(body, 'date')) { patch.date = body.date; } + if (Object.prototype.hasOwnProperty.call(body, 'createdAt')) { + patch.createdAt = body.createdAt; + } if (Object.prototype.hasOwnProperty.call(body, 'entityId')) { patch.entityId = body.entityId; } diff --git a/src/api/routes/quote.js b/src/api/routes/quote.js index b1bc861..996e3db 100644 --- a/src/api/routes/quote.js +++ b/src/api/routes/quote.js @@ -175,12 +175,13 @@ export function register(app, basePath = '') { return res.status(400).json({ error: selected.error }); } - const { number, date, entityId, lines } = req.body ?? {}; + const { number, date, createdAt, entityId, lines } = req.body ?? {}; try { const quote = await db.create(req.user.id, { number, date, + createdAt, entityId, lines, }); @@ -396,6 +397,9 @@ export function register(app, basePath = '') { if (Object.prototype.hasOwnProperty.call(body, 'date')) { patch.date = body.date; } + if (Object.prototype.hasOwnProperty.call(body, 'createdAt')) { + patch.createdAt = body.createdAt; + } if (Object.prototype.hasOwnProperty.call(body, 'entityId')) { patch.entityId = body.entityId; }