# Retail Pharmacy Platform — Statement of Work v2.0

**Caresoft Systems Private Limited**
**28 August 2026 · Supersedes v1.0**

v1 was written before any code existed. This version replaces it. Where
the two differ, this one is right — not because the thinking improved,
but because eleven modules were built, measured, and in five places found
v1 to be wrong.

Read alongside `pilot-readiness-pack.md`, which covers execution.

---

## 1. What changed from v1, and why

Five corrections. Each cost something to discover, so each is recorded
with its consequence rather than quietly amended.

### 1.1 The incumbent is not a desktop-only company

v1 positioned against "two Windows products". That was out of date before
it was written. Marg runs **MargBooks** (cloud), **Paas Ki Dukaan** (a
consumer ordering app, launched 2021, targeting 2,50,000 chemists), and
**Marg Digital Healthcare** (QR on the bill to consults, labs,
insurance).

**Consequence:** we are not first, and cannot say we are. Our
differentiation is execution and economics, not novelty. The genuinely
useful question is why five years and a far larger channel have not made
Paas Ki Dukaan work. That question is answered by chemists, not by us —
see §8.

### 1.2 MRP is not in the e-invoice schema

Verified against the notified standard, not assumed. `ItemList` carries
`UnitPrice` (the purchase rate), `Qty`, `FreeQty`, `Discount`, `AssAmt`,
`GstRt`, `HsnCd` and — **optionally** — `BchDtls{Nm, ExpDt}`. It carries
**no MRP at all**.

A pharmacy cannot post inward stock without a per-batch MRP: it is what
the counter bills against and what DPCO caps. Batch and expiry are not
mandatory fields, so they frequently arrive empty.

**Consequence:** "zero-touch purchase entry" is not achievable and was
never achievable. The real target is auto-filling the fifteen fields the
standard carries and confirming the two it cannot, on one fast screen.
That is still 40 minutes of typing reduced to 40 seconds — but anyone who
scopes this module as fully automatic has not read the schema.

### 1.3 Migration must not read the incumbent's files

v1 assumed a converter. The constraint is legal, not technical, and it
sharpened the day the incumbent's former sales head joined: a parser for
their file format sitting in our repository is the exhibit a plaintiff
uses when arguing for an injunction.

**Consequence:** the chemist exports from his own licensed copy and
uploads the file. Built and tested — 91% auto-match on a deliberately
messy export, zero rows lost. This is not a weaker product. It is a
cleaner one.

### 1.4 Accounting cannot sit in the counter path

Billing commits at **p95 6.4 ms**. Double-entry posting, GST aggregation
and ledger maths run afterwards, off the event log, by a worker.

**Consequence:** posting is idempotent and restartable by design.
Never put ledger arithmetic between a customer and their slip.

### 1.5 Excel exports needed a real XLSX writer

CSV hands cell interpretation to Excel, which destroys the data a
pharmacy depends on: a 19-digit batch number becomes `4.512E+18` and the
lost digits never come back. A batch number that cannot be read cannot be
recalled.

**Consequence:** a 300-line dependency-free XLSX writer, tested by
unzipping the output and reading the XML. Long numbers intact, leading
zeros preserved, dates on every row, money cells still numeric so SUM
works.

---

## 2. Product definition

Unchanged from v1 and restated because it is load-bearing.

**In scope:** retail chemist shop. Counter billing, batch/expiry
inventory, automated purchase entry, GST and accounting to trial balance,
statutory drug registers, a consumer ordering storefront branded to the
chemist, delivery, a cross-sell catalogue, customer profiles with refill
reminders, and an owner view across stores.

**Out of scope, permanently:** hospital pharmacy (served by Caresoft
HIS), distribution/stockist ERP, manufacturing, per-customer
customisation, and Caresoft taking title to or custody of stock.

**Positioning, in the chemist's words:**

> One login. One customer. One stock. One bill book. Whether he walks in
> or orders online.

Not "modern architecture" and not "cloud". Those are our concerns.

---

## 3. Architecture principles

All ten from v1 survive contact with the build. Two are now proven rather
than asserted.

| # | Principle | Status |
|---|---|---|
| P1 | One schema across counter and cloud | Built |
| P2 | One codebase; surfaces are clients of one API | Built |
| P3 | Masters are cloud-authoritative | Built |
| P4 | Transactions are append-only events | **Enforced by database triggers** |
| P5 | No backdated edits; corrections are new documents | **Enforced by triggers** |
| P6 | No module owns its own database | Held across 11 modules |
| P7 | One customer identity across channels | Built |
| P8 | One bill series, one ledger, both channels | **Proven: 40 counter + 10 online bills, one GSTR-1** |
| P9 | Counter bills with the WAN down, indefinitely | **Proven: 370 events queued, drained clean** |
| P10 | Zero per-customer customisation | Policy — untested until a partner loses a deal |

P10 is the only one still at risk, and it is at risk from us, not from
the technology.

### 3.1 "No sync issues", precisely

The counter holds a local replica, so data moves. What does not exist
anywhere in the codebase is **conflict resolution**: masters have exactly
one writer, transactions are append-only, so there is nothing to merge.
The replication layer is three small tables and contains no merge
algorithm. If a future change requires one, the change is wrong — fix the
data model instead.

---

## 4. Stack

**PHP 8.3 + PDO + MySQL 8 + Bootstrap 5. No frameworks. No Composer.**

This turned out to fit the architecture unusually well. The *same PHP
files* run on the cloud node and the counter node; a single `NODE_ROLE`
config value switches behaviour. Literal single-codebase, not just
single-schema.

Two deliberate exceptions:

