HealthcareSoftware ModernizationCustom Software

A Pharmacy Platform
Real Accounting Underneath

A single-client pharmacy system whose books never balanced had to become a product any pharmacy could adopt. We rebuilt it with a double-entry ledger and migrated 17 live pharmacies 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
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.

Only one leg of each transaction was posted, so healthy shops could show losses in the millions. Stock lived in two conflicting tables. The result had to become a multi-tenant product a 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, stock, purchasing, replenishment, discounts, accounting, identity, demo, insights and storefront each own their routes, migrations, permissions, React SPA and tests. A context map records where legacy code still crosses a boundary.

02
Events In, Journal Entries Out

Every sale, purchase, return, revaluation or expense fires a domain event that writes a balanced journal entry keyed on its source, so retries never double-post. The POS rejects any sale that can't balance its own journal.

03
Batch Truth First, Then the Reports

One batch store with oldest-first stock-out, a derived movement log, and a daily rollup at actual cost. Depleted batches are kept and alerts never hidden, so expiry and low-stock reports 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, where every demo transaction posts real books. Customers order by QR code with OTP-verified tracking, and a Flutter sales app shares the 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, and one shop showed a 9.1M loss on 1.7M gross profit because legacy purchases were expensed periodically. We wrote a fourteen-step idempotent migration, rehearsed it twice on production clones, and gated go-live on a clean reconciliation.

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
E-commerce · 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, from registration to KPI dashboards, on a single client record.
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 Needsthe Same Treatment?

Tell us where the friction is. We’ll propose a clear place to start.

Start a ProjectBook a Call
What Happens Next
01
Share your brief

Send a project request or book a call. A written scope helps but isn’t required. NDA on request.

02
Reply in one business day

A project manager reviews your request and sets up a free call with a senior engineer.

03
Scope fixed and quoted up front

You get deliverables, timeline, and cost agreed before any work starts.