modifying items add
This commit is contained in:
parent
a16d1400d4
commit
a38832e8a0
@ -365,12 +365,16 @@ body {
|
|||||||
padding: 10px;
|
padding: 10px;
|
||||||
font-family: 'Courier New', Courier, monospace;
|
font-family: 'Courier New', Courier, monospace;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
line-height: 1.2;
|
line-height: 1.4;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
color: #000;
|
color: #000;
|
||||||
break-inside: avoid;
|
break-inside: avoid;
|
||||||
page-break-inside: avoid;
|
page-break-inside: avoid;
|
||||||
}
|
}
|
||||||
|
.thermal-receipt.rtl {
|
||||||
|
direction: rtl;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
.thermal-receipt .center {
|
.thermal-receipt .center {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
@ -386,9 +390,12 @@ body {
|
|||||||
border-bottom: 1px dashed #000;
|
border-bottom: 1px dashed #000;
|
||||||
font-size: 10px;
|
font-size: 10px;
|
||||||
}
|
}
|
||||||
|
.thermal-receipt.rtl table th {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
.thermal-receipt table td {
|
.thermal-receipt table td {
|
||||||
padding: 5px 0;
|
padding: 5px 0;
|
||||||
font-size: 10px;
|
font-size: 11px;
|
||||||
}
|
}
|
||||||
.thermal-receipt .total-row {
|
.thermal-receipt .total-row {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|||||||
BIN
assets/pasted-20260219-061427-9bafaf53.png
Normal file
BIN
assets/pasted-20260219-061427-9bafaf53.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 8.0 KiB |
@ -238,24 +238,24 @@ if (empty($slides)) {
|
|||||||
|
|
||||||
<div class="totals-section">
|
<div class="totals-section">
|
||||||
<div class="total-row">
|
<div class="total-row">
|
||||||
<span>Subtotal</span>
|
<span id="labelSubtotal">Subtotal</span>
|
||||||
<span id="displaySubtotal">OMR 0.000</span>
|
<span id="displaySubtotal">OMR 0.000</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div id="displayTaxRow" class="total-row text-muted">
|
||||||
|
<span id="labelVAT">VAT</span>
|
||||||
|
<span id="displayTax">OMR 0.000</span>
|
||||||
|
</div>
|
||||||
<div id="displayDiscountRow" class="total-row text-danger" style="display: none;">
|
<div id="displayDiscountRow" class="total-row text-danger" style="display: none;">
|
||||||
<span>Discount</span>
|
<span id="labelDiscount">Discount</span>
|
||||||
<span id="displayDiscount">- OMR 0.000</span>
|
<span id="displayDiscount">- OMR 0.000</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="displayLoyaltyRow" class="total-row text-success" style="display: none;">
|
<div id="displayLoyaltyRow" class="total-row text-success" style="display: none;">
|
||||||
<span>Loyalty Redeemed</span>
|
<span id="labelLoyalty">Loyalty Redeemed</span>
|
||||||
<span id="displayLoyalty">- OMR 0.000</span>
|
<span id="displayLoyalty">- OMR 0.000</span>
|
||||||
</div>
|
</div>
|
||||||
<div id="displayTaxRow" class="total-row text-muted" style="display: none;">
|
|
||||||
<small>VAT Included</small>
|
|
||||||
<small id="displayTax">OMR 0.000</small>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="grand-total">
|
<div class="grand-total">
|
||||||
<div class="grand-total-label">Total to Pay</div>
|
<div class="grand-total-label" id="labelTotal">Total to Pay</div>
|
||||||
<div class="grand-total-amount" id="displayTotal">OMR 0.000</div>
|
<div class="grand-total-amount" id="displayTotal">OMR 0.000</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -283,87 +283,40 @@ if (empty($slides)) {
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
let lastTimestamp = 0;
|
let lastTimestamp = 0;
|
||||||
|
let currentCurrency = 'OMR';
|
||||||
|
|
||||||
function formatMoney(amount) {
|
function formatMoney(amount) {
|
||||||
return 'OMR ' + parseFloat(amount).toFixed(3);
|
return currentCurrency + ' ' + parseFloat(amount).toFixed(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateDisplay(data) {
|
function updateDisplay(data) {
|
||||||
if (!data) return;
|
if (!data) return;
|
||||||
lastTimestamp = data.timestamp || 0;
|
lastTimestamp = data.timestamp || 0;
|
||||||
|
currentCurrency = data.currency || 'OMR';
|
||||||
|
|
||||||
|
// Labels translation if Arabic detected in items
|
||||||
|
const isAr = data.items && data.items.some(i => /[\u0600-\u06FF]/.test(i.name));
|
||||||
|
if (isAr) {
|
||||||
|
document.getElementById('labelSubtotal').innerText = 'المجموع (بدون الضريبة) / Subtotal';
|
||||||
|
document.getElementById('labelVAT').innerText = 'الضريبة / VAT';
|
||||||
|
document.getElementById('labelDiscount').innerText = 'الخصم / Discount';
|
||||||
|
document.getElementById('labelLoyalty').innerText = 'الولاء / Loyalty';
|
||||||
|
document.getElementById('labelTotal').innerText = 'الإجمالي / Total';
|
||||||
|
} else {
|
||||||
|
document.getElementById('labelSubtotal').innerText = 'Subtotal';
|
||||||
|
document.getElementById('labelVAT').innerText = 'VAT';
|
||||||
|
document.getElementById('labelDiscount').innerText = 'Discount';
|
||||||
|
document.getElementById('labelLoyalty').innerText = 'Loyalty';
|
||||||
|
document.getElementById('labelTotal').innerText = 'Total to Pay';
|
||||||
|
}
|
||||||
|
|
||||||
// Debug info
|
// Debug info
|
||||||
const debugEl = document.getElementById('debugInfo');
|
...
|
||||||
if (debugEl) {
|
|
||||||
debugEl.innerText = 'Items: ' + (data.items ? data.items.length : 0);
|
|
||||||
// debugEl.style.display = 'block'; // Uncomment to show always
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update Theme
|
|
||||||
if (data.theme) {
|
|
||||||
document.body.className = data.theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Array.isArray(data.items) || data.items.length === 0) {
|
|
||||||
document.getElementById('activeCart').style.display = 'none';
|
|
||||||
document.getElementById('welcomeScreen').style.display = 'flex';
|
|
||||||
document.getElementById('customerName').innerText = 'Welcome';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.getElementById('welcomeScreen').style.display = 'none';
|
|
||||||
document.getElementById('activeCart').style.display = 'flex';
|
|
||||||
|
|
||||||
// Update Customer Name
|
|
||||||
if (data.customerName && data.customerName !== 'Walk-in Customer' && data.customerName !== 'عميل عابر') {
|
|
||||||
document.getElementById('customerName').innerHTML = '<i class="bi bi-person me-1"></i> ' + data.customerName;
|
|
||||||
} else {
|
|
||||||
document.getElementById('customerName').innerText = 'Welcome';
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update Items (Safer rendering)
|
|
||||||
const itemsList = document.getElementById('itemsList');
|
|
||||||
if (itemsList) {
|
|
||||||
itemsList.innerHTML = ''; // Clear existing
|
|
||||||
|
|
||||||
data.items.forEach(item => {
|
|
||||||
try {
|
|
||||||
const row = document.createElement('div');
|
|
||||||
row.className = 'cart-item';
|
|
||||||
|
|
||||||
const name = item.name || 'Item';
|
|
||||||
const price = parseFloat(item.price) || 0;
|
|
||||||
const qty = parseFloat(item.qty) || 0;
|
|
||||||
const total = price * qty;
|
|
||||||
|
|
||||||
row.innerHTML = `
|
|
||||||
<div>
|
|
||||||
<div class="item-name"></div>
|
|
||||||
<div class="item-details">
|
|
||||||
${qty} x ${formatMoney(price)}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="item-price">
|
|
||||||
${formatMoney(total)}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
// Set text content safely
|
|
||||||
const nameEl = row.querySelector('.item-name');
|
|
||||||
if (nameEl) nameEl.textContent = name;
|
|
||||||
itemsList.appendChild(row);
|
|
||||||
} catch (err) {
|
|
||||||
console.error('Error rendering item:', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Scroll to bottom of list
|
|
||||||
requestAnimationFrame(() => {
|
|
||||||
itemsList.scrollTop = itemsList.scrollHeight;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update Totals
|
// Update Totals
|
||||||
document.getElementById('displaySubtotal').innerText = formatMoney(data.subtotal || 0);
|
const vat = parseFloat(data.vat) || 0;
|
||||||
|
const subtotal = parseFloat(data.subtotal) || 0;
|
||||||
|
document.getElementById('displaySubtotal').innerText = formatMoney(subtotal - vat);
|
||||||
|
document.getElementById('displayTax').innerText = formatMoney(vat);
|
||||||
|
|
||||||
const discount = parseFloat(data.discount) || 0;
|
const discount = parseFloat(data.discount) || 0;
|
||||||
if (discount > 0) {
|
if (discount > 0) {
|
||||||
|
|||||||
@ -71,6 +71,27 @@ $translations = [
|
|||||||
'low_stock_items' => 'Low Stock Items',
|
'low_stock_items' => 'Low Stock Items',
|
||||||
'expired_items' => 'Expired Items',
|
'expired_items' => 'Expired Items',
|
||||||
'near_expiry_items' => 'Near Expiry Items',
|
'near_expiry_items' => 'Near Expiry Items',
|
||||||
|
'tax_invoice' => 'Tax Invoice',
|
||||||
|
'invoice_no' => 'Invoice No',
|
||||||
|
'date' => 'Date',
|
||||||
|
'customer' => 'Customer',
|
||||||
|
'item' => 'Item',
|
||||||
|
'qty' => 'Qty',
|
||||||
|
'price' => 'Price',
|
||||||
|
'subtotal' => 'Subtotal',
|
||||||
|
'discount' => 'Discount',
|
||||||
|
'total' => 'Total',
|
||||||
|
'loyalty' => 'Loyalty',
|
||||||
|
'payments' => 'Payments',
|
||||||
|
'thank_you' => 'Thank you for your business!',
|
||||||
|
'keep_receipt' => 'Please keep the receipt.',
|
||||||
|
'tel' => 'Tel',
|
||||||
|
'vat_no' => 'VAT No',
|
||||||
|
'walk_in_customer' => 'Walk-in Customer',
|
||||||
|
'currency' => 'ر.ع / OMR',
|
||||||
|
'cash' => 'Cash',
|
||||||
|
'card' => 'Card',
|
||||||
|
'credit' => 'Credit',
|
||||||
],
|
],
|
||||||
'ar' => [
|
'ar' => [
|
||||||
'dashboard' => 'لوحة القيادة',
|
'dashboard' => 'لوحة القيادة',
|
||||||
@ -143,6 +164,27 @@ $translations = [
|
|||||||
'low_stock_items' => 'نواقص المخزون',
|
'low_stock_items' => 'نواقص المخزون',
|
||||||
'expired_items' => 'أصناف منتهية الصلاحية',
|
'expired_items' => 'أصناف منتهية الصلاحية',
|
||||||
'near_expiry_items' => 'أصناف قريبة الانتهاء',
|
'near_expiry_items' => 'أصناف قريبة الانتهاء',
|
||||||
|
'tax_invoice' => 'فاتورة ضريبية',
|
||||||
|
'invoice_no' => 'رقم الفاتورة',
|
||||||
|
'date' => 'التاريخ',
|
||||||
|
'customer' => 'العميل',
|
||||||
|
'item' => 'البند',
|
||||||
|
'qty' => 'الكمية',
|
||||||
|
'price' => 'السعر',
|
||||||
|
'subtotal' => 'المجموع الفرعي',
|
||||||
|
'discount' => 'خصم',
|
||||||
|
'total' => 'الإجمالي',
|
||||||
|
'loyalty' => 'الولاء',
|
||||||
|
'payments' => 'المدفوعات',
|
||||||
|
'thank_you' => 'شكراً لتعاملكم معنا!',
|
||||||
|
'keep_receipt' => 'يرجى الاحتفاظ بالإيصال.',
|
||||||
|
'tel' => 'هاتف',
|
||||||
|
'vat_no' => 'الرقم الضريبي',
|
||||||
|
'walk_in_customer' => 'عميل نقدي',
|
||||||
|
'currency' => 'ر.ع / OMR',
|
||||||
|
'cash' => 'نقد',
|
||||||
|
'card' => 'بطاقة',
|
||||||
|
'credit' => 'آجل',
|
||||||
]
|
]
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
483
index.php
483
index.php
@ -485,7 +485,200 @@ function getPromotionalPrice($item) {
|
|||||||
return $price;
|
return $price;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- HR Handlers ---
|
// --- Inventory & Core Handlers ---
|
||||||
|
if (isset($_POST['add_item'])) {
|
||||||
|
$name_en = $_POST['name_en'] ?? '';
|
||||||
|
$name_ar = $_POST['name_ar'] ?? '';
|
||||||
|
$category_id = (int)$_POST['category_id'] ?: null;
|
||||||
|
$unit_id = (int)$_POST['unit_id'] ?: null;
|
||||||
|
$supplier_id = (int)$_POST['supplier_id'] ?: null;
|
||||||
|
$sku = $_POST['sku'] ?? '';
|
||||||
|
$sale_price = (float)($_POST['sale_price'] ?? 0);
|
||||||
|
$purchase_price = (float)($_POST['purchase_price'] ?? 0);
|
||||||
|
$stock_quantity = (float)($_POST['stock_quantity'] ?? 0);
|
||||||
|
$min_stock_level = (float)($_POST['min_stock_level'] ?? 0);
|
||||||
|
$vat_rate = (float)($_POST['vat_rate'] ?? 15.00);
|
||||||
|
$expiry_date = !empty($_POST['expiry_date']) ? $_POST['expiry_date'] : null;
|
||||||
|
$is_promotion = isset($_POST['is_promotion']) ? 1 : 0;
|
||||||
|
$promotion_start = !empty($_POST['promotion_start']) ? $_POST['promotion_start'] : null;
|
||||||
|
$promotion_end = !empty($_POST['promotion_end']) ? $_POST['promotion_end'] : null;
|
||||||
|
$promotion_percent = (float)($_POST['promotion_percent'] ?? 0);
|
||||||
|
|
||||||
|
$image_path = null;
|
||||||
|
if (isset($_FILES['image']) && $_FILES['image']['error'] === 0) {
|
||||||
|
$ext = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
|
||||||
|
$filename = 'uploads/item_' . time() . '.' . $ext;
|
||||||
|
if (!is_dir('uploads')) mkdir('uploads', 0777, true);
|
||||||
|
if (move_uploaded_file($_FILES['image']['tmp_name'], $filename)) $image_path = $filename;
|
||||||
|
}
|
||||||
|
$stmt = db()->prepare("INSERT INTO stock_items (name_en, name_ar, category_id, unit_id, supplier_id, sku, sale_price, purchase_price, stock_quantity, min_stock_level, image_path, vat_rate, expiry_date, is_promotion, promotion_start, promotion_end, promotion_percent) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$name_en, $name_ar, $category_id, $unit_id, $supplier_id, $sku, $sale_price, $purchase_price, $stock_quantity, $min_stock_level, $image_path, $vat_rate, $expiry_date, $is_promotion, $promotion_start, $promotion_end, $promotion_percent]);
|
||||||
|
$message = "Item added successfully!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['edit_item'])) {
|
||||||
|
$id = (int)$_POST['id'];
|
||||||
|
$name_en = $_POST['name_en'] ?? '';
|
||||||
|
$name_ar = $_POST['name_ar'] ?? '';
|
||||||
|
$category_id = (int)$_POST['category_id'] ?: null;
|
||||||
|
$unit_id = (int)$_POST['unit_id'] ?: null;
|
||||||
|
$supplier_id = (int)$_POST['supplier_id'] ?: null;
|
||||||
|
$sku = $_POST['sku'] ?? '';
|
||||||
|
$sale_price = (float)($_POST['sale_price'] ?? 0);
|
||||||
|
$purchase_price = (float)($_POST['purchase_price'] ?? 0);
|
||||||
|
$stock_quantity = (float)($_POST['stock_quantity'] ?? 0);
|
||||||
|
$min_stock_level = (float)($_POST['min_stock_level'] ?? 0);
|
||||||
|
$vat_rate = (float)($_POST['vat_rate'] ?? 15.00);
|
||||||
|
$expiry_date = !empty($_POST['expiry_date']) ? $_POST['expiry_date'] : null;
|
||||||
|
$is_promotion = isset($_POST['is_promotion']) ? 1 : 0;
|
||||||
|
$promotion_start = !empty($_POST['promotion_start']) ? $_POST['promotion_start'] : null;
|
||||||
|
$promotion_end = !empty($_POST['promotion_end']) ? $_POST['promotion_end'] : null;
|
||||||
|
$promotion_percent = (float)($_POST['promotion_percent'] ?? 0);
|
||||||
|
$stmt = db()->prepare("UPDATE stock_items SET name_en = ?, name_ar = ?, category_id = ?, unit_id = ?, supplier_id = ?, sku = ?, sale_price = ?, purchase_price = ?, stock_quantity = ?, min_stock_level = ?, vat_rate = ?, expiry_date = ?, is_promotion = ?, promotion_start = ?, promotion_end = ?, promotion_percent = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$name_en, $name_ar, $category_id, $unit_id, $supplier_id, $sku, $sale_price, $purchase_price, $stock_quantity, $min_stock_level, $vat_rate, $expiry_date, $is_promotion, $promotion_start, $promotion_end, $promotion_percent, $id]);
|
||||||
|
if (isset($_FILES['image']) && $_FILES['image']['error'] === 0) {
|
||||||
|
$ext = pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION);
|
||||||
|
$filename = 'uploads/item_' . $id . '_' . time() . '.' . $ext;
|
||||||
|
if (move_uploaded_file($_FILES['image']['tmp_name'], $filename)) db()->prepare("UPDATE stock_items SET image_path = ? WHERE id = ?")->execute([$filename, $id]);
|
||||||
|
}
|
||||||
|
$message = "Item updated successfully!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['delete_item'])) {
|
||||||
|
db()->prepare("DELETE FROM stock_items WHERE id = ?")->execute([(int)$_POST['id']]);
|
||||||
|
$message = "Item deleted successfully!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['add_category'])) {
|
||||||
|
db()->prepare("INSERT INTO stock_categories (name_en, name_ar) VALUES (?, ?)")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '']);
|
||||||
|
$message = "Category added!";
|
||||||
|
}
|
||||||
|
if (isset($_POST['add_unit'])) {
|
||||||
|
db()->prepare("INSERT INTO stock_units (name_en, name_ar, short_name_en, short_name_ar) VALUES (?, ?, ?, ?)")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '', $_POST['short_en'] ?? '', $_POST['short_ar'] ?? '']);
|
||||||
|
$message = "Unit added!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['add_customer'])) {
|
||||||
|
db()->prepare("INSERT INTO customers (name, email, phone, tax_id, balance, type) VALUES (?, ?, ?, ?, ?, ?)")->execute([$_POST['name'] ?? '', $_POST['email'] ?? '', $_POST['phone'] ?? '', $_POST['tax_id'] ?? '', (float)($_POST['balance'] ?? 0), $_POST['type'] ?? 'customer']);
|
||||||
|
$message = "Entity added!";
|
||||||
|
}
|
||||||
|
if (isset($_POST['edit_customer'])) {
|
||||||
|
db()->prepare("UPDATE customers SET name = ?, email = ?, phone = ?, tax_id = ?, balance = ? WHERE id = ?")->execute([$_POST['name'] ?? '', $_POST['email'] ?? '', $_POST['phone'] ?? '', $_POST['tax_id'] ?? '', (float)($_POST['balance'] ?? 0), (int)$_POST['id']]);
|
||||||
|
$message = "Entity updated!";
|
||||||
|
}
|
||||||
|
if (isset($_POST['delete_customer'])) {
|
||||||
|
db()->prepare("DELETE FROM customers WHERE id = ?")->execute([(int)$_POST['id']]);
|
||||||
|
$message = "Entity deleted!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Invoices
|
||||||
|
if (isset($_POST['add_invoice'])) {
|
||||||
|
$db = db();
|
||||||
|
try {
|
||||||
|
$db->beginTransaction();
|
||||||
|
$type = $_POST['type'] ?? 'sale';
|
||||||
|
$cust_id = (int)$_POST['customer_id'];
|
||||||
|
$inv_date = $_POST['invoice_date'] ?: date('Y-m-d');
|
||||||
|
$status = $_POST['status'] ?? 'pending';
|
||||||
|
$pay_type = $_POST['payment_type'] ?? 'cash';
|
||||||
|
$total_vat = (float)($_POST['total_vat'] ?? 0);
|
||||||
|
$total_with_vat = (float)($_POST['total_with_vat'] ?? 0);
|
||||||
|
$paid = (float)($_POST['paid_amount'] ?? 0);
|
||||||
|
|
||||||
|
$stmt = $db->prepare("INSERT INTO invoices (customer_id, type, invoice_date, status, payment_type, total_vat, total_with_vat, paid_amount) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
|
$stmt->execute([$cust_id, $type, $inv_date, $status, $pay_type, $total_vat, $total_with_vat, $paid]);
|
||||||
|
$inv_id = $db->lastInsertId();
|
||||||
|
|
||||||
|
$items = $_POST['items'] ?? [];
|
||||||
|
$qtys = $_POST['qtys'] ?? [];
|
||||||
|
$prices = $_POST['prices'] ?? [];
|
||||||
|
$vats = $_POST['vats'] ?? [];
|
||||||
|
|
||||||
|
$items_for_journal = [];
|
||||||
|
foreach ($items as $i => $item_id) {
|
||||||
|
if (!$item_id) continue;
|
||||||
|
$qty = (float)$qtys[$i];
|
||||||
|
$price = (float)$prices[$i];
|
||||||
|
$vat = (float)($vats[$i] ?? 0);
|
||||||
|
$subtotal = $qty * $price;
|
||||||
|
$db->prepare("INSERT INTO invoice_items (invoice_id, item_id, quantity, unit_price, vat_amount, subtotal) VALUES (?, ?, ?, ?, ?, ?)")->execute([$inv_id, $item_id, $qty, $price, $vat, $subtotal]);
|
||||||
|
$change = ($type === 'sale') ? -$qty : $qty;
|
||||||
|
$db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity + ? WHERE id = ?")->execute([$change, $item_id]);
|
||||||
|
$items_for_journal[] = ['id' => $item_id, 'qty' => $qty];
|
||||||
|
}
|
||||||
|
if ($type === 'sale') recordSaleJournal($inv_id, $total_with_vat, $inv_date, $items_for_journal, $total_vat);
|
||||||
|
else recordPurchaseJournal($inv_id, $total_with_vat, $inv_date, $items_for_journal, $total_vat);
|
||||||
|
$db->commit();
|
||||||
|
$message = "Invoice #$inv_id created!";
|
||||||
|
} catch (Exception $e) { $db->rollBack(); $message = "Error: " . $e->getMessage(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['record_payment'])) {
|
||||||
|
$inv_id = (int)$_POST['invoice_id'];
|
||||||
|
$amount = (float)$_POST['amount'];
|
||||||
|
$date = $_POST['payment_date'] ?: date('Y-m-d');
|
||||||
|
$method = $_POST['payment_method'] ?? 'Cash';
|
||||||
|
$db = db();
|
||||||
|
$db->prepare("INSERT INTO payments (invoice_id, amount, payment_date, payment_method, notes) VALUES (?, ?, ?, ?, ?)")->execute([$inv_id, $amount, $date, $method, $_POST['notes'] ?? '']);
|
||||||
|
$pay_id = $db->lastInsertId();
|
||||||
|
$db->prepare("UPDATE invoices SET paid_amount = paid_amount + ?, status = IF(paid_amount + ? >= total_with_vat, 'paid', 'partially_paid') WHERE id = ?")->execute([$amount, $amount, $inv_id]);
|
||||||
|
$inv = $db->query("SELECT type FROM invoices WHERE id = $inv_id")->fetch();
|
||||||
|
if ($inv['type'] === 'sale') recordPaymentReceivedJournal($pay_id, $amount, $date, $method);
|
||||||
|
else recordPaymentMadeJournal($pay_id, $amount, $date, $method);
|
||||||
|
$message = "Payment recorded!";
|
||||||
|
$_SESSION['trigger_receipt_modal'] = true; $_SESSION['show_receipt_id'] = $pay_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['add_expense'])) {
|
||||||
|
$amt = (float)$_POST['amount'];
|
||||||
|
$date = $_POST['expense_date'] ?: date('Y-m-d');
|
||||||
|
$desc = $_POST['description'] ?? '';
|
||||||
|
db()->prepare("INSERT INTO expenses (category_id, amount, expense_date, reference_no, description) VALUES (?, ?, ?, ?, ?)")->execute([(int)$_POST['category_id'], $amt, $date, $_POST['reference_no'] ?? '', $desc]);
|
||||||
|
recordExpenseJournal(db()->lastInsertId(), $amt, $date, $desc);
|
||||||
|
$message = "Expense recorded!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['import_items'])) {
|
||||||
|
if (isset($_FILES['excel_file']) && $_FILES['excel_file']['error'] === 0) {
|
||||||
|
$handle = fopen($_FILES['excel_file']['tmp_name'], "r");
|
||||||
|
fgetcsv($handle); $count = 0;
|
||||||
|
while (($data = fgetcsv($handle)) !== FALSE) {
|
||||||
|
if ($data[1] || $data[2]) {
|
||||||
|
db()->prepare("INSERT INTO stock_items (sku, name_en, name_ar, sale_price, purchase_price) VALUES (?, ?, ?, ?, ?)")->execute([$data[0] ?? '', $data[1] ?? '', $data[2] ?? '', (float)($data[3] ?? 0), (float)($data[4] ?? 0)]);
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fclose($handle); $message = "Imported $count items!";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['add_expense_category'])) {
|
||||||
|
db()->prepare("INSERT INTO expense_categories (name_en, name_ar) VALUES (?, ?)")->execute([$_POST['name_en'] ?? '', $_POST['name_ar'] ?? '']);
|
||||||
|
$message = "Expense category added!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['add_payment_method'])) {
|
||||||
|
db()->prepare("INSERT INTO payment_methods (name, type) VALUES (?, ?)")->execute([$_POST['name'] ?? '', $_POST['type'] ?? 'Cash']);
|
||||||
|
$message = "Payment method added!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['delete_invoice'])) {
|
||||||
|
$id = (int)$_POST['id'];
|
||||||
|
db()->prepare("DELETE FROM invoices WHERE id = ?")->execute([$id]);
|
||||||
|
db()->prepare("DELETE FROM invoice_items WHERE invoice_id = ?")->execute([$id]);
|
||||||
|
$message = "Invoice deleted!";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($_POST['edit_invoice'])) {
|
||||||
|
$id = (int)$_POST['id'];
|
||||||
|
$cust_id = (int)$_POST['customer_id'];
|
||||||
|
$date = $_POST['invoice_date'] ?: date('Y-m-d');
|
||||||
|
$status = $_POST['status'] ?? 'pending';
|
||||||
|
db()->prepare("UPDATE invoices SET customer_id = ?, invoice_date = ?, status = ? WHERE id = ?")->execute([$cust_id, $date, $status, $id]);
|
||||||
|
$message = "Invoice updated!";
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- HR Handlers ---
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
error_log("POST Request detected. Action: " . (print_r($_POST, true)));
|
error_log("POST Request detected. Action: " . (print_r($_POST, true)));
|
||||||
}
|
}
|
||||||
@ -3037,7 +3230,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
</div>
|
</div>
|
||||||
<div class="product-grid" id="productGrid">
|
<div class="product-grid" id="productGrid">
|
||||||
<?php foreach ($products as $p): ?>
|
<?php foreach ($products as $p): ?>
|
||||||
<div class="product-card" data-id="<?= $p['id'] ?>" data-name-en="<?= htmlspecialchars($p['name_en']) ?>" data-name-ar="<?= htmlspecialchars($p['name_ar']) ?>" data-price="<?= $p['sale_price'] ?>" data-sku="<?= htmlspecialchars($p['sku']) ?>" data-stock-quantity="<?= (float)$p['stock_quantity'] ?>">
|
<div class="product-card" data-id="<?= $p['id'] ?>" data-name-en="<?= htmlspecialchars($p['name_en']) ?>" data-name-ar="<?= htmlspecialchars($p['name_ar']) ?>" data-price="<?= $p['sale_price'] ?>" data-sku="<?= htmlspecialchars($p['sku']) ?>" data-stock-quantity="<?= (float)$p['stock_quantity'] ?>" data-vat-rate="<?= $p['vat_rate'] ?>">
|
||||||
<?php if ($p['image_path']): ?>
|
<?php if ($p['image_path']): ?>
|
||||||
<img src="<?= htmlspecialchars($p['image_path']) ?>" alt="<?= htmlspecialchars($p['name_en']) ?>">
|
<img src="<?= htmlspecialchars($p['image_path']) ?>" alt="<?= htmlspecialchars($p['name_en']) ?>">
|
||||||
<?php else: ?>
|
<?php else: ?>
|
||||||
@ -3130,12 +3323,16 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
|
|
||||||
<div class="cart-total">
|
<div class="cart-total">
|
||||||
<div class="d-flex justify-content-between mb-1">
|
<div class="d-flex justify-content-between mb-1">
|
||||||
<span>Subtotal</span>
|
<span data-en="Subtotal (Excl. VAT)" data-ar="المجموع (بدون الضريبة)">Subtotal (Excl. VAT)</span>
|
||||||
<span id="posSubtotal">OMR 0.000</span>
|
<span id="posSubtotal"><?= __('currency') ?> 0.000</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between mb-3 fw-bold fs-5">
|
<div class="d-flex justify-content-between mb-1">
|
||||||
<span>Total</span>
|
<span data-en="VAT" data-ar="الضريبة">VAT</span>
|
||||||
<span id="posTotal" class="text-primary">OMR 0.000</span>
|
<span id="posVat"><?= __('currency') ?> 0.000</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between mb-3 fw-bold fs-5 border-top pt-2">
|
||||||
|
<span data-en="Total" data-ar="الإجمالي">Total</span>
|
||||||
|
<span id="posTotal" class="text-primary"><?= __('currency') ?> 0.000</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-primary w-100 py-2 fw-bold" id="checkoutBtn" onclick="cart.checkout()">
|
<button class="btn btn-primary w-100 py-2 fw-bold" id="checkoutBtn" onclick="cart.checkout()">
|
||||||
PLACE ORDER
|
PLACE ORDER
|
||||||
@ -3172,6 +3369,12 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
if (!Array.isArray(this.items)) this.items = [];
|
if (!Array.isArray(this.items)) this.items = [];
|
||||||
|
|
||||||
const subtotal = this.items.reduce((sum, item) => sum + (parseFloat(item.price) * parseFloat(item.qty)), 0);
|
const subtotal = this.items.reduce((sum, item) => sum + (parseFloat(item.price) * parseFloat(item.qty)), 0);
|
||||||
|
const totalVat = this.items.reduce((sum, item) => {
|
||||||
|
const price = parseFloat(item.price) || 0;
|
||||||
|
const qty = parseFloat(item.qty) || 0;
|
||||||
|
const vatRate = item.vatRate || 5;
|
||||||
|
return sum + (price * qty * (vatRate / (100 + vatRate)));
|
||||||
|
}, 0);
|
||||||
let discountAmount = 0;
|
let discountAmount = 0;
|
||||||
if (this.discount) {
|
if (this.discount) {
|
||||||
discountAmount = this.discount.type === 'percentage' ? subtotal * (parseFloat(this.discount.value) / 100) : parseFloat(this.discount.value);
|
discountAmount = this.discount.type === 'percentage' ? subtotal * (parseFloat(this.discount.value) / 100) : parseFloat(this.discount.value);
|
||||||
@ -3185,15 +3388,24 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
const customerName = customerSelect ? customerSelect.options[customerSelect.selectedIndex].text : '';
|
const customerName = customerSelect ? customerSelect.options[customerSelect.selectedIndex].text : '';
|
||||||
|
|
||||||
const payload = {
|
const payload = {
|
||||||
items: this.items.map(i => ({
|
items: this.items.map(i => {
|
||||||
name: (document.documentElement.lang === 'ar' ? (i.nameAr || i.nameEn) : (i.nameEn || i.nameAr)) || 'Unknown Item',
|
const price = parseFloat(i.price) || 0;
|
||||||
price: parseFloat(i.price) || 0,
|
const qty = parseFloat(i.qty) || 0;
|
||||||
qty: parseFloat(i.qty) || 0
|
const vatRate = i.vatRate || 5;
|
||||||
})),
|
const vatAmount = price * qty * (vatRate / (100 + vatRate));
|
||||||
|
return {
|
||||||
|
name: (document.documentElement.lang === 'ar' ? (i.nameAr || i.nameEn) : (i.nameEn || i.nameAr)) || 'Unknown Item',
|
||||||
|
price: price,
|
||||||
|
qty: qty,
|
||||||
|
vat: vatAmount
|
||||||
|
};
|
||||||
|
}),
|
||||||
subtotal: parseFloat(subtotal) || 0,
|
subtotal: parseFloat(subtotal) || 0,
|
||||||
|
vat: parseFloat(totalVat) || 0,
|
||||||
discount: parseFloat(discountAmount) || 0,
|
discount: parseFloat(discountAmount) || 0,
|
||||||
loyalty: parseFloat(loyaltyRedeemed) || 0,
|
loyalty: parseFloat(loyaltyRedeemed) || 0,
|
||||||
total: parseFloat(total) || 0,
|
total: parseFloat(total) || 0,
|
||||||
|
currency: "<?= __('currency') ?>",
|
||||||
customerName: customerName,
|
customerName: customerName,
|
||||||
theme: document.body.className,
|
theme: document.body.className,
|
||||||
timestamp: Date.now()
|
timestamp: Date.now()
|
||||||
@ -3450,32 +3662,40 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
const totalEl = document.getElementById('posTotal');
|
const totalEl = document.getElementById('posTotal');
|
||||||
const checkoutBtn = document.getElementById('checkoutBtn');
|
const checkoutBtn = document.getElementById('checkoutBtn');
|
||||||
|
|
||||||
if (subtotalEl) subtotalEl.innerText = 'OMR 0.000';
|
if (subtotalEl) subtotalEl.innerText = '<?= __('currency') ?> 0.000';
|
||||||
if (totalEl) totalEl.innerText = 'OMR 0.000';
|
if (totalEl) totalEl.innerText = '<?= __('currency') ?> 0.000';
|
||||||
if (checkoutBtn) checkoutBtn.disabled = true;
|
if (checkoutBtn) checkoutBtn.disabled = true;
|
||||||
this.broadcast();
|
this.broadcast();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let subtotal = 0;
|
let subtotal = 0;
|
||||||
|
let totalVat = 0;
|
||||||
container.innerHTML = items.map(item => {
|
container.innerHTML = items.map(item => {
|
||||||
const price = parseFloat(item.price) || 0;
|
const price = parseFloat(item.price) || 0;
|
||||||
const qty = parseFloat(item.qty) || 0;
|
const qty = parseFloat(item.qty) || 0;
|
||||||
subtotal += price * qty;
|
const itemTotal = price * qty;
|
||||||
|
subtotal += itemTotal;
|
||||||
|
const vatRate = item.vatRate || 5;
|
||||||
|
const itemVat = itemTotal * (vatRate / (100 + vatRate));
|
||||||
|
totalVat += itemVat;
|
||||||
const displayName = (lang === 'ar' ? (item.nameAr || item.nameEn) : (item.nameEn || item.nameAr)) || 'Unknown Item';
|
const displayName = (lang === 'ar' ? (item.nameAr || item.nameEn) : (item.nameEn || item.nameAr)) || 'Unknown Item';
|
||||||
|
|
||||||
return `
|
return `
|
||||||
<div class="cart-item">
|
<div class="cart-item">
|
||||||
<div class="flex-grow-1">
|
<div class="flex-grow-1">
|
||||||
<div class="fw-bold small">${displayName}</div>
|
<div class="fw-bold small">${displayName}</div>
|
||||||
<div class="text-muted smaller">OMR ${price.toFixed(3)}</div>
|
<div class="text-muted smaller"><?= __('currency') ?> ${price.toFixed(3)} <span class="badge bg-light text-dark smaller">VAT ${vatRate}%</span></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="qty-controls mx-3">
|
<div class="qty-controls mx-3">
|
||||||
<button class="qty-btn" onclick="cart.updateQty(${item.id}, -1)">-</button>
|
<button class="qty-btn" onclick="cart.updateQty(${item.id}, -1)">-</button>
|
||||||
<span class="small fw-bold">${qty}</span>
|
<span class="small fw-bold">${qty}</span>
|
||||||
<button class="qty-btn" onclick="cart.updateQty(${item.id}, 1)">+</button>
|
<button class="qty-btn" onclick="cart.updateQty(${item.id}, 1)">+</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="fw-bold small">OMR ${(price * qty).toFixed(3)}</div>
|
<div class="text-end" style="min-width: 80px;">
|
||||||
|
<div class="fw-bold small"><?= __('currency') ?> ${itemTotal.toFixed(3)}</div>
|
||||||
|
<div class="smaller text-muted">VAT: ${itemVat.toFixed(3)}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
}).join('');
|
}).join('');
|
||||||
@ -3503,18 +3723,21 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
const pointsToEarn = Math.floor(total * multiplier);
|
const pointsToEarn = Math.floor(total * multiplier);
|
||||||
|
|
||||||
const subtotalDisplay = document.getElementById('posSubtotal');
|
const subtotalDisplay = document.getElementById('posSubtotal');
|
||||||
if (subtotalDisplay) subtotalDisplay.innerText = 'OMR ' + subtotal.toFixed(3);
|
if (subtotalDisplay) subtotalDisplay.innerText = '<?= __('currency') ?> ' + (subtotal - totalVat).toFixed(3);
|
||||||
|
|
||||||
|
const vatDisplay = document.getElementById('posVat');
|
||||||
|
if (vatDisplay) vatDisplay.innerText = '<?= __('currency') ?> ' + totalVat.toFixed(3);
|
||||||
|
|
||||||
let totalHtml = '';
|
let totalHtml = '';
|
||||||
if (discountAmount > 0) totalHtml += `<div class="smaller text-danger">- Disc: OMR ${discountAmount.toFixed(3)}</div>`;
|
if (discountAmount > 0) totalHtml += `<div class="smaller text-danger">- Disc: <?= __('currency') ?> ${discountAmount.toFixed(3)}</div>`;
|
||||||
if (loyaltyRedeemedValue > 0) totalHtml += `<div class="smaller text-success">- Loyalty: OMR ${loyaltyRedeemedValue.toFixed(3)}</div>`;
|
if (loyaltyRedeemedValue > 0) totalHtml += `<div class="smaller text-success">- Loyalty: <?= __('currency') ?> ${loyaltyRedeemedValue.toFixed(3)}</div>`;
|
||||||
|
|
||||||
const customerId = document.getElementById('posCustomer') ? document.getElementById('posCustomer').value : '';
|
const customerId = document.getElementById('posCustomer') ? document.getElementById('posCustomer').value : '';
|
||||||
if (customerId) {
|
if (customerId) {
|
||||||
totalHtml += `<div class="smaller text-info">+ Earn: ${pointsToEarn} pts</div>`;
|
totalHtml += `<div class="smaller text-info">+ Earn: ${pointsToEarn} pts</div>`;
|
||||||
}
|
}
|
||||||
|
|
||||||
totalHtml += 'OMR ' + total.toFixed(3);
|
totalHtml += '<?= __('currency') ?> ' + total.toFixed(3);
|
||||||
|
|
||||||
const totalDisplay = document.getElementById('posTotal');
|
const totalDisplay = document.getElementById('posTotal');
|
||||||
if (totalDisplay) {
|
if (totalDisplay) {
|
||||||
@ -3546,7 +3769,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
|
|
||||||
this.payments = [];
|
this.payments = [];
|
||||||
this.renderPayments();
|
this.renderPayments();
|
||||||
document.getElementById('paymentAmountDue').innerText = 'OMR ' + total.toFixed(3);
|
document.getElementById('paymentAmountDue').innerText = '<?= __('currency') ?> ' + total.toFixed(3);
|
||||||
document.getElementById('partialAmount').value = total.toFixed(3);
|
document.getElementById('partialAmount').value = total.toFixed(3);
|
||||||
|
|
||||||
// Sync credit customer selection if credit is default or already selected
|
// Sync credit customer selection if credit is default or already selected
|
||||||
@ -3627,7 +3850,7 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
<div class="payment-line">
|
<div class="payment-line">
|
||||||
<div>
|
<div>
|
||||||
<span class="method">${p.method}</span>
|
<span class="method">${p.method}</span>
|
||||||
<span class="ms-2 badge bg-secondary small">OMR ${p.amount.toFixed(3)}</span>
|
<span class="ms-2 badge bg-secondary small"><?= __('currency') ?> ${p.amount.toFixed(3)}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn btn-sm btn-outline-danger border-0" onclick="cart.removePaymentLine(${i})">
|
<button class="btn btn-sm btn-outline-danger border-0" onclick="cart.removePaymentLine(${i})">
|
||||||
<i class="bi bi-trash"></i>
|
<i class="bi bi-trash"></i>
|
||||||
@ -3757,53 +3980,76 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
showReceipt(invId, discountAmount, loyaltyRedeemed, transactionNo) {
|
showReceipt(invId, discountAmount, loyaltyRedeemed, transactionNo) {
|
||||||
const container = document.getElementById('posReceiptContent');
|
const container = document.getElementById('posReceiptContent');
|
||||||
const customerSelect = document.getElementById('posCustomer');
|
const customerSelect = document.getElementById('posCustomer');
|
||||||
const customerName = (customerSelect && customerSelect.selectedIndex >= 0) ? customerSelect.options[customerSelect.selectedIndex].text : 'Walk-in Customer';
|
const customerName = (customerSelect && customerSelect.selectedIndex >= 0 && customerSelect.value !== '') ? customerSelect.options[customerSelect.selectedIndex].text : '<?= $translations['ar']['walk_in_customer'] ?> / <?= $translations['en']['walk_in_customer'] ?>';
|
||||||
const paymentsHtml = this.payments.map(p => `
|
const paymentsHtml = this.payments.map(p => {
|
||||||
|
let m = p.method.toLowerCase();
|
||||||
|
let methodAr = m === 'cash' ? 'نقد' : (m === 'card' ? 'بطاقة' : (m === 'credit' ? 'آجل' : m));
|
||||||
|
let methodEn = m.charAt(0).toUpperCase() + m.slice(1);
|
||||||
|
return `
|
||||||
<div class="d-flex justify-content-between small">
|
<div class="d-flex justify-content-between small">
|
||||||
<span class="text-uppercase">${p.method}</span>
|
<span class="text-uppercase">${methodAr} / ${methodEn}</span>
|
||||||
<span>OMR ${p.amount.toFixed(3)}</span>
|
<span><?= __('currency') ?> ${p.amount.toFixed(3)}</span>
|
||||||
</div>
|
</div>
|
||||||
`).join('');
|
`;
|
||||||
|
}).join('');
|
||||||
const date = new Date().toLocaleString();
|
const date = new Date().toLocaleString();
|
||||||
|
|
||||||
let itemsHtml = this.items.map(item => `
|
let itemsHtml = this.items.map(item => {
|
||||||
|
const itemTotal = item.price * item.qty;
|
||||||
|
const vatRate = item.vatRate || 5; // Default to 5 if not set
|
||||||
|
const vatAmount = itemTotal * (vatRate / (100 + vatRate));
|
||||||
|
const exclVat = itemTotal - vatAmount;
|
||||||
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${item.nameEn}<br><small>${item.qty} x ${parseFloat(item.price).toFixed(3)}</small></td>
|
<td>
|
||||||
<td style="text-align: right; vertical-align: bottom;">${(item.price * item.qty).toFixed(3)}</td>
|
<div class="fw-bold">${item.nameAr || ''}</div>
|
||||||
|
<div>${item.nameEn}</div>
|
||||||
|
<small>${item.qty} x ${parseFloat(item.price).toFixed(3)}</small>
|
||||||
|
</td>
|
||||||
|
<td style="text-align: right; vertical-align: bottom;">${vatAmount.toFixed(3)}</td>
|
||||||
|
<td style="text-align: right; vertical-align: bottom;">${itemTotal.toFixed(3)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`).join('');
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
const subtotal = this.items.reduce((sum, item) => sum + (item.price * item.qty), 0);
|
const subtotal = this.items.reduce((sum, item) => sum + (item.price * item.qty), 0);
|
||||||
|
const totalVat = this.items.reduce((sum, item) => {
|
||||||
|
const vatRate = item.vatRate || 5;
|
||||||
|
return sum + ((item.price * item.qty) * (vatRate / (100 + vatRate)));
|
||||||
|
}, 0);
|
||||||
const total = subtotal - discountAmount - loyaltyRedeemed;
|
const total = subtotal - discountAmount - loyaltyRedeemed;
|
||||||
const companyName = "<?= htmlspecialchars($data['settings']['company_name'] ?? 'Accounting System') ?>";
|
const companyName = "<?= htmlspecialchars($data['settings']['company_name'] ?? 'Accounting System') ?>";
|
||||||
const companyPhone = "<?= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>";
|
const companyPhone = "<?= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>";
|
||||||
const companyVat = "<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>";
|
const companyVat = "<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>";
|
||||||
|
const companyLogo = "<?= htmlspecialchars($data['settings']['company_logo'] ?? '') ?>";
|
||||||
|
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<div class="thermal-receipt">
|
<div class="thermal-receipt <?= $lang === 'ar' ? 'rtl' : '' ?>">
|
||||||
<div class="center">
|
<div class="center">
|
||||||
|
${companyLogo ? `<img src="${companyLogo}" alt="Logo" style="max-height: 60px; width: auto; margin-bottom: 10px; display: block; margin-left: auto; margin-right: auto;">` : ''}
|
||||||
<h5 class="mb-0 fw-bold">${companyName}</h5>
|
<h5 class="mb-0 fw-bold">${companyName}</h5>
|
||||||
${companyPhone ? `<div>Tel: ${companyPhone}</div>` : ''}
|
${companyPhone ? `<div>هاتف / Tel: ${companyPhone}</div>` : ''}
|
||||||
${companyVat ? `<div>VAT: ${companyVat}</div>` : ''}
|
${companyVat ? `<div>الرقم الضريبي / VAT No: ${companyVat}</div>` : ''}
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<h6 class="fw-bold">TAX INVOICE</h6>
|
<h6 class="fw-bold text-uppercase">فاتورة ضريبية / TAX INVOICE</h6>
|
||||||
<div>Inv: ${transactionNo || 'POS-'+invId}</div>
|
<div>رقم الفاتورة / Invoice No: ${transactionNo || 'POS-'+invId}</div>
|
||||||
<div>Date: ${date}</div>
|
<div>التاريخ / Date: ${date}</div>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Customer:</strong> ${customerName}
|
<strong>العميل / Customer:</strong> ${customerName}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-1">
|
<div class="mt-1">
|
||||||
<strong>Payments:</strong>
|
<strong>المدفوعات / Payments:</strong>
|
||||||
${paymentsHtml}
|
${paymentsHtml}
|
||||||
</div>
|
</div>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<table>
|
<table class="table-borderless">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ITEM</th>
|
<th>البند / Item</th>
|
||||||
<th style="text-align: right;">TOTAL</th>
|
<th style="text-align: right;">ضريبة / VAT</th>
|
||||||
|
<th style="text-align: right;">الإجمالي / Total</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@ -3812,20 +4058,28 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
</table>
|
</table>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<div class="d-flex justify-content-between">
|
<div class="d-flex justify-content-between">
|
||||||
<span>Subtotal</span>
|
<span>المجموع الفرعي (غير شامل الضريبة) / Subtotal (Excl. VAT)</span>
|
||||||
<span>OMR ${subtotal.toFixed(3)}</span>
|
<span><?= __('currency') ?> ${(subtotal - totalVat).toFixed(3)}</span>
|
||||||
</div>
|
</div>
|
||||||
${discountAmount > 0 ? `<div class="d-flex justify-content-between text-danger"><span>Discount</span><span>- OMR ${parseFloat(discountAmount).toFixed(3)}</span></div>` : ''}
|
<div class="d-flex justify-content-between">
|
||||||
${loyaltyRedeemed > 0 ? `<div class="d-flex justify-content-between text-success"><span>Loyalty</span><span>- OMR ${parseFloat(loyaltyRedeemed).toFixed(3)}</span></div>` : ''}
|
<span>الضريبة / VAT</span>
|
||||||
|
<span><?= __('currency') ?> ${totalVat.toFixed(3)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between fw-bold">
|
||||||
|
<span>المجموع شامل الضريبة / Total (Incl. VAT)</span>
|
||||||
|
<span><?= __('currency') ?> ${subtotal.toFixed(3)}</span>
|
||||||
|
</div>
|
||||||
|
${discountAmount > 0 ? `<div class="d-flex justify-content-between text-danger"><span>خصم / Discount</span><span>- <?= __('currency') ?> ${parseFloat(discountAmount).toFixed(3)}</span></div>` : ''}
|
||||||
|
${loyaltyRedeemed > 0 ? `<div class="d-flex justify-content-between text-success"><span>الولاء / Loyalty</span><span>- <?= __('currency') ?> ${parseFloat(loyaltyRedeemed).toFixed(3)}</span></div>` : ''}
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<div class="d-flex justify-content-between total-row">
|
<div class="d-flex justify-content-between total-row">
|
||||||
<span>TOTAL</span>
|
<span>الإجمالي / Total</span>
|
||||||
<span>OMR ${total.toFixed(3)}</span>
|
<span><?= __('currency') ?> ${total.toFixed(3)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<div class="center small">
|
<div class="center small">
|
||||||
Thank you for your business!<br>
|
شكراً لتعاملكم معنا! / Thank you for your business!<br>
|
||||||
Please keep the receipt.
|
يرجى الاحتفاظ بالإيصال. / Please keep the receipt.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
@ -3856,7 +4110,8 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
nameEn: card.dataset.nameEn,
|
nameEn: card.dataset.nameEn,
|
||||||
nameAr: card.dataset.nameAr,
|
nameAr: card.dataset.nameAr,
|
||||||
price: parseFloat(card.dataset.price),
|
price: parseFloat(card.dataset.price),
|
||||||
stock_quantity: parseFloat(card.dataset.stockQuantity)
|
stock_quantity: parseFloat(card.dataset.stockQuantity),
|
||||||
|
vatRate: parseFloat(card.dataset.vatRate) || 0
|
||||||
};
|
};
|
||||||
cart.add(product);
|
cart.add(product);
|
||||||
});
|
});
|
||||||
@ -3888,7 +4143,8 @@ $projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? 'Accounting System';
|
|||||||
nameAr: card.dataset.nameAr,
|
nameAr: card.dataset.nameAr,
|
||||||
price: parseFloat(card.dataset.price),
|
price: parseFloat(card.dataset.price),
|
||||||
sku: card.dataset.sku,
|
sku: card.dataset.sku,
|
||||||
stock_quantity: parseFloat(card.dataset.stockQuantity)
|
stock_quantity: parseFloat(card.dataset.stockQuantity),
|
||||||
|
vatRate: parseFloat(card.dataset.vatRate) || 0
|
||||||
};
|
};
|
||||||
cart.add(product);
|
cart.add(product);
|
||||||
e.target.value = '';
|
e.target.value = '';
|
||||||
@ -7645,6 +7901,13 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
const sku = Math.floor(100000000000 + Math.random() * 900000000000).toString();
|
const sku = Math.floor(100000000000 + Math.random() * 900000000000).toString();
|
||||||
skuInput.value = sku;
|
skuInput.value = sku;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
skuInput.addEventListener('keydown', function(e) {
|
||||||
|
if (e.key === 'Enter') {
|
||||||
|
e.preventDefault();
|
||||||
|
console.log("Barcode scan detected in SKU field, preventing form submission");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Toggle Expiry Date visibility
|
// Toggle Expiry Date visibility
|
||||||
@ -8516,7 +8779,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="4" class="text-end">Total Return Amount:</th>
|
<th colspan="4" class="text-end">Total Return Amount:</th>
|
||||||
<th class="text-end" id="purchase_return_total_display">OMR 0.000</th>
|
<th class="text-end" id="purchase_return_total_display"><?= __('currency') ?> 0.000</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
@ -8580,7 +8843,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<th colspan="4" class="text-end">Total Return Amount:</th>
|
<th colspan="4" class="text-end">Total Return Amount:</th>
|
||||||
<th class="text-end" id="return_total_display">OMR 0.000</th>
|
<th class="text-end" id="return_total_display"><?= __('currency') ?> 0.000</th>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
@ -8896,17 +9159,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
||||||
<td class="text-end fw-bold" id="subtotal">OMR 0.000</td>
|
<td class="text-end fw-bold" id="subtotal"><?= __('currency') ?> 0.000</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
||||||
<td class="text-end fw-bold" id="totalVat">OMR 0.000</td>
|
<td class="text-end fw-bold" id="totalVat"><?= __('currency') ?> 0.000</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="table-primary">
|
<tr class="table-primary">
|
||||||
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
||||||
<td class="text-end fw-bold h5" id="grandTotal">OMR 0.000</td>
|
<td class="text-end fw-bold h5" id="grandTotal"><?= __('currency') ?> 0.000</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
@ -8996,17 +9259,17 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
||||||
<td class="text-end fw-bold" id="edit_subtotal">OMR 0.000</td>
|
<td class="text-end fw-bold" id="edit_subtotal"><?= __('currency') ?> 0.000</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
||||||
<td class="text-end fw-bold" id="edit_totalVat">OMR 0.000</td>
|
<td class="text-end fw-bold" id="edit_totalVat"><?= __('currency') ?> 0.000</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="table-primary">
|
<tr class="table-primary">
|
||||||
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
||||||
<td class="text-end fw-bold h5" id="edit_grandTotal">OMR 0.000</td>
|
<td class="text-end fw-bold h5" id="edit_grandTotal"><?= __('currency') ?> 0.000</td>
|
||||||
<td></td>
|
<td></td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
@ -9078,15 +9341,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
||||||
<td class="text-end fw-bold" id="quot_subtotal_display">OMR 0.000</td>
|
<td class="text-end fw-bold" id="quot_subtotal_display"><?= __('currency') ?> 0.000</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
||||||
<td class="text-end fw-bold" id="quot_vat_display">OMR 0.000</td>
|
<td class="text-end fw-bold" id="quot_vat_display"><?= __('currency') ?> 0.000</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="table-primary">
|
<tr class="table-primary">
|
||||||
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
||||||
<td class="text-end fw-bold h5" id="quot_grand_display">OMR 0.000</td>
|
<td class="text-end fw-bold h5" id="quot_grand_display"><?= __('currency') ?> 0.000</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
@ -9167,15 +9430,15 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<tfoot>
|
<tfoot>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Subtotal" data-ar="الإجمالي الفرعي">Subtotal</td>
|
||||||
<td class="text-end fw-bold" id="edit_quot_subtotal_display">OMR 0.000</td>
|
<td class="text-end fw-bold" id="edit_quot_subtotal_display"><?= __('currency') ?> 0.000</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
<td colspan="4" class="text-end fw-bold" data-en="Total VAT" data-ar="إجمالي الضريبة">Total VAT</td>
|
||||||
<td class="text-end fw-bold" id="edit_quot_vat_display">OMR 0.000</td>
|
<td class="text-end fw-bold" id="edit_quot_vat_display"><?= __('currency') ?> 0.000</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr class="table-primary">
|
<tr class="table-primary">
|
||||||
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
<td colspan="4" class="text-end fw-bold h5" data-en="Grand Total" data-ar="الإجمالي النهائي">Grand Total</td>
|
||||||
<td class="text-end fw-bold h5" id="edit_quot_grand_display">OMR 0.000</td>
|
<td class="text-end fw-bold h5" id="edit_quot_grand_display"><?= __('currency') ?> 0.000</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tfoot>
|
</tfoot>
|
||||||
</table>
|
</table>
|
||||||
@ -9258,8 +9521,20 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
.badge { border: 1px solid #000; color: #000 !important; }
|
.badge { border: 1px solid #000; color: #000 !important; }
|
||||||
|
|
||||||
/* Ensure the modal is the only thing visible ONLY when a modal is open */
|
/* Ensure the modal is the only thing visible ONLY when a modal is open */
|
||||||
body.modal-open:not(.printing-receipt) > *:not(.modal):not(.swal2-container) { display: none !important; }
|
body.modal-open:not(.printing-receipt) { visibility: hidden !important; }
|
||||||
body.modal-open:not(.printing-receipt) .main-content { display: none !important; }
|
body.modal-open:not(.printing-receipt) .modal.show {
|
||||||
|
visibility: visible !important;
|
||||||
|
display: block !important;
|
||||||
|
position: absolute !important;
|
||||||
|
left: 0 !important;
|
||||||
|
top: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
body.modal-open:not(.printing-receipt) .modal.show * { visibility: visible !important; }
|
||||||
|
|
||||||
|
/* Old rules that caused blank pages for nested modals */
|
||||||
|
/* body.modal-open:not(.printing-receipt) > *:not(.modal):not(.swal2-container) { display: none !important; } */
|
||||||
|
/* body.modal-open:not(.printing-receipt) .main-content { display: none !important; } */
|
||||||
|
|
||||||
/* POS Receipt printing specific */
|
/* POS Receipt printing specific */
|
||||||
body.printing-receipt .modal { display: none !important; }
|
body.printing-receipt .modal { display: none !important; }
|
||||||
@ -9369,7 +9644,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-6">
|
<div class="col-6">
|
||||||
<div class="d-flex justify-content-between mb-2">
|
<div class="d-flex justify-content-between mb-2">
|
||||||
<span class="text-muted">Subtotal / المجموع الفرعي</span>
|
<span class="text-muted">Subtotal (Excl. VAT) / المجموع الفرعي (دون الضريبة)</span>
|
||||||
<span id="invSubtotal" class="fw-bold text-nowrap"></span>
|
<span id="invSubtotal" class="fw-bold text-nowrap"></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between mb-2">
|
<div class="d-flex justify-content-between mb-2">
|
||||||
@ -9495,6 +9770,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<div class="modal-body p-0" id="printableReceipt">
|
<div class="modal-body p-0" id="printableReceipt">
|
||||||
<div class="receipt-container p-4">
|
<div class="receipt-container p-4">
|
||||||
<div class="text-center mb-4">
|
<div class="text-center mb-4">
|
||||||
|
<?php
|
||||||
|
$logo = $data['settings']['company_logo'] ?? '';
|
||||||
|
if ($logo): ?>
|
||||||
|
<img src="<?= htmlspecialchars($logo) ?>" alt="Logo" class="invoice-logo mb-3">
|
||||||
|
<?php endif; ?>
|
||||||
<h3 class="mb-1 fw-bold"><?= htmlspecialchars($data['settings']['company_name'] ?? 'Accounting System') ?></h3>
|
<h3 class="mb-1 fw-bold"><?= htmlspecialchars($data['settings']['company_name'] ?? 'Accounting System') ?></h3>
|
||||||
<p class="text-muted small mb-0"><?= nl2br(htmlspecialchars($data['settings']['company_address'] ?? '')) ?></p>
|
<p class="text-muted small mb-0"><?= nl2br(htmlspecialchars($data['settings']['company_address'] ?? '')) ?></p>
|
||||||
<hr class="my-4">
|
<hr class="my-4">
|
||||||
@ -9583,11 +9863,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<div class="d-flex justify-content-between px-3">
|
<div class="d-flex justify-content-between px-3">
|
||||||
<div class="text-start">
|
<div class="text-start">
|
||||||
<div class="label">Amount Due</div>
|
<div class="label">Amount Due</div>
|
||||||
<div class="value" id="paymentAmountDue">OMR 0.000</div>
|
<div class="value" id="paymentAmountDue"><?= __('currency') ?> 0.000</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-end">
|
<div class="text-end">
|
||||||
<div class="label text-danger">Remaining</div>
|
<div class="label text-danger">Remaining</div>
|
||||||
<div class="value text-danger" id="paymentRemaining">OMR 0.000</div>
|
<div class="value text-danger" id="paymentRemaining"><?= __('currency') ?> 0.000</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -9640,7 +9920,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<div id="cashPaymentSection" style="display: none;">
|
<div id="cashPaymentSection" style="display: none;">
|
||||||
<div class="d-flex justify-content-between align-items-center p-3 bg-primary-subtle rounded border border-primary-subtle">
|
<div class="d-flex justify-content-between align-items-center p-3 bg-primary-subtle rounded border border-primary-subtle">
|
||||||
<span class="fw-bold">Total Tendered (Cash)</span>
|
<span class="fw-bold">Total Tendered (Cash)</span>
|
||||||
<span class="h4 m-0 fw-bold text-primary" id="changeDue">OMR 0.000</span>
|
<span class="h4 m-0 fw-bold text-primary" id="changeDue"><?= __('currency') ?> 0.000</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="small text-muted mt-1">* Change is calculated based on cash payments only.</div>
|
<div class="small text-muted mt-1">* Change is calculated based on cash payments only.</div>
|
||||||
</div>
|
</div>
|
||||||
@ -10126,21 +10406,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
tr.innerHTML = `
|
tr.innerHTML = `
|
||||||
<td>${item.name_en} / ${item.name_ar}</td>
|
<td>${item.name_en} / ${item.name_ar}</td>
|
||||||
<td class="text-center">${item.quantity}</td>
|
<td class="text-center">${item.quantity}</td>
|
||||||
<td class="text-end"><small>OMR</small> ${parseFloat(item.unit_price).toFixed(3)}</td>
|
<td class="text-end"><small><?= __('currency') ?></small> ${parseFloat(item.unit_price).toFixed(3)}</td>
|
||||||
<td class="text-end">${parseFloat(item.vat_rate || 0).toFixed(0)}%</td>
|
<td class="text-end">${parseFloat(item.vat_rate || 0).toFixed(0)}%</td>
|
||||||
<td class="text-end"><small>OMR</small> ${parseFloat(item.total_price).toFixed(3)}</td>
|
<td class="text-end"><small><?= __('currency') ?></small> ${parseFloat(item.total_price).toFixed(3)}</td>
|
||||||
`;
|
`;
|
||||||
body.appendChild(tr);
|
body.appendChild(tr);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (document.getElementById('invSubtotal')) document.getElementById('invSubtotal').innerHTML = '<small>OMR</small> ' + parseFloat(data.total_amount).toFixed(3);
|
const vatVal = parseFloat(data.vat_amount || 0);
|
||||||
if (document.getElementById('invVatAmount')) document.getElementById('invVatAmount').innerHTML = '<small>OMR</small> ' + (parseFloat(data.vat_amount) || 0).toFixed(3);
|
const totalVal = parseFloat(data.total_amount || 0);
|
||||||
const grandTotalValue = (parseFloat(data.total_with_vat) || parseFloat(data.total_amount));
|
const grandTotalValue = (parseFloat(data.total_with_vat) || (totalVal + vatVal));
|
||||||
if (document.getElementById('invGrandTotal')) document.getElementById('invGrandTotal').innerHTML = '<small>OMR</small> ' + grandTotalValue.toFixed(3);
|
|
||||||
|
|
||||||
if (document.getElementById('invPaidInfo')) document.getElementById('invPaidInfo').innerHTML = '<small>OMR</small> ' + parseFloat(data.paid_amount || 0).toFixed(3);
|
if (document.getElementById('invSubtotal')) document.getElementById('invSubtotal').innerHTML = '<small><?= __('currency') ?></small> ' + (grandTotalValue - vatVal).toFixed(3);
|
||||||
|
if (document.getElementById('invVatAmount')) document.getElementById('invVatAmount').innerHTML = '<small><?= __('currency') ?></small> ' + vatVal.toFixed(3);
|
||||||
|
if (document.getElementById('invGrandTotal')) document.getElementById('invGrandTotal').innerHTML = '<small><?= __('currency') ?></small> ' + grandTotalValue.toFixed(3);
|
||||||
|
|
||||||
|
if (document.getElementById('invPaidInfo')) document.getElementById('invPaidInfo').innerHTML = '<small><?= __('currency') ?></small> ' + parseFloat(data.paid_amount || 0).toFixed(3);
|
||||||
const balance = grandTotalValue - parseFloat(data.paid_amount || 0);
|
const balance = grandTotalValue - parseFloat(data.paid_amount || 0);
|
||||||
if (document.getElementById('invBalanceInfo')) document.getElementById('invBalanceInfo').innerHTML = '<small>OMR</small> ' + balance.toFixed(3);
|
if (document.getElementById('invBalanceInfo')) document.getElementById('invBalanceInfo').innerHTML = '<small><?= __('currency') ?></small> ' + balance.toFixed(3);
|
||||||
|
|
||||||
// Generate QR Code for Zakat, Tax and Customs Authority (ZATCA) style or simple formal
|
// Generate QR Code for Zakat, Tax and Customs Authority (ZATCA) style or simple formal
|
||||||
const companyName = <?= json_encode($data['settings']['company_name'] ?? 'Accounting System') ?>;
|
const companyName = <?= json_encode($data['settings']['company_name'] ?? 'Accounting System') ?>;
|
||||||
@ -10181,20 +10464,35 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
|
|
||||||
window.printPosReceiptFromInvoice = function(inv) {
|
window.printPosReceiptFromInvoice = function(inv) {
|
||||||
const container = document.getElementById('posReceiptContent');
|
const container = document.getElementById('posReceiptContent');
|
||||||
const itemsHtml = inv.items.map(item => `
|
const itemsHtml = inv.items.map(item => {
|
||||||
|
const itemTotal = item.unit_price * item.quantity;
|
||||||
|
const vatRate = parseFloat(item.vat_rate || 5);
|
||||||
|
const vatAmount = itemTotal * (vatRate / (100 + vatRate));
|
||||||
|
return `
|
||||||
<tr>
|
<tr>
|
||||||
<td>${item.name_en} / ${item.name_ar}<br><small>${item.quantity} x ${parseFloat(item.unit_price).toFixed(3)}</small></td>
|
<td>${item.name_en} / ${item.name_ar}<br><small>${item.quantity} x ${parseFloat(item.unit_price).toFixed(3)}</small></td>
|
||||||
<td style="text-align: right; vertical-align: bottom;">${(item.unit_price * item.quantity).toFixed(3)}</td>
|
<td style="text-align: right; vertical-align: bottom;">${vatAmount.toFixed(3)}</td>
|
||||||
|
<td style="text-align: right; vertical-align: bottom;">${itemTotal.toFixed(3)}</td>
|
||||||
</tr>
|
</tr>
|
||||||
`).join('');
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
const totalVat = inv.items.reduce((sum, item) => {
|
||||||
|
const itemTotal = item.unit_price * item.quantity;
|
||||||
|
const vatRate = parseFloat(item.vat_rate || 5);
|
||||||
|
return sum + (itemTotal * (vatRate / (100 + vatRate)));
|
||||||
|
}, 0);
|
||||||
|
const subtotal = inv.items.reduce((sum, item) => sum + (item.unit_price * item.quantity), 0);
|
||||||
|
|
||||||
const companyName = "<?= htmlspecialchars($data['settings']['company_name'] ?? 'Accounting System') ?>";
|
const companyName = "<?= htmlspecialchars($data['settings']['company_name'] ?? 'Accounting System') ?>";
|
||||||
const companyPhone = "<?= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>";
|
const companyPhone = "<?= htmlspecialchars($data['settings']['company_phone'] ?? '') ?>";
|
||||||
const companyVat = "<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>";
|
const companyVat = "<?= htmlspecialchars($data['settings']['vat_number'] ?? '') ?>";
|
||||||
|
const companyLogo = "<?= htmlspecialchars($data['settings']['company_logo'] ?? '') ?>";
|
||||||
|
|
||||||
container.innerHTML = `
|
container.innerHTML = `
|
||||||
<div class="thermal-receipt">
|
<div class="thermal-receipt">
|
||||||
<div class="center">
|
<div class="center">
|
||||||
|
${companyLogo ? `<img src="${companyLogo}" alt="Logo" style="max-height: 60px; width: auto; margin-bottom: 10px; display: block; margin-left: auto; margin-right: auto;">` : ''}
|
||||||
<h5 class="mb-0 fw-bold">${companyName}</h5>
|
<h5 class="mb-0 fw-bold">${companyName}</h5>
|
||||||
${companyPhone ? `<div>Tel: ${companyPhone}</div>` : ''}
|
${companyPhone ? `<div>Tel: ${companyPhone}</div>` : ''}
|
||||||
${companyVat ? `<div>VAT: ${companyVat}</div>` : ''}
|
${companyVat ? `<div>VAT: ${companyVat}</div>` : ''}
|
||||||
@ -10212,6 +10510,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>ITEM / الصنف</th>
|
<th>ITEM / الصنف</th>
|
||||||
|
<th style="text-align: right;">VAT / الضريبة</th>
|
||||||
<th style="text-align: right;">TOTAL / الإجمالي</th>
|
<th style="text-align: right;">TOTAL / الإجمالي</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -10220,17 +10519,25 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
|
<div class="d-flex justify-content-between small">
|
||||||
|
<span>Subtotal (Excl. VAT) / المجموع الفرعي (دون الضريبة)</span>
|
||||||
|
<span><?= __('currency') ?> ${(subtotal - totalVat).toFixed(3)}</span>
|
||||||
|
</div>
|
||||||
|
<div class="d-flex justify-content-between small">
|
||||||
|
<span>VAT / الضريبة</span>
|
||||||
|
<span><?= __('currency') ?> ${totalVat.toFixed(3)}</span>
|
||||||
|
</div>
|
||||||
<div class="total-row d-flex justify-content-between">
|
<div class="total-row d-flex justify-content-between">
|
||||||
<span>TOTAL / الإجمالي</span>
|
<span>TOTAL (Incl. VAT) / الإجمالي (شامل الضريبة)</span>
|
||||||
<span>OMR ${parseFloat(inv.total_with_vat).toFixed(3)}</span>
|
<span><?= __('currency') ?> ${subtotal.toFixed(3)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between small">
|
<div class="d-flex justify-content-between small">
|
||||||
<span>PAID / المدفوع</span>
|
<span>PAID / المدفوع</span>
|
||||||
<span>OMR ${parseFloat(inv.paid_amount).toFixed(3)}</span>
|
<span><?= __('currency') ?> ${parseFloat(inv.paid_amount).toFixed(3)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="d-flex justify-content-between small fw-bold">
|
<div class="d-flex justify-content-between small fw-bold">
|
||||||
<span>BALANCE / الرصيد</span>
|
<span>BALANCE / الرصيد</span>
|
||||||
<span>OMR ${(inv.total_with_vat - inv.paid_amount).toFixed(3)}</span>
|
<span><?= __('currency') ?> ${(subtotal - inv.paid_amount).toFixed(3)}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
<div class="center small">
|
<div class="center small">
|
||||||
|
|||||||
@ -49,3 +49,11 @@
|
|||||||
2026-02-19 05:59:46 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
2026-02-19 05:59:46 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
2026-02-19 06:00:05 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
2026-02-19 06:00:05 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
2026-02-19 06:00:51 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
2026-02-19 06:00:51 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
|
2026-02-19 06:12:12 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
|
2026-02-19 06:12:47 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":2,\"qty\":1,\"price\":0.2125},{\"id\":1,\"qty\":1,\"price\":0.3825}]"}
|
||||||
|
2026-02-19 06:17:13 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
|
2026-02-19 06:17:33 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
|
2026-02-19 06:23:28 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.595}]","total_amount":"0.595","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":1,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
|
2026-02-19 06:50:40 - POST: {"name_en":"Tissue","name_ar":"\u0645\u062d\u0627\u0631\u0645 \u0648\u0631\u0642\u064a\u0629","category_id":"2","unit_id":"2","supplier_id":"6","sku":"5673086966938977","sale_price":"0.25","purchase_price":"0.2","stock_quantity":"0.000","min_stock_level":"0.000","vat_rate":"5","expiry_date":"","promotion_start":"","promotion_end":"","promotion_percent":"0.00","add_item":""}
|
||||||
|
2026-02-19 06:53:36 - POST: {"action":"save_pos_transaction","customer_id":"","payments":"[{\"method\":\"cash\",\"amount\":0.978}]","total_amount":"0.9775","discount_code_id":"","discount_amount":"0","loyalty_redeemed":"0","items":"[{\"id\":1,\"qty\":2,\"price\":0.3825},{\"id\":2,\"qty\":1,\"price\":0.2125}]"}
|
||||||
|
2026-02-19 07:01:43 - POST: {"name_en":"Tissue","name_ar":"\u0645\u062d\u0627\u0631\u0645 \u0648\u0631\u0642\u064a\u0629","category_id":"2","unit_id":"2","supplier_id":"6","sku":"760115926272","sale_price":"0.25","purchase_price":"0.2","stock_quantity":"5","min_stock_level":"0.000","vat_rate":"5","expiry_date":"","promotion_start":"","promotion_end":"","promotion_percent":"0.00","add_item":""}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user