Email digital receipts, add promo settings, and apply tips at charge.
Deploy Ladill POS / deploy (push) Successful in 31s

Send real receipt emails from the customer display and sale page, store
branch promo content for the idle screen, and fold selected tips into totals.
This commit is contained in:
isaacclad
2026-07-15 16:10:20 +00:00
parent 468346b183
commit 600aedb59d
20 changed files with 675 additions and 19 deletions
+19 -5
View File
@@ -109,10 +109,15 @@
<span class="text-emerald-600">Loyalty discount</span>
<span class="font-medium text-emerald-700" x-text="'' + formatMoney(loyaltyDiscountMinor)"></span>
</div>
<div class="flex justify-between text-sm" x-show="tipMinor > 0">
<span class="text-slate-500">Tip</span>
<span class="font-medium text-slate-800" x-text="formatMoney(tipMinor)"></span>
</div>
<div class="flex justify-between text-sm">
<span class="text-slate-500">Total</span>
<span class="font-semibold text-slate-900" x-text="formatMoney(payableTotal())"></span>
</div>
<input type="hidden" name="tip_minor" :value="tipMinor || 0">
</div>
<div class="mt-4 space-y-3">
@@ -197,6 +202,8 @@
loyaltyMaxRedeem: 0,
loyaltyDiscountMinor: 0,
loyaltyQuoteTimer: null,
tipMinor: 0,
tipPercent: null,
isRestaurant: !!config.isRestaurant,
currency: config.currency || 'GHS',
cart: [],
@@ -280,7 +287,7 @@
} catch (_) {}
},
payableTotal() {
return Math.max(0, this.cartTotal() - (this.loyaltyDiscountMinor || 0));
return Math.max(0, this.cartTotal() - (this.loyaltyDiscountMinor || 0)) + (this.tipMinor || 0);
},
onGlobalKeydown(e) {
if (this.shouldIgnoreScan(e)) return;
@@ -529,16 +536,23 @@
const action = data.action;
if (!action?.type) return;
if (action.type === 'tip') {
const tip = action.tip_minor != null
? (this.currency + ' ' + (Number(action.tip_minor) / 100).toFixed(2))
this.tipMinor = Number(action.tip_minor) || 0;
this.tipPercent = action.tip_percent ?? null;
const tip = this.tipMinor
? (this.currency + ' ' + (this.tipMinor / 100).toFixed(2))
: ((action.tip_percent ?? 0) + '%');
this.customerActionNote = 'Customer selected tip: ' + tip;
this.customerActionNote = this.tipMinor
? ('Tip applied: ' + tip + ' — will be added at charge.')
: 'Customer declined tip.';
this.scheduleDisplayPush();
} else if (action.type === 'signature') {
this.customerActionNote = 'Customer signature captured.';
} else if (action.type === 'receipt_email' && action.email) {
this.customerActionNote = 'Customer requested receipt email: ' + action.email;
const emailInput = document.getElementById('customer_email');
if (emailInput && !emailInput.value) emailInput.value = action.email;
this.customerActionNote = action.email_sent
? ('Receipt emailed to ' + action.email)
: (action.email_message || ('Receipt email: ' + action.email));
} else if (action.type === 'payment_option') {
this.customerActionNote = 'Customer chose payment: ' + (action.payment_option || '');
}