diff --git a/modules/erp/lager/api/lot.php b/modules/erp/lager/api/lot.php index ca141cf..106c04a 100644 --- a/modules/erp/lager/api/lot.php +++ b/modules/erp/lager/api/lot.php @@ -126,7 +126,7 @@ try { $lotNumber === '' ); sync_open_lot_activation_selection($pdo, $productId); - if (promote_open_lot_when_virtual_shortage_is_current($pdo, $productId, $lotId)) { + if (promote_open_lot_when_virtual_shortage_is_current($pdo, $productId, $lotId, (string) ($currentUser['username'] ?? $currentUser['email'] ?? 'unknown'))) { $preparedLot['status'] = 'current'; $preparedLot['next_active'] = false; } else { diff --git a/modules/erp/lager/service.php b/modules/erp/lager/service.php index adb6eb3..8c09ef2 100644 --- a/modules/erp/lager/service.php +++ b/modules/erp/lager/service.php @@ -590,7 +590,7 @@ function sync_open_lot_activation_selection(PDO $pdo, int $productId): void } } -function promote_open_lot_when_virtual_shortage_is_current(PDO $pdo, int $productId, int $lotId): bool +function promote_open_lot_when_virtual_shortage_is_current(PDO $pdo, int $productId, int $lotId, string $changedBy): bool { $startedTransaction = !$pdo->inTransaction(); if ($startedTransaction) { @@ -608,7 +608,8 @@ function promote_open_lot_when_virtual_shortage_is_current(PDO $pdo, int $produc FOR UPDATE" ); $currentStmt->execute([':product_id' => $productId]); - if ($currentStmt->fetchColumn() === false || !is_qualified_open_lot($pdo, $lotId)) { + $virtualShortageLotId = $currentStmt->fetchColumn(); + if ($virtualShortageLotId === false || !is_qualified_open_lot($pdo, $lotId)) { if ($startedTransaction) { $pdo->commit(); } @@ -675,6 +676,14 @@ function promote_open_lot_when_virtual_shortage_is_current(PDO $pdo, int $produc ); $promoteStmt->execute([':lot_id' => $lotId]); + reconcile_virtual_shortage_allocations( + $pdo, + (int) $virtualShortageLotId, + $lotId, + $changedBy, + 'Automatische Verrechnung bei Aktivierung einer echten Charge' + ); + if ($startedTransaction) { $pdo->commit(); } @@ -687,6 +696,78 @@ function promote_open_lot_when_virtual_shortage_is_current(PDO $pdo, int $produc } } +function reconcile_virtual_shortage_allocations( + PDO $pdo, + int $virtualLotId, + int $realLotId, + string $changedBy, + string $reason +): void { + $allocationStmt = $pdo->prepare( + "SELECT id, qty + FROM sales_order_line_lot_allocation + WHERE lot_id = :lot_id + AND allocation_status = 'reserved' + ORDER BY id + FOR UPDATE" + ); + $allocationStmt->execute([':lot_id' => $virtualLotId]); + $allocations = $allocationStmt->fetchAll(); + + foreach ($allocations as $allocation) { + $balanceStmt = $pdo->prepare( + "SELECT COALESCE(qty_net, 0) + FROM v_stock_lot_balance + WHERE stock_lot_id = :lot_id" + ); + $balanceStmt->execute([':lot_id' => $realLotId]); + $availableQty = (float) ($balanceStmt->fetchColumn() ?: 0); + if ($availableQty <= 0.0000001) { + break; + } + + $replaceQty = min((float) $allocation['qty'], $availableQty); + replace_virtual_lot_allocation( + $pdo, + (int) $allocation['id'], + $realLotId, + $replaceQty, + $changedBy, + $reason + ); + } + + $remainingStmt = $pdo->prepare( + "SELECT COUNT(*) + FROM sales_order_line_lot_allocation + WHERE lot_id = :lot_id + AND allocation_status = 'reserved'" + ); + $remainingStmt->execute([':lot_id' => $virtualLotId]); + if ((int) $remainingStmt->fetchColumn() !== 0) { + return; + } + + $closeStmt = $pdo->prepare( + "UPDATE stock_lot + SET status = 'closed', next_active = FALSE, updated_at = NOW() + WHERE id = :lot_id + AND status = 'virtual_shortage'" + ); + $closeStmt->execute([':lot_id' => $virtualLotId]); + if ($closeStmt->rowCount() !== 1) { + throw new RuntimeException('Virtuelle Fehlbestandscharge konnte nicht aufgeloest werden'); + } + system_write_audit_log($pdo, 'stock_lot', (string) $virtualLotId, 'closed', $changedBy, [ + 'status' => 'virtual_shortage', + ], [ + 'status' => 'closed', + ], [ + 'module' => 'erp.lager', + 'reason' => $reason, + ]); +} + function select_open_lot_for_activation(PDO $pdo, int $productId, int $lotId): void { if ($productId <= 0 || $lotId <= 0) { @@ -1217,8 +1298,7 @@ function replace_virtual_lot_allocation( $balanceStmt = $pdo->prepare( "SELECT COALESCE(v.qty_net, 0) FROM v_stock_lot_balance v - WHERE v.stock_lot_id = :lot_id - FOR UPDATE" + WHERE v.stock_lot_id = :lot_id" ); $balanceStmt->execute([':lot_id' => $realLotId]); $available = (float) ($balanceStmt->fetchColumn() ?: 0);