import * as mssqlQueries from '../../providers/mssql/queries/articlesupplier.js'; import * as sqliteQueries from '../../providers/sqlite3/queries/articlesupplier.js'; import resolveArticleBySku from '../article/resolveArticleBySku.js'; import taxEnsure from '../tax/ensure.js'; import { assertSupplier, optionalString, resolveCompanyDefaultTax, shapeArticleSupplier, } from './helpers.js'; import { recordSupplierAddHistory } from '../articlehistory/record.js'; import makeRunner from '../runner.js'; export default function create(provider) { const resolve = resolveArticleBySku(provider); const ensureTax = taxEnsure(provider); const isSqlite = provider?.dialect === 'sqlite3'; const queries = isSqlite ? sqliteQueries : mssqlQueries; /** * Assigns a supplier to an article, optionally creating a purchase price. * * @param {number} googleAuthId - The id of the Google auth user. * @param {string} sku - The article SKU. * @param {object} input - The supplier assignment input. * @param {number} input.entityId - The id of the supplier entity. * @param {string} [input.sku] - The supplier's item number/SKU. * @param {number} [input.pu] - Packaging unit (defaults to 1). * @param {number|null} [input.stock] - Current stock at the supplier. * @param {number} [input.sort] - Display sort order (defaults to next sort). * @param {number|null} [input.netPrice] - Net purchase price. * @param {number} [input.taxId] - The tax id for the purchase price. * @param {number} [input.taxRate] - The tax rate in percent (used if taxId is not provided). * @param {object} [tx] - Optional transaction to run within. * @returns {Promise<{id: number, articleId: number, entityId: number, supplierName: string|null, sku: string|null, pu: number, stock: number|null, sort: number, price: {net: number, taxId: number, taxRate?: number}|null}>} The created assignment. * @throws {Error} If input is invalid, article or supplier is not found, or supplier is already assigned. * * @example * const created = await store.articlesupplier.create(42, 'SKU-1', { * entityId: 88, * sku: 'MET-44910', * pu: 1, * netPrice: 4.20, * }); * // => { id, articleId, entityId, supplierName, sku, pu, stock, sort, price } */ return async (googleAuthId, sku, input = {}, tx) => { const entityId = input.entityId; if (entityId == null || !Number.isFinite(entityId)) { throw new Error('Supplier required'); } const pu = input.pu === undefined || input.pu === null ? 1 : input.pu; if (!Number.isFinite(pu) || pu <= 0) { throw new Error('Invalid pu'); } const supplierSku = optionalString(input.sku); const stock = input.stock === undefined || input.stock === null ? null : input.stock; if (stock !== null && !Number.isFinite(stock)) { throw new Error('Invalid stock'); } if (input.sort !== undefined && input.sort !== null) { if (!Number.isFinite(input.sort)) { throw new Error('Invalid sort'); } } const netPrice = input.netPrice === undefined || input.netPrice === null ? null : input.netPrice; if (netPrice !== null && !Number.isFinite(netPrice)) { throw new Error('Invalid price'); } const runCreate = async (runner) => { const { articleId, companyId, companyuserId } = await resolve( googleAuthId, sku, runner, ); const artCheck = await runner.query('SELECT is_parent FROM article WHERE id = @articleId', { articleId }); if (artCheck.rows[0]?.is_parent) { throw new Error('A parent article cannot have suppliers'); } await assertSupplier(runner, companyId, entityId); const existingCheck = await runner.query(queries.CHECK_EXISTS, { articleId, entityId, }); if (existingCheck.rows[0]) { throw new Error('Supplier already assigned'); } let sort = input.sort; if (sort === undefined || sort === null) { const sortRes = await runner.query(queries.GET_NEXT_SORT, { articleId, }); sort = sortRes.rows[0].sort; } let taxId = null; if (netPrice !== null) { taxId = input.taxId; if (taxId == null || !Number.isFinite(taxId)) { if (input.taxRate != null && Number.isFinite(input.taxRate)) { taxId = await ensureTax(input.taxRate, runner); } else { const defaultTaxId = await resolveCompanyDefaultTax( runner, companyId, ); taxId = defaultTaxId != null ? defaultTaxId : await ensureTax(0, runner); } } } const createdRes = await runner.query(queries.CREATE, { articleId, entityId, net: netPrice, taxId, sku: supplierSku, stock, pu, sort, }); const createdId = createdRes.rows[0]?.id ?? createdRes.insertId; const loadRes = await runner.query(queries.GET_BY_ID_AND_ARTICLE_ID, { id: createdId, articleId, companyId, }); const supplierRow = loadRes.rows[0]; const supplierName = supplierRow?.supplierName ?? (entityId ? `Lieferant #${entityId}` : 'Lieferant'); const actualSku = supplierRow?.sku ?? supplierSku; const actualPrice = supplierRow?.priceNet ?? netPrice; await recordSupplierAddHistory(runner, { articleId, companyuserId, supplierName, supplierSku: actualSku, netPrice: actualPrice, }); return shapeArticleSupplier(supplierRow); }; if (tx) { return runCreate(makeRunner(provider, tx)); } if (typeof provider?.transaction === 'function') { return provider.transaction(runCreate); } return runCreate(makeRunner(provider)); }; }