u
This commit is contained in:
6
API.md
6
API.md
@@ -207,15 +207,15 @@ The store is exposed as a `LibDashStore` instance (see `src/index.js`). Each met
|
|||||||
- `syncSummary(): 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}>}, 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)
|
- [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}>}>`
|
- `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}>}, 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}>}>`
|
- `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<object>`
|
- `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<object>`
|
||||||
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
|
- `delete(googleAuthId: number, id: number, tx?: object): Promise<void>`
|
||||||
- `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}>}>`
|
- `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<object>>`
|
- `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<object>>`
|
||||||
- `rebuildSummaries(provider: import('../../providers/provider.js').DatabaseProvider): void`
|
- `rebuildSummaries(provider: import('../../providers/provider.js').DatabaseProvider): void`
|
||||||
- `syncSummary(): 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)
|
- [shipment](API.shipment.md)
|
||||||
- `create(provider: import('../../providers/provider.js').DatabaseProvider): void`
|
- `create(provider: import('../../providers/provider.js').DatabaseProvider): void`
|
||||||
- `delete(provider: import('../../providers/provider.js').DatabaseProvider): void`
|
- `delete(provider: import('../../providers/provider.js').DatabaseProvider): void`
|
||||||
|
|||||||
@@ -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.date` | `string` | The order date. |
|
||||||
| `body.entityId` | `number` | The id of the customer entity. |
|
| `body.entityId` | `number` | The id of the customer entity. |
|
||||||
| `[body.externalNumber]` | `string` | An external (customer) reference number. |
|
| `[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. |
|
| `[tx]` | `object` | Optional transaction to run within. |
|
||||||
|
|
||||||
**Returns**
|
**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.createdAt]` | `string` | Creation timestamp; defaults to current time if omitted. |
|
||||||
| `[body.entityId]` | `number` | Customer entity id override; defaults to quote's entityId. |
|
| `[body.entityId]` | `number` | Customer entity id override; defaults to quote's entityId. |
|
||||||
| `[body.externalNumber]` | `string` | Customer reference override; defaults to quote's number. |
|
| `[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. |
|
| `[tx]` | `object` | Optional transaction to run within. |
|
||||||
|
|
||||||
**Returns**
|
**Returns**
|
||||||
@@ -226,7 +226,7 @@ Updates an order's header and/or lines.
|
|||||||
| `[patch.date]` | `string` | The new order date. |
|
| `[patch.date]` | `string` | The new order date. |
|
||||||
| `[patch.entityId]` | `number` | The new customer entity id. |
|
| `[patch.entityId]` | `number` | The new customer entity id. |
|
||||||
| `[patch.externalNumber]` | `string` | The new external (customer) reference number. |
|
| `[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. |
|
| `[tx]` | `object` | Optional transaction to run within. |
|
||||||
|
|
||||||
**Returns**
|
**Returns**
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ SELECT
|
|||||||
ma.net,
|
ma.net,
|
||||||
ma.rebate,
|
ma.rebate,
|
||||||
ma.taxrate,
|
ma.taxrate,
|
||||||
|
ma.shippable,
|
||||||
ma.comment
|
ma.comment
|
||||||
FROM manifestarticle ma
|
FROM manifestarticle ma
|
||||||
WHERE ma.manifest_id IN (SELECT CAST(value AS INT) FROM OPENJSON(@manifestIdsJson))
|
WHERE ma.manifest_id IN (SELECT CAST(value AS INT) FROM OPENJSON(@manifestIdsJson))
|
||||||
@@ -86,6 +87,7 @@ SELECT
|
|||||||
ma.net,
|
ma.net,
|
||||||
ma.rebate,
|
ma.rebate,
|
||||||
ma.taxrate,
|
ma.taxrate,
|
||||||
|
ma.shippable,
|
||||||
ma.comment
|
ma.comment
|
||||||
FROM manifestarticle ma
|
FROM manifestarticle ma
|
||||||
WHERE ma.manifest_id = @manifestId
|
WHERE ma.manifest_id = @manifestId
|
||||||
@@ -133,7 +135,8 @@ INSERT INTO manifestarticle (
|
|||||||
net,
|
net,
|
||||||
comment,
|
comment,
|
||||||
rebate,
|
rebate,
|
||||||
taxrate
|
taxrate,
|
||||||
|
shippable
|
||||||
)
|
)
|
||||||
OUTPUT INSERTED.id
|
OUTPUT INSERTED.id
|
||||||
VALUES (
|
VALUES (
|
||||||
@@ -145,7 +148,8 @@ VALUES (
|
|||||||
@net,
|
@net,
|
||||||
@comment,
|
@comment,
|
||||||
@rebate,
|
@rebate,
|
||||||
@taxrate
|
@taxrate,
|
||||||
|
@shippable
|
||||||
)
|
)
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ WHERE id = @orderId AND company_id = @companyId AND [type] = 'order';
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_ORDER_LINES = `
|
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
|
FROM manifestarticle
|
||||||
WHERE manifest_id = @orderId
|
WHERE manifest_id = @orderId
|
||||||
ORDER BY id ASC;
|
ORDER BY id ASC;
|
||||||
@@ -31,7 +31,7 @@ ORDER BY m.id ASC;
|
|||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_SHIPPED_AMOUNTS_BY_ORDER_PATTERN = `
|
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
|
FROM manifest m
|
||||||
INNER JOIN manifestarticle ma ON ma.manifest_id = m.id
|
INNER JOIN manifestarticle ma ON ma.manifest_id = m.id
|
||||||
LEFT JOIN shipmentsummary ss ON ss.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 m.[number] LIKE @pattern
|
||||||
AND (@excludeShipmentId IS NULL OR m.id != @excludeShipmentId)
|
AND (@excludeShipmentId IS NULL OR m.id != @excludeShipmentId)
|
||||||
AND (ss.state IS NULL OR ss.state != 'cancelled')
|
AND (ss.state IS NULL OR ss.state != 'cancelled')
|
||||||
GROUP BY ma.sku;
|
GROUP BY ma.sku, ma.name;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const GET_SHIPMENT_HEADER = `
|
export const GET_SHIPMENT_HEADER = `
|
||||||
|
|||||||
@@ -667,6 +667,7 @@ BEGIN
|
|||||||
comment NVARCHAR(MAX) NULL,
|
comment NVARCHAR(MAX) NULL,
|
||||||
rebate FLOAT NOT NULL CONSTRAINT DF_manifestarticle_rebate DEFAULT 0,
|
rebate FLOAT NOT NULL CONSTRAINT DF_manifestarticle_rebate DEFAULT 0,
|
||||||
taxrate FLOAT NOT NULL CONSTRAINT DF_manifestarticle_taxrate 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
|
CONSTRAINT FK_manifestarticle_manifest FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
END
|
END
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ SELECT
|
|||||||
ma.net,
|
ma.net,
|
||||||
ma.rebate,
|
ma.rebate,
|
||||||
ma.taxrate,
|
ma.taxrate,
|
||||||
|
ma.shippable,
|
||||||
ma.comment
|
ma.comment
|
||||||
FROM manifestarticle ma
|
FROM manifestarticle ma
|
||||||
WHERE ma.manifest_id IN (SELECT value FROM json_each(@manifestIdsJson))
|
WHERE ma.manifest_id IN (SELECT value FROM json_each(@manifestIdsJson))
|
||||||
|
|||||||
@@ -448,6 +448,7 @@ CREATE TABLE IF NOT EXISTS manifestarticle (
|
|||||||
comment TEXT,
|
comment TEXT,
|
||||||
rebate REAL NOT NULL DEFAULT 0,
|
rebate REAL NOT NULL DEFAULT 0,
|
||||||
taxrate 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
|
FOREIGN KEY (manifest_id) REFERENCES manifest(id) ON DELETE CASCADE
|
||||||
);
|
);
|
||||||
`;
|
`;
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import makeRunner from './runner.js';
|
|||||||
* Normalizes and validates an array of manifest line inputs.
|
* Normalizes and validates an array of manifest line inputs.
|
||||||
*
|
*
|
||||||
* @param {Array<object>} lines - The raw line inputs.
|
* @param {Array<object>} 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.
|
* @throws {Error} If the lines are invalid.
|
||||||
*/
|
*/
|
||||||
export function normalizeLines(lines) {
|
export function normalizeLines(lines) {
|
||||||
@@ -36,6 +36,8 @@ export function normalizeLines(lines) {
|
|||||||
const rebate =
|
const rebate =
|
||||||
entry?.rebate === undefined || entry?.rebate === null ? 0 : entry.rebate;
|
entry?.rebate === undefined || entry?.rebate === null ? 0 : entry.rebate;
|
||||||
const taxrate = entry?.taxrate;
|
const taxrate = entry?.taxrate;
|
||||||
|
const shippable =
|
||||||
|
entry?.shippable === undefined || entry?.shippable === null ? true : entry.shippable;
|
||||||
const comment =
|
const comment =
|
||||||
entry?.comment === undefined || entry?.comment === null
|
entry?.comment === undefined || entry?.comment === null
|
||||||
? null
|
? null
|
||||||
@@ -46,12 +48,13 @@ export function normalizeLines(lines) {
|
|||||||
typeof amount !== 'number' || !Number.isFinite(amount) ||
|
typeof amount !== 'number' || !Number.isFinite(amount) ||
|
||||||
typeof net !== 'number' || !Number.isFinite(net) ||
|
typeof net !== 'number' || !Number.isFinite(net) ||
|
||||||
typeof rebate !== 'number' || !Number.isFinite(rebate) ||
|
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');
|
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,
|
rebate: row.rebate,
|
||||||
taxrate,
|
taxrate,
|
||||||
comment: row.comment ?? null,
|
comment: row.comment ?? null,
|
||||||
|
shippable: Number(row.shippable) !== 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -162,6 +166,7 @@ export async function insertLines(
|
|||||||
comment: line.comment,
|
comment: line.comment,
|
||||||
rebate: line.rebate,
|
rebate: line.rebate,
|
||||||
taxrate: line.taxrate,
|
taxrate: line.taxrate,
|
||||||
|
shippable: line.shippable ? 1 : 0,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ export default function create(provider) {
|
|||||||
* @param {string} body.date - The order date.
|
* @param {string} body.date - The order date.
|
||||||
* @param {number} body.entityId - The id of the customer entity.
|
* @param {number} body.entityId - The id of the customer entity.
|
||||||
* @param {string} [body.externalNumber] - An external (customer) reference number.
|
* @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.
|
* @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.
|
* @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.
|
* @throws {Error} If the order is invalid, no entity is selected, or entity not found.
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ export default function createFromQuote(provider) {
|
|||||||
* @param {string} [body.createdAt] - Creation timestamp; defaults to current time if omitted.
|
* @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 {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 {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.
|
* @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.
|
* @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.
|
* @throws {Error} If the quote is not found, no entity is selected, or the order is invalid.
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ const SQL_GET_ORDER_BY_NUMBER_MSSQL = `
|
|||||||
const SQL_LINE_TOTALS = `
|
const SQL_LINE_TOTALS = `
|
||||||
SELECT
|
SELECT
|
||||||
COUNT(*) AS positions,
|
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
|
COALESCE(SUM(amount * (net * (1.0 - rebate / 100.0)) * (1.0 + taxrate / 100.0)), 0) AS gross_total
|
||||||
FROM manifestarticle
|
FROM manifestarticle
|
||||||
WHERE manifest_id = @manifestId
|
WHERE manifest_id = @manifestId
|
||||||
@@ -241,7 +241,7 @@ export async function rebuildOrderSummaries(runner, companyId) {
|
|||||||
SELECT
|
SELECT
|
||||||
manifest_id,
|
manifest_id,
|
||||||
COUNT(*) AS positions,
|
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
|
SUM(amount * (net * (1.0 - rebate / 100.0)) * (1.0 + taxrate / 100.0)) AS gross_total
|
||||||
FROM manifestarticle
|
FROM manifestarticle
|
||||||
GROUP BY manifest_id
|
GROUP BY manifest_id
|
||||||
@@ -315,7 +315,7 @@ export async function rebuildOrderSummaries(runner, companyId) {
|
|||||||
SELECT
|
SELECT
|
||||||
manifest_id,
|
manifest_id,
|
||||||
COUNT(*) AS positions,
|
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
|
SUM(amount * (net * (1.0 - rebate / 100.0)) * (1.0 + taxrate / 100.0)) AS gross_total
|
||||||
FROM manifestarticle
|
FROM manifestarticle
|
||||||
GROUP BY manifest_id
|
GROUP BY manifest_id
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ export default function update(provider) {
|
|||||||
* @param {string} [patch.date] - The new order date.
|
* @param {string} [patch.date] - The new order date.
|
||||||
* @param {number} [patch.entityId] - The new customer entity id.
|
* @param {number} [patch.entityId] - The new customer entity id.
|
||||||
* @param {string} [patch.externalNumber] - The new external (customer) reference number.
|
* @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.
|
* @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.
|
* @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.
|
* @throws {Error} If the order is not found.
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export default function create(provider) {
|
|||||||
* @param {number} googleAuthId - The id of the Google auth user.
|
* @param {number} googleAuthId - The id of the Google auth user.
|
||||||
* @param {object} input - The shipment input.
|
* @param {object} input - The shipment input.
|
||||||
* @param {number} input.orderId - The parent order manifest id.
|
* @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 {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 {'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).
|
* @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');
|
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 orderAmountBySku = new Map();
|
||||||
const orderLineBySku = new Map();
|
const orderLineBySku = new Map();
|
||||||
|
const orderLineById = new Map();
|
||||||
|
|
||||||
for (const ol of orderLines) {
|
for (const ol of orderLines) {
|
||||||
const sku = String(ol.sku ?? '').trim();
|
const key = lineKey(ol.sku, ol.name);
|
||||||
if (!sku) continue;
|
orderLineById.set(Number(ol.id), ol);
|
||||||
orderAmountBySku.set(sku, (orderAmountBySku.get(sku) || 0) + Number(ol.amount || 0));
|
orderAmountBySku.set(key, (orderAmountBySku.get(key) || 0) + Number(ol.amount || 0));
|
||||||
if (!orderLineBySku.has(sku)) {
|
if (!orderLineBySku.has(key)) {
|
||||||
orderLineBySku.set(sku, ol);
|
orderLineBySku.set(key, ol);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,8 +113,8 @@ export default function create(provider) {
|
|||||||
});
|
});
|
||||||
const shippedAmountBySku = new Map();
|
const shippedAmountBySku = new Map();
|
||||||
for (const row of shippedRes.rows ?? []) {
|
for (const row of shippedRes.rows ?? []) {
|
||||||
const sku = String(row.sku ?? '').trim();
|
const key = lineKey(row.sku, row.name);
|
||||||
shippedAmountBySku.set(sku, Number(row.shippedAmount || 0));
|
shippedAmountBySku.set(key, (shippedAmountBySku.get(key) || 0) + Number(row.shippedAmount || 0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. Determine warehouse and load stock for tracked articles
|
// 5. Determine warehouse and load stock for tracked articles
|
||||||
@@ -124,6 +130,7 @@ export default function create(provider) {
|
|||||||
const remainingStockBySku = new Map();
|
const remainingStockBySku = new Map();
|
||||||
|
|
||||||
for (const sku of orderAmountBySku.keys()) {
|
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 artRes = await runner.query(q.GET_ARTICLE_TRACK_STOCK, { companyId, sku });
|
||||||
const art = artRes.rows[0];
|
const art = artRes.rows[0];
|
||||||
const isTracked = Number(art?.track_stock ?? 0) === 1;
|
const isTracked = Number(art?.track_stock ?? 0) === 1;
|
||||||
@@ -152,9 +159,25 @@ export default function create(provider) {
|
|||||||
const proposedBySku = new Map();
|
const proposedBySku = new Map();
|
||||||
|
|
||||||
for (const line of input.lines) {
|
for (const line of input.lines) {
|
||||||
const sku = String(line?.sku ?? '').trim();
|
const rawSku = String(line?.sku ?? '').trim();
|
||||||
if (!sku || !orderAmountBySku.has(sku)) {
|
let sku = rawSku;
|
||||||
throw new Error(`Article (${sku}) is not part of order (${order.number})`);
|
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;
|
const ordered = orderAmountBySku.get(sku) || 0;
|
||||||
@@ -170,14 +193,14 @@ export default function create(provider) {
|
|||||||
} else {
|
} else {
|
||||||
// Default to remaining quantity
|
// Default to remaining quantity
|
||||||
if (remaining <= 0) {
|
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;
|
amount = remaining;
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentProposed = proposedBySku.get(sku) || 0;
|
const currentProposed = proposedBySku.get(sku) || 0;
|
||||||
if (amount > 0 && alreadyShipped + currentProposed + amount > ordered) {
|
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);
|
const info = articleInfoBySku.get(sku);
|
||||||
@@ -193,14 +216,15 @@ export default function create(provider) {
|
|||||||
|
|
||||||
const ol = orderLineBySku.get(sku);
|
const ol = orderLineBySku.get(sku);
|
||||||
linesToInsert.push({
|
linesToInsert.push({
|
||||||
sku,
|
sku: shipSku || null,
|
||||||
name: line.name ?? ol?.name ?? sku,
|
name: shipSku ? (line.name ?? ol?.name ?? shipSku) : ol.name,
|
||||||
pu: line.pu !== undefined ? line.pu : (ol?.pu ?? 1),
|
pu: line.pu !== undefined ? line.pu : (ol?.pu ?? 1),
|
||||||
amount,
|
amount,
|
||||||
net: line.net !== undefined ? Number(line.net) : Number(ol?.net ?? 0),
|
net: line.net !== undefined ? Number(line.net) : Number(ol?.net ?? 0),
|
||||||
rebate: line.rebate !== undefined ? Number(line.rebate) : Number(ol?.rebate ?? 0),
|
rebate: line.rebate !== undefined ? Number(line.rebate) : Number(ol?.rebate ?? 0),
|
||||||
taxrate: line.taxrate !== undefined ? Number(line.taxrate) : Number(ol?.taxrate ?? 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),
|
comment: line.comment !== undefined ? (line.comment ? String(line.comment) : null) : (ol?.comment ?? null),
|
||||||
|
shippable: true,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -338,6 +338,7 @@ export default function adjust(provider) {
|
|||||||
comment: item.text,
|
comment: item.text,
|
||||||
rebate: 0,
|
rebate: 0,
|
||||||
taxrate: 0,
|
taxrate: 0,
|
||||||
|
shippable: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
const manifestarticleId = lineRes.rows[0]?.id ?? lineRes.insertId;
|
const manifestarticleId = lineRes.rows[0]?.id ?? lineRes.insertId;
|
||||||
|
|||||||
Reference in New Issue
Block a user