MobiSkan WMS
MobiSkan WMS is the server-side counterpart to the MobiSkan Android application. It turns individual CipherLab data collectors into one coordinated warehouse system with a central catalogue, shared stock state, browser-based administration and controlled synchronisation. It is a standalone warehouse management platform, not an ERP connector wrapped in an admin panel, built for multiple users, multiple collectors and multiple warehouses working against one source of truth.
Stack and structure
- Backend: Node.js, TypeScript, NestJS. A generated Swagger schema documents the API so integrations don't need to reverse-engineer network calls.
- Database: PostgreSQL with Prisma owning the schema and migrations. UUID primary keys, fixed-precision numeric types for money and quantities, timezone-aware timestamps, and Row Level Security enforced as a second access boundary below the application layer.
- Frontend: React, shadcn/ui, Tailwind CSS, consuming TypeScript contracts shared with the API through
packages/shared-types. - Storage: product images and attachments in MinIO through an S3-compatible interface, so moving to AWS S3 later is an infrastructure change rather than a code change.
- Auth and security: JWT sessions with tenant context, Argon2 password hashing, scoped and revocable API tokens for device pairing, offline ECDSA P-256 license verification bound to a per-installation instance ID.
- Testing: Jest for the backend, Vitest and React Testing Library for the panel, JUnit for the Android collector. The synchronisation milestone shipped with 603 backend tests and 2,575 Android tests.
- Repository layout: a monorepo with
apps/apifor NestJS services and controllers,apps/adminfor the React panel, andpackages/shared-typesfor shared request/response contracts.
How it's built
The domain model is organised around a Goods Base (a per-warehouse product catalogue and stock register) and a set of operational document modules: Goods Receipt, Goods Issue, Internal Transfer, Internal Issue, Internal Return, Inventory Count, Order Picking, Data Collection and Customer Orders. Each module keeps its own business semantics (a receipt or issue changes stock immediately, a transfer can defer its effect until the document is closed), while a single StockMovementService centralises every quantity change, stock history entry and location update so no controller duplicates that arithmetic.
Multi-tenancy is built into the data model rather than added as a routing layer. Every operational record is scoped to a tenant, warehouses sit below that tenant, JWTs carry tenant context alongside user identity, and PostgreSQL Row Level Security backs up the application-level scoping, so a missing filter in a query doesn't automatically become a cross-tenant data leak.
The Android collector is treated as a first-class client rather than a thin form. Devices pair through their own API token, separate from the operator's browser session, so device identity and human identity stay distinct in the audit trail. The collector can download settings, scanner configuration, the full Goods Base mirror and open documents before an operator even signs in, queue local writes through module-specific outboxes, and push them once connectivity returns. Catalogue sync is asymmetric on purpose: the server pushes authoritative prices, VAT, units, locations and images, while the device can only submit newly scanned products without overwriting established master data.
Configuration replaces branching. Field visibility, order and length per module, collector behaviour (login requirements, screen wake, default quantities, scan vibration, UPC-A to EAN-13 conversion), and weighted-barcode parsing (prefixes, digit segmentation, value divisor) are all admin-configurable and synced to paired devices rather than hard-coded. A warehouse configurator extends the same idea to physical space: racks and zones placed on a 2D plan, mapped to storage locations, exportable and importable as JSON.
Licensing follows the same offline-first principle as the rest of the platform: a license is an ECDSA P-256 signed blob bound to the installation's instance ID, verified entirely on the server with no call to a vendor endpoint, supporting a signed 14-day demo and device limits without a mutable "is licensed" flag anywhere in the customer's database.
Deployment is staged deliberately: Docker Compose for repeatable on-premises installs first, a native installer for non-technical customers next, and AWS infrastructure for the hosted multi-tenant variant later. External ERP connectors stay a separate integration layer rather than a dependency of the WMS domain itself.

















