From 5caedbe3455047b9d547dbee46d7328c4aadb6d8 Mon Sep 17 00:00:00 2001 From: seb Date: Sun, 23 Aug 2026 03:53:08 +0200 Subject: [PATCH] u --- src/queries/delivery/index.js | 63 ++++++++++++++------------ src/queries/delivery/stock-shortage.js | 47 +++++++++++++++++-- 2 files changed, 78 insertions(+), 32 deletions(-) diff --git a/src/queries/delivery/index.js b/src/queries/delivery/index.js index 50b9ad4..b0274e7 100644 --- a/src/queries/delivery/index.js +++ b/src/queries/delivery/index.js @@ -1,7 +1,7 @@ import { openSession, discardSession, closeSession } from './session.js'; import { getOutgoingWarehouse, getWarehousePlace } from './warehouse.js'; import { reservePositions } from './reserve.js'; -import { bookStockShortfallsAndRereserve } from './stock-shortage.js'; +import { bookStockShortfallsAndRereserve, bookReturnPositions } from './stock-shortage.js'; import { commitPicklists } from './commit.js'; import { deliverPicklists } from './deliver.js'; import { logger } from '../../logger.js'; @@ -29,35 +29,42 @@ export async function deliverOrder(transaction, kBenutzer, kAuftrag, kVersandArt const kWarenLager = await getOutgoingWarehouse(transaction); const kWarenLagerPlatz = await getWarehousePlace(transaction, kWarenLager); logger.info(`deliverOrder: kWarenLager=${kWarenLager} kWarenLagerPlatz=${kWarenLagerPlatz}`); - const kSessionId = await openSession(transaction, kBenutzer); - try { - await reservePositions(transaction, kBenutzer, kSessionId, kWarenLager, deliveredItems); - await bookStockShortfallsAndRereserve( - transaction, - kBenutzer, - kSessionId, - kWarenLager, - kWarenLagerPlatz, - deliveredItems, - ); - await commitPicklists(transaction, kBenutzer, kSessionId, kAuftrag); - await deliverPicklists(transaction, kBenutzer, kSessionId, kAuftrag, kVersandArt); - logger.info(`deliverOrder: delivered kAuftrag=${kAuftrag} kSessionId=${kSessionId}`); - } catch (err) { - logger.error(`deliverOrder: FAILED kAuftrag=${kAuftrag} kSessionId=${kSessionId}: ${err.message}`); - throw err; - } finally { - // Safe to run unconditionally (success or error): spPicklistenVerwerfen only - // ever removes not-yet-delivered (nStatus < 10) Picklisten for this session. - // Swallow cleanup errors so they never mask an earlier, more relevant error - // (the outer transaction rollback in create-order.js is what actually matters - // on failure). + const positiveItems = deliveredItems.filter((i) => i.quantity > 0); + const returnItems = deliveredItems.filter((i) => i.quantity < 0); + + // 1. Process returned items: book them back into warehouse stock via Wareneingang + if (returnItems.length) { + await bookReturnPositions(transaction, kBenutzer, kWarenLagerPlatz, returnItems); + } + + // 2. Process positive items: standard JTL picklist/delivery fulfillment + if (positiveItems.length) { + const kSessionId = await openSession(transaction, kBenutzer); + try { - await discardSession(transaction, kBenutzer, kSessionId); - await closeSession(transaction, kSessionId); - } catch { - // best-effort cleanup only + await reservePositions(transaction, kBenutzer, kSessionId, kWarenLager, positiveItems); + await bookStockShortfallsAndRereserve( + transaction, + kBenutzer, + kSessionId, + kWarenLager, + kWarenLagerPlatz, + positiveItems, + ); + await commitPicklists(transaction, kBenutzer, kSessionId, kAuftrag); + await deliverPicklists(transaction, kBenutzer, kSessionId, kAuftrag, kVersandArt); + logger.info(`deliverOrder: delivered kAuftrag=${kAuftrag} kSessionId=${kSessionId}`); + } catch (err) { + logger.error(`deliverOrder: FAILED kAuftrag=${kAuftrag} kSessionId=${kSessionId}: ${err.message}`); + throw err; + } finally { + try { + await discardSession(transaction, kBenutzer, kSessionId); + await closeSession(transaction, kSessionId); + } catch { + // best-effort cleanup only + } } } } diff --git a/src/queries/delivery/stock-shortage.js b/src/queries/delivery/stock-shortage.js index f906b1e..da01ae0 100644 --- a/src/queries/delivery/stock-shortage.js +++ b/src/queries/delivery/stock-shortage.js @@ -21,7 +21,7 @@ async function getReservedQuantity(transaction, kSessionId, kAuftragPosition) { return reserved; } -async function getPositionArtikel(transaction, kAuftragPosition) { +export async function getPositionArtikel(transaction, kAuftragPosition) { const result = await new sql.Request(transaction) .input('kAuftragPosition', sql.Int, kAuftragPosition) .query(` @@ -35,14 +35,22 @@ async function getPositionArtikel(transaction, kAuftragPosition) { return row?.kArtikel ?? 0; } -async function bookWareneingang(transaction, kBenutzer, kWarenLagerPlatz, kArtikel, fehlmenge) { +export async function bookWareneingang( + transaction, + kBenutzer, + kWarenLagerPlatz, + kArtikel, + fehlmenge, + comment = POS_SHORTAGE_COMMENT, + buchungsart = BUCHUNGSART_WARENEINGANG, +) { await new sql.Request(transaction) .input('kArtikel', sql.Int, kArtikel) .input('kWarenLagerPlatz', sql.Int, kWarenLagerPlatz) .input('kBenutzer', sql.Int, kBenutzer) .input('fAnzahl', sql.Float, fehlmenge) - .input('cKommentar', sql.NVarChar, POS_SHORTAGE_COMMENT) - .input('kBuchungsart', sql.Int, BUCHUNGSART_WARENEINGANG) + .input('cKommentar', sql.NVarChar, comment) + .input('kBuchungsart', sql.Int, buchungsart) .query(` DECLARE @kWarenlagerEingang INT; EXEC dbo.spWarenlagerEingangSchreiben @@ -69,6 +77,37 @@ async function bookWareneingang(transaction, kBenutzer, kWarenLagerPlatz, kArtik `); } +export async function bookReturnPositions( + transaction, + kBenutzer, + kWarenLagerPlatz, + returnPositions, +) { + logger.info(`returnBooking: processing ${returnPositions.length} return position(s) at kWarenLagerPlatz=${kWarenLagerPlatz}`); + for (const { kAuftragPosition, quantity } of returnPositions) { + if (!kAuftragPosition || quantity >= 0) { + continue; + } + const returnQty = Math.abs(quantity); + const kArtikel = await getPositionArtikel(transaction, kAuftragPosition); + logger.info(`returnBooking: kBestellPos=${kAuftragPosition} kArtikel=${kArtikel} returnQty=${returnQty}`); + if (!kArtikel) { + logger.info(`returnBooking: kBestellPos=${kAuftragPosition} no kArtikel (free position / Pfand?), skip return booking`); + continue; + } + await bookWareneingang( + transaction, + kBenutzer, + kWarenLagerPlatz, + kArtikel, + returnQty, + 'Korrekturbuchung erstellt durch POS-Abgleich (Retoure)', + BUCHUNGSART_WARENEINGANG, + ); + logger.info(`returnBooking: successfully booked return of ${returnQty}x kArtikel=${kArtikel} into kWarenLagerPlatz=${kWarenLagerPlatz}`); + } +} + /** * PosStockPositionService.FehlbestandEinbuchen — book missing stock, then re-reserve. */