diff --git a/API.md b/API.md index e5831d1..2a35197 100644 --- a/API.md +++ b/API.md @@ -207,15 +207,15 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met - `syncSummary(): void` - `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - [order](API.order.md) - - `create(googleAuthId: number, body: {number: string, date: string, entityId: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - - `createFromQuote(googleAuthId: number, quoteId: number, body?: {number?: string, date?: string, createdAt?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` + - `create(googleAuthId: number, body: {number: string, date: string, entityId: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` + - `createFromQuote(googleAuthId: number, quoteId: number, body?: {number?: string, date?: string, createdAt?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - `createShipment(googleAuthId: number, orderId: number, input: {lines: Array<{sku: string, amount?: number, name?: string, pu?: number, net?: number, rebate?: number, taxrate?: number, comment?: string}>, date?: string|Date, externalNumber?: string}, tx?: object): Promise` - `delete(googleAuthId: number, id: number, tx?: object): Promise` - `getById(googleAuthId: number, id: number, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - `list(googleAuthId: number, opts?: {offset?: number, limit?: number, search?: string, onlyUnpaid?: boolean, onlyUndelivered?: boolean, seekMonths?: number}, tx?: object): Promise<{orders: Array<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, createdAt: string|null, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>, total: number, unfilteredTotal: number, offset: number, limit: number}|Array>` - `rebuildSummaries(provider: import('../../providers/provider.js').DatabaseProvider): void` - `syncSummary(): void` - - `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` + - `update(googleAuthId: number, id: number, patch: {number?: string, date?: string, entityId?: number, externalNumber?: string, lines?: Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>}, tx?: object): Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>` - [shipment](API.shipment.md) - `create(provider: import('../../providers/provider.js').DatabaseProvider): void` - `delete(provider: import('../../providers/provider.js').DatabaseProvider): void` diff --git a/API.order.md b/API.order.md index 4fc62af..21a93b6 100644 --- a/API.order.md +++ b/API.order.md @@ -18,7 +18,7 @@ Creates a new order for the user's selected company with its lines and records a | `body.date` | `string` | The order date. | | `body.entityId` | `number` | The id of the customer entity. | | `[body.externalNumber]` | `string` | An external (customer) reference number. | -| `[body.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The order lines. | +| `[body.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>` | The order lines. `shippable` (default true) false marks non-shippable lines (e.g. shipping cost, rounding) that are excluded from shipment and order completeness. | | `[tx]` | `object` | Optional transaction to run within. | **Returns** @@ -54,7 +54,7 @@ Creates a new sales order from an existing quote for the user's selected company | `[body.createdAt]` | `string` | Creation timestamp; defaults to current time if omitted. | | `[body.entityId]` | `number` | Customer entity id override; defaults to quote's entityId. | | `[body.externalNumber]` | `string` | Customer reference override; defaults to quote's number. | -| `[body.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | Lines override; defaults to quote's lines. | +| `[body.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>` | Lines override; defaults to quote's lines. `shippable` (default true) false excludes a line from shipment and order completeness. | | `[tx]` | `object` | Optional transaction to run within. | **Returns** @@ -226,7 +226,7 @@ Updates an order's header and/or lines. | `[patch.date]` | `string` | The new order date. | | `[patch.entityId]` | `number` | The new customer entity id. | | `[patch.externalNumber]` | `string` | The new external (customer) reference number. | -| `[patch.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>` | The new order lines. | +| `[patch.lines]` | `Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>` | The new order lines. `shippable` (default true) false marks non-shippable lines excluded from shipment and order completeness. | | `[tx]` | `object` | Optional transaction to run within. | **Returns** diff --git a/src/providers/mssql/queries/manifestDoc.js b/src/providers/mssql/queries/manifestDoc.js index b45c68f..18a62d6 100644 --- a/src/providers/mssql/queries/manifestDoc.js +++ b/src/providers/mssql/queries/manifestDoc.js @@ -55,6 +55,7 @@ SELECT ma.net, ma.rebate, ma.taxrate, + ma.shippable, ma.comment FROM manifestarticle ma WHERE ma.manifest_id IN (SELECT CAST(value AS INT) FROM OPENJSON(@manifestIdsJson)) @@ -86,6 +87,7 @@ SELECT ma.net, ma.rebate, ma.taxrate, + ma.shippable, ma.comment FROM manifestarticle ma WHERE ma.manifest_id = @manifestId @@ -133,7 +135,8 @@ INSERT INTO manifestarticle ( net, comment, rebate, - taxrate + taxrate, + shippable ) OUTPUT INSERTED.id VALUES ( @@ -145,7 +148,8 @@ VALUES ( @net, @comment, @rebate, - @taxrate + @taxrate, + @shippable ) `; diff --git a/src/providers/mssql/queries/shipment.js b/src/providers/mssql/queries/shipment.js index 68378f3..eb84e45 100644 --- a/src/providers/mssql/queries/shipment.js +++ b/src/providers/mssql/queries/shipment.js @@ -7,7 +7,7 @@ WHERE id = @orderId AND company_id = @companyId AND [type] = 'order'; `; export const GET_ORDER_LINES = ` -SELECT id, sku, name, pu, amount, net, rebate, taxrate, comment +SELECT id, sku, name, pu, amount, net, rebate, taxrate, shippable, comment FROM manifestarticle WHERE manifest_id = @orderId ORDER BY id ASC; @@ -31,7 +31,7 @@ ORDER BY m.id ASC; `; export const GET_SHIPPED_AMOUNTS_BY_ORDER_PATTERN = ` -SELECT ma.sku, SUM(ma.amount) AS shippedAmount +SELECT ma.sku, ma.name, SUM(ma.amount) AS shippedAmount FROM manifest m INNER JOIN manifestarticle ma ON ma.manifest_id = m.id LEFT JOIN shipmentsummary ss ON ss.manifest_id = m.id @@ -40,7 +40,7 @@ WHERE m.company_id = @companyId AND m.[number] LIKE @pattern AND (@excludeShipmentId IS NULL OR m.id != @excludeShipmentId) AND (ss.state IS NULL OR ss.state != 'cancelled') -GROUP BY ma.sku; +GROUP BY ma.sku, ma.name; `; export const GET_SHIPMENT_HEADER = ` diff --git a/src/providers/mssql/schema.js b/src/providers/mssql/schema.js index 9d3f217..e450916 100644 --- a/src/providers/mssql/schema.js +++ b/src/providers/mssql/schema.js @@ -667,6 +667,7 @@ BEGIN comment NVARCHAR(MAX) NULL, rebate FLOAT NOT NULL CONSTRAINT DF_manifestarticle_rebate DEFAULT 0, taxrate FLOAT NOT NULL CONSTRAINT DF_manifestarticle_taxrate DEFAULT 0, + shippable TINYINT NOT NULL CONSTRAINT DF_manifestarticle_shippable DEFAULT 1, CONSTRAINT FK_manifestarticle_manifest FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE ); END diff --git a/src/providers/sqlite3/queries/manifestDoc.js b/src/providers/sqlite3/queries/manifestDoc.js index acc7ed2..9c5c241 100644 --- a/src/providers/sqlite3/queries/manifestDoc.js +++ b/src/providers/sqlite3/queries/manifestDoc.js @@ -35,6 +35,7 @@ SELECT ma.net, ma.rebate, ma.taxrate, + ma.shippable, ma.comment FROM manifestarticle ma WHERE ma.manifest_id IN (SELECT value FROM json_each(@manifestIdsJson)) diff --git a/src/providers/sqlite3/schema.js b/src/providers/sqlite3/schema.js index cb22e83..3d7115b 100644 --- a/src/providers/sqlite3/schema.js +++ b/src/providers/sqlite3/schema.js @@ -448,6 +448,7 @@ CREATE TABLE IF NOT EXISTS manifestarticle ( comment TEXT, rebate REAL NOT NULL DEFAULT 0, taxrate REAL NOT NULL DEFAULT 0, + shippable INTEGER NOT NULL DEFAULT 1, FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE ); `; diff --git a/src/store/manifestLines.js b/src/store/manifestLines.js index 2655e5c..8117058 100644 --- a/src/store/manifestLines.js +++ b/src/store/manifestLines.js @@ -12,7 +12,7 @@ import makeRunner from './runner.js'; * Normalizes and validates an array of manifest line inputs. * * @param {Array} lines - The raw line inputs. - * @returns {Array<{sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>} The normalized lines. + * @returns {Array<{sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null, shippable: boolean}>} The normalized lines. * @throws {Error} If the lines are invalid. */ export function normalizeLines(lines) { @@ -36,6 +36,8 @@ export function normalizeLines(lines) { const rebate = entry?.rebate === undefined || entry?.rebate === null ? 0 : entry.rebate; const taxrate = entry?.taxrate; + const shippable = + entry?.shippable === undefined || entry?.shippable === null ? true : entry.shippable; const comment = entry?.comment === undefined || entry?.comment === null ? null @@ -46,12 +48,13 @@ export function normalizeLines(lines) { typeof amount !== 'number' || !Number.isFinite(amount) || typeof net !== 'number' || !Number.isFinite(net) || typeof rebate !== 'number' || !Number.isFinite(rebate) || - typeof taxrate !== 'number' || !Number.isFinite(taxrate) || taxrate < 0 + typeof taxrate !== 'number' || !Number.isFinite(taxrate) || taxrate < 0 || + typeof shippable !== 'boolean' ) { throw new Error('Invalid lines'); } - return { sku, name, pu, amount, net, rebate, taxrate, comment }; + return { sku, name, pu, amount, net, rebate, taxrate, comment, shippable }; }); } @@ -92,6 +95,7 @@ export function shapeLine(row) { rebate: row.rebate, taxrate, comment: row.comment ?? null, + shippable: Number(row.shippable) !== 0, }; } @@ -162,6 +166,7 @@ export async function insertLines( comment: line.comment, rebate: line.rebate, taxrate: line.taxrate, + shippable: line.shippable ? 1 : 0, }); } } diff --git a/src/store/order/create.js b/src/store/order/create.js index 0e0de23..5533d4d 100644 --- a/src/store/order/create.js +++ b/src/store/order/create.js @@ -38,7 +38,7 @@ export default function create(provider) { * @param {string} body.date - The order date. * @param {number} body.entityId - The id of the customer entity. * @param {string} [body.externalNumber] - An external (customer) reference number. - * @param {Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>} [body.lines] - The order lines. + * @param {Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>} [body.lines] - The order lines. `shippable` (default true) false marks non-shippable lines (e.g. shipping cost, rounding) that are excluded from shipment and order completeness. * @param {object} [tx] - Optional transaction to run within. * @returns {Promise<{id: number, number: string, type: string, entityId: number, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>} The created order. * @throws {Error} If the order is invalid, no entity is selected, or entity not found. diff --git a/src/store/order/createFromQuote.js b/src/store/order/createFromQuote.js index 787ddb2..c20d86f 100644 --- a/src/store/order/createFromQuote.js +++ b/src/store/order/createFromQuote.js @@ -48,7 +48,7 @@ export default function createFromQuote(provider) { * @param {string} [body.createdAt] - Creation timestamp; defaults to current time if omitted. * @param {number} [body.entityId] - Customer entity id override; defaults to quote's entityId. * @param {string} [body.externalNumber] - Customer reference override; defaults to quote's number. - * @param {Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>} [body.lines] - Lines override; defaults to quote's lines. + * @param {Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>} [body.lines] - Lines override; defaults to quote's lines. `shippable` (default true) false excludes a line from shipment and order completeness. * @param {object} [tx] - Optional transaction to run within. * @returns {Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>} The created order. * @throws {Error} If the quote is not found, no entity is selected, or the order is invalid. diff --git a/src/store/order/syncSummary.js b/src/store/order/syncSummary.js index 118d05e..c3a7522 100644 --- a/src/store/order/syncSummary.js +++ b/src/store/order/syncSummary.js @@ -33,7 +33,7 @@ const SQL_GET_ORDER_BY_NUMBER_MSSQL = ` const SQL_LINE_TOTALS = ` SELECT COUNT(*) AS positions, - COALESCE(SUM(amount), 0) AS ordered_quantity, + COALESCE(SUM(CASE WHEN shippable = 1 THEN amount ELSE 0 END), 0) AS ordered_quantity, COALESCE(SUM(amount * (net * (1.0 - rebate / 100.0)) * (1.0 + taxrate / 100.0)), 0) AS gross_total FROM manifestarticle WHERE manifest_id = @manifestId @@ -241,7 +241,7 @@ export async function rebuildOrderSummaries(runner, companyId) { SELECT manifest_id, COUNT(*) AS positions, - SUM(amount) AS ordered_quantity, + SUM(CASE WHEN shippable = 1 THEN amount ELSE 0 END) AS ordered_quantity, SUM(amount * (net * (1.0 - rebate / 100.0)) * (1.0 + taxrate / 100.0)) AS gross_total FROM manifestarticle GROUP BY manifest_id @@ -315,7 +315,7 @@ export async function rebuildOrderSummaries(runner, companyId) { SELECT manifest_id, COUNT(*) AS positions, - SUM(amount) AS ordered_quantity, + SUM(CASE WHEN shippable = 1 THEN amount ELSE 0 END) AS ordered_quantity, SUM(amount * (net * (1.0 - rebate / 100.0)) * (1.0 + taxrate / 100.0)) AS gross_total FROM manifestarticle GROUP BY manifest_id diff --git a/src/store/order/update.js b/src/store/order/update.js index cb9bb4b..24724e9 100644 --- a/src/store/order/update.js +++ b/src/store/order/update.js @@ -37,7 +37,7 @@ export default function update(provider) { * @param {string} [patch.date] - The new order date. * @param {number} [patch.entityId] - The new customer entity id. * @param {string} [patch.externalNumber] - The new external (customer) reference number. - * @param {Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null}>} [patch.lines] - The new order lines. + * @param {Array<{sku?: string, name?: string, pu?: number, amount: number, net: number, rebate?: number, taxrate: number, comment?: string|null, shippable?: boolean}>} [patch.lines] - The new order lines. `shippable` (default true) false marks non-shippable lines excluded from shipment and order completeness. * @param {object} [tx] - Optional transaction to run within. * @returns {Promise<{id: number, number: string, type: string, entityId: number|null, externalNumber: string|null, date: string, lines: Array<{id: number, sku: string|null, name: string|null, pu: number, amount: number, net: number, rebate: number, taxrate: number, comment: string|null}>}>} The updated order. * @throws {Error} If the order is not found. diff --git a/src/store/shipment/create.js b/src/store/shipment/create.js index b9461a6..6ec5704 100644 --- a/src/store/shipment/create.js +++ b/src/store/shipment/create.js @@ -33,7 +33,7 @@ export default function create(provider) { * @param {number} googleAuthId - The id of the Google auth user. * @param {object} input - The shipment input. * @param {number} input.orderId - The parent order manifest id. - * @param {Array<{sku: string, amount?: number, name?: string, pu?: number, net?: number, rebate?: number, taxrate?: number, comment?: string}>} input.lines - The shipment positions. + * @param {Array<{sku?: string, orderLineId?: number, amount?: number, name?: string, pu?: number, net?: number, rebate?: number, taxrate?: number, comment?: string}>} input.lines - The shipment positions. Order lines with a SKU are referenced by `sku`; SKU-less order lines by `orderLineId` (matched across shipments by name). * @param {string|Date} [input.date] - Optional shipment date (defaults to current date). * @param {'new'|'delivered'|'returned'} [input.state='new'] - Optional delivery state (defaults to 'new'). * @param {string} [input.externalNumber] - Optional external delivery note number (defaults to order.externalNumber or order.number). @@ -82,15 +82,21 @@ export default function create(provider) { throw new Error('Order has no lines'); } + // Lines are keyed by SKU; SKU-less lines are keyed by name (shipment lines copy the name). + const lineKey = (sku, name) => { + const s = String(sku ?? '').trim(); + return s || `\u0000name:${String(name ?? '').trim()}`; + }; const orderAmountBySku = new Map(); const orderLineBySku = new Map(); + const orderLineById = new Map(); for (const ol of orderLines) { - const sku = String(ol.sku ?? '').trim(); - if (!sku) continue; - orderAmountBySku.set(sku, (orderAmountBySku.get(sku) || 0) + Number(ol.amount || 0)); - if (!orderLineBySku.has(sku)) { - orderLineBySku.set(sku, ol); + const key = lineKey(ol.sku, ol.name); + orderLineById.set(Number(ol.id), ol); + orderAmountBySku.set(key, (orderAmountBySku.get(key) || 0) + Number(ol.amount || 0)); + if (!orderLineBySku.has(key)) { + orderLineBySku.set(key, ol); } } @@ -107,8 +113,8 @@ export default function create(provider) { }); const shippedAmountBySku = new Map(); for (const row of shippedRes.rows ?? []) { - const sku = String(row.sku ?? '').trim(); - shippedAmountBySku.set(sku, Number(row.shippedAmount || 0)); + const key = lineKey(row.sku, row.name); + shippedAmountBySku.set(key, (shippedAmountBySku.get(key) || 0) + Number(row.shippedAmount || 0)); } // 5. Determine warehouse and load stock for tracked articles @@ -124,6 +130,7 @@ export default function create(provider) { const remainingStockBySku = new Map(); for (const sku of orderAmountBySku.keys()) { + if (sku.startsWith('\u0000name:')) continue; // SKU-less lines carry no stock const artRes = await runner.query(q.GET_ARTICLE_TRACK_STOCK, { companyId, sku }); const art = artRes.rows[0]; const isTracked = Number(art?.track_stock ?? 0) === 1; @@ -152,9 +159,25 @@ export default function create(provider) { const proposedBySku = new Map(); for (const line of input.lines) { - const sku = String(line?.sku ?? '').trim(); - if (!sku || !orderAmountBySku.has(sku)) { - throw new Error(`Article (${sku}) is not part of order (${order.number})`); + const rawSku = String(line?.sku ?? '').trim(); + let sku = rawSku; + let shipSku = rawSku; + let label = rawSku; + if (!rawSku) { + // SKU-less line: referenced by order line id + const target = orderLineById.get(Number(line?.orderLineId)); + if (!target || String(target.sku ?? '').trim()) { + throw new Error(`Article (${line?.orderLineId ?? ''}) is not part of order (${order.number})`); + } + sku = lineKey(null, target.name); + shipSku = null; + label = String(target.name ?? '').trim(); + } + if (!orderAmountBySku.has(sku)) { + throw new Error(`Article (${label}) is not part of order (${order.number})`); + } + if (Number(orderLineBySku.get(sku)?.shippable) === 0) { + throw new Error(`Article (${label}) is not shippable`); } const ordered = orderAmountBySku.get(sku) || 0; @@ -170,14 +193,14 @@ export default function create(provider) { } else { // Default to remaining quantity if (remaining <= 0) { - throw new Error(`Shipment amount exceeds ordered amount for article (${sku})`); + throw new Error(`Shipment amount exceeds ordered amount for article (${label})`); } amount = remaining; } const currentProposed = proposedBySku.get(sku) || 0; if (amount > 0 && alreadyShipped + currentProposed + amount > ordered) { - throw new Error(`Shipment amount exceeds ordered amount for article (${sku})`); + throw new Error(`Shipment amount exceeds ordered amount for article (${label})`); } const info = articleInfoBySku.get(sku); @@ -193,14 +216,15 @@ export default function create(provider) { const ol = orderLineBySku.get(sku); linesToInsert.push({ - sku, - name: line.name ?? ol?.name ?? sku, + sku: shipSku || null, + name: shipSku ? (line.name ?? ol?.name ?? shipSku) : ol.name, pu: line.pu !== undefined ? line.pu : (ol?.pu ?? 1), amount, net: line.net !== undefined ? Number(line.net) : Number(ol?.net ?? 0), rebate: line.rebate !== undefined ? Number(line.rebate) : Number(ol?.rebate ?? 0), taxrate: line.taxrate !== undefined ? Number(line.taxrate) : Number(ol?.taxrate ?? 0), comment: line.comment !== undefined ? (line.comment ? String(line.comment) : null) : (ol?.comment ?? null), + shippable: true, }); } diff --git a/src/store/stock/adjust.js b/src/store/stock/adjust.js index fb1348d..36f4f7b 100644 --- a/src/store/stock/adjust.js +++ b/src/store/stock/adjust.js @@ -338,6 +338,7 @@ export default function adjust(provider) { comment: item.text, rebate: 0, taxrate: 0, + shippable: 1, }); const manifestarticleId = lineRes.rows[0]?.id ?? lineRes.insertId;