From 95198464897119178551c91a79a7bfcff0b51722 Mon Sep 17 00:00:00 2001 From: "Torsten Schulz (local)" Date: Thu, 29 Jan 2026 16:44:02 +0100 Subject: [PATCH] Refactor inventory handling in FalukantService: Update product transport logic to retrieve all stocks for a branch, improving inventory checks and ensuring accurate transport cost calculations. Implement locking during inventory updates to maintain data integrity during transactions. --- backend/services/falukantService.js | 51 ++++++++++++++++++----------- 1 file changed, 32 insertions(+), 19 deletions(-) diff --git a/backend/services/falukantService.js b/backend/services/falukantService.js index cd42fd3..12c34b2 100644 --- a/backend/services/falukantService.js +++ b/backend/services/falukantService.js @@ -994,33 +994,33 @@ class FalukantService extends BaseService { // Produkt-Transport oder leerer Transport (nur Fahrzeuge bewegen)? const isEmptyTransport = !productId || !quantity || quantity <= 0; - let inventory = []; + let sourceStockIds = []; let available = 0; let maxByInventory = 0; let hardMax = 0; let requested = 0; let transportCost = 0.1; // Minimale Kosten für leeren Transport + let productIdForTransport = productId; if (!isEmptyTransport) { - // Produkt-Transport: Inventar prüfen - const stock = await FalukantStock.findOne({ where: { branchId: sourceBranch.id } }); - if (!stock) { + // Produkt-Transport: alle Stocks der Quell-Niederlassung (wie getInventory) + const sourceStocks = await FalukantStock.findAll({ where: { branchId: sourceBranch.id }, attributes: ['id'] }); + if (!sourceStocks?.length) { throw new Error('Stock not found'); } + sourceStockIds = sourceStocks.map((s) => s.id); - inventory = await Inventory.findAll({ - where: { stockId: stock.id }, + const inventoryCheck = await Inventory.findAll({ + where: { + stockId: { [Op.in]: sourceStockIds }, + productId, + }, include: [ - { - model: ProductType, - as: 'productType', - required: true, - where: { id: productId }, - }, + { model: ProductType, as: 'productType', required: true, where: { id: productId }, attributes: ['id', 'sellCost'] }, ], }); - available = inventory.reduce((sum, i) => sum + i.quantity, 0); + available = inventoryCheck.reduce((sum, i) => sum + i.quantity, 0); if (available <= 0) { throw new PreconditionError('noInventory'); } @@ -1034,7 +1034,7 @@ class FalukantService extends BaseService { } // Transportkosten: 1 % des Warenwerts, mindestens 0,1 - const productType = inventory[0]?.productType; + const productType = inventoryCheck[0]?.productType; const unitValue = productType?.sellCost || 0; const totalValue = unitValue * requested; transportCost = Math.max(0.1, totalValue * 0.01); @@ -1090,15 +1090,28 @@ class FalukantService extends BaseService { } // Inventar in der Quell-Niederlassung reduzieren (nur bei Produkt-Transport) - if (!isEmptyTransport && inventory.length > 0) { + // Innerhalb der Transaktion mit Lock laden, damit aktuelle Mengen verwendet werden + if (!isEmptyTransport && sourceStockIds.length > 0) { + const inventoryRows = await Inventory.findAll({ + where: { + stockId: { [Op.in]: sourceStockIds }, + productId: productIdForTransport, + }, + order: [['id', 'ASC']], + lock: true, // SELECT ... FOR UPDATE + transaction: tx, + }); + let left = requested; - for (const inv of inventory) { + for (const inv of inventoryRows) { if (left <= 0) break; - if (inv.quantity <= left) { - left -= inv.quantity; + const qty = Number(inv.quantity) || 0; + if (qty <= 0) continue; + if (qty <= left) { + left -= qty; await inv.destroy({ transaction: tx }); } else { - await inv.update({ quantity: inv.quantity - left }, { transaction: tx }); + await inv.update({ quantity: qty - left }, { transaction: tx }); left = 0; break; }