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.
A plain inventory management system answers one question for one business: how much stock do I have, and where is it? This product answers a different question, because the warehouse operator is storing goods on behalf of other businesses, not their own:
In short: a normal inventory system answers "how much stock do I have." This answers "whose stock is this, and what do I bill them for storing it" — which is why small-3PL billing software (PackemWMS, Fulfillor, GOIS) is already its own category, separate from plain inventory software, and why none of it is localized for Malaysia.
Bahagian ini meringkaskan produk dan perbezaannya dengan sistem pengurusan inventori biasa, untuk digunakan semasa pitching kepada pelanggan atau rakan kongsi yang lebih selesa dalam Bahasa Malaysia.
Apa dia: aplikasi web untuk gudang kontrak kecil yang menyimpan barang bagi beberapa syarikat pelanggan. Sistem ini mengasingkan stok setiap pelanggan secara automatik, membolehkan kakitangan gudang merekod stok masuk dan keluar terus dari telefon, dan secara automatik menjana bil bulanan yang betul untuk setiap pelanggan — lengkap dengan SST yang dikira dengan tepat.
Siapa patut beli: pemilik atau pengurus operasi gudang kontrak kecil atau unit taman logistik yang menyimpan barang bagi 1–15 syarikat pelanggan berasingan. Pada masa ini mereka kemungkinan besar menguruskan ini melalui spreadsheet (satu tab setiap pelanggan) dan mengira bil setiap pelanggan secara manual pada hujung bulan.
Sistem pengurusan inventori biasa menjawab satu soalan untuk satu perniagaan sahaja: berapa banyak stok yang saya ada, dan di mana ia berada? Produk ini menjawab soalan yang berbeza, kerana operator gudang menyimpan barang bagi pihak perniagaan lain, bukan barang milik sendiri:
Ringkasnya: sistem inventori biasa menjawab "berapa banyak stok yang saya ada." Produk ini menjawab "stok ini milik siapa, dan apa yang perlu saya caj untuk menyimpannya" — sebab itulah perisian pengebilan 3PL kecil (PackemWMS, Fulfillor, GOIS) sudah menjadi kategori tersendiri, berasingan daripada perisian inventori biasa, dan mengapa tiada satu pun daripadanya disesuaikan untuk pasaran Malaysia.
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 |