editing items

This commit is contained in:
Flatlogic Bot 2026-03-03 17:37:36 +00:00
parent a2fc645f85
commit c34a6ddf37
3 changed files with 126 additions and 288 deletions

View File

@ -20,7 +20,7 @@ function load_dotenv(): void {
[$k, $v] = array_map('trim', explode('=', $line, 2));
if ($k === '') continue;
// Strip potential surrounding quotes
// Strip potential surrounding quotes (single or double)
$v = trim($v, "' ");
// Set env if not already set or if empty
@ -36,5 +36,4 @@ function load_dotenv(): void {
}
// Auto-load on include
load_dotenv();
load_dotenv();

View File

@ -3,8 +3,10 @@
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/lang.php';
$isAjax = isset($_POST['ajax']) || (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && $_SERVER['HTTP_X_REQUESTED_WITH'] === 'XMLHttpRequest');
if (!isset($_SESSION['user_id'])) {
if (isset($_POST['ajax'])) {
if ($isAjax) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
exit;
@ -15,7 +17,7 @@ if (!isset($_SESSION['user_id'])) {
// Initial view check
if (!has_permission('view')) {
if (isset($_POST['ajax'])) {
if ($isAjax) {
header('Content-Type: application/json');
echo json_encode(['success' => false, 'error' => 'Forbidden']);
exit;
@ -124,7 +126,6 @@ function renderServiceList($lang) {
// Handle Actions
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
$action = $_POST['action'];
$isAjax = isset($_POST['ajax']);
// Permission mapping for actions
$required_permission = 'view';
@ -278,7 +279,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['action'])) {
ON DUPLICATE KEY UPDATE price = ?");
$stmt->execute([$item_id, $service_id, $price, $price]);
if (isset($_POST['ajax'])) {
if ($isAjax) {
header('Content-Type: application/json');
echo json_encode(['success' => true]);
exit;
@ -430,8 +431,8 @@ foreach ($prices_raw as $p) {
<td class="ps-4">
<div class="d-flex align-items-center">
<div class="bg-light rounded-3 me-3 d-flex align-items-center justify-content-center" style="width: 48px; height: 48px; overflow: hidden;">
<?php if($item['image_url']): ?>
<img src="<?= $item['image_url'] ?>?v=<?= time() ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php if($item['image_url']):
?><img src="<?= $item['image_url'] ?>?v=<?= time() ?>" alt="" style="width: 100%; height: 100%; object-fit: cover;">
<?php else:
?><i class="bi bi-image text-muted"></i>
<?php endif; ?>
@ -954,4 +955,4 @@ document.getElementById('itemImageFile').addEventListener('change', function(e)
});
</script>
<?php require_once __DIR__ . '/includes/footer.php'; ?>
<?php require_once __DIR__ . '/includes/footer.php'; ?>

View File

@ -1,4 +1,5 @@
<?php
session_start();
require_once __DIR__ . '/db/config.php';
require_once __DIR__ . '/includes/lang.php';
@ -18,8 +19,8 @@ $stmt = db()->prepare("SELECT o.*,
c.name_en as customer_name_en, c.name_ar as customer_name_ar, c.phone as customer_phone,
b.name_en as branch_name_en, b.name_ar as branch_name_ar, b.address_en as branch_address_en, b.address_ar as branch_address_ar, b.phone as branch_phone
FROM orders o
LEFT JOIN customers c ON o.customer_id = c.id
JOIN branches b ON o.branch_id = b.id
LEFT JOIN customers c ON o.customer_id = c.id
LEFT JOIN branches b ON o.branch_id = b.id
WHERE o.id = ?");
$stmt->execute([$order_id]);
$order = $stmt->fetch();
@ -28,319 +29,156 @@ if (!$order) {
die("Order not found");
}
// Fetch Order Items
$stmt = db()->prepare("SELECT oi.*, i.name_en as item_en, i.name_ar as item_ar,
s.name_en as service_en, s.name_ar as service_ar
// Fetch items for this order
$stmt = db()->prepare("SELECT oi.*, i.name_en, i.name_ar, s.name_en as service_en, s.name_ar as service_ar
FROM order_items oi
JOIN items i ON oi.item_id = i.id
JOIN services s ON oi.service_id = s.id
WHERE oi.order_id = ?");
$stmt->execute([$order_id]);
$order_items = $stmt->fetchAll();
$items = $stmt->fetchAll();
// Fetch Payments
$stmt = db()->prepare("SELECT SUM(amount) as total_paid FROM payments WHERE order_id = ?");
$stmt->execute([$order_id]);
$total_paid = $stmt->fetch()['total_paid'] ?? 0;
$remaining = ($order['total_price'] - $order['loyalty_discount']) - $total_paid;
// Bilingual Helper for Labels
function b_label($key) {
global $translations;
$en = $translations['en'][$key] ?? $key;
$ar = $translations['ar'][$key] ?? $key;
return '<span class="label-en">' . $en . '</span> / <span class="label-ar">' . $ar . '</span>';
$lang = $_SESSION['lang'] ?? 'en';
function is_arabic() {
global $lang;
return $lang === 'ar';
}
// Fallbacks
$display_company_name_en = $company['name_en'] ?: 'Laundry Brand';
$display_company_name_ar = $company['name_ar'] ?: 'علامة غسيل';
$display_vat = $company['vat_no'] ?: $company['vat_number'];
$display_ctr = $company['ctr_no'];
function format_currency($amount) {
return number_format($amount, decimals()) . ' ' . currency();
}
?>
<!DOCTYPE html>
<html lang="en" dir="ltr">
<html lang="<?= $lang ?>" dir="<?= is_arabic() ? 'rtl' : 'ltr' ?>">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Receipt #<?= $order['order_number'] ?></title>
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;700&family=Inter:wght@400;700&display=swap" rel="stylesheet">
<title>Receipt #<?= $order['id'] ?></title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;600&family=IBM+Plex+Sans+Arabic:wght@400;600&display=swap" rel="stylesheet">
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: 'Inter', 'Cairo', sans-serif;
font-size: 12px;
line-height: 1.4;
color: #000;
background-color: #fff;
font-family: 'IBM Plex Sans', 'IBM Plex Sans Arabic', sans-serif;
background: #f8f9fa;
color: #333;
-webkit-print-color-adjust: exact;
}
.receipt-container {
width: 80mm;
padding: 5mm;
margin: 0 auto;
background: white;
margin: 20px auto;
padding: 10px;
box-shadow: 0 0 10px rgba(0,0,0,0.1);
}
.text-center { text-align: center; }
.text-end { text-align: right; }
.text-start { text-align: left; }
.fw-bold { font-weight: bold; }
.mb-1 { margin-bottom: 2mm; }
.mb-2 { margin-bottom: 4mm; }
.mt-2 { margin-top: 4mm; }
.logo {
max-width: 50mm;
max-height: 30mm;
margin-bottom: 3mm;
}
.receipt-header {
border-bottom: 1px dashed #000;
padding-bottom: 3mm;
margin-bottom: 3mm;
}
.info-row {
display: flex;
justify-content: space-between;
margin-bottom: 1.5mm;
align-items: flex-start;
}
.info-label {
font-size: 10px;
flex-shrink: 0;
margin-right: 2mm;
}
.info-value {
text-align: right;
word-break: break-all;
}
.label-en { font-size: 10px; }
.label-ar { font-size: 11px; font-family: 'Cairo', sans-serif; }
table {
width: 100%;
border-collapse: collapse;
margin-bottom: 3mm;
}
th {
text-align: inherit;
border-bottom: 1px solid #000;
padding: 1mm 0;
font-size: 10px;
}
td {
padding: 2mm 0;
vertical-align: top;
border-bottom: 0.1mm solid #eee;
}
.item-name-ar {
font-family: 'Cairo', sans-serif;
font-size: 11px;
display: block;
margin-top: 0.5mm;
}
.item-service {
font-size: 9px;
color: #555;
display: block;
}
.totals {
border-top: 1px dashed #000;
padding-top: 2mm;
}
.total-row {
display: flex;
justify-content: space-between;
margin-bottom: 1mm;
align-items: center;
}
.grand-total {
font-size: 14px;
border-top: 1px solid #000;
padding-top: 1mm;
margin-top: 1mm;
}
.footer {
margin-top: 5mm;
border-top: 1px dashed #000;
padding-top: 3mm;
font-size: 10px;
}
.arabic-text {
font-family: 'Cairo', sans-serif;
direction: rtl;
}
@media print {
body {
width: 80mm;
margin: 0;
padding: 3mm;
}
.no-print {
display: none;
}
body { background: white; margin: 0; padding: 0; }
.receipt-container { margin: 0; box-shadow: none; width: 100%; }
.no-print { display: none; }
}
.header { text-align: center; border-bottom: 1px dashed #ccc; padding-bottom: 10px; margin-bottom: 10px; }
.item-row { display: flex; justify-content: space-between; margin-bottom: 5px; font-size: 0.9rem; }
.item-name { flex-grow: 1; }
.item-price { text-align: right; margin-left: 10px; }
.item-service { font-size: 0.75rem; color: #666; }
.totals { border-top: 1px dashed #ccc; padding-top: 10px; margin-top: 10px; }
.total-row { display: flex; justify-content: space-between; font-weight: 600; font-size: 1rem; }
.info-row { font-size: 0.8rem; margin-bottom: 3px; display: flex; justify-content: space-between; }
.barcode { text-align: center; margin-top: 15px; }
.arabic-text { font-family: 'IBM Plex Sans Arabic', sans-serif; }
</style>
</head>
<body>
<div class="no-print text-center mb-2">
<button onclick="window.print()" style="padding: 10px 20px; cursor: pointer; border-radius: 8px; border: 1px solid #000; background: #eee;">Print / طباعة</button>
<button onclick="window.close()" style="padding: 10px 20px; cursor: pointer; border-radius: 8px; border: 1px solid #000; background: #fff;">Close / إغلاق</button>
</div>
<div class="text-center receipt-header">
<?php if ($company['logo']): ?>
<img src="<?= $company['logo'] ?>" alt="Logo" class="logo">
<?php else: ?>
<div class="mb-2" style="font-size: 24px; color: #0d6efd;"></div>
<div class="no-print text-center my-3">
<button onclick="window.print()" class="btn btn-primary btn-sm px-4">Print Receipt</button>
<a href="pos.php" class="btn btn-outline-secondary btn-sm ms-2">Back to POS</a>
</div>
<div class="receipt-container">
<div class="header">
<h5 class="fw-bold m-0"><?= $company['name_en'] ?? 'Laundry POS' ?></h5>
<?php if ($company['name_ar']): ?>
<div class="arabic-text small"><?= $company['name_ar'] ?></div>
<?php endif; ?>
<h3 class="fw-bold"><?= htmlspecialchars($display_company_name_en) ?></h3>
<h3 class="fw-bold arabic-text"><?= htmlspecialchars($display_company_name_ar) ?></h3>
<div class="mt-1">
<div class="fw-bold"><?= $order['branch_name_en'] ?></div>
<div class="fw-bold arabic-text"><?= $order['branch_name_ar'] ?></div>
</div>
<div class="small mt-1">
<div><?= $order['branch_address_en'] ?></div>
<div class="arabic-text"><?= $order['branch_address_ar'] ?></div>
</div>
<div class="mt-1">
<span><?= $translations['en']['phone'] ?> / <?= $translations['ar']['phone'] ?>:</span>
<span><?= $order['branch_phone'] ?></span>
</div>
<?php if (!empty($display_ctr)): ?>
<div>
<span><?= $translations['en']['ctr_no'] ?> / <?= $translations['ar']['ctr_no'] ?>:</span>
<span><?= htmlspecialchars($display_ctr) ?></span>
</div>
<?php endif; ?>
<?php if (!empty($display_vat)): ?>
<div>
<span><?= $translations['en']['vat_no'] ?> / <?= $translations['ar']['vat_no'] ?>:</span>
<span><?= htmlspecialchars($display_vat) ?></span>
</div>
<div class="small mt-1"><?= $order['branch_name_en'] ?> / <span class="arabic-text"><?= $order['branch_name_ar'] ?></span></div>
<div class="small"><?= $order['branch_phone'] ?></div>
<?php if ($company['vat_no']): ?>
<div class="small">VAT: <?= $company['vat_no'] ?></div>
<?php endif; ?>
</div>
<div class="mb-2">
<div class="info-row">
<div class="info-label"><?= b_label('order') ?> #</div>
<div class="info-value fw-bold"><?= $order['order_number'] ?></div>
</div>
<div class="info-row">
<div class="info-label"><?= b_label('date') ?></div>
<div class="info-value"><?= date('d/m/Y H:i', strtotime($order['created_at'])) ?></div>
</div>
<div class="info-row">
<div class="info-label"><?= b_label('customer') ?></div>
<div class="info-value">
<div><?= $order['customer_name_en'] ?: 'Walk-in' ?></div>
<?php if ($order['customer_name_ar']): ?>
<div class="arabic-text"><?= $order['customer_name_ar'] ?></div>
<?php endif; ?>
</div>
</div>
<?php if ($order['customer_phone']): ?>
<div class="info-row">
<div class="info-label"><?= b_label('phone') ?></div>
<div class="info-value"><?= $order['customer_phone'] ?></div>
</div>
<?php endif; ?>
<div class="info-row">
<span>Date:</span>
<span><?= date('Y-m-d H:i', strtotime($order['created_at'])) ?></span>
</div>
<div class="info-row">
<span>Order #:</span>
<span class="fw-bold"><?= $order['id'] ?></span>
</div>
<div class="info-row">
<span>Customer:</span>
<span><?= $order['customer_name_en'] ?></span>
</div>
<div class="info-row">
<span>Phone:</span>
<span><?= $order['customer_phone'] ?></span>
</div>
<table>
<thead>
<tr>
<th style="width: 55%;"><?= b_label('item') ?></th>
<th class="text-center" style="width: 15%;"><?= b_label('qty') ?></th>
<th class="text-end" style="width: 30%;"><?= b_label('total') ?></th>
</tr>
</thead>
<tbody>
<?php foreach($order_items as $item): ?>
<tr>
<td>
<div class="fw-bold" style="font-size: 11px;"><?= $item['item_en'] ?></div>
<div class="item-name-ar"><?= $item['item_ar'] ?></div>
<div class="item-service"><?= $item['service_en'] ?> / <span class="arabic-text"><?= $item['service_ar'] ?></span></div>
</td>
<td class="text-center"><?= $item['quantity'] ?></td>
<td class="text-end fw-bold"><?= number_format($item['subtotal'] + ($item['vat_amount'] * $item['quantity']), decimals()) ?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
<div class="mt-3 border-top pt-2">
<?php foreach($items as $item): ?>
<div class="mb-2">
<div class="item-row">
<div class="item-name"><?= $item['name_en'] ?> / <span class="arabic-text"><?= $item['name_ar'] ?></span> x <?= $item['quantity'] ?></div>
<div class="item-price"><?= format_currency($item['subtotal']) ?></div>
</div>
<div class="item-service"><?= $item['service_en'] ?> / <span class="arabic-text"><?= $item['service_ar'] ?></span></div>
</div>
<?php endforeach; ?>
</div>
<div class="totals">
<div class="total-row">
<div class="info-label"><?= b_label('subtotal') ?></div>
<div class="info-value"><?= format_amount($order['total_price'] - $order['vat_total']) ?></div>
</div>
<div class="total-row">
<div class="info-label"><?= b_label('vat') ?></div>
<div class="info-value"><?= format_amount($order['vat_total']) ?></div>
</div>
<?php if ($order['loyalty_discount'] > 0): ?>
<div class="total-row text-success fw-bold">
<div class="info-label"><?= b_label('loyalty_discount') ?></div>
<div class="info-value">-<?= format_amount($order['loyalty_discount']) ?></div>
<div class="info-row">
<span>Subtotal:</span>
<span><?= format_currency($order['subtotal']) ?></span>
</div>
<?php if ($order['discount_amount'] > 0): ?>
<div class="info-row">
<span>Discount:</span>
<span>-<?= format_currency($order['discount_amount']) ?></span>
</div>
<?php endif; ?>
<?php if ($order['vat_amount'] > 0): ?>
<div class="info-row">
<span>VAT (<?= $order['vat_amount'] > 0 ? round(($order['vat_amount'] / ($order['subtotal'] ?: 1)) * 100) : 0 ?>%):</span>
<span><?= format_currency($order['vat_amount']) ?></span>
</div>
<?php endif; ?>
<div class="total-row fw-bold grand-total">
<div class="info-label"><?= b_label('total') ?></div>
<div class="info-value" style="font-size: 16px;"><?= format_amount($order['total_price'] - $order['loyalty_discount']) ?></div>
</div>
<div class="total-row mt-2">
<div class="info-label"><?= b_label('paid') ?></div>
<div class="info-value"><?= format_amount($total_paid) ?></div>
<span>Total:</span>
<span><?= format_currency($order['total_amount']) ?></span>
</div>
<?php if ($remaining > 0): ?>
<div class="total-row">
<div class="info-label"><?= b_label('remaining_amount') ?></div>
<div class="info-value fw-bold text-danger"><?= format_amount($remaining) ?></div>
</div>
<?php endif; ?>
</div>
<div class="text-center footer">
<div class="fw-bold mb-1">Thank you for your visit!</div>
<div class="fw-bold arabic-text mb-1">شكراً لزيارتكم!</div>
<div class="mt-1">Please keep your receipt</div>
<div class="arabic-text">يرجى الاحتفاظ بالإيصال</div>
<div class="mt-4 text-center small">
<div>Thank you for choosing us!</div>
<div class="arabic-text">شكراً لاختياركم لنا!</div>
</div>
<script>
window.onload = function() {
// Check if we are in the print iframe
if (window.location.search.includes('id=')) {
// Auto print is handled by the parent in pos.php
}
}
</script>
<div class="barcode">
<svg id="barcode"></svg>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/jsbarcode@3.11.0/dist/JsBarcode.all.min.js"></script>
<script>
JsBarcode("#barcode", "<?= $order['id'] ?>", {
format: "CODE128",
lineColor: "#000",
width: 1.5,
height: 40,
displayValue: false
});
</script>
</body>
</html>
</html>