← Back to Blogs

Building PegWise: Bar & Restaurant Management Software Made in the Andaman Islands

How a single bar’s billing app grew into a multi-tenant SaaS: tax-correct bills, liquor counted to the millilitre, QR ordering from the table, and DJ nights run on wristbands.

Table of Contents

  1. What is PegWise?
  2. From One Bar’s App to a SaaS
  3. The Stack
  4. Tenant Isolation That Fails Closed
  5. One Bill, Two Taxes
  6. Liquor Stock to the Millilitre
  7. Roles That Match the Floor
  8. QR Ordering & WhatsApp Bills
  9. Pub Nights: The Band Replaces the Table
  10. Selling It as a SaaS
  11. Engineering Lessons
  12. FAQs

1. What is PegWise?

PegWise is bar and restaurant management software built in Port Blair (Sri Vijaya Puram) for venues across the Andaman & Nicobar Islands, including Havelock (Swaraj Dweep) and Neil (Shaheed Dweep). It’s live at pegwise.threegeeks.in as a multi-tenant SaaS with a 14-day free trial and no card needed.

Most POS software in India treats a bar as a restaurant with a liquor sticker on it. An island bar has different problems. It sells a 750 ml bottle as 30, 60 and 90 ml pegs. It charges VAT on liquor and GST on food on the same bill. It files a VAT return every month. PegWise is built around those problems first.

What a venue runs on PegWise:

  • Tables, orders and separate kitchen and bar KOTs
  • One invoice with VAT on liquor and CGST/SGST on food
  • Liquor stock counted to the millilitre, godown to bar
  • Guests ordering from a QR code on the table, with no app to install
  • The paid bill sent as a PDF on WhatsApp, with a review link
  • VAT return working, expenses, vendor payouts and a real P&L
  • Five staff roles, scoped per outlet
  • DJ and pub nights with wristband tabs, door entry and advance UPI bookings
  • An Android POS app for the door, the bar and the floor, which keeps working offline

2. From One Bar’s App to a SaaS

PegWise didn’t start in an office. Its predecessor was a single-tenant PHP app I built for a working island bar. Over its life it issued more than 84,650 real bills and recorded more than 135,000 stock movements. Every billing, stock and tax rule in PegWise was first worked out on a live shift there.

For the rewrite, the old app became the specification. I ran its production data through a spike before writing the new schema, so the catalogue and stock model were designed against real data, not a guess. The rewrite also departs from the old behaviour in three places on purpose:

Immutable invoices

The old app let you edit an issued bill. PegWise voids it and issues a credit note, as GST requires.

Split GST

A single GST total became CGST, SGST and IGST, which a tax invoice must show.

Real order lines

Lines stored as comma-separated text became proper rows, which made discounts, voids and reports possible.

3. The Stack

Laravel 13 · PHP 8.4
PostgreSQL 16 + RLS
Redis + queues
Inertia + React
Flutter (Android POS)
Razorpay subscriptions
WhatsApp via GeekAds
QZ Tray for KOT printing
Pest + PHPStan level 8

Laravel serves a JSON API to the Flutter app and Inertia pages to the web. Production is plain PHP-FPM, nginx, Postgres and Redis on a VPS. Docker is only for local development. The test suite runs against real PostgreSQL, not SQLite, because SQLite can’t express row-level security. A passing SQLite run would tell you nothing about tenant isolation.

4. Tenant Isolation That Fails Closed

PegWise keeps every venue in one shared database with a tenant_id column. That is the cheapest model to run, and the riskiest if you get it wrong. So isolation is enforced by two independent layers:

Two layers, two failure modes

  • Application: a global Eloquent scope filters every query by tenant. If tenancy isn’t initialised, it applies no filter, so it fails open.
  • Database: a PostgreSQL row-level security policy on every tenant table. With no tenant set, it matches nothing, so it fails closed.

