Compare commits
2 Commits
b3051953b0
...
20d83b00fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20d83b00fc | ||
|
|
54066fba7f |
@@ -7,7 +7,7 @@ description: >-
|
|||||||
|
|
||||||
# Dash API Routes Overview (All)
|
# Dash API Routes Overview (All)
|
||||||
|
|
||||||
Overview of all 255 API endpoints across all modules.
|
Overview of all 261 API endpoints across all modules.
|
||||||
|
|
||||||
### Notation
|
### Notation
|
||||||
|
|
||||||
@@ -16,46 +16,47 @@ Params: path values first, then `query:` and `body:` (`body?` = optional body).
|
|||||||
|
|
||||||
### Reusable types
|
### Reusable types
|
||||||
|
|
||||||
- `T1` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T19[], externalNumber?: str|null, createdAt?: str, shipping?: T25|null, shippings?: T25[], payment?: T30|null, payments?: T30[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T3[], files?: T9[] }`
|
- `T1` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T20[], externalNumber?: str|null, createdAt?: str, shipping?: T19|null, shippings?: T19[], payment?: T30|null, payments?: T30[], paid?: bool, shipped?: bool, delivered?: bool, orderTotal?: num, paidTotal?: num, orderedQuantity?: num, shippedQuantity?: num, shipments?: T3[], files?: T9[] }`
|
||||||
- `T2` = `{ 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: T23[], eans: str[], prices: T34[], suppliers: T11[] }`
|
- `T2` = `{ 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: T24[], eans: str[], prices: T35[], suppliers: T11[] }`
|
||||||
- `T3` = `{ id: int, number: str, type: str, date: str, lines: T19[], shippings: T25[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int, files?: T9[], state?: str }`
|
- `T3` = `{ id: int, number: str, type: str, date: str, lines: T20[], shippings: T19[], entityId?: int|null, externalNumber?: str|null, createdAt?: str|null, lineCount?: int, packageCount?: int, files?: T9[], state?: str }`
|
||||||
- `T4` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T19[], externalNumber?: str|null, createdAt?: str, positions?: int, grossTotal?: num, totalQuantity?: num, customerName?: str|null, files?: T9[] }`
|
- `T4` = `{ id: int, number: str, type: str, entityId: int|null, date: str, lines: T20[], externalNumber?: str|null, createdAt?: str, positions?: int, grossTotal?: num, totalQuantity?: num, customerName?: str|null, files?: T9[] }`
|
||||||
- `T5` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T19[], files: T9[], externalNumber?: str|null, createdAt?: str, ledger?: T38 }`
|
- `T5` = `{ id: int, number: str, type: str, supplierId: int|null, date: str, lines: T20[], files: T9[], externalNumber?: str|null, createdAt?: str, ledger?: T39 }`
|
||||||
- `T6` = `{ name: str, path: str, totalSizeBytes: int, timestamp: str|null, manifest?: T7|null }`
|
- `T6` = `{ name: str, path: str, totalSizeBytes: int, timestamp: str|null, manifest?: T7|null }`
|
||||||
- `T7` = `{ id: str, timestamp: str, dateKey: str, monthKey: str, dbProvider: str, files: T12, totalSizeBytes: int, durationMs: int }`
|
- `T7` = `{ id: str, timestamp: str, dateKey: str, monthKey: str, dbProvider: str, files: T12, totalSizeBytes: int, durationMs: int }`
|
||||||
- `T8` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: { objects: T20[] }, thumb?: str|null }`
|
- `T8` = `{ id: int, name: str, width: num, height: num, dpi: int, type: str, content: { objects: T21[] }, thumb?: str|null }`
|
||||||
- `T9` = `{ 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 }`
|
- `T9` = `{ 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, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }`
|
- `T10` = `{ id: int, name: str, pairingpin?: str|null, token?: str|null, pairedAt?: str|null, clientserial?: str|null, rootCategoryId?: int|null, syncRootArticles?: bool, lastseenAt?: str|null, updatedAt?: str|null, categoryIds?: int[] }`
|
||||||
- `T11` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T40|null }`
|
- `T11` = `{ id: int, articleId: int, entityId: int, pu: num, sort: int, supplierName?: str|null, sku?: str|null, stock?: num|null, priceId?: int|null, price?: T41|null }`
|
||||||
- `T12` = `{ vfs: T39, storage: T39, database: T32 }`
|
- `T12` = `{ vfs: T40, storage: T40, database: T33 }`
|
||||||
- `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 }`
|
- `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, 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 }`
|
- `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, manifestarticle_id: int, transaction_type: str, amount: num, transaction_text: str, hash_version: int, prev_hash?: str, sku?: str|null, created_at?: str }`
|
- `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, name: str, sort: int, exclusive: bool, values: T28[], articleCount?: int }`
|
- `T16` = `{ id: int, name: str, sort: int, exclusive: bool, values: T28[], articleCount?: int }`
|
||||||
- `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 }`
|
- `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, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num }`
|
- `T18` = `{ id: int, name: str, rootCategoryId: int|null, languageId: int|null, iso639: str|null, taxrate: num, reducedtaxrate: num, taxfreerate: num }`
|
||||||
- `T19` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }`
|
- `T19` = `{ id: int, manifestId: int, number: str, carrierId: int, type: str, state: str, trackingCode?: str|null, carrier?: str|null }`
|
||||||
- `T20` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }`
|
- `T20` = `{ id: int, sku: str|null, name: str|null, pu: num, amount: num, net: num, rebate: num, taxrate: num, comment: str|null }`
|
||||||
- `T21` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }`
|
- `T21` = `{ id: str, type: str, x: num, y: num, w?: num, h?: num, text?: str, font?: str, fontSize?: num, barcodeFormat?: str }`
|
||||||
- `T22` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }`
|
- `T22` = `{ id: int, company_id: int, address: str, type: str, sort: int, model?: str|null, serial?: str|null, desc?: str|null }`
|
||||||
- `T23` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }`
|
- `T23` = `{ amount: num, net: num, taxrate: num, sku?: str|null, name?: str|null, pu?: num, rebate?: num, comment?: str|null }`
|
||||||
- `T24` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }`
|
- `T24` = `{ sort: int, fullHash: str, fullW: int, fullH: int, fullSize: int, thumbW: int, thumbH: int, thumbHash: str }`
|
||||||
- `T25` = `{ id: int, manifestId: int, number: str, type: str, state: str, trackingCode?: str|null, carrier?: str|null }`
|
- `T25` = `{ id: int, customergroupId: int, firstName: str|null, name: str, number: str|null, createdAt: str, updatedAt: str }`
|
||||||
- `T26` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }`
|
- `T26` = `{ name: str, id?: int|null, fullHash?: str|null, thumbW?: int|null, thumbH?: int|null, thumbHash?: str|null }`
|
||||||
- `T27` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }`
|
- `T27` = `{ si: str, basesi: str, basesifactor: num, symbol?: str|null, name?: str|null }`
|
||||||
- `T28` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }`
|
- `T28` = `{ id: int, attributeId: int, value: str, sort: int, articleCount?: int }`
|
||||||
- `T29` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }`
|
- `T29` = `{ id: int, name: str, sort: int, exclusive: bool, articleCount?: int }`
|
||||||
- `T30` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }`
|
- `T30` = `{ id: int, accountId: int, manifestId: int, type: str, amount: num }`
|
||||||
- `T31` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }`
|
- `T31` = `{ id: int, name: str, sort: int, hasPackages: bool, isReverse: bool }`
|
||||||
- `T32` = `{ name: str|null, sizeBytes: int, backedUp: bool, reason?: str|null }`
|
- `T32` = `{ id: int, categoryId: int, name: str, sku: str, track_stock: bool }`
|
||||||
- `T33` = `{ id: int, email: str, selectedCompanyId: int|null, picture?: str }`
|
- `T33` = `{ name: str|null, sizeBytes: int, backedUp: bool, reason?: str|null }`
|
||||||
- `T34` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }`
|
- `T34` = `{ id: int, email: str, selectedCompanyId: int|null, picture?: str }`
|
||||||
- `T35` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }`
|
- `T35` = `{ minmultiplier: num, net: num, taxRate: num, customergroupId: int }`
|
||||||
- `T36` = `{ id: int, entityId: int, contactType: str, contact: str }`
|
- `T36` = `{ fullHash: str, thumbW: int, thumbH: int, thumbHash: str }`
|
||||||
- `T37` = `{ id: int, name: str, createdAt: str, updatedAt: str }`
|
- `T37` = `{ id: int, entityId: int, contactType: str, contact: str }`
|
||||||
- `T38` = `{ totalPositions: int, completelyOnLedgerPositions: int }`
|
- `T38` = `{ id: int, name: str, createdAt: str, updatedAt: str }`
|
||||||
- `T39` = `{ name: str, sizeBytes: int, sourceExists: bool }`
|
- `T39` = `{ totalPositions: int, completelyOnLedgerPositions: int }`
|
||||||
- `T40` = `{ net: num, taxId: int, taxRate?: num|null }`
|
- `T40` = `{ name: str, sizeBytes: int, sourceExists: bool }`
|
||||||
|
- `T41` = `{ net: num, taxId: int, taxRate?: num|null }`
|
||||||
|
|
||||||
### Routes
|
### Routes
|
||||||
|
|
||||||
@@ -85,7 +86,7 @@ _none_
|
|||||||
|
|
||||||
#### Create article in category — `POST /article`
|
#### Create article in category — `POST /article`
|
||||||
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
- Params: `body: { categoryId: int, name: str, desc?: str, seo?: str, track_stock?: bool }`
|
||||||
- Returns: `201 { article: T31 }`
|
- Returns: `201 { article: T32 }`
|
||||||
#### Export articles as ZIP — `GET /article/export.zip`
|
#### Export articles as ZIP — `GET /article/export.zip`
|
||||||
- Params: `query: categoryId?: int`
|
- Params: `query: categoryId?: int`
|
||||||
- Returns: `200 zip`
|
- Returns: `200 zip`
|
||||||
@@ -121,10 +122,10 @@ _none_
|
|||||||
- Returns: `200 { children: { id: int, sku: str, name: str, parentsort: int, attributeValueId: int, attributeValue: str }[] }`
|
- 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`
|
#### 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 }`
|
- Params: `sku: str, body: { attributeValueId?: int, value?: str, name?: str }`
|
||||||
- Returns: `201 { article: T31 }`
|
- Returns: `201 { article: T32 }`
|
||||||
#### Attach an existing article as a child to a parent article — `PUT /article/{sku}/children`
|
#### 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 }`
|
- 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: T32, 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}`
|
#### Remove a child article from a parent article — `DELETE /article/{sku}/children/{childSku}`
|
||||||
- Params: `sku: str, childSku: str, query: deleteArticle?: bool`
|
- Params: `sku: str, childSku: str, query: deleteArticle?: bool`
|
||||||
- Returns: `200 { success: bool, deleted: bool }`
|
- Returns: `200 { success: bool, deleted: bool }`
|
||||||
@@ -151,7 +152,7 @@ _none_
|
|||||||
- Returns: `200 { options: { id: int, articleId: int, sku: str, name: str, count: int, sort: int, mandated: bool }[] }`
|
- 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`
|
#### Toggle parent mode for an article — `POST /article/{sku}/parent`
|
||||||
- Params: `sku: str, body: { isParent: bool, attributeNameId?: int }`
|
- Params: `sku: str, body: { isParent: bool, attributeNameId?: int }`
|
||||||
- Returns: `200 { article: T31 }`
|
- Returns: `200 { article: T32 }`
|
||||||
#### Replace POS prices by SKU (net > gross; price=net, tax=taxRate %) — `PUT /article/{sku}/prices`
|
#### 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 }[] }`
|
- Params: `sku: str, body: { prices: { net?: num, gross?: num, price?: num, taxRate?: num, tax?: num, minmultiplier?: num }[] }`
|
||||||
- Returns: `200 { article: T2 }`
|
- Returns: `200 { article: T2 }`
|
||||||
@@ -226,25 +227,25 @@ _none_
|
|||||||
- Returns: `200 { attributevalue: T28 }`
|
- 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: T35[] } }`
|
- Returns: `200 { attributevalue: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T36[] } }`
|
||||||
#### 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: T35[] } }`
|
- Returns: `201 { attributevalue: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T36[] } }`
|
||||||
|
|
||||||
### 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: T33 }`
|
- Returns: `200 { message: str, user: T34 }`
|
||||||
#### 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: T33, googleClientId: str }`
|
- Returns: `200 { user: T34, googleClientId: 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: T33 }`
|
- Returns: `200 { user: T34 }`
|
||||||
|
|
||||||
### Section: /backup
|
### Section: /backup
|
||||||
|
|
||||||
@@ -265,7 +266,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: T35[], gsprpinfo?: str|null } }`
|
- Returns: `201 { brand: { id: int, name: str, pics: T36[], 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`
|
||||||
@@ -274,10 +275,31 @@ _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: T35[] } }`
|
- Returns: `200 { brand: { id: int, thumbW: int|null, thumbH: int|null, thumbHash: str|null, pics: T36[] } }`
|
||||||
#### 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: T35[] } }`
|
- Returns: `201 { brand: { id: int, fullHash: str, thumbW: int, thumbH: int, thumbHash: str, pics: T36[] } }`
|
||||||
|
|
||||||
|
### Section: /carrier
|
||||||
|
|
||||||
|
#### List carriers — `GET /carrier`
|
||||||
|
_none_
|
||||||
|
- Returns: `200 { carriers: T31[] }`
|
||||||
|
#### Create carrier — `POST /carrier`
|
||||||
|
- Params: `body: { name: str, sort?: int, hasPackages?: bool, isReverse?: bool }`
|
||||||
|
- Returns: `201 { carrier: T31 }`
|
||||||
|
#### Reorder carriers — `PUT /carrier/order`
|
||||||
|
- Params: `body: { carrierIds: int[] }`
|
||||||
|
- Returns: `200 { carrierIds: int[] }`
|
||||||
|
#### Delete carrier — `DELETE /carrier/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `204 void`
|
||||||
|
#### Get carrier by ID — `GET /carrier/{id}`
|
||||||
|
- Params: `id: int`
|
||||||
|
- Returns: `200 { carrier: T31 }`
|
||||||
|
#### Update carrier — `PATCH /carrier/{id}`
|
||||||
|
- Params: `id: int, body: { name?: str, sort?: int, hasPackages?: bool, isReverse?: bool }`
|
||||||
|
- Returns: `200 { carrier: T31 }`
|
||||||
|
|
||||||
### Section: /category
|
### Section: /category
|
||||||
|
|
||||||
@@ -331,7 +353,7 @@ _none_
|
|||||||
- Returns: `200 { companies: T18[] }`
|
- 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: T18, user: T33 }`
|
- Returns: `201 { company: T18, user: T34 }`
|
||||||
#### 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`
|
||||||
@@ -385,9 +407,9 @@ _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|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T38 }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
- Returns: `200 { deliveries: { id: int, number: str, type: str, supplierId: int|null, date: str, externalNumber?: str|null, createdAt?: str, ledger?: T39 }[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Create delivery — `POST /delivery`
|
#### Create delivery — `POST /delivery`
|
||||||
- Params: `body: { externalNumber: str, date: str, lines: T22[], supplierId?: int|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, lines: T23[], 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 { delivery: T5 }`
|
- Returns: `201 { delivery: T5 }`
|
||||||
#### Delete delivery — `DELETE /delivery/{id}`
|
#### Delete delivery — `DELETE /delivery/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
@@ -396,7 +418,7 @@ _none_
|
|||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { delivery: T5 }`
|
- Returns: `200 { delivery: T5 }`
|
||||||
#### Update delivery — `PATCH /delivery/{id}`
|
#### Update delivery — `PATCH /delivery/{id}`
|
||||||
- Params: `id: int, body: { externalNumber?: str|null, date?: str, supplierId?: int|null, lines?: T22[], 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?: T23[], files?: { mimetype: str, name: str, content: str, sort?: int, size?: int, hash?: str, created?: str, modified?: str, meta?: { [k]: str } }[] }`
|
||||||
- Returns: `200 { delivery: T5 }`
|
- Returns: `200 { delivery: T5 }`
|
||||||
#### Delete delivery file attachment — `DELETE /delivery/{id}/file/{fileId}`
|
#### Delete delivery file attachment — `DELETE /delivery/{id}/file/{fileId}`
|
||||||
- Params: `id: int, fileId: int`
|
- Params: `id: int, fileId: int`
|
||||||
@@ -412,10 +434,10 @@ _none_
|
|||||||
|
|
||||||
#### 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 }`
|
||||||
@@ -433,25 +455,25 @@ _none_
|
|||||||
- Returns: `200 { address: T13 }`
|
- 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: T36[] }`
|
- Returns: `200 { contacts: T37[] }`
|
||||||
#### 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: T36 }`
|
- Returns: `201 { contact: T37 }`
|
||||||
#### 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: T36 }`
|
- Returns: `200 { contact: T37 }`
|
||||||
#### 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
|
||||||
|
|
||||||
@@ -493,7 +515,7 @@ _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: T20[] } }`
|
- Params: `body: { name: str, width: num, height: num, dpi: int, type: str, content: { objects: T21[] } }`
|
||||||
- Returns: `201 { labeltemplate: T8 }`
|
- Returns: `201 { labeltemplate: T8 }`
|
||||||
#### Delete label template — `DELETE /labeltemplate/{id}`
|
#### Delete label template — `DELETE /labeltemplate/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
@@ -502,10 +524,10 @@ _none_
|
|||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { labeltemplate: T8 }`
|
- Returns: `200 { labeltemplate: T8 }`
|
||||||
#### 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: T20[] } }`
|
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T21[] } }`
|
||||||
- Returns: `200 { labeltemplate: T8 }`
|
- Returns: `200 { labeltemplate: T8 }`
|
||||||
#### 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: T20[] } }`
|
- Params: `id: int, body: { name?: str, width?: num, height?: num, dpi?: int, type?: str, content?: { objects: T21[] } }`
|
||||||
- Returns: `200 { labeltemplate: T8 }`
|
- Returns: `200 { labeltemplate: T8 }`
|
||||||
|
|
||||||
### Section: /ledger
|
### Section: /ledger
|
||||||
@@ -598,7 +620,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, lines: T22[], entityId?: int|null, externalNumber?: str|null }`
|
- Params: `body: { number: str, date: str, lines: T23[], entityId?: int|null, 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`
|
||||||
@@ -607,7 +629,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?: T22[] }`
|
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T23[] }`
|
||||||
- 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`
|
||||||
@@ -632,10 +654,10 @@ _none_
|
|||||||
- Returns: `201 { shipment: T3 }`
|
- Returns: `201 { shipment: T3 }`
|
||||||
#### 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: T25[] }`
|
- Returns: `200 { shippings: T19[] }`
|
||||||
#### Create shipping for an order — `POST /order/{id}/shipping`
|
#### Create shipping for an order — `POST /order/{id}/shipping`
|
||||||
- Params: `id: int, body: { type: str, state: str, warehouseId?: int }`
|
- Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }`
|
||||||
- Returns: `201 { shipping: T25 }`
|
- Returns: `201 { shipping: T19 }`
|
||||||
|
|
||||||
### Section: /package
|
### Section: /package
|
||||||
|
|
||||||
@@ -680,10 +702,10 @@ _none_
|
|||||||
|
|
||||||
#### List printers — `GET /printer`
|
#### List printers — `GET /printer`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { printers: T21[] }`
|
- Returns: `200 { printers: T22[] }`
|
||||||
#### 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: T21 }`
|
- Returns: `201 { printer: T22 }`
|
||||||
#### 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[] }`
|
||||||
@@ -692,13 +714,13 @@ _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: T21 }`
|
- Returns: `200 { printer: T22 }`
|
||||||
#### 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: T21 }`
|
- Returns: `200 { printer: T22 }`
|
||||||
#### 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: T21 }`
|
- Returns: `200 { printer: T22 }`
|
||||||
|
|
||||||
### Section: /propertyname
|
### Section: /propertyname
|
||||||
|
|
||||||
@@ -728,7 +750,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: 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: T23[], 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: T24[], 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 }[] }`
|
||||||
@@ -739,7 +761,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: T4[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
- Returns: `200 { quotes: T4[], total: int, unfilteredTotal: int, offset: int, limit: int }`
|
||||||
#### Create quote — `POST /quote`
|
#### Create quote — `POST /quote`
|
||||||
- Params: `body: { number: str, date: str, lines: T22[], entityId?: int|null, externalNumber?: str|null }`
|
- Params: `body: { number: str, date: str, lines: T23[], entityId?: int|null, externalNumber?: str|null }`
|
||||||
- Returns: `201 { quote: T4 }`
|
- Returns: `201 { quote: T4 }`
|
||||||
#### Delete quote — `DELETE /quote/{id}`
|
#### Delete quote — `DELETE /quote/{id}`
|
||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
@@ -748,7 +770,7 @@ _none_
|
|||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- Returns: `200 { quote: T4 }`
|
- Returns: `200 { quote: T4 }`
|
||||||
#### 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?: T22[] }`
|
- Params: `id: int, body: { number?: str, date?: str, entityId?: int|null, externalNumber?: str|null, lines?: T23[] }`
|
||||||
- Returns: `200 { quote: T4 }`
|
- Returns: `200 { quote: T4 }`
|
||||||
#### 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`
|
||||||
@@ -757,7 +779,7 @@ _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?: T22[] }`
|
- Params: `id: int, body?: { number?: str, date?: str, createdAt?: str, entityId?: int|null, externalNumber?: str|null, lines?: T23[] }`
|
||||||
- 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`
|
||||||
@@ -803,10 +825,10 @@ _none_
|
|||||||
- Returns: `201 { attachment: T9, fileUrl: str }`
|
- Returns: `201 { attachment: T9, 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: T25[] }`
|
- Returns: `200 { shippings: T19[] }`
|
||||||
#### Create package for a shipment — `POST /shipment/{id}/shipping`
|
#### Create package for a shipment — `POST /shipment/{id}/shipping`
|
||||||
- Params: `id: int, body: { type: str, state: str, trackingCode?: str, carrier?: str, warehouseId?: int }`
|
- Params: `id: int, body: { carrierId: int, type: str, state: str, trackingCode?: str, warehouseId?: int }`
|
||||||
- Returns: `201 { shipping: T25 }`
|
- Returns: `201 { shipping: T19 }`
|
||||||
|
|
||||||
### Section: /shipping
|
### Section: /shipping
|
||||||
|
|
||||||
@@ -814,8 +836,8 @@ _none_
|
|||||||
- Params: `id: int`
|
- Params: `id: int`
|
||||||
- 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, carrier?: str, warehouseId?: int }`
|
- Params: `id: int, body: { type?: str, state?: str, trackingCode?: str, carrierId?: int, warehouseId?: int }`
|
||||||
- Returns: `200 { shipping: T25 }`
|
- Returns: `200 { shipping: T19 }`
|
||||||
#### 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 }[] }`
|
||||||
@@ -863,22 +885,22 @@ _none_
|
|||||||
|
|
||||||
#### List suppliers — `GET /supplier`
|
#### List suppliers — `GET /supplier`
|
||||||
_none_
|
_none_
|
||||||
- Returns: `200 { suppliers: T37[] }`
|
- Returns: `200 { suppliers: T38[] }`
|
||||||
#### Create supplier — `POST /supplier`
|
#### Create supplier — `POST /supplier`
|
||||||
- Params: `body: { name: str }`
|
- Params: `body: { name: str }`
|
||||||
- Returns: `201 { supplier: T37 }`
|
- Returns: `201 { supplier: T38 }`
|
||||||
#### 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: T37 }`
|
- Returns: `200 { supplier: T38 }`
|
||||||
#### 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?: T40|null }[] }`
|
- 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?: T41|null }[] }`
|
||||||
#### 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?: T40|null }[] }`
|
- 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?: T41|null }[] }`
|
||||||
|
|
||||||
### Section: /thumb
|
### Section: /thumb
|
||||||
|
|
||||||
|
|||||||
49
src/api/carrier.js
Normal file
49
src/api/carrier.js
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { http } from './http.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Company-scoped carriers / Lieferarten.
|
||||||
|
* Shape: Carrier = { id, name, sort, hasPackages, isReverse }
|
||||||
|
*/
|
||||||
|
export const carrier = {
|
||||||
|
/** GET /api/carrier → { carriers: Carrier[] } */
|
||||||
|
list() {
|
||||||
|
return http('/api/carrier');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /api/carrier
|
||||||
|
* Body: { name, sort?, hasPackages?, isReverse? }
|
||||||
|
* → 201 { carrier: Carrier }
|
||||||
|
*/
|
||||||
|
create(body) {
|
||||||
|
return http('/api/carrier', { method: 'POST', body });
|
||||||
|
},
|
||||||
|
|
||||||
|
/** GET /api/carrier/:id → 200 { carrier: Carrier } */
|
||||||
|
get(id) {
|
||||||
|
return http(`/api/carrier/${id}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PATCH /api/carrier/:id
|
||||||
|
* Body: { name?, sort?, hasPackages?, isReverse? }
|
||||||
|
* → 200 { carrier: Carrier }
|
||||||
|
*/
|
||||||
|
update(id, body) {
|
||||||
|
return http(`/api/carrier/${id}`, { method: 'PATCH', body });
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PUT /api/carrier/order
|
||||||
|
* Body: { carrierIds: number[] }
|
||||||
|
* → 200 { carrierIds: number[] }
|
||||||
|
*/
|
||||||
|
order(body) {
|
||||||
|
return http('/api/carrier/order', { method: 'PUT', body });
|
||||||
|
},
|
||||||
|
|
||||||
|
/** DELETE /api/carrier/:id → 204 · 409 Conflict if in use */
|
||||||
|
delete(id) {
|
||||||
|
return http(`/api/carrier/${id}`, { method: 'DELETE' });
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -32,3 +32,4 @@ export { payment } from './payment.js';
|
|||||||
export { account } from './account.js';
|
export { account } from './account.js';
|
||||||
export { mcp } from './mcp.js';
|
export { mcp } from './mcp.js';
|
||||||
export { backup } from './backup.js';
|
export { backup } from './backup.js';
|
||||||
|
export { carrier } from './carrier.js';
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import AuftragBestellungNeuPage from './routes/auftrag/bestellung/neu';
|
|||||||
import AuftragBestellungEditPage from './routes/auftrag/bestellung/_orderId';
|
import AuftragBestellungEditPage from './routes/auftrag/bestellung/_orderId';
|
||||||
import AuftragAusliefernPage from './routes/auftrag/ausliefern';
|
import AuftragAusliefernPage from './routes/auftrag/ausliefern';
|
||||||
import AuftragZahlungenPage from './routes/auftrag/zahlungen';
|
import AuftragZahlungenPage from './routes/auftrag/zahlungen';
|
||||||
|
import AuftragLieferartenPage from './routes/auftrag/lieferarten';
|
||||||
|
|
||||||
import KundeListePage from './routes/kunde';
|
import KundeListePage from './routes/kunde';
|
||||||
import KundeNeuPage from './routes/kunde/neu';
|
import KundeNeuPage from './routes/kunde/neu';
|
||||||
@@ -104,6 +105,7 @@ const routes = [
|
|||||||
{ path: 'bestellung/:orderId', element: <AuftragBestellungEditPage /> },
|
{ path: 'bestellung/:orderId', element: <AuftragBestellungEditPage /> },
|
||||||
{ path: 'ausliefern', element: <AuftragAusliefernPage /> },
|
{ path: 'ausliefern', element: <AuftragAusliefernPage /> },
|
||||||
{ path: 'zahlungen', element: <AuftragZahlungenPage /> },
|
{ path: 'zahlungen', element: <AuftragZahlungenPage /> },
|
||||||
|
{ path: 'lieferarten', element: <AuftragLieferartenPage /> },
|
||||||
{ path: ':quoteId', element: <AuftragAngebotEditPage /> },
|
{ path: ':quoteId', element: <AuftragAngebotEditPage /> },
|
||||||
|
|
||||||
],
|
],
|
||||||
|
|||||||
251
src/automats/carrierAutomat.js
Normal file
251
src/automats/carrierAutomat.js
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
/**
|
||||||
|
* @file carrierAutomat.js
|
||||||
|
* @description Manages cached shipping carriers / Lieferarten (`GET /api/carrier`) for the active company.
|
||||||
|
*
|
||||||
|
* Upstream Triggers:
|
||||||
|
* - `authAutomat`: Subscribes to auth state changes (`syncWithAuth`) — triggers
|
||||||
|
* `actions.load()` on `'authed'` with a `selectedCompanyId`, and `actions.clear()` on `'anon'`.
|
||||||
|
* - `sseAutomat`: Calls `actions.reload()` upon receiving a `'carrier'` SSE event.
|
||||||
|
* - UI / Routes: Calls to `actions.load()`, `actions.reload()`, or mutation actions.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Automat } from 'react-automat';
|
||||||
|
import { carrier as carrierApi } from '../api';
|
||||||
|
import authAutomat from './authAutomat';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} Carrier
|
||||||
|
* @property {number} id Carrier identifier.
|
||||||
|
* @property {string} name Carrier name.
|
||||||
|
* @property {number} sort Sort order.
|
||||||
|
* @property {boolean} hasPackages Whether carrier uses package tracking.
|
||||||
|
* @property {boolean} isReverse Whether carrier is used for returns.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} CarrierState
|
||||||
|
* @property {Array<Carrier>} carriers Company-scoped carriers.
|
||||||
|
* @property {Array<Carrier>} filteredCarriers Filtered carriers according to active filter query.
|
||||||
|
* @property {string} filter Active search/filter string.
|
||||||
|
* @property {'idle'|'loading'|'ready'|'failed'} status Load lifecycle state.
|
||||||
|
* @property {Error|null} error Last load error.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** @type {CarrierState} */
|
||||||
|
const initialState = {
|
||||||
|
carriers: [],
|
||||||
|
filteredCarriers: [],
|
||||||
|
filter: '',
|
||||||
|
status: 'idle',
|
||||||
|
error: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
let carrierAutomat;
|
||||||
|
|
||||||
|
const applyFilter = (carriers, filter) => {
|
||||||
|
const query = String(filter ?? '').trim().toLowerCase();
|
||||||
|
return query ? carriers.filter((c) => String(c.name ?? '').toLowerCase().includes(query)) : carriers;
|
||||||
|
};
|
||||||
|
|
||||||
|
const updateStateWithCarriers = (carriers, extra = {}) => {
|
||||||
|
const list = Array.isArray(carriers) ? carriers : [];
|
||||||
|
const filteredCarriers = applyFilter(list, carrierAutomat.state.filter);
|
||||||
|
carrierAutomat.setState({ carriers: list, filteredCarriers, ...extra });
|
||||||
|
return list;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the company-scoped carrier list.
|
||||||
|
*
|
||||||
|
* @returns {Promise<Array<Carrier>>} Carrier list.
|
||||||
|
*/
|
||||||
|
async function fetchCarriers() {
|
||||||
|
const data = await carrierApi.list();
|
||||||
|
if (Array.isArray(data)) return data;
|
||||||
|
if (data && Array.isArray(data.carriers)) return data.carriers;
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** In-flight `load()` promise so concurrent callers share one request. */
|
||||||
|
let inflightLoad = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the carrier list once, deduplicating concurrent callers.
|
||||||
|
*
|
||||||
|
* @returns {Promise<Array<Carrier>>} Loaded carriers.
|
||||||
|
*/
|
||||||
|
function loadCarriers() {
|
||||||
|
if (inflightLoad) return inflightLoad;
|
||||||
|
inflightLoad = (async () => {
|
||||||
|
carrierAutomat.setState({ status: 'loading', error: null });
|
||||||
|
try {
|
||||||
|
const carriers = await fetchCarriers();
|
||||||
|
updateStateWithCarriers(carriers, { status: 'ready', error: null });
|
||||||
|
return carriers;
|
||||||
|
} catch (error) {
|
||||||
|
carrierAutomat.setState({ status: 'failed', error });
|
||||||
|
throw error;
|
||||||
|
} finally {
|
||||||
|
inflightLoad = null;
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return inflightLoad;
|
||||||
|
}
|
||||||
|
|
||||||
|
const actions = {
|
||||||
|
/** Sets the active carrier search filter and updates filteredCarriers. */
|
||||||
|
filter(query) {
|
||||||
|
const filter = String(query ?? '');
|
||||||
|
carrierAutomat.setState({
|
||||||
|
filter,
|
||||||
|
filteredCarriers: applyFilter(carrierAutomat.state.carriers, filter),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the carrier list, exposing a loading/failed lifecycle.
|
||||||
|
*
|
||||||
|
* @returns {Promise<Array<Carrier>>} Loaded carriers.
|
||||||
|
*/
|
||||||
|
load() {
|
||||||
|
return loadCarriers();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Silently re-fetches the carrier list while keeping the current state.
|
||||||
|
*
|
||||||
|
* @returns {Promise<Array<Carrier>>} Fresh carriers.
|
||||||
|
*/
|
||||||
|
async reload() {
|
||||||
|
try {
|
||||||
|
const carriers = await fetchCarriers();
|
||||||
|
updateStateWithCarriers(carriers, { status: 'ready', error: null });
|
||||||
|
return carriers;
|
||||||
|
} catch (error) {
|
||||||
|
carrierAutomat.setState({ error });
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a carrier and refreshes the cached list.
|
||||||
|
*
|
||||||
|
* @param {{name: string, sort?: number, hasPackages?: boolean, isReverse?: boolean}} body Creation payload.
|
||||||
|
* @returns {Promise<Carrier>} Created carrier.
|
||||||
|
*/
|
||||||
|
async create(body) {
|
||||||
|
const data = await carrierApi.create(body);
|
||||||
|
const created = data?.carrier || data;
|
||||||
|
await carrierAutomat.actions.reload();
|
||||||
|
return created;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates a carrier and refreshes the cached list.
|
||||||
|
*
|
||||||
|
* @param {string|number} id Carrier identifier.
|
||||||
|
* @param {{name?: string, sort?: number, hasPackages?: boolean, isReverse?: boolean}} body Update payload.
|
||||||
|
* @returns {Promise<Carrier>} Updated carrier.
|
||||||
|
*/
|
||||||
|
async update(id, body) {
|
||||||
|
const data = await carrierApi.update(id, body);
|
||||||
|
const updated = data?.carrier || data;
|
||||||
|
await carrierAutomat.actions.reload();
|
||||||
|
return updated;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes a carrier and refreshes the cached list.
|
||||||
|
*
|
||||||
|
* @param {string|number} id Carrier identifier.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async remove(id) {
|
||||||
|
await carrierApi.delete(id);
|
||||||
|
await carrierAutomat.actions.reload();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Persists a new sort order for all carriers.
|
||||||
|
*
|
||||||
|
* @param {Array<string|number>} carrierIds IDs in the desired order.
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
async reorder(carrierIds) {
|
||||||
|
const byId = new Map(carrierAutomat.state.carriers.map((c) => [c.id, c]));
|
||||||
|
const reordered = carrierIds.map((id) => byId.get(id)).filter(Boolean);
|
||||||
|
if (reordered.length === carrierAutomat.state.carriers.length) {
|
||||||
|
updateStateWithCarriers(reordered);
|
||||||
|
}
|
||||||
|
await carrierApi.order({ carrierIds });
|
||||||
|
await carrierAutomat.actions.reload();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replaces the cached list without a round-trip.
|
||||||
|
*
|
||||||
|
* @param {Array<Carrier>} carriers Carriers to cache.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
setCarriers(carriers) {
|
||||||
|
updateStateWithCarriers(carriers, {
|
||||||
|
status: 'ready',
|
||||||
|
error: null,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forgets the cached list and returns to the initial state.
|
||||||
|
*
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
clear() {
|
||||||
|
carrierAutomat.setState({ ...initialState });
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Company-scoped carrier cache. Loads automatically once an authenticated
|
||||||
|
* session with a selected company exists and is cleared when auth is gone.
|
||||||
|
*
|
||||||
|
* @type {Automat<CarrierState>}
|
||||||
|
*/
|
||||||
|
carrierAutomat = new Automat(initialState, actions, { name: 'carrier' });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Derives the cache key from the auth state: the selected company while
|
||||||
|
* authenticated, otherwise `null`.
|
||||||
|
*
|
||||||
|
* @param {import('./authAutomat').AuthState} authState Auth automat state.
|
||||||
|
* @returns {string|null} Cache key.
|
||||||
|
*/
|
||||||
|
function authKey(authState) {
|
||||||
|
if (authState?.status !== 'authed' || !authState.user) return null;
|
||||||
|
const companyId = authState.user.selectedCompanyId;
|
||||||
|
return companyId == null ? null : String(companyId);
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastAuthKey;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads the carrier list when auth/company becomes available and forgets it
|
||||||
|
* when the session ends.
|
||||||
|
*
|
||||||
|
* @param {import('./authAutomat').AuthState} authState Auth automat state.
|
||||||
|
* @returns {void}
|
||||||
|
*/
|
||||||
|
function syncWithAuth(authState) {
|
||||||
|
const key = authKey(authState);
|
||||||
|
if (key === lastAuthKey) return;
|
||||||
|
lastAuthKey = key;
|
||||||
|
if (key == null) {
|
||||||
|
carrierAutomat.actions.clear();
|
||||||
|
} else {
|
||||||
|
void carrierAutomat.actions.load().catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
authAutomat.subscribe(syncWithAuth);
|
||||||
|
syncWithAuth(authAutomat.state);
|
||||||
|
|
||||||
|
export default carrierAutomat;
|
||||||
@@ -46,6 +46,7 @@ export {
|
|||||||
|
|
||||||
export { default as supplierAutomat } from './supplierAutomat';
|
export { default as supplierAutomat } from './supplierAutomat';
|
||||||
export { default as warehouseAutomat } from './warehouseAutomat';
|
export { default as warehouseAutomat } from './warehouseAutomat';
|
||||||
|
export { default as carrierAutomat } from './carrierAutomat';
|
||||||
|
|
||||||
/** Article search over the derived index (replaces StorageManager.doSearch). */
|
/** Article search over the derived index (replaces StorageManager.doSearch). */
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ import deliveryAutomat from './deliveryAutomat';
|
|||||||
import orderAutomat from './orderAutomat';
|
import orderAutomat from './orderAutomat';
|
||||||
import quoteAutomat from './quoteAutomat';
|
import quoteAutomat from './quoteAutomat';
|
||||||
import shipmentAutomat from './shipmentAutomat';
|
import shipmentAutomat from './shipmentAutomat';
|
||||||
|
import carrierAutomat from './carrierAutomat';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -223,6 +224,12 @@ function connect() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addTrackedListener(es, 'carrier', () => {
|
||||||
|
void carrierAutomat.actions.reload().catch((err) => {
|
||||||
|
console.error('[sse] carrier refresh failed', err);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
addTrackedListener(es, 'attributename', () => {
|
addTrackedListener(es, 'attributename', () => {
|
||||||
void attributeAutomat.actions.load().catch((err) => {
|
void attributeAutomat.actions.load().catch((err) => {
|
||||||
console.error('[sse] attributename refresh failed', err);
|
console.error('[sse] attributename refresh failed', err);
|
||||||
|
|||||||
213
src/components/carrier/CarrierItem.jsx
Normal file
213
src/components/carrier/CarrierItem.jsx
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
import { PureComponent } from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import IconButton from '@mui/material/IconButton';
|
||||||
|
import ListItem from '@mui/material/ListItem';
|
||||||
|
import Switch from '@mui/material/Switch';
|
||||||
|
import FormControlLabel from '@mui/material/FormControlLabel';
|
||||||
|
import Tooltip from '@mui/material/Tooltip';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import DeleteOutlinedIcon from '@mui/icons-material/DeleteOutlined';
|
||||||
|
import DragIndicatorIcon from '@mui/icons-material/DragIndicator';
|
||||||
|
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
|
||||||
|
import { enqueueSnackbar } from 'notistack';
|
||||||
|
import carrierAutomat from '../../automats/carrierAutomat';
|
||||||
|
import { notifyError } from '../../utils/notify';
|
||||||
|
|
||||||
|
class CarrierItem extends PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const item = props.carrier ?? props.item;
|
||||||
|
this.state = {
|
||||||
|
draft: item?.name ?? '',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps) {
|
||||||
|
const prevItem = prevProps.carrier ?? prevProps.item;
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
if (prevItem?.name !== item?.name) {
|
||||||
|
this.setState({ draft: item?.name ?? '' });
|
||||||
|
} else if (prevProps.disabled && !this.props.disabled) {
|
||||||
|
if (this.state.draft !== item?.name) {
|
||||||
|
this.setState({ draft: item?.name ?? '' });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleChange = (next) => {
|
||||||
|
this.setState({ draft: next });
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
this.props.onChange?.(next, item);
|
||||||
|
};
|
||||||
|
|
||||||
|
handleBlur = () => {
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
if (!item) return;
|
||||||
|
const draft = this.state.draft.trim();
|
||||||
|
if (!draft) {
|
||||||
|
this.setState({ draft: item.name ?? '' });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (draft !== item.name) {
|
||||||
|
void this.updateCarrier(item, { name: draft });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async updateCarrier(item, patch) {
|
||||||
|
try {
|
||||||
|
await carrierAutomat.actions.update(item.id, patch);
|
||||||
|
enqueueSnackbar('Lieferart gespeichert.', { variant: 'success' });
|
||||||
|
} catch (err) {
|
||||||
|
this.setState({ draft: item.name ?? '' });
|
||||||
|
notifyError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleToggleHasPackages = (e) => {
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
if (!item) return;
|
||||||
|
const nextVal = e.target.checked;
|
||||||
|
void this.updateCarrier(item, { hasPackages: nextVal });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleToggleIsReverse = (e) => {
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
if (!item) return;
|
||||||
|
const nextVal = e.target.checked;
|
||||||
|
void this.updateCarrier(item, { isReverse: nextVal });
|
||||||
|
};
|
||||||
|
|
||||||
|
handleKeyDown = (event) => {
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
event.target.blur();
|
||||||
|
}
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
event.preventDefault();
|
||||||
|
this.setState({ draft: item?.name ?? '' });
|
||||||
|
event.target.blur();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
handleDelete = () => {
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
if (!item) return;
|
||||||
|
if (!window.confirm(`Lieferart „${item.name}“ wirklich löschen?`)) return;
|
||||||
|
void (async () => {
|
||||||
|
try {
|
||||||
|
await carrierAutomat.actions.remove(item.id);
|
||||||
|
enqueueSnackbar('Lieferart gelöscht.', { variant: 'success' });
|
||||||
|
} catch (err) {
|
||||||
|
notifyError(err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { disabled, sx } = this.props;
|
||||||
|
const item = this.props.carrier ?? this.props.item;
|
||||||
|
const { draft } = this.state;
|
||||||
|
const name = item?.name ?? '';
|
||||||
|
const hasPackages = Boolean(item?.hasPackages);
|
||||||
|
const isReverse = Boolean(item?.isReverse);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ListItem
|
||||||
|
disableGutters
|
||||||
|
data-id={item?.id}
|
||||||
|
sx={{
|
||||||
|
gap: 1,
|
||||||
|
py: 0.75,
|
||||||
|
alignItems: 'center',
|
||||||
|
width: '100%',
|
||||||
|
minWidth: 0,
|
||||||
|
flexWrap: { xs: 'wrap', sm: 'nowrap' },
|
||||||
|
...sx,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box
|
||||||
|
className="drag-handle"
|
||||||
|
sx={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
width: 40,
|
||||||
|
height: 40,
|
||||||
|
flexShrink: 0,
|
||||||
|
color: 'text.disabled',
|
||||||
|
cursor: disabled ? 'default' : 'grab',
|
||||||
|
userSelect: 'none',
|
||||||
|
touchAction: 'none',
|
||||||
|
'&:hover': disabled ? undefined : { color: 'text.secondary' },
|
||||||
|
transition: 'color 0.15s',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DragIndicatorIcon sx={{ fontSize: 24, pointerEvents: 'none' }} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<SingleLineTextEdit
|
||||||
|
size="small"
|
||||||
|
label=""
|
||||||
|
value={draft}
|
||||||
|
disabled={disabled || !item}
|
||||||
|
onChange={this.handleChange}
|
||||||
|
onBlur={this.handleBlur}
|
||||||
|
onKeyDown={this.handleKeyDown}
|
||||||
|
sx={{ flex: 1, minWidth: 140 }}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1.5, flexShrink: 0 }}>
|
||||||
|
<Tooltip title="Paketverfolgung / Tracking für Sendungen aktivieren">
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={hasPackages}
|
||||||
|
disabled={disabled || !item}
|
||||||
|
onChange={this.handleToggleHasPackages}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography variant="caption" sx={{ userSelect: 'none', fontWeight: 500 }}>
|
||||||
|
Pakete
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
sx={{ m: 0, gap: 0.5 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
|
||||||
|
<Tooltip title="Für Retouren / Rücksendungen verwenden">
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Switch
|
||||||
|
size="small"
|
||||||
|
checked={isReverse}
|
||||||
|
disabled={disabled || !item}
|
||||||
|
onChange={this.handleToggleIsReverse}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={
|
||||||
|
<Typography variant="caption" sx={{ userSelect: 'none', fontWeight: 500 }}>
|
||||||
|
Retoure
|
||||||
|
</Typography>
|
||||||
|
}
|
||||||
|
sx={{ m: 0, gap: 0.5 }}
|
||||||
|
/>
|
||||||
|
</Tooltip>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
aria-label={`„${name}“ löschen`}
|
||||||
|
disabled={disabled || !item}
|
||||||
|
onClick={this.handleDelete}
|
||||||
|
sx={{ width: 40, height: 40, flexShrink: 0 }}
|
||||||
|
>
|
||||||
|
<DeleteOutlinedIcon sx={{ fontSize: 24 }} />
|
||||||
|
</IconButton>
|
||||||
|
</ListItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CarrierItem;
|
||||||
1
src/components/carrier/index.js
Normal file
1
src/components/carrier/index.js
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export { default as CarrierItem } from './CarrierItem';
|
||||||
@@ -31,16 +31,17 @@ import { notifyError } from '../../utils/notify';
|
|||||||
import { formatDate } from './manifestConfig';
|
import { formatDate } from './manifestConfig';
|
||||||
import orderAutomat from '../../automats/orderAutomat';
|
import orderAutomat from '../../automats/orderAutomat';
|
||||||
import shipmentAutomat from '../../automats/shipmentAutomat';
|
import shipmentAutomat from '../../automats/shipmentAutomat';
|
||||||
|
import carrierAutomat from '../../automats/carrierAutomat';
|
||||||
import { SURFACE_RAISED, WHITE } from '../../colors';
|
import { SURFACE_RAISED, WHITE } from '../../colors';
|
||||||
|
|
||||||
const CARRIERS = ['DHL', 'UPS', 'DPD', 'GLS', 'Hermes', 'Spedition', 'Selbstabholung'];
|
|
||||||
|
|
||||||
// MARK: <AddPackageDialog/>
|
// MARK: <AddPackageDialog/>
|
||||||
class AddPackageDialog extends PureComponent {
|
class AddPackageDialog extends PureComponent {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
const carriers = carrierAutomat.state.carriers || [];
|
||||||
this.state = {
|
this.state = {
|
||||||
carrier: 'DHL',
|
carriers,
|
||||||
|
carrierId: carriers[0]?.id ?? '',
|
||||||
trackingCode: '',
|
trackingCode: '',
|
||||||
type: 'parcel',
|
type: 'parcel',
|
||||||
state: 'draft',
|
state: 'draft',
|
||||||
@@ -49,15 +50,34 @@ class AddPackageDialog extends PureComponent {
|
|||||||
this.handleSubmit = this.handleSubmit.bind(this);
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.unsub = carrierAutomat.subscribe(this, (state) => {
|
||||||
|
const carriers = state.carriers || [];
|
||||||
|
const currentId = this.state.carrierId;
|
||||||
|
const valid = carriers.some((c) => c.id === currentId);
|
||||||
|
return {
|
||||||
|
carriers,
|
||||||
|
carrierId: valid ? currentId : (carriers[0]?.id ?? ''),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
if (carrierAutomat.state.status !== 'ready') {
|
||||||
|
void carrierAutomat.actions.load().catch(() => {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unsub?.();
|
||||||
|
}
|
||||||
|
|
||||||
async handleSubmit() {
|
async handleSubmit() {
|
||||||
const { shipment, onClose, onSuccess } = this.props;
|
const { shipment, onClose, onSuccess } = this.props;
|
||||||
const { carrier, trackingCode, type, state } = this.state;
|
const { carrierId, trackingCode, type, state } = this.state;
|
||||||
if (!shipment?.id) return;
|
if (!shipment?.id || !carrierId) return;
|
||||||
|
|
||||||
this.setState({ saving: true });
|
this.setState({ saving: true });
|
||||||
try {
|
try {
|
||||||
await shippingApi.create(shipment.id, {
|
await shippingApi.create(shipment.id, {
|
||||||
carrier: carrier.trim() || undefined,
|
carrierId: Number(carrierId),
|
||||||
trackingCode: trackingCode.trim() || undefined,
|
trackingCode: trackingCode.trim() || undefined,
|
||||||
type,
|
type,
|
||||||
state,
|
state,
|
||||||
@@ -77,7 +97,8 @@ class AddPackageDialog extends PureComponent {
|
|||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { open, onClose, shipment } = this.props;
|
const { open, onClose, shipment } = this.props;
|
||||||
const { carrier, trackingCode, type, state, saving } = this.state;
|
const { carriers, carrierId, trackingCode, type, state, saving } = this.state;
|
||||||
|
const canSubmit = Boolean(carrierId) && !saving;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppDialog open={open} onClose={onClose} maxWidth="xs" fullWidth>
|
<AppDialog open={open} onClose={onClose} maxWidth="xs" fullWidth>
|
||||||
@@ -97,13 +118,21 @@ class AddPackageDialog extends PureComponent {
|
|||||||
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mb: 3 }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', gap: 2, mb: 3 }}>
|
||||||
<TextField
|
<TextField
|
||||||
select
|
select
|
||||||
label="Versanddienstleister (Carrier)"
|
label="Lieferart (Carrier)"
|
||||||
size="small"
|
size="small"
|
||||||
value={carrier}
|
value={carrierId}
|
||||||
onChange={(e) => this.setState({ carrier: e.target.value })}
|
onChange={(e) => this.setState({ carrierId: e.target.value })}
|
||||||
|
error={carriers.length === 0}
|
||||||
|
helperText={
|
||||||
|
carriers.length === 0
|
||||||
|
? 'Keine Lieferarten eingerichtet (unter Auftrag → Lieferarten anlegen)'
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
>
|
>
|
||||||
{CARRIERS.map((c) => (
|
{carriers.map((c) => (
|
||||||
<MenuItem key={c} value={c}>{c}</MenuItem>
|
<MenuItem key={c.id} value={c.id}>
|
||||||
|
{c.name}
|
||||||
|
</MenuItem>
|
||||||
))}
|
))}
|
||||||
</TextField>
|
</TextField>
|
||||||
|
|
||||||
@@ -145,7 +174,7 @@ class AddPackageDialog extends PureComponent {
|
|||||||
<Button variant="outlined" onClick={onClose} disabled={saving}>
|
<Button variant="outlined" onClick={onClose} disabled={saving}>
|
||||||
Abbrechen
|
Abbrechen
|
||||||
</Button>
|
</Button>
|
||||||
<Button variant="contained" onClick={this.handleSubmit} disabled={saving}>
|
<Button variant="contained" onClick={this.handleSubmit} disabled={!canSubmit}>
|
||||||
{saving ? 'Wird gespeichert…' : 'Paket anlegen'}
|
{saving ? 'Wird gespeichert…' : 'Paket anlegen'}
|
||||||
</Button>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import StorefrontIcon from '@mui/icons-material/Storefront';
|
|||||||
import RequestQuoteIcon from '@mui/icons-material/RequestQuote';
|
import RequestQuoteIcon from '@mui/icons-material/RequestQuote';
|
||||||
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
|
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';
|
||||||
import PaymentsIcon from '@mui/icons-material/Payments';
|
import PaymentsIcon from '@mui/icons-material/Payments';
|
||||||
|
import LocalShippingOutlinedIcon from '@mui/icons-material/LocalShippingOutlined';
|
||||||
import LabelIcon from '@mui/icons-material/Label';
|
import LabelIcon from '@mui/icons-material/Label';
|
||||||
|
|
||||||
import HistoryIcon from '@mui/icons-material/History';
|
import HistoryIcon from '@mui/icons-material/History';
|
||||||
@@ -226,6 +227,13 @@ export const SUB_NAV = {
|
|||||||
icon: PaymentsIcon,
|
icon: PaymentsIcon,
|
||||||
help: 'Zahlungseingänge erfassen und Auftragszahlungen überwachen',
|
help: 'Zahlungseingänge erfassen und Auftragszahlungen überwachen',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
segment: 'lieferarten',
|
||||||
|
label: 'Lieferarten',
|
||||||
|
title: 'Lieferarten',
|
||||||
|
icon: LocalShippingOutlinedIcon,
|
||||||
|
help: 'Versanddienstleister und Lieferarten (Carriers) verwalten',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
'/kunde': [
|
'/kunde': [
|
||||||
{
|
{
|
||||||
|
|||||||
270
src/routes/auftrag/lieferarten.jsx
Normal file
270
src/routes/auftrag/lieferarten.jsx
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
import { PureComponent, createRef } from 'react';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import CircularProgress from '@mui/material/CircularProgress';
|
||||||
|
import List from '@mui/material/List';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Sortable from 'sortablejs';
|
||||||
|
import { enqueueSnackbar } from 'notistack';
|
||||||
|
import BackendError from '../../components/BackendError';
|
||||||
|
import SingleLineTextEdit from 'dashgui/SingleLineTextEdit';
|
||||||
|
import { PanelTile, PanelTileGrid } from 'dashgui/panels';
|
||||||
|
import withNavigate from '../../components/withNavigate';
|
||||||
|
import { backendErrorFrom } from '../../utils/backendError';
|
||||||
|
import { notifyError } from '../../utils/notify';
|
||||||
|
import carrierAutomat from '../../automats/carrierAutomat';
|
||||||
|
import { CarrierItem } from '../../components/carrier';
|
||||||
|
import { GREEN_PANEL } from '../../colors';
|
||||||
|
|
||||||
|
class LieferartenSearchField extends PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const initial = carrierAutomat.state.filter || '';
|
||||||
|
this.state = {
|
||||||
|
input: initial,
|
||||||
|
submitting: false,
|
||||||
|
};
|
||||||
|
this.handleSubmit = this.handleSubmit.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.unsub = carrierAutomat.subscribe(this, (state) => ({
|
||||||
|
input: state.filter,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this.unsub?.();
|
||||||
|
}
|
||||||
|
|
||||||
|
onInputChange(input) {
|
||||||
|
this.setState({ input });
|
||||||
|
carrierAutomat.actions.filter(input);
|
||||||
|
}
|
||||||
|
|
||||||
|
async handleSubmit() {
|
||||||
|
const name = this.state.input.trim();
|
||||||
|
if (!name || this.state.submitting) return;
|
||||||
|
|
||||||
|
this.setState({ submitting: true });
|
||||||
|
try {
|
||||||
|
const row = await carrierAutomat.actions.create({
|
||||||
|
name,
|
||||||
|
hasPackages: true,
|
||||||
|
isReverse: false,
|
||||||
|
});
|
||||||
|
enqueueSnackbar(`Lieferart „${row.name || name}“ angelegt.`, { variant: 'success' });
|
||||||
|
this.setState({ input: '', submitting: false });
|
||||||
|
carrierAutomat.actions.filter('');
|
||||||
|
} catch (err) {
|
||||||
|
this.setState({ submitting: false });
|
||||||
|
notifyError(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { inputRef } = this.props;
|
||||||
|
const { input, submitting } = this.state;
|
||||||
|
const canSubmit = Boolean(input.trim()) && !submitting;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PanelTileGrid>
|
||||||
|
<Box
|
||||||
|
ref={inputRef}
|
||||||
|
sx={{
|
||||||
|
gridColumn: {
|
||||||
|
xs: '1 / -1',
|
||||||
|
sm: 'span 2',
|
||||||
|
md: 'span 2',
|
||||||
|
},
|
||||||
|
display: 'flex',
|
||||||
|
flexDirection: 'column',
|
||||||
|
gap: 1,
|
||||||
|
pb: 2,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
|
||||||
|
<SingleLineTextEdit
|
||||||
|
sx={{ flex: 1 }}
|
||||||
|
size="small"
|
||||||
|
search
|
||||||
|
clear
|
||||||
|
placeholder="Lieferart suchen oder neu anlegen..."
|
||||||
|
value={input}
|
||||||
|
disabled={submitting}
|
||||||
|
onChange={(next) => this.onInputChange(next)}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === 'Enter') {
|
||||||
|
event.preventDefault();
|
||||||
|
void this.handleSubmit();
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
disabled={!canSubmit}
|
||||||
|
onClick={() => void this.handleSubmit()}
|
||||||
|
>
|
||||||
|
Hinzufügen
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</PanelTileGrid>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AuftragLieferartenPage extends PureComponent {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
const initialCarriers =
|
||||||
|
carrierAutomat.state.filteredCarriers ?? carrierAutomat.state.carriers ?? [];
|
||||||
|
this.state = {
|
||||||
|
status: carrierAutomat.state.status === 'ready' ? 'ready' : 'loading',
|
||||||
|
error: carrierAutomat.state.error ? backendErrorFrom(carrierAutomat.state.error) : null,
|
||||||
|
carriers: initialCarriers,
|
||||||
|
filter: carrierAutomat.state.filter ?? '',
|
||||||
|
};
|
||||||
|
this.searchRef = createRef();
|
||||||
|
this.listRef = createRef();
|
||||||
|
this._sortable = null;
|
||||||
|
this._cancelled = false;
|
||||||
|
this.handleRetry = this.handleRetry.bind(this);
|
||||||
|
this._initSortable = this._initSortable.bind(this);
|
||||||
|
this._destroySortable = this._destroySortable.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this._cancelled = false;
|
||||||
|
|
||||||
|
this.unsub = carrierAutomat.subscribe(this, (state) => ({
|
||||||
|
carriers: state.filteredCarriers ?? state.carriers,
|
||||||
|
filter: state.filter ?? '',
|
||||||
|
status: state.status,
|
||||||
|
error: state.error ? backendErrorFrom(state.error) : null,
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (carrierAutomat.state.status !== 'ready') {
|
||||||
|
void carrierAutomat.actions.load().catch((err) => notifyError(err));
|
||||||
|
} else {
|
||||||
|
this._initSortable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (
|
||||||
|
prevState.status !== this.state.status ||
|
||||||
|
prevState.carriers !== this.state.carriers ||
|
||||||
|
prevState.filter !== this.state.filter
|
||||||
|
) {
|
||||||
|
if (this.state.status === 'ready') {
|
||||||
|
this._initSortable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
componentWillUnmount() {
|
||||||
|
this._cancelled = true;
|
||||||
|
this.unsub?.();
|
||||||
|
this._destroySortable();
|
||||||
|
}
|
||||||
|
|
||||||
|
_initSortable() {
|
||||||
|
this._destroySortable();
|
||||||
|
const el = this.listRef.current;
|
||||||
|
if (!el) return;
|
||||||
|
|
||||||
|
const isFiltered = Boolean(this.state.filter?.trim());
|
||||||
|
|
||||||
|
this._sortable = Sortable.create(el, {
|
||||||
|
handle: '.drag-handle',
|
||||||
|
animation: 150,
|
||||||
|
ghostClass: 'sortable-ghost',
|
||||||
|
chosenClass: 'sortable-chosen',
|
||||||
|
disabled: isFiltered,
|
||||||
|
onEnd: (evt) => {
|
||||||
|
const { oldIndex, newIndex } = evt;
|
||||||
|
if (oldIndex === newIndex) return;
|
||||||
|
|
||||||
|
this.setState(
|
||||||
|
(prev) => {
|
||||||
|
const next = [...prev.carriers];
|
||||||
|
const [moved] = next.splice(oldIndex, 1);
|
||||||
|
next.splice(newIndex, 0, moved);
|
||||||
|
return { carriers: next };
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
const ids = this.state.carriers.map((c) => c.id);
|
||||||
|
void this._persistOrder(ids);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
_destroySortable() {
|
||||||
|
this._sortable?.destroy();
|
||||||
|
this._sortable = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async _persistOrder(carrierIds) {
|
||||||
|
try {
|
||||||
|
await carrierAutomat.actions.reorder(carrierIds);
|
||||||
|
} catch (err) {
|
||||||
|
if (this._cancelled) return;
|
||||||
|
notifyError(err);
|
||||||
|
void carrierAutomat.actions.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleRetry() {
|
||||||
|
void carrierAutomat.actions.load().catch((err) => notifyError(err));
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
const { status, error, carriers, filter } = this.state;
|
||||||
|
|
||||||
|
if (status === 'loading' || status === 'idle') {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', justifyContent: 'center', py: 6 }}>
|
||||||
|
<CircularProgress />
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status === 'failed') {
|
||||||
|
return <BackendError error={error} onRetry={this.handleRetry} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmedFilter = filter?.trim() ?? '';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<LieferartenSearchField inputRef={this.searchRef} />
|
||||||
|
<PanelTileGrid>
|
||||||
|
<PanelTile wide={2} minHeight={0} color={GREEN_PANEL}>
|
||||||
|
<List dense disablePadding ref={this.listRef} sx={{ width: '100%' }}>
|
||||||
|
{carriers.length === 0 ? (
|
||||||
|
<Typography variant="body2" color="text.secondary" sx={{ py: 1 }}>
|
||||||
|
{trimmedFilter
|
||||||
|
? `Keine Treffer für „${trimmedFilter}“.`
|
||||||
|
: 'Noch keine Lieferarten angelegt.'}
|
||||||
|
</Typography>
|
||||||
|
) : (
|
||||||
|
carriers.map((c) => (
|
||||||
|
<CarrierItem
|
||||||
|
key={c.id}
|
||||||
|
carrier={c}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</List>
|
||||||
|
</PanelTile>
|
||||||
|
</PanelTileGrid>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { CarrierItem, LieferartenSearchField };
|
||||||
|
export default withNavigate(AuftragLieferartenPage);
|
||||||
Reference in New Issue
Block a user