update pos
This commit is contained in:
parent
8c0d2453dd
commit
40fd435ffd
93
index.php
93
index.php
@ -8332,6 +8332,11 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
|
||||
const paid = this.payments.reduce((sum, p) => sum + p.amount, 0);
|
||||
return total - paid;
|
||||
},
|
||||
setCompleteOrderButtonsDisabled(disabled) {
|
||||
document.querySelectorAll('.pos-complete-order-btn').forEach(button => {
|
||||
button.disabled = !!disabled;
|
||||
});
|
||||
},
|
||||
renderPayments() {
|
||||
const container = document.getElementById('paymentList');
|
||||
const methodLabels = {
|
||||
@ -8379,14 +8384,14 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
|
||||
if (remaining <= 0.0001 || currentInput >= remaining - 0.0001) {
|
||||
display.classList.remove('text-danger');
|
||||
display.classList.add('text-success');
|
||||
document.getElementById('confirmPaymentBtn').disabled = false;
|
||||
this.setCompleteOrderButtonsDisabled(false);
|
||||
} else {
|
||||
display.classList.remove('text-success');
|
||||
display.classList.add('text-danger');
|
||||
document.getElementById('confirmPaymentBtn').disabled = true;
|
||||
this.setCompleteOrderButtonsDisabled(true);
|
||||
}
|
||||
},
|
||||
async completeOrder() {
|
||||
async completeOrder(autoPrint = true) {
|
||||
if (this.items.length === 0) {
|
||||
Swal.fire(<?= json_encode($lang === 'ar' ? 'خطأ' : 'Error', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, <?= json_encode($lang === 'ar' ? 'السلة فارغة' : 'Cart is empty', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, 'error');
|
||||
return;
|
||||
@ -8421,10 +8426,34 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = document.getElementById('confirmPaymentBtn');
|
||||
const originalText = btn.innerText;
|
||||
btn.disabled = true;
|
||||
btn.innerText = <?= json_encode($lang === 'ar' ? 'جارٍ المعالجة...' : 'PROCESSING...', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
const activeBtn = document.getElementById(autoPrint ? 'confirmPaymentPrintBtn' : 'confirmPaymentSaveBtn');
|
||||
const actionButtons = Array.from(document.querySelectorAll('.pos-complete-order-btn'));
|
||||
const originalButtonHtml = actionButtons.reduce((carry, button) => {
|
||||
carry[button.id] = button.innerHTML;
|
||||
return carry;
|
||||
}, {});
|
||||
|
||||
actionButtons.forEach(button => {
|
||||
button.disabled = true;
|
||||
});
|
||||
|
||||
if (activeBtn) {
|
||||
activeBtn.innerText = <?= json_encode($lang === 'ar' ? 'جارٍ المعالجة...' : 'PROCESSING...', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
}
|
||||
|
||||
const restoreActionButtons = () => {
|
||||
actionButtons.forEach(button => {
|
||||
if (Object.prototype.hasOwnProperty.call(originalButtonHtml, button.id)) {
|
||||
button.innerHTML = originalButtonHtml[button.id];
|
||||
}
|
||||
button.disabled = false;
|
||||
});
|
||||
};
|
||||
|
||||
const savedTitle = <?= json_encode($lang === 'ar' ? 'تم الحفظ' : 'Saved', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
const savedWithoutPrintText = <?= json_encode($lang === 'ar' ? 'تم حفظ الفاتورة بدون طباعة' : 'Invoice saved without printing', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
const savedWithoutPrintPrefix = <?= json_encode($lang === 'ar' ? 'تم حفظ الفاتورة بدون طباعة. الرقم:' : 'Invoice saved without printing. No:', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
const okText = <?= json_encode($lang === 'ar' ? 'حسنًا' : 'OK', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
|
||||
const subtotal = this.items.reduce((sum, item) => sum + (parseFloat(item.price) * item.qty), 0);
|
||||
const totalVat = this.items.reduce((sum, item) => {
|
||||
@ -8465,22 +8494,34 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
|
||||
}
|
||||
|
||||
if (result.success) {
|
||||
const payModal = bootstrap.Modal.getInstance(document.getElementById('posPaymentModal'));
|
||||
const payModalEl = document.getElementById('posPaymentModal');
|
||||
const payModal = payModalEl ? bootstrap.Modal.getInstance(payModalEl) : null;
|
||||
if (payModal) payModal.hide();
|
||||
this.showReceipt(result.invoice_id, discountAmount, loyaltyRedeemed, result.transaction_no);
|
||||
|
||||
if (autoPrint) {
|
||||
this.showReceipt(result.invoice_id, discountAmount, loyaltyRedeemed, result.transaction_no, true);
|
||||
} else {
|
||||
this.clear();
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: savedTitle,
|
||||
text: result.transaction_no ? `${savedWithoutPrintPrefix} ${result.transaction_no}` : savedWithoutPrintText,
|
||||
confirmButtonText: okText
|
||||
}).then(() => {
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
Swal.fire(<?= json_encode($lang === 'ar' ? 'خطأ' : 'Error', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, result.error, 'error');
|
||||
btn.disabled = false;
|
||||
btn.innerText = originalText;
|
||||
restoreActionButtons();
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
Swal.fire(<?= json_encode($lang === 'ar' ? 'خطأ' : 'Error', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, err.message || <?= json_encode($lang === 'ar' ? 'حدث خطأ ما' : 'Something went wrong', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>, 'error');
|
||||
btn.disabled = false;
|
||||
btn.innerText = originalText;
|
||||
restoreActionButtons();
|
||||
}
|
||||
},
|
||||
showReceipt(invId, discountAmount, loyaltyRedeemed, transactionNo) {
|
||||
showReceipt(invId, discountAmount, loyaltyRedeemed, transactionNo, autoPrint = false) {
|
||||
const container = document.getElementById('posReceiptContent');
|
||||
const customerSelect = document.getElementById('posCustomer');
|
||||
const customerName = (customerSelect && customerSelect.selectedIndex >= 0 && customerSelect.value !== '') ? customerSelect.options[customerSelect.selectedIndex].text : <?= json_encode(__('walk_in_customer'), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>;
|
||||
@ -8589,14 +8630,23 @@ runtime_debug_mark('page:rendering', ['page' => (string)$page]);
|
||||
</div>
|
||||
`;
|
||||
|
||||
const modal = new bootstrap.Modal(document.getElementById('posReceiptModal'));
|
||||
const receiptModalEl = document.getElementById('posReceiptModal');
|
||||
const modal = bootstrap.Modal.getOrCreateInstance(receiptModalEl);
|
||||
modal.show();
|
||||
|
||||
this.clear();
|
||||
|
||||
document.getElementById('posReceiptModal').addEventListener('hidden.bs.modal', function () {
|
||||
receiptModalEl.addEventListener('hidden.bs.modal', function () {
|
||||
location.reload();
|
||||
}, { once: true });
|
||||
|
||||
if (autoPrint) {
|
||||
setTimeout(() => {
|
||||
if (typeof window.printPosReceipt === 'function') {
|
||||
window.printPosReceipt();
|
||||
}
|
||||
}, 250);
|
||||
}
|
||||
},
|
||||
|
||||
async syncCustomer(val) {
|
||||
@ -12198,7 +12248,9 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
#posPaymentModal .form-control { font-size: 0.85rem; padding: 0.35rem 0.6rem; }
|
||||
#posPaymentModal .btn-primary { padding: 0.35rem 0.8rem; font-size: 0.85rem; }
|
||||
#posPaymentModal .modal-header { padding: 0.75rem 1rem 0.25rem; }
|
||||
#posPaymentModal .modal-footer { padding: 0.25rem 1rem 0.75rem; }
|
||||
#posPaymentModal .modal-footer { padding: 0.25rem 1rem 0.75rem; gap: 0.5rem; flex-wrap: wrap; }
|
||||
#posPaymentModal .modal-footer .btn-light { min-width: 110px; }
|
||||
#posPaymentModal .modal-footer .pos-complete-order-btn { flex: 1 1 180px; }
|
||||
</style>
|
||||
|
||||
<!-- POS Payment Modal -->
|
||||
@ -12299,8 +12351,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
</div>
|
||||
<div class="modal-footer border-0">
|
||||
<button type="button" class="btn btn-light" data-bs-dismiss="modal" data-en="Cancel" data-ar="إلغاء">Cancel</button>
|
||||
<button type="button" class="btn btn-primary px-4" id="confirmPaymentBtn" onclick="cart.completeOrder()">
|
||||
<?= $lang === 'ar' ? 'ادفع وأكمل' : 'PAY & COMPLETE' ?>
|
||||
<button type="button" class="btn btn-outline-primary pos-complete-order-btn" id="confirmPaymentSaveBtn" onclick="cart.completeOrder(false)" data-en="Save Without Print" data-ar="حفظ بدون طباعة">
|
||||
<?= $lang === 'ar' ? 'حفظ بدون طباعة' : 'SAVE WITHOUT PRINT' ?>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary pos-complete-order-btn" id="confirmPaymentPrintBtn" onclick="cart.completeOrder(true)" data-en="Save & Print" data-ar="حفظ وطباعة">
|
||||
<?= $lang === 'ar' ? 'حفظ وطباعة' : 'SAVE & PRINT' ?>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -852,14 +852,158 @@
|
||||
printArea.innerHTML = `<div class="thermal-receipt thermal-receipt-print">${sourceHtml || ''}</div>`;
|
||||
};
|
||||
|
||||
function printPosReceipt() {
|
||||
const content = document.getElementById('posReceiptContent').innerHTML;
|
||||
window.printReceiptHtml = function(sourceHtml, options = {}) {
|
||||
const rawHtml = (sourceHtml || '').trim();
|
||||
if (!rawHtml) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.innerHTML = rawHtml;
|
||||
|
||||
const receipt = wrapper.querySelector('.thermal-receipt');
|
||||
const receiptHtml = receipt ? receipt.outerHTML : `<div class="thermal-receipt">${rawHtml}</div>`;
|
||||
const docTitle = String(options.title || 'Receipt').replace(/[<>"']/g, '');
|
||||
const pageLang = document.documentElement.getAttribute('lang') || 'en';
|
||||
const pageDir = document.documentElement.getAttribute('dir') || (receipt && receipt.classList.contains('rtl') ? 'rtl' : 'ltr');
|
||||
|
||||
const iframe = document.createElement('iframe');
|
||||
iframe.setAttribute('aria-hidden', 'true');
|
||||
iframe.style.position = 'fixed';
|
||||
iframe.style.right = '0';
|
||||
iframe.style.bottom = '0';
|
||||
iframe.style.width = '0';
|
||||
iframe.style.height = '0';
|
||||
iframe.style.border = '0';
|
||||
document.body.appendChild(iframe);
|
||||
|
||||
const frameDoc = iframe.contentWindow.document;
|
||||
frameDoc.open();
|
||||
frameDoc.write(`<!doctype html>
|
||||
<html lang="${pageLang}" dir="${pageDir}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>${docTitle}</title>
|
||||
<style>
|
||||
* { box-sizing: border-box; }
|
||||
@page { size: 80mm auto; margin: 0; }
|
||||
html, body {
|
||||
width: 80mm !important;
|
||||
max-width: 80mm !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 0 !important;
|
||||
background: #fff !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
body {
|
||||
font-family: 'Courier New', Courier, monospace;
|
||||
font-size: 12px;
|
||||
line-height: 1.4;
|
||||
-webkit-print-color-adjust: exact;
|
||||
print-color-adjust: exact;
|
||||
}
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
h5, h6, p {
|
||||
margin: 0 0 4px 0;
|
||||
}
|
||||
.thermal-receipt {
|
||||
width: 72mm !important;
|
||||
max-width: 72mm !important;
|
||||
margin: 0 auto !important;
|
||||
padding: 2mm 0 !important;
|
||||
background: #fff !important;
|
||||
color: #000 !important;
|
||||
}
|
||||
.thermal-receipt.rtl {
|
||||
direction: rtl;
|
||||
text-align: right;
|
||||
}
|
||||
.thermal-receipt .center {
|
||||
text-align: center;
|
||||
}
|
||||
.thermal-receipt .separator {
|
||||
border-top: 1px dashed #000;
|
||||
margin: 8px 0;
|
||||
}
|
||||
.thermal-receipt table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.thermal-receipt table th {
|
||||
text-align: left;
|
||||
border-bottom: 1px dashed #000;
|
||||
font-size: 10px;
|
||||
padding-bottom: 4px;
|
||||
word-break: break-word;
|
||||
}
|
||||
.thermal-receipt.rtl table th {
|
||||
text-align: right;
|
||||
}
|
||||
.thermal-receipt table td {
|
||||
padding: 4px 0;
|
||||
font-size: 11px;
|
||||
word-break: break-word;
|
||||
vertical-align: top;
|
||||
}
|
||||
.d-flex { display: flex !important; }
|
||||
.justify-content-between { justify-content: space-between !important; }
|
||||
.align-items-center { align-items: center !important; }
|
||||
.fw-bold { font-weight: 700 !important; }
|
||||
.small { font-size: 11px !important; }
|
||||
.text-uppercase { text-transform: uppercase !important; }
|
||||
.text-danger { color: #b91c1c !important; }
|
||||
.text-success { color: #047857 !important; }
|
||||
.mb-0 { margin-bottom: 0 !important; }
|
||||
.mt-1 { margin-top: 0.25rem !important; }
|
||||
.total-row {
|
||||
font-weight: 700;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>${receiptHtml}</body>
|
||||
</html>`);
|
||||
frameDoc.close();
|
||||
|
||||
setTimeout(() => {
|
||||
iframe.contentWindow.focus();
|
||||
iframe.contentWindow.print();
|
||||
setTimeout(() => {
|
||||
document.body.removeChild(iframe);
|
||||
}, 2000);
|
||||
}, 350);
|
||||
};
|
||||
|
||||
window.printPosReceipt = function(options = {}) {
|
||||
const contentEl = document.getElementById('posReceiptContent');
|
||||
if (!contentEl) {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = contentEl.innerHTML;
|
||||
if (!(content || '').trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window.printReceiptHtml === 'function') {
|
||||
window.printReceiptHtml(content, {
|
||||
title: options.title || <?= json_encode($lang === 'ar' ? 'إيصال نقطة البيع' : 'POS Receipt', JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) ?>
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const printArea = document.getElementById('posPrintArea');
|
||||
window.prepareReceiptPrintArea(content, printArea);
|
||||
|
||||
|
||||
document.body.classList.add('printing-receipt');
|
||||
window.print();
|
||||
document.body.classList.remove('printing-receipt');
|
||||
location.reload();
|
||||
if (printArea) {
|
||||
printArea.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -200,27 +200,34 @@
|
||||
|
||||
setTimeout(() => {
|
||||
const receiptContent = document.getElementById('posReceiptContent');
|
||||
const printArea = document.getElementById('posPrintArea');
|
||||
|
||||
if (!receiptContent || !printArea) {
|
||||
if (!receiptContent) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof window.prepareReceiptPrintArea === 'function') {
|
||||
window.prepareReceiptPrintArea(receiptContent.innerHTML, printArea);
|
||||
if (typeof window.printReceiptHtml === 'function') {
|
||||
window.printReceiptHtml(receiptContent.innerHTML, {
|
||||
title: data.document_no || data.transaction_no || 'Sale Receipt'
|
||||
});
|
||||
} else {
|
||||
printArea.innerHTML = receiptContent.innerHTML;
|
||||
const receipt = printArea.querySelector('.thermal-receipt');
|
||||
if (receipt) {
|
||||
receipt.classList.add('thermal-receipt-print');
|
||||
const printArea = document.getElementById('posPrintArea');
|
||||
if (printArea) {
|
||||
if (typeof window.prepareReceiptPrintArea === 'function') {
|
||||
window.prepareReceiptPrintArea(receiptContent.innerHTML, printArea);
|
||||
} else {
|
||||
printArea.innerHTML = receiptContent.innerHTML;
|
||||
const receipt = printArea.querySelector('.thermal-receipt');
|
||||
if (receipt) {
|
||||
receipt.classList.add('thermal-receipt-print');
|
||||
}
|
||||
}
|
||||
|
||||
document.body.classList.add('printing-receipt');
|
||||
window.print();
|
||||
document.body.classList.remove('printing-receipt');
|
||||
printArea.innerHTML = '';
|
||||
}
|
||||
}
|
||||
|
||||
document.body.classList.add('printing-receipt');
|
||||
window.print();
|
||||
document.body.classList.remove('printing-receipt');
|
||||
printArea.innerHTML = '';
|
||||
|
||||
const receiptModalEl = document.getElementById('posReceiptModal');
|
||||
const receiptModal = receiptModalEl ? bootstrap.Modal.getInstance(receiptModalEl) : null;
|
||||
if (receiptModal) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user