A warehouse operator running storage for 1–15 clients tracks whose stock is whose, and bills each client for space and handling, by hand. This blueprint turns that gap into a buildable product for a one-developer team and a sellable product for a non-technical commercial founder.
What it is: a web app for a small contract warehouse that stores goods for several client businesses. It keeps every client's stock separately counted, lets warehouse staff log stock coming in and going out from a phone, and automatically prints each client a correct monthly bill for storage and handling — with Malaysian SST already applied correctly.
Who should buy it: the owner or operations manager of a small contract warehouse or logistics-park unit storing goods for 1–15 separate client businesses. Today they likely track this in spreadsheets, one tab per client, and calculate each client's bill by hand at month-end.
Two flows matter most when pitching: how a new warehouse gets started, and the loop they run every single day once they're live.
flowchart LR A[Owner signs up] --> B[Add each client
and their billing rate] B --> C[Import existing stock
via spreadsheet] C --> D{Stock counts
match a physical count?} D -- yes --> E[Go live] D -- no --> F[Reconcile the mismatch
with warehouse staff] F --> D
flowchart LR R[Stock arrives
for a client] --> S[Staff scans it in
on phone] S --> T[(Inventory,
per client)] O[Order comes in
from a client] --> P[Staff picks & packs
scanning each item] P --> T T --> M[End of month] M --> N[Invoice auto-built
per client, with SST] N --> X[Owner reviews & sends]
| Included at launch (P0) | Added once we have paying customers (P1) | Not planned yet (P2) |
|---|---|---|
| Per-client stock separation, so one client never sees or touches another's goods. Receiving and picking logged from a phone browser (no app download). Automatic monthly invoice per client with SST line items. One warehouse, one location. | A proper installable scanning app for faster barcode capture. Multiple warehouse locations under one account. Client self-service portal to see their own stock live. | Smart shelf-slotting recommendations. Optimized pick-path routing for large orders. Integrations beyond QuickBooks-style accounting export. |
9–11 weeks from a confirmed pilot customer to a working MVP in their hands, assuming: the founder works on this full-time, the barcode-scanning flow stays web-based (no native app) for v1, and the pilot customer's stock data is importable from a spreadsheet rather than another system. If any of those assumptions break, add 2–4 weeks.
| Role | Who | Can do |
|---|---|---|
| operator_owner | Warehouse operator / account owner | Everything: manage clients, rates, staff, billing, exports |
| warehouse_staff | Floor staff doing receiving/picking | Scan stock in/out only; no billing or client-rate access |
| client_viewer | The operator's own client (P1, not in MVP) | Read-only view of their own stock and invoices |
Stock never gets mixed up between clients, and the bill writes itself from what actually happened — instead of a spreadsheet and an evening of manual math every month.
One warehouse operator account, their clients and rates, phone-browser receiving/picking tied to each client, and one correct auto-generated SST-compliant invoice at month-end. If a pilot customer can run one real month through this and trust the invoice, the MSP has done its job.
| Priority | Feature | Why this tier |
|---|---|---|
| P0 | Client & rate setup | Nothing else works without this existing first |
| P0 | Per-client inventory segregation | The core promise of the product |
| P0 | Web-browser barcode scan in/out | Camera-based scanning via browser avoids app-store overhead for v1 |
| P0 | Auto-generated monthly invoice, SST line items | This is the feature a customer pays for |
| P1 | Installable PWA for scanning | Faster, offline-tolerant scanning once volume justifies it |
| P1 | Multi-location per account | Only matters once a pilot customer actually operates two sites |
| P1 | Client self-service read-only portal | A trust-building feature, not a day-one blocker |
| P2 | Slotting/pick-path optimization | Needs real usage data to even design correctly — speculative before then |
| P2 | Accounting-system integrations beyond CSV export | Wait until a customer actually asks which system |
A modular monolith on managed infrastructure — there is no load or team-size reason to run separate services here, and every extra moving part is extra 2am pages for a solo developer.
flowchart TB
subgraph Client["Client devices"]
PWA["Phone browser
(scan in/out)"]
Desk["Desktop browser
(owner: billing, setup)"]
end
subgraph App["Next.js app, deployed on Vercel"]
UI[Server-rendered pages + API routes]
Auth[Auth.js session layer]
Jobs["Monthly invoice job
(Vercel Cron)"]
end
subgraph Data["Managed Postgres (Supabase/Neon)"]
DB[(Relational data:
clients, inventory,
movements, invoices)]
Storage[(File storage:
generated invoice PDFs)]
end
PWA -->|HTTPS| UI
Desk -->|HTTPS| UI
UI --> Auth
UI --> DB
Jobs --> DB
Jobs --> Storage
UI --> Storage
| Layer | Choice | Why |
|---|---|---|
| Frontend + backend | Next.js (App Router), one codebase | Server-rendered pages for the desk UI, API routes for the scanning PWA — one deploy, one mental model for a solo dev |
| Database | Postgres via Supabase or Neon | Managed, generous free tier for the pilot phase, row-level security available if ever needed |
| ORM | Drizzle | Typed schema and migrations without an opaque abstraction layer |
| Auth | Auth.js (NextAuth) with email/password or magic link | No need for social login at this customer segment; keep it boring |
| Scanning | Browser camera via the BarcodeDetector API / a JS barcode-reading library in a PWA, not a native app | Ships inside the same web app, no app-store review cycle, works on any staff phone immediately |
| PDF invoices | Server-side PDF generation (e.g. a Node PDF library) triggered by a scheduled job | Runs inside the same app, no separate rendering service |
| Hosting | Vercel | Matches this founder's existing house stack; zero-ops deploys |
The customer-facing journey and onboarding flows are in Document A §A·2. These four cover roles, money, and admin.
flowchart TD
U[Incoming request] --> Role{Which role?}
Role -->|operator_owner| Full[Full access:
clients, rates, staff,
invoices, exports]
Role -->|warehouse_staff| Scoped[Scan stock in/out only]
Role -->|client_viewer, P1| ReadOnly[Read-only: own stock
+ own invoices]
Scoped -.blocked.-> Billing[/Billing & rate endpoints/]
ReadOnly -.blocked.-> Billing
flowchart LR Signup[Operator signs up] --> Setup[One-time RM2,500
setup fee invoiced] Setup --> Active[Account active,
RM349/mo billed] Active --> Grow{Client count
exceeds plan tier?} Grow -- yes --> Upgrade[Move to next
pricing tier] Grow -- no --> Active Active --> Fail{Monthly payment
fails?} Fail -- yes --> Grace[7-day grace period,
email reminder] Grace --> Suspend[Account read-only
if unresolved] Fail -- no --> Active
sequenceDiagram
participant Staff as Warehouse staff (phone)
participant App as App
participant DB as Database
Staff->>App: Scan incoming item for Client A
App->>DB: insert movement(type=in, client_id=A)
Staff->>App: Scan item picked for Client A's order
App->>DB: insert movement(type=out, client_id=A)
Note over DB: End of billing period
App->>DB: aggregate movements + storage-days per client
App->>App: apply client A's rate card + SST
App-->>Staff: (owner) reviews draft invoice
flowchart LR Owner[Operator] --> AddClient[Add new client
+ rate card] Owner --> AddStaff[Add staff account] Owner --> ReviewInv[Review draft invoice
before sending] ReviewInv --> Edit{Dispute or
correction needed?} Edit -- yes --> Adjust[Add a manual
adjustment line, logged] Edit -- no --> Send[Mark sent, export PDF] Adjust --> Send
Multi-tenancy here is two levels deep: the operator is the paying tenant, and each operator's clients are a second, nested scope. Every inventory and billing row must carry both IDs, and every query must filter by both.
erDiagram
OPERATOR ||--o{ STAFF_USER : employs
OPERATOR ||--o{ CLIENT : stores_for
CLIENT ||--o{ RATE_CARD : has
CLIENT ||--o{ SKU : owns
SKU ||--o{ MOVEMENT : tracked_by
CLIENT ||--o{ INVOICE : billed
INVOICE ||--o{ INVOICE_LINE : contains
RATE_CARD ||--o{ INVOICE_LINE : priced_by
OPERATOR {
uuid id PK
text company_name
text subscription_tier
}
STAFF_USER {
uuid id PK
uuid operator_id FK
text role
}
CLIENT {
uuid id PK
uuid operator_id FK
text name
}
RATE_CARD {
uuid id PK
uuid client_id FK
numeric storage_rate_per_pallet_day
numeric handling_rate_per_order
}
SKU {
uuid id PK
uuid client_id FK
text sku_code
text description
}
MOVEMENT {
uuid id PK
uuid sku_id FK
text type
int quantity
timestamp occurred_at
}
INVOICE {
uuid id PK
uuid client_id FK
date period_start
date period_end
text status
}
INVOICE_LINE {
uuid id PK
uuid invoice_id FK
uuid rate_card_id FK
text description
numeric amount
numeric sst_amount
}
| Endpoint | Method | Auth | Request / response |
|---|---|---|---|
| /api/clients | POST | operator_owner | req: {name, rate_card}. res: {client_id} |
| /api/clients/:id/skus | GET | operator_owner, warehouse_staff | res: [{sku_code, description, on_hand_qty}] |
| /api/movements | POST | warehouse_staff, operator_owner | req: {sku_id, type, quantity}. res: {movement_id}. Rejects if type=out and on_hand_qty < quantity |
| /api/clients/:id/invoices/draft | POST | operator_owner only | Triggers aggregation for a period; res: {invoice_id, lines[]} |
| /api/invoices/:id/send | POST | operator_owner only | Locks the invoice, generates PDF, res: {pdf_url} |
| /api/invoices/:id/adjustments | POST | operator_owner only | req: {description, amount}. Appends a logged line, never edits existing ones |
Two distinct contexts: the warehouse floor (phone, thumbs, gloves, moving) and the back office (desktop, billing, setup). Design for both, don't force one UI to serve both badly.
The scan screen is mobile-first and should work one-handed in portrait orientation. The back-office screens are desktop-first but must degrade to usable (not broken) on a tablet, since an owner will likely check things from a phone between warehouse visits.
Sized for one developer, sequenced to get a pilot warehouse live as early as possible rather than building the full spec before anyone uses it.
| Phase | Objective | Key deliverables | Completion criteria |
|---|---|---|---|
| 1 · 2 wks | Foundation | Auth, operator/client/SKU data model, client CRUD, rate card editor | Operator can log in and set up their client list with rates |
| 2 · 3 wks | Inventory core | Movement logging API, stock-level aggregation, per-client segregation enforced end-to-end | A movement for Client A never appears in Client B's counts, verified with a real test, not just by inspection |
| 3 · 2 wks | Scanning UI | Mobile scan screen, browser barcode detection, in/out flow, manual-entry fallback | A real phone, on real warehouse wifi, can log an in-scan and an out-scan end to end |
| 4 · 2 wks | Billing engine | Period aggregation job, rate-card application, SST line calculation, draft invoice review screen | A full month of test movements produces an invoice the operator agrees is correct |
| 5 · 1–2 wks | Pilot hardening | PDF export, CSV spreadsheet import for existing stock, basic error monitoring, backup verification | One pilot warehouse's real historical stock is imported and matches a physical count |
| Stage | Technical founder | Commercial founder |
|---|---|---|
| Discovery | Confirm browser barcode scanning is reliable enough on common staff phones (test before committing to the approach) | Confirm a real warehouse operator feels the billing pain badly enough to want a pilot — not just agrees it "sounds useful" |
| Planning | Finalize the data model and SST calculation rules with an accountant's sign-off | Confirm the RM2,500 + RM349/mo pricing against what a pilot operator will actually pay |
| Development | Build phases 1–5 above | Line up 1–2 pilot warehouses willing to run real stock through the MVP |
| Testing | Fix issues surfaced by the pilot's real stock import and first live month | Sit with the pilot through their first invoice review, collect what felt wrong or unclear |
| Launch | Deploy, monitor the first live billing cycle closely | Convert the pilot to paying, start outreach to the next 2–3 prospects |