This commit is contained in:
seb
2026-10-12 13:52:16 +02:00
parent c614a67b97
commit 3d6bc97411
5 changed files with 127 additions and 38 deletions

View File

@@ -231,32 +231,18 @@ export default function add(provider) {
transactionText?.trim() ||
(manifestArticle.manifestNumber ? `Lieferung ${manifestArticle.manifestNumber}` : 'Lieferung');
historyText = `${base}${suffix}`;
} else if (isShipping) {
const details = [];
if (manifestArticle.supplierName) {
details.push(`Kunde: "${manifestArticle.supplierName}"`);
}
if (warehouseName) {
details.push(`Lager: "${warehouseName}"`);
}
if (amount != null && Number.isFinite(Number(amount))) {
details.push(`Menge: ${amount}`);
}
const suffix = details.length ? ` (${details.join(', ')})` : '';
const base =
transactionText?.trim() ||
(manifestArticle.manifestNumber ? `Versand Auftrag ${manifestArticle.manifestNumber}` : 'Versand');
historyText = `${base}${suffix}`;
} else if (warehouseName && !transactionText.includes(warehouseName)) {
historyText = `${transactionText} (Lager: "${warehouseName}")`;
}
for (const match of articleMatches.rows) {
await runner.query(queries.INSERT_ARTICLE_HISTORY, {
articleId: match.articleId,
companyuserId,
text: historyText,
});
if (!isShipping) {
for (const match of articleMatches.rows) {
await runner.query(queries.INSERT_ARTICLE_HISTORY, {
articleId: match.articleId,
companyuserId,
text: historyText,
});
}
}
}

View File

