Case study · Enterprise warehouse inventory · Foresee Solutions
SmartScan
Every tag, accounted for.
RFID middleware that sits between Android handhelds and any ERP, starting with NetSuite. It tracks every physical unit by its tag, runs the warehouse's floor workflows, and posts only finished, consistent transactions to the ERP.
- Role
- Freelancer · end-to-end ownership
- Client
- InnovateNex, for Foresee Solutions
- Timeline
- Jun – Sep 2026 · live in production
- Surfaces
- REST API · Admin web · Android · AWS
API endpoints
Postgres tables
Permission modules
Handheld workflows
The problem
The floor moves faster than the ERP.
A handheld reader picks up dozens of tags a second. The ERP wants clean, complete transactions, one at a time, and it takes its time to answer. Wire the two together directly and you get timeouts on the floor, quantities typed in by hand, and no record of which physical unit went where.
SmartScan owns the warehouse state in between. Operators scan against it at floor speed. The ERP stays the financial system of record and receives only the finished result.
No typed quantities
A quantity is the count of tags physically scanned. Operators never key in a number.
Nothing gets lost
Not on a dead battery, a dropped Wi-Fi link, or a Redis restart mid-sync.
Not married to one ERP
The core has no ERP-specific code in it. An ERP is a connector class you register.
What I owned
Whiteboard to warehouse floor.
InnovateNex brought me in as a freelancer to deliver SmartScan for their client, Foresee Solutions. I owned the whole of it: the system design, the ERP integration, what shipped, the handheld experience, and the build and deploy.
Architecture
Designed the system before a line of it was written.
- Put the middleware in charge of warehouse state and kept the ERP as the financial record — the call everything else hangs on.
- Chose tag-level tracking over quantities, a Postgres outbox ahead of Redis, and database-enforced locks.
- Made the ERP a pluggable connector, and multi-tenancy a schema per client, so one deployment serves many companies, on any ERP.
Under the hood
Correctness, enforced by the database.
Built for the handheld
Operators live on an Android RFID gun. The app drives the reader through a native module and scopes everything to their warehouse.

One statement, every edge case
A single conditional UPDATE claims the scanned tags. Whatever it doesn't return is rejected, with a reason for each.
UPDATE epc_tags SET state = 'in_stock' WHERE epc = ANY(:scanned) AND item_id = :line AND state = :expected RETURNING epc;
Session locks
One active session per bin or order, via a partial unique index.
Durable outbox
Postgres first, Redis for dispatch, retries with backoff.
19 modules × 3 actions
Granular RBAC. Stock edits are split from catalog edits; force-releasing a lock needs delete rights.
Schema per tenant
New clients are cloned from a template schema.
Survives a dead battery
The scan buffer syncs to the server as a draft.
The life of one tag
EPC E280·6894·A3F1
Scroll to follow a single unit through the warehouse. Every hop is one row in its append-only history.
01 / 06 · CSV import · batch B-0142
Imported
The tag enters the registry bound to one SKU. It exists on paper, not on a shelf — and a re-import can't rebind it once it has history.
epc_tags.bin_history
On the floor
Five workflows, one scanning model.
One scanning model
Every workflow starts the same way — pick a document or a bin, scan, review, complete — and every one is scoped to the operator's warehouse. Learn one, and you know them all.
Back office
When the ERP rejects a sync, you know why.
The admin dashboard is where supervisors map records to the ERP and watch the sync queue. Every failed post carries the exact payload sent and the ERP's response, so an integration dispute takes minutes, not a log search.

Architecture
The middleware is the only thing that talks to the ERP.
The ERP pushes items, bins and orders in over an API key (Flow A). Completed receipts, fulfillments, transfers and count variances go back out through the outbox (Flow B).
Decisions that paid off
The ledger.
Track tags, not quantities
Every quantity is derived from tags that were physically scanned, so typing errors stop existing.
One conditional UPDATE claims the tags
Wrong item, duplicate, already-shipped and two operators racing are all resolved by the database in one statement.
Locks live in the database
A partial unique index allows one active session per bin or order. The second operator gets a 409, not a race.
Postgres outbox in front of Redis
Every ERP job is written to Postgres before it's queued. Losing Redis delays a post; it never drops one.
Keep the payload of every ERP attempt
When the ERP rejects a post, the queue shows what was sent next to what came back — no log-diving.
Schema per tenant, fail-closed
Each client gets its own Postgres schema. A request without a resolved tenant is refused outright.
Stack
API
Admin web
Handheld
Cloud
Honest limits
- One connector so far. The abstraction is proven against NetSuite only; SAP is the next class to write.
- Android only. The reader SDK is Android, so the handheld app is too.
- No inbound quantity sync. If someone edits a stock quantity by hand in the ERP, SmartScan doesn't pick it up — stock is expected to move through the floor, where every change is scanned.