The first layer does the everyday work. The second layer is why a forgotten scope, a raw DB::table() query or a background job without context returns an empty list instead of another bar’s sales.

Two traps that silently switch RLS off

I hit both of these while building it, and both leave the policies installed and visible while enforcing nothing:

  • ENABLE without FORCE. A table’s owner is exempt from its own policies, and Laravel migrates and queries as the same role that owns every table. Without FORCE ROW LEVEL SECURITY, the policy doesn’t apply to the app at all.
  • Connecting as a superuser. Superusers bypass RLS unconditionally. The Postgres Docker image creates its default user as a superuser, so the obvious setup is the broken one. The app connects as a dedicated role with NOSUPERUSER and NOBYPASSRLS.

An IsolationCoverageTest asserts both at runtime, and it fails the build if any table carrying tenant_id lacks a policy. A later feature can’t quietly add an unprotected table.

5. One Bill, Two Taxes

An Andaman bar bill typically carries both VAT (on liquor) and GST (on food), and the two are filed separately. PegWise totals them separately on the same invoice: VAT at the rate the venue sets, and GST split into CGST and SGST. Invoice numbers are allocated per financial year without gaps. An issued invoice is never edited. It is voided with a credit note, and its stock comes back through a reversal movement, so the ledger still shows that the sale happened and was undone.

The month-end work is done as the shift runs. Output VAT is collected on every liquor line and input VAT is credited from purchases. The VAT working is laid out to match the Andaman & Nicobar return, so filing is a matter of copying figures across, and every figure traces back to a real bill.

6. Liquor Stock to the Millilitre

Stock follows the path a bottle takes: purchase → godown → transfer → bar → sale. Liquor becomes servable only once someone moves it to the bar, and the bar can’t go below zero. That makes the bar balance mean “what the bartender can actually reach”, so refusing a sale on it is meaningful rather than bureaucratic.

AOne base unit per item: millilitres for spirits, pieces for beer, so 3 × 60 ml is exactly 180, with no drifting fractions of a litre
BQuantities are always positive, and the movement reason applies the sign, so a sale can’t be booked as a receipt by mistake
CAvailable = bar balance − quantity committed on open orders, so two waiters can’t promise the last peg to two tables
DThe check runs when the order is taken, not at billing, because by billing time the drink has already been poured
EShortage alerts say what to do: “transfer from the godown” (a ten-second fix) or “out of stock” (tell the guest)
FBalances update in one atomic upsert, so two tills selling the same item at once both count

Messages use the item’s own terms. “9000” means nothing to a bartender. “12 × 750 ml in the godown” does.

7. Roles That Match the Floor

PegWise has five fixed roles: Owner, Manager, Cashier, Waiter and Storekeeper. Each is assigned per outlet, because a manager at one location is an outsider at another. The split is deliberate. A waiter who can void a served line, or a cashier who can void an issued bill, is the classic till fiddle.

Every screen declares on its route the ability it needs. There is a separate check for whether the business has paid for that module. One asks what the person may do, the other what the business bought, and a screen needs both. A test walks every route and fails if one has neither an ability nor a written reason for being open. Guards also stop anyone from promoting themselves or reaching above their own role. A last-owner rule makes sure a business can never be left with nobody able to reach billing or staff settings.

I chose a fixed role enum over a permissions package on purpose. Bars don’t need custom roles. A permissions cache shared across tenants is one more place a leak could hide.

8. QR Ordering & WhatsApp Bills

Each table can have a QR code. Scanning it opens the live menu in the guest’s own browser, with no app and no login, just a name and mobile number entered once. Anything sold out is already hidden. The guest’s picks land on the table’s tab, and staff fire the KOT as usual, so the kitchen flow doesn’t change.

When the guest pays, a PDF of the bill can go straight to their WhatsApp with the venue’s review link, so every settled table becomes a chance at the next rating.

9. Pub Nights: The Band Replaces the Table

