Compare commits

..

2 Commits

Author SHA1 Message Date
seb
56d376c9bd u 2026-10-30 17:30:10 +01:00
seb
72f168f188 u 2026-10-30 16:50:15 +01:00
7 changed files with 898 additions and 124 deletions

View File

@@ -131,7 +131,7 @@ Strip supplierName?: str from T11 (resolve via entityId in cached supplier list)
# Dash API Routes Overview (All) # Dash API Routes Overview (All)
Overview of all 284 API endpoints across all modules. Overview of all 288 API endpoints across all modules.
### Notation ### Notation
@@ -140,44 +140,45 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
### Reusable types ### Reusable types
- `T1` = `{ id: int, number: str, type: str, entityId: int, date: str, lines: T18[], externalNumber?: str|null, createdAt?: str, shipping?: T20|null, shippings?: T20[], payment?: T29|null, payments?: T29[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T2[], files?: T10[] }` - `T1` = `{ id: int, number: str, type: str, entityId: int, date: str, lines: T19[], externalNumber?: str|null, createdAt?: str, shipping?: T21|null, shippings?: T21[], payment?: T30|null, payments?: T30[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T2[], files?: T11[] }`
- `T2` = `{ id: int, number: str, type: str, date: str, lines: T18[], shippings: T20[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int, files?: T10[], state?: str }` - `T2` = `{ id: int, number: str, type: str, date: str, lines: T19[], shippings: T21[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int, files?: T11[], state?: str }`
- `T3` = `{ id: int, number: str, type: str, date: str, lines: T18[], entityId?: int|null, externalNumber?: str|null, createdAt?: str, positions?: int, grossTotal?: num, totalQuantity?: num, customerName?: str|null, files?: T10[] }` - `T3` = `{ id: int, number: str, type: str, date: str, lines: T19[], entityId?: int|null, externalNumber?: str|null, createdAt?: str, positions?: int, grossTotal?: num, totalQuantity?: num, customerName?: str|null, files?: T11[] }`
- `T4` = `{ id: int, number: str, type: str, supplierId: int, date: str, lines: T21[], files: T10[], externalNumber?: str|null, createdAt?: str, ledger?: T36 }` - `T4` = `{ id: int, number: str, type: str, supplierId: int, date: str, lines: T22[], files: T11[], externalNumber?: str|null, createdAt?: str, ledger?: T37 }`
- `T5` = `{ name: str, path: str, totalSizeBytes: int, timestamp: str|null, manifest?: T6|null }` - `T5` = `{ name: str, path: str, totalSizeBytes: int, timestamp: str|null, manifest?: T6|null }`
- `T6` = `{ id: str, timestamp: str, dateKey: str, monthKey: str, dbProvider: str, files: T11, totalSizeBytes: int, durationMs: int }` - `T6` = `{ id: str, timestamp: str, dateKey: str, monthKey: str, dbProvider: str, files: T12, totalSizeBytes: int, durationMs: int }`
- `T7` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, sort?: int, enabled?: bool, clientversion?: str|null, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }` - `T7` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, sort?: int, enabled?: bool, clientversion?: str|null, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }`
- `T8` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T38|null, incoming?: { date: str, amount: num }[] }` - `T8` = `{ id: int, name: str, companyId?: int, token?: str|null, pairingpin?: str|null, pairedAt?: str|null, clientserial?: str|null, clientmodel?: str|null, clientstate?: str|null, agentversion?: str|null, lastseenAt?: str|null, updatedAt?: str, sort?: int }`
- `T9` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: { objects: T22[] }, thumb?: str|null }` - `T9` = `{ 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, incoming?: { date: str, amount: num }[] }`
- `T10` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, skeledash?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|null }` - `T10` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: { objects: T23[] }, thumb?: str|null }`
- `T11` = `{ vfs: T37, storage: T37, database: T31 }` - `T11` = `{ id: int, manifestId: int, fileId: int, sort: int, hash: str, mimetype: str, name: str, size: int, created?: str, modified?: str, skeledash?: str|null, thumbx?: int|null, thumby?: int|null, meta?: { [k]: str }|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 }` - `T12` = `{ vfs: T38, storage: T38, database: T32 }`
- `T13` = `{ id: int, company_id: int, prompt: str, max_itr: int, cron: str, created_at: str, desc?: str|null, skills?: str|null, lastagentnote?: str|null, lastrun_at?: str|null }` - `T13` = `{ 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 }`
- `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 }` - `T14` = `{ id: int, company_id: int, prompt: str, max_itr: int, cron: str, created_at: str, desc?: str|null, skills?: str|null, lastagentnote?: str|null, lastrun_at?: str|null }`
- `T15` = `{ id: int, name: str, sort: int, exclusive: bool, values: T27[], articleCount?: int }` - `T15` = `{ 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 }`
- `T16` = `{ id: int, companyuser_id: int, provider: str, model: str, created_at: str, updated_at: str, state: str, started_by?: int|null, first_message?: str|null }` - `T16` = `{ id: int, name: str, sort: int, exclusive: bool, values: T28[], articleCount?: int }`
- `T17` = `{ id: int, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num }` - `T17` = `{ id: int, companyuser_id: int, provider: str, model: str, created_at: str, updated_at: str, state: str, started_by?: int|null, first_message?: str|null }`
- `T18` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null, shippable: bool }` - `T18` = `{ id: int, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num }`
- `T19` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null, shippable?: bool }` - `T19` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null, shippable: bool }`
- `T20` = `{ id: int, manifestId: int, number: str, carrierId: int, type: str, state: str, trackingCode?: str|null, carrier?: str|null }` - `T20` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null, shippable?: bool }`
- `T21` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }` - `T21` = `{ id: int, manifestId: int, number: str, carrierId: int, type: str, state: str, trackingCode?: str|null, carrier?: str|null }`
- `T22` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }` - `T22` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }`
- `T23` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }` - `T23` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }`
- `T24` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }` - `T24` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }`
- `T25` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }` - `T25` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }`
- `T26` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }` - `T26` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }`
- `T27` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }` - `T27` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }`
- `T28` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }` - `T28` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }`
- `T29` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }` - `T29` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }`
- `T30` = `{ id: int, name: str, sort: int, hasPackages: bool, isReverse: bool }` - `T30` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }`
- `T31` = `{ name: str|null, sizeBytes: int, backedUp: bool, reason?: str|null }` - `T31` = `{ id: int, name: str, sort: int, hasPackages: bool, isReverse: bool }`
- `T32` = `{ id: int, email: str, selectedCompanyId: int|null, picture?: str }` - `T32` = `{ name: str|null, sizeBytes: int, backedUp: bool, reason?: str|null }`
- `T33` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }` - `T33` = `{ id: int, email: str, selectedCompanyId: int|null, picture?: str }`
- `T34` = `{ id: int, entityId: int, contactType: str, contact: str }` - `T34` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }`
- `T35` = `{ id: int, name: str, createdAt: str, updatedAt: str }` - `T35` = `{ id: int, entityId: int, contactType: str, contact: str }`
- `T36` = `{ totalPositions: int, completelyOnLedgerPositions: int }` - `T36` = `{ id: int, name: str, createdAt: str, updatedAt: str }`
- `T37` = `{ name: str, sizeBytes: int, sourceExists: bool }` - `T37` = `{ totalPositions: int, completelyOnLedgerPositions: int }`
- `T38` = `{ net: num, taxId: int, taxRate?: num|null }` - `T38` = `{ name: str, sizeBytes: int, sourceExists: bool }`
- `T39` = `{ net: num, taxId: int, taxRate?: num|null }`
### Routes ### Routes
@@ -219,7 +220,7 @@ _none_
- Returns: `204 void` - Returns: `204 void`
#### Get article by SKU — `GET /article/{sku}` #### Get article by SKU — `GET /article/{sku}`
- Params: `sku: str` - Params: `sku: str`
- Returns: `200 { article: { id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T26, brand: T25|null, categoryIds: int[], photos: { sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[], prices: { minmultiplier: num, net: num, taxRate: num, customergroupId: int }[], suppliers: T8[], undeliveredOrders: { orderId: int, orderNumber: str, date: str, amount: num, customerName?: str|null }[] } }` - Returns: `200 { article: { id: int, sku: str, name: str, slug: str, desc: str|null, seo: str|null, track_stock: bool, unitamount: num|null, weight: num|null, unit: T27, brand: T26|null, categoryIds: int[], photos: { sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[], prices: { minmultiplier: num, net: num, taxRate: num, customergroupId: int }[], suppliers: T9[], undeliveredOrders: { orderId: int, orderNumber: str, date: str, amount: num, customerName?: str|null }[] } }`
#### Update article by SKU — `PATCH /article/{sku}` #### Update article by SKU — `PATCH /article/{sku}`
- Params: `sku: str, body: { name?: str, desc?: str, seo?: str, unitamount?: num, unitSi?: str, track_stock?: bool }` - Params: `sku: str, body: { name?: str, desc?: str, seo?: str, unitamount?: num, unitSi?: str, track_stock?: bool }`
- Returns: `204 void` - Returns: `204 void`
@@ -294,10 +295,10 @@ _none_
- Returns: `200 { ok: bool }` - Returns: `200 { ok: bool }`
#### List article suppliers — `GET /article/{sku}/supplier` #### List article suppliers — `GET /article/{sku}/supplier`
- Params: `sku: str` - Params: `sku: str`
- Returns: `200 { suppliers: T8[] }` - Returns: `200 { suppliers: T9[] }`
#### Assign supplier to article — `POST /article/{sku}/supplier` #### Assign supplier to article — `POST /article/{sku}/supplier`
- 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 }` - 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: T8 }` - Returns: `201 { supplier: T9 }`
#### Reorder article suppliers — `PUT /article/{sku}/supplier/order` #### Reorder article suppliers — `PUT /article/{sku}/supplier/order`
- Params: `sku: str, body: { ids: int[] }` - Params: `sku: str, body: { ids: int[] }`
- Returns: `200 { ids: int[] }` - Returns: `200 { ids: int[] }`
@@ -306,7 +307,7 @@ _none_
- Returns: `204 void` - Returns: `204 void`
#### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}` #### Update article supplier assignment — `PATCH /article/{sku}/supplier/{id}`
- 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 }` - 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: T8 }` - Returns: `200 { supplier: T9 }`
#### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock` #### Set article track_stock flag by SKU — `PUT /article/{sku}/track_stock`
- Params: `sku: str, body: { track_stock: bool }` - Params: `sku: str, body: { track_stock: bool }`
- Returns: `204 void` - Returns: `204 void`
@@ -318,19 +319,19 @@ _none_
#### List attribute names — `GET /attributename` #### List attribute names — `GET /attributename`
_none_ _none_
- Returns: `200 { attributenames: T15[] }` - Returns: `200 { attributenames: T16[] }`
#### Create attribute name — `POST /attributename` #### Create attribute name — `POST /attributename`
- Params: `body: { name: str, sort?: int, exclusive?: bool }` - Params: `body: { name: str, sort?: int, exclusive?: bool }`
- Returns: `201 { attributename: T15 }` - Returns: `201 { attributename: T16 }`
#### Reorder attribute names — `PUT /attributename/order` #### Reorder attribute names — `PUT /attributename/order`
- Params: `body: { attributenameIds: int[] }` - Params: `body: { attributenameIds: int[] }`
- Returns: `200 { ok: bool }` - Returns: `200 { ok: bool }`
#### List values for an attribute — `GET /attributename/{attributeId}/value` #### List values for an attribute — `GET /attributename/{attributeId}/value`
- Params: `attributeId: int` - Params: `attributeId: int`
- Returns: `200 { values: T27[] }` - Returns: `200 { values: T28[] }`
#### Create value for an attribute — `POST /attributename/{attributeId}/value` #### Create value for an attribute — `POST /attributename/{attributeId}/value`
- Params: `attributeId: int, body: { value: str, sort?: int }` - Params: `attributeId: int, body: { value: str, sort?: int }`
- Returns: `201 { attributevalue: T27 }` - Returns: `201 { attributevalue: T28 }`
#### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order` #### Reorder values of an attribute — `PUT /attributename/{attributeId}/value/order`
- Params: `attributeId: int, body: { attributevalueIds: int[] }` - Params: `attributeId: int, body: { attributevalueIds: int[] }`
- Returns: `200 { ok: bool }` - Returns: `200 { ok: bool }`
@@ -339,7 +340,7 @@ _none_
- Returns: `200 { ok: bool }` - Returns: `200 { ok: bool }`
#### Update attribute name — `PATCH /attributename/{id}` #### Update attribute name — `PATCH /attributename/{id}`
- Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }`
- Returns: `200 { attributename: T15 }` - Returns: `200 { attributename: T16 }`
### Section: /attributevalue ### Section: /attributevalue
@@ -348,28 +349,28 @@ _none_
- Returns: `200 { ok: bool }` - Returns: `200 { ok: bool }`
#### Update attribute value — `PATCH /attributevalue/{id}` #### Update attribute value — `PATCH /attributevalue/{id}`
- Params: `id: int, body: { value?: str, sort?: int }` - Params: `id: int, body: { value?: str, sort?: int }`
- Returns: `200 { attributevalue: T27 }` - Returns: `200 { attributevalue: T28 }`
#### Delete attribute value image — `DELETE /attributevalue/{id}/image` #### Delete attribute value image — `DELETE /attributevalue/{id}/image`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T33[] } }` - Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T34[] } }`
#### Add or replace attribute value image — `POST /attributevalue/{id}/image` #### Add or replace attribute value image — `POST /attributevalue/{id}/image`
- Params: `id: int, image/avif` - Params: `id: int, image/avif`
- Returns: `201 { attributevalue: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T33[] } }` - Returns: `201 { attributevalue: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T34[] } }`
### Section: /auth ### Section: /auth
#### Login with Google ID token — `POST /auth/login` #### Login with Google ID token — `POST /auth/login`
- Params: `body: { idToken: str }` - Params: `body: { idToken: str }`
- Returns: `200 { message: str, user: T32 }` - Returns: `200 { message: str, user: T33 }`
#### Logout — `POST /auth/logout` #### Logout — `POST /auth/logout`
_none_ _none_
- Returns: `200 { message: str }` - Returns: `200 { message: str }`
#### Current user — `GET /auth/me` #### Current user — `GET /auth/me`
_none_ _none_
- Returns: `200 { user: T32, googleClientId: str, schemaVersion: int }` - Returns: `200 { user: T33, googleClientId: str, schemaVersion: int, vhost: bool, vhostSettings: str }`
#### Set selected company — `PUT /auth/selected-company` #### Set selected company — `PUT /auth/selected-company`
- Params: `body: { companyId: int }` - Params: `body: { companyId: int }`
- Returns: `200 { user: T32 }` - Returns: `200 { user: T33 }`
### Section: /backup ### Section: /backup
@@ -390,7 +391,7 @@ _none_
- Returns: `200 { brands: { id: int, name: str, articleCount: int, pics: { fullHash: str, thumbW: int, thumbH: int, thumbHash: str, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null }[], gsprpinfo?: str|null }[], total: int, offset: int, limit: int }` - Returns: `200 { brands: { id: int, name: str, articleCount: int, pics: { fullHash: str, thumbW: int, thumbH: int, thumbHash: str, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null }[], gsprpinfo?: str|null }[], total: int, offset: int, limit: int }`
#### Create brand — `POST /brand` #### Create brand — `POST /brand`
- Params: `body: { name: str, gsprpinfo?: str }` - Params: `body: { name: str, gsprpinfo?: str }`
- Returns: `201 { brand: { id: int, name: str, pics: T33[], gsprpinfo?: str|null } }` - Returns: `201 { brand: { id: int, name: str, pics: T34[], gsprpinfo?: str|null } }`
#### Delete brand — `DELETE /brand/{id}` #### Delete brand — `DELETE /brand/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `204 void` - Returns: `204 void`
@@ -399,19 +400,19 @@ _none_
- Returns: `200 { brand: { id: int, name: str, touchedArticleCount: int, gsprpinfo?: str|null } }` - Returns: `200 { brand: { id: int, name: str, touchedArticleCount: int, gsprpinfo?: str|null } }`
#### Delete brand image — `DELETE /brand/{id}/image` #### Delete brand image — `DELETE /brand/{id}/image`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T33[] } }` - Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T34[] } }`
#### Add or replace brand image — `POST /brand/{id}/image` #### Add or replace brand image — `POST /brand/{id}/image`
- Params: `id: int, image/avif` - Params: `id: int, image/avif`
- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T33[] } }` - Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T34[] } }`
### Section: /carrier ### Section: /carrier
#### List carriers — `GET /carrier` #### List carriers — `GET /carrier`
_none_ _none_
- Returns: `200 { carriers: T30[] }` - Returns: `200 { carriers: T31[] }`
#### Create carrier — `POST /carrier` #### Create carrier — `POST /carrier`
- Params: `body: { name: str, sort?: int, hasPackages?: bool, isReverse?: bool }` - Params: `body: { name: str, sort?: int, hasPackages?: bool, isReverse?: bool }`
- Returns: `201 { carrier: T30 }` - Returns: `201 { carrier: T31 }`
#### Reorder carriers — `PUT /carrier/order` #### Reorder carriers — `PUT /carrier/order`
- Params: `body: { carrierIds: int[] }` - Params: `body: { carrierIds: int[] }`
- Returns: `200 { carrierIds: int[] }` - Returns: `200 { carrierIds: int[] }`
@@ -420,10 +421,10 @@ _none_
- Returns: `204 void` - Returns: `204 void`
#### Get carrier by ID — `GET /carrier/{id}` #### Get carrier by ID — `GET /carrier/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { carrier: T30 }` - Returns: `200 { carrier: T31 }`
#### Update carrier — `PATCH /carrier/{id}` #### Update carrier — `PATCH /carrier/{id}`
- Params: `id: int, body: { name?: str, sort?: int, hasPackages?: bool, isReverse?: bool }` - Params: `id: int, body: { name?: str, sort?: int, hasPackages?: bool, isReverse?: bool }`
- Returns: `200 { carrier: T30 }` - Returns: `200 { carrier: T31 }`
### Section: /category ### Section: /category
@@ -447,7 +448,7 @@ _none_
- Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }` - Returns: `200 { category: { id: int, name: str, slug: str, parentId: int, sort: int } }`
#### Articles in category — `GET /category/{id}/articles` #### Articles in category — `GET /category/{id}/articles`
- Params: `id: int, query: includeChildren?: bool` - Params: `id: int, query: includeChildren?: bool`
- 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, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null, brand?: T25|null, articlewarehousesummary?: { id: int, articleId: int, warehouseId: int, stock: num, article_id?: int, warehouse_id?: int, warehouseName?: str|null }[], warehouses?: { id: int, name: str, stock: num }[] }[] }` - 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, smallThumbW?: int|null, smallThumbH?: int|null, smallThumbBinary?: str|null, brand?: T26|null, articlewarehousesummary?: { id: int, articleId: int, warehouseId: int, stock: num, article_id?: int, warehouse_id?: int, warehouseName?: str|null }[], warehouses?: { id: int, name: str, stock: num }[] }[] }`
#### List category attribute values — `GET /category/{id}/attributevalue` #### List category attribute values — `GET /category/{id}/attributevalue`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }` - Returns: `200 { attributevalues: { id: int, attributeId: int, attributeName: str, attributeValueId: int, value: str, sort: int, categoryId?: int, exclusive?: bool }[] }`
@@ -477,10 +478,10 @@ _none_
#### List companies — `GET /company` #### List companies — `GET /company`
_none_ _none_
- Returns: `200 { companies: T17[] }` - Returns: `200 { companies: T18[] }`
#### Create company — `POST /company` #### Create company — `POST /company`
- Params: `body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }` - Params: `body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }`
- Returns: `201 { company: T17, user: T32 }` - Returns: `201 { company: T18, user: T33 }`
#### Export company catalog as zip archive (SSE) — `GET /company/export` #### Export company catalog as zip archive (SSE) — `GET /company/export`
- Params: `query: stream?: str` - Params: `query: stream?: str`
- Returns: `200 SSE` - Returns: `200 SSE`
@@ -510,7 +511,7 @@ _none_
- Returns: `204 void` - Returns: `204 void`
#### Update company — `PUT /company/{id}` #### Update company — `PUT /company/{id}`
- Params: `id: int, body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }` - Params: `id: int, body: { iso639: str, name: str, taxrate: num, reducedtaxrate: num }`
- Returns: `200 { company: T17 }` - Returns: `200 { company: T18 }`
### Section: /customergroup ### Section: /customergroup
@@ -534,7 +535,7 @@ _none_
#### List deliveries — `GET /delivery` #### List deliveries — `GET /delivery`
- Params: `query: offset?: int, limit?: int, search?: str, onlyUnbooked?: bool, supplierId?: int, seekMonths?: int` - Params: `query: offset?: int, limit?: int, search?: str, onlyUnbooked?: bool, supplierId?: int, seekMonths?: int`
- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T36 }[], total: int, unfilteredTotal: int, offset: int, limit: int }` - Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T37 }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
#### Create delivery — `POST /delivery` #### Create delivery — `POST /delivery`
- Params: `body: { externalNumber: str, date: str, supplierId: int, lines: { amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }[], 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, supplierId: int, lines: { amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }`
- Returns: `201 { delivery: T4 }` - Returns: `201 { delivery: T4 }`
@@ -555,52 +556,52 @@ _none_
- Returns: `200 str` - Returns: `200 str`
#### Generate and attach delivery note PDF (goods receipt) — `POST /delivery/{id}/pdf` #### Generate and attach delivery note PDF (goods receipt) — `POST /delivery/{id}/pdf`
- Params: `id: int` - Params: `id: int`
- Returns: `201 { attachment: T10, fileUrl: str }` - Returns: `201 { attachment: T11, fileUrl: str }`
### Section: /entity ### Section: /entity
#### List entities — `GET /entity` #### List entities — `GET /entity`
- Params: `query: customergroupId?: int, role?: str` - Params: `query: customergroupId?: int, role?: str`
- Returns: `200 { entities: T24[] }` - Returns: `200 { entities: T25[] }`
#### Create entity — `POST /entity` #### Create entity — `POST /entity`
- Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }` - Params: `body: { customergroupId: int, name: str, firstName?: str|null, number?: str }`
- Returns: `201 { entity: T24 }` - Returns: `201 { entity: T25 }`
#### Peek next customer number — `GET /entity/next-number` #### Peek next customer number — `GET /entity/next-number`
_none_ _none_
- Returns: `200 { number: str }` - Returns: `200 { number: str }`
#### List entity addresses — `GET /entity/{entityId}/address` #### List entity addresses — `GET /entity/{entityId}/address`
- Params: `entityId: int` - Params: `entityId: int`
- Returns: `200 { addresses: T12[] }` - Returns: `200 { addresses: T13[] }`
#### Create entity address — `POST /entity/{entityId}/address` #### Create entity address — `POST /entity/{entityId}/address`
- 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 }` - 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 }` - Returns: `201 { address: T13 }`
#### Delete entity address — `DELETE /entity/{entityId}/address/{id}` #### Delete entity address — `DELETE /entity/{entityId}/address/{id}`
- Params: `entityId: int, id: int` - Params: `entityId: int, id: int`
- Returns: `204 void` - Returns: `204 void`
#### Update entity address — `PATCH /entity/{entityId}/address/{id}` #### Update entity address — `PATCH /entity/{entityId}/address/{id}`
- 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 }` - 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 }` - Returns: `200 { address: T13 }`
#### List entity contacts — `GET /entity/{entityId}/contact` #### List entity contacts — `GET /entity/{entityId}/contact`
- Params: `entityId: int` - Params: `entityId: int`
- Returns: `200 { contacts: T34[] }` - Returns: `200 { contacts: T35[] }`
#### Create entity contact — `POST /entity/{entityId}/contact` #### Create entity contact — `POST /entity/{entityId}/contact`
- Params: `entityId: int, body: { contactType: str, contact: str }` - Params: `entityId: int, body: { contactType: str, contact: str }`
- Returns: `201 { contact: T34 }` - Returns: `201 { contact: T35 }`
#### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}` #### Delete entity contact — `DELETE /entity/{entityId}/contact/{id}`
- Params: `entityId: int, id: int` - Params: `entityId: int, id: int`
- Returns: `204 void` - Returns: `204 void`
#### Update entity contact — `PATCH /entity/{entityId}/contact/{id}` #### Update entity contact — `PATCH /entity/{entityId}/contact/{id}`
- Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }` - Params: `entityId: int, id: int, body: { contactType?: str, contact?: str }`
- Returns: `200 { contact: T34 }` - Returns: `200 { contact: T35 }`
#### Delete entity — `DELETE /entity/{id}` #### Delete entity — `DELETE /entity/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `204 void` - Returns: `204 void`
#### Get entity — `GET /entity/{id}` #### Get entity — `GET /entity/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { entity: T24 }` - Returns: `200 { entity: T25 }`
#### Update entity — `PATCH /entity/{id}` #### Update entity — `PATCH /entity/{id}`
- Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }` - Params: `id: int, body: { name?: str, firstName?: str|null, number?: str|null }`
- Returns: `200 { entity: T24 }` - Returns: `200 { entity: T25 }`
### Section: /events ### Section: /events
@@ -648,35 +649,35 @@ _none_
_none_ _none_
- Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }` - Returns: `200 { labeltemplates: { id: int, name: str, width: num, height: num, dpi: int, type: str, thumb?: str|null }[] }`
#### Create label template — `POST /labeltemplate` #### Create label template — `POST /labeltemplate`
- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: { objects: T22[] } }` - Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: { objects: T23[] } }`
- Returns: `201 { labeltemplate: T9 }` - Returns: `201 { labeltemplate: T10 }`
#### Delete label template — `DELETE /labeltemplate/{id}` #### Delete label template — `DELETE /labeltemplate/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `204 void` - Returns: `204 void`
#### Get label template by ID — `GET /labeltemplate/{id}` #### Get label template by ID — `GET /labeltemplate/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { labeltemplate: T9 }` - Returns: `200 { labeltemplate: T10 }`
#### Update label template (partial) — `PATCH /labeltemplate/{id}` #### Update label template (partial) — `PATCH /labeltemplate/{id}`
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T22[] } }` - Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T23[] } }`
- Returns: `200 { labeltemplate: T9 }` - Returns: `200 { labeltemplate: T10 }`
#### Update label template — `PUT /labeltemplate/{id}` #### Update label template — `PUT /labeltemplate/{id}`
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T22[] } }` - Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T23[] } }`
- Returns: `200 { labeltemplate: T9 }` - Returns: `200 { labeltemplate: T10 }`
### Section: /ledger ### Section: /ledger
#### Query ledger by manifest article ID or SKU — `GET /ledger` #### Query ledger by manifest article ID or SKU — `GET /ledger`
- Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str` - Params: `query: manifestarticle_id?: int, sku?: str, transaction_type?: str`
- Returns: `200 { ledger: T14[] }` - Returns: `200 { ledger: T15[] }`
#### Query company-wide ledger history — `GET /ledger/history` #### Query company-wide ledger history — `GET /ledger/history`
- Params: `query: offset?: int, limit?: int, search?: str, seekMonths?: int` - Params: `query: offset?: int, limit?: int, search?: str, seekMonths?: int`
- Returns: `200 { ledger: { id: int, date: str, transaction_type: str, transaction_text: str, sku?: str|null, name?: str|null, amount?: num }[], total: int, unfilteredTotal: int, offset: int, limit: int }` - Returns: `200 { ledger: { id: int, date: str, transaction_type: str, transaction_text: str, sku?: str|null, name?: str|null, amount?: num }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
#### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}` #### Query ledger by manifest article ID — `GET /ledger/manifestarticle/{manifestarticleId}`
- Params: `manifestarticleId: int, query: transaction_type?: str` - Params: `manifestarticleId: int, query: transaction_type?: str`
- Returns: `200 { ledger: T14[] }` - Returns: `200 { ledger: T15[] }`
#### Query ledger by article SKU — `GET /ledger/sku/{sku}` #### Query ledger by article SKU — `GET /ledger/sku/{sku}`
- Params: `sku: str, query: transaction_type?: str` - Params: `sku: str, query: transaction_type?: str`
- Returns: `200 { ledger: T14[] }` - Returns: `200 { ledger: T15[] }`
### Section: /mcp ### Section: /mcp
@@ -706,16 +707,16 @@ _none_
#### List conversations — `GET /modelconversation` #### List conversations — `GET /modelconversation`
_none_ _none_
- Returns: `200 { conversations: T16[] }` - Returns: `200 { conversations: T17[] }`
#### Create conversation — `POST /modelconversation` #### Create conversation — `POST /modelconversation`
- Params: `body: { provider: str, model: str }` - Params: `body: { provider: str, model: str }`
- Returns: `201 { conversation: T16 }` - Returns: `201 { conversation: T17 }`
#### Delete conversation — `DELETE /modelconversation/{id}` #### Delete conversation — `DELETE /modelconversation/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `204 void` - Returns: `204 void`
#### Get conversation — `GET /modelconversation/{id}` #### Get conversation — `GET /modelconversation/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { conversation: T16 }` - Returns: `200 { conversation: T17 }`
#### List conversation messages — `GET /modelconversation/{id}/message` #### List conversation messages — `GET /modelconversation/{id}/message`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { messages: { id: int, conversation_id: int, created_at: str, role: str, message: str }[] }` - Returns: `200 { messages: { id: int, conversation_id: int, created_at: str, role: str, message: str }[] }`
@@ -756,7 +757,7 @@ _none_
- Params: `query: offset?: int, limit?: int, search?: str, onlyUnpaid?: bool, onlyUndelivered?: bool, seekMonths?: int` - Params: `query: offset?: int, limit?: int, search?: str, onlyUnpaid?: bool, onlyUndelivered?: bool, seekMonths?: int`
- Returns: `200 { orders: T1[], total: int, unfilteredTotal: int, offset: int, limit: int }` - Returns: `200 { orders: T1[], total: int, unfilteredTotal: int, offset: int, limit: int }`
#### Create order — `POST /order` #### Create order — `POST /order`
- Params: `body: { number: str, date: str, entityId: int, lines: T19[], externalNumber?: str|null }` - Params: `body: { number: str, date: str, entityId: int, lines: T20[], externalNumber?: str|null }`
- Returns: `201 { order: T1 }` - Returns: `201 { order: T1 }`
#### Delete order — `DELETE /order/{id}` #### Delete order — `DELETE /order/{id}`
- Params: `id: int` - Params: `id: int`
@@ -765,7 +766,7 @@ _none_
- Params: `id: int` - Params: `id: int`
- Returns: `200 { order: T1 }` - Returns: `200 { order: T1 }`
#### Update order — `PATCH /order/{id}` #### Update order — `PATCH /order/{id}`
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T19[] }` - Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T20[] }`
- Returns: `200 { order: T1 }` - Returns: `200 { order: T1 }`
#### Delete order file attachment — `DELETE /order/{id}/file/{fileId}` #### Delete order file attachment — `DELETE /order/{id}/file/{fileId}`
- Params: `id: int, fileId: int` - Params: `id: int, fileId: int`
@@ -775,13 +776,13 @@ _none_
- Returns: `200 str` - Returns: `200 str`
#### List payments of an order — `GET /order/{id}/payment` #### List payments of an order — `GET /order/{id}/payment`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { payments: T29[] }` - Returns: `200 { payments: T30[] }`
#### Add payment to an order — `POST /order/{id}/payment` #### Add payment to an order — `POST /order/{id}/payment`
- Params: `id: int, body: { accountId: int, type: str, amount: num }` - Params: `id: int, body: { accountId: int, type: str, amount: num }`
- Returns: `201 { payment: T29 }` - Returns: `201 { payment: T30 }`
#### Generate and attach order PDF — `POST /order/{id}/pdf` #### Generate and attach order PDF — `POST /order/{id}/pdf`
- Params: `id: int` - Params: `id: int`
- Returns: `201 { attachment: T10, fileUrl: str }` - Returns: `201 { attachment: T11, fileUrl: str }`
#### List shipments of an order — `GET /order/{id}/shipment` #### List shipments of an order — `GET /order/{id}/shipment`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { shipments: T2[] }` - Returns: `200 { shipments: T2[] }`
@@ -790,10 +791,10 @@ _none_
- Returns: `201 { shipment: T2 }` - Returns: `201 { shipment: T2 }`
#### List shippings of an order — `GET /order/{id}/shipping` #### List shippings of an order — `GET /order/{id}/shipping`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { shippings: T20[] }` - Returns: `200 { shippings: T21[] }`
#### Create shipping for an order — `POST /order/{id}/shipping` #### Create shipping for an order — `POST /order/{id}/shipping`
- Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }` - Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }`
- Returns: `201 { shipping: T20 }` - Returns: `201 { shipping: T21 }`
### Section: /package ### Section: /package
@@ -838,10 +839,10 @@ _none_
#### List printers — `GET /printer` #### List printers — `GET /printer`
_none_ _none_
- Returns: `200 { printers: T23[] }` - Returns: `200 { printers: T24[] }`
#### Create printer — `POST /printer` #### Create printer — `POST /printer`
- Params: `body: { address: str, type: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` - Params: `body: { address: str, type: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }`
- Returns: `201 { printer: T23 }` - Returns: `201 { printer: T24 }`
#### Reorder printers — `PUT /printer/order` #### Reorder printers — `PUT /printer/order`
- Params: `body: { printerIds: int[] }` - Params: `body: { printerIds: int[] }`
- Returns: `200 { printerIds: int[] }` - Returns: `200 { printerIds: int[] }`
@@ -850,22 +851,37 @@ _none_
- Returns: `204 void` - Returns: `204 void`
#### Get printer by ID — `GET /printer/{id}` #### Get printer by ID — `GET /printer/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { printer: T23 }` - Returns: `200 { printer: T24 }`
#### Update printer (partial) — `PATCH /printer/{id}` #### Update printer (partial) — `PATCH /printer/{id}`
- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` - Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }`
- Returns: `200 { printer: T23 }` - Returns: `200 { printer: T24 }`
#### Update printer — `PUT /printer/{id}` #### Update printer — `PUT /printer/{id}`
- Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }` - Params: `id: int, body: { address?: str, type?: str, model?: str|null, serial?: str|null, sort?: int, desc?: str|null }`
- Returns: `200 { printer: T23 }` - Returns: `200 { printer: T24 }`
### Section: /printeragent
#### List printer agents — `GET /printeragent`
_none_
- Returns: `200 { printeragents: T8[] }`
#### Create printer agent — `PUT /printeragent`
- Params: `body: { name: str, sort?: int }`
- Returns: `200 { printeragent: T8 }`
#### Delete printer agent — `DELETE /printeragent/{id}`
- Params: `id: int`
- Returns: `204 void`
#### Update printer agent — `PATCH /printeragent/{id}`
- Params: `id: int, body: { name?: str, sort?: int }`
- Returns: `200 { printeragent: T8 }`
### Section: /propertyname ### Section: /propertyname
#### List property names — `GET /propertyname` #### List property names — `GET /propertyname`
_none_ _none_
- Returns: `200 { propertynames: T28[] }` - Returns: `200 { propertynames: T29[] }`
#### Create property name — `POST /propertyname` #### Create property name — `POST /propertyname`
- Params: `body: { name: str, sort?: int, exclusive?: bool }` - Params: `body: { name: str, sort?: int, exclusive?: bool }`
- Returns: `201 { propertyname: T28 }` - Returns: `201 { propertyname: T29 }`
#### Reorder property names — `PUT /propertyname/order` #### Reorder property names — `PUT /propertyname/order`
- Params: `body: { propertynameIds: int[] }` - Params: `body: { propertynameIds: int[] }`
- Returns: `200 { ok: bool }` - Returns: `200 { ok: bool }`
@@ -874,7 +890,7 @@ _none_
- Returns: `200 { ok: bool }` - Returns: `200 { ok: bool }`
#### Update property name — `PATCH /propertyname/{id}` #### Update property name — `PATCH /propertyname/{id}`
- Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }` - Params: `id: int, body: { name?: str, sort?: int, exclusive?: bool }`
- Returns: `200 { propertyname: T28 }` - Returns: `200 { propertyname: T29 }`
### Section: /public ### Section: /public
@@ -886,7 +902,7 @@ _none_
- 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 }[] }` - 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}` #### Get public article detail — `GET /public/{companyId}/articles/{sku}`
- Params: `companyId: int, sku: str` - Params: `companyId: int, sku: str`
- Returns: `200 { article: { id: int, sku: str, name: str, slug: str, description: str, unitamount: num|null, weight: num|null, trackStock: bool, unit: T26|null, brand: T25|null, price: { minmultiplier: num, net: num|null, taxRate: num|null, gross: num|null }, prices: { minmultiplier: num, net: num|null, taxRate: num|null, gross: num|null }[], categories: { id: int, name: str, slug: str }[], photos: { sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[], isParent: bool, parentSku: str|null, variants: { id: int, sku: str, sort: int, name: str, slug: str, attributeValueId: int|null, attributeValue: str|null, unitamount: num|null, weight: num|null, trackStock: bool }[] } }` - Returns: `200 { article: { id: int, sku: str, name: str, slug: str, description: str, unitamount: num|null, weight: num|null, trackStock: bool, unit: T27|null, brand: T26|null, price: { minmultiplier: num, net: num|null, taxRate: num|null, gross: num|null }, prices: { minmultiplier: num, net: num|null, taxRate: num|null, gross: num|null }[], categories: { id: int, name: str, slug: str }[], photos: { sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }[], eans: str[], isParent: bool, parentSku: str|null, variants: { id: int, sku: str, sort: int, name: str, slug: str, attributeValueId: int|null, attributeValue: str|null, unitamount: num|null, weight: num|null, trackStock: bool }[] } }`
#### List public categories — `GET /public/{companyId}/categories` #### List public categories — `GET /public/{companyId}/categories`
- Params: `companyId: int` - Params: `companyId: int`
- 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 }[] }` - 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 }[] }`
@@ -897,7 +913,7 @@ _none_
- Params: `query: offset?: int, limit?: int, search?: str, seekMonths?: int` - Params: `query: offset?: int, limit?: int, search?: str, seekMonths?: int`
- Returns: `200 { quotes: T3[], total: int, unfilteredTotal: int, offset: int, limit: int }` - Returns: `200 { quotes: T3[], total: int, unfilteredTotal: int, offset: int, limit: int }`
#### Create quote — `POST /quote` #### Create quote — `POST /quote`
- Params: `body: { number: str, date: str, lines: T19[], entityId?: int|null, externalNumber?: str|null }` - Params: `body: { number: str, date: str, lines: T20[], entityId?: int|null, externalNumber?: str|null }`
- Returns: `201 { quote: T3 }` - Returns: `201 { quote: T3 }`
#### Delete quote — `DELETE /quote/{id}` #### Delete quote — `DELETE /quote/{id}`
- Params: `id: int` - Params: `id: int`
@@ -906,7 +922,7 @@ _none_
- Params: `id: int` - Params: `id: int`
- Returns: `200 { quote: T3 }` - Returns: `200 { quote: T3 }`
#### Update quote — `PATCH /quote/{id}` #### Update quote — `PATCH /quote/{id}`
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T19[] }` - Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T20[] }`
- Returns: `200 { quote: T3 }` - Returns: `200 { quote: T3 }`
#### Delete quote file attachment — `DELETE /quote/{id}/file/{fileId}` #### Delete quote file attachment — `DELETE /quote/{id}/file/{fileId}`
- Params: `id: int, fileId: int` - Params: `id: int, fileId: int`
@@ -915,11 +931,11 @@ _none_
- Params: `id: int, fileId: int, query: download?: bool` - Params: `id: int, fileId: int, query: download?: bool`
- Returns: `200 str` - Returns: `200 str`
#### Create order from quote — `POST /quote/{id}/order` #### Create order from quote — `POST /quote/{id}/order`
- Params: `id: int, body?: { number?: str, date?: str, createdAt?: str, entityId?: int|null, externalNumber?: str|null, lines?: T19[] }` - Params: `id: int, body?: { number?: str, date?: str, createdAt?: str, entityId?: int|null, externalNumber?: str|null, lines?: T20[] }`
- Returns: `201 { order: T1 }` - Returns: `201 { order: T1 }`
#### Generate and attach quote PDF — `POST /quote/{id}/pdf` #### Generate and attach quote PDF — `POST /quote/{id}/pdf`
- Params: `id: int` - Params: `id: int`
- Returns: `201 { attachment: T10, fileUrl: str }` - Returns: `201 { attachment: T11, fileUrl: str }`
### Section: /retoure ### Section: /retoure
@@ -1006,13 +1022,13 @@ _none_
- Returns: `200 str` - Returns: `200 str`
#### Generate and attach shipment PDF (delivery note) — `POST /shipment/{id}/pdf` #### Generate and attach shipment PDF (delivery note) — `POST /shipment/{id}/pdf`
- Params: `id: int` - Params: `id: int`
- Returns: `201 { attachment: T10, fileUrl: str }` - Returns: `201 { attachment: T11, fileUrl: str }`
#### List packages of a shipment — `GET /shipment/{id}/shipping` #### List packages of a shipment — `GET /shipment/{id}/shipping`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { shippings: T20[] }` - Returns: `200 { shippings: T21[] }`
#### Create package for a shipment — `POST /shipment/{id}/shipping` #### Create package for a shipment — `POST /shipment/{id}/shipping`
- Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }` - Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }`
- Returns: `201 { shipping: T20 }` - Returns: `201 { shipping: T21 }`
### Section: /shipping ### Section: /shipping
@@ -1021,7 +1037,7 @@ _none_
- Returns: `204 void` - Returns: `204 void`
#### Update shipping — `PATCH /shipping/{id}` #### Update shipping — `PATCH /shipping/{id}`
- Params: `id: int, body: { type?: str, state?: str, trackingCode?: str, carrierId?: int, warehouseId?: int }` - Params: `id: int, body: { type?: str, state?: str, trackingCode?: str, carrierId?: int, warehouseId?: int }`
- Returns: `200 { shipping: T20 }` - Returns: `200 { shipping: T21 }`
#### List packages of a shipping — `GET /shipping/{id}/package` #### List packages of a shipping — `GET /shipping/{id}/package`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }` - Returns: `200 { packages: { id: int, shippingId: int, entityaddressId: int, number: str, type: str, state: str, details?: str|null }[] }`
@@ -1042,7 +1058,7 @@ _none_
- Returns: `200` - Returns: `200`
#### Adjust stock manually — `POST /stock/adjust` #### Adjust stock manually — `POST /stock/adjust`
- Params: `body: { sku?: str, warehouseId?: int, amount?: num, text?: str|null, lines?: { sku: str, amount: num, warehouseId?: int, text?: str }[] }` - 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[] }` - 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: T15[] }`
#### Ship shipment manifest item(s) — `POST /stock/ship` #### Ship shipment manifest item(s) — `POST /stock/ship`
- Params: `body: { manifestarticleId?: int, warehouseId?: int, items?: { manifestarticleId: int, warehouseId?: int }[] }` - Params: `body: { manifestarticleId?: int, warehouseId?: int, items?: { manifestarticleId: int, warehouseId?: int }[] }`
- Returns: `200` - Returns: `200`
@@ -1051,40 +1067,40 @@ _none_
#### List subagent schedules — `GET /subagentschedule` #### List subagent schedules — `GET /subagentschedule`
_none_ _none_
- Returns: `200 { schedules: T13[] }` - Returns: `200 { schedules: T14[] }`
#### Create subagent schedule — `POST /subagentschedule` #### Create subagent schedule — `POST /subagentschedule`
- Params: `body: { prompt: str, cron: str, desc?: str|null, skills?: str|null, lastagentnote?: str|null, max_itr?: int }` - Params: `body: { prompt: str, cron: str, desc?: str|null, skills?: str|null, lastagentnote?: str|null, max_itr?: int }`
- Returns: `201 { schedule: T13 }` - Returns: `201 { schedule: T14 }`
#### Delete subagent schedule — `DELETE /subagentschedule/{id}` #### Delete subagent schedule — `DELETE /subagentschedule/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `204 void` - Returns: `204 void`
#### Get subagent schedule by ID — `GET /subagentschedule/{id}` #### Get subagent schedule by ID — `GET /subagentschedule/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `200 { schedule: T13 }` - Returns: `200 { schedule: T14 }`
#### Update subagent schedule — `PUT /subagentschedule/{id}` #### Update subagent schedule — `PUT /subagentschedule/{id}`
- Params: `id: int, body: { prompt?: str, cron?: str, desc?: str|null, skills?: str|null, lastagentnote?: str|null, max_itr?: int, lastrun_at?: str|null }` - Params: `id: int, body: { prompt?: str, cron?: str, desc?: str|null, skills?: str|null, lastagentnote?: str|null, max_itr?: int, lastrun_at?: str|null }`
- Returns: `200 { schedule: T13 }` - Returns: `200 { schedule: T14 }`
### Section: /supplier ### Section: /supplier
#### List suppliers — `GET /supplier` #### List suppliers — `GET /supplier`
_none_ _none_
- Returns: `200 { suppliers: T35[] }` - Returns: `200 { suppliers: T36[] }`
#### Create supplier — `POST /supplier` #### Create supplier — `POST /supplier`
- Params: `body: { name: str }` - Params: `body: { name: str }`
- Returns: `201 { supplier: T35 }` - Returns: `201 { supplier: T36 }`
#### Delete supplier — `DELETE /supplier/{id}` #### Delete supplier — `DELETE /supplier/{id}`
- Params: `id: int` - Params: `id: int`
- Returns: `204 void` - Returns: `204 void`
#### Update supplier — `PATCH /supplier/{id}` #### Update supplier — `PATCH /supplier/{id}`
- Params: `id: int, body: { name: str }` - Params: `id: int, body: { name: str }`
- Returns: `200 { supplier: T35 }` - Returns: `200 { supplier: T36 }`
#### List supplier articles by SKUs — `GET /supplier/{id}/articles` #### List supplier articles by SKUs — `GET /supplier/{id}/articles`
- Params: `id: int, query: skus?: str` - Params: `id: int, query: skus?: 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?: T38|null, incoming?: { date: str, amount: num }[] }[] }` - 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, incoming?: { date: str, amount: num }[] }[] }`
#### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles` #### Query supplier articles by SKUs (bulk) — `POST /supplier/{id}/articles`
- Params: `id: int, body?: { skus?: str[] }` - Params: `id: int, body?: { skus?: 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?: T38|null, incoming?: { date: str, amount: num }[] }[] }` - 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, incoming?: { date: str, amount: num }[] }[] }`
### Section: /thumb ### Section: /thumb
@@ -1099,10 +1115,10 @@ _none_
- Returns: `204 void` - Returns: `204 void`
#### List units — `GET /unit` #### List units — `GET /unit`
_none_ _none_
- Returns: `200 { units: T26[] }` - Returns: `200 { units: T27[] }`
#### Upsert unit — `PUT /unit` #### Upsert unit — `PUT /unit`
- Params: `body: { si: str, basesi: str, basesifactor: num, symbol: str, name: str }` - Params: `body: { si: str, basesi: str, basesifactor: num, symbol: str, name: str }`
- Returns: `200 { unit: T26 }` - Returns: `200 { unit: T27 }`
### Section: /v1 ### Section: /v1

View File

@@ -15,6 +15,7 @@ export { number } from './number.js';
export { order } from './order.js'; export { order } from './order.js';
export { posterminal } from './posterminal.js'; export { posterminal } from './posterminal.js';
export { printer } from './printer.js'; export { printer } from './printer.js';
export { printeragent } from './printeragent.js';
export { propertyname } from './propertyname.js'; export { propertyname } from './propertyname.js';
export { quote } from './quote.js'; export { quote } from './quote.js';
export { retoure } from './retoure.js'; export { retoure } from './retoure.js';

57
src/api/printeragent.js Normal file
View File

@@ -0,0 +1,57 @@
import { http } from './http.js';
export const printeragent = {
/**
* GET /api/printeragent → { printeragents: Printeragent[] }
* List all printer agents for the selected company, sorted by sort ASC, id ASC.
*/
list() {
return http('/api/printeragent');
},
/**
* PUT /api/printeragent
* Body: { name: string, sort?: number }
* Creates a new printer agent (auto-generates 6-digit pairingpin).
* → 200 { printeragent: Printeragent }
*/
create({ name, sort } = {}) {
const body = { name };
if (sort !== undefined && sort !== null && sort !== '') {
body.sort = Number(sort);
}
return http('/api/printeragent', {
method: 'PUT',
body,
});
},
/**
* PATCH /api/printeragent/:id
* Body: { name?: string, sort?: number }
* Updates name, sort, or both.
* → 200 { printeragent: Printeragent }
*/
update(id, patch = {}) {
const body = {};
if (patch.name !== undefined) body.name = patch.name;
if (patch.sort !== undefined && patch.sort !== null && patch.sort !== '') {
body.sort = Number(patch.sort);
}
return http(`/api/printeragent/${id}`, {
method: 'PATCH',
body,
});
},
/**
* DELETE /api/printeragent/:id → 204 No Content
* Permanently deletes agent.
*/
delete(id) {
return http(`/api/printeragent/${id}`, {
method: 'DELETE',
});
},
};

View File

@@ -25,6 +25,7 @@ import {
company as companyApi, company as companyApi,
number as numberApi, number as numberApi,
posterminal as posterminalApi, posterminal as posterminalApi,
printeragent as printeragentApi,
supplier as supplierApi, supplier as supplierApi,
unit as unitApi, unit as unitApi,
warehouse as warehouseApi, warehouse as warehouseApi,
@@ -168,6 +169,15 @@ const sortTerminals = (terminals) => {
}); });
}; };
const sortPrinterAgents = (agents) => {
if (!Array.isArray(agents)) return [];
return [...agents].sort((a, b) => {
const sDiff = (Number(a.sort) || 0) - (Number(b.sort) || 0);
if (sDiff !== 0) return sDiff;
return (Number(a.id) || 0) - (Number(b.id) || 0);
});
};
const sortAccounts = (items) => const sortAccounts = (items) =>
[...items].sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''), 'de')); [...items].sort((a, b) => String(a.name ?? '').localeCompare(String(b.name ?? ''), 'de'));
@@ -237,6 +247,12 @@ const initialState = {
terminalsStatus: 'idle', terminalsStatus: 'idle',
terminalsError: null, terminalsError: null,
// Printer agents
printeragents: [],
printeragentsCompanyId: null,
printeragentsStatus: 'idle',
printeragentsError: null,
// 4. Suppliers // 4. Suppliers
suppliers: [], suppliers: [],
articlesBySupplier: {}, articlesBySupplier: {},
@@ -303,6 +319,7 @@ let companyAutomat;
let inflightCompany = null; let inflightCompany = null;
let inflightNumbers = null; let inflightNumbers = null;
let inflightTerminals = null; let inflightTerminals = null;
let inflightPrinterAgents = null;
let inflightSuppliers = null; let inflightSuppliers = null;
let inflightUnits = null; let inflightUnits = null;
let inflightWarehouses = null; let inflightWarehouses = null;
@@ -496,6 +513,7 @@ const actions = {
actions.load(), actions.load(),
actions.loadNumbers(), actions.loadNumbers(),
actions.loadTerminals(), actions.loadTerminals(),
actions.loadPrinterAgents(),
actions.loadSuppliers(), actions.loadSuppliers(),
actions.loadUnits(), actions.loadUnits(),
actions.loadWarehouses(), actions.loadWarehouses(),
@@ -512,6 +530,7 @@ const actions = {
inflightCompany = null; inflightCompany = null;
inflightNumbers = null; inflightNumbers = null;
inflightTerminals = null; inflightTerminals = null;
inflightPrinterAgents = null;
inflightSuppliers = null; inflightSuppliers = null;
inflightUnits = null; inflightUnits = null;
inflightWarehouses = null; inflightWarehouses = null;
@@ -698,6 +717,72 @@ const actions = {
}, },
// #endregion // #endregion
// #region Printer agents (printeragent)
// MARK: calls printeragent.list from ../api/index.js
async loadPrinterAgents() {
const companyId =
authAutomat.state.user?.selectedCompanyId ?? companyAutomat.state.current?.id;
if (!companyId) return [];
if (inflightPrinterAgents) return inflightPrinterAgents;
companyAutomat.setState({ printeragentsStatus: 'loading', printeragentsError: null });
inflightPrinterAgents = (async () => {
try {
const res = await printeragentApi.list();
const printeragents = sortPrinterAgents(Array.isArray(res.printeragents) ? res.printeragents : []);
companyAutomat.setState({
printeragents,
printeragentsCompanyId: companyId,
printeragentsStatus: 'succeeded',
printeragentsError: null,
});
return printeragents;
} catch (err) {
companyAutomat.setState({
printeragentsStatus: 'failed',
printeragentsError: err?.message ?? String(err),
});
throw err;
} finally {
inflightPrinterAgents = null;
}
})();
return inflightPrinterAgents;
},
reloadPrinterAgents() {
return actions.loadPrinterAgents();
},
async createPrinterAgent(payload) {
const res = await printeragentApi.create(payload);
const agent = res.printeragent;
companyAutomat.setState({
printeragents: sortPrinterAgents([...companyAutomat.state.printeragents, agent]),
});
return agent;
},
async updatePrinterAgent(agentId, patch) {
const res = await printeragentApi.update(agentId, patch);
const updated = res.printeragent;
companyAutomat.setState({
printeragents: sortPrinterAgents(
companyAutomat.state.printeragents.map((a) =>
a.id === agentId ? { ...a, ...updated } : a,
),
),
});
return updated;
},
async deletePrinterAgent(agentId) {
await printeragentApi.delete(agentId);
companyAutomat.setState({
printeragents: companyAutomat.state.printeragents.filter((a) => a.id !== agentId),
});
},
// #endregion
// #region 4. Suppliers (supplier) // #region 4. Suppliers (supplier)
// MARK: calls supplier.list from ../api/index.js // MARK: calls supplier.list from ../api/index.js
loadSuppliers() { loadSuppliers() {
@@ -1553,6 +1638,9 @@ sseAutomat.actions.on('carrier', () => {
sseAutomat.actions.on('posterminal', () => { sseAutomat.actions.on('posterminal', () => {
void companyAutomat.actions.loadTerminals().catch(() => {}); void companyAutomat.actions.loadTerminals().catch(() => {});
}); });
sseAutomat.actions.on('printeragent', () => {
void companyAutomat.actions.loadPrinterAgents().catch(() => {});
});
sseAutomat.actions.on('customergroup', () => { sseAutomat.actions.on('customergroup', () => {
void companyAutomat.actions.reloadCustomergroups().catch(() => {}); void companyAutomat.actions.reloadCustomergroups().catch(() => {});
}); });
@@ -1593,6 +1681,11 @@ export const selectPosterminals = () => companyAutomat.state.terminals;
export const selectPosterminalStatus = () => companyAutomat.state.terminalsStatus; export const selectPosterminalStatus = () => companyAutomat.state.terminalsStatus;
export const selectHasPosterminals = () => companyAutomat.state.terminals.length > 0; export const selectHasPosterminals = () => companyAutomat.state.terminals.length > 0;
// Printer agents selectors
export const selectPrinterAgents = () => companyAutomat.state.printeragents;
export const selectPrinterAgentStatus = () => companyAutomat.state.printeragentsStatus;
export const selectHasPrinterAgents = () => companyAutomat.state.printeragents.length > 0;
// Suppliers selectors // Suppliers selectors
export const selectSuppliers = () => companyAutomat.state.suppliers; export const selectSuppliers = () => companyAutomat.state.suppliers;
export const selectSuppliersStatus = () => companyAutomat.state.suppliersStatus; export const selectSuppliersStatus = () => companyAutomat.state.suppliersStatus;

View File

@@ -151,6 +151,9 @@ export {
selectPosterminals, selectPosterminals,
selectPosterminalStatus, selectPosterminalStatus,
selectHasPosterminals, selectHasPosterminals,
selectPrinterAgents,
selectPrinterAgentStatus,
selectHasPrinterAgents,
selectSuppliers, selectSuppliers,
selectSuppliersStatus, selectSuppliersStatus,
selectUnits, selectUnits,

View File

@@ -0,0 +1,600 @@
import { PureComponent } from 'react';
import Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Chip from '@mui/material/Chip';
import Divider from '@mui/material/Divider';
import IconButton from '@mui/material/IconButton';
import TextField from '@mui/material/TextField';
import Tooltip from '@mui/material/Tooltip';
import Typography from '@mui/material/Typography';
import AddIcon from '@mui/icons-material/Add';
import ArrowDownwardIcon from '@mui/icons-material/ArrowDownward';
import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward';
import CheckIcon from '@mui/icons-material/Check';
import CloseIcon from '@mui/icons-material/Close';
import ContentCopyIcon from '@mui/icons-material/ContentCopy';
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
import EditOutlinedIcon from '@mui/icons-material/EditOutlined';
import LocalPrintshopOutlinedIcon from '@mui/icons-material/LocalPrintshopOutlined';
import CheckCircleOutlinedIcon from '@mui/icons-material/CheckCircleOutlined';
import HourglassEmptyIcon from '@mui/icons-material/HourglassEmpty';
import { enqueueSnackbar } from 'notistack';
import BackendError from '../BackendError';
import companyAutomat from '../../automats/companyAutomat';
import { backendErrorFrom } from '../../utils/backendError';
import { notifyError } from '../../utils/notify';
function formatDateTime(val) {
if (!val) return null;
try {
const d = new Date(val);
if (isNaN(d.getTime())) return null;
return d.toLocaleString('de-DE', {
day: '2-digit',
month: '2-digit',
year: 'numeric',
hour: '2-digit',
minute: '2-digit',
});
} catch {
return null;
}
}
export default class PrinterAgentSettingsPanel extends PureComponent {
constructor(props) {
super(props);
this.state = {
printeragents: Array.isArray(companyAutomat.state.printeragents)
? companyAutomat.state.printeragents
: [],
error: companyAutomat.state.printeragentsError
? backendErrorFrom(companyAutomat.state.printeragentsError)
: null,
newAgentName: '',
creatingAgent: false,
deletingId: null,
patchingAgentId: null,
editingId: null,
editNameDraft: '',
sortDrafts: {},
};
this.handleRetry = this.handleRetry.bind(this);
this.handleCreateAgent = this.handleCreateAgent.bind(this);
this.handleDeleteAgent = this.handleDeleteAgent.bind(this);
this.handleStartRename = this.handleStartRename.bind(this);
this.handleCancelRename = this.handleCancelRename.bind(this);
this.handleSaveRename = this.handleSaveRename.bind(this);
this.handleSortDraftChange = this.handleSortDraftChange.bind(this);
this.handleSortSubmit = this.handleSortSubmit.bind(this);
this.handleMoveAgent = this.handleMoveAgent.bind(this);
}
componentDidMount() {
this.unsub = companyAutomat.subscribe(this, (state) => ({
printeragents: Array.isArray(state.printeragents) ? state.printeragents : [],
error: state.printeragentsError ? backendErrorFrom(state.printeragentsError) : null,
}));
if (companyAutomat.state.printeragentsStatus === 'idle') {
void companyAutomat.actions.loadPrinterAgents().catch(() => {});
}
}
componentWillUnmount() {
this.unsub?.();
}
handleRetry() {
void companyAutomat.actions.loadPrinterAgents().catch(notifyError);
}
async handleCreateAgent() {
const { newAgentName, printeragents } = this.state;
const trimmed = newAgentName.trim();
if (!trimmed) return;
this.setState({ creatingAgent: true });
try {
const nextSort =
printeragents.length > 0
? Math.max(...printeragents.map((a) => Number(a.sort) || 0)) + 1
: 0;
await companyAutomat.actions.createPrinterAgent({
name: trimmed,
sort: nextSort,
});
this.setState({ newAgentName: '' });
enqueueSnackbar('Drucker-Agent hinzugefügt.', { variant: 'success' });
} catch (err) {
notifyError(err);
} finally {
this.setState({ creatingAgent: false });
}
}
async handleDeleteAgent(agent) {
if (!window.confirm(`Möchten Sie den Drucker-Agenten „${agent.name}“ wirklich löschen?`)) {
return;
}
this.setState({ deletingId: agent.id });
try {
await companyAutomat.actions.deletePrinterAgent(agent.id);
enqueueSnackbar('Drucker-Agent gelöscht.', { variant: 'success' });
} catch (err) {
notifyError(err);
} finally {
this.setState({ deletingId: null });
}
}
handleStartRename(agent) {
this.setState({ editingId: agent.id, editNameDraft: agent.name || '' });
}
handleCancelRename() {
this.setState({ editingId: null, editNameDraft: '' });
}
async handleSaveRename(agent) {
const { editNameDraft } = this.state;
const trimmed = editNameDraft.trim();
if (!trimmed || trimmed === agent.name) {
this.handleCancelRename();
return;
}
this.setState({ patchingAgentId: agent.id });
try {
await companyAutomat.actions.updatePrinterAgent(agent.id, { name: trimmed });
enqueueSnackbar('Name aktualisiert.', { variant: 'success' });
this.setState({ editingId: null, editNameDraft: '' });
} catch (err) {
notifyError(err);
} finally {
this.setState({ patchingAgentId: null });
}
}
handleSortDraftChange(agentId, value) {
this.setState((prev) => ({
sortDrafts: { ...prev.sortDrafts, [agentId]: value },
}));
}
async handleSortSubmit(agent) {
const draft = this.state.sortDrafts[agent.id];
if (draft === undefined) return;
const nextSort = Number(draft);
if (isNaN(nextSort) || nextSort === (agent.sort ?? 0)) {
this.setState((prev) => {
const next = { ...prev.sortDrafts };
delete next[agent.id];
return { sortDrafts: next };
});
return;
}
this.setState({ patchingAgentId: agent.id });
try {
await companyAutomat.actions.updatePrinterAgent(agent.id, { sort: nextSort });
this.setState((prev) => {
const next = { ...prev.sortDrafts };
delete next[agent.id];
return { sortDrafts: next };
});
enqueueSnackbar('Reihenfolge gespeichert.', { variant: 'success' });
} catch (err) {
notifyError(err);
} finally {
this.setState({ patchingAgentId: null });
}
}
async handleMoveAgent(agent, direction) {
const { printeragents } = this.state;
const currentIndex = printeragents.findIndex((a) => a.id === agent.id);
if (currentIndex === -1) return;
const targetIndex = direction === 'up' ? currentIndex - 1 : currentIndex + 1;
if (targetIndex < 0 || targetIndex >= printeragents.length) return;
const targetAgent = printeragents[targetIndex];
const currentSort = Number(agent.sort) || 0;
const targetSort = Number(targetAgent.sort) || 0;
let newCurrentSort = targetSort;
let newTargetSort = currentSort;
if (newCurrentSort === newTargetSort) {
newCurrentSort = direction === 'up' ? targetSort - 1 : targetSort + 1;
}
this.setState({ patchingAgentId: agent.id });
try {
await Promise.all([
companyAutomat.actions.updatePrinterAgent(agent.id, { sort: newCurrentSort }),
companyAutomat.actions.updatePrinterAgent(targetAgent.id, { sort: newTargetSort }),
]);
} catch (err) {
notifyError(err);
} finally {
this.setState({ patchingAgentId: null });
}
}
render() {
const {
error,
printeragents,
newAgentName,
creatingAgent,
deletingId,
patchingAgentId,
editingId,
editNameDraft,
} = this.state;
if (error && printeragents.length === 0) {
return (
<Box sx={{ p: 3 }}>
<BackendError error={error} onRetry={this.handleRetry} />
</Box>
);
}
return (
<Box sx={{ p: 3, maxWidth: 900, mx: 'auto' }}>
<Box sx={{ mb: 2 }}>
<Typography variant="h6" fontWeight={600} gutterBottom>
Drucker-Agenten
</Typography>
<Typography variant="body2" color="text.secondary">
Verwalten Sie externe Drucker-Agenten für Druckaufträge. Nach Erstellung eines Agenten
geben Sie die 6-stellige PIN auf dem Agenten-Gerät ein, um die Kopplung abzuschließen.
</Typography>
</Box>
{/* Add new agent row */}
<Box sx={{ display: 'flex', gap: 1.5, mb: 3, alignItems: 'center' }}>
<TextField
size="small"
placeholder="Neuer Drucker-Agent Name (z.B. Bar Receipt Printer)"
value={newAgentName}
onChange={(e) => this.setState({ newAgentName: e.target.value })}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
void this.handleCreateAgent();
}
}}
disabled={creatingAgent}
autoComplete="off"
slotProps={{
htmlInput: {
autoComplete: 'off',
autoCorrect: 'off',
autoCapitalize: 'off',
spellCheck: false,
},
input: {
startAdornment: <LocalPrintshopOutlinedIcon sx={{ mr: 1, color: 'text.secondary' }} />,
},
}}
sx={{ flex: 1 }}
/>
<Button
variant="contained"
size="small"
startIcon={<AddIcon />}
onClick={this.handleCreateAgent}
disabled={creatingAgent || !newAgentName.trim()}
>
Hinzufügen
</Button>
</Box>
<Divider sx={{ mb: 2 }} />
{printeragents.length === 0 ? (
<Box sx={{ textAlign: 'center', py: 6, color: 'text.secondary' }}>
<LocalPrintshopOutlinedIcon sx={{ fontSize: 48, mb: 1, opacity: 0.5 }} />
<Typography variant="body2">Keine Drucker-Agenten angelegt.</Typography>
</Box>
) : (
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2 }}>
{printeragents.map((agent, index) => {
const isDeleting = deletingId === agent.id;
const isPatching = patchingAgentId === agent.id;
const isEditing = editingId === agent.id;
const isPaired = agent.pairedAt != null;
const pairedDateText = formatDateTime(agent.pairedAt);
const lastseenText = formatDateTime(agent.lastseenAt) ?? 'Nie';
const updatedText = formatDateTime(agent.updatedAt) ?? '—';
return (
<Box
key={agent.id}
sx={{
p: 2,
borderRadius: 2,
border: '1px solid',
borderColor: isPaired ? 'divider' : 'warning.light',
bgcolor: 'background.paper',
display: 'flex',
flexDirection: 'column',
gap: 1.5,
transition: 'border-color 0.2s ease',
}}
>
{/* Header: Icon, Name (or Edit input), Status chip, Actions */}
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: 1,
flexWrap: 'wrap',
}}
>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, flex: 1, minWidth: 260 }}>
<LocalPrintshopOutlinedIcon
sx={{ color: isPaired ? 'primary.main' : 'warning.main', fontSize: 24 }}
/>
{isEditing ? (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1, flex: 1 }}>
<TextField
size="small"
value={editNameDraft}
onChange={(e) => this.setState({ editNameDraft: e.target.value })}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
void this.handleSaveRename(agent);
} else if (e.key === 'Escape') {
this.handleCancelRename();
}
}}
autoFocus
disabled={isPatching}
autoComplete="off"
slotProps={{
htmlInput: {
autoComplete: 'off',
autoCorrect: 'off',
autoCapitalize: 'off',
spellCheck: false,
},
}}
sx={{ minWidth: 200 }}
/>
<IconButton
size="small"
color="primary"
onClick={() => void this.handleSaveRename(agent)}
disabled={isPatching}
>
<CheckIcon fontSize="small" />
</IconButton>
<IconButton
size="small"
onClick={this.handleCancelRename}
disabled={isPatching}
>
<CloseIcon fontSize="small" />
</IconButton>
</Box>
) : (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<Typography variant="subtitle1" fontWeight={600}>
{agent.name}
</Typography>
<Tooltip title="Name bearbeiten">
<IconButton
size="small"
onClick={() => this.handleStartRename(agent)}
disabled={isPatching || isDeleting}
sx={{ p: 0.5, color: 'text.secondary' }}
>
<EditOutlinedIcon sx={{ fontSize: 16 }} />
</IconButton>
</Tooltip>
</Box>
)}
{isPaired ? (
<Chip
icon={<CheckCircleOutlinedIcon />}
label="Gekoppelt"
size="small"
variant="outlined"
color="success"
sx={{ fontWeight: 600, fontSize: '0.75rem' }}
/>
) : (
<Chip
icon={<HourglassEmptyIcon />}
label="Nicht gekoppelt"
size="small"
variant="outlined"
color="warning"
sx={{ fontWeight: 600, fontSize: '0.75rem' }}
/>
)}
</Box>
{/* Delete control */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<IconButton
size="small"
aria-label={`Drucker-Agent ${agent.name} löschen`}
onClick={() => void this.handleDeleteAgent(agent)}
disabled={isDeleting || creatingAgent || isPatching}
color="error"
>
<DeleteOutlinedIcon fontSize="small" />
</IconButton>
</Box>
</Box>
{/* Body: Pairing details or PIN info */}
<Box
sx={{
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
flexWrap: 'wrap',
gap: 2,
pt: 0.5,
}}
>
{!isPaired ? (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, flexWrap: 'wrap' }}>
<Typography variant="body2" color="text.secondary">
Kopplungs-PIN:
</Typography>
<Tooltip title="PIN in die Zwischenablage kopieren">
<Chip
icon={<ContentCopyIcon sx={{ fontSize: 14 }} />}
label={agent.pairingpin || '—'}
onClick={() => {
if (agent.pairingpin) {
navigator.clipboard?.writeText(agent.pairingpin);
enqueueSnackbar('PIN in Zwischenablage kopiert.', { variant: 'info' });
}
}}
clickable
color="warning"
variant="filled"
sx={{
fontFamily: 'monospace',
fontWeight: 700,
fontSize: '0.9rem',
letterSpacing: '0.1em',
px: 0.5,
}}
/>
</Tooltip>
<Typography variant="caption" color="text.secondary">
Geben Sie diesen PIN auf dem Drucker-Agent-Gerät ein.
</Typography>
</Box>
) : (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, flexWrap: 'wrap' }}>
{agent.clientserial ? (
<Chip
label={`S/N: ${agent.clientserial}`}
size="small"
variant="outlined"
sx={{ fontFamily: 'monospace', fontSize: '0.75rem' }}
/>
) : null}
{agent.clientmodel ? (
<Chip
label={`Modell: ${agent.clientmodel}`}
size="small"
variant="outlined"
sx={{ fontSize: '0.75rem' }}
/>
) : null}
{agent.agentversion ? (
<Chip
label={`v${agent.agentversion}`}
size="small"
variant="outlined"
color="info"
sx={{ fontFamily: 'monospace', fontWeight: 600, fontSize: '0.75rem' }}
/>
) : null}
{agent.clientstate ? (
<Chip
label={`Status: ${agent.clientstate}`}
size="small"
variant="outlined"
sx={{ fontSize: '0.75rem' }}
/>
) : null}
</Box>
)}
{/* Sort Order Controls */}
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, ml: 'auto' }}>
<Typography variant="body2" color="text.secondary">
Reihenfolge:
</Typography>
<Tooltip title="Nach oben verschieben">
<span>
<IconButton
size="small"
disabled={index === 0 || isPatching || isDeleting}
onClick={() => void this.handleMoveAgent(agent, 'up')}
sx={{ p: 0.25 }}
>
<ArrowUpwardIcon sx={{ fontSize: 16 }} />
</IconButton>
</span>
</Tooltip>
<Tooltip title="Nach unten verschieben">
<span>
<IconButton
size="small"
disabled={index === printeragents.length - 1 || isPatching || isDeleting}
onClick={() => void this.handleMoveAgent(agent, 'down')}
sx={{ p: 0.25 }}
>
<ArrowDownwardIcon sx={{ fontSize: 16 }} />
</IconButton>
</span>
</Tooltip>
<TextField
type="number"
size="small"
disabled={isPatching || isDeleting}
value={this.state.sortDrafts[agent.id] ?? agent.sort ?? 0}
onChange={(e) => this.handleSortDraftChange(agent.id, e.target.value)}
onBlur={() => void this.handleSortSubmit(agent)}
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault();
e.target.blur();
}
}}
slotProps={{
htmlInput: {
step: 1,
style: { width: 44, padding: '3px 6px', textAlign: 'center', fontSize: '0.8125rem' },
},
}}
sx={{ width: 56 }}
/>
</Box>
</Box>
{/* Dates Row */}
<Divider sx={{ my: 0.25 }} />
<Box
sx={{
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
gap: 2.5,
color: 'text.secondary',
}}
>
{isPaired && pairedDateText ? (
<Typography variant="caption">
Gekoppelt am: <strong>{pairedDateText}</strong>
</Typography>
) : null}
<Typography variant="caption">
Zuletzt gesehen: <strong>{lastseenText}</strong>
</Typography>
<Typography variant="caption">
Aktualisiert: <strong>{updatedText}</strong>
</Typography>
</Box>
</Box>
);
})}
</Box>
)}
</Box>
);
}
}

View File

@@ -18,6 +18,7 @@ import authAutomat from '../../automats/authAutomat';
import CompanySettingsPanel from './CompanySettingsPanel'; import CompanySettingsPanel from './CompanySettingsPanel';
import NumberSettingsPanel from './NumberSettingsPanel'; import NumberSettingsPanel from './NumberSettingsPanel';
import PrinterSettingsPanel from './PrinterSettingsPanel'; import PrinterSettingsPanel from './PrinterSettingsPanel';
import PrinterAgentSettingsPanel from './PrinterAgentSettingsPanel';
import SettingsKeysPanel from './SettingsKeysPanel'; import SettingsKeysPanel from './SettingsKeysPanel';
import ImportExportSettingsPanel from './ImportExportSettingsPanel'; import ImportExportSettingsPanel from './ImportExportSettingsPanel';
import PosTerminalSettingsPanel from './PosTerminalSettingsPanel'; import PosTerminalSettingsPanel from './PosTerminalSettingsPanel';
@@ -27,6 +28,7 @@ import HealthSettingsPanel from './HealthSettingsPanel';
import MonitorHeartOutlinedIcon from '@mui/icons-material/MonitorHeartOutlined'; import MonitorHeartOutlinedIcon from '@mui/icons-material/MonitorHeartOutlined';
import SecurityIcon from '@mui/icons-material/Security'; import SecurityIcon from '@mui/icons-material/Security';
import BackupTableOutlinedIcon from '@mui/icons-material/BackupTableOutlined'; import BackupTableOutlinedIcon from '@mui/icons-material/BackupTableOutlined';
import LocalPrintshopOutlinedIcon from '@mui/icons-material/LocalPrintshopOutlined';
import { SURFACE_BASE } from '../../colors'; import { SURFACE_BASE } from '../../colors';
const TAB_KEYS = { const TAB_KEYS = {
@@ -35,6 +37,7 @@ const TAB_KEYS = {
keys: 'keys', keys: 'keys',
numbers: 'numbers', numbers: 'numbers',
printers: 'printers', printers: 'printers',
printeragent: 'printeragent',
importExport: 'importExport', importExport: 'importExport',
backup: 'backup', backup: 'backup',
posterminal: 'posterminal', posterminal: 'posterminal',
@@ -47,6 +50,7 @@ const TABS = [
{ id: TAB_KEYS.keys, label: 'Einstellungen', icon: <TuneIcon />, Panel: SettingsKeysPanel }, { id: TAB_KEYS.keys, label: 'Einstellungen', icon: <TuneIcon />, Panel: SettingsKeysPanel },
{ id: TAB_KEYS.numbers, label: 'Nummernkreise', icon: <NumbersIcon />, Panel: NumberSettingsPanel }, { id: TAB_KEYS.numbers, label: 'Nummernkreise', icon: <NumbersIcon />, Panel: NumberSettingsPanel },
{ id: TAB_KEYS.printers, label: 'Drucker', icon: <PrintOutlinedIcon />, Panel: PrinterSettingsPanel }, { id: TAB_KEYS.printers, label: 'Drucker', icon: <PrintOutlinedIcon />, Panel: PrinterSettingsPanel },
{ id: TAB_KEYS.printeragent, label: 'Drucker-Agent', icon: <LocalPrintshopOutlinedIcon />, Panel: PrinterAgentSettingsPanel },
{ id: TAB_KEYS.importExport, label: 'Import/Export', icon: <ImportExportIcon />, Panel: ImportExportSettingsPanel }, { id: TAB_KEYS.importExport, label: 'Import/Export', icon: <ImportExportIcon />, Panel: ImportExportSettingsPanel },
{ id: TAB_KEYS.backup, label: 'Backups', icon: <BackupTableOutlinedIcon />, Panel: BackupSettingsPanel }, { id: TAB_KEYS.backup, label: 'Backups', icon: <BackupTableOutlinedIcon />, Panel: BackupSettingsPanel },
{ id: TAB_KEYS.posterminal, label: 'POS Terminal', icon: <PointOfSaleIcon />, Panel: PosTerminalSettingsPanel }, { id: TAB_KEYS.posterminal, label: 'POS Terminal', icon: <PointOfSaleIcon />, Panel: PosTerminalSettingsPanel },