This commit is contained in:
seb
2026-10-23 20:31:34 +02:00
parent 57347bdef9
commit b9bd0380cb
7 changed files with 533 additions and 78 deletions

184
src/utils/orderLedger.js Normal file
View File

@@ -0,0 +1,184 @@
import { ledger as defaultLedgerApi, shipment as defaultShipmentApi } from '../api';
import warehouseAutomat from '../automats/warehouseAutomat';
/**
* Fetches ledger entries for an order by inspecting:
* 1. All lines of the order's shipments (each shipment line id is a manifestarticle_id)
* 2. All lines of the order itself (in case order lines have direct ledger bookings)
*
* @param {number|string} orderId
* @param {Object} [options]
* @param {Array<object>} [options.shipments] Preloaded shipments
* @param {Array<object>} [options.lines] Order lines
* @param {Object} [options.ledgerApi]
* @param {Object} [options.shipmentApi]
* @returns {Promise<Array<object>>} Flattened, correlated and sorted ledger entries
*/
export async function fetchOrderLedgerEntries(orderId, {
shipments = null,
lines = null,
ledgerApi = defaultLedgerApi,
shipmentApi = defaultShipmentApi,
} = {}) {
let shipmentList = shipments;
if (!shipmentList && orderId) {
try {
const res = await shipmentApi.listByOrder(orderId);
shipmentList = Array.isArray(res?.shipments) ? res.shipments : [];
} catch (err) {
console.warn('[orderLedger] Failed to fetch shipments for order:', err);
shipmentList = [];
}
}
// Ensure shipments have their lines loaded
if (Array.isArray(shipmentList)) {
shipmentList = await Promise.all(
shipmentList.map(async (sh) => {
if (Array.isArray(sh.lines) && sh.lines.length > 0) return sh;
if (!sh.id) return sh;
try {
const detailRes = await shipmentApi.get(sh.id);
const detail = detailRes?.shipment ?? detailRes;
return detail?.lines ? detail : sh;
} catch {
return sh;
}
})
);
}
// Collect target lines to check for ledger bookings
const targets = [];
const seenIds = new Set();
for (const sh of shipmentList || []) {
const docNum = sh.number || `Lieferschein #${sh.id}`;
for (const sLine of sh.lines || []) {
if (sLine?.id && !seenIds.has(sLine.id)) {
seenIds.add(sLine.id);
targets.push({
manifestarticleId: sLine.id,
sku: sLine.sku || null,
name: sLine.name || null,
lineAmount: sLine.amount ?? null,
documentNumber: docNum,
documentType: 'shipment',
});
}
}
}
for (const oLine of lines || []) {
if (oLine?.id && !seenIds.has(oLine.id)) {
seenIds.add(oLine.id);
targets.push({
manifestarticleId: oLine.id,
sku: oLine.sku || null,
name: oLine.name || null,
lineAmount: oLine.amount ?? null,
documentNumber: null,
documentType: 'order',
});
}
}
if (targets.length === 0) return [];
// Query ledger entries for each manifestarticleId in parallel
const results = await Promise.all(
targets.map(async (t) => {
try {
const res = await ledgerApi.listByManifestarticleId(t.manifestarticleId);
const entries = Array.isArray(res?.ledger) ? res.ledger : [];
return { target: t, entries };
} catch (err) {
console.warn(`[orderLedger] Failed to fetch ledger for line ${t.manifestarticleId}:`, err);
return { target: t, entries: [] };
}
})
);
const warehouses = warehouseAutomat.state.warehouses || [];
const allEntries = [];
for (const { target, entries } of results) {
if (!entries || entries.length === 0) continue;
const docEntries = entries.filter((e) => {
const type = String(e.transaction_type || '').toUpperCase();
return type !== 'WAREHOUSE';
});
const warehouseEntries = entries.filter((e) => {
const type = String(e.transaction_type || '').toUpperCase();
return type === 'WAREHOUSE';
});
if (docEntries.length > 0) {
docEntries.forEach((docEntry, idx) => {
const matchingWh =
warehouseEntries.find(
(w) =>
w.warehouse_id != null &&
(w.transaction_text === docEntry.transaction_text ||
Math.abs(new Date(w.created_at) - new Date(docEntry.created_at)) < 15000)
) || warehouseEntries[idx] || null;
const warehouseId = matchingWh?.warehouse_id ?? docEntry.warehouse_id ?? null;
const wh = warehouseId != null
? warehouses.find((w) => Number(w.id) === Number(warehouseId))
: null;
const warehouseName = wh?.name || (warehouseId != null ? `Lager #${warehouseId}` : '–');
const rawAmount = matchingWh?.amount ?? docEntry.amount;
const mengeVerbucht = rawAmount != null
? (Number(rawAmount) > 0 ? `+${rawAmount}` : String(rawAmount))
: '–';
allEntries.push({
id: docEntry.id,
created_at: docEntry.created_at,
sku: docEntry.sku || target.sku || '–',
name: target.name || '–',
lineAmount: target.lineAmount != null ? String(target.lineAmount) : '–',
documentNumber: target.documentNumber,
warehouseName,
warehouseId,
mengeVerbucht,
rawAmount: rawAmount != null ? Number(rawAmount) : null,
transaction_type: docEntry.transaction_type,
transaction_text: docEntry.transaction_text || '–',
});
});
} else if (warehouseEntries.length > 0) {
for (const w of warehouseEntries) {
const wh = w.warehouse_id != null
? warehouses.find((item) => Number(item.id) === Number(w.warehouse_id))
: null;
const warehouseName = wh?.name || (w.warehouse_id != null ? `Lager #${w.warehouse_id}` : '–');
const rawAmount = w.amount;
const mengeVerbucht = rawAmount != null
? (Number(rawAmount) > 0 ? `+${rawAmount}` : String(rawAmount))
: '–';
allEntries.push({
id: w.id,
created_at: w.created_at,
sku: w.sku || target.sku || '–',
name: target.name || '–',
lineAmount: target.lineAmount != null ? String(target.lineAmount) : '–',
documentNumber: target.documentNumber,
warehouseName,
warehouseId: w.warehouse_id,
mengeVerbucht,
rawAmount: rawAmount != null ? Number(rawAmount) : null,
transaction_type: w.transaction_type,
transaction_text: w.transaction_text || '–',
});
}
}
}
allEntries.sort((a, b) => String(b.created_at || '').localeCompare(String(a.created_at || '')));
return allEntries;
}