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.

This commit is contained in:
Torsten Schulz (local)
2026-01-29 16:44:02 +01:00
parent f7a977df33
commit 9519846489

View File

@@ -994,33 +994,33 @@ class FalukantService extends BaseService {
// Produkt-Transport oder leerer Transport (nur Fahrzeuge bewegen)? // Produkt-Transport oder leerer Transport (nur Fahrzeuge bewegen)?
const isEmptyTransport = !productId || !quantity || quantity <= 0; const isEmptyTransport = !productId || !quantity || quantity <= 0;
let inventory = []; let sourceStockIds = [];
let available = 0; let available = 0;
let maxByInventory = 0; let maxByInventory = 0;
let hardMax = 0; let hardMax = 0;
let requested = 0; let requested = 0;
let transportCost = 0.1; // Minimale Kosten für leeren Transport let transportCost = 0.1; // Minimale Kosten für leeren Transport
let productIdForTransport = productId;
if (!isEmptyTransport) { if (!isEmptyTransport) {
// Produkt-Transport: Inventar prüfen // Produkt-Transport: alle Stocks der Quell-Niederlassung (wie getInventory)
const stock = await FalukantStock.findOne({ where: { branchId: sourceBranch.id } }); const sourceStocks = await FalukantStock.findAll({ where: { branchId: sourceBranch.id }, attributes: ['id'] });
if (!stock) { if (!sourceStocks?.length) {
throw new Error('Stock not found'); throw new Error('Stock not found');
} }
sourceStockIds = sourceStocks.map((s) => s.id);
inventory = await Inventory.findAll({ const inventoryCheck = await Inventory.findAll({
where: { stockId: stock.id }, where: {
stockId: { [Op.in]: sourceStockIds },
productId,
},
include: [ include: [
{ { model: ProductType, as: 'productType', required: true, where: { id: productId }, attributes: ['id', 'sellCost'] },
model: ProductType,
as: 'productType',
required: true,
where: { id: productId },
},
], ],
}); });
available = inventory.reduce((sum, i) => sum + i.quantity, 0); available = inventoryCheck.reduce((sum, i) => sum + i.quantity, 0);
if (available <= 0) { if (available <= 0) {
throw new PreconditionError('noInventory'); throw new PreconditionError('noInventory');
} }
@@ -1034,7 +1034,7 @@ class FalukantService extends BaseService {
} }
// Transportkosten: 1 % des Warenwerts, mindestens 0,1 // Transportkosten: 1 % des Warenwerts, mindestens 0,1
const productType = inventory[0]?.productType; const productType = inventoryCheck[0]?.productType;
const unitValue = productType?.sellCost || 0; const unitValue = productType?.sellCost || 0;
const totalValue = unitValue * requested; const totalValue = unitValue * requested;
transportCost = Math.max(0.1, totalValue * 0.01); 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) // 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; let left = requested;
for (const inv of inventory) { for (const inv of inventoryRows) {
if (left <= 0) break; if (left <= 0) break;
if (inv.quantity <= left) { const qty = Number(inv.quantity) || 0;
left -= inv.quantity; if (qty <= 0) continue;
if (qty <= left) {
left -= qty;
await inv.destroy({ transaction: tx }); await inv.destroy({ transaction: tx });
} else { } else {
await inv.update({ quantity: inv.quantity - left }, { transaction: tx }); await inv.update({ quantity: qty - left }, { transaction: tx });
left = 0; left = 0;
break; break;
} }