From 7ad8a3a301bf43954822329423c22f09f3344963 Mon Sep 17 00:00:00 2001 From: seb Date: Fri, 16 Oct 2026 19:36:08 +0200 Subject: [PATCH] u --- .../manifest/BookDeliveryDialog.jsx | 130 ++++++++++-------- src/components/manifest/ManifestFormLine.jsx | 2 +- src/components/manifest/ManifestHeader.jsx | 2 +- src/routes/lieferung/neu.jsx | 42 +++--- src/utils/deliveryLedger.js | 86 ++++++++++-- 5 files changed, 167 insertions(+), 95 deletions(-) diff --git a/src/components/manifest/BookDeliveryDialog.jsx b/src/components/manifest/BookDeliveryDialog.jsx index 77d09c3..c2f9e5b 100644 --- a/src/components/manifest/BookDeliveryDialog.jsx +++ b/src/components/manifest/BookDeliveryDialog.jsx @@ -47,6 +47,7 @@ export default class BookDeliveryDialog extends PureComponent { let initialAmounts = {}; if (Array.isArray(props.summary?.lines)) { for (const line of props.summary.lines) { + if (!line.trackStock) continue; if (props.prefillLineId != null) { initialAmounts[line.id] = line.id === props.prefillLineId @@ -174,6 +175,7 @@ export default class BookDeliveryDialog extends PureComponent { const amounts = {}; if (Array.isArray(summary?.lines)) { for (const line of summary.lines) { + if (!line.trackStock) continue; if (prefillLineId != null) { amounts[line.id] = line.id === prefillLineId @@ -208,6 +210,7 @@ export default class BookDeliveryDialog extends PureComponent { if (!summary?.lines) return; const amounts = {}; for (const line of summary.lines) { + if (!line.trackStock) continue; amounts[line.id] = line.remainingAmount > 0 ? line.remainingAmount : 0; } this.setState({ amountsToBook: amounts }); @@ -218,6 +221,7 @@ export default class BookDeliveryDialog extends PureComponent { if (!summary?.lines) return; const amounts = {}; for (const line of summary.lines) { + if (!line.trackStock) continue; amounts[line.id] = 0; } this.setState({ amountsToBook: amounts }); @@ -229,6 +233,7 @@ export default class BookDeliveryDialog extends PureComponent { if (!summary?.lines) return; const linesToBook = summary.lines + .filter((line) => line.trackStock) .map((line) => ({ manifestarticleId: line.id, sku: line.sku, @@ -382,12 +387,12 @@ export default class BookDeliveryDialog extends PureComponent { renderLinesTable() { const { summary, amountsToBook, isBooking } = this.state; - const lines = summary?.lines || []; + const lines = (summary?.lines || []).filter((l) => l.trackStock); if (lines.length === 0) { return ( - Diese Lieferung enthält keine Artikelpositionen zum Einbuchen. + Diese Lieferung enthält keine lagergeführten Artikel zum Einbuchen. ); } @@ -490,7 +495,8 @@ export default class BookDeliveryDialog extends PureComponent { warehouseError, } = this.state; - const lines = summary?.lines || []; + const lines = (summary?.lines || []).filter((l) => l.trackStock); + const hasTrackStock = lines.length > 0; const prefillLine = this.props.prefillLineId != null ? lines.find((l) => l.id === this.props.prefillLineId) @@ -502,7 +508,7 @@ export default class BookDeliveryDialog extends PureComponent { const countLinesToBook = lines.filter( (l) => Number(amountsToBook[l.id]) > 0 ).length; - const isFullyBooked = summary?.status === 'booked'; + const isFullyBooked = hasTrackStock && summary?.status === 'booked'; const selectedWarehouse = warehouses.find((w) => w.id === selectedWarehouseId); const isWarehouseMissing = !selectedWarehouseId; @@ -554,7 +560,7 @@ export default class BookDeliveryDialog extends PureComponent { )} {this.renderLinesTable()} - {!isFullyBooked && lines.length > 0 && ( + {!isFullyBooked && hasTrackStock && ( )} - {docId != null && kind === 'delivery' && onOpenBookDialog && ( + {docId != null && kind === 'delivery' && onOpenBookDialog && ledgerSummary?.hasTrackStock && ( + {ledgerSummary.hasTrackStock && ( + + )} {allEntries.length > 0 && ( diff --git a/src/utils/deliveryLedger.js b/src/utils/deliveryLedger.js index 68fabfa..7a66560 100644 --- a/src/utils/deliveryLedger.js +++ b/src/utils/deliveryLedger.js @@ -1,5 +1,34 @@ import { delivery as defaultDeliveryApi, ledger as defaultLedgerApi } from '../api'; -import artikelAutomat from '../automats/artikelAutomat'; +import artikelAutomat, { getArticle } from '../automats/artikelAutomat'; +import articleIndexAutomat from '../automats/articleIndexAutomat'; + +/** + * Determines whether a delivery line represents an article that tracks stock. + * + * @param {object} line Delivery line + * @param {object|null} [article] Optional pre-resolved article + * @returns {boolean} + */ +export function isLineStockTracked(line, article = null) { + if (!line) return false; + if (line.track_stock !== undefined) { + return line.track_stock !== false && line.track_stock !== 0 && line.track_stock !== '0'; + } + if (line.trackStock !== undefined) { + return Boolean(line.trackStock); + } + if (line.article?.track_stock !== undefined) { + return line.article.track_stock !== false && line.article.track_stock !== 0 && line.article.track_stock !== '0'; + } + const sku = line.sku ? String(line.sku).trim() : null; + if (!sku) return false; + + const art = article || getArticle(sku) || articleIndexAutomat.state.items?.find((item) => String(item.sku) === sku); + if (art?.track_stock !== undefined) { + return art.track_stock !== false && art.track_stock !== 0 && art.track_stock !== '0'; + } + return false; +} /** * @typedef {Object} DeliveryLineLedgerStatus @@ -10,6 +39,8 @@ import artikelAutomat from '../automats/artikelAutomat'; * @property {number} bookedAmount - Quantity booked in ledger * @property {number} remainingAmount - Quantity remaining to be booked * @property {boolean} isBooked - Whether this position is fully booked + * @property {boolean} trackStock - Whether this article tracks inventory + * @property {boolean} isBookable - Whether this position can be booked into the warehouse * @property {Array} ledgerEntries - Ledger entries for this line */ @@ -22,6 +53,7 @@ import artikelAutomat from '../automats/artikelAutomat'; * @property {number} bookedPositions * @property {number} totalQuantity * @property {number} bookedQuantity + * @property {boolean} hasTrackStock * @property {Array} lines */ @@ -48,6 +80,7 @@ export async function fetchDeliveryLedgerSummary(deliveryId, { deliveryApi = def bookedPositions: 0, totalQuantity: 0, bookedQuantity: 0, + hasTrackStock: false, lines: [], }; } @@ -56,20 +89,42 @@ export async function fetchDeliveryLedgerSummary(deliveryId, { deliveryApi = def const linesWithLedger = await Promise.all( rawLines.map(async (line) => { let ledgerEntries = []; - try { - const ledgerRes = await ledgerApi.listByManifestarticleId(line.id, 'DELIVERY'); - ledgerEntries = Array.isArray(ledgerRes?.ledger) ? ledgerRes.ledger : []; - } catch (err) { - console.error(`[deliveryLedger] Failed to fetch ledger for line ${line.id}:`, err); + let article = null; + + const promises = [ + ledgerApi.listByManifestarticleId(line.id, 'DELIVERY').catch((err) => { + console.error(`[deliveryLedger] Failed to fetch ledger for line ${line.id}:`, err); + return null; + }), + ]; + + if (line.sku) { + const cached = getArticle(line.sku); + if (cached) { + article = cached; + } else { + promises.push( + artikelAutomat.actions.loadArticle(line.sku).catch(() => null) + ); + } } + const results = await Promise.all(promises); + const ledgerRes = results[0]; + if (results[1]) { + article = results[1]; + } + ledgerEntries = Array.isArray(ledgerRes?.ledger) ? ledgerRes.ledger : []; + + const trackStock = isLineStockTracked(line, article); + const deliveryEntries = ledgerEntries.filter( (e) => String(e.transaction_type).toUpperCase() === 'DELIVERY' ); const bookedAmount = deliveryEntries.reduce((sum, e) => sum + Number(e.amount || 0), 0); const targetAmount = Number(line.amount || 0); - const remainingAmount = Math.max(0, targetAmount - bookedAmount); - const isBooked = targetAmount > 0 ? bookedAmount >= targetAmount : bookedAmount > 0; + const remainingAmount = trackStock ? Math.max(0, targetAmount - bookedAmount) : 0; + const isBooked = trackStock ? (targetAmount > 0 ? bookedAmount >= targetAmount : bookedAmount > 0) : true; return { ...line, @@ -77,18 +132,22 @@ export async function fetchDeliveryLedgerSummary(deliveryId, { deliveryApi = def bookedAmount, remainingAmount, isBooked, + trackStock, + isBookable: trackStock, ledgerEntries: deliveryEntries, }; }) ); - const totalPositions = linesWithLedger.length; - const bookedPositions = linesWithLedger.filter((l) => l.isBooked).length; - const totalQuantity = linesWithLedger.reduce((sum, l) => sum + l.targetAmount, 0); - const bookedQuantity = linesWithLedger.reduce((sum, l) => sum + l.bookedAmount, 0); + const stockLines = linesWithLedger.filter((l) => l.trackStock); + const totalPositions = stockLines.length; + const bookedPositions = stockLines.filter((l) => l.isBooked).length; + const totalQuantity = stockLines.reduce((sum, l) => sum + l.targetAmount, 0); + const bookedQuantity = stockLines.reduce((sum, l) => sum + l.bookedAmount, 0); + const hasTrackStock = stockLines.length > 0; let status; - if (totalPositions === 0) { + if (!hasTrackStock) { status = 'empty'; } else if (bookedPositions === totalPositions && bookedQuantity >= totalQuantity) { status = 'booked'; @@ -106,6 +165,7 @@ export async function fetchDeliveryLedgerSummary(deliveryId, { deliveryApi = def bookedPositions, totalQuantity, bookedQuantity, + hasTrackStock, lines: linesWithLedger, }; }