HealthcareSoftware ModernizationCustom Software

A Pharmacy Platform With Real Accounting Underneath

An eight-year-old system built for one health organisation had to become a product any pharmacy owner could adopt, and its books had never balanced. We reworked it into a modular Laravel and React platform where every sale, purchase and due posts to a real double-entry ledger, then moved seventeen live pharmacies onto balanced books without closing a counter.

Project SnapshotMediPharma24 · BD
17
Live Pharmacies
Multi-shop tenants
26k+
Medicines
Shared, revision-safe
09
Domain Packages
Bounded contexts
360+
API Endpoints
Web, app, storefront
Client
MediPharma24
Location
Bangladesh
Platform
Laravel, React & Flutter
Engagement
SaaS Rework & Data Migration
The Challenge

Sales on Screen, Profit Nowhere

Any billing app can show yesterday’s sales. None of them can tell an owner whether the shop earned anything, and eight years of bespoke code had the same blind spot.

The system recorded sales, purchases and stock, but posted only one leg of each transaction, so the ledger never balanced and a healthy shop could look like it was losing millions. Stock lived in two tables that disagreed. All of it had to become a multi-tenant product a skeptical shopkeeper could try from a phone.

What Was Breaking
  • A ledger that posted one leg per transaction, leaving the books 1.78M out across shops at rehearsal
  • Periodic purchase expensing that turned one shop’s 1.7M gross profit into a 9.1M paper loss
  • Two stock stores, a batch lookup and a movement log, drifting apart with no rule for which one wins
  • Depleted and expired batches deleted from view, so low-stock alerts and expiry value silently vanished
  • A single-organisation codebase with no tenancy boundary, no trial path and no role tiers for a second branch
Our Approach

Split the Code, Centralise the Money

One Laravel application, nine bounded-context packages, one shared database. Every financial side-effect travels as a domain event into an accounting package that alone is allowed to write the ledger.

01
Bounded Contexts as Composer Packages

Sales and POS, stock and catalogue, purchasing, replenishment, discounts, accounting, identity, demo, insights and storefront each own their routes, migrations, permissions, React SPA and tests. A written context map records where legacy code still crosses a boundary, so the split stays mechanical.

02
Events In, Journal Entries Out

A sale, purchase, return, revaluation or expense fires a domain event; posting listeners in the accounting package write balanced journal entries keyed on a source reference, so retries post nothing twice. The POS refuses any sale that cannot balance its own journal.

03
Batch Truth First, Then the Reports

One authoritative batch store with oldest-first stock-out, a derived movement log, and a daily rollup that stock reports read at actual cost. Depleted batches are retained and alerts are never hidden, so expiry value by month and low-stock worklists stay honest.

04
Tenancy at the Lookup, Rules in Policies

Organisation-to-shop scoping is enforced where a record is fetched, never after, over five access tiers and a 125-slug permission registry. Record-state rules live in Laravel policies. A salesperson bills without ever seeing a margin.

05
Try Demo, Storefront and the Mobile App

Name, pharmacy and mobile number provision a private tenant in about two minutes, seeded through the live services so every demo transaction posts real books. Customers order by QR code with OTP-verified tracking, and a Flutter sales app shares the same API.

Architecture

How the Pieces Connect

Counter & Back Office
01POS & Sales
02Purchases & Suppliers
03Batch Stock & Expiry
04Replenishment
Accounting Core
Double-Entry Posting Layer

Consumes domain events from every context, posts balanced and idempotent journal entries, and serves the P&L, balance sheet, trial balance and drawer reconciliation.

Laravel 12React 19MySQLRedisMeilisearch
Owner & Customer Surfaces
01Insights Dashboard
02Financial Reports
03QR Storefront
04Mobile Sales App
The Outcome

Real Profit, Not a Guess

Seventeen live pharmacies moved onto balanced books without closing a counter, and a shopkeeper can now try the product from a phone before talking to anyone.

Where It Landed
  • Live fleet migrated to double-entry with opening inventory, restated cost of goods and per-invoice supplier settlement, gated on a three-way reconciliation that had to pass clean
  • Profit and loss, balance sheet and trial balance generated automatically from counter activity, in Bangla or English
  • Expiry by month and value, low-stock alerts that feed requisitions and purchase orders, and drawer sessions closed against a counted variance
  • 1,233 depleted batches restored across 850 shop-products, so low-stock and expiry worklists tell the truth
  • Self-service demo provisioning, QR storefront orders in production, and an MCP catalogue server for AI-assisted product review, all covered by 1,100+ backend tests and 52 end-to-end specs
Engineering Note

The rehearsal clone was 1.78M out before reconciliation, and one shop showed a 9.1M loss on a healthy 1.7M gross profit because legacy purchases had been expensed periodically. Rather than patch figures, we wrote a fourteen-step idempotent migration command, rehearsed it twice on production clones, and made go-live conditional on a reconciliation that passed clean.

Stack

Technologies Used

Laravel 12PHP 8.4React 19TypeScriptAnt DesignTailwind CSSMySQLRedisLaravel HorizonMeilisearchLaravel PassportOneSignalFlutterPHPUnitPlaywrightGitHub Actions
More Work

Other Case Studies

eBayAmazonEtsyShopify
Normalise
Field & Variant Mapper
Type
Images
Stock
Woo ProductDokan Vendor
eCommerce · Marketplace
A Multi-Channel Import Engine for Woo & Dokan
Vendor catalogues imported from four marketplaces, with a three tier identity layer for verified stores.
Read case study
RegistrationConsultationProsthetics
Single Source
The Client Record
StockFinanceKPI Board
Healthcare · Clinic Ops
From Paper Ledgers to a Clinic Platform
Six operational areas on one client record, from registration through to KPI dashboards.
Read case study
One Record
The Renovation Job
Tasks
Budget
CEOClientTrade
Real Estate · Project Ops
A Renovation Business Run From Four Dashboards
Four permission tiers and three role dashboards over one governed job record.
Read case study
Start a Conversation

Have a Project That Needs The Same Treatment?

Tell us where the friction is. We will map it with you and propose a clear place to start.

Start Your ProjectBook a Call
Response within one business day
Initial consultation at no cost
Backed by 15+ years of product engineering