170 lines
4.8 KiB
JavaScript
170 lines
4.8 KiB
JavaScript
import {
|
|
CHECK_ARTICLE_IS_PARENT as MSSQL_CHECK_ARTICLE_IS_PARENT,
|
|
CREATE_LINE as MSSQL_CREATE_LINE,
|
|
} from '../providers/mssql/queries/manifestDoc.js';
|
|
import {
|
|
CHECK_ARTICLE_IS_PARENT as SQLITE_CHECK_ARTICLE_IS_PARENT,
|
|
CREATE_LINE as SQLITE_CREATE_LINE,
|
|
} from '../providers/sqlite3/queries/manifestDoc.js';
|
|
import makeRunner from './runner.js';
|
|
|
|
/**
|
|
* Normalizes and validates an array of manifest 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.
|
|
* @throws {Error} If the lines are invalid.
|
|
*/
|
|
export function normalizeLines(lines) {
|
|
if (!Array.isArray(lines)) {
|
|
throw new Error('Invalid lines');
|
|
}
|
|
|
|
return lines.map((entry) => {
|
|
const sku = entry?.sku?.trim?.() ? entry.sku.trim() : null;
|
|
const name = entry?.name?.trim?.() ? entry.name.trim() : null;
|
|
|
|
// Must have either an SKU or a name.
|
|
if (!sku && !name) {
|
|
throw new Error('Invalid lines');
|
|
}
|
|
|
|
const pu =
|
|
entry?.pu === undefined || entry?.pu === null ? null : entry.pu;
|
|
const amount = entry?.amount;
|
|
const net = entry?.net;
|
|
const rebate =
|
|
entry?.rebate === undefined || entry?.rebate === null ? 0 : entry.rebate;
|
|
const taxrate = entry?.taxrate;
|
|
const comment =
|
|
entry?.comment === undefined || entry?.comment === null
|
|
? null
|
|
: String(entry.comment);
|
|
|
|
if (
|
|
(pu !== null && (typeof pu !== 'number' || !Number.isFinite(pu) || pu <= 0)) ||
|
|
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
|
|
) {
|
|
throw new Error('Invalid lines');
|
|
}
|
|
|
|
return { sku, name, pu, amount, net, rebate, taxrate, comment };
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Parses a date value (Date instance or ISO string) into a Date.
|
|
*
|
|
* @param {Date|string} value - The date value.
|
|
* @returns {Date|null} The parsed date, or null if invalid.
|
|
*/
|
|
export function parseDate(value) {
|
|
if (value instanceof Date && !Number.isNaN(value.getTime())) {
|
|
return value;
|
|
}
|
|
if (typeof value === 'string' && value.trim()) {
|
|
const d = new Date(value);
|
|
if (!Number.isNaN(d.getTime())) {
|
|
return d;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Shapes a manifestarticle row into a line object.
|
|
*
|
|
* @param {object} row - The manifestarticle row.
|
|
* @returns {object} The shaped line.
|
|
*/
|
|
export function shapeLine(row) {
|
|
const taxrate = Number(row.taxrate);
|
|
return {
|
|
id: row.id,
|
|
sku: row.sku ?? null,
|
|
name: row.name ?? null,
|
|
pu: row.pu !== undefined && row.pu !== null ? Number(row.pu) : 1,
|
|
amount: row.amount,
|
|
net: row.net,
|
|
rebate: row.rebate,
|
|
taxrate,
|
|
comment: row.comment ?? null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Inserts manifest lines for a document, storing each SKU and taxrate directly,
|
|
* and ensuring the tax rate exists in the tax catalog.
|
|
*
|
|
* @param {object} runnerOrPool - The runner or mssql connection pool.
|
|
* @param {object} txOrParams - The transaction or params object.
|
|
* @param {object} [maybeParams] - The insert parameters.
|
|
* @returns {Promise<void>}
|
|
*/
|
|
export async function insertLines(
|
|
runnerOrPool,
|
|
txOrParams,
|
|
maybeParams,
|
|
) {
|
|
let runner;
|
|
let params;
|
|
if (maybeParams !== undefined) {
|
|
runner = makeRunner(runnerOrPool, txOrParams);
|
|
params = maybeParams;
|
|
} else {
|
|
runner = makeRunner(runnerOrPool);
|
|
params = txOrParams;
|
|
}
|
|
|
|
const { manifestId, lines, ensureTax } = params;
|
|
const isSqlite = runner?.dialect === 'sqlite3';
|
|
const createLineSql = isSqlite ? SQLITE_CREATE_LINE : MSSQL_CREATE_LINE;
|
|
const checkParentSql = isSqlite ? SQLITE_CHECK_ARTICLE_IS_PARENT : MSSQL_CHECK_ARTICLE_IS_PARENT;
|
|
|
|
for (const line of lines) {
|
|
let resolvedName = line.name;
|
|
let resolvedPu = line.pu;
|
|
|
|
if (line.sku) {
|
|
const parentCheck = await runner.query(checkParentSql, {
|
|
manifestId,
|
|
sku: line.sku,
|
|
});
|
|
const art = parentCheck.rows[0];
|
|
if (art?.isParent) {
|
|
throw new Error(`A parent article cannot be booked on documents (${line.sku})`);
|
|
}
|
|
if (!resolvedName && art?.articleName) {
|
|
resolvedName = art.articleName;
|
|
}
|
|
if (resolvedPu == null && art?.pu != null) {
|
|
resolvedPu = art.pu;
|
|
}
|
|
}
|
|
|
|
resolvedName = resolvedName || line.sku || null;
|
|
resolvedPu = resolvedPu ?? 1;
|
|
|
|
if (ensureTax) {
|
|
await ensureTax(line.taxrate, runner);
|
|
}
|
|
|
|
await runner.query(createLineSql, {
|
|
manifestId,
|
|
sku: line.sku ?? null,
|
|
name: resolvedName,
|
|
pu: resolvedPu,
|
|
amount: line.amount,
|
|
net: line.net,
|
|
comment: line.comment,
|
|
rebate: line.rebate,
|
|
taxrate: line.taxrate,
|
|
});
|
|
}
|
|
}
|
|
|
|
|