@@ -6,6 +6,14 @@ import {
CREATE_MANIFEST as SQLITE_CREATE_MANIFEST,
ORDER_TYPE as SQLITE_ORDER_TYPE,
} from '../../providers/sqlite3/queries/manifestDoc.js';
import {
FIND_ARTICLES_FOR_HISTORY as MSSQL_FIND_ARTICLES,
INSERT_ARTICLE_HISTORY as MSSQL_INSERT_HISTORY,
} from '../../providers/mssql/queries/ledger.js';
import {
FIND_ARTICLES_FOR_HISTORY as SQLITE_FIND_ARTICLES,
INSERT_ARTICLE_HISTORY as SQLITE_INSERT_HISTORY,
} from '../../providers/sqlite3/queries/ledger.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import taxEnsure from '../tax/ensure.js';
import { insertLines, normalizeLines, parseDate } from '../manifestLines.js';
@@ -19,9 +27,11 @@ export default function create(provider) {
const createManifestSql = isSqlite ? SQLITE_CREATE_MANIFEST : MSSQL_CREATE_MANIFEST;
const orderType = isSqlite ? SQLITE_ORDER_TYPE : MSSQL_ORDER_TYPE;
const findArticlesSql = isSqlite ? SQLITE_FIND_ARTICLES : MSSQL_FIND_ARTICLES;
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
/**
* Creates a new order for the user's selected company with its lines.
* Creates a new order for the user's selected company with its lines and records article history.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} body - The order input.
@@ -55,7 +65,7 @@ export default function create(provider) {
const lines = normalizeLines(body.lines ?? []);
const runCreate = async (runner) => {
const { companyId } = await access(googleAuthId, runner);
const { companyId, companyuserId } = await access(googleAuthId, runner);
const entityId = await assertCustomerEntity(
runner,
@@ -81,6 +91,41 @@ export default function create(provider) {
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;
}
for (const line of lines) {
const sku = line.sku ? String(line.sku).trim() : '';
if (!sku) continue;
const articleMatches = await runner.query(findArticlesSql, {
sku,
companyId,
});
const details = [];
if (customerName) {
details.push(`Kunde: "${customerName}"`);
}
if (line.amount != null && Number.isFinite(Number(line.amount))) {
details.push(`Menge: ${line.amount}`);
}
const suffix = details.length ? ` (${details.join(', ')})` : '';
const historyText = `Auftrag ${number}${suffix}`;
for (const match of articleMatches.rows) {
await runner.query(insertHistorySql, {
articleId: match.articleId,
companyuserId,
text: historyText,
});
}
}
const doc = await loadDoc(runner, {
type: orderType,
companyId,
@@ -99,4 +144,3 @@ export default function create(provider) {
return runCreate(makeRunner(provider));
};
}

View File

@@ -1,4 +1,12 @@
import { queriesFor, requireId, requireText } from '../logistics/helpers.js';
import {
FIND_ARTICLES_FOR_HISTORY as MSSQL_FIND_ARTICLES,
INSERT_ARTICLE_HISTORY as MSSQL_INSERT_HISTORY,
} from '../../providers/mssql/queries/ledger.js';
import {
FIND_ARTICLES_FOR_HISTORY as SQLITE_FIND_ARTICLES,
INSERT_ARTICLE_HISTORY as SQLITE_INSERT_HISTORY,
} from '../../providers/sqlite3/queries/ledger.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import nextNumber from '../numbers/nextNumber.js';
import ledgerAdd from '../ledger/add.js';
@@ -19,12 +27,15 @@ export default function create(provider) {
const allocateNumber = nextNumber(provider);
const addLedger = ledgerAdd(provider);
const q = queriesFor(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const findArticlesSql = isSqlite ? SQLITE_FIND_ARTICLES : MSSQL_FIND_ARTICLES;
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
/**
* Creates a shipping for a manifest (order). The number is allocated from the
* `shipping` numbers sequence (own prefix + counter). If state is 'shipped',
* adjusts stock by appending negative ledger movements for tracked order lines,
* writes article history, and ensures the order was not already shipped.
* writes article history for all items, and ensures the order was not already shipped.
*
* @param {number} googleAuthId - The id of the Google auth user.
* @param {object} input - Shipping input.
@@ -46,7 +57,7 @@ export default function create(provider) {
const isShipped = state.trim().toLowerCase() === 'shipped';
const run = async (runner, innerTx) => {
const { companyId } = await access(googleAuthId, runner);
const { companyId, companyuserId } = await access(googleAuthId, runner);
// Verify manifest exists and belongs to company
const manifestRes = await runner.query(q.GET_MANIFEST_HEADER_BY_ID, {
@@ -91,8 +102,9 @@ export default function create(provider) {
});
const lines = linesRes.rows ?? [];
// Check which lines need warehouse stock deduction
// Check which lines need warehouse stock deduction vs untracked history
const trackedLines = [];
const untrackedLines = [];
for (const line of lines) {
const sku = line.sku ? String(line.sku).trim() : '';
const amount = Number(line.amount);
@@ -107,11 +119,24 @@ export default function create(provider) {
const isUntracked = (trackRes.rows ?? []).some(
(r) => Number(r.track_stock ?? 1) === 0
);
if (!isUntracked && (trackRes.rows ?? []).length > 0) {
if (isUntracked) {
untrackedLines.push({ ...line, amount });
} else if ((trackRes.rows ?? []).length > 0) {
trackedLines.push({ ...line, amount });
}
}
const manifestNumber = manifest.number || String(manifestId);
let customerName = null;
if (manifest.entityId) {
const entRes = await runner.query('SELECT name FROM entity WHERE id = @entityId', {
entityId: manifest.entityId,
});
customerName = entRes.rows[0]?.name ?? null;
}
// Book warehouse ledger movements for tracked lines
if (trackedLines.length > 0) {
let warehouseId = null;
if (input.warehouseId != null) {
@@ -134,7 +159,6 @@ export default function create(provider) {
warehouseId = defaultWh.rows[0].id;
}
const manifestNumber = manifest.number || String(manifestId);
for (const line of trackedLines) {
await addLedger(
googleAuthId,

View File

@@ -1,4 +1,12 @@
import { queriesFor, requireId, requireText } from '../logistics/helpers.js';
import {
FIND_ARTICLES_FOR_HISTORY as MSSQL_FIND_ARTICLES,
INSERT_ARTICLE_HISTORY as MSSQL_INSERT_HISTORY,
} from '../../providers/mssql/queries/ledger.js';
import {
FIND_ARTICLES_FOR_HISTORY as SQLITE_FIND_ARTICLES,
INSERT_ARTICLE_HISTORY as SQLITE_INSERT_HISTORY,
} from '../../providers/sqlite3/queries/ledger.js';
import resolveSelectedCompanyAccess from '../companyuser/resolveSelectedCompanyAccess.js';
import ledgerAdd from '../ledger/add.js';
import makeRunner from '../runner.js';
@@ -24,6 +32,9 @@ export default function update(provider) {
const access = resolveSelectedCompanyAccess(provider);
const addLedger = ledgerAdd(provider);
const q = queriesFor(provider);
const isSqlite = provider?.dialect === 'sqlite3';
const findArticlesSql = isSqlite ? SQLITE_FIND_ARTICLES : MSSQL_FIND_ARTICLES;
const insertHistorySql = isSqlite ? SQLITE_INSERT_HISTORY : MSSQL_INSERT_HISTORY;
/**
* Updates a shipping record.
@@ -42,7 +53,7 @@ export default function update(provider) {
const shippingId = requireId(id, 'Shipping');
const run = async (runner, innerTx) => {
const { companyId } = await access(googleAuthId, runner);
const { companyId, companyuserId } = await access(googleAuthId, runner);
const check = await runner.query(q.GET_SHIPPING_BY_ID, {
id: shippingId,
@@ -98,6 +109,7 @@ export default function update(provider) {
});
const lines = linesRes.rows ?? [];
const trackedLines = [];
const untrackedLines = [];
for (const line of lines) {
const sku = line.sku ? String(line.sku).trim() : '';
const amount = Number(line.amount);
@@ -110,11 +122,23 @@ export default function update(provider) {
const isUntracked = (trackRes.rows ?? []).some(
(r) => Number(r.track_stock ?? 1) === 0
);
if (!isUntracked && (trackRes.rows ?? []).length > 0) {
if (isUntracked) {
untrackedLines.push({ ...line, amount });
} else if ((trackRes.rows ?? []).length > 0) {
trackedLines.push({ ...line, amount });
}
}
const manifestNumber = manifest.number || String(manifestId);
let customerName = null;
if (manifest.entityId) {
const entRes = await runner.query('SELECT name FROM entity WHERE id = @entityId', {
entityId: manifest.entityId,
});
customerName = entRes.rows[0]?.name ?? null;
}
if (trackedLines.length > 0) {
let warehouseId = null;
if (patch.warehouseId != null) {
@@ -137,7 +161,6 @@ export default function update(provider) {
warehouseId = defaultWh.rows[0].id;
}
const manifestNumber = manifest.number || String(manifestId);
for (const line of trackedLines) {
await addLedger(
googleAuthId,