This commit is contained in:
seb
2026-10-16 22:09:12 +02:00
parent 796f6a3272
commit 7635821dad
38 changed files with 6191 additions and 3350 deletions

View File

@@ -1,8 +1,11 @@
import * as mssqlQueries from '../../providers/mssql/queries/manifestDoc.js';
import * as sqliteQueries from '../../providers/sqlite3/queries/manifestDoc.js';
import * as mssqlLedgerQueries from '../../providers/mssql/queries/ledger.js';
import * as sqliteLedgerQueries from '../../providers/sqlite3/queries/ledger.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import resolveArticleBySku from '../article/resolveArticleBySku.js';
import { recordStockAdjustmentHistory } from '../articlehistory/record.js';
import { recordStockAdjustmentHistory, recordWarehouseHistory } from '../articlehistory/record.js';
import { computePrevHash, shape } from '../ledger/add.js';
import nextNumber from '../numbers/nextNumber.js';
import ledgerAdd from '../ledger/add.js';
import makeRunner from '../runner.js';
@@ -24,6 +27,7 @@ export default function adjust(provider) {
const addLedger = ledgerAdd(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const queries = isSqlite ? sqliteQueries : mssqlQueries;
const ledgerQueries = isSqlite ? sqliteLedgerQueries : mssqlLedgerQueries;
/**
* Adjusts article stock manually by creating an implicit manifest of type "adjustment",
@@ -42,6 +46,139 @@ export default function adjust(provider) {
* @returns {Promise<{manifest: {id: number, number: string, type: string, date: string, createdAt: string}, lines: Array<object>, ledger: Array<object>}>}
*/
return async (googleAuthId, input = {}, tx) => {
const rawManifestarticleId = input.manifestarticleId ?? input.manifestarticle_id;
if (rawManifestarticleId != null) {
const manifestarticleId = Number(rawManifestarticleId);
if (!Number.isInteger(manifestarticleId) || manifestarticleId <= 0) {
throw new Error('Invalid manifest article id');
}
const amount = Number(input.amount);
if (!Number.isFinite(amount) || amount === 0) {
throw new Error('Amount must be a non-zero number');
}
const runManifestarticleAdjust = async (runner, activeTx) => {
const { companyId, companyuserId } = await access(googleAuthId, runner);
const checkRes = await runner.query(
`SELECT ma.id, ma.manifest_id, ma.sku, ma.name, m.company_id, m.type AS manifest_type, m.number AS manifest_number
FROM manifestarticle ma
INNER JOIN manifest m ON m.id = ma.manifest_id
WHERE ma.id = @manifestarticleId AND m.company_id = @companyId`,
{ manifestarticleId, companyId },
);
const manifestArticle = checkRes.rows[0];
if (!manifestArticle) {
throw new Error('Manifest article not found');
}
const sku = manifestArticle.sku ? String(manifestArticle.sku).trim() : '';
if (!sku) {
throw new Error('Manifest article has no SKU');
}
const trackRes = await runner.query(
`SELECT a.id, a.sku, a.track_stock, a.is_parent, a.articlelist_id FROM article a
WHERE a.company_id = @companyId
AND (a.sku = @sku OR EXISTS (SELECT 1 FROM articlesupplier asup WHERE asup.article_id = a.id AND asup.sku = @sku))`,
{ companyId, sku },
);
const art = trackRes.rows[0];
if (!art) {
throw new Error(`Article not found (${sku})`);
}
if (art.is_parent) {
throw new Error(`A parent article cannot have stock (${sku})`);
}
if (art.articlelist_id != null) {
throw new Error(`A Stückliste cannot have manual stock adjustments (${sku})`);
}
if (Number(art.track_stock ?? 1) === 0) {
throw new Error(`Stock tracking is disabled for article (${sku})`);
}
let warehouseId = input.warehouseId ?? input.warehouse_id;
let warehouseName = '';
if (warehouseId != null) {
warehouseId = Number(warehouseId);
if (!Number.isInteger(warehouseId) || warehouseId <= 0) {
throw new Error('Warehouse must be a positive integer');
}
const whCheck = await runner.query(
ledgerQueries.CHECK_WAREHOUSE_ACCESS,
{ warehouseId, companyId },
);
if (!whCheck.rows[0]) throw new Error('Warehouse not found');
warehouseName = whCheck.rows[0].name;
} else {
const defWh = await runner.query(
ledgerQueries.GET_DEFAULT_WAREHOUSE,
{ companyId },
);
if (!defWh.rows[0]) throw new Error('Warehouse not found');
warehouseId = defWh.rows[0].id;
warehouseName = defWh.rows[0].name;
}
const text = input.text ?? input.transactionText ?? input.transaction_text ??
(amount > 0 ? `Stock addition (+${amount})` : `Stock deduction (${amount})`);
const latest = await runner.query(ledgerQueries.GET_LATEST_ROW);
const prevRow = latest.rows[0];
const prevHash = computePrevHash(prevRow);
const inserted = await runner.query(ledgerQueries.INSERT_ROW, {
manifestarticleId,
warehouseId,
transactionType: 'WAREHOUSE',
amount,
transactionText: text,
hashVersion: 1,
prevHash,
});
const ledgerRow = inserted.rows[0];
// Write warehouse history for tracked article
if (manifestArticle.manifest_type === 'delivery') {
await recordWarehouseHistory(runner, {
articleId: art.id,
companyuserId,
manifestNumber: manifestArticle.manifest_number,
manifestType: 'delivery',
warehouseName,
amount,
transactionText: text,
});
} else if (manifestArticle.manifest_type === 'shipment') {
await recordWarehouseHistory(runner, {
articleId: art.id,
companyuserId,
manifestNumber: manifestArticle.manifest_number,
manifestType: 'shipment',
warehouseName,
amount,
transactionText: text,
});
}
return {
manifestarticle_id: manifestarticleId,
warehouse_id: warehouseId,
amount,
text,
ledger: shape(ledgerRow),
};
};
if (tx) return runManifestarticleAdjust(makeRunner(provider, tx), tx);
if (typeof provider?.transaction === 'function') {
return provider.transaction((runner, activeTx) => runManifestarticleAdjust(runner, activeTx));
}
return runManifestarticleAdjust(makeRunner(provider));
}
const rawLines = Array.isArray(input.lines)
? input.lines
: [