- **Counter Helper** — a thin .NET 8 Windows service. Raw printing has no
  managed API; `winspool` is the only route. Zero NuGet dependencies,
  self-contained single file.
- **Operational screens** — the counter, order console, rider app and
  owner dashboard use plain CSS rather than Bootstrap. A full-screen tool
  on a ten-year-old shop PC should not carry a 60 KB framework. The
  storefront and admin pages remain Bootstrap.

---

## 5. What is built

| Module | Verification |
|---|---|
| Phase 0 core schema (30 tables) + immutability triggers | 44 assertions |
| ULID, replication, number blocks, offline resilience | included above |
| Print engine (ESC/P + ESC/POS, template language) | render **0.13 ms** |
| Purchase automation: e-invoice parse, item matching, alias learning | 29 assertions, **79% cold / 100% learned**, zero false matches |
| GSTR-2B reconciliation | 20 assertions, 4 exceptions from 18 documents |
| Counter billing engine: FEFO, MRP-inclusive GST, DPCO, Rx gate | 28 assertions, **p95 6.4 ms** |
| Double-entry accounting, trial balance, P&L, GSTR-1/3B | 22 assertions, trial balance zero across 52 vouchers |
| Migration: import, dry run, opening journal | 43 assertions, **zero rows lost** |
| Consumer orders: lifecycle, Rx gate, geo-fence, split settlement | 33 assertions |
| Report pack + XLSX writer | 23 assertions |
| Messaging + refill engine: consent, quiet hours, opt-out | 27 assertions |
| **Total** | **269 assertions, all passing** |

**Screens:** counter billing, chemist order console, consumer storefront,
setup wizard, delivery rider app, owner dashboard.

**Not verified:** Counter Helper. Written and reviewed, never compiled.
Treat as unproven until §2.3 of the readiness pack is closed.

---

## 6. What remains

### 6.1 Before the pilot

- Counter Helper compiled, run, and field-tested on real hardware
- Catalogue sourced and loaded
- Legal gates closed (readiness pack §2.1)
- WhatsApp templates approved
- Payment aggregator onboarded

### 6.2 Phase 1 completion

- Sale return and credit note documents (Phase 1C deferred these)
- Settlement discounts on credit customers
- Closing-stock journal at period end — without it the P&L is
  structurally wrong, as the test run showed
- Bank reconciliation
- Purchase order and reorder suggestions

### 6.3 Phase 3

- Cross-sell catalogue and merchandising
- Loyalty
- Live-stock enablement, per store, only after months of verified data
- Partner console: onboarding pipeline, commissions, store health

---

## 7. Commercial model

Unchanged in shape; one number now has evidence behind it.

| Line | Model |
|---|---|
| Software subscription | ₹400–700 per counter per month |
| Cross-sell commission | % of the **non-medicine** basket |
| Delivery fee | To consumer |
| Medicine commission | **None, ever** |

The no-fee-on-medicine rule is now structural rather than a policy: drug
lines are flagged non-commissionable at order-request time, and the
chemist sees the arithmetic on his own screen on every order. Chemist
margin on scheduled drugs runs 16–20% with MRP capped under DPCO; a cut
of that is a fifth of his margin and he will either refuse or take the
customer offline for the refill.

**Unit economics remain unsigned** and still gate build approval
(readiness pack §2.5). If contribution margin does not clear cost-to-serve
at a realistic support rate, cut scope. Price is capped by the market.

---

## 8. The open question that matters most

Paas Ki Dukaan has existed for five years, backed by a channel of 800+
support centres, and has not made local-chemist e-commerce work.

We do not know why. Plausible answers — chemists fear disintermediation,
consumers default to aggregators, delivery economics fail below a basket
size, the confirm loop is too slow — imply completely different products.

**Twenty interviews with chemists who were pitched it and declined.**
Cheapest item on the whole plan, and it is the actual specification for
Phase 2. Do it before Phase 2 scope is frozen.

The person best placed to arrange those interviews now works here. Use
him for market judgment — which is exactly what he can lawfully bring —
and nothing else.

---

## 9. Risks

| Risk | Severity | Position |
|---|---|---|
| Counter is not faster than the incumbent | **Critical** | Untested on real hardware. Kill criterion. |
| Custom bill format requests erode P10 | **Critical** | Policy written; will be tested by the first lost deal |
| Litigation arising from the ex-Marg hire | **Critical** | Migration designed around it; counsel review outstanding |
| Purchase automation underdelivers | High | Mitigated — 79% cold, 100% learned, but on synthetic data |
| Consumer adoption fails as it did for the incumbent | High | Unaddressed until the 20 interviews happen |
| Regulatory action on online medicine sale | High | Seller-of-record structure; counsel review outstanding |
| Catalogue data quality | Medium | Provisional-item promotion loop built; source undecided |
| Support load exceeds the model | Medium | Instrumented from day one; weekly review |
| Scope creep into distribution | Medium | §2 is binding |

---

## 10. What is deliberately not being built

- Custom bill formats, for anyone, at any price
- Per-customer code
- A WYSIWYG template designer — it recreates customisation by another route
- Live stock on the storefront, until data quality is proven
- Commission on medicine
- Any parser for a competitor's file format
- A platform-held customer balance — there is no column for it, so it
  cannot be built by accident

---

## Appendix — the three sentences

For the chemist:

> Your shop and your online shop, on one system. Works even when the
> internet doesn't.

For the partner:

> A product you can install in an afternoon and never have to visit
> again.

For us, when someone asks for an exception:

> If a store needs a visit in the next three months, that is a fault in
> the product, not in the shop.