DJ nights are the hardest thing a bar runs, so they got their own module. The core idea is that a wristband replaces the table. A guest’s tab is found by scanning their band, and the tab is an ordinary order with an event attached. Billing, stock and tax don’t fork into a separate system.

How a night runs:

  • Set up entries, time-based cover prices, credit per entry and tab limits
  • Print bands with check digits, so a mistyped number is caught
  • Door, bar, floor and exit each run on Android devices that queue work offline and catch up when the signal returns
  • A live board shows the door count, the money and any line waiting for a manager’s decision
  • Lost band? The tab moves to a new band, and drinks poured before the swap still land
  • At close, every held line is decided, and every tab still owing is marked as a walkout or a named credit

Nights can also be sold in advance. The venue publishes a public page with the poster and gallery, and guests book and pay by UPI while their places are held for 15 minutes. A person then checks each UPI reference against the bank before confirming. Nothing else can make a booking real. The guest gets their ticket on WhatsApp and email, and at the door the paid cover becomes credit on their tab, even with no signal.

Cover charges and part-payments aren’t sales. They sit in an append-only tab ledger until settlement applies them to the bill as an advance, so VAT and GST are worked out on what was actually consumed. A night’s report ends with one line saying whether it balances. Before any change to billing ships, a concurrency script races real processes against the test database: 150 guests, 1,200 lines, several devices and tills. It has to end CLEAN.

10. Selling It as a SaaS

Pricing is modular. The base price follows the business type (bar, restaurant, or both), and venues switch on only the modules they use: inventory, reports, the expense ledger, staff roles, events. The pricing section on the homepage reads the same live catalogue that signup charges from, so the site can’t promise a price or feature that doesn’t exist.

Subscriptions go through Razorpay. Because merchant approval is per website, payments go through a small HMAC-authenticated broker on an already-approved Three Geeks domain. PegWise never holds the merchant keys.

The marketing pages are an Inertia app, so for crawlers that don’t run JavaScript, every page also ships a server-rendered copy of the same words from a single source. Bing, ChatGPT search, Perplexity and WhatsApp link previews see real content, and the two versions can never disagree.

11. Engineering Lessons From the Build

AIn a shared-database SaaS, make the database enforce isolation too. App-level scopes fail open, and RLS with FORCE fails closed.
BTest against the database you deploy on. SQLite can’t tell you whether row-level security works.
CStore liquor in millilitres and let the movement reason decide the sign. Most stock bugs disappear.
DCheck stock when the order is taken, not when the bill is printed.
EGate every GET as well as every POST. A hidden tab is not a permission check.
FAssert flag values in tests, not return values. Laravel mass assignment fails silently.

12. Frequently Asked Questions

What is PegWise?

A bar and restaurant management SaaS made in Port Blair for Andaman & Nicobar venues. It covers KOTs, a VAT + GST bill, liquor stock to the millilitre, QR ordering, WhatsApp bills, expenses, VAT returns, staff roles and pub-night wristband tabs, on the web and an Android POS app.

How does it handle VAT on liquor and GST on food?

Each line carries its own tax kind. Liquor collects VAT, food collects CGST and SGST, and both are totalled separately on one invoice. Issued invoices are immutable and are corrected with a credit note.

How is liquor stock tracked?

In millilitres, from godown to bar. Pegs deplete exactly, the bar can’t go below zero, and stock already committed on open orders is held back so the last peg can’t be sold twice.

How is each venue’s data kept separate?

By the application and by PostgreSQL row-level security together. If the app ever misses a filter, the database returns nothing rather than another venue’s data.

Can you build something like this for my business?

Yes. Whether it’s a bar, restaurant, resort or café chain in Port Blair, Havelock, Neil or anywhere in the Andamans, I can design and build billing, inventory, a POS app or a full SaaS end to end.

Need billing, POS or inventory software for your Andaman business?

Tell me how your counter runs today and I’ll come back with a scope, timeline and price. Or try PegWise free for 14 days.