From 3f84ff2be85e0029c86f785b42053100e7e521d6 Mon Sep 17 00:00:00 2001 From: seb Date: Fri, 2 Oct 2026 00:15:22 +0200 Subject: [PATCH] u --- docs/api/openapi.json | 35 +++++++++++++++++++-- docs/api/routes.md | 16 +++++----- docs/api/routes/article/_sku/track_stock.md | 24 ++++++++++++-- docs/api/routes/article/get.md | 24 ++++++++++++-- docs/api/routes/article/set-brand.md | 24 ++++++++++++-- docs/api/routes/article/set-categories.md | 24 ++++++++++++-- docs/api/routes/article/set-eans.md | 24 ++++++++++++-- docs/api/routes/article/set-prices.md | 26 +++++++++++++-- docs/api/routes/article/set-weight.md | 24 ++++++++++++-- docs/api/routes/article/update.md | 24 ++++++++++++-- src/api/openapi.js | 22 +++++++++++-- src/api/routes/article.js | 2 +- 12 files changed, 238 insertions(+), 31 deletions(-) diff --git a/docs/api/openapi.json b/docs/api/openapi.json index 1debb0e..a179b3a 100644 --- a/docs/api/openapi.json +++ b/docs/api/openapi.json @@ -11955,7 +11955,7 @@ "Article" ], "summary": "Replace POS prices by SKU", - "description": "Replace-all prices for the company POS customer group.\nEach entry: `price` (net), `tax` (rate, ensured via tax.ensure), optional `minmultiplier` (default 1).\nEmpty `prices` clears all POS prices. Returns the same shape as GET by SKU.\n", + "description": "Replace-all prices for the company POS customer group.\nEach entry: `price` (net), `tax` (rate, ensured via tax.ensure), optional `minmultiplier` (default 1).\nEmpty `prices` clears all POS prices. Returns the same shape as GET by SKU (article `prices` array contains `{ minmultiplier, net, taxRate, customergroupId }`).\n", "security": [ { "cookieAuth": [] @@ -14543,18 +14543,47 @@ }, "prices": { "type": "array", + "description": "Selling prices by customer group and quantity tier (field is net, not price)", "items": { - "type": "object" + "$ref": "#/components/schemas/ArticlePriceEntry" } }, "suppliers": { "type": "array", "items": { - "type": "object" + "$ref": "#/components/schemas/ArticleSupplier" } } } }, + "ArticlePriceEntry": { + "type": "object", + "description": "Article selling price tier for a customer group (note price field is net, not price)", + "required": [ + "minmultiplier", + "net", + "taxRate", + "customergroupId" + ], + "properties": { + "minmultiplier": { + "type": "number", + "description": "Quantity threshold for volume pricing (defaults to 1)" + }, + "net": { + "type": "number", + "description": "Net selling price" + }, + "taxRate": { + "type": "number", + "description": "Tax rate in percent (e.g. 19 or 8.1)" + }, + "customergroupId": { + "type": "integer", + "description": "Customer group ID (e.g. company POS customer group)" + } + } + }, "ArticleHistoryEntry": { "type": "object", "required": [ diff --git a/docs/api/routes.md b/docs/api/routes.md index 95626d5..aa89afc 100644 --- a/docs/api/routes.md +++ b/docs/api/routes.md @@ -14,25 +14,25 @@ Overview of all 141 API endpoints across all modules. |:---|:---|:---|:---|:---| | `POST` | `/article` | `body: { categoryId, name, track_stock? }` | `201 { article: { id, categoryId, name, sku, track_stock } }` | Create article in category | | `DELETE` | `/article/{sku}` | `sku:string, query: cascade?:boolean` | `204 void` | Delete article by SKU | -| `GET` | `/article/{sku}` | `sku:string` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Get article by SKU | -| `PATCH` | `/article/{sku}` | `sku:string, body: { name?, unitamount?, unitSi?, track_stock? }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Update article by SKU | +| `GET` | `/article/{sku}` | `sku:string` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Get article by SKU | +| `PATCH` | `/article/{sku}` | `sku:string, body: { name?, unitamount?, unitSi?, track_stock? }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Update article by SKU | | `GET` | `/article/{sku}/articlelist` | `sku:string` | `200 { items: { id, articleId, sku, name, count, sort }[] }` | Get Stückliste (bundle items) for an article | | `PUT` | `/article/{sku}/articlelist` | `sku:string, body: { items: { articleId?, sku?: string, count?, sort? }[] }` | `200 { items: object[] }` | Set Stückliste (bundle items) for an article | | `GET` | `/article/{sku}/attributevalue` | `sku:string` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | List article attribute values | | `PUT` | `/article/{sku}/attributevalue` | `sku:string, body: { attributevalues: any[] }` | `200 { attributevalues: { id, articleId, attributeId, attributeName, exclusive, attributeValueId, value, sort }[] }` | Bulk set article attribute values | -| `PUT` | `/article/{sku}/brand` | `sku:string, body: { brandId: integer }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Set or clear article brand by SKU | -| `PUT` | `/article/{sku}/categories` | `sku:string, body: { categoryIds: integer[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Replace article categories by SKU | +| `PUT` | `/article/{sku}/brand` | `sku:string, body: { brandId: integer }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Set or clear article brand by SKU | +| `PUT` | `/article/{sku}/categories` | `sku:string, body: { categoryIds: integer[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Replace article categories by SKU | | `GET` | `/article/{sku}/children` | `sku:string` | `200 { children: { id, sku, name, fathersort, attributeValueId, attributeValue }[] }` | List child articles of a father article | | `POST` | `/article/{sku}/children` | `sku:string, body: { attributeValueId?, value?, name?, sku?: string }` | `201 { article: { id, categoryId, name, sku, track_stock } }` | Add a child article to a father article | | `DELETE` | `/article/{sku}/children/{childSku}` | `sku:string, childSku:string, query: deleteArticle?:boolean` | `200 { success, deleted }` | Remove a child article from a father article | -| `PUT` | `/article/{sku}/eans` | `sku:string, body: { eans: string[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Replace article EANs by SKU | +| `PUT` | `/article/{sku}/eans` | `sku:string, body: { eans: string[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Replace article EANs by SKU | | `POST` | `/article/{sku}/father` | `sku:string, body: { isFather, attributeNameId? }` | `200 { article: { id, categoryId, name, sku, track_stock } }` | Toggle father mode for an article | | `GET` | `/article/{sku}/history` | `sku:string, query: limit?:integer` | `200 { history: { id, date, text }[], truncated }` | Get article history by SKU | | `DELETE` | `/article/{sku}/image/{fullHash}` | `sku:string, fullHash:string` | `204 void` | Delete article image | | `PUT` | `/article/{sku}/images/order` | `sku:string, body: { fullHashes: string[] }` | `200 { fullHashes: string[] }` | Reorder article images | | `GET` | `/article/{sku}/optionslist` | `sku:string` | `200 { options: { id, articleId, sku, name, count, sort, mandated }[] }` | Get options list for an article | | `PUT` | `/article/{sku}/optionslist` | `sku:string, body: { options: { articleId?, sku?: string, count?, sort?, mandated? }[] }` | `200 { options: object[] }` | Set options list for an article | -| `PUT` | `/article/{sku}/prices` | `sku:string, body: { prices: { price, tax, minmultiplier? }[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Replace POS prices by SKU | +| `PUT` | `/article/{sku}/prices` | `sku:string, body: { prices: { price, tax, minmultiplier? }[] }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Replace POS prices by SKU | | `POST` | `/article/{sku}/print` | `sku:string, body: { templateId, printerId, quantity }` | `200` | Print article label | | `GET` | `/article/{sku}/property` | `sku:string` | `200 { properties: { id, articleId, propertyId, name, exclusive, value, sort }[] }` | List article properties | | `PUT` | `/article/{sku}/property` | `sku:string, body: { properties: { propertyId, value, sort? }[] }` | `200 { properties: { id, articleId, propertyId, name, exclusive, value, sort }[] }` | Bulk set article properties | @@ -42,8 +42,8 @@ Overview of all 141 API endpoints across all modules. | `PUT` | `/article/{sku}/supplier/order` | `sku:string, body: { ids: integer[] }` | `200 { ids: integer[] }` | Reorder article suppliers | | `DELETE` | `/article/{sku}/supplier/{id}` | `sku:string, id:integer` | `204 void` | Remove supplier assignment from article | | `PATCH` | `/article/{sku}/supplier/{id}` | `sku:string, id:integer, body: { sku?: string, pu?, stock?, sort?, netPrice?, taxId?, taxRate? }` | `200 { supplier: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price } }` | Update article supplier assignment | -| `PUT` | `/article/{sku}/track_stock` | `sku:string, body: { track_stock: boolean }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Set article track_stock flag by SKU | -| `PUT` | `/article/{sku}/weight` | `sku:string, body: { weight: number }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: object[], suppliers: object[] } }` | Set article weight by SKU | +| `PUT` | `/article/{sku}/track_stock` | `sku:string, body: { track_stock: boolean }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Set article track_stock flag by SKU | +| `PUT` | `/article/{sku}/weight` | `sku:string, body: { weight: number }` | `200 { article: { id, sku, name, slug, track_stock, unitamount, weight, unit, brand, categoryIds: integer[], photos: object[], eans: string[], prices: { minmultiplier, net, taxRate, customergroupId }[], suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] } }` | Set article weight by SKU | | `GET` | `/articleIndex` | — | `200 { articles: { id, sku, name, eans: string[], categoryIds: integer[] }[] }` | List article search index | | `GET` | `/attributename` | — | `200 { attributenames: { id, name, sort, exclusive, values: { id, attributeId, value, sort }[] }[] }` | List attribute names | | `POST` | `/attributename` | `body: { name, sort?, exclusive? }` | `201 { attributename: { id, name, sort, exclusive, values: { id, attributeId, value, sort }[] } }` | Create attribute name | diff --git a/docs/api/routes/article/_sku/track_stock.md b/docs/api/routes/article/_sku/track_stock.md index 2190094..1642ea3 100644 --- a/docs/api/routes/article/_sku/track_stock.md +++ b/docs/api/routes/article/_sku/track_stock.md @@ -55,10 +55,30 @@ Updated article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/docs/api/routes/article/get.md b/docs/api/routes/article/get.md index 633dc0c..07a9437 100644 --- a/docs/api/routes/article/get.md +++ b/docs/api/routes/article/get.md @@ -38,10 +38,30 @@ Article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/docs/api/routes/article/set-brand.md b/docs/api/routes/article/set-brand.md index bbac5e8..3d5e6ef 100644 --- a/docs/api/routes/article/set-brand.md +++ b/docs/api/routes/article/set-brand.md @@ -56,10 +56,30 @@ Updated article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/docs/api/routes/article/set-categories.md b/docs/api/routes/article/set-categories.md index 780490d..0af867f 100644 --- a/docs/api/routes/article/set-categories.md +++ b/docs/api/routes/article/set-categories.md @@ -60,10 +60,30 @@ Updated article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/docs/api/routes/article/set-eans.md b/docs/api/routes/article/set-eans.md index e5b1ea4..ca72edb 100644 --- a/docs/api/routes/article/set-eans.md +++ b/docs/api/routes/article/set-eans.md @@ -58,10 +58,30 @@ Updated article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/docs/api/routes/article/set-prices.md b/docs/api/routes/article/set-prices.md index ac7de63..061889f 100644 --- a/docs/api/routes/article/set-prices.md +++ b/docs/api/routes/article/set-prices.md @@ -6,7 +6,7 @@ Replace POS prices by SKU Replace-all prices for the company POS customer group. Each entry: `price` (net), `tax` (rate, ensured via tax.ensure), optional `minmultiplier` (default 1). -Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. +Empty `prices` clears all POS prices. Returns the same shape as GET by SKU (article `prices` array contains `{ minmultiplier, net, taxRate, customergroupId }`). ## Path parameters @@ -70,10 +70,30 @@ Updated article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/docs/api/routes/article/set-weight.md b/docs/api/routes/article/set-weight.md index 5f7bfde..7f4540f 100644 --- a/docs/api/routes/article/set-weight.md +++ b/docs/api/routes/article/set-weight.md @@ -56,10 +56,30 @@ Updated article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/docs/api/routes/article/update.md b/docs/api/routes/article/update.md index 93c1061..512a9fa 100644 --- a/docs/api/routes/article/update.md +++ b/docs/api/routes/article/update.md @@ -60,10 +60,30 @@ Updated article detail "…" ], "prices": [ - {} + { + "minmultiplier": 1, + "net": 1, + "taxRate": 1, + "customergroupId": 1 + } ], "suppliers": [ - {} + { + "id": 1, + "articleId": 1, + "entityId": 1, + "supplierName": "…", + "sku": "…", + "pu": 1, + "stock": 1, + "sort": 1, + "priceId": 1, + "price": { + "net": 1, + "taxId": 1, + "taxRate": 1 + } + } ] } } diff --git a/src/api/openapi.js b/src/api/openapi.js index 26c6805..4642c66 100644 --- a/src/api/openapi.js +++ b/src/api/openapi.js @@ -152,12 +152,30 @@ * type: string * prices: * type: array + * description: Selling prices by customer group and quantity tier (field is net, not price) * items: - * type: object + * $ref: '#/components/schemas/ArticlePriceEntry' * suppliers: * type: array * items: - * type: object + * $ref: '#/components/schemas/ArticleSupplier' + * ArticlePriceEntry: + * type: object + * description: Article selling price tier for a customer group (note price field is net, not price) + * required: [minmultiplier, net, taxRate, customergroupId] + * properties: + * minmultiplier: + * type: number + * description: Quantity threshold for volume pricing (defaults to 1) + * net: + * type: number + * description: Net selling price + * taxRate: + * type: number + * description: Tax rate in percent (e.g. 19 or 8.1) + * customergroupId: + * type: integer + * description: Customer group ID (e.g. company POS customer group) * ArticleHistoryEntry: * type: object * required: [id, date, text] diff --git a/src/api/routes/article.js b/src/api/routes/article.js index 62e348a..13bcbbb 100644 --- a/src/api/routes/article.js +++ b/src/api/routes/article.js @@ -707,7 +707,7 @@ export function register(app, basePath = '') { * description: | * Replace-all prices for the company POS customer group. * Each entry: `price` (net), `tax` (rate, ensured via tax.ensure), optional `minmultiplier` (default 1). - * Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. + * Empty `prices` clears all POS prices. Returns the same shape as GET by SKU (article `prices` array contains `{ minmultiplier, net, taxRate, customergroupId }`). * security: * - cookieAuth: [] * x-signals: [article-]