diff --git a/docs/api/routes-all.md b/docs/api/routes-all.md index c0c817e..4b25b25 100644 --- a/docs/api/routes-all.md +++ b/docs/api/routes-all.md @@ -10,54 +10,55 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). ### Reusable types - `T1` = `{ article: T2 }` -- `T2` = `{ id: int, sku: str, name: str, slug: str, track_stock: bool, unitamount: num, weight: num, unit: T32, brand: T28, categoryIds: int[], photos: T22[], eans: str[], prices: T33[], suppliers: T11[] }` +- `T2` = `{ id: int, sku: str, name: str, slug: str, track_stock: bool, unitamount: num|null, weight: num|null, unit: T31, brand: T27|null, categoryIds: int[], photos: T25[], eans: str[], prices: T35[], suppliers: T11[] }` - `T3` = `{ delivery: T4 }` -- `T4` = `{ id: int, number: str, type: str, supplierId: int, externalNumber: str, date: str, createdAt: str, ledger: T38, lines: T23[], files: T10[] }` +- `T4` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T19[], files: T9[], externalNumber?: str|null, createdAt?: str, ledger?: T39 }` - `T5` = `{ labeltemplate: T6 }` -- `T6` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T16, thumb: str }` -- `T7` = `{ order: T9 }` -- `T8` = `{ quote: T9 }` -- `T9` = `{ id: int, number: str, type: str, entityId: int, date: str, createdAt: str, lines: T23[] }` -- `T10` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created: str, modified: str, thumbavif: str, thumbx: int, thumby: int, meta: { [k]: str } }` -- `T11` = `{ id: int, articleId: int, entityId: int, supplierName: str, sku: str, pu: num, stock: num, sort: int, priceId: int, price: T41 }` -- `T12` = `{ ledger: T13[] }` -- `T13` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash: str, sku: str, created_at: str }` -- `T14` = `{ id: int, entityId: int, firstName: str, name: str, street: str, streetNumber: str, zip: str, city: str, county: str, addressType: str }` -- `T15` = `{ objects: T17[] }` -- `T16` = `{ objects: T19[] }` -- `T17` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` -- `T18` = `{ id: int, name: str, sort: int, exclusive: bool, values: T36[] }` -- `T19` = `{ id: str, type: str, x: num, y: num, w: num, h: num, text: str, font: str, fontSize: num, barcodeFormat: str }` -- `T20` = `{ entity: T25 }` -- `T21` = `{ printer: T26 }` -- `T22` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` -- `T23` = `{ id: int, sku: str, name: str, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str }` -- `T24` = `{ id: int, companyuser_id: int, provider: str, model: str, created_at: str, updated_at: str, state: str }` -- `T25` = `{ id: int, customergroupId: int, firstName: str, name: str, number: str, createdAt: str, updatedAt: str }` -- `T26` = `{ id: int, company_id: int, address: str, type: str, model: str, serial: str, sort: int, desc: str }` -- `T27` = `{ amount: num, net: num, taxrate: num, sku?: str, name?: str, pu?: num, rebate?: num, comment?: str }` -- `T28` = `{ id: int, name: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` -- `T29` = `{ article: T31 }` -- `T30` = `{ id: int, iso639: str, name: str, taxrate: num, reducedtaxrate: num }` -- `T31` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` -- `T32` = `{ si: str, basesi: str, basesifactor: num, symbol: str, name: str }` -- `T33` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` -- `T34` = `{ id: int, email: str, selectedCompanyId: int, picture: str }` -- `T35` = `{ id: int, entityId: int, contactType: str, contact: str }` -- `T36` = `{ id: int, attributeId: int, value: str, sort: int }` -- `T37` = `{ id: int, name: str, createdAt: str, updatedAt: str }` -- `T38` = `{ totalPositions: int, completelyOnLedgerPositions: int }` -- `T39` = `{ id: int, name: str, sort: int, exclusive: bool }` -- `T40` = `{ warehouse: T42 }` -- `T41` = `{ net: num, taxId: int, taxRate: num }` -- `T42` = `{ id: int, name: str, sort: int }` -- `T43` = `{ id: int, categoryIds: int[] }` +- `T6` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T15, thumb?: str|null }` +- `T7` = `{ order: T10 }` +- `T8` = `{ quote: T10 }` +- `T9` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` +- `T10` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T19[], createdAt?: str }` +- `T11` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T42|null }` +- `T12` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- `T13` = `{ ledger: T14[] }` +- `T14` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }` +- `T15` = `{ objects: T20[] }` +- `T16` = `{ objects: T21[] }` +- `T17` = `{ printer: T22 }` +- `T18` = `{ entity: T26 }` +- `T19` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }` +- `T20` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T21` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T22` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }` +- `T23` = `{ id: int, name: str, sort: int, exclusive: bool, values: T37[] }` +- `T24` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }` +- `T25` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` +- `T26` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }` +- `T27` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }` +- `T28` = `{ id: int, companyuser_id: int, provider: str, model: str, created_at: str, updated_at: str, state: str }` +- `T29` = `{ id: int, sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` +- `T30` = `{ article: T33 }` +- `T31` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }` +- `T32` = `{ id: int, iso639: str, name: str, taxrate: num, reducedtaxrate: num }` +- `T33` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` +- `T34` = `{ id: int, email: str, selectedCompanyId: int|null, picture?: str }` +- `T35` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` +- `T36` = `{ id: int, entityId: int, contactType: str, contact: str }` +- `T37` = `{ id: int, attributeId: int, value: str, sort: int }` +- `T38` = `{ id: int, name: str, createdAt: str, updatedAt: str }` +- `T39` = `{ totalPositions: int, completelyOnLedgerPositions: int }` +- `T40` = `{ id: int, name: str, sort: int, exclusive: bool }` +- `T41` = `{ warehouse: T43 }` +- `T42` = `{ net: num, taxId: int, taxRate?: num|null }` +- `T43` = `{ id: int, name: str, sort: int }` +- `T44` = `{ id: int, categoryIds: int[] }` ### Routes #### Create article in category — `POST /article` - Params: `body: { categoryId: int, name: str, track_stock?: bool }` -- Returns: `201 T29` +- Returns: `201 T30` #### Export articles as ZIP — `GET /article/export.zip` - Params: `query: categoryId?: int` - Returns: `200 zip` @@ -78,12 +79,12 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 { items: { id: int, articleId: int, sku: str, name: str, count: int, sort: int }[] }` #### List article attribute values — `GET /article/{sku}/attributevalue` - Params: `sku: str` -- Returns: `200 { attributevalues: { id: int, articleId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }` #### Bulk set article attribute values — `PUT /article/{sku}/attributevalue` - Params: `sku: str, body: int[]` -- Returns: `200 { attributevalues: { id: int, articleId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` -#### Set or clear article brand by SKU — `PUT /article/{sku}/brand` -- Params: `sku: str, body: { brandId: int }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }` +#### Set or clear article brand by SKU (null to clear) — `PUT /article/{sku}/brand` +- Params: `sku: str, body: { brandId: int|null }` - Returns: `200 T1` #### Replace article categories by SKU — `PUT /article/{sku}/categories` - Params: `sku: str, body: { categoryIds: int[] }` @@ -93,10 +94,10 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Create and attach a new child article to a parent article — `POST /article/{sku}/children` - Params: `sku: str, body: { attributeValueId?: int, value?: str, name?: str }` -- Returns: `201 T29` +- Returns: `201 T30` #### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children` - Params: `sku: str, body: { sku: str, attributeValueId?: int, value?: str, reparent?: bool }` -- Returns: `200 { article: T31, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` +- Returns: `200 { article: T33, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}` - Params: `sku: str, childSku: str, query: deleteArticle?: bool` - Returns: `200 { success: bool, deleted: bool }` @@ -123,27 +124,27 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 { options: { id: int, articleId: int, sku: str, name: str, count: int, sort: int, mandated: bool }[] }` #### Toggle parent mode for an article — `POST /article/{sku}/parent` - Params: `sku: str, body: { isParent: bool, attributeNameId?: int }` -- Returns: `200 T29` -#### Replace POS prices by SKU — `PUT /article/{sku}/prices` +- Returns: `200 T30` +#### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices` - Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }` - Returns: `200 T1` #### Print article label — `POST /article/{sku}/print` - Params: `sku: str, body: { templateId: int, printerId: int, quantity: int }` -- Returns: `200` +- Returns: `200 { success: bool }` #### List article properties — `GET /article/{sku}/property` - Params: `sku: str` -- Returns: `200 { properties: { id: int, articleId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, articleId?: int }[] }` #### Bulk set article properties — `PUT /article/{sku}/property` - Params: `sku: str, body: { properties: { propertyId: int, value: str, sort?: int }[] }` -- Returns: `200 { properties: { id: int, articleId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, articleId?: int }[] }` #### Delete article property — `DELETE /article/{sku}/property/{propertyId}` - Params: `sku: str, propertyId: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### List article suppliers — `GET /article/{sku}/supplier` - Params: `sku: str` - Returns: `200 { suppliers: T11[] }` #### Assign supplier to article — `POST /article/{sku}/supplier` -- Params: `sku: str, body: { entityId: int, sku?: str, pu?: num, stock?: num, sort?: int, netPrice?: num, taxId?: int, taxRate?: num }` +- Params: `sku: str, body: { entityId: int, sku?: str|null, pu?: num, stock?: num|null, sort?: int|null, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` - Returns: `201 { supplier: T11 }` #### Reorder article suppliers — `PUT /article/{sku}/supplier/order` - Params: `sku: str, body: { ids: int[] }` @@ -152,7 +153,7 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Params: `sku: str, id: int` - Returns: `204 void` #### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}` -- Params: `sku: str, id: int, body: { sku?: str, pu?: num, stock?: num, sort?: int, netPrice?: num, taxId?: int, taxRate?: num }` +- Params: `sku: str, id: int, body: { sku?: str|null, pu?: num, stock?: num|null, sort?: int, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` - Returns: `200 { supplier: T11 }` #### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock` - Params: `sku: str, body: { track_stock: bool }` @@ -162,34 +163,34 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 T1` #### List attribute names — `GET /attributename` _none_ -- Returns: `200 { attributenames: T18[] }` +- Returns: `200 { attributenames: T23[] }` #### Create attribute name — `POST /attributename` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { attributename: T18 }` +- Returns: `201 { attributename: T23 }` #### Reorder attribute names — `PUT /attributename/order` - Params: `body: { attributenameIds: int[] }` -- Returns: `200` +- Returns: `200 { ok: bool }` #### List values for an attribute — `GET /attributename/{attributeId}/value` - Params: `attributeId: int` -- Returns: `200 { values: T36[] }` +- Returns: `200 { values: T37[] }` #### Create value for an attribute — `POST /attributename/{attributeId}/value` - Params: `attributeId: int, body: { value: str, sort?: int }` -- Returns: `201 { attributevalue: T36 }` +- Returns: `201 { attributevalue: T37 }` #### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order` - Params: `attributeId: int, body: { attributevalueIds: int[] }` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Delete attribute name — `DELETE /attributename/{id}` - Params: `id: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Update attribute name — `PATCH /attributename/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { attributename: T18 }` +- Returns: `200 { attributename: T23 }` #### Delete attribute value — `DELETE /attributevalue/{id}` - Params: `id: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Update attribute value — `PATCH /attributevalue/{id}` - Params: `id: int, body: { value?: str, sort?: int }` -- Returns: `200 { attributevalue: T36 }` +- Returns: `200 { attributevalue: T37 }` #### Login with Google ID token — `POST /auth/login` - Params: `body: { idToken: str }` - Returns: `200 { message: str, user: T34 }` @@ -204,22 +205,22 @@ _none_ - Returns: `200 { user: T34 }` #### List brands — `GET /brand` _none_ -- Returns: `200 { brands: { id: int, name: str, gsprpinfo: str, pics: any[] }[] }` +- Returns: `200 { brands: { id: int, name: str, pics: T29[], gsprpinfo?: str|null }[] }` #### Upsert brand — `PUT /brand` - Params: `body: { name: str, gsprpinfo?: str }` -- Returns: `200 { brand: { id: int, name: str, gsprpinfo: str, pics: any[] } }` +- Returns: `200 { brand: { id: int, name: str, pics: T29[], gsprpinfo?: str|null } }` #### Delete brand — `DELETE /brand/{id}` - Params: `id: int` - Returns: `204 void` #### Delete brand image — `DELETE /brand/{id}/image` - Params: `id: int` -- Returns: `200 { brand: { id: int, thumbW: int, thumbH: int, thumbHash: str, pics: { id: int, sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[] } }` +- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T29[] } }` #### Add or replace brand image — `POST /brand/{id}/image` - Params: `id: int, image/avif` -- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: { id: int, sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[] } }` +- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T29[] } }` #### Create category — `POST /category` - Params: `body: { parentId: int, name: str }` -- Returns: `201 { category: { id: int, parentId: int, sort: int, name: str } }` +- Returns: `201 { category: { id: int, parentId: int|null, sort: int, name: str } }` #### Export categories as ZIP — `GET /category/export.zip` _none_ - Returns: `200 zip` @@ -228,7 +229,7 @@ _none_ - Returns: `200 { categoryIds: int[] }` #### Category tree — `GET /category/tree` _none_ -- Returns: `200 { languageId: int, tree: { id: int, parentId: int, sort: int, name: str, slug: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, posterminalIds: int[] }[], articles: { id: int, sku: str, name: str, slug: str, net: num, taxRate: num, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, parentArticleId: int, parentSku: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, brand: T28 }[] }` +- Returns: `200 { languageId: int, tree: { id: int, sort: int, posterminalIds: int[], parentId?: int|null, name?: str|null, slug?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }[], articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T27|null }[] }` #### Delete category — `DELETE /category/{id}` - Params: `id: int` - Returns: `204 void` @@ -237,61 +238,61 @@ _none_ - Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }` #### Articles in category — `GET /category/{id}/articles` - Params: `id: int` -- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, net: num, taxRate: num, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, parentArticleId: int, parentSku: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, brand: T28 }[] }` +- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T27|null }[] }` #### List category attribute values — `GET /category/{id}/attributevalue` - Params: `id: int` -- Returns: `200 { attributevalues: { id: int, categoryId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }` #### Bulk set category attribute values — `PUT /category/{id}/attributevalue` - Params: `id: int, body: int[]` -- Returns: `200 { attributevalues: { id: int, categoryId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }` #### Delete category image — `DELETE /category/{id}/image` - Params: `id: int` -- Returns: `200 { category: { id: int, thumbW: int, thumbH: int, thumbHash: str } }` +- Returns: `200 { category: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null } }` #### Add category image — `POST /category/{id}/image` - Params: `id: int, image/avif` - Returns: `201 { category: { id: int, thumbW: int, thumbH: int, thumbHash: str } }` #### List category properties — `GET /category/{id}/property` - Params: `id: int` -- Returns: `200 { properties: { id: int, categoryId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, categoryId?: int }[] }` #### Bulk set category properties — `PUT /category/{id}/property` - Params: `id: int, body: { properties: { propertyId: int, value: str, sort?: int }[] }` -- Returns: `200 { properties: { id: int, categoryId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, categoryId?: int }[] }` #### Delete category property — `DELETE /category/{id}/property/{propertyId}` - Params: `id: int, propertyId: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### List companies — `GET /company` _none_ -- Returns: `200 { companies: T30[] }` +- Returns: `200 { companies: T32[] }` #### Create company — `POST /company` - Params: `body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }` -- Returns: `201 { company: T30, user: T34 }` +- Returns: `201 { company: T32, user: T34 }` #### Get company history — `GET /company/history` - Params: `query: limit?: int` -- Returns: `200 { history: { id: int, companyuserId: int, date: str, text: str }[], truncated: bool }` +- Returns: `200 { history: { id: int, date: str, text: str, companyuserId?: int|null }[], truncated: bool }` #### Add company history entry — `POST /company/history` - Params: `body: { text: str }` -- Returns: `201 { history: { id: int, companyuserId: int, date: str, text: str } }` +- Returns: `201 { history: { id: int, date: str, text: str, companyuserId?: int|null } }` #### List company settings — `GET /company/setting` _none_ -- Returns: `200 { settings: { key: str, value: str }[] }` +- Returns: `200 { settings: { key: str, value: str|null }[] }` #### Get company setting by key — `GET /company/setting/{key}` - Params: `key: str` -- Returns: `200 { setting: { key: str, value: str } }` +- Returns: `200 { setting: { key: str, value: str|null } }` #### Upsert company setting — `PUT /company/setting/{key}` - Params: `key: str, body: { value: str, frontend?: bool }` -- Returns: `200 { setting: { key: str, value: str } }` +- Returns: `200 { setting: { key: str, value: str|null } }` #### Delete company — `DELETE /company/{id}` - Params: `id: int` - Returns: `204 void` #### Update company — `PUT /company/{id}` - Params: `id: int, body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }` -- Returns: `200 { company: T30 }` +- Returns: `200 { company: T32 }` #### List customer groups — `GET /customergroup` _none_ - Returns: `200 { customergroups: { id: int, name: str, sort: int, editable: bool }[] }` #### Create customer group — `POST /customergroup` - Params: `body: { name: str }` -- Returns: `201 { customergroup: T42 }` +- Returns: `201 { customergroup: T43 }` #### Reorder customer groups — `PUT /customergroup/order` - Params: `body: { customergroupIds: int[] }` - Returns: `200 { customergroupIds: int[] }` @@ -300,12 +301,12 @@ _none_ - Returns: `204 void` #### Update customer group — `PATCH /customergroup/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { customergroup: T42 }` +- Returns: `200 { customergroup: T43 }` #### List deliveries — `GET /delivery` _none_ -- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int, externalNumber: str, date: str, createdAt: str, ledger: T38 }[] }` +- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T39 }[] }` #### Create delivery — `POST /delivery` -- Params: `body: { externalNumber: str, date: str, lines: T27[], supplierId?: int, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `body: { externalNumber: str, date: str, lines: T24[], supplierId?: int|null, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `201 T3` #### Parse document (SSE) — `POST /delivery/parse` - Params: `form: { file: str }` @@ -317,53 +318,53 @@ _none_ - Params: `id: int` - Returns: `200 T3` #### Update delivery — `PATCH /delivery/{id}` -- Params: `id: int, body: { externalNumber?: str, date?: str, supplierId?: int, lines?: T27[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T24[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `200 T3` #### Get delivery attachment file content — `GET /delivery/{id}/file/{fileId}` - Params: `id: int, fileId: int, query: download?: bool` - Returns: `200 str` #### List entities — `GET /entity` - Params: `query: customergroupId?: int, role?: str` -- Returns: `200 { entities: T25[] }` +- Returns: `200 { entities: T26[] }` #### Create entity — `POST /entity` -- Params: `body: { customergroupId: int, name: str, firstName?: str, number?: str }` -- Returns: `201 T20` +- Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }` +- Returns: `201 T18` #### Peek next customer number — `GET /entity/next-number` _none_ - Returns: `200 { number: str }` #### List entity addresses — `GET /entity/{entityId}/address` - Params: `entityId: int` -- Returns: `200 { addresses: T14[] }` +- Returns: `200 { addresses: T12[] }` #### Create entity address — `POST /entity/{entityId}/address` -- Params: `entityId: int, body: { addressType: str, firstName?: str, name?: str, street?: str, streetNumber?: str, zip?: str, city?: str, county?: str }` -- Returns: `201 { address: T14 }` +- Params: `entityId: int, body: { addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- Returns: `201 { address: T12 }` #### Delete entity address — `DELETE /entity/{entityId}/address/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity address — `PATCH /entity/{entityId}/address/{id}` -- Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str, name?: str, street?: str, streetNumber?: str, zip?: str, city?: str, county?: str }` -- Returns: `200 { address: T14 }` +- Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- Returns: `200 { address: T12 }` #### List entity contacts — `GET /entity/{entityId}/contact` - Params: `entityId: int` -- Returns: `200 { contacts: T35[] }` +- Returns: `200 { contacts: T36[] }` #### Create entity contact — `POST /entity/{entityId}/contact` - Params: `entityId: int, body: { contactType: str, contact: str }` -- Returns: `201 { contact: T35 }` +- Returns: `201 { contact: T36 }` #### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity contact — `PATCH /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }` -- Returns: `200 { contact: T35 }` +- Returns: `200 { contact: T36 }` #### Delete entity — `DELETE /entity/{id}` - Params: `id: int` - Returns: `204 void` #### Get entity — `GET /entity/{id}` - Params: `id: int` -- Returns: `200 T20` +- Returns: `200 T18` #### Update entity — `PATCH /entity/{id}` -- Params: `id: int, body: { name?: str, firstName?: str, number?: str }` -- Returns: `200 T20` +- Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }` +- Returns: `200 T18` #### Subscribe to update signals (SSE) — `GET /events` _none_ - Returns: `200 SSE` @@ -375,9 +376,9 @@ _none_ - Returns: `200 avif` #### List label templates — `GET /labeltemplate` _none_ -- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb: str }[] }` +- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }` #### Create label template — `POST /labeltemplate` -- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T15 }` +- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T16 }` - Returns: `201 T5` #### Delete label template — `DELETE /labeltemplate/{id}` - Params: `id: int` @@ -386,35 +387,35 @@ _none_ - Params: `id: int` - Returns: `200 T5` #### Update label template (partial) — `PATCH /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T15 }` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T16 }` - Returns: `200 T5` #### Update label template — `PUT /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T15 }` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T16 }` - Returns: `200 T5` #### Query ledger by manifest article ID or SKU — `GET /ledger` - Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str` -- Returns: `200 T12` +- Returns: `200 T13` #### Add ledger entry — `POST /ledger` - Params: `body: { manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str }` -- Returns: `201 { ledger: T13 }` +- Returns: `201 { ledger: T14 }` #### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}` - Params: `manifestarticleId: int, query: transaction_type?: str` -- Returns: `200 T12` +- Returns: `200 T13` #### Query ledger by article SKU — `GET /ledger/sku/{sku}` - Params: `sku: str, query: transaction_type?: str` -- Returns: `200 T12` +- Returns: `200 T13` #### List conversations — `GET /modelconversation` _none_ -- Returns: `200 { conversations: T24[] }` +- Returns: `200 { conversations: T28[] }` #### Create conversation — `POST /modelconversation` - Params: `body: { provider: str, model: str }` -- Returns: `201 { conversation: T24 }` +- Returns: `201 { conversation: T28 }` #### Delete conversation — `DELETE /modelconversation/{id}` - Params: `id: int` - Returns: `204 void` #### Get conversation — `GET /modelconversation/{id}` - Params: `id: int` -- Returns: `200 { conversation: T24 }` +- Returns: `200 { conversation: T28 }` #### List conversation messages — `GET /modelconversation/{id}/message` - Params: `id: int` - Returns: `200 { messages: { id: int, conversation_id: int, created_at: str, role: str, message: str }[] }` @@ -432,9 +433,9 @@ _none_ - Returns: `200 { number: { id: int, type: str, prefix: str, padding: int, next: int } }` #### List orders — `GET /order` _none_ -- Returns: `200 { orders: { id: int, number: str, type: str, entityId: int, date: str, createdAt: str }[] }` +- Returns: `200 { orders: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }` #### Create order — `POST /order` -- Params: `body: { number: str, date: str, lines: T27[], entityId?: int }` +- Params: `body: { number: str, date: str, lines: T24[], entityId?: int|null }` - Returns: `201 T7` #### Delete order — `DELETE /order/{id}` - Params: `id: int` @@ -443,35 +444,35 @@ _none_ - Params: `id: int` - Returns: `200 T7` #### Update order — `PATCH /order/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int, lines?: T27[] }` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }` - Returns: `200 T7` #### List POS terminals — `GET /posterminal` _none_ -- Returns: `200 { posterminals: { id: int, name: str, pairingpin: str, categoryIds: int[] }[] }` +- Returns: `200 { posterminals: { id: int, name: str, pairingpin?: str|null, categoryIds?: int[] }[] }` #### Create POS terminal — `PUT /posterminal` - Params: `body: { name: str }` -- Returns: `200 { posterminal: { id: int, name: str, pairingpin: str, categoryIds: int[] } }` +- Returns: `200 { posterminal: { id: int, name: str, pairingpin?: str|null, categoryIds?: int[] } }` #### Delete POS terminal — `DELETE /posterminal/{id}` - Params: `id: int` - Returns: `204 void` #### Disable all categories on terminal — `DELETE /posterminal/{id}/all` - Params: `id: int` -- Returns: `200 T43` +- Returns: `200 T44` #### Enable all categories on terminal — `PUT /posterminal/{id}/all` - Params: `id: int` -- Returns: `200 T43` +- Returns: `200 T44` #### Disable category on terminal — `DELETE /posterminal/{id}/category/{categoryId}` - Params: `id: int, categoryId: int` -- Returns: `200 T43` +- Returns: `200 T44` #### Enable category on terminal — `PUT /posterminal/{id}/category/{categoryId}` - Params: `id: int, categoryId: int` -- Returns: `200 T43` +- Returns: `200 T44` #### List printers — `GET /printer` _none_ -- Returns: `200 { printers: T26[] }` +- Returns: `200 { printers: T22[] }` #### Create printer — `POST /printer` -- Params: `body: { address: str, type: str, model?: str, serial?: str, sort?: int, desc?: str }` -- Returns: `201 T21` +- Params: `body: { address: str, type: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` +- Returns: `201 T17` #### Reorder printers — `PUT /printer/order` - Params: `body: { printerIds: int[] }` - Returns: `200 { printerIds: int[] }` @@ -480,42 +481,42 @@ _none_ - Returns: `204 void` #### Get printer by ID — `GET /printer/{id}` - Params: `id: int` -- Returns: `200 T21` +- Returns: `200 T17` #### Update printer (partial) — `PATCH /printer/{id}` -- Params: `id: int, body: { address?: str, type?: str, model?: str, serial?: str, sort?: int, desc?: str }` -- Returns: `200 T21` +- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` +- Returns: `200 T17` #### Update printer — `PUT /printer/{id}` -- Params: `id: int, body: { address?: str, type?: str, model?: str, serial?: str, sort?: int, desc?: str }` -- Returns: `200 T21` +- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` +- Returns: `200 T17` #### List property names — `GET /propertyname` _none_ -- Returns: `200 { propertynames: T39[] }` +- Returns: `200 { propertynames: T40[] }` #### Create property name — `POST /propertyname` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { propertyname: T39 }` +- Returns: `201 { propertyname: T40 }` #### Reorder property names — `PUT /propertyname/order` - Params: `body: { propertynameIds: int[] }` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Delete property name — `DELETE /propertyname/{id}` - Params: `id: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Update property name — `PATCH /propertyname/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { propertyname: T39 }` +- Returns: `200 { propertyname: T40 }` #### List public articles — `GET /public/{companyId}/articles` - Params: `companyId: int, query: categoryId?: int, search?: str, limit?: int, offset?: int` -- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, net: num, taxRate: num, gross: num, trackStock: bool, brand: { id: int, name: str }, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[] }` +- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, net: num|null, taxRate: num|null, gross: num|null, trackStock: bool, brand: { id: int|null, name: str }, fullHash: str|null, thumbW: int|null, thumbH: int|null, thumbHash: str|null }[] }` #### Get public article detail — `GET /public/{companyId}/articles/{sku}` - Params: `companyId: int, sku: str` -- Returns: `200 { article: { id: int, sku: str, name: str, slug: str, description: str, unitamount: num, weight: num, trackStock: bool, unit: {}, brand: {}, price: {}, prices: {}[], categories: {}[], photos: {}[], eans: str[], isParent: bool, parentSku: str, variants: {}[] } }` +- Returns: `200 { article: { id: int, sku: str, name: str, slug: str, description: str, unitamount: num|null, weight: num|null, trackStock: bool, unit: {}|null, brand: {}|null, price: {}|null, prices: {}[], categories: {}[], photos: {}[], eans: str[], isParent: bool, parentSku: str|null, variants: {}[] } }` #### List public categories — `GET /public/{companyId}/categories` - Params: `companyId: int` -- Returns: `200 { categories: { id: int, parentId: int, sort: int, name: str, slug: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[] }` +- Returns: `200 { categories: { id: int, parentId: int|null, sort: int, name: str, slug: str, fullHash: str|null, thumbW: int|null, thumbH: int|null, thumbHash: str|null }[] }` #### List quotes — `GET /quote` _none_ -- Returns: `200 { quotes: { id: int, number: str, type: str, entityId: int, date: str, createdAt: str }[] }` +- Returns: `200 { quotes: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }` #### Create quote — `POST /quote` -- Params: `body: { number: str, date: str, lines: T27[], entityId?: int }` +- Params: `body: { number: str, date: str, lines: T24[], entityId?: int|null }` - Returns: `201 T8` #### Delete quote — `DELETE /quote/{id}` - Params: `id: int` @@ -524,7 +525,7 @@ _none_ - Params: `id: int` - Returns: `200 T8` #### Update quote — `PATCH /quote/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int, lines?: T27[] }` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }` - Returns: `200 T8` #### Crawl image from URL, crop white/transparency borders, and return AVIF — `POST /serper/image` - Params: `body: { url: str }` @@ -533,26 +534,26 @@ _none_ - Params: `body: { q: str, gl?: str, hl?: str, num?: int, preferSource?: bool }` - Returns: `200 SSE` #### Adjust stock manually — `POST /stock/adjust` -- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }` -- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T13[] }` +- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }` +- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T14[] }` #### List suppliers — `GET /supplier` _none_ -- Returns: `200 { suppliers: T37[] }` +- Returns: `200 { suppliers: T38[] }` #### Create supplier — `POST /supplier` - Params: `body: { name: str }` -- Returns: `201 { supplier: T37 }` +- Returns: `201 { supplier: T38 }` #### Delete supplier — `DELETE /supplier/{id}` - Params: `id: int` - Returns: `204 void` #### Update supplier — `PATCH /supplier/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { supplier: T37 }` +- Returns: `200 { supplier: T38 }` #### List supplier articles by SKUs — `GET /supplier/{id}/articles` - Params: `id: int, query: skus?: string` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, supplierName: str, sku: str, pu: num, stock: num, sort: int, priceId: int, price: T41, article: { id: int, sku: str, name: str, slug: str, photos: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[] } }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T42|null }[] }` #### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles` - Params: `id: int, body?: { skus?: str[] }` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, supplierName: str, sku: str, pu: num, stock: num, sort: int, priceId: int, price: T41, article: { id: int, sku: str, name: str, slug: str, photos: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[] } }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T42|null }[] }` #### Thumb image by hash — `GET /thumb/{fullHash}` - Params: `fullHash: str` - Returns: `200 avif` @@ -561,16 +562,16 @@ _none_ - Returns: `204 void` #### List units — `GET /unit` _none_ -- Returns: `200 { units: T32[] }` +- Returns: `200 { units: T31[] }` #### Upsert unit — `PUT /unit` - Params: `body: { si: str, basesi: str, basesifactor: num, symbol: str, name: str }` -- Returns: `200 { unit: T32 }` +- Returns: `200 { unit: T31 }` #### JTL-POS category synchronization — `GET /v1/category` - Params: `query: lastChangedCategory?: str, limit?: int` - Returns: `200 {}[]` #### POS discovery and pairing — `GET /v1/client` - Params: `query: authCode: str, name?: str` -- Returns: `200 { authCode: str, authToken: str, certificateFingerprint: str, certificateSerialNumber: str, mandantId: str, mandantName: str, mandantDatabase: str, serverFingerprint: str, name: str, serverTimestamp: str }` +- Returns: `200 { authCode: str|null, authToken: str|null, certificateFingerprint: str, certificateSerialNumber: str, mandantId: str, mandantName: str|null, mandantDatabase: str|null, serverFingerprint: str|null, name: str|null, serverTimestamp: str }` #### POS sync initialization counts — `GET /v1/init` - Params: `query: lastChangedProduct?: int, lastChangedCategory?: int, lastChangedCustomer?: int, lastChangedCustomerGroup?: int, lastChangedCompositeProduct?: int, lastChangedDeletedEntity?: int` - Returns: `200 { version: str, product_count: str, category_count: str, customer_count: str, customerGroup_count: str, compositeProduct_count: str, configurationGroup_count: str, configurationItem_count: str, deletedEntity_count: str, max_orderId_count: str }` @@ -603,10 +604,10 @@ _none_ - Returns: `200 { success: bool, file: { name: str, size: int, mime: str, updated_at: str } }` #### List warehouses — `GET /warehouse` _none_ -- Returns: `200 { warehouses: T42[] }` +- Returns: `200 { warehouses: T43[] }` #### Create warehouse — `POST /warehouse` - Params: `body: { name: str, sort?: int }` -- Returns: `201 T40` +- Returns: `201 T41` #### Reorder warehouses — `PUT /warehouse/order` - Params: `body: { warehouseIds: int[] }` - Returns: `200 { warehouseIds: int[] }` @@ -615,10 +616,10 @@ _none_ - Returns: `204 void` #### Get warehouse by ID — `GET /warehouse/{id}` - Params: `id: int` -- Returns: `200 T40` +- Returns: `200 T41` #### Update warehouse (partial) — `PATCH /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T40` +- Returns: `200 T41` #### Update warehouse — `PUT /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T40` +- Returns: `200 T41` diff --git a/docs/api/routes.md b/docs/api/routes.md index 833faf0..97b35aa 100644 --- a/docs/api/routes.md +++ b/docs/api/routes.md @@ -10,50 +10,51 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). ### Reusable types - `T1` = `{ article: T2 }` -- `T2` = `{ id: int, sku: str, name: str, slug: str, track_stock: bool, unitamount: num, weight: num, unit: T30, brand: T27, categoryIds: int[], photos: T22[], eans: str[], prices: T31[], suppliers: T11[] }` +- `T2` = `{ id: int, sku: str, name: str, slug: str, track_stock: bool, unitamount: num|null, weight: num|null, unit: T30, brand: T27|null, categoryIds: int[], photos: T25[], eans: str[], prices: T32[], suppliers: T11[] }` - `T3` = `{ delivery: T4 }` -- `T4` = `{ id: int, number: str, type: str, supplierId: int, externalNumber: str, date: str, createdAt: str, ledger: T35, lines: T23[], files: T10[] }` +- `T4` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T19[], files: T9[], externalNumber?: str|null, createdAt?: str, ledger?: T36 }` - `T5` = `{ labeltemplate: T6 }` -- `T6` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T16, thumb: str }` -- `T7` = `{ order: T9 }` -- `T8` = `{ quote: T9 }` -- `T9` = `{ id: int, number: str, type: str, entityId: int, date: str, createdAt: str, lines: T23[] }` -- `T10` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created: str, modified: str, thumbavif: str, thumbx: int, thumby: int, meta: { [k]: str } }` -- `T11` = `{ id: int, articleId: int, entityId: int, supplierName: str, sku: str, pu: num, stock: num, sort: int, priceId: int, price: T38 }` -- `T12` = `{ ledger: T13[] }` -- `T13` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash: str, sku: str, created_at: str }` -- `T14` = `{ id: int, entityId: int, firstName: str, name: str, street: str, streetNumber: str, zip: str, city: str, county: str, addressType: str }` -- `T15` = `{ objects: T17[] }` -- `T16` = `{ objects: T19[] }` -- `T17` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` -- `T18` = `{ id: int, name: str, sort: int, exclusive: bool, values: T33[] }` -- `T19` = `{ id: str, type: str, x: num, y: num, w: num, h: num, text: str, font: str, fontSize: num, barcodeFormat: str }` -- `T20` = `{ entity: T24 }` -- `T21` = `{ printer: T25 }` -- `T22` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` -- `T23` = `{ id: int, sku: str, name: str, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str }` -- `T24` = `{ id: int, customergroupId: int, firstName: str, name: str, number: str, createdAt: str, updatedAt: str }` -- `T25` = `{ id: int, company_id: int, address: str, type: str, model: str, serial: str, sort: int, desc: str }` -- `T26` = `{ amount: num, net: num, taxrate: num, sku?: str, name?: str, pu?: num, rebate?: num, comment?: str }` -- `T27` = `{ id: int, name: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` -- `T28` = `{ article: T29 }` -- `T29` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` -- `T30` = `{ si: str, basesi: str, basesifactor: num, symbol: str, name: str }` -- `T31` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` -- `T32` = `{ id: int, entityId: int, contactType: str, contact: str }` -- `T33` = `{ id: int, attributeId: int, value: str, sort: int }` -- `T34` = `{ id: int, name: str, createdAt: str, updatedAt: str }` -- `T35` = `{ totalPositions: int, completelyOnLedgerPositions: int }` -- `T36` = `{ id: int, name: str, sort: int, exclusive: bool }` -- `T37` = `{ warehouse: T39 }` -- `T38` = `{ net: num, taxId: int, taxRate: num }` -- `T39` = `{ id: int, name: str, sort: int }` +- `T6` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: T15, thumb?: str|null }` +- `T7` = `{ order: T10 }` +- `T8` = `{ quote: T10 }` +- `T9` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, thumbavif?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` +- `T10` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T19[], createdAt?: str }` +- `T11` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T39|null }` +- `T12` = `{ id: int, entityId: int, addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- `T13` = `{ ledger: T14[] }` +- `T14` = `{ id: int, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }` +- `T15` = `{ objects: T20[] }` +- `T16` = `{ objects: T21[] }` +- `T17` = `{ printer: T22 }` +- `T18` = `{ entity: T26 }` +- `T19` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }` +- `T20` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T21` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` +- `T22` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }` +- `T23` = `{ id: int, name: str, sort: int, exclusive: bool, values: T34[] }` +- `T24` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }` +- `T25` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }` +- `T26` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }` +- `T27` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }` +- `T28` = `{ id: int, sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` +- `T29` = `{ article: T31 }` +- `T30` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }` +- `T31` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }` +- `T32` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }` +- `T33` = `{ id: int, entityId: int, contactType: str, contact: str }` +- `T34` = `{ id: int, attributeId: int, value: str, sort: int }` +- `T35` = `{ id: int, name: str, createdAt: str, updatedAt: str }` +- `T36` = `{ totalPositions: int, completelyOnLedgerPositions: int }` +- `T37` = `{ id: int, name: str, sort: int, exclusive: bool }` +- `T38` = `{ warehouse: T40 }` +- `T39` = `{ net: num, taxId: int, taxRate?: num|null }` +- `T40` = `{ id: int, name: str, sort: int }` ### Routes #### Create article in category — `POST /article` - Params: `body: { categoryId: int, name: str, track_stock?: bool }` -- Returns: `201 T28` +- Returns: `201 T29` #### Delete article by SKU — `DELETE /article/{sku}` - Params: `sku: str, query: cascade?: bool` - Returns: `204 void` @@ -71,12 +72,12 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 { items: { id: int, articleId: int, sku: str, name: str, count: int, sort: int }[] }` #### List article attribute values — `GET /article/{sku}/attributevalue` - Params: `sku: str` -- Returns: `200 { attributevalues: { id: int, articleId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }` #### Bulk set article attribute values — `PUT /article/{sku}/attributevalue` - Params: `sku: str, body: int[]` -- Returns: `200 { attributevalues: { id: int, articleId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` -#### Set or clear article brand by SKU — `PUT /article/{sku}/brand` -- Params: `sku: str, body: { brandId: int }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, articleId?: int, exclusive?: bool }[] }` +#### Set or clear article brand by SKU (null to clear) — `PUT /article/{sku}/brand` +- Params: `sku: str, body: { brandId: int|null }` - Returns: `200 T1` #### Replace article categories by SKU — `PUT /article/{sku}/categories` - Params: `sku: str, body: { categoryIds: int[] }` @@ -86,10 +87,10 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Create and attach a new child article to a parent article — `POST /article/{sku}/children` - Params: `sku: str, body: { attributeValueId?: int, value?: str, name?: str }` -- Returns: `201 T28` +- Returns: `201 T29` #### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children` - Params: `sku: str, body: { sku: str, attributeValueId?: int, value?: str, reparent?: bool }` -- Returns: `200 { article: T29, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` +- Returns: `200 { article: T31, children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }` #### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}` - Params: `sku: str, childSku: str, query: deleteArticle?: bool` - Returns: `200 { success: bool, deleted: bool }` @@ -113,27 +114,27 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 { options: { id: int, articleId: int, sku: str, name: str, count: int, sort: int, mandated: bool }[] }` #### Toggle parent mode for an article — `POST /article/{sku}/parent` - Params: `sku: str, body: { isParent: bool, attributeNameId?: int }` -- Returns: `200 T28` -#### Replace POS prices by SKU — `PUT /article/{sku}/prices` +- Returns: `200 T29` +#### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices` - Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }` - Returns: `200 T1` #### Print article label — `POST /article/{sku}/print` - Params: `sku: str, body: { templateId: int, printerId: int, quantity: int }` -- Returns: `200` +- Returns: `200 { success: bool }` #### List article properties — `GET /article/{sku}/property` - Params: `sku: str` -- Returns: `200 { properties: { id: int, articleId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, articleId?: int }[] }` #### Bulk set article properties — `PUT /article/{sku}/property` - Params: `sku: str, body: { properties: { propertyId: int, value: str, sort?: int }[] }` -- Returns: `200 { properties: { id: int, articleId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, articleId?: int }[] }` #### Delete article property — `DELETE /article/{sku}/property/{propertyId}` - Params: `sku: str, propertyId: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### List article suppliers — `GET /article/{sku}/supplier` - Params: `sku: str` - Returns: `200 { suppliers: T11[] }` #### Assign supplier to article — `POST /article/{sku}/supplier` -- Params: `sku: str, body: { entityId: int, sku?: str, pu?: num, stock?: num, sort?: int, netPrice?: num, taxId?: int, taxRate?: num }` +- Params: `sku: str, body: { entityId: int, sku?: str|null, pu?: num, stock?: num|null, sort?: int|null, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` - Returns: `201 { supplier: T11 }` #### Reorder article suppliers — `PUT /article/{sku}/supplier/order` - Params: `sku: str, body: { ids: int[] }` @@ -142,7 +143,7 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Params: `sku: str, id: int` - Returns: `204 void` #### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}` -- Params: `sku: str, id: int, body: { sku?: str, pu?: num, stock?: num, sort?: int, netPrice?: num, taxId?: int, taxRate?: num }` +- Params: `sku: str, id: int, body: { sku?: str|null, pu?: num, stock?: num|null, sort?: int, netPrice?: num|null, taxId?: int|null, taxRate?: num|null }` - Returns: `200 { supplier: T11 }` #### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock` - Params: `sku: str, body: { track_stock: bool }` @@ -152,64 +153,64 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body). - Returns: `200 T1` #### List attribute names — `GET /attributename` _none_ -- Returns: `200 { attributenames: T18[] }` +- Returns: `200 { attributenames: T23[] }` #### Create attribute name — `POST /attributename` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { attributename: T18 }` +- Returns: `201 { attributename: T23 }` #### Reorder attribute names — `PUT /attributename/order` - Params: `body: { attributenameIds: int[] }` -- Returns: `200` +- Returns: `200 { ok: bool }` #### List values for an attribute — `GET /attributename/{attributeId}/value` - Params: `attributeId: int` -- Returns: `200 { values: T33[] }` +- Returns: `200 { values: T34[] }` #### Create value for an attribute — `POST /attributename/{attributeId}/value` - Params: `attributeId: int, body: { value: str, sort?: int }` -- Returns: `201 { attributevalue: T33 }` +- Returns: `201 { attributevalue: T34 }` #### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order` - Params: `attributeId: int, body: { attributevalueIds: int[] }` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Delete attribute name — `DELETE /attributename/{id}` - Params: `id: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Update attribute name — `PATCH /attributename/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { attributename: T18 }` +- Returns: `200 { attributename: T23 }` #### Delete attribute value — `DELETE /attributevalue/{id}` - Params: `id: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Update attribute value — `PATCH /attributevalue/{id}` - Params: `id: int, body: { value?: str, sort?: int }` -- Returns: `200 { attributevalue: T33 }` +- Returns: `200 { attributevalue: T34 }` #### Login with Google ID token — `POST /auth/login` - Params: `body: { idToken: str }` -- Returns: `200 { message: str, user: { id: int, email: str, selectedCompanyId: int, picture: str } }` +- Returns: `200 { message: str, user: { id: int, email: str, selectedCompanyId: int|null, picture?: str } }` #### Logout — `POST /auth/logout` _none_ - Returns: `200 { message: str }` #### List brands — `GET /brand` _none_ -- Returns: `200 { brands: { id: int, name: str, gsprpinfo: str, pics: any[] }[] }` +- Returns: `200 { brands: { id: int, name: str, pics: T28[], gsprpinfo?: str|null }[] }` #### Upsert brand — `PUT /brand` - Params: `body: { name: str, gsprpinfo?: str }` -- Returns: `200 { brand: { id: int, name: str, gsprpinfo: str, pics: any[] } }` +- Returns: `200 { brand: { id: int, name: str, pics: T28[], gsprpinfo?: str|null } }` #### Delete brand — `DELETE /brand/{id}` - Params: `id: int` - Returns: `204 void` #### Delete brand image — `DELETE /brand/{id}/image` - Params: `id: int` -- Returns: `200 { brand: { id: int, thumbW: int, thumbH: int, thumbHash: str, pics: { id: int, sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[] } }` +- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T28[] } }` #### Add or replace brand image — `POST /brand/{id}/image` - Params: `id: int, image/avif` -- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: { id: int, sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[] } }` +- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T28[] } }` #### Create category — `POST /category` - Params: `body: { parentId: int, name: str }` -- Returns: `201 { category: { id: int, parentId: int, sort: int, name: str } }` +- Returns: `201 { category: { id: int, parentId: int|null, sort: int, name: str } }` #### Reorder sibling categories — `PUT /category/order` - Params: `body: { parentId: int, categoryIds: int[] }` - Returns: `200 { categoryIds: int[] }` #### Category tree — `GET /category/tree` _none_ -- Returns: `200 { languageId: int, tree: { id: int, parentId: int, sort: int, name: str, slug: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, posterminalIds: int[] }[], articles: { id: int, sku: str, name: str, slug: str, net: num, taxRate: num, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, parentArticleId: int, parentSku: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, brand: T27 }[] }` +- Returns: `200 { languageId: int, tree: { id: int, sort: int, posterminalIds: int[], parentId?: int|null, name?: str|null, slug?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }[], articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T27|null }[] }` #### Delete category — `DELETE /category/{id}` - Params: `id: int` - Returns: `204 void` @@ -218,43 +219,43 @@ _none_ - Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }` #### Articles in category — `GET /category/{id}/articles` - Params: `id: int` -- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, net: num, taxRate: num, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, parentArticleId: int, parentSku: str, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, brand: T27 }[] }` +- Returns: `200 { articles: { id: int, sku: str, name: str, slug: str, track_stock: bool, optionsCount: int, articleListCount: int, childCount: int, net?: num|null, taxRate?: num|null, parentArticleId?: int|null, parentSku?: str|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null, brand?: T27|null }[] }` #### List category attribute values — `GET /category/{id}/attributevalue` - Params: `id: int` -- Returns: `200 { attributevalues: { id: int, categoryId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }` #### Bulk set category attribute values — `PUT /category/{id}/attributevalue` - Params: `id: int, body: int[]` -- Returns: `200 { attributevalues: { id: int, categoryId: int, attributeId: int, attributeName: str, exclusive: bool, attributeValueId: int, value: str, sort: int }[] }` +- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }` #### Delete category image — `DELETE /category/{id}/image` - Params: `id: int` -- Returns: `200 { category: { id: int, thumbW: int, thumbH: int, thumbHash: str } }` +- Returns: `200 { category: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null } }` #### List category properties — `GET /category/{id}/property` - Params: `id: int` -- Returns: `200 { properties: { id: int, categoryId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, categoryId?: int }[] }` #### Bulk set category properties — `PUT /category/{id}/property` - Params: `id: int, body: { properties: { propertyId: int, value: str, sort?: int }[] }` -- Returns: `200 { properties: { id: int, categoryId: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int }[] }` +- Returns: `200 { properties: { id: int, propertyId: int, name: str, exclusive: bool, value: str, sort: int, categoryId?: int }[] }` #### Delete category property — `DELETE /category/{id}/property/{propertyId}` - Params: `id: int, propertyId: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### List companies — `GET /company` _none_ - Returns: `200 { companies: { id: int, iso639: str, name: str, taxrate: num, reducedtaxrate: num }[] }` #### Get company history — `GET /company/history` - Params: `query: limit?: int` -- Returns: `200 { history: { id: int, companyuserId: int, date: str, text: str }[], truncated: bool }` +- Returns: `200 { history: { id: int, date: str, text: str, companyuserId?: int|null }[], truncated: bool }` #### Add company history entry — `POST /company/history` - Params: `body: { text: str }` -- Returns: `201 { history: { id: int, companyuserId: int, date: str, text: str } }` +- Returns: `201 { history: { id: int, date: str, text: str, companyuserId?: int|null } }` #### List company settings — `GET /company/setting` _none_ -- Returns: `200 { settings: { key: str, value: str }[] }` +- Returns: `200 { settings: { key: str, value: str|null }[] }` #### Get company setting by key — `GET /company/setting/{key}` - Params: `key: str` -- Returns: `200 { setting: { key: str, value: str } }` +- Returns: `200 { setting: { key: str, value: str|null } }` #### Upsert company setting — `PUT /company/setting/{key}` - Params: `key: str, body: { value: str, frontend?: bool }` -- Returns: `200 { setting: { key: str, value: str } }` +- Returns: `200 { setting: { key: str, value: str|null } }` #### Update company — `PUT /company/{id}` - Params: `id: int, body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }` - Returns: `200 { company: { id: int, iso639: str, name: str, taxrate: num, reducedtaxrate: num } }` @@ -263,7 +264,7 @@ _none_ - Returns: `200 { customergroups: { id: int, name: str, sort: int, editable: bool }[] }` #### Create customer group — `POST /customergroup` - Params: `body: { name: str }` -- Returns: `201 { customergroup: T39 }` +- Returns: `201 { customergroup: T40 }` #### Reorder customer groups — `PUT /customergroup/order` - Params: `body: { customergroupIds: int[] }` - Returns: `200 { customergroupIds: int[] }` @@ -272,12 +273,12 @@ _none_ - Returns: `204 void` #### Update customer group — `PATCH /customergroup/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { customergroup: T39 }` +- Returns: `200 { customergroup: T40 }` #### List deliveries — `GET /delivery` _none_ -- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int, externalNumber: str, date: str, createdAt: str, ledger: T35 }[] }` +- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T36 }[] }` #### Create delivery — `POST /delivery` -- Params: `body: { externalNumber: str, date: str, lines: T26[], supplierId?: int, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `body: { externalNumber: str, date: str, lines: T24[], supplierId?: int|null, files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `201 T3` #### Delete delivery — `DELETE /delivery/{id}` - Params: `id: int` @@ -286,58 +287,58 @@ _none_ - Params: `id: int` - Returns: `200 T3` #### Update delivery — `PATCH /delivery/{id}` -- Params: `id: int, body: { externalNumber?: str, date?: str, supplierId?: int, lines?: T26[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` +- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T24[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }` - Returns: `200 T3` #### Get delivery attachment file content — `GET /delivery/{id}/file/{fileId}` - Params: `id: int, fileId: int, query: download?: bool` - Returns: `200 str` #### List entities — `GET /entity` - Params: `query: customergroupId?: int, role?: str` -- Returns: `200 { entities: T24[] }` +- Returns: `200 { entities: T26[] }` #### Create entity — `POST /entity` -- Params: `body: { customergroupId: int, name: str, firstName?: str, number?: str }` -- Returns: `201 T20` +- Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }` +- Returns: `201 T18` #### Peek next customer number — `GET /entity/next-number` _none_ - Returns: `200 { number: str }` #### List entity addresses — `GET /entity/{entityId}/address` - Params: `entityId: int` -- Returns: `200 { addresses: T14[] }` +- Returns: `200 { addresses: T12[] }` #### Create entity address — `POST /entity/{entityId}/address` -- Params: `entityId: int, body: { addressType: str, firstName?: str, name?: str, street?: str, streetNumber?: str, zip?: str, city?: str, county?: str }` -- Returns: `201 { address: T14 }` +- Params: `entityId: int, body: { addressType: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- Returns: `201 { address: T12 }` #### Delete entity address — `DELETE /entity/{entityId}/address/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity address — `PATCH /entity/{entityId}/address/{id}` -- Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str, name?: str, street?: str, streetNumber?: str, zip?: str, city?: str, county?: str }` -- Returns: `200 { address: T14 }` +- Params: `entityId: int, id: int, body: { addressType?: str, firstName?: str|null, name?: str|null, street?: str|null, streetNumber?: str|null, zip?: str|null, city?: str|null, county?: str|null }` +- Returns: `200 { address: T12 }` #### List entity contacts — `GET /entity/{entityId}/contact` - Params: `entityId: int` -- Returns: `200 { contacts: T32[] }` +- Returns: `200 { contacts: T33[] }` #### Create entity contact — `POST /entity/{entityId}/contact` - Params: `entityId: int, body: { contactType: str, contact: str }` -- Returns: `201 { contact: T32 }` +- Returns: `201 { contact: T33 }` #### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int` - Returns: `204 void` #### Update entity contact — `PATCH /entity/{entityId}/contact/{id}` - Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }` -- Returns: `200 { contact: T32 }` +- Returns: `200 { contact: T33 }` #### Delete entity — `DELETE /entity/{id}` - Params: `id: int` - Returns: `204 void` #### Get entity — `GET /entity/{id}` - Params: `id: int` -- Returns: `200 T20` +- Returns: `200 T18` #### Update entity — `PATCH /entity/{id}` -- Params: `id: int, body: { name?: str, firstName?: str, number?: str }` -- Returns: `200 T20` +- Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }` +- Returns: `200 T18` #### List label templates — `GET /labeltemplate` _none_ -- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb: str }[] }` +- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }` #### Create label template — `POST /labeltemplate` -- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T15 }` +- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: T16 }` - Returns: `201 T5` #### Delete label template — `DELETE /labeltemplate/{id}` - Params: `id: int` @@ -346,23 +347,23 @@ _none_ - Params: `id: int` - Returns: `200 T5` #### Update label template (partial) — `PATCH /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T15 }` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T16 }` - Returns: `200 T5` #### Update label template — `PUT /labeltemplate/{id}` -- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T15 }` +- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: T16 }` - Returns: `200 T5` #### Query ledger by manifest article ID or SKU — `GET /ledger` - Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str` -- Returns: `200 T12` +- Returns: `200 T13` #### Add ledger entry — `POST /ledger` - Params: `body: { manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str }` -- Returns: `201 { ledger: T13 }` +- Returns: `201 { ledger: T14 }` #### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}` - Params: `manifestarticleId: int, query: transaction_type?: str` -- Returns: `200 T12` +- Returns: `200 T13` #### Query ledger by article SKU — `GET /ledger/sku/{sku}` - Params: `sku: str, query: transaction_type?: str` -- Returns: `200 T12` +- Returns: `200 T13` #### List number sequences — `GET /number` _none_ - Returns: `200 { numbers: { id: int, type: str, prefix: str, padding: int, next: int }[] }` @@ -371,9 +372,9 @@ _none_ - Returns: `200 { number: { id: int, type: str, prefix: str, padding: int, next: int } }` #### List orders — `GET /order` _none_ -- Returns: `200 { orders: { id: int, number: str, type: str, entityId: int, date: str, createdAt: str }[] }` +- Returns: `200 { orders: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }` #### Create order — `POST /order` -- Params: `body: { number: str, date: str, lines: T26[], entityId?: int }` +- Params: `body: { number: str, date: str, lines: T24[], entityId?: int|null }` - Returns: `201 T7` #### Delete order — `DELETE /order/{id}` - Params: `id: int` @@ -382,14 +383,14 @@ _none_ - Params: `id: int` - Returns: `200 T7` #### Update order — `PATCH /order/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int, lines?: T26[] }` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }` - Returns: `200 T7` #### List printers — `GET /printer` _none_ -- Returns: `200 { printers: T25[] }` +- Returns: `200 { printers: T22[] }` #### Create printer — `POST /printer` -- Params: `body: { address: str, type: str, model?: str, serial?: str, sort?: int, desc?: str }` -- Returns: `201 T21` +- Params: `body: { address: str, type: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` +- Returns: `201 T17` #### Reorder printers — `PUT /printer/order` - Params: `body: { printerIds: int[] }` - Returns: `200 { printerIds: int[] }` @@ -398,33 +399,33 @@ _none_ - Returns: `204 void` #### Get printer by ID — `GET /printer/{id}` - Params: `id: int` -- Returns: `200 T21` +- Returns: `200 T17` #### Update printer (partial) — `PATCH /printer/{id}` -- Params: `id: int, body: { address?: str, type?: str, model?: str, serial?: str, sort?: int, desc?: str }` -- Returns: `200 T21` +- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` +- Returns: `200 T17` #### Update printer — `PUT /printer/{id}` -- Params: `id: int, body: { address?: str, type?: str, model?: str, serial?: str, sort?: int, desc?: str }` -- Returns: `200 T21` +- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` +- Returns: `200 T17` #### List property names — `GET /propertyname` _none_ -- Returns: `200 { propertynames: T36[] }` +- Returns: `200 { propertynames: T37[] }` #### Create property name — `POST /propertyname` - Params: `body: { name: str, sort?: int, exclusive?: bool }` -- Returns: `201 { propertyname: T36 }` +- Returns: `201 { propertyname: T37 }` #### Reorder property names — `PUT /propertyname/order` - Params: `body: { propertynameIds: int[] }` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Delete property name — `DELETE /propertyname/{id}` - Params: `id: int` -- Returns: `200` +- Returns: `200 { ok: bool }` #### Update property name — `PATCH /propertyname/{id}` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` -- Returns: `200 { propertyname: T36 }` +- Returns: `200 { propertyname: T37 }` #### List quotes — `GET /quote` _none_ -- Returns: `200 { quotes: { id: int, number: str, type: str, entityId: int, date: str, createdAt: str }[] }` +- Returns: `200 { quotes: { id: int, number: str, type: str, entityId: int|null, date: str, createdAt?: str }[] }` #### Create quote — `POST /quote` -- Params: `body: { number: str, date: str, lines: T26[], entityId?: int }` +- Params: `body: { number: str, date: str, lines: T24[], entityId?: int|null }` - Returns: `201 T8` #### Delete quote — `DELETE /quote/{id}` - Params: `id: int` @@ -433,29 +434,29 @@ _none_ - Params: `id: int` - Returns: `200 T8` #### Update quote — `PATCH /quote/{id}` -- Params: `id: int, body: { number?: str, date?: str, entityId?: int, lines?: T26[] }` +- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, lines?: T24[] }` - Returns: `200 T8` #### Adjust stock manually — `POST /stock/adjust` -- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }` -- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T13[] }` +- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }` +- Returns: `201 { manifest: { id: int, number: str, type: str, date: str, createdAt: str }, lines: { manifestarticle_id: int, sku: str, name: str, warehouse_id: int, amount: num, text: str }[], ledger: T14[] }` #### List suppliers — `GET /supplier` _none_ -- Returns: `200 { suppliers: T34[] }` +- Returns: `200 { suppliers: T35[] }` #### Create supplier — `POST /supplier` - Params: `body: { name: str }` -- Returns: `201 { supplier: T34 }` +- Returns: `201 { supplier: T35 }` #### Delete supplier — `DELETE /supplier/{id}` - Params: `id: int` - Returns: `204 void` #### Update supplier — `PATCH /supplier/{id}` - Params: `id: int, body: { name: str }` -- Returns: `200 { supplier: T34 }` +- Returns: `200 { supplier: T35 }` #### List supplier articles by SKUs — `GET /supplier/{id}/articles` - Params: `id: int, query: skus?: string` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, supplierName: str, sku: str, pu: num, stock: num, sort: int, priceId: int, price: T38, article: { id: int, sku: str, name: str, slug: str, photos: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[] } }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T39|null }[] }` #### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles` - Params: `id: int, body?: { skus?: str[] }` -- Returns: `200 { articles: { id: int, articleId: int, entityId: int, supplierName: str, sku: str, pu: num, stock: num, sort: int, priceId: int, price: T38, article: { id: int, sku: str, name: str, slug: str, photos: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[] } }[] }` +- Returns: `200 { articles: { id: int, articleId: int, entityId: int, pu: num, sort: int, article: { id: int, sku: str, name?: str|null, slug?: str|null, photos?: { sort: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str }[], eans?: str[] }, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T39|null }[] }` #### Delete unit — `DELETE /unit` - Params: `query: si: str` - Returns: `204 void` @@ -467,10 +468,10 @@ _none_ - Returns: `200 { unit: T30 }` #### List warehouses — `GET /warehouse` _none_ -- Returns: `200 { warehouses: T39[] }` +- Returns: `200 { warehouses: T40[] }` #### Create warehouse — `POST /warehouse` - Params: `body: { name: str, sort?: int }` -- Returns: `201 T37` +- Returns: `201 T38` #### Reorder warehouses — `PUT /warehouse/order` - Params: `body: { warehouseIds: int[] }` - Returns: `200 { warehouseIds: int[] }` @@ -479,13 +480,13 @@ _none_ - Returns: `204 void` #### Get warehouse by ID — `GET /warehouse/{id}` - Params: `id: int` -- Returns: `200 T37` +- Returns: `200 T38` #### Update warehouse (partial) — `PATCH /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T37` +- Returns: `200 T38` #### Update warehouse — `PUT /warehouse/{id}` - Params: `id: int, body: { name?: str, sort?: int }` -- Returns: `200 T37` +- Returns: `200 T38` ### Binary & streaming routes (reachable only via `fetch()` in javascript_execute) These routes are NOT in the table above because their responses are binary or long-lived streams, which the `dashback_request` tool cannot return usefully. They ARE reachable from inside `javascript_execute` with `fetch("/route")`, where you can decode the bytes, write them to the VFS and embed them in your reply: diff --git a/docs/api/routes/article/_sku/print.md b/docs/api/routes/article/_sku/print.md index 7a92a42..7e5bcbf 100644 --- a/docs/api/routes/article/_sku/print.md +++ b/docs/api/routes/article/_sku/print.md @@ -32,6 +32,12 @@ Prints a label for the article using a label template. Print job dispatched +```json +{ + "success": true +} +``` + ## Errors | Status | Description | diff --git a/docs/api/routes/article/_sku/property/_propertyId.md b/docs/api/routes/article/_sku/property/_propertyId.md index 495a060..aafc8e4 100644 --- a/docs/api/routes/article/_sku/property/_propertyId.md +++ b/docs/api/routes/article/_sku/property/_propertyId.md @@ -17,6 +17,12 @@ Removes a single property from an article. Deleted successfully +```json +{ + "ok": true +} +``` + --- _Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/article/set-brand.md b/docs/api/routes/article/set-brand.md index 5b27b1c..9fa6067 100644 --- a/docs/api/routes/article/set-brand.md +++ b/docs/api/routes/article/set-brand.md @@ -2,7 +2,7 @@ Protected. Requires a valid session cookie (`credentials: 'include'`). -Set or clear article brand by SKU +Set or clear article brand by SKU (null to clear) Sets the article's `brandId` (must belong to the article's company). Pass `null` to clear the brand. Returns the same shape as GET by SKU. @@ -18,7 +18,7 @@ Pass `null` to clear the brand. Returns the same shape as GET by SKU. | Field | Required | Type | Description | |-------|----------|------|-------------| -| `brandId` | yes | integer | | +| `brandId` | yes | integer | Brand ID to assign, or null to remove the brand | ```json { diff --git a/docs/api/routes/article/set-prices.md b/docs/api/routes/article/set-prices.md index 07d4f84..b6cae6d 100644 --- a/docs/api/routes/article/set-prices.md +++ b/docs/api/routes/article/set-prices.md @@ -2,12 +2,14 @@ Protected. Requires a valid session cookie (`credentials: 'include'`). -Replace POS prices by SKU +Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) Replace-all prices for the company POS customer group. -Each entry accepts `net` OR `gross` price (or legacy `price`), `taxRate` OR `tax` (percentage, e.g. 19 for 19%, ensured via tax.ensure), optional `minmultiplier` (default 1). +`net` takes precedence over `price` (legacy alias for net) and `gross`. +`tax` and `taxRate` both specify the tax rate percentage (e.g. 19 for 19%), not monetary tax. When `gross` is provided, net price is computed as `gross / (1 + taxRate / 100)`. -Empty `prices` clears all POS prices. Returns the same shape as GET by SKU (article `prices` array contains `{ minmultiplier, net, taxRate, customergroupId }`). +Prices are strictly scoped to the company's designated POS customer group (customergroupId returned on GET). +Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. ## Path parameters diff --git a/docs/api/routes/attributename/_attributeId/value/order.md b/docs/api/routes/attributename/_attributeId/value/order.md index 4785298..8e03b30 100644 --- a/docs/api/routes/attributename/_attributeId/value/order.md +++ b/docs/api/routes/attributename/_attributeId/value/order.md @@ -30,6 +30,12 @@ Updates the display sort order of values belonging to an attribute. Reordered successfully +```json +{ + "ok": true +} +``` + --- _Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/attributename/_id.delete.md b/docs/api/routes/attributename/_id.delete.md index 414b797..ad289e6 100644 --- a/docs/api/routes/attributename/_id.delete.md +++ b/docs/api/routes/attributename/_id.delete.md @@ -16,6 +16,12 @@ Deletes an attribute name and cascades to attribute values and article assignmen Deleted successfully +```json +{ + "ok": true +} +``` + ## Errors | Status | Description | diff --git a/docs/api/routes/attributename/order.md b/docs/api/routes/attributename/order.md index 001daf4..bbe0a01 100644 --- a/docs/api/routes/attributename/order.md +++ b/docs/api/routes/attributename/order.md @@ -24,6 +24,12 @@ Updates the display sort order of attribute names for the selected company. Reordered successfully +```json +{ + "ok": true +} +``` + --- _Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/attributevalue/_id.delete.md b/docs/api/routes/attributevalue/_id.delete.md index 4ff81a8..373436d 100644 --- a/docs/api/routes/attributevalue/_id.delete.md +++ b/docs/api/routes/attributevalue/_id.delete.md @@ -16,6 +16,12 @@ Deletes an attribute value and cascades to article assignments. Deleted successfully +```json +{ + "ok": true +} +``` + ## Errors | Status | Description | diff --git a/docs/api/routes/brand/list.md b/docs/api/routes/brand/list.md index 0f14560..8dd1ed8 100644 --- a/docs/api/routes/brand/list.md +++ b/docs/api/routes/brand/list.md @@ -17,7 +17,16 @@ Brand list "id": 1, "name": "…", "gsprpinfo": "…", - "pics": [] + "pics": [ + { + "id": 1, + "sort": 1, + "fullHash": "…", + "thumbW": 1, + "thumbH": 1, + "thumbHash": "…" + } + ] } ] } diff --git a/docs/api/routes/brand/upsert.md b/docs/api/routes/brand/upsert.md index 3a2216a..933803a 100644 --- a/docs/api/routes/brand/upsert.md +++ b/docs/api/routes/brand/upsert.md @@ -31,7 +31,16 @@ Upserted brand "id": 1, "name": "…", "gsprpinfo": "…", - "pics": [] + "pics": [ + { + "id": 1, + "sort": 1, + "fullHash": "…", + "thumbW": 1, + "thumbH": 1, + "thumbHash": "…" + } + ] } } ``` diff --git a/docs/api/routes/category/_id/property/_propertyId.md b/docs/api/routes/category/_id/property/_propertyId.md index 9bd9a3e..0f3c884 100644 --- a/docs/api/routes/category/_id/property/_propertyId.md +++ b/docs/api/routes/category/_id/property/_propertyId.md @@ -17,6 +17,12 @@ Removes a single property from a category. Deleted successfully +```json +{ + "ok": true +} +``` + --- _Generated from `@openapi` JSDoc. Do not edit by hand — run `npm run docs:api`._ diff --git a/docs/api/routes/propertyname/_id.delete.md b/docs/api/routes/propertyname/_id.delete.md index f4c3175..8665ecf 100644 --- a/docs/api/routes/propertyname/_id.delete.md +++ b/docs/api/routes/propertyname/_id.delete.md @@ -16,6 +16,12 @@ Deletes a property name and cascades to article properties. Deleted successfully +```json +{ + "ok": true +} +``` + ## Errors | Status | Description | diff --git a/docs/api/routes/propertyname/order.md b/docs/api/routes/propertyname/order.md index b2ecf52..a34d3ad 100644 --- a/docs/api/routes/propertyname/order.md +++ b/docs/api/routes/propertyname/order.md @@ -24,6 +24,12 @@ Updates the display sort order of property names for the selected company. Reordered successfully +```json +{ + "ok": true +} +``` + ## Errors | Status | Description | diff --git a/scripts/generateApiDocs.js b/scripts/generateApiDocs.js index 9e38112..de5bb56 100644 --- a/scripts/generateApiDocs.js +++ b/scripts/generateApiDocs.js @@ -547,6 +547,7 @@ function summarizeSchema( const alias = shape && aliases.get(`${markOptional ? 1 : 0}\0${shape}`); if (alias && !skipRootAlias) return alias; + const hasExplicitRequired = Array.isArray(resolved.required); const required = new Set(resolved.required || []); let entries = Object.entries(resolved.properties || {}); if (entries.length === 0) { @@ -558,7 +559,7 @@ function summarizeSchema( } // Required props first, then optional ones (stable within each group). - if (markOptional) { + if (markOptional || hasExplicitRequired) { entries = entries .map(([name], i) => ({ name, i })) .sort((a, b) => (required.has(a.name) ? 0 : 1) - (required.has(b.name) ? 0 : 1) || a.i - b.i) @@ -566,16 +567,21 @@ function summarizeSchema( } const parts = entries.map(([name, prop]) => { - const opt = markOptional && !required.has(name) ? '?' : ''; - const type = summarizeSchema(prop, components, depth + 1, markOptional, aliases) || 'any'; + const isOpt = markOptional ? !required.has(name) : (hasExplicitRequired && !required.has(name)); + const opt = isOpt ? '?' : ''; + let type = summarizeSchema(prop, components, depth + 1, markOptional, aliases) || 'any'; + if (prop?.nullable && !type.includes('|null')) { + type = `${type}|null`; + } return `${name}${opt}: ${type}`; }); return `{ ${parts.join(', ')} }`; } if (resolved.type) { + let t = resolved.type; if (aliases?.compactTypes) { - return { + t = { string: 'str', integer: 'int', number: 'num', @@ -583,7 +589,10 @@ function summarizeSchema( object: '{}', }[resolved.type] || resolved.type; } - return resolved.type; + if (resolved.nullable && !t.includes('|null')) { + t = `${t}|null`; + } + return t; } return schemaRefName(schema) || ''; } diff --git a/src/api/openapi.js b/src/api/openapi.js index 555c9b7..6a90601 100644 --- a/src/api/openapi.js +++ b/src/api/openapi.js @@ -50,6 +50,13 @@ * type: number * reducedtaxrate: * type: number + * SuccessResponse: + * type: object + * required: [ok] + * properties: + * ok: + * type: boolean + * example: true * CompanySetting: * type: object * required: [key, value] @@ -73,14 +80,18 @@ * type: string * Unit: * type: object + * description: Measurement unit with conversion factor to base SI unit (e.g. g -> kg, ml -> l) * required: [si, basesi, basesifactor] * properties: * si: * type: string + * description: Unit code identifier (e.g. "g", "kg", "ml", "l", "stk") * basesi: * type: string + * description: Base SI reference unit (e.g. "kg" for mass, "l" or "m3" for volume, "stk" for counts) * basesifactor: * type: number + * description: Conversion factor to base SI (1 si = basesifactor * basesi; e.g. 0.001 for g -> kg) * symbol: * type: string * nullable: true @@ -130,9 +141,11 @@ * unitamount: * type: number * nullable: true + * description: Net package content quantity measured in article unit (e.g. 500 for 500g, 0.75 for 0.75l) * weight: * type: number * nullable: true + * description: Shipping weight in kg (must be >= 0) * unit: * $ref: '#/components/schemas/Unit' * brand: @@ -353,6 +366,7 @@ * type: array * description: Brand pictures sorted by sort * items: + * $ref: '#/components/schemas/BrandPic' * ArticleBrand: * type: object * required: [name] diff --git a/src/api/routes/article.js b/src/api/routes/article.js index 22c659f..7ff414e 100644 --- a/src/api/routes/article.js +++ b/src/api/routes/article.js @@ -717,12 +717,14 @@ export function register(app, basePath = '') { * /article/{sku}/prices: * put: * tags: [Article] - * summary: Replace POS prices by SKU + * summary: Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) * description: | * Replace-all prices for the company POS customer group. - * Each entry accepts `net` OR `gross` price (or legacy `price`), `taxRate` OR `tax` (percentage, e.g. 19 for 19%, ensured via tax.ensure), optional `minmultiplier` (default 1). + * `net` takes precedence over `price` (legacy alias for net) and `gross`. + * `tax` and `taxRate` both specify the tax rate percentage (e.g. 19 for 19%), not monetary tax. * When `gross` is provided, net price is computed as `gross / (1 + taxRate / 100)`. - * Empty `prices` clears all POS prices. Returns the same shape as GET by SKU (article `prices` array contains `{ minmultiplier, net, taxRate, customergroupId }`). + * Prices are strictly scoped to the company's designated POS customer group (customergroupId returned on GET). + * Empty `prices` clears all POS prices. Returns the same shape as GET by SKU. * security: * - cookieAuth: [] * x-signals: [article-] @@ -1199,7 +1201,7 @@ export function register(app, basePath = '') { * /article/{sku}/brand: * put: * tags: [Article] - * summary: Set or clear article brand by SKU + * summary: Set or clear article brand by SKU (null to clear) * description: | * Sets the article's `brandId` (must belong to the article's company). * Pass `null` to clear the brand. Returns the same shape as GET by SKU. @@ -1224,6 +1226,7 @@ export function register(app, basePath = '') { * brandId: * type: integer * nullable: true + * description: Brand ID to assign, or null to remove the brand * example: * brandId: 3 * responses: @@ -1585,6 +1588,15 @@ export function register(app, basePath = '') { * responses: * 200: * description: Print job dispatched + * content: + * application/json: + * schema: + * type: object + * required: [success] + * properties: + * success: + * type: boolean + * example: true * 400: * description: Invalid parameters * 404: diff --git a/src/api/routes/articleproperty.js b/src/api/routes/articleproperty.js index 85224f7..6c264d7 100644 --- a/src/api/routes/articleproperty.js +++ b/src/api/routes/articleproperty.js @@ -178,6 +178,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Deleted successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' */ app.delete(`${basePath}/article/:sku/property/:propertyId`, async (req, res) => { const selected = requireSelectedCompany(req); diff --git a/src/api/routes/attributename.js b/src/api/routes/attributename.js index 109eb11..6e2edd0 100644 --- a/src/api/routes/attributename.js +++ b/src/api/routes/attributename.js @@ -133,6 +133,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Reordered successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' */ app.put(`${basePath}/attributename/order`, async (req, res) => { const selected = requireSelectedCompany(req); @@ -202,6 +206,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Deleted successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' * 400: * description: Bad request or no company selected * content: diff --git a/src/api/routes/attributevalue.js b/src/api/routes/attributevalue.js index 38b003d..8d9c2cf 100644 --- a/src/api/routes/attributevalue.js +++ b/src/api/routes/attributevalue.js @@ -150,6 +150,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Reordered successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' */ app.put(`${basePath}/attributename/:attributeId/value/order`, async (req, res) => { const selected = requireSelectedCompany(req); @@ -218,6 +222,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Deleted successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' * 400: * description: Bad request or no company selected * content: diff --git a/src/api/routes/categoryproperty.js b/src/api/routes/categoryproperty.js index 80cb4a3..d43fdbc 100644 --- a/src/api/routes/categoryproperty.js +++ b/src/api/routes/categoryproperty.js @@ -180,6 +180,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Deleted successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' */ app.delete(`${basePath}/category/:id/property/:propertyId`, async (req, res) => { const selected = requireSelectedCompany(req); diff --git a/src/api/routes/propertyname.js b/src/api/routes/propertyname.js index f4159a2..57bac86 100644 --- a/src/api/routes/propertyname.js +++ b/src/api/routes/propertyname.js @@ -145,6 +145,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Reordered successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' * 400: * description: Bad request */ @@ -216,6 +220,10 @@ export function register(app, basePath = '') { * responses: * 200: * description: Deleted successfully + * content: + * application/json: + * schema: + * $ref: '#/components/schemas/SuccessResponse' * 400: * description: Bad request or no company selected * content: