modifying logics

This commit is contained in:
Flatlogic Bot 2026-02-19 05:25:35 +00:00
parent ece7b3d236
commit 1259663b17
4 changed files with 320 additions and 97 deletions

3
fingerprint.php Normal file
View File

@ -0,0 +1,3 @@
<?php
require_once 'lib/LicenseService.php';
echo LicenseService::getFingerprint();

337
index.php
View File

@ -127,6 +127,30 @@ function numberToWords($num) {
return (string)$num;
}
function numberToWordsArabic($num) {
$num = (int)$num;
if ($num == 0) return "صفر";
$ones = ["", "واحد", "اثنان", "ثلاثة", "أربعة", "خمسة", "ستة", "سبعة", "ثمانية", "تسعة", "عشرة", "أحد عشر", "اثنا عشر", "ثلاثة عشر", "أربعة عشر", "خمسة عشر", "ستة عشر", "سبعة عشر", "ثمانية عشر", "تسعة عشر"];
$tens = ["", "", "عشرون", "ثلاثون", "أربعون", "خمسون", "ستون", "سبعون", "ثمانون", "تسعون"];
$hundreds = ["", "مائة", "مائتان", "ثلاثمائة", "أربعمائة", "خمسمائة", "ستمائة", "سبعمائة", "ثمانمائة", "تسعمائة"];
if ($num < 20) return $ones[$num];
if ($num < 100) return ($num % 10 ? $ones[$num % 10] . " و " : "") . $tens[(int)($num / 10)];
if ($num < 1000) return $hundreds[(int)($num / 100)] . ($num % 100 ? " و " . numberToWordsArabic($num % 100) : "");
if ($num < 1000000) {
$thousands = (int)($num / 1000);
$rem = $num % 1000;
$tStr = "ألف";
if ($thousands == 1) $tStr = "ألف";
else if ($thousands == 2) $tStr = "ألفين";
else if ($thousands >= 3 && $thousands <= 10) $tStr = numberToWordsArabic($thousands) . " آلاف";
else $tStr = numberToWordsArabic($thousands) . " ألف";
return $tStr . ($rem ? " و " . numberToWordsArabic($rem) : "");
}
return (string)$num;
}
// Login Logic
$login_error = '';
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['login'])) {
@ -289,6 +313,46 @@ if (isset($_GET['action']) || isset($_POST['action'])) {
echo json_encode(['success' => true]);
exit;
}
if ($action === 'get_invoice_items') {
header('Content-Type: application/json');
$invoice_id = (int)$_GET['invoice_id'];
$stmt = db()->prepare("SELECT ii.*, i.name_en, i.name_ar, i.sku
FROM invoice_items ii
JOIN stock_items i ON ii.item_id = i.id
WHERE ii.invoice_id = ?");
$stmt->execute([$invoice_id]);
echo json_encode($stmt->fetchAll(PDO::FETCH_ASSOC));
exit;
}
if ($action === 'get_return_details') {
header('Content-Type: application/json');
$return_id = (int)$_GET['return_id'];
$type = $_GET['type'] ?? 'sale';
if ($type === 'purchase') {
$stmt = db()->prepare("SELECT pr.*, c.name as party_name FROM purchase_returns pr LEFT JOIN customers c ON pr.supplier_id = c.id WHERE pr.id = ?");
$stmt->execute([$return_id]);
$return = $stmt->fetch(PDO::FETCH_ASSOC);
if ($return) {
$stmtItems = db()->prepare("SELECT pri.*, i.name_en, i.name_ar, i.sku FROM purchase_return_items pri JOIN stock_items i ON pri.item_id = i.id WHERE pri.return_id = ?");
$stmtItems->execute([$return_id]);
$return['items'] = $stmtItems->fetchAll(PDO::FETCH_ASSOC);
}
} else {
$stmt = db()->prepare("SELECT sr.*, c.name as party_name FROM sales_returns sr LEFT JOIN customers c ON sr.customer_id = c.id WHERE sr.id = ?");
$stmt->execute([$return_id]);
$return = $stmt->fetch(PDO::FETCH_ASSOC);
if ($return) {
$stmtItems = db()->prepare("SELECT sri.*, i.name_en, i.name_ar, i.sku FROM sales_return_items sri JOIN stock_items i ON sri.item_id = i.id WHERE sri.return_id = ?");
$stmtItems->execute([$return_id]);
$return['items'] = $stmtItems->fetchAll(PDO::FETCH_ASSOC);
}
}
echo json_encode($return);
exit;
}
}
// Redirect to login if not authenticated
@ -355,14 +419,25 @@ function numberToWordsOMR($number) {
$number = number_format((float)$number, 3, '.', '');
list($rials, $baisas) = explode('.', $number);
$rialsWords = numberToWords((int)$rials);
$baisasWords = numberToWords((int)$baisas);
$rialsWordsEn = numberToWords((int)$rials);
$baisasWordsEn = numberToWords((int)$baisas);
$result = $rialsWords . " Omani Rials";
$enResult = $rialsWordsEn . " Omani Rials";
if ((int)$baisas > 0) {
$result .= " and " . $baisasWords . " Baisas";
$enResult .= " and " . $baisasWordsEn . " Baisas";
}
return $result . " Only";
$enResult .= " Only";
$rialsWordsAr = numberToWordsArabic((int)$rials);
$baisasWordsAr = numberToWordsArabic((int)$baisas);
$arResult = $rialsWordsAr . " ريال عماني";
if ((int)$baisas > 0) {
$arResult .= " و " . $baisasWordsAr . " بيسة";
}
$arResult .= " فقط";
return $enResult . " / " . $arResult;
}
function getPromotionalPrice($item) {
@ -519,6 +594,108 @@ if (isset($_POST['add_hr_department'])) {
}
}
if (isset($_POST['add_sales_return'])) {
$invoice_id = (int)$_POST['invoice_id'];
$return_date = $_POST['return_date'] ?: date('Y-m-d');
$notes = $_POST['notes'] ?? '';
$item_ids = $_POST['item_ids'] ?? [];
$quantities = $_POST['quantities'] ?? [];
$prices = $_POST['prices'] ?? [];
if ($invoice_id && !empty($item_ids)) {
$db = db();
try {
$db->beginTransaction();
// Get customer_id from invoice
$stmtInv = $db->prepare("SELECT customer_id FROM invoices WHERE id = ?");
$stmtInv->execute([$invoice_id]);
$customer_id = $stmtInv->fetchColumn();
$total_return = 0;
foreach ($quantities as $i => $qty) {
$total_return += (float)$qty * (float)$prices[$i];
}
// Insert Sales Return
$stmt = $db->prepare("INSERT INTO sales_returns (invoice_id, customer_id, return_date, total_amount, notes) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$invoice_id, $customer_id, $return_date, $total_return, $notes]);
$return_id = $db->lastInsertId();
// Insert Return Items and Update Stock
$stmtItem = $db->prepare("INSERT INTO sales_return_items (return_id, item_id, quantity, unit_price, total_price) VALUES (?, ?, ?, ?, ?)");
$stmtStock = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity + ? WHERE id = ?");
foreach ($item_ids as $i => $item_id) {
$qty = (float)$quantities[$i];
if ($qty > 0) {
$price = (float)$prices[$i];
$line_total = $qty * $price;
$stmtItem->execute([$return_id, $item_id, $qty, $price, $line_total]);
$stmtStock->execute([$qty, $item_id]);
}
}
$db->commit();
$message = "Sales Return processed successfully!";
} catch (Exception $e) {
$db->rollBack();
$message = "Error processing return: " . $e->getMessage();
}
}
}
if (isset($_POST['add_purchase_return'])) {
$invoice_id = (int)$_POST['invoice_id'];
$return_date = $_POST['return_date'] ?: date('Y-m-d');
$notes = $_POST['notes'] ?? '';
$item_ids = $_POST['item_ids'] ?? [];
$quantities = $_POST['quantities'] ?? [];
$prices = $_POST['prices'] ?? [];
if ($invoice_id && !empty($item_ids)) {
$db = db();
try {
$db->beginTransaction();
// Get supplier_id (customer_id column) from invoice
$stmtInv = $db->prepare("SELECT customer_id FROM invoices WHERE id = ?");
$stmtInv->execute([$invoice_id]);
$supplier_id = $stmtInv->fetchColumn();
$total_return = 0;
foreach ($quantities as $i => $qty) {
$total_return += (float)$qty * (float)$prices[$i];
}
// Insert Purchase Return
$stmt = $db->prepare("INSERT INTO purchase_returns (invoice_id, supplier_id, return_date, total_amount, notes) VALUES (?, ?, ?, ?, ?)");
$stmt->execute([$invoice_id, $supplier_id, $return_date, $total_return, $notes]);
$return_id = $db->lastInsertId();
// Insert Return Items and Update Stock
$stmtItem = $db->prepare("INSERT INTO purchase_return_items (return_id, item_id, quantity, unit_price, total_price) VALUES (?, ?, ?, ?, ?)");
$stmtStock = $db->prepare("UPDATE stock_items SET stock_quantity = stock_quantity - ? WHERE id = ?");
foreach ($item_ids as $i => $item_id) {
$qty = (float)$quantities[$i];
if ($qty > 0) {
$price = (float)$prices[$i];
$line_total = $qty * $price;
$stmtItem->execute([$return_id, $item_id, $qty, $price, $line_total]);
$stmtStock->execute([$qty, $item_id]);
}
}
$db->commit();
$message = "Purchase Return processed successfully!";
} catch (Exception $e) {
$db->rollBack();
$message = "Error processing return: " . $e->getMessage();
}
}
}
// --- Biometric Devices Handlers ---
if (isset($_POST['add_biometric_device'])) {
$name = $_POST['device_name'] ?? '';
@ -1193,6 +1370,12 @@ switch ($page) {
}
$data['items_list'] = $items_list_raw;
$data['customers_list'] = db()->query("SELECT id, name FROM customers WHERE type = '" . ($type === 'sale' ? 'customer' : 'supplier') . "' ORDER BY name ASC")->fetchAll();
if ($type === 'sale') {
$data['sales_invoices'] = db()->query("SELECT id, invoice_date, total_with_vat FROM invoices WHERE type = 'sale' ORDER BY id DESC")->fetchAll();
} else {
$data['purchase_invoices'] = db()->query("SELECT id, invoice_date, total_with_vat FROM invoices WHERE type = 'purchase' ORDER BY id DESC")->fetchAll();
}
break;
case 'sales_returns':
@ -7899,12 +8082,12 @@ document.addEventListener('DOMContentLoaded', function() {
${companyPhone}
</div>
<div class="col-6 text-end">
<h1 class="invoice-title fw-bold mb-0 text-uppercase">Quotation</h1>
<h1 class="invoice-title fw-bold mb-0 text-uppercase">Quotation / عرض سعر</h1>
<div class="mt-2">${statusBadge}</div>
<div class="mt-3">
<p class="mb-0 fs-5">No: <strong class="text-primary">${quotNo}</strong></p>
<p class="mb-0">Date: <span class="fw-bold">${quotDate}</span></p>
<p class="mb-0">Valid Until: <span class="fw-bold">${quotValid}</span></p>
<p class="mb-0 fs-5">No / رقم: <strong class="text-primary">${quotNo}</strong></p>
<p class="mb-0">Date / التاريخ: <span class="fw-bold">${quotDate}</span></p>
<p class="mb-0">Valid Until / صالح لغاية: <span class="fw-bold">${quotValid}</span></p>
</div>
</div>
</div>
@ -7913,7 +8096,7 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="row mb-4 g-3">
<div class="col-6">
<div class="invoice-info-card">
<p class="text-muted small text-uppercase fw-bold mb-2 border-bottom pb-1">To</p>
<p class="text-muted small text-uppercase fw-bold mb-2 border-bottom pb-1">To / إلى</p>
<h5 class="mb-1 fw-bold">${customerName}</h5>
</div>
</div>
@ -7923,25 +8106,25 @@ document.addEventListener('DOMContentLoaded', function() {
<thead class="bg-dark text-white">
<tr>
<th>#</th>
<th>Item Description</th>
<th class="text-center">Qty</th>
<th class="text-end">Unit Price</th>
<th class="text-center">VAT</th>
<th class="text-end">Total</th>
<th>Item Description / وصف الصنف</th>
<th class="text-center">Qty / الكمية</th>
<th class="text-end">Unit Price / سعر الوحدة</th>
<th class="text-center">VAT / الضريبة</th>
<th class="text-end">Total / الإجمالي</th>
</tr>
</thead>
<tbody>${itemsHtml}</tbody>
<tfoot>
<tr>
<th colspan="5" class="text-end">Subtotal</th>
<th colspan="5" class="text-end">Subtotal / المجموع الفرعي</th>
<td class="text-end fw-bold">${parseFloat(data.total_amount).toFixed(3)}</td>
</tr>
<tr>
<th colspan="5" class="text-end">VAT Amount</th>
<th colspan="5" class="text-end">VAT Amount / مبلغ الضريبة</th>
<td class="text-end fw-bold">${parseFloat(data.vat_amount).toFixed(3)}</td>
</tr>
<tr class="table-primary">
<th colspan="5" class="text-end h5">Grand Total (OMR)</th>
<th colspan="5" class="text-end h5">Grand Total (OMR) / المجموع الكلي (رع)</th>
<td class="text-end h5 fw-bold">${parseFloat(data.total_with_vat).toFixed(3)}</td>
</tr>
</tfoot>
@ -7950,19 +8133,19 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="mt-5 pt-3 border-top">
<div class="row">
<div class="col-6">
<p class="small text-muted text-uppercase fw-bold">Terms & Conditions:</p>
<p class="small text-muted text-uppercase fw-bold">Terms & Conditions / الشروط والأحكام:</p>
<ul class="small text-muted">
<li>Quotation is valid until the date mentioned above.</li>
<li>Prices are inclusive of VAT where applicable.</li>
<li>Quotation is valid until the date mentioned above. / عرض السعر صالح لغاية التاريخ المذكور أعلاه.</li>
<li>Prices are inclusive of VAT where applicable. / الأسعار تشمل ضريبة القيمة المضافة حيثما ينطبق ذلك.</li>
</ul>
</div>
<div class="col-6 text-end pt-4">
<div class="border-top d-inline-block px-5">Authorized Signature</div>
<div class="border-top d-inline-block px-5">Authorized Signature / التوقيع المعتمد</div>
</div>
</div>
</div>
<div class="mt-4 text-center">
<p class="text-muted x-small mb-0">Generated by ${companyName}</p>
<p class="text-muted x-small mb-0">Generated by / تم إنشاؤه بواسطة ${companyName}</p>
</div>
</div>
`;
@ -8286,7 +8469,7 @@ document.addEventListener('DOMContentLoaded', function() {
</div>
</div>
<?php if ($page === 'sales_returns' || $page === 'purchase_returns'): ?>
<?php if ($page === 'sales_returns' || $page === 'purchase_returns' || $page === 'sales' || $page === 'purchases'): ?>
<div class="modal fade" id="addPurchaseReturnModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content text-start border-0 shadow">
@ -9096,12 +9279,12 @@ document.addEventListener('DOMContentLoaded', function() {
<?php endif; ?>
</div>
<div class="col-6 text-end">
<h1 class="invoice-title fw-bold mb-0 text-uppercase">Tax Invoice</h1>
<h1 class="invoice-title fw-bold mb-0 text-uppercase">Tax Invoice / فاتورة ضريبية</h1>
<div class="mt-2"><span id="invoiceTypeLabel" class="badge"></span></div>
<div class="mt-3">
<p class="mb-0 fs-5">Invoice No: <strong id="invNumber" class="text-primary"></strong></p>
<p class="mb-0">Date: <span id="invDate" class="fw-bold"></span></p>
<p class="mb-0 small">Status: <span id="invoiceStatusLabel"></span></p>
<p class="mb-0 fs-5">Invoice No / رقم الفاتورة: <strong id="invNumber" class="text-primary"></strong></p>
<p class="mb-0">Date / التاريخ: <span id="invDate" class="fw-bold"></span></p>
<p class="mb-0 small">Status / الحالة: <span id="invoiceStatusLabel"></span></p>
</div>
</div>
</div>
@ -9110,17 +9293,17 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="row mb-4 g-3">
<div class="col-6">
<div class="invoice-info-card">
<p class="text-muted small text-uppercase fw-bold mb-2 border-bottom pb-1" id="invPartyLabel">Bill To</p>
<p class="text-muted small text-uppercase fw-bold mb-2 border-bottom pb-1" id="invPartyLabel">Bill To / فاتورة إلى</p>
<h5 class="mb-1 fw-bold"><span id="invCustomerName"></span></h5>
<p class="small text-muted mb-0" id="invCustomerTaxIdContainer">VAT: <span id="invCustomerTaxId"></span></p>
<p class="small text-muted mb-0" id="invCustomerPhoneContainer">Phone: <span id="invCustomerPhone"></span></p>
<p class="small text-muted mb-0" id="invCustomerTaxIdContainer">VAT / الضريبة: <span id="invCustomerTaxId"></span></p>
<p class="small text-muted mb-0" id="invCustomerPhoneContainer">Phone / الهاتف: <span id="invCustomerPhone"></span></p>
</div>
</div>
<div class="col-6">
<div class="invoice-info-card text-end">
<p class="text-muted small text-uppercase fw-bold mb-2 border-bottom pb-1">Payment Details</p>
<p class="mb-1">Method: <strong id="invPaymentType"></strong></p>
<p class="mb-0 small text-muted">Currency: <strong>OMR</strong></p>
<p class="text-muted small text-uppercase fw-bold mb-2 border-bottom pb-1">Payment Details / تفاصيل الدفع</p>
<p class="mb-1">Method / الطريقة: <strong id="invPaymentType"></strong></p>
<p class="mb-0 small text-muted">Currency / العملة: <strong>OMR / ريال عماني</strong></p>
</div>
</div>
</div>
@ -9128,11 +9311,11 @@ document.addEventListener('DOMContentLoaded', function() {
<table class="table table-bordered table-formal">
<thead>
<tr>
<th>Item Description</th>
<th class="text-center" style="width: 80px;">Qty</th>
<th class="text-end" style="width: 120px;">Unit Price</th>
<th class="text-end" style="width: 100px;">VAT %</th>
<th class="text-end" style="width: 150px;">Total</th>
<th>Item Description / وصف الصنف</th>
<th class="text-center" style="width: 80px;">Qty / الكمية</th>
<th class="text-end" style="width: 120px;">Unit Price / سعر الوحدة</th>
<th class="text-end" style="width: 100px;">VAT % / الضريبة %</th>
<th class="text-end" style="width: 150px;">Total / الإجمالي</th>
</tr>
</thead>
<tbody id="invItemsBody"></tbody>
@ -9141,51 +9324,51 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="row mt-4">
<div class="col-7">
<div class="p-3 bg-light rounded" style="min-height: 100px;">
<p class="text-muted small text-uppercase fw-bold mb-1">Amount in Words</p>
<p class="text-muted small text-uppercase fw-bold mb-1">Amount in Words / المبلغ بالحروف</p>
<p id="invAmountInWords" class="small fw-bold mb-0"></p>
</div>
<div class="mt-4">
<p class="text-muted small text-uppercase fw-bold mb-1">Terms & Conditions</p>
<p class="text-muted small text-uppercase fw-bold mb-1">Terms & Conditions / الشروط والأحكام</p>
<ul class="small text-muted ps-3">
<li>Goods once sold will not be taken back or exchanged.</li>
<li>Payment is due within the agreed credit period.</li>
<li>This is a computer-generated invoice and does not require a physical signature.</li>
<li>Goods once sold will not be taken back or exchanged. / البضاعة التي تباع لا ترد ولا تستبدل.</li>
<li>Payment is due within the agreed credit period. / الدفع مستحق خلال فترة الائتمان المتفق عليها.</li>
<li>This is a computer-generated invoice and does not require a physical signature. / هذه فاتورة تم إنشاؤها بواسطة الكمبيوتر ولا تتطلب توقيعًا فعليًا.</li>
</ul>
</div>
</div>
<div class="col-5">
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">Subtotal</span>
<span class="text-muted">Subtotal / المجموع الفرعي</span>
<span id="invSubtotal" class="fw-bold"></span>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-muted">VAT Amount</span>
<span class="text-muted">VAT Amount / مبلغ الضريبة</span>
<span id="invVatAmount" class="fw-bold"></span>
</div>
<div class="d-flex justify-content-between mb-3 border-top pt-2">
<span class="h4 fw-bold">Grand Total</span>
<span class="h4 fw-bold">Grand Total / المجموع الكلي</span>
<span id="invGrandTotal" class="h4 fw-bold text-primary"></span>
</div>
<div id="invPaymentsSection" class="mt-4 border-top pt-3">
<p class="text-muted small text-uppercase fw-bold mb-2">Payment Tracking</p>
<p class="text-muted small text-uppercase fw-bold mb-2">Payment Tracking / تتبع الدفع</p>
<table class="table table-sm table-bordered small mb-3">
<thead class="table-light">
<tr>
<th>Date</th>
<th>Method</th>
<th class="text-end">Amount</th>
<th>Date / التاريخ</th>
<th>Method / الطريقة</th>
<th class="text-end">Amount / المبلغ</th>
</tr>
</thead>
<tbody id="invPaymentsBody"></tbody>
</table>
<div class="bg-light p-3 rounded">
<div class="d-flex justify-content-between small mb-1">
<span>Paid Amount</span>
<span>Paid Amount / المبلغ المدفوع</span>
<span id="invPaidInfo" class="text-success fw-bold"></span>
</div>
<div class="d-flex justify-content-between small fw-bold">
<span>Balance Due</span>
<span>Balance Due / الرصيد المتبقي</span>
<span id="invBalanceInfo" class="text-danger"></span>
</div>
</div>
@ -9197,7 +9380,7 @@ document.addEventListener('DOMContentLoaded', function() {
<div class="row">
<div class="col-4">
<div class="border-top pt-2 mx-auto" style="width: 150px;">
<p class="small text-muted">Customer Signature</p>
<p class="small text-muted">Customer Signature / توقيع العميل</p>
</div>
</div>
<div class="col-4">
@ -9205,7 +9388,7 @@ document.addEventListener('DOMContentLoaded', function() {
</div>
<div class="col-4">
<div class="border-top pt-2 mx-auto" style="width: 150px;">
<p class="small text-muted">Authorized Signatory</p>
<p class="small text-muted">Authorized Signatory / التوقيع المعتمد</p>
</div>
</div>
</div>
@ -9284,47 +9467,47 @@ document.addEventListener('DOMContentLoaded', function() {
<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>
<hr class="my-4">
<h4 class="letter-spacing-2 fw-bold text-uppercase">Payment Receipt</h4>
<h4 class="letter-spacing-2 fw-bold text-uppercase">Payment Receipt / سند قبض</h4>
</div>
<div class="row mb-4">
<div class="col-6">
<p class="mb-1 text-muted small text-uppercase fw-bold">Receipt No</p>
<p class="mb-1 text-muted small text-uppercase fw-bold">Receipt No / رقم السند</p>
<p class="fw-bold h5 text-primary" id="receiptNo"></p>
</div>
<div class="col-6 text-end">
<p class="mb-1 text-muted small text-uppercase fw-bold">Date</p>
<p class="mb-1 text-muted small text-uppercase fw-bold">Date / التاريخ</p>
<p class="fw-bold" id="receiptDate"></p>
</div>
</div>
<div class="mb-4 p-3 bg-light rounded">
<div class="row mb-2">
<div class="col-4 text-muted small text-uppercase fw-bold" id="receiptPartyLabel">Received From</div>
<div class="col-4 text-muted small text-uppercase fw-bold" id="receiptPartyLabel">Received From / استلمنا من</div>
<div class="col-8 fw-bold" id="receiptCustomer"></div>
</div>
<div class="row mb-2">
<div class="col-4 text-muted small text-uppercase fw-bold" id="receiptAgainstLabel">Against Invoice</div>
<div class="col-4 text-muted small text-uppercase fw-bold" id="receiptAgainstLabel">Against Invoice / مقابل فاتورة</div>
<div class="col-8 fw-bold" id="receiptInvNo"></div>
</div>
<div class="row mb-2">
<div class="col-4 text-muted small text-uppercase fw-bold">Payment Method</div>
<div class="col-4 text-muted small text-uppercase fw-bold">Payment Method / طريقة الدفع</div>
<div class="col-8 fw-bold" id="receiptMethod"></div>
</div>
</div>
<div class="text-center my-4 py-4 border-top border-bottom">
<p class="mb-1 text-muted small text-uppercase fw-bold">Amount Paid</p>
<p class="mb-1 text-muted small text-uppercase fw-bold">Amount Paid / المبلغ المدفوع</p>
<h1 class="display-5 fw-bold text-primary mb-1" id="receiptAmount"></h1>
<p class="text-muted small font-italic" id="receiptAmountWords"></p>
</div>
<div id="receiptNotesContainer" class="mb-4" style="display: none;">
<p class="mb-1 text-muted small text-uppercase fw-bold">Notes</p>
<p class="mb-1 text-muted small text-uppercase fw-bold">Notes / ملاحظات</p>
<p class="small p-2 bg-light rounded" id="receiptNotes"></p>
</div>
<div class="row mt-5 pt-4">
<div class="col-6">
<div class="border-top pt-2 text-center small text-muted">Receiver's Signature</div>
<div class="border-top pt-2 text-center small text-muted">Receiver's Signature / توقيع المستلم</div>
</div>
<div class="col-6">
<div class="border-top pt-2 text-center small text-muted">Authorized Signatory</div>
<div class="border-top pt-2 text-center small text-muted">Authorized Signatory / التوقيع المعتمد</div>
</div>
</div>
</div>
@ -9885,7 +10068,7 @@ document.addEventListener('DOMContentLoaded', function() {
document.getElementById('invAmountInWords').textContent = data.total_in_words || '';
document.getElementById('invPartyLabel').textContent = data.type === 'sale' ? 'Bill To' : 'Bill From';
document.getElementById('invPartyLabel').textContent = data.type === 'sale' ? 'Bill To / فاتورة إلى' : 'Bill From / فاتورة من';
document.getElementById('invPartyLabel').setAttribute('data-en', data.type === 'sale' ? 'Bill To' : 'Bill From');
document.getElementById('invPartyLabel').setAttribute('data-ar', data.type === 'sale' ? 'فاتورة إلى' : 'فاتورة من');
document.getElementById('invoiceTypeLabel').textContent = data.type;
@ -9969,7 +10152,7 @@ document.addEventListener('DOMContentLoaded', function() {
const container = document.getElementById('posReceiptContent');
const itemsHtml = inv.items.map(item => `
<tr>
<td>${item.name_en}<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>
</tr>
`).join('');
@ -9985,20 +10168,20 @@ document.addEventListener('DOMContentLoaded', function() {
${companyPhone ? `<div>Tel: ${companyPhone}</div>` : ''}
${companyVat ? `<div>VAT: ${companyVat}</div>` : ''}
<div class="separator"></div>
<h6 class="fw-bold">TAX INVOICE</h6>
<div>Inv: INV-${inv.id.toString().padStart(5, '0')}</div>
<div>Date: ${inv.invoice_date}</div>
<h6 class="fw-bold">TAX INVOICE / فاتورة ضريبية</h6>
<div>Inv / رقم: INV-${inv.id.toString().padStart(5, '0')}</div>
<div>Date / التاريخ: ${inv.invoice_date}</div>
<div class="separator"></div>
</div>
<div>
<strong>Customer:</strong> ${inv.customer_name || 'Walk-in'}
<strong>Customer / العميل:</strong> ${inv.customer_name || 'Walk-in / عميل عابر'}
</div>
<div class="separator"></div>
<table>
<thead>
<tr>
<th>ITEM</th>
<th style="text-align: right;">TOTAL</th>
<th>ITEM / الصنف</th>
<th style="text-align: right;">TOTAL / الإجمالي</th>
</tr>
</thead>
<tbody>
@ -10007,20 +10190,20 @@ document.addEventListener('DOMContentLoaded', function() {
</table>
<div class="separator"></div>
<div class="total-row d-flex justify-content-between">
<span>TOTAL</span>
<span>TOTAL / الإجمالي</span>
<span>OMR ${parseFloat(inv.total_with_vat).toFixed(3)}</span>
</div>
<div class="d-flex justify-content-between small">
<span>PAID</span>
<span>PAID / المدفوع</span>
<span>OMR ${parseFloat(inv.paid_amount).toFixed(3)}</span>
</div>
<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>
</div>
<div class="separator"></div>
<div class="center small">
<p>Thank You for your business!</p>
<p>Thank You for your business! / شكراً لتعاملكم معنا!</p>
</div>
</div>
`;

View File

@ -71,26 +71,53 @@ if ($step === 3 && $_SERVER['REQUEST_METHOD'] === 'POST') {
try {
$pdo = db();
// Create tables if they don't exist
$pdo->exec("CREATE TABLE IF NOT EXISTS role_groups (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
permissions TEXT
)");
// Import full schema if available
$schemaFile = __DIR__ . '/../complete_schema.sql';
if (file_exists($schemaFile)) {
$host = DB_HOST;
$name = DB_NAME;
$user = DB_USER;
$pass = DB_PASS;
// Use mysql CLI for reliable import
$cmd = sprintf(
'mysql -h %s -u %s %s %s < %s',
escapeshellarg($host),
escapeshellarg($user),
($pass ? '-p' . escapeshellarg($pass) : ''),
escapeshellarg($name),
escapeshellarg($schemaFile)
);
exec($cmd, $output, $returnVar);
if ($returnVar !== 0) {
// Fallback to manual execution if CLI fails
$sql = file_get_contents($schemaFile);
$pdo->exec($sql);
}
} else {
// Fallback to base tables if complete_schema.sql is missing
$pdo->exec("CREATE TABLE IF NOT EXISTS role_groups (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
permissions TEXT
)");
$pdo->exec("CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
phone VARCHAR(20),
group_id INT,
status ENUM('active', 'inactive') DEFAULT 'active',
profile_pic VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
$pdo->exec("CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
email VARCHAR(100),
phone VARCHAR(20),
group_id INT,
status ENUM('active', 'inactive') DEFAULT 'active',
profile_pic VARCHAR(255),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)");
}
// Insert Default Admin Role if missing
// Ensure Admin Role exists (might be in schema but just in case)
$stmt = $pdo->prepare("SELECT id FROM role_groups WHERE name = 'Administrator'");
$stmt->execute();
$role = $stmt->fetch();
@ -101,9 +128,15 @@ if ($step === 3 && $_SERVER['REQUEST_METHOD'] === 'POST') {
$roleId = $role['id'];
}
// Insert Admin User
// Insert Admin User (Use ON DUPLICATE KEY UPDATE to handle existing user from schema)
$hashedPass = password_hash($adminPass, PASSWORD_DEFAULT);
$stmt = $pdo->prepare("INSERT INTO users (username, password, email, group_id, status) VALUES (?, ?, ?, ?, 'active')");
$stmt = $pdo->prepare("INSERT INTO users (username, password, email, group_id, status)
VALUES (?, ?, ?, ?, 'active')
ON DUPLICATE KEY UPDATE
password = VALUES(password),
email = VALUES(email),
group_id = VALUES(group_id),
status = 'active'");
$stmt->execute([$adminUser, $hashedPass, $adminEmail, $roleId]);
header("Location: index.php?step=4");

View File

@ -41,3 +41,7 @@
2026-02-18 18:09:44 - 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-18 18:09:48 - POST: {"open_register":"1","register_id":"1","opening_balance":"0"}
2026-02-18 18:10:17 - POST: {"close_register":"1","session_id":"2","cash_in_hand":"5","notes":""}
2026-02-19 05:17:29 - POST: {"license_key":"FLAT-5FDB-C2BB","activate":""}
2026-02-19 05:18:29 - POST: {"open_register":"1","register_id":"1","opening_balance":"0"}
2026-02-19 05:18:39 - 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:18:51 - POST: {"open_register":"1","register_id":"1","opening_balance":"0"}