diff --git a/api/place_order.php b/api/place_order.php index a7ed2a8..0e2e05f 100644 --- a/api/place_order.php +++ b/api/place_order.php @@ -73,6 +73,8 @@ if (empty($processedItems)) { $totalAmount = $subtotal + $totalVat; try { + $db->beginTransaction(); + $stmt = $db->prepare("INSERT INTO online_orders (customer_name, customer_phone, customer_address, items_json, subtotal, vat_amount, total_amount) VALUES (?, ?, ?, ?, ?, ?, ?)"); $stmt->execute([ $name, @@ -83,10 +85,13 @@ try { $totalVat, $totalAmount ]); + + $orderId = (int) $db->lastInsertId(); + sync_online_order_stock_reservation([], 'rejected', $processedItems, 'pending'); + $db->commit(); // Optional: send telegram and WhatsApp notifications if configured try { - $orderId = (int) $db->lastInsertId(); $orderData = [ 'id' => $orderId, 'customer_name' => $name, @@ -122,7 +127,7 @@ try { $data = ['chat_id' => $chatId, 'text' => $msg, 'parse_mode' => 'Markdown']; $options = [ 'http' => [ - 'header' => "Content-type: application/x-www-form-urlencoded + 'header' => "Content-type: application/x-www-form-urlencoded ", 'method' => 'POST', 'content' => http_build_query($data) @@ -140,6 +145,9 @@ try { } echo json_encode(['success' => true]); -} catch (Exception $e) { +} catch (Throwable $e) { + if ($db->inTransaction()) { + $db->rollBack(); + } echo json_encode(['success' => false, 'error' => 'Database error: ' . $e->getMessage()]); } \ No newline at end of file diff --git a/edit_online_order.php b/edit_online_order.php index c61ad42..2543036 100644 --- a/edit_online_order.php +++ b/edit_online_order.php @@ -69,28 +69,43 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') { if ($normalized === []) { $error = tr('الطلب غير صالح بعد التحقق من الأصناف.', 'The order is invalid after product validation.'); } else { - $statusChanged = ($editOrder['status'] ?? 'pending') !== $saleStatus; - $stmt = db()->prepare('UPDATE online_orders SET - customer_name = :customer_name, - customer_phone = :customer_phone, - customer_address = :customer_address, - items_json = :items_json, - subtotal = :subtotal, - vat_amount = :vat_amount, - total_amount = :total_amount, - status = :status - WHERE id = :id'); - $stmt->execute([ - ':customer_name' => $customerName, - ':customer_phone' => $customerPhone, - ':customer_address' => $customerAddress, - ':items_json' => json_encode($normalized, JSON_UNESCAPED_UNICODE), - ':subtotal' => $subtotal, - ':vat_amount' => $totalVat, - ':total_amount' => $subtotal + $totalVat, - ':status' => $saleStatus, - ':id' => $editOrderId, - ]); + $oldStatus = (string) ($editOrder['status'] ?? 'pending'); + $statusChanged = $oldStatus !== $saleStatus; + $previousItems = json_decode((string) ($editOrder['items_json'] ?? '[]'), true) ?: []; + + db()->beginTransaction(); + try { + sync_online_order_stock_reservation($previousItems, $oldStatus, $normalized, $saleStatus); + + $stmt = db()->prepare('UPDATE online_orders SET + customer_name = :customer_name, + customer_phone = :customer_phone, + customer_address = :customer_address, + items_json = :items_json, + subtotal = :subtotal, + vat_amount = :vat_amount, + total_amount = :total_amount, + status = :status + WHERE id = :id'); + $stmt->execute([ + ':customer_name' => $customerName, + ':customer_phone' => $customerPhone, + ':customer_address' => $customerAddress, + ':items_json' => json_encode($normalized, JSON_UNESCAPED_UNICODE), + ':subtotal' => $subtotal, + ':vat_amount' => $totalVat, + ':total_amount' => $subtotal + $totalVat, + ':status' => $saleStatus, + ':id' => $editOrderId, + ]); + + db()->commit(); + } catch (Throwable $e) { + if (db()->inTransaction()) { + db()->rollBack(); + } + throw $e; + } if ($statusChanged && wablas_is_configured()) { wablas_notify_online_order([ diff --git a/includes/app.php b/includes/app.php index 275a9ce..3858ee8 100644 --- a/includes/app.php +++ b/includes/app.php @@ -89,6 +89,38 @@ try { @file_put_contents($flagFileV6, '1'); } + + $flagFileV7 = sys_get_temp_dir() . '/.schema_migrated_v7_' . md5(__DIR__); + if (!file_exists($flagFileV7)) { + $pdo = db(); + $hasOnlineOrdersTable = (bool) $pdo->query("SHOW TABLES LIKE 'online_orders'")->fetchColumn(); + if ($hasOnlineOrdersTable) { + $onlineOrderStmt = $pdo->query("SELECT items_json FROM online_orders WHERE status IN ('pending', 'accepted', 'completed')"); + $reservedBySku = []; + foreach ($onlineOrderStmt->fetchAll(PDO::FETCH_ASSOC) as $orderRow) { + $orderItems = json_decode((string) ($orderRow['items_json'] ?? '[]'), true) ?: []; + foreach ($orderItems as $item) { + $sku = (string) ($item['sku'] ?? ''); + $qty = (int) ($item['qty'] ?? 0); + if ($sku === '' || $qty <= 0) { + continue; + } + $reservedBySku[$sku] = ($reservedBySku[$sku] ?? 0) + $qty; + } + } + + if ($reservedBySku !== []) { + $adjustStmt = $pdo->prepare("UPDATE items SET base_stock = base_stock - :qty WHERE sku = :sku"); + foreach ($reservedBySku as $sku => $qty) { + $adjustStmt->bindValue(':qty', $qty, PDO::PARAM_INT); + $adjustStmt->bindValue(':sku', $sku); + $adjustStmt->execute(); + } + } + } + + @file_put_contents($flagFileV7, '1'); + } } catch (\Throwable $e) {} @@ -1322,6 +1354,27 @@ function sync_order_stock_reservation(array $oldItems, string $oldStatus, array adjust_item_base_stock($stockDeltaBySku); } +function online_order_reserves_stock(string $status): bool +{ + return in_array($status, ['pending', 'accepted', 'completed'], true); +} + +function sync_online_order_stock_reservation(array $oldItems, string $oldStatus, array $newItems, string $newStatus): void +{ + $previousReserved = online_order_reserves_stock($oldStatus) ? sale_item_quantities($oldItems) : []; + $nextReserved = online_order_reserves_stock($newStatus) ? sale_item_quantities($newItems) : []; + + $stockDeltaBySku = []; + foreach ($previousReserved as $sku => $qty) { + $stockDeltaBySku[$sku] = ($stockDeltaBySku[$sku] ?? 0) + $qty; + } + foreach ($nextReserved as $sku => $qty) { + $stockDeltaBySku[$sku] = ($stockDeltaBySku[$sku] ?? 0) - $qty; + } + + adjust_item_base_stock($stockDeltaBySku); +} + function base_sales_query_filters(array &$params, ?string $mode = null, ?string $branch = null): string { $sql = ' WHERE 1=1 '; diff --git a/online_orders.php b/online_orders.php index b87ea78..713c860 100644 --- a/online_orders.php +++ b/online_orders.php @@ -20,8 +20,25 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { $beforeStmt->execute([$id]); $order = $beforeStmt->fetch(PDO::FETCH_ASSOC); - $stmt = $db->prepare("UPDATE online_orders SET status = ? WHERE id = ?"); - $stmt->execute([$status, $id]); + if ($order) { + $previousItems = json_decode((string) ($order['items_json'] ?? '[]'), true) ?: []; + $oldStatus = (string) ($order['status'] ?? 'pending'); + + $db->beginTransaction(); + try { + sync_online_order_stock_reservation($previousItems, $oldStatus, $previousItems, $status); + + $stmt = $db->prepare("UPDATE online_orders SET status = ? WHERE id = ?"); + $stmt->execute([$status, $id]); + + $db->commit(); + } catch (Throwable $e) { + if ($db->inTransaction()) { + $db->rollBack(); + } + throw $e; + } + } if ($order && ($order['status'] ?? 'pending') !== $status) { $order['status'] = $status; @@ -34,8 +51,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) { redirect_to('online_orders.php'); } elseif ($_POST['action'] === 'delete') { $id = (int)$_POST['id']; - $stmt = $db->prepare("DELETE FROM online_orders WHERE id = ?"); - $stmt->execute([$id]); + $beforeStmt = $db->prepare("SELECT * FROM online_orders WHERE id = ?"); + $beforeStmt->execute([$id]); + $order = $beforeStmt->fetch(PDO::FETCH_ASSOC); + + $db->beginTransaction(); + try { + if ($order) { + $previousItems = json_decode((string) ($order['items_json'] ?? '[]'), true) ?: []; + sync_online_order_stock_reservation($previousItems, (string) ($order['status'] ?? 'pending'), [], 'rejected'); + } + + $stmt = $db->prepare("DELETE FROM online_orders WHERE id = ?"); + $stmt->execute([$id]); + $db->commit(); + } catch (Throwable $e) { + if ($db->inTransaction()) { + $db->rollBack(); + } + throw $e; + } + set_flash('success', tr('تم حذف الطلب بنجاح', 'Order deleted successfully')); redirect_to('online_orders.php'); }