Developments

Kahera POS: A Free Offline Cash Register for Philippine Cafés That Gets Senior Discounts Right

Efraim Ray Efraim Ray 0 Comments
Web App Showcase

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.

Type Web App (PWA)
Stack Vanilla JS · no backend
Frontend HTML + CSS Grid
Version v1.0.0
License Free · MIT



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.

efraimray.github.io/kahera-pos/
KaheraKapehan sa Lantaw · sample café
Order K-0007

    Change₱0.00

    Sample menu with VAT-inclusive prices. The full app adds search, e-wallet and card payments, order history, voids, shift reports, CSV export and a menu editor.

    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:

    12.6
    All App JS
    KB gzipped
    4.8
    Stylesheet
    KB gzipped
    9/9
    Money Tests
    node, 0 failures
    0
    npm Dependencies
    no framework, no build

    Technology Stack & Architecture Layers

    Interface

    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.

    Logic

    Vanilla JavaScript

    A pure money.js module for totals, discounts, change and reports, and an app.js controller that renders with event delegation.

    Storage

    localStorage + fallback

    Menu, settings, cart and orders are saved on the device. If storage is blocked, an in-memory copy keeps the shift running.

    Offline

    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

    Pillar 01

    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.

    Pillar 02

    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.

    Pillar 03

    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.

    Pillar 04

    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:

    1

    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.

    2

    Next: Modifiers & Notes

    Item options and notes such as "less ice" or "extra rice", plus an undo for a cleared order.

    3

    Planned: Kitchen Display

    A second screen for the kitchen with an order queue that moves from "Preparing" to "Ready".

    4

    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

    Checkout

    Order Slip With Every Deduction

    Each sale ends with a printable slip that shows the VAT removed, the discount and the change.

    Shift Report

    End-of-Shift Numbers in One Tap

    Net sales, discounts given, sales by hour, payment mix, top items and a CSV export.

    Discount Engine

    Shared Bills, Computed Correctly

    Only the ID holder's share is discounted, and VAT comes off before the 20%.

    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.

    Launch Kahera

    GitHub Repository

    Browse the source, download v1.0.0, open an issue or suggest a feature. Free and open source under the MIT license.

    View on GitHub

    Tutorial & Docs

    A beginner-friendly walkthrough of every file, the discount formula, and deployment to GitHub Pages, Netlify, Vercel or Cloudflare.

    Read the Tutorial
    Engineering Takeaway

    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.

    Efraim Ray
    Written by

    Efraim Ray

    Software Developer and AI Specialist. BS Information Technology graduate. I pride myself on being conscientious, attentive to detail, and always open to learning — because the best solutions come from listening, taking notes, and exploring what's next.

    Comments