false, 'error' => 'Unauthorized']); } if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'POST') { json_response(405, ['ok' => false, 'error' => 'Method Not Allowed']); } $jsonInput = file_get_contents('php://input'); if ($jsonInput === false || trim($jsonInput) === '') { json_response(400, ['ok' => false, 'error' => 'Empty payload']); } $data = json_decode($jsonInput, true, 512, JSON_THROW_ON_ERROR); if (!is_array($data) || !auth_validate_csrf_token($data['csrfToken'] ?? null)) { json_response(403, ['ok' => false, 'error' => 'Invalid security token']); } $action = trim((string) ($data['action'] ?? '')); if ($action === 'create_manufacturer') { $manufacturer = create_manufacturer( $pdo, (string) ($data['name'] ?? ''), isset($data['email']) ? (string) $data['email'] : null, isset($data['phone']) ? (string) $data['phone'] : null ); json_response(201, ['ok' => true, 'manufacturer' => $manufacturer]); } if ($action === 'update_lot') { $manufacturerPartyId = $data['manufacturerPartyId'] ?? null; $manufacturerPartyId = $manufacturerPartyId === null || $manufacturerPartyId === '' ? null : (int) $manufacturerPartyId; $receiptQty = isset($data['receiptQty']) && $data['receiptQty'] !== '' ? (float) $data['receiptQty'] : 0.0; $adjustmentQty = isset($data['adjustmentQty']) && $data['adjustmentQty'] !== '' ? (float) $data['adjustmentQty'] : 0.0; $outQty = isset($data['outQty']) && $data['outQty'] !== '' ? (float) $data['outQty'] : 0.0; $updatedLot = update_stock_lot_traceability( $pdo, (int) ($data['lotId'] ?? 0), $manufacturerPartyId, isset($data['manufacturerLotNumber']) ? (string) $data['manufacturerLotNumber'] : null, isset($data['mfgDate']) ? (string) $data['mfgDate'] : null, isset($data['expiryDate']) ? (string) $data['expiryDate'] : null, isset($data['purchaseDate']) ? (string) $data['purchaseDate'] : null, (string) ($currentUser['username'] ?? $currentUser['email'] ?? 'unknown'), $receiptQty, $adjustmentQty, $outQty ); $productId = (int) $updatedLot['product_id']; refresh_lager_stock_lot_forecast_for_product($pdo, $productId); $forecast = get_lager_product_forecast($pdo, $productId); json_response(200, ['ok' => true, 'productId' => $productId, 'lot' => $updatedLot, 'balance' => get_stock_lot_balance($pdo, (int) ($data['lotId'] ?? 0)), 'forecast' => $forecast]); } if ($action === 'delete_open_lot') { $productId = delete_open_stock_lot( $pdo, (int) ($data['lotId'] ?? 0), (string) ($currentUser['username'] ?? $currentUser['email'] ?? 'unknown') ); sync_open_lot_activation_selection($pdo, $productId); json_response(200, ['ok' => true, 'productId' => $productId, 'availableQty' => get_lager_product_available_qty($pdo, $productId)]); } if ($action === 'prepare_lot') { $productId = (int) ($data['productId'] ?? 0); $lotNumber = trim((string) ($data['lotNumber'] ?? '')); $lotId = (int) ($data['lotId'] ?? 0); $manufacturerPartyId = $data['manufacturerPartyId'] ?? null; $manufacturerPartyId = $manufacturerPartyId === null || $manufacturerPartyId === '' ? null : (int) $manufacturerPartyId; $manufacturerLotNumber = trim((string) ($data['manufacturerLotNumber'] ?? '')); $mfgDate = trim((string) ($data['mfgDate'] ?? '')); $expiryDate = trim((string) ($data['expiryDate'] ?? '')); $purchaseDate = trim((string) ($data['purchaseDate'] ?? '')); $receiptQty = isset($data['receiptQty']) && $data['receiptQty'] !== '' ? (float) $data['receiptQty'] : 0.0; $targetQty = isset($data['targetQty']) && $data['targetQty'] !== '' ? (float) $data['targetQty'] : null; if ($lotId > 0) { $lotStmt = $pdo->prepare( "SELECT id FROM stock_lot WHERE id = :lot_id AND product_id = :product_id AND status = 'open'" ); $lotStmt->execute([':lot_id' => $lotId, ':product_id' => $productId]); if ($lotStmt->fetchColumn() === false) { throw new InvalidArgumentException('Offene Charge nicht gefunden'); } } else { $lotId = create_open_lot($pdo, $productId); } $preparedLot = update_stock_lot_traceability( $pdo, $lotId, $manufacturerPartyId, $manufacturerLotNumber, $mfgDate, $expiryDate, $purchaseDate, (string) ($currentUser['username'] ?? $currentUser['email'] ?? 'unknown'), $receiptQty, 0.0, 0.0, $lotNumber, $targetQty, $lotNumber === '' ); sync_open_lot_activation_selection($pdo, $productId); refresh_lager_stock_lot_forecast_for_product($pdo, $productId); $forecast = get_lager_product_forecast($pdo, $productId); json_response(200, ['ok' => true, 'productId' => $productId, 'lot' => $preparedLot, 'balance' => get_stock_lot_balance($pdo, $lotId), 'availableQty' => get_lager_product_available_qty($pdo, $productId), 'forecast' => $forecast]); } if ($action === 'select_open_lot') { $productId = (int) ($data['productId'] ?? 0); $lotId = (int) ($data['lotId'] ?? 0); select_open_lot_for_activation($pdo, $productId, $lotId); json_response(200, ['ok' => true, 'productId' => $productId, 'selectedLotId' => $lotId]); } json_response(422, ['ok' => false, 'error' => 'Unsupported action']); } catch (JsonException) { json_response(400, ['ok' => false, 'error' => 'Invalid JSON payload']); } catch (InvalidArgumentException $error) { json_response(422, ['ok' => false, 'error' => $error->getMessage()]); } catch (Throwable $error) { json_response(500, ['ok' => false, 'error' => 'Interner Serverfehler']); }