modifying logics
This commit is contained in:
parent
ece7b3d236
commit
1259663b17
3
fingerprint.php
Normal file
3
fingerprint.php
Normal file
@ -0,0 +1,3 @@
|
||||
<?php
|
||||
require_once 'lib/LicenseService.php';
|
||||
echo LicenseService::getFingerprint();
|
||||
337
index.php
337
index.php
@ -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>
|
||||
`;
|
||||
|
||||
@ -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");
|
||||
|
||||
@ -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"}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user