u
This commit is contained in:
@@ -99,6 +99,7 @@ import quoteRebuildSummaries from './store/quote/rebuildSummaries.js';
|
||||
import orderList from './store/order/list.js';
|
||||
import orderGetById from './store/order/getById.js';
|
||||
import orderCreate from './store/order/create.js';
|
||||
import orderCreateFromQuote from './store/order/createFromQuote.js';
|
||||
import orderUpdate from './store/order/update.js';
|
||||
import orderDelete from './store/order/delete.js';
|
||||
import orderRebuildSummaries from './store/order/rebuildSummaries.js';
|
||||
@@ -432,6 +433,7 @@ export default class LibDashStore {
|
||||
list: orderList(provider),
|
||||
getById: orderGetById(provider),
|
||||
create: orderCreate(provider),
|
||||
createFromQuote: orderCreateFromQuote(provider),
|
||||
update: orderUpdate(provider),
|
||||
delete: orderDelete(provider),
|
||||
rebuildSummaries: orderRebuildSummaries(provider),
|
||||
|
||||
177
src/store/order/createFromQuote.js
Normal file
177
src/store/order/createFromQuote.js
Normal file
@@ -0,0 +1,177 @@
|
||||
import {
|
||||
CREATE_MANIFEST as MSSQL_CREATE_MANIFEST,
|
||||
ORDER_TYPE as MSSQL_ORDER_TYPE,
|
||||
QUOTE_TYPE as MSSQL_QUOTE_TYPE,
|
||||
} from '../../providers/mssql/queries/manifestDoc.js';
|
||||
import {
|
||||
CREATE_MANIFEST as SQLITE_CREATE_MANIFEST,
|
||||
ORDER_TYPE as SQLITE_ORDER_TYPE,
|
||||
QUOTE_TYPE as SQLITE_QUOTE_TYPE,
|
||||
} from '../../providers/sqlite3/queries/manifestDoc.js';
|
||||
import {
|
||||
FIND_ARTICLES_FOR_HISTORY as MSSQL_FIND_ARTICLES,
|
||||
} from '../../providers/mssql/queries/ledger.js';
|
||||
import {
|
||||
FIND_ARTICLES_FOR_HISTORY as SQLITE_FIND_ARTICLES,
|
||||
} from '../../providers/sqlite3/queries/ledger.js';
|
||||
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
|
||||
import nextNumber from '../numbers/nextNumber.js';
|
||||
import taxEnsure from '../tax/ensure.js';
|
||||
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
|
||||
import { assertCustomerEntity, loadDoc } from '../manifestDoc/helpers.js';
|
||||
import { recordOrderHistory } from '../articlehistory/record.js';
|
||||
import { syncOrderSummary } from './syncSummary.js';
|
||||
import makeRunner from '../runner.js';
|
||||
|
||||
export default function createFromQuote(provider) {
|
||||
const access = resolveSelectedCompanyAccess(provider);
|
||||
const ensureTax = taxEnsure(provider);
|
||||
const allocateNumber = nextNumber(provider);
|
||||
const isSqlite = provider?.dialect === 'sqlite3';
|
||||
|
||||
const createManifestSql = isSqlite ? SQLITE_CREATE_MANIFEST : MSSQL_CREATE_MANIFEST;
|
||||
const orderType = isSqlite ? SQLITE_ORDER_TYPE : MSSQL_ORDER_TYPE;
|
||||
const quoteType = isSqlite ? SQLITE_QUOTE_TYPE : MSSQL_QUOTE_TYPE;
|
||||
const findArticlesSql = isSqlite ? SQLITE_FIND_ARTICLES : MSSQL_FIND_ARTICLES;
|
||||
|
||||
/**
|
||||
* Creates a new sales order from an existing quote for the user's selected company.
|
||||
* Copies lines, entity, and external number from the quote unless explicitly overridden.
|
||||
* If order number is omitted, it is auto-allocated from the 'order' number sequence.
|
||||
*
|
||||
* @param {number} googleAuthId - The id of the Google auth user.
|
||||
* @param {number} quoteId - The id of the source quote.
|
||||
* @param {object} [body] - Optional overrides for the order.
|
||||
* @param {string} [body.number] - The order number; auto-allocated from sequence if omitted.
|
||||
* @param {string} [body.date] - The order date; defaults to current date 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 {string} [body.externalNumber] - Customer reference override; defaults to quote's externalNumber.
|
||||
* @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 {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, or the order is invalid.
|
||||
*
|
||||
* @example
|
||||
* const order = await store.order.createFromQuote(42, 10);
|
||||
* // => { id, number, type: 'order', entityId, externalNumber, date, lines: [...] }
|
||||
*/
|
||||
return async (googleAuthId, quoteId, body = {}, tx) => {
|
||||
if (!Number.isFinite(quoteId)) {
|
||||
throw new Error('Quote not found');
|
||||
}
|
||||
|
||||
const runCreate = async (runner) => {
|
||||
const { companyId, companyuserId } = await access(googleAuthId, runner);
|
||||
|
||||
const quoteDoc = await loadDoc(runner, {
|
||||
type: quoteType,
|
||||
companyId,
|
||||
id: quoteId,
|
||||
});
|
||||
|
||||
if (!quoteDoc) {
|
||||
throw new Error('Quote not found');
|
||||
}
|
||||
|
||||
const date = body.date !== undefined ? parseDate(body.date) : new Date();
|
||||
if (!date) {
|
||||
throw new Error('Invalid order');
|
||||
}
|
||||
|
||||
const createdAt = body.createdAt !== undefined ? parseDate(body.createdAt) : new Date();
|
||||
if (!createdAt) {
|
||||
throw new Error('Invalid order');
|
||||
}
|
||||
|
||||
let number = body.number?.trim?.() ? body.number.trim() : null;
|
||||
if (!number) {
|
||||
number = await allocateNumber(companyId, orderType, runner);
|
||||
}
|
||||
|
||||
const targetEntityId = body.entityId !== undefined ? body.entityId : quoteDoc.entityId;
|
||||
const entityId = await assertCustomerEntity(
|
||||
runner,
|
||||
companyId,
|
||||
targetEntityId ?? null,
|
||||
);
|
||||
|
||||
const externalNumber = body.externalNumber !== undefined
|
||||
? (body.externalNumber?.trim?.() ? body.externalNumber.trim() : null)
|
||||
: (quoteDoc.externalNumber ?? null);
|
||||
|
||||
const rawLines = body.lines !== undefined ? body.lines : (quoteDoc.lines ?? []);
|
||||
const lines = normalizeLines(rawLines);
|
||||
|
||||
const created = await runner.query(createManifestSql, {
|
||||
companyId,
|
||||
number,
|
||||
type: orderType,
|
||||
entityId,
|
||||
externalNumber,
|
||||
date,
|
||||
createdAt,
|
||||
});
|
||||
|
||||
const manifestId = created.rows[0]?.id ?? created.insertId;
|
||||
|
||||
await insertLines(runner, {
|
||||
manifestId,
|
||||
lines,
|
||||
ensureTax,
|
||||
});
|
||||
|
||||
// Record article history for all order items
|
||||
let customerName = null;
|
||||
if (entityId) {
|
||||
const entRes = await runner.query('SELECT name FROM entity WHERE id = @entityId', { entityId });
|
||||
customerName = entRes.rows[0]?.name ?? null;
|
||||
}
|
||||
|
||||
const historyDate = body.createdAt ? createdAt : date;
|
||||
|
||||
for (const line of lines) {
|
||||
const sku = line.sku ? String(line.sku).trim() : '';
|
||||
if (!sku) continue;
|
||||
|
||||
const articleMatches = await runner.query(findArticlesSql, {
|
||||
sku,
|
||||
companyId,
|
||||
});
|
||||
|
||||
for (const match of articleMatches.rows) {
|
||||
await recordOrderHistory(runner, {
|
||||
articleId: match.articleId,
|
||||
companyuserId,
|
||||
date: historyDate,
|
||||
orderId: manifestId,
|
||||
number,
|
||||
customerName,
|
||||
amount: line.amount,
|
||||
net: line.net,
|
||||
rebate: line.rebate,
|
||||
taxrate: line.taxrate,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const doc = await loadDoc(runner, {
|
||||
type: orderType,
|
||||
companyId,
|
||||
id: manifestId,
|
||||
});
|
||||
|
||||
await syncOrderSummary(runner, { companyId, manifestId });
|
||||
|
||||
return doc;
|
||||
};
|
||||
|
||||
if (tx) {
|
||||
return runCreate(makeRunner(provider, tx));
|
||||
}
|
||||
if (typeof provider?.transaction === 'function') {
|
||||
return provider.transaction(runCreate);
|
||||
}
|
||||
return runCreate(makeRunner(provider));
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user