Kahera POS: A Free Offline Cash Register for Philippine Cafés That Gets Senior Discounts Right
Walk into almost any karinderya or small café at lunch and you will see the same thing: a calculator, a notebook, and a cashier trying to work out a senior citizen's discount while the line grows. When VAT is involved, that math is easy to get wrong. I built Kahera POS, a free point-of-sale screen that runs in any browser, works offline, and computes Senior and PWD discounts correctly every time.
Kahera is Filipino for cashier. It is a progressive web app made with plain HTML, CSS and JavaScript, so a shop can install it on a tablet like a normal app, with no server, subscription or database to set up.
Try It: Ring Up a Senior's Order
This is a trimmed-down version of the real register. Tap items to add them, switch on Senior, change how many people share the bill, then tap a cash amount and complete the sale.
Build Footprint & Quality Checks
A shop tablet is often a budget Android device on prepaid data, so I kept Kahera small and dependency-free. These are the measured numbers from the v1.0.0 build:
Technology Stack & Architecture Layers
HTML5 + CSS Grid
Large tap targets, a sticky order rail on tablets, a fixed order bar on phones, and light and dark themes from CSS custom properties.
Vanilla JavaScript
A pure money.js module for totals, discounts, change and reports, and an app.js controller that renders with event delegation.
localStorage + fallback
Menu, settings, cart and orders are saved on the device. If storage is blocked, an in-memory copy keeps the shift running.
Service Worker PWA
A cache-first service worker and a web manifest let the shop install Kahera on a tablet and keep selling when the internet drops.
Key Architectural Decisions
Integer Centavos
Every amount is a whole number of centavos (₱65.00 is 6500). Floating-point pesos drift by a centavo over a busy day; integers never do.
Pro-Rated Senior/PWD
The discount applies to each ID holder's equal share of a shared bill. For VAT-registered sellers, VAT is removed from that share before the 20% is taken off.
Pure, Tested Math
money.js never touches the page, so the same file runs in the browser and in Node, where nine unit tests lock in known bills.
Safe for the Counter
Voids and new shifts use inline two-step confirmations, slips say "not an official receipt", and CSV export blocks spreadsheet formula injection.
Discount Math, Event Delegation & Tests
Here are the three pieces that matter most: the Senior/PWD calculation, the single click handler behind every menu tile, and the test that proves the ₱1,120 example comes out to ₱1,040.
// js/money.js — pure function, amounts in integer centavos
function computeTotals(lines, opts) {
opts = opts || {};
var vatReg = opts.vatRegistered !== false;
var gross = subtotal(lines);
var diners = clampInt(opts.diners, 1, 99);
var holders = clampInt(opts.idHolders, 0, diners);
var kind = (opts.discount === 'senior' || opts.discount === 'pwd') ? opts.discount : 'none';
var r = {
gross: gross, items: itemCount(lines), discountKind: 'none', diners: diners, idHolders: 0,
share: 0, vatRemoved: 0, discount: 0, vatable: 0, vat: 0, vatExemptSales: 0,
total: gross, vatRegistered: vatReg
};
var regular = gross;
if (kind !== 'none' && holders > 0 && gross > 0) {
var share = Math.round(gross * holders / diners);
var base = vatReg ? Math.round(share / (1 + VAT_RATE)) : share;
var disc = Math.round(base * SC_PWD_RATE);
regular = gross - share;
r.discountKind = kind;
r.idHolders = holders;
r.share = share;
r.vatRemoved = share - base;
r.discount = disc;
r.vatExemptSales = vatReg ? base : 0;
r.total = regular + (base - disc);
}
if (vatReg) {
r.vatable = Math.round(regular / (1 + VAT_RATE));
r.vat = regular - r.vatable;
}
return r;
}
// js/app.js — one delegated listener serves every tile, even after re-renders
$('grid').addEventListener('click', function (e) {
var b = e.target.closest('[data-add]');
if (b) addItem(b.getAttribute('data-add'));
});
function addItem(id) {
var m = S.menu.filter(function (x) { return x.id === id; })[0];
if (!m) return;
if (!m.available) { toast(m.name + ' is marked sold out'); return; }
var l = S.cart.lines.filter(function (x) { return x.id === id; })[0];
if (l) l.qty = Math.min(99, l.qty + 1);
else S.cart.lines.push({ id: m.id, name: m.name, price: m.price, qty: 1 });
saveCart(); renderCart(); renderGrid(id);
}
// tests/money.test.js — run with: npm test
const assert = require('assert');
const M = require('../js/money.js');
test('senior, 1 of 4 diners, VAT-registered (₱1,120 bill → ₱1,040)', () => {
const t = M.computeTotals([{ price: 28000, qty: 4 }], { vatRegistered: true, discount: 'senior', diners: 4, idHolders: 1 });
Development Roadmap & Milestones
Version 1.0.0 is released. The next milestones come straight from the project's roadmap:
v1.0.0: Core Register (Released)
Menu tiles, pro-rated Senior/PWD with VAT exemption, cash/e-wallet/card, order slips, voids, shift summary, CSV export, menu editor and offline PWA.
Next: Modifiers & Notes
Item options and notes such as "less ice" or "extra rice", plus an undo for a cleared order.
Planned: Kitchen Display
A second screen for the kitchen with an order queue that moves from "Preparing" to "Ready".
Planned: Inventory & Branch Sync
Stock counts with low-stock warnings, then optional cloud sync so a business with several branches sees one report.
User Interface & Feature Showcase
Project Links, Code & Documentation
Open the Live App
Run the full register in your browser. On a tablet, use "Add to Home Screen" to install it and use it offline.
GitHub Repository
Browse the source, download v1.0.0, open an issue or suggest a feature. Free and open source under the MIT license.
Tutorial & Docs
A beginner-friendly walkthrough of every file, the discount formula, and deployment to GitHub Pages, Netlify, Vercel or Cloudflare.
Faster Lines, Correct Discounts
A cash register looks simple until shared bills, VAT exemption, centavo rounding and spotty internet meet a lunch rush. Kahera handles all four with no monthly fee. Want it set up with your menu on your shop's tablet, or a custom version with inventory and branch reports?
The café, menu and orders in the demo are sample data. Kahera's order slips are not official receipts; businesses that issue receipts need a BIR-registered system, and discount rules differ by business type, so check with your accountant before using it for real sales.

Comments
Post a Comment