Add manufacturer lot traceability
This commit is contained in:
1 parent
2aadce17bd
commit
19ce29c3d0
10 files changed
+418
-14
No files matched your search
@@ -2,6 +2,7 @@
|
||||
declare(strict_types=1);
|
||||
|
||||
require_once __DIR__ . '/forecast.php';
|
||||
require_once __DIR__ . '/../../system/audit.php';
|
||||
|
||||
function get_default_location_ids(PDO $pdo): array
|
||||
{
|
||||
@@ -76,8 +77,13 @@ function get_lager_product_overview(PDO $pdo): array
|
||||
p.uom,
|
||||
COALESCE(SUM(v.qty_net), 0) AS available_qty,
|
||||
sl.lot_number,
|
||||
sl.supplier_lot_number,
|
||||
sl.id AS lot_id,
|
||||
sl.manufacturer_party_id,
|
||||
sl.manufacturer_lot_number,
|
||||
mp.name AS manufacturer_name,
|
||||
sl.mfg_date,
|
||||
sl.expiry_date,
|
||||
sl.purchase_date,
|
||||
sl.sellout_date,
|
||||
sl.sellout_forecast_status,
|
||||
(
|
||||
@@ -92,11 +98,13 @@ function get_lager_product_overview(PDO $pdo): array
|
||||
LEFT JOIN stock_lot sl
|
||||
ON sl.product_id = p.id
|
||||
AND sl.status = 'current'
|
||||
LEFT JOIN party mp ON mp.id = sl.manufacturer_party_id
|
||||
LEFT JOIN v_stock_lot_balance v
|
||||
ON v.stock_lot_id = sl.id
|
||||
WHERE p.status = 'active'
|
||||
GROUP BY p.id, p.name, p.sku, p.uom, sl.lot_number, sl.supplier_lot_number,
|
||||
sl.expiry_date, sl.sellout_date, sl.sellout_forecast_status
|
||||
GROUP BY p.id, p.name, p.sku, p.uom, sl.id, sl.lot_number, sl.manufacturer_party_id,
|
||||
sl.manufacturer_lot_number, mp.name, sl.mfg_date, sl.expiry_date, sl.purchase_date,
|
||||
sl.sellout_date, sl.sellout_forecast_status
|
||||
ORDER BY p.name ASC, p.id ASC"
|
||||
);
|
||||
|
||||
@@ -107,9 +115,14 @@ function get_lager_product_overview(PDO $pdo): array
|
||||
'sku' => (string) $row['sku'],
|
||||
'uom' => (string) $row['uom'],
|
||||
'available_qty' => (float) $row['available_qty'],
|
||||
'lot_id' => $row['lot_id'] === null ? null : (int) $row['lot_id'],
|
||||
'lot_number' => $row['lot_number'],
|
||||
'supplier_lot_number' => $row['supplier_lot_number'],
|
||||
'manufacturer_party_id' => $row['manufacturer_party_id'] === null ? null : (int) $row['manufacturer_party_id'],
|
||||
'manufacturer_lot_number' => $row['manufacturer_lot_number'],
|
||||
'manufacturer_name' => $row['manufacturer_name'],
|
||||
'mfg_date' => $row['mfg_date'],
|
||||
'expiry_date' => $row['expiry_date'],
|
||||
'purchase_date' => $row['purchase_date'],
|
||||
'sellout_date' => $row['sellout_date'],
|
||||
'sellout_forecast_status' => (string) ($row['sellout_forecast_status'] ?? 'no_demand'),
|
||||
'virtual_lot_number' => $row['virtual_lot_number'],
|
||||
@@ -118,6 +131,126 @@ function get_lager_product_overview(PDO $pdo): array
|
||||
);
|
||||
}
|
||||
|
||||
function update_stock_lot_traceability(
|
||||
PDO $pdo,
|
||||
int $lotId,
|
||||
?int $manufacturerPartyId,
|
||||
?string $manufacturerLotNumber,
|
||||
?string $mfgDate,
|
||||
?string $expiryDate,
|
||||
?string $purchaseDate,
|
||||
string $changedBy
|
||||
): array {
|
||||
if ($lotId <= 0) {
|
||||
throw new InvalidArgumentException('Chargen-ID fehlt');
|
||||
}
|
||||
|
||||
$manufacturerLotNumber = $manufacturerLotNumber !== null ? trim($manufacturerLotNumber) : null;
|
||||
if ($manufacturerLotNumber === '') {
|
||||
$manufacturerLotNumber = null;
|
||||
}
|
||||
|
||||
$dates = [
|
||||
'mfg_date' => $mfgDate,
|
||||
'expiry_date' => $expiryDate,
|
||||
'purchase_date' => $purchaseDate,
|
||||
];
|
||||
foreach ($dates as $field => $value) {
|
||||
if ($value !== null && $value !== '') {
|
||||
$parsed = DateTimeImmutable::createFromFormat('!Y-m-d', $value);
|
||||
$errors = DateTimeImmutable::getLastErrors();
|
||||
if ($parsed === false || (is_array($errors) && (($errors['warning_count'] ?? 0) > 0 || ($errors['error_count'] ?? 0) > 0))) {
|
||||
throw new InvalidArgumentException("Ungueltiges Datum fuer {$field}");
|
||||
}
|
||||
}
|
||||
$dates[$field] = $value !== null && $value !== '' ? $value : null;
|
||||
}
|
||||
|
||||
$startedTransaction = !$pdo->inTransaction();
|
||||
if ($startedTransaction) {
|
||||
$pdo->beginTransaction();
|
||||
}
|
||||
|
||||
try {
|
||||
$lotStmt = $pdo->prepare(
|
||||
"SELECT id, product_id, lot_number, manufacturer_party_id, manufacturer_lot_number,
|
||||
mfg_date, expiry_date, purchase_date, status
|
||||
FROM stock_lot
|
||||
WHERE id = :id
|
||||
FOR UPDATE"
|
||||
);
|
||||
$lotStmt->execute([':id' => $lotId]);
|
||||
$before = $lotStmt->fetch();
|
||||
if (!is_array($before)) {
|
||||
throw new RuntimeException('Charge nicht gefunden');
|
||||
}
|
||||
if ((string) $before['status'] === 'virtual_shortage') {
|
||||
throw new InvalidArgumentException('Virtuelle Fehlbestandschargen koennen nicht gepflegt werden');
|
||||
}
|
||||
|
||||
if ($manufacturerPartyId !== null) {
|
||||
$manufacturerStmt = $pdo->prepare(
|
||||
"SELECT p.id
|
||||
FROM party p
|
||||
INNER JOIN party_role pr ON pr.party_id = p.id AND pr.role_code = 'manufacturer'
|
||||
WHERE p.id = :id AND p.status = 'active'
|
||||
LIMIT 1"
|
||||
);
|
||||
$manufacturerStmt->execute([':id' => $manufacturerPartyId]);
|
||||
if ($manufacturerStmt->fetchColumn() === false) {
|
||||
throw new InvalidArgumentException('Hersteller ist nicht aktiv oder nicht als Hersteller gepflegt');
|
||||
}
|
||||
}
|
||||
|
||||
if ($manufacturerPartyId === null && $manufacturerLotNumber !== null) {
|
||||
throw new InvalidArgumentException('Hersteller-Chargennummer benoetigt einen Hersteller');
|
||||
}
|
||||
if ($manufacturerPartyId !== null && $manufacturerLotNumber === null && (string) $before['status'] !== 'open') {
|
||||
throw new InvalidArgumentException('Hersteller-Chargennummer fehlt');
|
||||
}
|
||||
|
||||
$updateStmt = $pdo->prepare(
|
||||
"UPDATE stock_lot
|
||||
SET manufacturer_party_id = :manufacturer_party_id,
|
||||
manufacturer_lot_number = :manufacturer_lot_number,
|
||||
mfg_date = :mfg_date,
|
||||
expiry_date = :expiry_date,
|
||||
purchase_date = :purchase_date,
|
||||
updated_at = NOW()
|
||||
WHERE id = :id"
|
||||
);
|
||||
$updateStmt->execute([
|
||||
':manufacturer_party_id' => $manufacturerPartyId,
|
||||
':manufacturer_lot_number' => $manufacturerLotNumber,
|
||||
':mfg_date' => $dates['mfg_date'],
|
||||
':expiry_date' => $dates['expiry_date'],
|
||||
':purchase_date' => $dates['purchase_date'],
|
||||
':id' => $lotId,
|
||||
]);
|
||||
|
||||
$after = $before;
|
||||
$after['manufacturer_party_id'] = $manufacturerPartyId;
|
||||
$after['manufacturer_lot_number'] = $manufacturerLotNumber;
|
||||
$after['mfg_date'] = $dates['mfg_date'];
|
||||
$after['expiry_date'] = $dates['expiry_date'];
|
||||
$after['purchase_date'] = $dates['purchase_date'];
|
||||
system_write_audit_log($pdo, 'stock_lot', (string) $lotId, 'traceability_updated', $changedBy, $before, $after, [
|
||||
'module' => 'erp.lager',
|
||||
]);
|
||||
|
||||
if ($startedTransaction) {
|
||||
$pdo->commit();
|
||||
}
|
||||
|
||||
return $after;
|
||||
} catch (Throwable $error) {
|
||||
if ($startedTransaction && $pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
|
||||
function get_current_lot_balance_for_update(PDO $pdo, int $productId): array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
|
||||
Reference in new issue
Block a user