From e358eccca9d32429de5f47490eb761f7b4e291cb Mon Sep 17 00:00:00 2001 From: seb Date: Wed, 30 Sep 2026 19:29:13 +0200 Subject: [PATCH] u --- docs/api/routes.md | 336 +++++++++--------- .../routes/article/_sku/articlelist.put.md | 4 + .../routes/article/_sku/attributevalue.put.md | 4 + docs/api/routes/article/_sku/children.post.md | 14 +- docs/api/routes/article/_sku/father.md | 13 +- .../routes/article/_sku/optionslist.put.md | 4 + docs/api/routes/article/_sku/print.md | 6 + docs/api/routes/article/_sku/property.put.md | 4 + docs/api/routes/article/_sku/track_stock.md | 31 +- docs/api/routes/article/create.md | 12 +- docs/api/routes/article/get.md | 27 +- docs/api/routes/article/order-images.md | 4 + docs/api/routes/article/set-brand.md | 31 +- docs/api/routes/article/set-categories.md | 31 +- docs/api/routes/article/set-eans.md | 31 +- docs/api/routes/article/set-prices.md | 31 +- docs/api/routes/article/set-weight.md | 31 +- docs/api/routes/article/supplier/create.md | 11 + docs/api/routes/article/supplier/order.md | 4 + docs/api/routes/article/supplier/update.md | 10 + docs/api/routes/article/update.md | 34 +- docs/api/routes/attributename.post.md | 6 + .../attributename/_attributeId/value.post.md | 5 + .../attributename/_attributeId/value/order.md | 4 + docs/api/routes/attributename/_id.patch.md | 6 + docs/api/routes/attributename/order.md | 4 + docs/api/routes/attributevalue/_id.patch.md | 5 + docs/api/routes/auth/login.md | 4 + docs/api/routes/auth/selected-company.md | 4 + docs/api/routes/brand/upsert.md | 5 + docs/api/routes/category/create.md | 5 + docs/api/routes/category/order.md | 5 + docs/api/routes/category/update.md | 5 + docs/api/routes/company/create.md | 7 + docs/api/routes/company/update.md | 7 + docs/api/routes/companysetting/set.md | 5 + docs/api/routes/customergroup/create.md | 4 + docs/api/routes/customergroup/order.md | 4 + docs/api/routes/customergroup/update.md | 4 + docs/api/routes/delivery/create.md | 8 + docs/api/routes/delivery/update.md | 8 + docs/api/routes/entity/address/create.md | 11 + docs/api/routes/entity/address/update.md | 11 + docs/api/routes/entity/contact/create.md | 5 + docs/api/routes/entity/contact/update.md | 5 + docs/api/routes/entity/create.md | 7 + docs/api/routes/entity/update.md | 6 + docs/api/routes/labeltemplate/create.md | 29 +- docs/api/routes/labeltemplate/get.md | 11 +- docs/api/routes/labeltemplate/list.md | 11 +- docs/api/routes/labeltemplate/update.md | 29 +- docs/api/routes/ledger.post.md | 7 + docs/api/routes/number/update.md | 6 + docs/api/routes/order/create.md | 7 + docs/api/routes/order/update.md | 7 + docs/api/routes/posterminal/list.md | 9 +- docs/api/routes/posterminal/upsert.md | 13 +- docs/api/routes/printer/_id.put.md | 9 + docs/api/routes/printer/create.md | 9 + docs/api/routes/printer/order.md | 4 + docs/api/routes/printer/update.md | 9 + docs/api/routes/propertyname.post.md | 6 + docs/api/routes/propertyname/_id.patch.md | 6 + docs/api/routes/propertyname/order.md | 4 + docs/api/routes/quote/create.md | 7 + docs/api/routes/quote/update.md | 7 + docs/api/routes/serper/image.md | 4 + docs/api/routes/serper/images.md | 8 + docs/api/routes/stock/adjust.md | 8 + docs/api/routes/supplier/articles.post.md | 6 +- docs/api/routes/supplier/create.md | 4 + docs/api/routes/supplier/update.md | 4 + docs/api/routes/unit/upsert.md | 8 + docs/api/routes/warehouse/_id.put.md | 5 + docs/api/routes/warehouse/create.md | 5 + docs/api/routes/warehouse/order.md | 4 + docs/api/routes/warehouse/update.md | 5 + scripts/generateApiDocs.js | 193 ++++++++-- src/api/openapi.js | 125 ++++++- src/api/routes/article.js | 18 +- 80 files changed, 1185 insertions(+), 225 deletions(-) diff --git a/docs/api/routes.md b/docs/api/routes.md index 15979b6..a3fbcb4 100644 --- a/docs/api/routes.md +++ b/docs/api/routes.md @@ -2,167 +2,175 @@ Overview of all 162 API endpoints across all modules. -| Method | Route | Params | Return Type | Summary | Docs | -|:---|:---|:---|:---|:---|:---| -| `POST` | `/article` | `body: { parentId, name, track_stock }` | `201 { article: ArticleSummary }` | Create article | [`routes/article/create.md`](routes/article/create.md) | -| `GET` | `/article/export.zip` | `?categoryId:integer` | `200 zip` | Export articles as ZIP | [`routes/article/export.zip.md`](routes/article/export.zip.md) | -| `DELETE` | `/article/{sku}` | `sku:string` | `204 void` | Delete article by SKU | [`routes/article/delete.md`](routes/article/delete.md) | -| `GET` | `/article/{sku}` | `sku:string` | `200 { article: ArticleDetail }` | Get article by SKU | [`routes/article/get.md`](routes/article/get.md) | -| `PATCH` | `/article/{sku}` | `sku:string, body: { name, unitamount, unitSi, … }` | `200 { article: ArticleDetail }` | Update article by SKU | [`routes/article/update.md`](routes/article/update.md) | -| `GET` | `/article/{sku}/articlelist` | `sku:string` | `200 { items: { id, articleId, sku, … }[] }` | Get Stückliste (bundle items) for an article | [`routes/article/_sku/articlelist.get.md`](routes/article/_sku/articlelist.get.md) | -| `PUT` | `/article/{sku}/articlelist` | `sku:string, body: { items: { articleId, sku, count, … }[] }` | `200 { items: object[] }` | Set Stückliste (bundle items) for an article | [`routes/article/_sku/articlelist.put.md`](routes/article/_sku/articlelist.put.md) | -| `GET` | `/article/{sku}/attributevalue` | `sku:string` | `200 { attributevalues: ArticleAttributeValue[] }` | List article attribute values | [`routes/article/_sku/attributevalue.get.md`](routes/article/_sku/attributevalue.get.md) | -| `PUT` | `/article/{sku}/attributevalue` | `sku:string, body: { attributevalues: any[] }` | `200 { attributevalues: ArticleAttributeValue[] }` | Bulk set article attribute values | [`routes/article/_sku/attributevalue.put.md`](routes/article/_sku/attributevalue.put.md) | -| `PUT` | `/article/{sku}/brand` | `sku:string, body: { brandId: integer }` | `200 { article: ArticleDetail }` | Set or clear article brand by SKU | [`routes/article/set-brand.md`](routes/article/set-brand.md) | -| `PUT` | `/article/{sku}/categories` | `sku:string, body: { categoryIds: integer[] }` | `200 { article: ArticleDetail }` | Replace article categories by SKU | [`routes/article/set-categories.md`](routes/article/set-categories.md) | -| `GET` | `/article/{sku}/children` | `sku:string` | `200 { children: { id, sku, name, … }[] }` | List child articles of a father article | [`routes/article/_sku/children.get.md`](routes/article/_sku/children.get.md) | -| `POST` | `/article/{sku}/children` | `sku:string, body: { attributeValueId, value, name }` | `201 { article: Article }` | Add a child article to a father article | [`routes/article/_sku/children.post.md`](routes/article/_sku/children.post.md) | -| `DELETE` | `/article/{sku}/children/{childSku}` | `sku:string, childSku:string, ?deleteArticle:boolean` | `200 { success, deleted }` | Remove a child article from a father article | [`routes/article/_sku/children/_childSku.md`](routes/article/_sku/children/_childSku.md) | -| `PUT` | `/article/{sku}/eans` | `sku:string, body: { eans: string[] }` | `200 { article: ArticleDetail }` | Replace article EANs by SKU | [`routes/article/set-eans.md`](routes/article/set-eans.md) | -| `POST` | `/article/{sku}/father` | `sku:string, body: { isFather, attributeNameId }` | `200 { article: Article }` | Toggle father mode for an article | [`routes/article/_sku/father.md`](routes/article/_sku/father.md) | -| `GET` | `/article/{sku}/history` | `sku:string` | `200 { history: ArticleHistoryEntry[] }` | Get article history by SKU | [`routes/article/history.md`](routes/article/history.md) | -| `POST` | `/article/{sku}/image` | `sku:string, image/avif` | `201 { image: ArticleImage }` | Add article image | [`routes/article/add-image.md`](routes/article/add-image.md) | -| `DELETE` | `/article/{sku}/image/{fullHash}` | `sku:string, fullHash:string` | `204 void` | Delete article image | [`routes/article/delete-image.md`](routes/article/delete-image.md) | -| `PUT` | `/article/{sku}/images/order` | `sku:string, body: { fullHashes: string[] }` | `200 { fullHashes: string[] }` | Reorder article images | [`routes/article/order-images.md`](routes/article/order-images.md) | -| `GET` | `/article/{sku}/optionslist` | `sku:string` | `200 { options: { id, articleId, sku, … }[] }` | Get options list for an article | [`routes/article/_sku/optionslist.get.md`](routes/article/_sku/optionslist.get.md) | -| `PUT` | `/article/{sku}/optionslist` | `sku:string, body: { options: { articleId, sku, count, … }[] }` | `200 { options: object[] }` | Set options list for an article | [`routes/article/_sku/optionslist.put.md`](routes/article/_sku/optionslist.put.md) | -| `PUT` | `/article/{sku}/prices` | `sku:string, body: { prices: { price, tax, minmultiplier }[] }` | `200 { article: ArticleDetail }` | Replace POS prices by SKU | [`routes/article/set-prices.md`](routes/article/set-prices.md) | -| `POST` | `/article/{sku}/print` | `sku:string, body: { templateId, printerId, quantity }` | `200` | Print article label | [`routes/article/_sku/print.md`](routes/article/_sku/print.md) | -| `GET` | `/article/{sku}/property` | `sku:string` | `200 { properties: ArticleProperty[] }` | List article properties | [`routes/article/_sku/property.get.md`](routes/article/_sku/property.get.md) | -| `PUT` | `/article/{sku}/property` | `sku:string, body: { properties: { propertyId, value, sort }[] }` | `200 { properties: ArticleProperty[] }` | Bulk set article properties | [`routes/article/_sku/property.put.md`](routes/article/_sku/property.put.md) | -| `DELETE` | `/article/{sku}/property/{propertyId}` | `sku:string, propertyId:integer` | `200` | Delete article property | [`routes/article/_sku/property/_propertyId.md`](routes/article/_sku/property/_propertyId.md) | -| `GET` | `/article/{sku}/supplier` | `sku:string` | `200 { suppliers: ArticleSupplier[] }` | List article suppliers | [`routes/article/supplier/list.md`](routes/article/supplier/list.md) | -| `POST` | `/article/{sku}/supplier` | `sku:string, body: { entityId, sku, pu, … }` | `201 { supplier: ArticleSupplier }` | Assign supplier to article | [`routes/article/supplier/create.md`](routes/article/supplier/create.md) | -| `PUT` | `/article/{sku}/supplier/order` | `sku:string, body: { ids: integer[] }` | `200 { ids: integer[] }` | Reorder article suppliers | [`routes/article/supplier/order.md`](routes/article/supplier/order.md) | -| `DELETE` | `/article/{sku}/supplier/{id}` | `sku:string, id:integer` | `204 void` | Remove supplier assignment from article | [`routes/article/supplier/delete.md`](routes/article/supplier/delete.md) | -| `PATCH` | `/article/{sku}/supplier/{id}` | `sku:string, id:integer, body: { sku, pu, stock, … }` | `200 { supplier: ArticleSupplier }` | Update article supplier assignment | [`routes/article/supplier/update.md`](routes/article/supplier/update.md) | -| `PUT` | `/article/{sku}/track_stock` | `sku:string, body: { track_stock: boolean }` | `200 { article: ArticleDetail }` | Set article track_stock flag by SKU | [`routes/article/_sku/track_stock.md`](routes/article/_sku/track_stock.md) | -| `PUT` | `/article/{sku}/weight` | `sku:string, body: { weight: number }` | `200 { article: ArticleDetail }` | Set article weight by SKU | [`routes/article/set-weight.md`](routes/article/set-weight.md) | -| `GET` | `/articleIndex` | — | `200 { articles: { id, sku, name, … }[] }` | List article search index | [`routes/articleIndex.md`](routes/articleIndex.md) | -| `GET` | `/attributename` | — | `200 { attributenames: Attributename[] }` | List attribute names | [`routes/attributename.get.md`](routes/attributename.get.md) | -| `POST` | `/attributename` | `body: { name, sort, exclusive }` | `201 { attributename: Attributename }` | Create attribute name | [`routes/attributename.post.md`](routes/attributename.post.md) | -| `PUT` | `/attributename/order` | `body: { attributenameIds: integer[] }` | `200` | Reorder attribute names | [`routes/attributename/order.md`](routes/attributename/order.md) | -| `GET` | `/attributename/{attributeId}/value` | `attributeId:integer` | `200 { values: AttributeValue[] }` | List values for an attribute | [`routes/attributename/_attributeId/value.get.md`](routes/attributename/_attributeId/value.get.md) | -| `POST` | `/attributename/{attributeId}/value` | `attributeId:integer, body: { value, sort }` | `201 { attributevalue: AttributeValue }` | Create value for an attribute | [`routes/attributename/_attributeId/value.post.md`](routes/attributename/_attributeId/value.post.md) | -| `PUT` | `/attributename/{attributeId}/value/order` | `attributeId:integer, body: { attributevalueIds: integer[] }` | `200` | Reorder values of an attribute | [`routes/attributename/_attributeId/value/order.md`](routes/attributename/_attributeId/value/order.md) | -| `DELETE` | `/attributename/{id}` | `id:integer` | `200` | Delete attribute name | [`routes/attributename/_id.delete.md`](routes/attributename/_id.delete.md) | -| `PATCH` | `/attributename/{id}` | `id:integer, body: { name, sort, exclusive }` | `200 { attributename: Attributename }` | Update attribute name | [`routes/attributename/_id.patch.md`](routes/attributename/_id.patch.md) | -| `DELETE` | `/attributevalue/{id}` | `id:integer` | `200` | Delete attribute value | [`routes/attributevalue/_id.delete.md`](routes/attributevalue/_id.delete.md) | -| `PATCH` | `/attributevalue/{id}` | `id:integer, body: { value, sort }` | `200 { attributevalue: AttributeValue }` | Update attribute value | [`routes/attributevalue/_id.patch.md`](routes/attributevalue/_id.patch.md) | -| `POST` | `/auth/login` | `body: { idToken: string }` | `200 { message, user }` | Login with Google ID token | [`routes/auth/login.md`](routes/auth/login.md) | -| `POST` | `/auth/logout` | — | `200 { message: string }` | Logout | [`routes/auth/logout.md`](routes/auth/logout.md) | -| `GET` | `/auth/me` | — | `200 { user, googleClientId }` | Current user | [`routes/auth/me.md`](routes/auth/me.md) | -| `PUT` | `/auth/selected-company` | `body: { companyId: integer }` | `200 { user: User }` | Set selected company | [`routes/auth/selected-company.md`](routes/auth/selected-company.md) | -| `GET` | `/brand` | — | `200 { brands: Brand[] }` | List brands | [`routes/brand/list.md`](routes/brand/list.md) | -| `PUT` | `/brand` | `body: { name, gsprpinfo }` | `200 { brand: Brand }` | Upsert brand | [`routes/brand/upsert.md`](routes/brand/upsert.md) | -| `DELETE` | `/brand/{id}` | `id:integer` | `204 void` | Delete brand | [`routes/brand/delete.md`](routes/brand/delete.md) | -| `POST` | `/category` | `body: { parentId, name }` | `201 { category: Category }` | Create category | [`routes/category/create.md`](routes/category/create.md) | -| `GET` | `/category/export.zip` | — | `200 zip` | Export categories as ZIP | [`routes/category/export.zip.md`](routes/category/export.zip.md) | -| `PUT` | `/category/order` | `body: { parentId, categoryIds }` | `200 { categoryIds: integer[] }` | Reorder sibling categories | [`routes/category/order.md`](routes/category/order.md) | -| `GET` | `/category/tree` | — | `200 { languageId, tree, articles }` | Category tree | [`routes/category/tree.md`](routes/category/tree.md) | -| `DELETE` | `/category/{id}` | `id:integer` | `204 void` | Delete category | [`routes/category/delete.md`](routes/category/delete.md) | -| `PATCH` | `/category/{id}` | `id:integer, body: { name, parentId }` | `200 { category: { id, name, slug, … } }` | Update category (rename or move) | [`routes/category/update.md`](routes/category/update.md) | -| `GET` | `/category/{id}/articles` | `id:integer` | `200 { articles: { id, sku, name, … }[] }` | Articles in category | [`routes/category/articles.md`](routes/category/articles.md) | -| `DELETE` | `/category/{id}/image` | `id:integer` | `200 { category: { id, thumbW, thumbH, … } }` | Delete category image | [`routes/category/delete-image.md`](routes/category/delete-image.md) | -| `POST` | `/category/{id}/image` | `id:integer, image/avif` | `201 { category: { id, thumbW, thumbH, … } }` | Add category image | [`routes/category/add-image.md`](routes/category/add-image.md) | -| `GET` | `/company` | — | `200 { companies: Company[] }` | List companies | [`routes/company/list.md`](routes/company/list.md) | -| `POST` | `/company` | `body: { iso639, name, taxrate, … }` | `201 { company, user }` | Create company | [`routes/company/create.md`](routes/company/create.md) | -| `GET` | `/company/setting` | — | `200 { settings: CompanySetting[] }` | List company settings | [`routes/companysetting/list.md`](routes/companysetting/list.md) | -| `GET` | `/company/setting/{key}` | `key:string` | `200 { setting: CompanySetting }` | Get company setting by key | [`routes/companysetting/get.md`](routes/companysetting/get.md) | -| `PUT` | `/company/setting/{key}` | `key:string, body: { value, frontend }` | `200 { setting: CompanySetting }` | Upsert company setting | [`routes/companysetting/set.md`](routes/companysetting/set.md) | -| `DELETE` | `/company/{id}` | `id:integer` | `204 void` | Delete company | [`routes/company/delete.md`](routes/company/delete.md) | -| `PUT` | `/company/{id}` | `id:integer, body: { iso639, name, taxrate, … }` | `200 { company: Company }` | Update company | [`routes/company/update.md`](routes/company/update.md) | -| `GET` | `/customergroup` | — | `200 { customergroups: CustomergroupListItem[] }` | List customer groups | [`routes/customergroup/list.md`](routes/customergroup/list.md) | -| `POST` | `/customergroup` | `body: { name: string }` | `201 { customergroup: Customergroup }` | Create customer group | [`routes/customergroup/create.md`](routes/customergroup/create.md) | -| `PUT` | `/customergroup/order` | `body: { customergroupIds: integer[] }` | `200 { customergroupIds: integer[] }` | Reorder customer groups | [`routes/customergroup/order.md`](routes/customergroup/order.md) | -| `DELETE` | `/customergroup/{id}` | `id:integer` | `204 void` | Delete customer group | [`routes/customergroup/delete.md`](routes/customergroup/delete.md) | -| `PATCH` | `/customergroup/{id}` | `id:integer, body: { name: string }` | `200 { customergroup: Customergroup }` | Update customer group | [`routes/customergroup/update.md`](routes/customergroup/update.md) | -| `GET` | `/delivery` | — | `200 { deliveries: DeliverySummary[] }` | List deliveries | [`routes/delivery/list.md`](routes/delivery/list.md) | -| `POST` | `/delivery` | `body: { externalNumber, date, supplierId, … }` | `201 { delivery: Delivery }` | Create delivery | [`routes/delivery/create.md`](routes/delivery/create.md) | -| `POST` | `/delivery/parse` | `form: { file }` | `200 SSE` | Parse document (SSE) | [`routes/delivery/parse.md`](routes/delivery/parse.md) | -| `DELETE` | `/delivery/{id}` | `id:integer` | `204 void` | Delete delivery | [`routes/delivery/delete.md`](routes/delivery/delete.md) | -| `GET` | `/delivery/{id}` | `id:integer` | `200 { delivery: Delivery }` | Get delivery | [`routes/delivery/get.md`](routes/delivery/get.md) | -| `PATCH` | `/delivery/{id}` | `id:integer, body: { externalNumber, date, supplierId, … }` | `200 { delivery: Delivery }` | Update delivery | [`routes/delivery/update.md`](routes/delivery/update.md) | -| `GET` | `/delivery/{id}/file/{fileId}` | `id:integer, fileId:integer, ?download:boolean` | `200 string` | Get delivery attachment file content | [`routes/delivery/file.md`](routes/delivery/file.md) | -| `GET` | `/entity` | `?customergroupId:integer, ?role:string` | `200 { entities: Entity[] }` | List entities | [`routes/entity/list.md`](routes/entity/list.md) | -| `POST` | `/entity` | `body: { customergroupId, name, firstName, … }` | `201 { entity: Entity }` | Create entity | [`routes/entity/create.md`](routes/entity/create.md) | -| `GET` | `/entity/next-number` | — | `200 { number: string }` | Peek next customer number | [`routes/entity/next-number.md`](routes/entity/next-number.md) | -| `GET` | `/entity/{entityId}/address` | `entityId:integer` | `200 { addresses: EntityAddress[] }` | List entity addresses | [`routes/entity/address/list.md`](routes/entity/address/list.md) | -| `POST` | `/entity/{entityId}/address` | `entityId:integer, body: { addressType, firstName, name, … }` | `201 { address: EntityAddress }` | Create entity address | [`routes/entity/address/create.md`](routes/entity/address/create.md) | -| `DELETE` | `/entity/{entityId}/address/{id}` | `entityId:integer, id:integer` | `204 void` | Delete entity address | [`routes/entity/address/delete.md`](routes/entity/address/delete.md) | -| `PATCH` | `/entity/{entityId}/address/{id}` | `entityId:integer, id:integer, body: { addressType, firstName, name, … }` | `200 { address: EntityAddress }` | Update entity address | [`routes/entity/address/update.md`](routes/entity/address/update.md) | -| `GET` | `/entity/{entityId}/contact` | `entityId:integer` | `200 { contacts: EntityContact[] }` | List entity contacts | [`routes/entity/contact/list.md`](routes/entity/contact/list.md) | -| `POST` | `/entity/{entityId}/contact` | `entityId:integer, body: { contactType, contact }` | `201 { contact: EntityContact }` | Create entity contact | [`routes/entity/contact/create.md`](routes/entity/contact/create.md) | -| `DELETE` | `/entity/{entityId}/contact/{id}` | `entityId:integer, id:integer` | `204 void` | Delete entity contact | [`routes/entity/contact/delete.md`](routes/entity/contact/delete.md) | -| `PATCH` | `/entity/{entityId}/contact/{id}` | `entityId:integer, id:integer, body: { contactType, contact }` | `200 { contact: EntityContact }` | Update entity contact | [`routes/entity/contact/update.md`](routes/entity/contact/update.md) | -| `DELETE` | `/entity/{id}` | `id:integer` | `204 void` | Delete entity | [`routes/entity/delete.md`](routes/entity/delete.md) | -| `GET` | `/entity/{id}` | `id:integer` | `200 { entity: Entity }` | Get entity | [`routes/entity/get.md`](routes/entity/get.md) | -| `PATCH` | `/entity/{id}` | `id:integer, body: { name, firstName, number }` | `200 { entity: Entity }` | Update entity | [`routes/entity/update.md`](routes/entity/update.md) | -| `GET` | `/events` | — | `200 SSE` | Subscribe to update signals (SSE) | [`routes/events.md`](routes/events.md) | -| `GET` | `/health` | — | `200 { ok: boolean }` | Database health check | [`routes/health.md`](routes/health.md) | -| `GET` | `/image/{fullHash}` | `fullHash:string` | `200 avif` | Full image by hash | [`routes/image/get.md`](routes/image/get.md) | -| `GET` | `/labeltemplate` | — | `200 { labeltemplates: LabelTemplate[] }` | List label templates | [`routes/labeltemplate/list.md`](routes/labeltemplate/list.md) | -| `POST` | `/labeltemplate` | `body: LabelTemplateCreateInput` | `201 { labeltemplate: LabelTemplate }` | Create label template | [`routes/labeltemplate/create.md`](routes/labeltemplate/create.md) | -| `DELETE` | `/labeltemplate/{id}` | `id:integer` | `204 void` | Delete label template | [`routes/labeltemplate/delete.md`](routes/labeltemplate/delete.md) | -| `GET` | `/labeltemplate/{id}` | `id:integer` | `200 { labeltemplate: LabelTemplate }` | Get label template by ID | [`routes/labeltemplate/get.md`](routes/labeltemplate/get.md) | -| `PATCH` | `/labeltemplate/{id}` | `id:integer, body: LabelTemplateUpdateInput` | `200 { labeltemplate: LabelTemplate }` | Update label template (partial) | [`routes/labeltemplate/update.md`](routes/labeltemplate/update.md) | -| `PUT` | `/labeltemplate/{id}` | `id:integer, body: LabelTemplateUpdateInput` | `200 { labeltemplate: LabelTemplate }` | Update label template | [`routes/labeltemplate/update.md`](routes/labeltemplate/update.md) | -| `GET` | `/ledger` | `?manifestarticle_id:integer, ?sku:string, ?transaction_type:string` | `200 { ledger: LedgerEntry[] }` | Query ledger by manifest article ID or SKU | [`routes/ledger.get.md`](routes/ledger.get.md) | -| `POST` | `/ledger` | `body: LedgerEntryInput` | `201 { ledger: LedgerEntry }` | Add ledger entry | [`routes/ledger.post.md`](routes/ledger.post.md) | -| `GET` | `/ledger/manifestarticle/{manifestarticleId}` | `manifestarticleId:integer, ?transaction_type:string` | `200 { ledger: LedgerEntry[] }` | Query ledger by manifest article ID | [`routes/ledger/manifestarticle/_manifestarticleId.md`](routes/ledger/manifestarticle/_manifestarticleId.md) | -| `GET` | `/ledger/sku/{sku}` | `sku:string, ?transaction_type:string` | `200 { ledger: LedgerEntry[] }` | Query ledger by article SKU | [`routes/ledger/sku/_sku.md`](routes/ledger/sku/_sku.md) | -| `GET` | `/number` | — | `200 { numbers: NumberSequence[] }` | List number sequences | [`routes/number/list.md`](routes/number/list.md) | -| `PATCH` | `/number/{id}` | `id:integer, body: { prefix, padding, next }` | `200 { number: NumberSequence }` | Update number sequence | [`routes/number/update.md`](routes/number/update.md) | -| `GET` | `/order` | — | `200 { orders: OrderSummary[] }` | List orders | [`routes/order/list.md`](routes/order/list.md) | -| `POST` | `/order` | `body: { number, date, entityId, … }` | `201 { order: Order }` | Create order | [`routes/order/create.md`](routes/order/create.md) | -| `DELETE` | `/order/{id}` | `id:integer` | `204 void` | Delete order | [`routes/order/delete.md`](routes/order/delete.md) | -| `GET` | `/order/{id}` | `id:integer` | `200 { order: Order }` | Get order | [`routes/order/get.md`](routes/order/get.md) | -| `PATCH` | `/order/{id}` | `id:integer, body: { number, date, entityId, … }` | `200 { order: Order }` | Update order | [`routes/order/update.md`](routes/order/update.md) | -| `GET` | `/posterminal` | — | `200 { posterminals: Posterminal[] }` | List POS terminals | [`routes/posterminal/list.md`](routes/posterminal/list.md) | -| `PUT` | `/posterminal` | `body: { name: string }` | `200 { posterminal: Posterminal }` | Create POS terminal | [`routes/posterminal/upsert.md`](routes/posterminal/upsert.md) | -| `DELETE` | `/posterminal/{id}` | `id:integer` | `204 void` | Delete POS terminal | [`routes/posterminal/delete.md`](routes/posterminal/delete.md) | -| `DELETE` | `/posterminal/{id}/all` | `id:integer` | `200 { id, categoryIds }` | Disable all categories on terminal | [`routes/posterminal/setOffAll.md`](routes/posterminal/setOffAll.md) | -| `PUT` | `/posterminal/{id}/all` | `id:integer` | `200 { id, categoryIds }` | Enable all categories on terminal | [`routes/posterminal/setOnAll.md`](routes/posterminal/setOnAll.md) | -| `DELETE` | `/posterminal/{id}/category/{categoryId}` | `id:integer, categoryId:integer` | `200 { id, categoryIds }` | Disable category on terminal | [`routes/posterminal/setOff.md`](routes/posterminal/setOff.md) | -| `PUT` | `/posterminal/{id}/category/{categoryId}` | `id:integer, categoryId:integer` | `200 { id, categoryIds }` | Enable category on terminal | [`routes/posterminal/setOn.md`](routes/posterminal/setOn.md) | -| `GET` | `/printer` | — | `200 { printers: Printer[] }` | List printers | [`routes/printer/list.md`](routes/printer/list.md) | -| `POST` | `/printer` | `body: PrinterCreateInput` | `201 { printer: Printer }` | Create printer | [`routes/printer/create.md`](routes/printer/create.md) | -| `PUT` | `/printer/order` | `body: { printerIds: integer[] }` | `200 { printerIds: integer[] }` | Reorder printers | [`routes/printer/order.md`](routes/printer/order.md) | -| `DELETE` | `/printer/{id}` | `id:integer` | `204 void` | Delete printer | [`routes/printer/delete.md`](routes/printer/delete.md) | -| `GET` | `/printer/{id}` | `id:integer` | `200 { printer: Printer }` | Get printer by ID | [`routes/printer/get.md`](routes/printer/get.md) | -| `PATCH` | `/printer/{id}` | `id:integer, body: PrinterUpdateInput` | `200 { printer: Printer }` | Update printer (partial) | [`routes/printer/update.md`](routes/printer/update.md) | -| `PUT` | `/printer/{id}` | `id:integer, body: PrinterUpdateInput` | `200 { printer: Printer }` | Update printer | [`routes/printer/_id.put.md`](routes/printer/_id.put.md) | -| `GET` | `/propertyname` | — | `200 { propertynames: Propertyname[] }` | List property names | [`routes/propertyname.get.md`](routes/propertyname.get.md) | -| `POST` | `/propertyname` | `body: { name, sort, exclusive }` | `201 { propertyname: Propertyname }` | Create property name | [`routes/propertyname.post.md`](routes/propertyname.post.md) | -| `PUT` | `/propertyname/order` | `body: { propertynameIds: integer[] }` | `200` | Reorder property names | [`routes/propertyname/order.md`](routes/propertyname/order.md) | -| `DELETE` | `/propertyname/{id}` | `id:integer` | `200` | Delete property name | [`routes/propertyname/_id.delete.md`](routes/propertyname/_id.delete.md) | -| `PATCH` | `/propertyname/{id}` | `id:integer, body: { name, sort, exclusive }` | `200 { propertyname: Propertyname }` | Update property name | [`routes/propertyname/_id.patch.md`](routes/propertyname/_id.patch.md) | -| `GET` | `/quote` | — | `200 { quotes: QuoteSummary[] }` | List quotes | [`routes/quote/list.md`](routes/quote/list.md) | -| `POST` | `/quote` | `body: { number, date, entityId, … }` | `201 { quote: Quote }` | Create quote | [`routes/quote/create.md`](routes/quote/create.md) | -| `DELETE` | `/quote/{id}` | `id:integer` | `204 void` | Delete quote | [`routes/quote/delete.md`](routes/quote/delete.md) | -| `GET` | `/quote/{id}` | `id:integer` | `200 { quote: Quote }` | Get quote | [`routes/quote/get.md`](routes/quote/get.md) | -| `PATCH` | `/quote/{id}` | `id:integer, body: { number, date, entityId, … }` | `200 { quote: Quote }` | Update quote | [`routes/quote/update.md`](routes/quote/update.md) | -| `POST` | `/serper/image` | `body: { url: string }` | `200 avif` | Crawl image from URL, crop white/transparency borders, and return AVIF | [`routes/serper/image.md`](routes/serper/image.md) | -| `POST` | `/serper/images` | `body: { q, gl, hl, … }` | `200 SSE` | Search images via Serper.dev and stream WebP thumbnails (SSE) | [`routes/serper/images.md`](routes/serper/images.md) | -| `POST` | `/stock/adjust` | `body: StockAdjustmentInput` | `201 StockAdjustmentResult` | Adjust stock manually | [`routes/stock/adjust.md`](routes/stock/adjust.md) | -| `GET` | `/supplier` | — | `200 { suppliers: Supplier[] }` | List suppliers | [`routes/supplier/list.md`](routes/supplier/list.md) | -| `POST` | `/supplier` | `body: { name: string }` | `201 { supplier: Supplier }` | Create supplier | [`routes/supplier/create.md`](routes/supplier/create.md) | -| `DELETE` | `/supplier/{id}` | `id:integer` | `204 void` | Delete supplier | [`routes/supplier/delete.md`](routes/supplier/delete.md) | -| `PATCH` | `/supplier/{id}` | `id:integer, body: { name: string }` | `200 { supplier: Supplier }` | Update supplier | [`routes/supplier/update.md`](routes/supplier/update.md) | -| `GET` | `/supplier/{id}/articles` | `id:integer, ?skus` | `200 { articles: SupplierArticle[] }` | List supplier articles by SKUs | [`routes/supplier/articles.get.md`](routes/supplier/articles.get.md) | -| `POST` | `/supplier/{id}/articles` | `id:integer, body: { skus: string[] }` | `200 { articles: SupplierArticle[] }` | Query supplier articles by SKUs (bulk) | [`routes/supplier/articles.post.md`](routes/supplier/articles.post.md) | -| `GET` | `/thumb/{fullHash}` | `fullHash:string` | `200 avif` | Thumb image by hash | [`routes/thumb/get.md`](routes/thumb/get.md) | -| `DELETE` | `/unit` | `?si:string` | `204 void` | Delete unit | [`routes/unit/delete.md`](routes/unit/delete.md) | -| `GET` | `/unit` | — | `200 { units: Unit[] }` | List units | [`routes/unit/list.md`](routes/unit/list.md) | -| `PUT` | `/unit` | `body: { si, basesi, basesifactor, … }` | `200 { unit: Unit }` | Upsert unit | [`routes/unit/upsert.md`](routes/unit/upsert.md) | -| `GET` | `/v1/category` | `?lastChangedCategory:string, ?limit:integer` | `200 object[]` | JTL-POS category synchronization | [`routes/v1/category.md`](routes/v1/category.md) | -| `GET` | `/v1/client` | `?authCode:string, ?name:string` | `200 { authCode, authToken, certificateFingerprint, … }` | POS discovery and pairing | [`routes/pos/client.md`](routes/pos/client.md) | -| `GET` | `/v1/init` | `?lastChangedProduct:integer, ?lastChangedCategory:integer, ?lastChangedCustomer:integer, ?lastChangedCustomerGroup:integer, ?lastChangedCompositeProduct:integer, ?lastChangedDeletedEntity:integer` | `200 { version, product_count, category_count, … }` | POS sync initialization counts | [`routes/pos/init.md`](routes/pos/init.md) | -| `GET` | `/v1/product` | `?lastChangedProduct:string, ?limit:integer` | `200 object[]` | JTL-POS product catalog synchronization | [`routes/v1/product.md`](routes/v1/product.md) | -| `GET` | `/warehouse` | — | `200 { warehouses: Warehouse[] }` | List warehouses | [`routes/warehouse/list.md`](routes/warehouse/list.md) | -| `POST` | `/warehouse` | `body: WarehouseCreateInput` | `201 { warehouse: Warehouse }` | Create warehouse | [`routes/warehouse/create.md`](routes/warehouse/create.md) | -| `PUT` | `/warehouse/order` | `body: { warehouseIds: integer[] }` | `200 { warehouseIds: integer[] }` | Reorder warehouses | [`routes/warehouse/order.md`](routes/warehouse/order.md) | -| `DELETE` | `/warehouse/{id}` | `id:integer` | `204 void` | Delete warehouse | [`routes/warehouse/delete.md`](routes/warehouse/delete.md) | -| `GET` | `/warehouse/{id}` | `id:integer` | `200 { warehouse: Warehouse }` | Get warehouse by ID | [`routes/warehouse/get.md`](routes/warehouse/get.md) | -| `PATCH` | `/warehouse/{id}` | `id:integer, body: WarehouseUpdateInput` | `200 { warehouse: Warehouse }` | Update warehouse (partial) | [`routes/warehouse/update.md`](routes/warehouse/update.md) | -| `PUT` | `/warehouse/{id}` | `id:integer, body: WarehouseUpdateInput` | `200 { warehouse: Warehouse }` | Update warehouse | [`routes/warehouse/_id.put.md`](routes/warehouse/_id.put.md) | +### Parameter Notation + +- **Required parameters** have no `?` (e.g. `sku:string`, `categoryId`, `query: si:string`, `body: { name }`). +- **Optional parameters** are marked with `?` (e.g. `query: categoryId?:integer`, `track_stock?`, `body?: ...`). +- **Path parameters** match `{param}` in the route URL. +- **Query parameters** are prefixed with `query: ...`. +- **Request bodies** are prefixed with `body: ...` (or `body?: ...` if optional). + +| Method | Route | Params | Return Type | Summary | +|:---|:---|:---|:---|:---| +| `POST` | `/article` | `body: { categoryId, name, track_stock? }` | `201 { article: { id, categoryId, name, sku, track_stock } }` | Create article in category | +| `GET` | `/article/export.zip` | `query: categoryId?:integer` | `200 zip` | Export articles as ZIP | +| `DELETE` | `/article/{sku}` | `sku:string` | `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}/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?, 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 | +| `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? }` | `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 | +| `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` | `200 { history: { id, date, text }[] }` | Get article history by SKU | +| `POST` | `/article/{sku}/image` | `sku:string, image/avif` | `201 { image: { picId, sort, full, thumb, hash, thumbHash } }` | Add article image | +| `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?, 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 | +| `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 | +| `DELETE` | `/article/{sku}/property/{propertyId}` | `sku:string, propertyId:integer` | `200` | Delete article property | +| `GET` | `/article/{sku}/supplier` | `sku:string` | `200 { suppliers: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price }[] }` | List article suppliers | +| `POST` | `/article/{sku}/supplier` | `sku:string, body: { entityId, sku?, pu?, stock?, sort?, netPrice?, taxId?, taxRate? }` | `201 { supplier: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price } }` | Assign supplier to article | +| `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?, 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 | +| `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 | +| `PUT` | `/attributename/order` | `body: { attributenameIds: integer[] }` | `200` | Reorder attribute names | +| `GET` | `/attributename/{attributeId}/value` | `attributeId:integer` | `200 { values: { id, attributeId, value, sort }[] }` | List values for an attribute | +| `POST` | `/attributename/{attributeId}/value` | `attributeId:integer, body: { value, sort? }` | `201 { attributevalue: { id, attributeId, value, sort } }` | Create value for an attribute | +| `PUT` | `/attributename/{attributeId}/value/order` | `attributeId:integer, body: { attributevalueIds: integer[] }` | `200` | Reorder values of an attribute | +| `DELETE` | `/attributename/{id}` | `id:integer` | `200` | Delete attribute name | +| `PATCH` | `/attributename/{id}` | `id:integer, body: { name?, sort?, exclusive? }` | `200 { attributename: { id, name, sort, exclusive, values: { id, attributeId, value, sort }[] } }` | Update attribute name | +| `DELETE` | `/attributevalue/{id}` | `id:integer` | `200` | Delete attribute value | +| `PATCH` | `/attributevalue/{id}` | `id:integer, body: { value?, sort? }` | `200 { attributevalue: { id, attributeId, value, sort } }` | Update attribute value | +| `POST` | `/auth/login` | `body: { idToken: string }` | `200 { message, user }` | Login with Google ID token | +| `POST` | `/auth/logout` | — | `200 { message: string }` | Logout | +| `GET` | `/auth/me` | — | `200 { user, googleClientId }` | Current user | +| `PUT` | `/auth/selected-company` | `body: { companyId: integer }` | `200 { user: { id, email, selectedCompanyId, picture } }` | Set selected company | +| `GET` | `/brand` | — | `200 { brands: { id, name, gsprpinfo, pics: { id, sort, fullHash, thumbW, thumbH, thumbHash }[] }[] }` | List brands | +| `PUT` | `/brand` | `body: { name, gsprpinfo? }` | `200 { brand: { id, name, gsprpinfo, pics: { id, sort, fullHash, thumbW, thumbH, thumbHash }[] } }` | Upsert brand | +| `DELETE` | `/brand/{id}` | `id:integer` | `204 void` | Delete brand | +| `POST` | `/category` | `body: { parentId, name }` | `201 { category: { id, parentId, sort, name } }` | Create category | +| `GET` | `/category/export.zip` | — | `200 zip` | Export categories as ZIP | +| `PUT` | `/category/order` | `body: { parentId, categoryIds: integer[] }` | `200 { categoryIds: integer[] }` | Reorder sibling categories | +| `GET` | `/category/tree` | — | `200 { languageId, tree: object[], articles: object[] }` | Category tree | +| `DELETE` | `/category/{id}` | `id:integer` | `204 void` | Delete category | +| `PATCH` | `/category/{id}` | `id:integer, body: { name?, parentId? }` | `200 { category: { id, name, slug, parentId, sort } }` | Update category (rename or move) | +| `GET` | `/category/{id}/articles` | `id:integer` | `200 { articles: { id, sku, name, slug, net, taxRate, track_stock, optionsCount, articleListCount, childCount, fullHash, thumbW, thumbH, thumbHash, brand }[] }` | Articles in category | +| `DELETE` | `/category/{id}/image` | `id:integer` | `200 { category: { id, thumbW, thumbH, thumbHash } }` | Delete category image | +| `POST` | `/category/{id}/image` | `id:integer, image/avif` | `201 { category: { id, thumbW, thumbH, thumbHash } }` | Add category image | +| `GET` | `/company` | — | `200 { companies: { id, iso639, name, taxrate, reducedtaxrate }[] }` | List companies | +| `POST` | `/company` | `body: { iso639, name, taxrate, reducedtaxrate }` | `201 { company, user }` | Create company | +| `GET` | `/company/setting` | — | `200 { settings: { key, value }[] }` | List company settings | +| `GET` | `/company/setting/{key}` | `key:string` | `200 { setting: { key, value } }` | Get company setting by key | +| `PUT` | `/company/setting/{key}` | `key:string, body: { value, frontend? }` | `200 { setting: { key, value } }` | Upsert company setting | +| `DELETE` | `/company/{id}` | `id:integer` | `204 void` | Delete company | +| `PUT` | `/company/{id}` | `id:integer, body: { iso639, name, taxrate, reducedtaxrate }` | `200 { company: { id, iso639, name, taxrate, reducedtaxrate } }` | Update company | +| `GET` | `/customergroup` | — | `200 { customergroups: { id, name, sort, editable }[] }` | List customer groups | +| `POST` | `/customergroup` | `body: { name: string }` | `201 { customergroup: { id, name, sort } }` | Create customer group | +| `PUT` | `/customergroup/order` | `body: { customergroupIds: integer[] }` | `200 { customergroupIds: integer[] }` | Reorder customer groups | +| `DELETE` | `/customergroup/{id}` | `id:integer` | `204 void` | Delete customer group | +| `PATCH` | `/customergroup/{id}` | `id:integer, body: { name: string }` | `200 { customergroup: { id, name, sort } }` | Update customer group | +| `GET` | `/delivery` | — | `200 { deliveries: { id, number, type, supplierId, externalNumber, date, createdAt, ledger }[] }` | List deliveries | +| `POST` | `/delivery` | `body: { externalNumber, date, lines: { amount, net, taxrate, sku?, name?, pu?, rebate?, comment? }[], supplierId?, files?: { mimetype, name, content, sort?, size?, hash?, created?, modified?, meta? }[] }` | `201 { delivery: { id, number, type, supplierId, externalNumber, date, createdAt, ledger, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[], files: { id, manifestId, fileId, sort, hash, mimetype, name, size, created, modified, thumbavif, thumbx, thumby, meta }[] } }` | Create delivery | +| `POST` | `/delivery/parse` | `form: { file: string }` | `200 SSE` | Parse document (SSE) | +| `DELETE` | `/delivery/{id}` | `id:integer` | `204 void` | Delete delivery | +| `GET` | `/delivery/{id}` | `id:integer` | `200 { delivery: { id, number, type, supplierId, externalNumber, date, createdAt, ledger, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[], files: { id, manifestId, fileId, sort, hash, mimetype, name, size, created, modified, thumbavif, thumbx, thumby, meta }[] } }` | Get delivery | +| `PATCH` | `/delivery/{id}` | `id:integer, body: { externalNumber?, date?, supplierId?, lines?: { amount, net, taxrate, sku?, name?, pu?, rebate?, comment? }[], files?: { mimetype, name, content, sort?, size?, hash?, created?, modified?, meta? }[] }` | `200 { delivery: { id, number, type, supplierId, externalNumber, date, createdAt, ledger, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[], files: { id, manifestId, fileId, sort, hash, mimetype, name, size, created, modified, thumbavif, thumbx, thumby, meta }[] } }` | Update delivery | +| `GET` | `/delivery/{id}/file/{fileId}` | `id:integer, fileId:integer, query: download?:boolean` | `200 string` | Get delivery attachment file content | +| `GET` | `/entity` | `query: customergroupId?:integer, role?:string` | `200 { entities: { id, customergroupId, firstName, name, number, createdAt, updatedAt }[] }` | List entities | +| `POST` | `/entity` | `body: { customergroupId, name, firstName?, number? }` | `201 { entity: { id, customergroupId, firstName, name, number, createdAt, updatedAt } }` | Create entity | +| `GET` | `/entity/next-number` | — | `200 { number: string }` | Peek next customer number | +| `GET` | `/entity/{entityId}/address` | `entityId:integer` | `200 { addresses: { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType }[] }` | List entity addresses | +| `POST` | `/entity/{entityId}/address` | `entityId:integer, body: { addressType, firstName?, name?, street?, streetNumber?, zip?, city?, county? }` | `201 { address: { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType } }` | Create entity address | +| `DELETE` | `/entity/{entityId}/address/{id}` | `entityId:integer, id:integer` | `204 void` | Delete entity address | +| `PATCH` | `/entity/{entityId}/address/{id}` | `entityId:integer, id:integer, body: { addressType?, firstName?, name?, street?, streetNumber?, zip?, city?, county? }` | `200 { address: { id, entityId, firstName, name, street, streetNumber, zip, city, county, addressType } }` | Update entity address | +| `GET` | `/entity/{entityId}/contact` | `entityId:integer` | `200 { contacts: { id, entityId, contactType, contact }[] }` | List entity contacts | +| `POST` | `/entity/{entityId}/contact` | `entityId:integer, body: { contactType, contact }` | `201 { contact: { id, entityId, contactType, contact } }` | Create entity contact | +| `DELETE` | `/entity/{entityId}/contact/{id}` | `entityId:integer, id:integer` | `204 void` | Delete entity contact | +| `PATCH` | `/entity/{entityId}/contact/{id}` | `entityId:integer, id:integer, body: { contactType?, contact? }` | `200 { contact: { id, entityId, contactType, contact } }` | Update entity contact | +| `DELETE` | `/entity/{id}` | `id:integer` | `204 void` | Delete entity | +| `GET` | `/entity/{id}` | `id:integer` | `200 { entity: { id, customergroupId, firstName, name, number, createdAt, updatedAt } }` | Get entity | +| `PATCH` | `/entity/{id}` | `id:integer, body: { name?, firstName?, number? }` | `200 { entity: { id, customergroupId, firstName, name, number, createdAt, updatedAt } }` | Update entity | +| `GET` | `/events` | — | `200 SSE` | Subscribe to update signals (SSE) | +| `GET` | `/health` | — | `200 { ok: boolean }` | Database health check | +| `GET` | `/image/{fullHash}` | `fullHash:string` | `200 avif` | Full image by hash | +| `GET` | `/labeltemplate` | — | `200 { labeltemplates: { id, name, width, height, dpi, type, content, thumb }[] }` | List label templates | +| `POST` | `/labeltemplate` | `body: { name, width, height, dpi, type, content }` | `201 { labeltemplate: { id, name, width, height, dpi, type, content, thumb } }` | Create label template | +| `DELETE` | `/labeltemplate/{id}` | `id:integer` | `204 void` | Delete label template | +| `GET` | `/labeltemplate/{id}` | `id:integer` | `200 { labeltemplate: { id, name, width, height, dpi, type, content, thumb } }` | Get label template by ID | +| `PATCH` | `/labeltemplate/{id}` | `id:integer, body: { name?, width?, height?, dpi?, type?, content? }` | `200 { labeltemplate: { id, name, width, height, dpi, type, content, thumb } }` | Update label template (partial) | +| `PUT` | `/labeltemplate/{id}` | `id:integer, body: { name?, width?, height?, dpi?, type?, content? }` | `200 { labeltemplate: { id, name, width, height, dpi, type, content, thumb } }` | Update label template | +| `GET` | `/ledger` | `query: manifestarticle_id?:integer, sku?:string, transaction_type?:string` | `200 { ledger: { id, manifestarticle_id, transaction_type, amount, transaction_text, hash_version, prev_hash, sku, created_at }[] }` | Query ledger by manifest article ID or SKU | +| `POST` | `/ledger` | `body: { manifestarticle_id, transaction_type, amount, transaction_text }` | `201 { ledger: { id, manifestarticle_id, transaction_type, amount, transaction_text, hash_version, prev_hash, sku, created_at } }` | Add ledger entry | +| `GET` | `/ledger/manifestarticle/{manifestarticleId}` | `manifestarticleId:integer, query: transaction_type?:string` | `200 { ledger: { id, manifestarticle_id, transaction_type, amount, transaction_text, hash_version, prev_hash, sku, created_at }[] }` | Query ledger by manifest article ID | +| `GET` | `/ledger/sku/{sku}` | `sku:string, query: transaction_type?:string` | `200 { ledger: { id, manifestarticle_id, transaction_type, amount, transaction_text, hash_version, prev_hash, sku, created_at }[] }` | Query ledger by article SKU | +| `GET` | `/number` | — | `200 { numbers: { id, type, prefix, padding, next }[] }` | List number sequences | +| `PATCH` | `/number/{id}` | `id:integer, body: { prefix?, padding?, next? }` | `200 { number: { id, type, prefix, padding, next } }` | Update number sequence | +| `GET` | `/order` | — | `200 { orders: { id, number, type, entityId, date, createdAt }[] }` | List orders | +| `POST` | `/order` | `body: { number, date, lines: { amount, net, taxrate, sku?, name?, pu?, rebate?, comment? }[], entityId? }` | `201 { order: { id, number, type, entityId, date, createdAt, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[] } }` | Create order | +| `DELETE` | `/order/{id}` | `id:integer` | `204 void` | Delete order | +| `GET` | `/order/{id}` | `id:integer` | `200 { order: { id, number, type, entityId, date, createdAt, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[] } }` | Get order | +| `PATCH` | `/order/{id}` | `id:integer, body: { number?, date?, entityId?, lines?: { amount, net, taxrate, sku?, name?, pu?, rebate?, comment? }[] }` | `200 { order: { id, number, type, entityId, date, createdAt, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[] } }` | Update order | +| `GET` | `/posterminal` | — | `200 { posterminals: { id, name, pairingpin, categoryIds: integer[] }[] }` | List POS terminals | +| `PUT` | `/posterminal` | `body: { name: string }` | `200 { posterminal: { id, name, pairingpin, categoryIds: integer[] } }` | Create POS terminal | +| `DELETE` | `/posterminal/{id}` | `id:integer` | `204 void` | Delete POS terminal | +| `DELETE` | `/posterminal/{id}/all` | `id:integer` | `200 { id, categoryIds: integer[] }` | Disable all categories on terminal | +| `PUT` | `/posterminal/{id}/all` | `id:integer` | `200 { id, categoryIds: integer[] }` | Enable all categories on terminal | +| `DELETE` | `/posterminal/{id}/category/{categoryId}` | `id:integer, categoryId:integer` | `200 { id, categoryIds: integer[] }` | Disable category on terminal | +| `PUT` | `/posterminal/{id}/category/{categoryId}` | `id:integer, categoryId:integer` | `200 { id, categoryIds: integer[] }` | Enable category on terminal | +| `GET` | `/printer` | — | `200 { printers: { id, company_id, address, type, model, serial, sort, desc }[] }` | List printers | +| `POST` | `/printer` | `body: { address, type, model?, serial?, sort?, desc? }` | `201 { printer: { id, company_id, address, type, model, serial, sort, desc } }` | Create printer | +| `PUT` | `/printer/order` | `body: { printerIds: integer[] }` | `200 { printerIds: integer[] }` | Reorder printers | +| `DELETE` | `/printer/{id}` | `id:integer` | `204 void` | Delete printer | +| `GET` | `/printer/{id}` | `id:integer` | `200 { printer: { id, company_id, address, type, model, serial, sort, desc } }` | Get printer by ID | +| `PATCH` | `/printer/{id}` | `id:integer, body: { address?, type?, model?, serial?, sort?, desc? }` | `200 { printer: { id, company_id, address, type, model, serial, sort, desc } }` | Update printer (partial) | +| `PUT` | `/printer/{id}` | `id:integer, body: { address?, type?, model?, serial?, sort?, desc? }` | `200 { printer: { id, company_id, address, type, model, serial, sort, desc } }` | Update printer | +| `GET` | `/propertyname` | — | `200 { propertynames: { id, name, sort, exclusive }[] }` | List property names | +| `POST` | `/propertyname` | `body: { name, sort?, exclusive? }` | `201 { propertyname: { id, name, sort, exclusive } }` | Create property name | +| `PUT` | `/propertyname/order` | `body: { propertynameIds: integer[] }` | `200` | Reorder property names | +| `DELETE` | `/propertyname/{id}` | `id:integer` | `200` | Delete property name | +| `PATCH` | `/propertyname/{id}` | `id:integer, body: { name?, sort?, exclusive? }` | `200 { propertyname: { id, name, sort, exclusive } }` | Update property name | +| `GET` | `/quote` | — | `200 { quotes: { id, number, type, entityId, date, createdAt }[] }` | List quotes | +| `POST` | `/quote` | `body: { number, date, lines: { amount, net, taxrate, sku?, name?, pu?, rebate?, comment? }[], entityId? }` | `201 { quote: { id, number, type, entityId, date, createdAt, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[] } }` | Create quote | +| `DELETE` | `/quote/{id}` | `id:integer` | `204 void` | Delete quote | +| `GET` | `/quote/{id}` | `id:integer` | `200 { quote: { id, number, type, entityId, date, createdAt, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[] } }` | Get quote | +| `PATCH` | `/quote/{id}` | `id:integer, body: { number?, date?, entityId?, lines?: { amount, net, taxrate, sku?, name?, pu?, rebate?, comment? }[] }` | `200 { quote: { id, number, type, entityId, date, createdAt, lines: { id, sku, name, pu, amount, net, rebate, taxrate, comment }[] } }` | Update quote | +| `POST` | `/serper/image` | `body: { url: string }` | `200 avif` | Crawl image from URL, crop white/transparency borders, and return AVIF | +| `POST` | `/serper/images` | `body: { q, gl?, hl?, num?, preferSource? }` | `200 SSE` | Search images via Serper.dev and stream WebP thumbnails (SSE) | +| `POST` | `/stock/adjust` | `body: { sku?, warehouseId?, amount?, text?, lines?: { sku, amount, warehouseId?, text? }[] }` | `201 { manifest, lines: object[], ledger: { id, manifestarticle_id, transaction_type, amount, transaction_text, hash_version, prev_hash, sku, created_at }[] }` | Adjust stock manually | +| `GET` | `/supplier` | — | `200 { suppliers: { id, name, createdAt, updatedAt }[] }` | List suppliers | +| `POST` | `/supplier` | `body: { name: string }` | `201 { supplier: { id, name, createdAt, updatedAt } }` | Create supplier | +| `DELETE` | `/supplier/{id}` | `id:integer` | `204 void` | Delete supplier | +| `PATCH` | `/supplier/{id}` | `id:integer, body: { name: string }` | `200 { supplier: { id, name, createdAt, updatedAt } }` | Update supplier | +| `GET` | `/supplier/{id}/articles` | `id:integer, query: skus?` | `200 { articles: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price, article }[] }` | List supplier articles by SKUs | +| `POST` | `/supplier/{id}/articles` | `id:integer, body?: { skus?: string[] }` | `200 { articles: { id, articleId, entityId, supplierName, sku, pu, stock, sort, priceId, price, article }[] }` | Query supplier articles by SKUs (bulk) | +| `GET` | `/thumb/{fullHash}` | `fullHash:string` | `200 avif` | Thumb image by hash | +| `DELETE` | `/unit` | `query: si:string` | `204 void` | Delete unit | +| `GET` | `/unit` | — | `200 { units: { si, basesi, basesifactor, symbol, name }[] }` | List units | +| `PUT` | `/unit` | `body: { si, basesi, basesifactor, symbol, name }` | `200 { unit: { si, basesi, basesifactor, symbol, name } }` | Upsert unit | +| `GET` | `/v1/category` | `query: lastChangedCategory?:string, limit?:integer` | `200 object[]` | JTL-POS category synchronization | +| `GET` | `/v1/client` | `query: authCode:string, name?:string` | `200 { authCode, authToken, certificateFingerprint, certificateSerialNumber, mandantId, mandantName, mandantDatabase, serverFingerprint, name, serverTimestamp }` | POS discovery and pairing | +| `GET` | `/v1/init` | `query: lastChangedProduct?:integer, lastChangedCategory?:integer, lastChangedCustomer?:integer, lastChangedCustomerGroup?:integer, lastChangedCompositeProduct?:integer, lastChangedDeletedEntity?:integer` | `200 { version, product_count, category_count, customer_count, customerGroup_count, compositeProduct_count, configurationGroup_count, configurationItem_count, deletedEntity_count, max_orderId_count }` | POS sync initialization counts | +| `GET` | `/v1/product` | `query: lastChangedProduct?:string, limit?:integer` | `200 object[]` | JTL-POS product catalog synchronization | +| `GET` | `/warehouse` | — | `200 { warehouses: { id, name, sort }[] }` | List warehouses | +| `POST` | `/warehouse` | `body: { name, sort? }` | `201 { warehouse: { id, name, sort } }` | Create warehouse | +| `PUT` | `/warehouse/order` | `body: { warehouseIds: integer[] }` | `200 { warehouseIds: integer[] }` | Reorder warehouses | +| `DELETE` | `/warehouse/{id}` | `id:integer` | `204 void` | Delete warehouse | +| `GET` | `/warehouse/{id}` | `id:integer` | `200 { warehouse: { id, name, sort } }` | Get warehouse by ID | +| `PATCH` | `/warehouse/{id}` | `id:integer, body: { name?, sort? }` | `200 { warehouse: { id, name, sort } }` | Update warehouse (partial) | +| `PUT` | `/warehouse/{id}` | `id:integer, body: { name?, sort? }` | `200 { warehouse: { id, name, sort } }` | Update warehouse | diff --git a/docs/api/routes/article/_sku/articlelist.put.md b/docs/api/routes/article/_sku/articlelist.put.md index da64330..b4290df 100644 --- a/docs/api/routes/article/_sku/articlelist.put.md +++ b/docs/api/routes/article/_sku/articlelist.put.md @@ -14,6 +14,10 @@ Configures the article as a Stückliste covering a list of count * article. A fa ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `items` | yes | object[] | | + ```json { "items": [ diff --git a/docs/api/routes/article/_sku/attributevalue.put.md b/docs/api/routes/article/_sku/attributevalue.put.md index 0ebdf54..1dde00d 100644 --- a/docs/api/routes/article/_sku/attributevalue.put.md +++ b/docs/api/routes/article/_sku/attributevalue.put.md @@ -14,6 +14,10 @@ Replaces all attribute values for an article. Enforces exclusive constraint (max ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `attributevalues` | yes | any[] | | + ```json { "attributevalues": [ diff --git a/docs/api/routes/article/_sku/children.post.md b/docs/api/routes/article/_sku/children.post.md index bb36431..e884bb9 100644 --- a/docs/api/routes/article/_sku/children.post.md +++ b/docs/api/routes/article/_sku/children.post.md @@ -14,6 +14,12 @@ Automatically generates SKU as fatherSku-attributeValueId and assigns the varian ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `attributeValueId` | no | integer | ID of existing attribute value for the father's attribute | +| `value` | no | string | String value to find or create under the father's attribute | +| `name` | no | string | Optional custom variant name | + ```json { "attributeValueId": 1, @@ -28,7 +34,13 @@ Created child article ```json { - "article": {} + "article": { + "id": 1, + "categoryId": 1, + "name": "…", + "sku": "…", + "track_stock": true + } } ``` diff --git a/docs/api/routes/article/_sku/father.md b/docs/api/routes/article/_sku/father.md index 2ade8b4..27820f5 100644 --- a/docs/api/routes/article/_sku/father.md +++ b/docs/api/routes/article/_sku/father.md @@ -14,6 +14,11 @@ Enables or disables father article status. Enabling requires specifying an attri ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `isFather` | yes | boolean | True to make article a father, false to revert to regular article | +| `attributeNameId` | no | integer | Required when isFather is true. The attributename id for variants. | + ```json { "isFather": true, @@ -27,7 +32,13 @@ Updated article ```json { - "article": {} + "article": { + "id": 1, + "categoryId": 1, + "name": "…", + "sku": "…", + "track_stock": true + } } ``` diff --git a/docs/api/routes/article/_sku/optionslist.put.md b/docs/api/routes/article/_sku/optionslist.put.md index a341510..26900ee 100644 --- a/docs/api/routes/article/_sku/optionslist.put.md +++ b/docs/api/routes/article/_sku/optionslist.put.md @@ -14,6 +14,10 @@ Configures options for an article. Some options can be mandated (come with the a ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `options` | yes | object[] | | + ```json { "options": [ diff --git a/docs/api/routes/article/_sku/print.md b/docs/api/routes/article/_sku/print.md index dbcd39b..7a92a42 100644 --- a/docs/api/routes/article/_sku/print.md +++ b/docs/api/routes/article/_sku/print.md @@ -14,6 +14,12 @@ Prints a label for the article using a label template. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `templateId` | yes | integer | | +| `printerId` | yes | integer | | +| `quantity` | yes | integer | | + ```json { "templateId": 1, diff --git a/docs/api/routes/article/_sku/property.put.md b/docs/api/routes/article/_sku/property.put.md index cb60b9f..9dd2d5f 100644 --- a/docs/api/routes/article/_sku/property.put.md +++ b/docs/api/routes/article/_sku/property.put.md @@ -14,6 +14,10 @@ Replaces all properties for an article. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `properties` | yes | object[] | | + ```json { "properties": [ diff --git a/docs/api/routes/article/_sku/track_stock.md b/docs/api/routes/article/_sku/track_stock.md index 1c3e9b6..2190094 100644 --- a/docs/api/routes/article/_sku/track_stock.md +++ b/docs/api/routes/article/_sku/track_stock.md @@ -15,6 +15,10 @@ Enables or disables inventory stock tracking for this article. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `track_stock` | yes | boolean | Whether to track inventory stock for this article | + ```json { "track_stock": true @@ -31,7 +35,32 @@ Updated article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/article/create.md b/docs/api/routes/article/create.md index f4f352b..79e5b7c 100644 --- a/docs/api/routes/article/create.md +++ b/docs/api/routes/article/create.md @@ -2,15 +2,21 @@ Protected. Requires a valid session cookie (`credentials: 'include'`). -Create article +Create article in category Creates an article under an authorized category (`parentId`). ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `categoryId` | yes | integer | Category ID under which to create the article | +| `name` | yes | string | | +| `track_stock` | no | boolean | Whether to track inventory stock (defaults to true) | + ```json { - "parentId": 10, + "categoryId": 10, "name": "New article" } ``` @@ -27,7 +33,7 @@ Created { "article": { "id": 1, - "parentId": 1, + "categoryId": 1, "name": "…", "sku": "…", "track_stock": true diff --git a/docs/api/routes/article/get.md b/docs/api/routes/article/get.md index fdb72c1..633dc0c 100644 --- a/docs/api/routes/article/get.md +++ b/docs/api/routes/article/get.md @@ -18,7 +18,32 @@ Article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/article/order-images.md b/docs/api/routes/article/order-images.md index f28382b..8f1f905 100644 --- a/docs/api/routes/article/order-images.md +++ b/docs/api/routes/article/order-images.md @@ -16,6 +16,10 @@ Sets `articlepic.sort` to `0..n-1` in the given order. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `fullHashes` | yes | string[] | | + ```json { "fullHashes": [ diff --git a/docs/api/routes/article/set-brand.md b/docs/api/routes/article/set-brand.md index 27d062c..bbac5e8 100644 --- a/docs/api/routes/article/set-brand.md +++ b/docs/api/routes/article/set-brand.md @@ -16,6 +16,10 @@ Pass `null` to clear the brand. Returns the same shape as GET by SKU. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `brandId` | yes | integer | | + ```json { "brandId": 3 @@ -32,7 +36,32 @@ Updated article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/article/set-categories.md b/docs/api/routes/article/set-categories.md index d8b7a10..780490d 100644 --- a/docs/api/routes/article/set-categories.md +++ b/docs/api/routes/article/set-categories.md @@ -17,6 +17,10 @@ article from all categories. Returns the same shape as GET by SKU. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `categoryIds` | yes | integer[] | | + ```json { "categoryIds": [ @@ -36,7 +40,32 @@ Updated article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/article/set-eans.md b/docs/api/routes/article/set-eans.md index 4a8b2d7..a7411fa 100644 --- a/docs/api/routes/article/set-eans.md +++ b/docs/api/routes/article/set-eans.md @@ -16,6 +16,10 @@ empty array clears all EANs. Returns the same shape as GET by SKU. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `eans` | yes | string[] | | + ```json { "eans": [ @@ -34,7 +38,32 @@ Updated article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/article/set-prices.md b/docs/api/routes/article/set-prices.md index a131eb3..7a9ab22 100644 --- a/docs/api/routes/article/set-prices.md +++ b/docs/api/routes/article/set-prices.md @@ -17,6 +17,10 @@ Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `prices` | yes | object[] | | + ```json { "prices": [ @@ -43,7 +47,32 @@ Updated article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/article/set-weight.md b/docs/api/routes/article/set-weight.md index 6b9dd60..5f7bfde 100644 --- a/docs/api/routes/article/set-weight.md +++ b/docs/api/routes/article/set-weight.md @@ -16,6 +16,10 @@ Returns the same shape as GET by SKU. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `weight` | yes | number | | + ```json { "weight": 1.5 @@ -32,7 +36,32 @@ Updated article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/article/supplier/create.md b/docs/api/routes/article/supplier/create.md index 7ff57c8..bbba19f 100644 --- a/docs/api/routes/article/supplier/create.md +++ b/docs/api/routes/article/supplier/create.md @@ -15,6 +15,17 @@ Assigns a supplier to an article with optional purchase price, stock, and suppli ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `entityId` | yes | integer | Supplier entity ID (in Lieferanten group) | +| `sku` | no | string | Supplier's item number | +| `pu` | no | number | Packaging unit (VPE) | +| `stock` | no | number | Supplier stock | +| `sort` | no | integer | Display sort order (defaults to next sort) | +| `netPrice` | no | number | Net purchase price | +| `taxId` | no | integer | Tax ID for purchase price | +| `taxRate` | no | number | Tax rate in percent | + ```json { "entityId": 88, diff --git a/docs/api/routes/article/supplier/order.md b/docs/api/routes/article/supplier/order.md index 17d6b17..235e2bc 100644 --- a/docs/api/routes/article/supplier/order.md +++ b/docs/api/routes/article/supplier/order.md @@ -16,6 +16,10 @@ Expects an array of all assigned `articlesupplier` IDs in the desired order. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `ids` | yes | integer[] | Full list of articlesupplier ids in their new order | + ```json { "ids": [ diff --git a/docs/api/routes/article/supplier/update.md b/docs/api/routes/article/supplier/update.md index 277abe9..fafcfa5 100644 --- a/docs/api/routes/article/supplier/update.md +++ b/docs/api/routes/article/supplier/update.md @@ -16,6 +16,16 @@ Updates an existing article-supplier association (supplier SKU, packaging unit, ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `sku` | no | string | | +| `pu` | no | number | | +| `stock` | no | number | | +| `sort` | no | integer | Display sort order | +| `netPrice` | no | number | | +| `taxId` | no | integer | | +| `taxRate` | no | number | | + ```json { "sku": "MET-44910-B", diff --git a/docs/api/routes/article/update.md b/docs/api/routes/article/update.md index 1869eba..93c1061 100644 --- a/docs/api/routes/article/update.md +++ b/docs/api/routes/article/update.md @@ -16,6 +16,13 @@ Patches article fields. At least one of `name`, `unitamount`, `unitSi` required. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `unitamount` | no | number | Must be finite and `> 0` | +| `unitSi` | no | string | Existing company unit `si` | +| `track_stock` | no | boolean | Whether to track inventory stock | + ```json { "unitamount": 0.24, @@ -33,7 +40,32 @@ Updated article detail ```json { - "article": {} + "article": { + "id": 1, + "sku": "…", + "name": "…", + "slug": "…", + "track_stock": true, + "unitamount": 1, + "weight": 1, + "unit": {}, + "brand": {}, + "categoryIds": [ + 1 + ], + "photos": [ + {} + ], + "eans": [ + "…" + ], + "prices": [ + {} + ], + "suppliers": [ + {} + ] + } } ``` diff --git a/docs/api/routes/attributename.post.md b/docs/api/routes/attributename.post.md index 6e1a210..e1a26c9 100644 --- a/docs/api/routes/attributename.post.md +++ b/docs/api/routes/attributename.post.md @@ -8,6 +8,12 @@ Creates an attribute name for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | +| `sort` | no | integer | | +| `exclusive` | no | boolean | | + ```json { "name": "…", diff --git a/docs/api/routes/attributename/_attributeId/value.post.md b/docs/api/routes/attributename/_attributeId/value.post.md index d169876..0068ced 100644 --- a/docs/api/routes/attributename/_attributeId/value.post.md +++ b/docs/api/routes/attributename/_attributeId/value.post.md @@ -14,6 +14,11 @@ Adds a new predefined value to an attribute. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `value` | yes | string | | +| `sort` | no | integer | | + ```json { "value": "…", diff --git a/docs/api/routes/attributename/_attributeId/value/order.md b/docs/api/routes/attributename/_attributeId/value/order.md index 73e1e25..4785298 100644 --- a/docs/api/routes/attributename/_attributeId/value/order.md +++ b/docs/api/routes/attributename/_attributeId/value/order.md @@ -14,6 +14,10 @@ Updates the display sort order of values belonging to an attribute. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `attributevalueIds` | yes | integer[] | | + ```json { "attributevalueIds": [ diff --git a/docs/api/routes/attributename/_id.patch.md b/docs/api/routes/attributename/_id.patch.md index 5fe3ec6..6ea57a3 100644 --- a/docs/api/routes/attributename/_id.patch.md +++ b/docs/api/routes/attributename/_id.patch.md @@ -14,6 +14,12 @@ Updates name, sort, and/or exclusive flag for an attribute name. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `sort` | no | integer | | +| `exclusive` | no | boolean | | + ```json { "name": "…", diff --git a/docs/api/routes/attributename/order.md b/docs/api/routes/attributename/order.md index 8084ca6..001daf4 100644 --- a/docs/api/routes/attributename/order.md +++ b/docs/api/routes/attributename/order.md @@ -8,6 +8,10 @@ Updates the display sort order of attribute names for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `attributenameIds` | yes | integer[] | | + ```json { "attributenameIds": [ diff --git a/docs/api/routes/attributevalue/_id.patch.md b/docs/api/routes/attributevalue/_id.patch.md index 2423e39..2112013 100644 --- a/docs/api/routes/attributevalue/_id.patch.md +++ b/docs/api/routes/attributevalue/_id.patch.md @@ -14,6 +14,11 @@ Updates value text and/or sort for an attribute value. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `value` | no | string | | +| `sort` | no | integer | | + ```json { "value": "…", diff --git a/docs/api/routes/auth/login.md b/docs/api/routes/auth/login.md index 07af993..9017043 100644 --- a/docs/api/routes/auth/login.md +++ b/docs/api/routes/auth/login.md @@ -11,6 +11,10 @@ stores Google `sub` and profile image bytes, and sets an HTTP-only `token` cooki ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `idToken` | yes | string | Google ID token from Google Identity Services | + ```json { "idToken": "" diff --git a/docs/api/routes/auth/selected-company.md b/docs/api/routes/auth/selected-company.md index ebb9c0a..31a6899 100644 --- a/docs/api/routes/auth/selected-company.md +++ b/docs/api/routes/auth/selected-company.md @@ -8,6 +8,10 @@ Sets the authenticated user's selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `companyId` | yes | integer | | + ```json { "companyId": 1 diff --git a/docs/api/routes/brand/upsert.md b/docs/api/routes/brand/upsert.md index 810020c..933803a 100644 --- a/docs/api/routes/brand/upsert.md +++ b/docs/api/routes/brand/upsert.md @@ -9,6 +9,11 @@ Insert or update brand by `name` for the selected company. `gsprpinfo` optional. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | +| `gsprpinfo` | no | string | | + ```json { "name": "Acme", diff --git a/docs/api/routes/category/create.md b/docs/api/routes/category/create.md index a93f4a2..0accb3a 100644 --- a/docs/api/routes/category/create.md +++ b/docs/api/routes/category/create.md @@ -8,6 +8,11 @@ Creates a category under an authorized parent. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `parentId` | yes | integer | | +| `name` | yes | string | | + ```json { "parentId": 1, diff --git a/docs/api/routes/category/order.md b/docs/api/routes/category/order.md index a9fb2b0..aeb9f16 100644 --- a/docs/api/routes/category/order.md +++ b/docs/api/routes/category/order.md @@ -12,6 +12,11 @@ to its index `0..n-1` in one transaction. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `parentId` | yes | integer | Parent category under which sibling order applies | +| `categoryIds` | yes | integer[] | | + ```json { "parentId": 1, diff --git a/docs/api/routes/category/update.md b/docs/api/routes/category/update.md index 3fdb18b..875a90b 100644 --- a/docs/api/routes/category/update.md +++ b/docs/api/routes/category/update.md @@ -18,6 +18,11 @@ Either `name` or `parentId` (or both) is required. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `parentId` | no | integer | New parent category id | + ```json { "parentId": 3 diff --git a/docs/api/routes/company/create.md b/docs/api/routes/company/create.md index 0b1d9df..e237ee0 100644 --- a/docs/api/routes/company/create.md +++ b/docs/api/routes/company/create.md @@ -8,6 +8,13 @@ Creates a company and selects it for the current user. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `iso639` | yes | string | | +| `name` | yes | string | | +| `taxrate` | yes | number | | +| `reducedtaxrate` | yes | number | | + ```json { "iso639": "de", diff --git a/docs/api/routes/company/update.md b/docs/api/routes/company/update.md index ca11a5a..491eee4 100644 --- a/docs/api/routes/company/update.md +++ b/docs/api/routes/company/update.md @@ -14,6 +14,13 @@ Updates a company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `iso639` | yes | string | | +| `name` | yes | string | | +| `taxrate` | yes | number | | +| `reducedtaxrate` | yes | number | | + ```json { "iso639": "…", diff --git a/docs/api/routes/companysetting/set.md b/docs/api/routes/companysetting/set.md index 3c74c49..8706456 100644 --- a/docs/api/routes/companysetting/set.md +++ b/docs/api/routes/companysetting/set.md @@ -17,6 +17,11 @@ Only a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`, ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `value` | yes | string | | +| `frontend` | no | boolean | | + ```json { "value": "dark", diff --git a/docs/api/routes/customergroup/create.md b/docs/api/routes/customergroup/create.md index a305282..6459017 100644 --- a/docs/api/routes/customergroup/create.md +++ b/docs/api/routes/customergroup/create.md @@ -11,6 +11,10 @@ are not allowed (`Name not allowed`). ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | + ```json { "name": "Kunden" diff --git a/docs/api/routes/customergroup/order.md b/docs/api/routes/customergroup/order.md index f876e8e..ce8e62e 100644 --- a/docs/api/routes/customergroup/order.md +++ b/docs/api/routes/customergroup/order.md @@ -11,6 +11,10 @@ else `Invalid customer group order`. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `customergroupIds` | yes | integer[] | | + ```json { "customergroupIds": [ diff --git a/docs/api/routes/customergroup/update.md b/docs/api/routes/customergroup/update.md index 9961f34..a65ff37 100644 --- a/docs/api/routes/customergroup/update.md +++ b/docs/api/routes/customergroup/update.md @@ -16,6 +16,10 @@ renamed (`Name cannot be changed`). Reorder via `PUT /customergroup/order`. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | + ```json { "name": "Stammkunden" diff --git a/docs/api/routes/delivery/create.md b/docs/api/routes/delivery/create.md index 6b71e7b..7864c02 100644 --- a/docs/api/routes/delivery/create.md +++ b/docs/api/routes/delivery/create.md @@ -13,6 +13,14 @@ The manifest `number` is allocated automatically via ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `externalNumber` | yes | string | User-supplied reference number | +| `date` | yes | string | | +| `supplierId` | no | integer | | +| `lines` | yes | DeliveryLineInput[] | | +| `files` | no | DeliveryFileInput[] | Attached documents (pdf or txt) stored with the delivery and returned in `files` on every delivery response. Thumbnails (AVIF) are generated server-side. | + ```json { "externalNumber": "DHL-123", diff --git a/docs/api/routes/delivery/update.md b/docs/api/routes/delivery/update.md index d746fd7..97e0557 100644 --- a/docs/api/routes/delivery/update.md +++ b/docs/api/routes/delivery/update.md @@ -18,6 +18,14 @@ replaces all lines; if omitted, leaves lines unchanged. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `externalNumber` | no | string | User-supplied reference number (empty/null clears it) | +| `date` | no | string | | +| `supplierId` | no | integer | | +| `lines` | no | DeliveryLineInput[] | | +| `files` | no | DeliveryFileInput[] | Appends attachments (pdf or txt) to the delivery. Omit to leave existing files unchanged. Thumbnails (AVIF) are generated server-side. | + ```json { "externalNumber": "DHL-124", diff --git a/docs/api/routes/entity/address/create.md b/docs/api/routes/entity/address/create.md index bf900ef..e0fb586 100644 --- a/docs/api/routes/entity/address/create.md +++ b/docs/api/routes/entity/address/create.md @@ -14,6 +14,17 @@ Creates an address for the entity. `addressType` is required. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `addressType` | yes | string | | +| `firstName` | no | string | | +| `name` | no | string | | +| `street` | no | string | | +| `streetNumber` | no | string | | +| `zip` | no | string | | +| `city` | no | string | | +| `county` | no | string | | + ```json { "addressType": "billing", diff --git a/docs/api/routes/entity/address/update.md b/docs/api/routes/entity/address/update.md index b36834d..f69ff8d 100644 --- a/docs/api/routes/entity/address/update.md +++ b/docs/api/routes/entity/address/update.md @@ -15,6 +15,17 @@ Patches address fields that are present. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `addressType` | no | string | | +| `firstName` | no | string | | +| `name` | no | string | | +| `street` | no | string | | +| `streetNumber` | no | string | | +| `zip` | no | string | | +| `city` | no | string | | +| `county` | no | string | | + ```json { "city": "München", diff --git a/docs/api/routes/entity/contact/create.md b/docs/api/routes/entity/contact/create.md index 2fb3c51..ea602b8 100644 --- a/docs/api/routes/entity/contact/create.md +++ b/docs/api/routes/entity/contact/create.md @@ -14,6 +14,11 @@ Creates a contact for the entity. `contactType` and `contact` are required. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `contactType` | yes | string | e.g. email, phone | +| `contact` | yes | string | | + ```json { "contactType": "email", diff --git a/docs/api/routes/entity/contact/update.md b/docs/api/routes/entity/contact/update.md index c60f259..acf894d 100644 --- a/docs/api/routes/entity/contact/update.md +++ b/docs/api/routes/entity/contact/update.md @@ -15,6 +15,11 @@ Patches `contactType` and/or `contact`. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `contactType` | no | string | | +| `contact` | no | string | | + ```json { "contact": "anna.mueller@example.com" diff --git a/docs/api/routes/entity/create.md b/docs/api/routes/entity/create.md index 66a6055..bc2ca9f 100644 --- a/docs/api/routes/entity/create.md +++ b/docs/api/routes/entity/create.md @@ -10,6 +10,13 @@ Optional `number`: omit to allocate the next customer number (`type` entity). ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `customergroupId` | yes | integer | | +| `name` | yes | string | | +| `firstName` | no | string | | +| `number` | no | string | Customer number; omit to auto-allocate | + ```json { "customergroupId": 1, diff --git a/docs/api/routes/entity/update.md b/docs/api/routes/entity/update.md index 5da2f7b..bf78387 100644 --- a/docs/api/routes/entity/update.md +++ b/docs/api/routes/entity/update.md @@ -17,6 +17,12 @@ Writes `entityhistory` when values change. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `firstName` | no | string | | +| `number` | no | string | | + ```json { "name": "Müller", diff --git a/docs/api/routes/labeltemplate/create.md b/docs/api/routes/labeltemplate/create.md index 5776a23..3df52a5 100644 --- a/docs/api/routes/labeltemplate/create.md +++ b/docs/api/routes/labeltemplate/create.md @@ -11,8 +11,24 @@ and stored with the template. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | +| `width` | yes | number | | +| `height` | yes | number | | +| `dpi` | yes | integer | | +| `type` | yes | string | | +| `content` | yes | object | | + ```json -{} +{ + "name": "…", + "width": 1, + "height": 1, + "dpi": 1, + "type": "…", + "content": {} +} ``` ## Response `201` @@ -21,7 +37,16 @@ Created label template ```json { - "labeltemplate": {} + "labeltemplate": { + "id": 1, + "name": "…", + "width": 1, + "height": 1, + "dpi": 1, + "type": "…", + "content": {}, + "thumb": "…" + } } ``` diff --git a/docs/api/routes/labeltemplate/get.md b/docs/api/routes/labeltemplate/get.md index 73f1c4a..9d692c5 100644 --- a/docs/api/routes/labeltemplate/get.md +++ b/docs/api/routes/labeltemplate/get.md @@ -18,7 +18,16 @@ Label template details ```json { - "labeltemplate": {} + "labeltemplate": { + "id": 1, + "name": "…", + "width": 1, + "height": 1, + "dpi": 1, + "type": "…", + "content": {}, + "thumb": "…" + } } ``` diff --git a/docs/api/routes/labeltemplate/list.md b/docs/api/routes/labeltemplate/list.md index 22585d7..64a98bd 100644 --- a/docs/api/routes/labeltemplate/list.md +++ b/docs/api/routes/labeltemplate/list.md @@ -13,7 +13,16 @@ List of label templates ```json { "labeltemplates": [ - {} + { + "id": 1, + "name": "…", + "width": 1, + "height": 1, + "dpi": 1, + "type": "…", + "content": {}, + "thumb": "…" + } ] } ``` diff --git a/docs/api/routes/labeltemplate/update.md b/docs/api/routes/labeltemplate/update.md index feeb222..7c09c5d 100644 --- a/docs/api/routes/labeltemplate/update.md +++ b/docs/api/routes/labeltemplate/update.md @@ -16,8 +16,24 @@ When `content` (or `width`/`height`) is provided, the thumbnail is automatically ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `width` | no | number | | +| `height` | no | number | | +| `dpi` | no | integer | | +| `type` | no | string | | +| `content` | no | object | | + ```json -{} +{ + "name": "…", + "width": 1, + "height": 1, + "dpi": 1, + "type": "…", + "content": {} +} ``` ## Response `200` @@ -26,7 +42,16 @@ Updated label template ```json { - "labeltemplate": {} + "labeltemplate": { + "id": 1, + "name": "…", + "width": 1, + "height": 1, + "dpi": 1, + "type": "…", + "content": {}, + "thumb": "…" + } } ``` diff --git a/docs/api/routes/ledger.post.md b/docs/api/routes/ledger.post.md index 97a4088..e22e16e 100644 --- a/docs/api/routes/ledger.post.md +++ b/docs/api/routes/ledger.post.md @@ -13,6 +13,13 @@ Requires authentication and membership in the company owning the referenced mani ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `manifestarticle_id` | yes | integer | References manifestarticle.id | +| `transaction_type` | yes | string | Transaction identifier / type (max 100 chars) | +| `amount` | yes | number | Movement amount | +| `transaction_text` | yes | string | Transaction memo / details | + ```json { "manifestarticle_id": 1, diff --git a/docs/api/routes/number/update.md b/docs/api/routes/number/update.md index d2d07bd..8879043 100644 --- a/docs/api/routes/number/update.md +++ b/docs/api/routes/number/update.md @@ -19,6 +19,12 @@ unchanged. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `prefix` | no | string | | +| `padding` | no | integer | | +| `next` | no | integer | | + ```json { "prefix": "A-", diff --git a/docs/api/routes/order/create.md b/docs/api/routes/order/create.md index 569bca0..c487145 100644 --- a/docs/api/routes/order/create.md +++ b/docs/api/routes/order/create.md @@ -12,6 +12,13 @@ Each line requires `sku` or `name`, `amount`, `net`, and `taxrate` (rate); `pu` ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `number` | yes | string | | +| `date` | yes | string | Document date / expected delivery | +| `entityId` | no | integer | Customer entity (manifest.entity_id) | +| `lines` | yes | ManifestLineInput[] | | + ```json { "number": "O-2026-001", diff --git a/docs/api/routes/order/update.md b/docs/api/routes/order/update.md index d2f595a..e3126c2 100644 --- a/docs/api/routes/order/update.md +++ b/docs/api/routes/order/update.md @@ -17,6 +17,13 @@ all lines; if omitted, leaves lines unchanged. Returns the full order. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `number` | no | string | | +| `date` | no | string | Document date / expected delivery | +| `entityId` | no | integer | | +| `lines` | no | ManifestLineInput[] | | + ```json { "number": "O-2026-001b", diff --git a/docs/api/routes/posterminal/list.md b/docs/api/routes/posterminal/list.md index 54ab43e..9b7ee32 100644 --- a/docs/api/routes/posterminal/list.md +++ b/docs/api/routes/posterminal/list.md @@ -13,7 +13,14 @@ Posterminal list ```json { "posterminals": [ - {} + { + "id": 1, + "name": "…", + "pairingpin": "…", + "categoryIds": [ + 1 + ] + } ] } ``` diff --git a/docs/api/routes/posterminal/upsert.md b/docs/api/routes/posterminal/upsert.md index 78632b6..021ed0d 100644 --- a/docs/api/routes/posterminal/upsert.md +++ b/docs/api/routes/posterminal/upsert.md @@ -10,6 +10,10 @@ The pairingpin is randomly generated server-side and returned in the response. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | + ```json { "name": "Kassa 1" @@ -22,7 +26,14 @@ Created posterminal with generated pairingpin ```json { - "posterminal": {} + "posterminal": { + "id": 1, + "name": "…", + "pairingpin": "…", + "categoryIds": [ + 1 + ] + } } ``` diff --git a/docs/api/routes/printer/_id.put.md b/docs/api/routes/printer/_id.put.md index ba67446..38ebb50 100644 --- a/docs/api/routes/printer/_id.put.md +++ b/docs/api/routes/printer/_id.put.md @@ -14,6 +14,15 @@ Updates specified fields of an existing printer for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `address` | no | string | | +| `type` | no | string | | +| `model` | no | string | | +| `serial` | no | string | | +| `sort` | no | integer | | +| `desc` | no | string | | + ```json { "address": "…", diff --git a/docs/api/routes/printer/create.md b/docs/api/routes/printer/create.md index 56d717b..b2d3087 100644 --- a/docs/api/routes/printer/create.md +++ b/docs/api/routes/printer/create.md @@ -10,6 +10,15 @@ Creates a new printer for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `address` | yes | string | Printer network address or hostname | +| `type` | yes | string | Printer type ("esc/pos" for lines, "zpl" for pages, or "ipp") | +| `model` | no | string | | +| `serial` | no | string | | +| `sort` | no | integer | | +| `desc` | no | string | | + ```json { "address": "…", diff --git a/docs/api/routes/printer/order.md b/docs/api/routes/printer/order.md index 47b9feb..5466aac 100644 --- a/docs/api/routes/printer/order.md +++ b/docs/api/routes/printer/order.md @@ -10,6 +10,10 @@ Reorders printers for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `printerIds` | yes | integer[] | | + ```json { "printerIds": [ diff --git a/docs/api/routes/printer/update.md b/docs/api/routes/printer/update.md index d78c6ab..b0704f4 100644 --- a/docs/api/routes/printer/update.md +++ b/docs/api/routes/printer/update.md @@ -14,6 +14,15 @@ Updates specified fields of an existing printer for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `address` | no | string | | +| `type` | no | string | | +| `model` | no | string | | +| `serial` | no | string | | +| `sort` | no | integer | | +| `desc` | no | string | | + ```json { "address": "…", diff --git a/docs/api/routes/propertyname.post.md b/docs/api/routes/propertyname.post.md index 4b4908f..accdf31 100644 --- a/docs/api/routes/propertyname.post.md +++ b/docs/api/routes/propertyname.post.md @@ -8,6 +8,12 @@ Creates a new property name for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | +| `sort` | no | integer | | +| `exclusive` | no | boolean | | + ```json { "name": "…", diff --git a/docs/api/routes/propertyname/_id.patch.md b/docs/api/routes/propertyname/_id.patch.md index 49f6a78..9876390 100644 --- a/docs/api/routes/propertyname/_id.patch.md +++ b/docs/api/routes/propertyname/_id.patch.md @@ -14,6 +14,12 @@ Updates name and/or sort for a property name. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `sort` | no | integer | | +| `exclusive` | no | boolean | | + ```json { "name": "…", diff --git a/docs/api/routes/propertyname/order.md b/docs/api/routes/propertyname/order.md index 12ef5d2..b2ecf52 100644 --- a/docs/api/routes/propertyname/order.md +++ b/docs/api/routes/propertyname/order.md @@ -8,6 +8,10 @@ Updates the display sort order of property names for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `propertynameIds` | yes | integer[] | | + ```json { "propertynameIds": [ diff --git a/docs/api/routes/quote/create.md b/docs/api/routes/quote/create.md index 92b33ed..b3ed692 100644 --- a/docs/api/routes/quote/create.md +++ b/docs/api/routes/quote/create.md @@ -12,6 +12,13 @@ Each line requires `sku` or `name`, `amount`, `net`, and `taxrate` (rate); `pu` ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `number` | yes | string | | +| `date` | yes | string | Document date / valid-until | +| `entityId` | no | integer | Customer entity (manifest.entity_id) | +| `lines` | yes | ManifestLineInput[] | | + ```json { "number": "Q-2026-001", diff --git a/docs/api/routes/quote/update.md b/docs/api/routes/quote/update.md index 87a3f16..3b88aa0 100644 --- a/docs/api/routes/quote/update.md +++ b/docs/api/routes/quote/update.md @@ -17,6 +17,13 @@ all lines; if omitted, leaves lines unchanged. Returns the full quote. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `number` | no | string | | +| `date` | no | string | Document date / valid-until | +| `entityId` | no | integer | | +| `lines` | no | ManifestLineInput[] | | + ```json { "number": "Q-2026-001b", diff --git a/docs/api/routes/serper/image.md b/docs/api/routes/serper/image.md index edaa703..d365e06 100644 --- a/docs/api/routes/serper/image.md +++ b/docs/api/routes/serper/image.md @@ -10,6 +10,10 @@ transparent border margins, resizes to max 1000px, and returns the AVIF image. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `url` | yes | string | Absolute HTTP/HTTPS URL of the image to crawl | + ```json { "url": "https://example.com/logo.png" diff --git a/docs/api/routes/serper/images.md b/docs/api/routes/serper/images.md index c80328d..a60c1c5 100644 --- a/docs/api/routes/serper/images.md +++ b/docs/api/routes/serper/images.md @@ -15,6 +15,14 @@ results via Server-Sent Events (`text/event-stream`). ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `q` | yes | string | Image search query (e.g. "Purize logo") | +| `gl` | no | string | Country code | +| `hl` | no | string | Language code | +| `num` | no | integer | Number of search results to fetch | +| `preferSource` | no | boolean | Whether to prefer downloading from the full imageUrl before falling back to Google thumbnailUrl | + ```json { "q": "Purize logo", diff --git a/docs/api/routes/stock/adjust.md b/docs/api/routes/stock/adjust.md index d085ea3..d6a0aa7 100644 --- a/docs/api/routes/stock/adjust.md +++ b/docs/api/routes/stock/adjust.md @@ -10,6 +10,14 @@ of type "adjustment", allocating a number from the "adjustment" number scheme. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `sku` | no | string | Article SKU (for single-item adjustment) | +| `warehouseId` | no | integer | Target warehouse ID | +| `amount` | no | number | Signed adjustment quantity (+ to add, - to remove) | +| `text` | no | string | Reason or memo | +| `lines` | no | object[] | Batch adjustment lines | + ```json { "sku": "…", diff --git a/docs/api/routes/supplier/articles.post.md b/docs/api/routes/supplier/articles.post.md index 26e1f82..c88bfbf 100644 --- a/docs/api/routes/supplier/articles.post.md +++ b/docs/api/routes/supplier/articles.post.md @@ -15,7 +15,11 @@ Includes linked article details (name, slug, photos/articlepic, eans/articleean) |-------|----------|------|-------------| | `id` | yes | integer | Supplier entity ID | -## Body (JSON) +## Body (JSON) — _optional_ + +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `skus` | no | string[] | List of SKUs to filter by (empty array returns empty list) | ```json { diff --git a/docs/api/routes/supplier/create.md b/docs/api/routes/supplier/create.md index 93606ac..c9cb31e 100644 --- a/docs/api/routes/supplier/create.md +++ b/docs/api/routes/supplier/create.md @@ -9,6 +9,10 @@ Creates a supplier in the company `Lieferanten` group (created on first use). ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | + ```json { "name": "Großhandel Müller" diff --git a/docs/api/routes/supplier/update.md b/docs/api/routes/supplier/update.md index b79dcfe..e75a8a0 100644 --- a/docs/api/routes/supplier/update.md +++ b/docs/api/routes/supplier/update.md @@ -14,6 +14,10 @@ Renames a supplier for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | + ```json { "name": "Großhandel Müller GmbH" diff --git a/docs/api/routes/unit/upsert.md b/docs/api/routes/unit/upsert.md index f7dc657..7d9285c 100644 --- a/docs/api/routes/unit/upsert.md +++ b/docs/api/routes/unit/upsert.md @@ -10,6 +10,14 @@ Insert or update unit by `si` for the selected company. Upserts `unittext` for t ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `si` | yes | string | | +| `basesi` | yes | string | | +| `basesifactor` | yes | number | | +| `symbol` | yes | string | | +| `name` | yes | string | | + ```json { "si": "l", diff --git a/docs/api/routes/warehouse/_id.put.md b/docs/api/routes/warehouse/_id.put.md index 3302311..e2c5a56 100644 --- a/docs/api/routes/warehouse/_id.put.md +++ b/docs/api/routes/warehouse/_id.put.md @@ -14,6 +14,11 @@ Updates specified fields of an existing warehouse for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `sort` | no | integer | Sort order | + ```json { "name": "…", diff --git a/docs/api/routes/warehouse/create.md b/docs/api/routes/warehouse/create.md index d0af428..b703ac3 100644 --- a/docs/api/routes/warehouse/create.md +++ b/docs/api/routes/warehouse/create.md @@ -10,6 +10,11 @@ Sort order will be automatically appended if omitted. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | yes | string | | +| `sort` | no | integer | Optional sort order (defaults to auto-increment) | + ```json { "name": "…", diff --git a/docs/api/routes/warehouse/order.md b/docs/api/routes/warehouse/order.md index 64b8438..207b4a9 100644 --- a/docs/api/routes/warehouse/order.md +++ b/docs/api/routes/warehouse/order.md @@ -10,6 +10,10 @@ Reorders warehouses for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `warehouseIds` | yes | integer[] | | + ```json { "warehouseIds": [ diff --git a/docs/api/routes/warehouse/update.md b/docs/api/routes/warehouse/update.md index b740896..1685b03 100644 --- a/docs/api/routes/warehouse/update.md +++ b/docs/api/routes/warehouse/update.md @@ -14,6 +14,11 @@ Updates specified fields of an existing warehouse for the selected company. ## Body (JSON) +| Field | Required | Type | Description | +|-------|----------|------|-------------| +| `name` | no | string | | +| `sort` | no | integer | Sort order | + ```json { "name": "…", diff --git a/scripts/generateApiDocs.js b/scripts/generateApiDocs.js index d293764..565b587 100644 --- a/scripts/generateApiDocs.js +++ b/scripts/generateApiDocs.js @@ -82,6 +82,29 @@ function schemaRefName(schema) { return null; } +function resolveSchema(schema, components) { + if (!schema) return null; + if (schema.$ref) { + const name = schemaRefName(schema); + const resolved = components?.schemas?.[name]; + return resolved ? resolveSchema(resolved, components) : schema; + } + if (Array.isArray(schema.allOf) && schema.allOf.length) { + const merged = { type: 'object', properties: {}, required: [] }; + for (const part of schema.allOf) { + const resolvedPart = resolveSchema(part, components); + if (resolvedPart?.properties) { + Object.assign(merged.properties, resolvedPart.properties); + } + if (Array.isArray(resolvedPart?.required)) { + merged.required.push(...resolvedPart.required); + } + } + return merged; + } + return schema; +} + /** * Build a sample JSON value from an OpenAPI schema (expands $ref / allOf / array items). * @param {object} schema @@ -152,6 +175,33 @@ function contentSchema(content) { return json?.schema ?? null; } +function renderSchemaPropertiesTable(schema, components) { + const resolved = resolveSchema(schema, components); + if (!resolved?.properties) return []; + + const lines = []; + const required = new Set(resolved.required || []); + const entries = Object.entries(resolved.properties); + if (entries.length === 0) return []; + + lines.push('| Field | Required | Type | Description |'); + lines.push('|-------|----------|------|-------------|'); + for (const [name, prop] of entries) { + const isReq = required.has(name) ? 'yes' : 'no'; + let type = prop.type || ''; + if (!type && prop.$ref) { + type = schemaRefName(prop); + } else if (type === 'array') { + const itemType = prop.items?.type || schemaRefName(prop.items) || 'any'; + type = `${itemType}[]`; + } + const desc = (prop.description || '').replace(/[\r\n|]+/g, ' ').trim(); + lines.push(`| \`${name}\` | ${isReq} | ${type} | ${desc} |`); + } + lines.push(''); + return lines; +} + function renderRequestBody(requestBody) { if (!requestBody?.content) return null; const lines = []; @@ -159,8 +209,13 @@ function renderRequestBody(requestBody) { const jsonSchema = contentSchema(requestBody.content); if (jsonSchema) { - lines.push('## Body (JSON)'); + const isOpt = requestBody.required === false; + lines.push(`## Body (JSON)${isOpt ? ' — _optional_' : ''}`); lines.push(''); + const propTable = renderSchemaPropertiesTable(jsonSchema, spec.components); + if (propTable.length) { + lines.push(...propTable); + } lines.push(formatSchema(jsonSchema, spec.components)); lines.push(''); } @@ -436,55 +491,125 @@ function renderReadme(operations) { return lines.join('\n'); } -function summarizeSchemaCompact(schema) { - if (!schema) return ''; - if (schema.$ref) return schemaRefName(schema); - if (schema.type === 'array') { - const item = summarizeSchemaCompact(schema.items) || 'any'; +function summarizeSchemaCompact(schema, components = spec.components, depth = 0) { + if (!schema || depth > 4) return ''; + const resolved = resolveSchema(schema, components); + if (!resolved) return schemaRefName(schema) || ''; + + if (resolved.type === 'array') { + const item = summarizeSchemaCompact(resolved.items, components, depth + 1) || 'any'; return `${item}[]`; } - if (schema.type === 'object' || schema.properties) { - const props = Object.keys(schema.properties || {}); - if (props.length === 0) return 'object'; + if (resolved.type === 'object' || resolved.properties) { + const props = Object.keys(resolved.properties || {}); + if (props.length === 0) return schemaRefName(schema) || 'object'; if (props.length === 1) { const k = props[0]; - const v = summarizeSchemaCompact(schema.properties[k]); + const v = summarizeSchemaCompact(resolved.properties[k], components, depth + 1); return v ? `{ ${k}: ${v} }` : `{ ${k} }`; } - if (props.length <= 3) return `{ ${props.join(', ')} }`; - return `{ ${props.slice(0, 3).join(', ')}, … }`; + const inner = props.map((k) => { + const sub = resolved.properties[k]; + if (sub && sub.type === 'array') { + const subItem = summarizeSchemaCompact(sub.items, components, depth + 1) || 'any'; + return `${k}: ${subItem}[]`; + } + return k; + }); + return `{ ${inner.join(', ')} }`; } - if (schema.type) return schema.type; - return ''; + if (resolved.type) return resolved.type; + return schemaRefName(schema) || ''; +} + +function summarizeParamSchema(schema, components) { + if (!schema) return ''; + const resolved = resolveSchema(schema, components); + if (!resolved) return schemaRefName(schema) || ''; + + if (resolved.type === 'array') { + const item = summarizeParamSchema(resolved.items, components) || 'any'; + return `${item}[]`; + } + + if (resolved.type === 'object' || resolved.properties) { + const required = new Set(resolved.required || []); + const props = Object.keys(resolved.properties || {}); + if (props.length === 0) { + return schemaRefName(schema) || 'object'; + } + // Prioritize required props before optional props + const sortedProps = [...props].sort((a, b) => { + const reqA = required.has(a) ? 0 : 1; + const reqB = required.has(b) ? 0 : 1; + return reqA - reqB; + }); + + const formattedProps = sortedProps.map((k) => { + const isOpt = !required.has(k); + const propSchema = resolved.properties[k]; + const optSuffix = isOpt ? '?' : ''; + if (propSchema?.type === 'array') { + const subItem = summarizeParamSchema(propSchema.items, components) || 'any'; + return `${k}${optSuffix}: ${subItem}[]`; + } + if (propSchema?.type === 'object' && propSchema.properties) { + const sub = summarizeParamSchema(propSchema, components); + return `${k}${optSuffix}: ${sub}`; + } + if (sortedProps.length === 1 && propSchema?.type) { + return `${k}${optSuffix}: ${propSchema.type}`; + } + return `${k}${optSuffix}`; + }); + + return `{ ${formattedProps.join(', ')} }`; + } + + if (resolved.type) return resolved.type; + return schemaRefName(schema) || ''; } function summarizeOperationParams(operation) { const parts = []; const params = operation.parameters || []; + const pathParams = []; + const queryParams = []; + for (const p of params) { - if (p.in === 'header') continue; - const prefix = p.in === 'query' ? '?' : ''; + if (p.in === 'header' || p.in === 'cookie') continue; const type = p.schema?.type ? `:${p.schema.type}` : ''; - parts.push(`${prefix}${p.name}${type}`); + const isOpt = !p.required; + const optSuffix = isOpt ? '?' : ''; + if (p.in === 'path') { + pathParams.push(`${p.name}${type}`); + } else if (p.in === 'query') { + queryParams.push(`${p.name}${optSuffix}${type}`); + } + } + + if (pathParams.length) { + parts.push(...pathParams); + } + if (queryParams.length) { + parts.push(`query: ${queryParams.join(', ')}`); } if (operation.requestBody?.content) { + const isBodyRequired = operation.requestBody.required === true; + const bodyPrefix = isBodyRequired ? 'body' : 'body?'; const content = operation.requestBody.content; if (content['application/json']?.schema) { const s = content['application/json'].schema; - const ref = schemaRefName(s); - if (ref) { - parts.push(`body: ${ref}`); - } else { - const sub = summarizeSchemaCompact(s); - parts.push(sub ? `body: ${sub}` : 'body: json'); - } + const sub = summarizeParamSchema(s, spec.components); + parts.push(`${bodyPrefix}: ${sub || 'json'}`); } else if (content['multipart/form-data']) { - const props = Object.keys(content['multipart/form-data'].schema?.properties || {}); - parts.push(props.length ? `form: { ${props.join(', ')} }` : 'form-data'); + const formSchema = content['multipart/form-data'].schema; + const formSub = summarizeParamSchema(formSchema, spec.components); + parts.push(`form: ${formSub || 'multipart'}`); } else { const ct = Object.keys(content)[0]; - parts.push(ct); + parts.push(isBodyRequired ? ct : `${ct}?`); } } @@ -539,14 +664,22 @@ function renderRoutesOverview(operations) { lines.push(''); lines.push(`Overview of all ${operations.length} API endpoints across all modules.`); lines.push(''); - lines.push('| Method | Route | Params | Return Type | Summary | Docs |'); - lines.push('|:---|:---|:---|:---|:---|:---|'); + lines.push('### Parameter Notation'); + lines.push(''); + lines.push('- **Required parameters** have no `?` (e.g. `sku:string`, `categoryId`, `query: si:string`, `body: { name }`).'); + lines.push('- **Optional parameters** are marked with `?` (e.g. `query: categoryId?:integer`, `track_stock?`, `body?: ...`).'); + lines.push('- **Path parameters** match `{param}` in the route URL.'); + lines.push('- **Query parameters** are prefixed with `query: ...`.'); + lines.push('- **Request bodies** are prefixed with `body: ...` (or `body?: ...` if optional).'); + lines.push(''); + lines.push('| Method | Route | Params | Return Type | Summary |'); + lines.push('|:---|:---|:---|:---|:---|'); for (const op of operations) { const method = op.method.toUpperCase(); const summary = (op.summary || '').replace(/[\r\n|]+/g, ' ').trim(); const paramsStr = op.params === '—' ? '—' : `\`${op.params.replace(/\|/g, '\\|')}\``; const returnsStr = op.returns === '—' ? '—' : `\`${op.returns.replace(/\|/g, '\\|')}\``; - lines.push(`| \`${method}\` | \`${op.path}\` | ${paramsStr} | ${returnsStr} | ${summary} | [\`routes/${op.file}\`](routes/${op.file}) |`); + lines.push(`| \`${method}\` | \`${op.path}\` | ${paramsStr} | ${returnsStr} | ${summary} |`); } lines.push(''); return lines.join('\n'); diff --git a/src/api/openapi.js b/src/api/openapi.js index ca237b9..171d3e8 100644 --- a/src/api/openapi.js +++ b/src/api/openapi.js @@ -87,7 +87,20 @@ * properties: * id: * type: integer - * parentId: + * categoryId: + * type: integer + * name: + * type: string + * sku: + * type: string + * track_stock: + * type: boolean + * Article: + * type: object + * properties: + * id: + * type: integer + * categoryId: * type: integer * name: * type: string @@ -98,7 +111,48 @@ * ArticleDetail: * type: object * description: Full article payload (unit, brand, categoryIds, photos, eans, prices, track_stock, …) - * additionalProperties: true + * properties: + * id: + * type: integer + * sku: + * type: string + * name: + * type: string + * slug: + * type: string + * track_stock: + * type: boolean + * unitamount: + * type: number + * nullable: true + * weight: + * type: number + * nullable: true + * unit: + * type: object + * brand: + * type: object + * nullable: true + * categoryIds: + * type: array + * items: + * type: integer + * photos: + * type: array + * items: + * type: object + * eans: + * type: array + * items: + * type: string + * prices: + * type: array + * items: + * type: object + * suppliers: + * type: array + * items: + * type: object * ArticleHistoryEntry: * type: object * required: [id, date, text] @@ -824,6 +878,73 @@ * desc: * type: string * nullable: true + * Posterminal: + * type: object + * required: [id, name] + * properties: + * id: + * type: integer + * name: + * type: string + * pairingpin: + * type: string + * nullable: true + * categoryIds: + * type: array + * items: + * type: integer + * LabelTemplate: + * type: object + * required: [id, name, width, height, dpi, type, content] + * properties: + * id: + * type: integer + * name: + * type: string + * width: + * type: number + * height: + * type: number + * dpi: + * type: integer + * type: + * type: string + * content: + * type: object + * thumb: + * type: string + * nullable: true + * LabelTemplateCreateInput: + * type: object + * required: [name, width, height, dpi, type, content] + * properties: + * name: + * type: string + * width: + * type: number + * height: + * type: number + * dpi: + * type: integer + * type: + * type: string + * content: + * type: object + * LabelTemplateUpdateInput: + * type: object + * properties: + * name: + * type: string + * width: + * type: number + * height: + * type: number + * dpi: + * type: integer + * type: + * type: string + * content: + * type: object * Warehouse: * type: object * required: [id, name, sort] diff --git a/src/api/routes/article.js b/src/api/routes/article.js index 67e4c9c..b7966d9 100644 --- a/src/api/routes/article.js +++ b/src/api/routes/article.js @@ -111,7 +111,7 @@ export function register(app, basePath = '') { * /article: * post: * tags: [Article] - * summary: Create article + * summary: Create article in category * description: Creates an article under an authorized category (`parentId`). * security: * - cookieAuth: [] @@ -123,18 +123,18 @@ export function register(app, basePath = '') { * application/json: * schema: * type: object - * required: [parentId, name] + * required: [categoryId, name] * properties: - * parentId: + * categoryId: * type: integer - * description: Category id + * description: Category ID under which to create the article * name: * type: string * track_stock: * type: boolean * description: Whether to track inventory stock (defaults to true) * example: - * parentId: 10 + * categoryId: 10 * name: New article * responses: * 201: @@ -178,7 +178,7 @@ export function register(app, basePath = '') { * $ref: '#/components/schemas/Error' */ app.post(`${basePath}/article`, async (req, res) => { - const parent = parsePositiveInt(req.body?.parentId, 'parentId'); + const parent = parsePositiveInt(req.body?.categoryId ?? req.body?.parentId, 'categoryId'); if (parent.error) { return res.status(400).json({ error: parent.error }); } @@ -195,13 +195,17 @@ export function register(app, basePath = '') { const track_stock = rawTrack !== undefined ? Boolean(rawTrack) : true; try { - const article = await db.createUnderParent({ + const created = await db.createUnderParent({ googleAuthId: req.user.id, parentId: parent.id, name, track_stock, }); emitArticlesSignal(req); + const article = { + ...created, + categoryId: created.parentId, + }; res.status(201).json({ article }); } catch (error) { const status = articleErrorStatus(error);