ANKPAL: Building a Full GST ERP Mobile App

ANKPAL's GST ERP (purchase and sales cycles, inventory, accounting, GSTR filing, 40+ reports) existed only as a desktop web app, while the people generating the data day to day (warehouse staff, salesmen, owners) are rarely at a desk. None of the underlying business logic was documented anywhere: the behaviour of the web app was the only specification there was.
Correctness is non-negotiable
Accounting software can't be approximately correct. A report off by one rupee is a filing risk. Every figure had to match the web app exactly, on the same company, same financial year.
A client, not a fork
The mobile app consumes the same production API as the web app: no new endpoints, no mobile-only data model, no second source of truth.
Designed for the thumb
The web ERP's wide grids and dense toolbars don't survive a 390px viewport. Every screen was rebuilt as a mobile-first flow, not shrunk to fit.
A native Android and iOS app, plus an installable PWA, covering all 64 modules of the ERP: the full purchase and sales cycle, inventory and batch tracking, accounting vouchers, 40+ reports, and GST/e-Invoice/e-Way Bill filing. Built as one Vue 3 + Capacitor codebase on top of the same production API the web app already uses.
Treat the production web app as the test oracle
Rather than guess at intended behaviour, the live web ERP became the authority. For every feature, both apps were driven side by side against the same QA company (same party, same product, same date), with the full request/response from each diffed field by field against the backend contract. That turned "does the mobile app work?" from an opinion into a measurement, and surfaced a category of bug code review never finds: screens that rendered perfectly, submitted a payload the API accepted with a 200, and silently stored the wrong thing.
Build a shared core, then one feature at a time
The codebase is organised as 64 feature modules sitting on a deliberately thin shared layer (document totals, tax computation, discount normalisation, batch allocation, decimal precision, credit-due-date logic, permission resolution, file download), each existing exactly once. Purchase Order, Purchase Invoice, Purchase Quotation, GRN and Purchase CN/DN share roughly 70% of their behaviour, so pushing that behaviour down meant a single fix to discount round-tripping landed across the entire purchase cycle at once. Delivery was strictly incremental: one feature per branch, one branch per pull request, each verified against the web app before it merged.
Design for the thumb, not the mouse
Data grids became stacked cards with progressive disclosure. Multi-column voucher forms became tabbed flows (party, items, totals), with the item editor as a focused sheet rather than an inline row. Report filter panels became collapsible drawers that remember what was set last time. The information architecture mirrors the web app, so a user who knows one knows the other, but no screen asks for sideways scrolling.
Three problems worth describing
An ORM default that returned empty reports. Several reports came back as a clean, successful, completely empty result: no error, status 200. The backend's query layer defaulted every related-table join to an inner join, so any document whose optional child record happened not to exist (no transport details, no batch) disqualified the entire parent row. The fix was one flag per include; finding it meant recognising an empty report as a data bug wearing a UI bug's clothes.
Edit forms that lost fields they had just displayed. A voucher would save, reload, and then quietly drop a field on the second save: line discounts, scheme selections, transporter details. The create and edit payloads were built by different code paths, and the edit path reconstructed form state from a response whose shape didn't match what it submitted. The fix was a single normalise-on-load / denormalise-on-submit pair per document type, making round-trip fidelity an explicit, tested property, which also caught a genuine enum mismatch that had been silently corrupting a discount type.
Moving financial statement export to the client. Final Account PDF export was assumed to be a server feature; it wasn't. The API returned data only, and formatting lived entirely in the browser. Rather than request a new backend endpoint, the formatting logic was ported into the app, generating documents client-side through a platform-aware download layer (MediaStore on Android, the share sheet on iOS). Export now works offline and costs the backend nothing.
- 64 feature modules shipped from a standing start in six months, as a single maintainable codebase rather than two platform-specific ones.
- Field-verified parity: every voucher and report was reconciled against the production web ERP on identical data before release, not spot-checked, diffed.
- Defects found upstream. The parity process surfaced genuine backend and web-app bugs (silently empty reports, a corrupted discount enum, mis-stored line totals) that had been live and unnoticed, several fixed for all ANKPAL users as a result.
- A shared core that pays forward: tax, totals, discount, batch and permission logic exist once, so new document types now start at roughly 70% complete.
- A ship-ready release pipeline: scripted, signed Android builds with environment-aware QA and production targets.


Discovery
Understand the problem, the users, and the real constraints before any design work starts.
Design
Architecture and UX decisions made deliberately, before a line of production code is written.
Build
Iterative development with regular check-ins, so direction can be corrected early and cheaply.
Test
QA and hardening against real-world edge cases before anything reaches production.
Ship & Support
Launch, then stay involved: monitoring, fixes, and iteration as the product keeps growing.



