181 lines
5.7 KiB
PHP
181 lines
5.7 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
require_once __DIR__ . '/../../../../modules/shared/db.php';
|
|
require_once __DIR__ . '/../../../../modules/shared/webhook_throttle.php';
|
|
require_once __DIR__ . '/../../kontakte/service.php';
|
|
require_once __DIR__ . '/../../bestellungen/service.php';
|
|
require_once __DIR__ . '/../../artikel-mapping/service.php';
|
|
require_once __DIR__ . '/../../lager/service.php';
|
|
require_once __DIR__ . '/../../import-integration/service.php';
|
|
|
|
header('Content-Type: application/json; charset=utf-8');
|
|
|
|
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']);
|
|
}
|
|
|
|
try {
|
|
$data = json_decode($jsonInput, true, 512, JSON_THROW_ON_ERROR);
|
|
} catch (JsonException) {
|
|
json_response(400, ['ok' => false, 'error' => 'Invalid JSON payload']);
|
|
}
|
|
|
|
if (!is_array($data)) {
|
|
json_response(400, ['ok' => false, 'error' => 'JSON object expected']);
|
|
}
|
|
|
|
$required = ['products', 'totalPrice', 'paymentMethod', 'billing'];
|
|
foreach ($required as $field) {
|
|
if (!array_key_exists($field, $data)) {
|
|
json_response(422, ['ok' => false, 'error' => "Missing field: {$field}"]);
|
|
}
|
|
}
|
|
|
|
$products = $data['products'];
|
|
$totalPrice = parse_number($data['totalPrice']);
|
|
$paymentMethodCode = trim((string) $data['paymentMethod']);
|
|
$billing = is_array($data['billing']) ? $data['billing'] : [];
|
|
|
|
if (!is_array($products) || count($products) === 0) {
|
|
json_response(422, ['ok' => false, 'error' => 'No products specified']);
|
|
}
|
|
|
|
if ($totalPrice === null || $totalPrice <= 0) {
|
|
json_response(422, ['ok' => false, 'error' => 'Invalid total price']);
|
|
}
|
|
|
|
$resolvedProducts = [];
|
|
$totalQty = 0.0;
|
|
foreach ($products as $product) {
|
|
if (!is_array($product) || !isset($product['qty'], $product['productId'])) {
|
|
json_response(422, ['ok' => false, 'error' => 'Product missing productId or qty']);
|
|
}
|
|
|
|
$qty = parse_number($product['qty']);
|
|
$productId = (int) $product['productId'];
|
|
if ($qty === null || $qty <= 0 || $productId <= 0) {
|
|
json_response(422, ['ok' => false, 'error' => 'Invalid product quantity or productId']);
|
|
}
|
|
|
|
$resolvedProducts[] = [
|
|
'qty' => (float) $qty,
|
|
'productId' => $productId,
|
|
];
|
|
$totalQty += (float) $qty;
|
|
}
|
|
|
|
if ($totalQty <= 0) {
|
|
json_response(422, ['ok' => false, 'error' => 'No product quantity specified']);
|
|
}
|
|
|
|
try {
|
|
$env = expand_env_values(parse_env_file(__DIR__ . '/../../../../.env'));
|
|
$pdo = connect_database($env);
|
|
$pdo->beginTransaction();
|
|
|
|
$locations = get_default_location_ids($pdo);
|
|
|
|
$paymentMethodId = lookup_method_id($pdo, 'payment_method', $paymentMethodCode);
|
|
if ($paymentMethodId === null) {
|
|
throw new RuntimeException('Invalid payment method');
|
|
}
|
|
|
|
$partyId = find_or_create_party($pdo, $billing);
|
|
upsert_addresses($pdo, $partyId, $billing);
|
|
|
|
$order = create_direct_sales_order($pdo, [
|
|
':party_id' => $partyId,
|
|
':payment_method_id' => $paymentMethodId,
|
|
':amount_net' => $totalPrice,
|
|
':total_amount' => $totalPrice,
|
|
]);
|
|
|
|
$orderId = (int) $order['id'];
|
|
$externalRef = (string) $order['external_ref'];
|
|
|
|
$remainingTotal = round((float) $totalPrice, 2);
|
|
$lineNo = 0;
|
|
|
|
foreach ($resolvedProducts as $product) {
|
|
$lineNo++;
|
|
$qty = (float) $product['qty'];
|
|
$productId = (int) $product['productId'];
|
|
|
|
$productStmt = $pdo->prepare(
|
|
"SELECT id, sku, name
|
|
FROM product
|
|
WHERE id = :id
|
|
AND status = 'active'
|
|
LIMIT 1"
|
|
);
|
|
$productStmt->execute([':id' => $productId]);
|
|
$resolvedProduct = $productStmt->fetch();
|
|
if (!is_array($resolvedProduct)) {
|
|
throw new RuntimeException("Kein aktives ERP-Produkt fuer Produkt-ID {$productId} gefunden");
|
|
}
|
|
|
|
$sellableItemId = find_sellable_item_for_product($pdo, $productId);
|
|
if ($sellableItemId === null) {
|
|
throw new RuntimeException("Kein Artikel-Mapping fuer Produkt-ID {$productId} gefunden");
|
|
}
|
|
|
|
if ($lineNo === count($resolvedProducts)) {
|
|
$unitPrice = round($remainingTotal / $qty, 4);
|
|
$lineTotal = $remainingTotal;
|
|
} else {
|
|
$unitPrice = round((float) $totalPrice / (float) $totalQty, 4);
|
|
$lineTotal = round($qty * $unitPrice, 2);
|
|
$remainingTotal = round($remainingTotal - $lineTotal, 2);
|
|
}
|
|
|
|
$lineId = insert_sales_order_line($pdo, [
|
|
':sales_order_id' => $orderId,
|
|
':line_no' => $lineNo,
|
|
':sellable_item_id' => $sellableItemId,
|
|
':article_number' => (string) $resolvedProduct['sku'],
|
|
':title' => (string) $resolvedProduct['name'],
|
|
':qty' => $qty,
|
|
':unit_price' => $unitPrice,
|
|
':line_total' => $lineTotal,
|
|
]);
|
|
|
|
allocate_line_inventory(
|
|
$pdo,
|
|
$orderId,
|
|
$lineId,
|
|
$lineNo,
|
|
$qty,
|
|
$sellableItemId,
|
|
$locations
|
|
);
|
|
}
|
|
|
|
$pdo->commit();
|
|
|
|
$excelTrigger = trigger_excel_webhook($externalRef, $env);
|
|
|
|
json_response(201, [
|
|
'ok' => true,
|
|
'orderId' => $orderId,
|
|
'externalRef' => $externalRef,
|
|
'partyId' => $partyId,
|
|
'excelTrigger' => $excelTrigger,
|
|
'message' => 'OTC order created successfully',
|
|
]);
|
|
} catch (Throwable $e) {
|
|
if (isset($pdo) && $pdo instanceof PDO && $pdo->inTransaction()) {
|
|
$pdo->rollBack();
|
|
}
|
|
|
|
json_response(500, [
|
|
'ok' => false,
|
|
'error' => 'Internal server error: ' . $e->getMessage(),
|
|
]);
|
|
}
|