Compare commits
2 Commits
73d256e8a6
...
fe0d2e43f4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fe0d2e43f4 | ||
|
|
5caedbe345 |
@@ -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,17 +29,28 @@ 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 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 reservePositions(transaction, kBenutzer, kSessionId, kWarenLager, deliveredItems);
|
||||
await reservePositions(transaction, kBenutzer, kSessionId, kWarenLager, positiveItems);
|
||||
await bookStockShortfallsAndRereserve(
|
||||
transaction,
|
||||
kBenutzer,
|
||||
kSessionId,
|
||||
kWarenLager,
|
||||
kWarenLagerPlatz,
|
||||
deliveredItems,
|
||||
positiveItems,
|
||||
);
|
||||
await commitPicklists(transaction, kBenutzer, kSessionId, kAuftrag);
|
||||
await deliverPicklists(transaction, kBenutzer, kSessionId, kAuftrag, kVersandArt);
|
||||
@@ -48,11 +59,6 @@ export async function deliverOrder(transaction, kBenutzer, kAuftrag, kVersandArt
|
||||
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).
|
||||
try {
|
||||
await discardSession(transaction, kBenutzer, kSessionId);
|
||||
await closeSession(transaction, kSessionId);
|
||||
@@ -60,4 +66,5 @@ export async function deliverOrder(transaction, kBenutzer, kAuftrag, kVersandArt
|
||||
// best-effort cleanup only
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -36,6 +36,21 @@ SELECT TOP (@limit)
|
||||
FROM dbo.tkategorieartikel ka
|
||||
WHERE ka.kArtikel = a.kArtikel
|
||||
) AS categoryIds,
|
||||
(
|
||||
SELECT COUNT(*)
|
||||
FROM dbo.tKategorieArtikel ka2
|
||||
INNER JOIN dbo.tArtikel a2 ON a2.kArtikel = ka2.kArtikel AND a2.cAktiv = 'Y'
|
||||
INNER JOIN dbo.tArtikelBeschreibung ab2 ON ab2.kArtikel = a2.kArtikel AND ab2.kSprache = @languageId
|
||||
WHERE ka2.kKategorie = (
|
||||
SELECT TOP 1 ka1.kKategorie
|
||||
FROM dbo.tKategorieArtikel ka1
|
||||
WHERE ka1.kArtikel = a.kArtikel
|
||||
)
|
||||
AND (
|
||||
ab2.cName < ab.cName
|
||||
OR (ab2.cName = ab.cName AND a2.kArtikel < a.kArtikel)
|
||||
)
|
||||
) AS sort,
|
||||
a.nIstVater AS isParent,
|
||||
a.kVaterArtikel AS parentArticleId,
|
||||
CASE WHEN a.kStueckliste <> 0 THEN '1' ELSE '0' END AS isCompositeProduct,
|
||||
@@ -204,6 +219,7 @@ export async function getProductList({ cursor = 0, limit = 20 } = {}) {
|
||||
price: basePrice,
|
||||
created_at: formatDateTime(product.createdAt),
|
||||
lastChanged: String(product.lastChanged),
|
||||
sort: String(product.sort ?? 0),
|
||||
categories_id: categoryIds[0] ?? '0',
|
||||
categories: categoryIds.map((categoryId) => ({ categoryId })),
|
||||
prices,
|
||||
|
||||
Reference in New Issue
Block a user