Delivered remotely for a business based in Portland, United States. Names withheld by agreement.
Project Overview
Industry: Equipment and commercial vehicle dealerships — parts, service, rental and fleet management.
Type of solution: A family of mobile applications over a shared backend that integrates with an established core dealer management platform: a cross-platform customer application, a native staff operations application, and a companion application supporting controlled enterprise distribution.
Business context: Dealerships in this sector run on mature core systems that work well for what they were designed to do and were never designed for mobile access. The consequence shows up at both ends of the business. Customers cannot check whether a part is in stock without calling during opening hours. Staff performing stock counts work on paper or at fixed terminals, then re-enter what they wrote down. Both problems are obvious, and both have historically been blocked by the same assumption — that solving them means replacing the core system, which no dealership wants to do.
General users: Dealership customers who own or operate equipment fleets, dealership staff performing inventory and receiving operations, and the dealer organisations whose data scopes what customers see.
General purpose: To give customers self-service access to parts, service, rental and their own fleet, and to give staff a mobile tool for warehouse and yard work — both integrating with the existing core system rather than replacing it.
The Business Challenge
Customers had no self-service route. Checking parts availability, pricing, service options or rental stock meant a phone call to the counter during business hours. That is a poor customer experience and an expensive one for the dealership, because every routine enquiry occupies a person.
Parts pricing is not general e-commerce. The trade carries pricing components that ordinary retail does not, applied per item. Display them incorrectly and the quoted price is simply wrong — which erodes trust in the entire channel on the first order.
Inventory work happens where there is no signal. Stock counts take place in metal-framed warehouses and open yards. An application that assumes a live connection is unusable exactly where the work happens.
Re-keying is where errors enter. Counting on paper and typing the results into a terminal afterwards introduces transcription errors into the inventory record that then take longer to find than the count took to perform.
Scanning throughput compounds. During a stock count the same interaction repeats hundreds of times. A second of friction per scan is not a minor annoyance — it determines whether the tool gets used or abandoned.
Order baskets are assembled over time. A parts order is often built across several sessions, and losing it to a dropped connection or a closed app means starting again.
The core system could not be replaced. It runs the dealership. Any mobile capability had to work with it — including where its interfaces predate current conventions.
Staff devices are not personal phones. The staff application is distributed to dealership-owned devices outside a public app store, which raises its own access and distribution questions.
Our Approach
Extend outward, don't replace. We treated the core dealer management system as fixed and built mobile capability around it. Where modern services existed we consumed them; where only an older interface was available, the client speaks that interface directly rather than waiting for a middleware layer that would have delayed the whole project.
Make the customer basket local first. The parts cart is held in an on-device relational database behind a repository abstraction, so assembling an order does not depend on connectivity and nothing is lost to a dropped connection or a closed application. The repository wrapper keeps the storage implementation replaceable rather than spreading database calls through the screens.
Model trade pricing properly, per line. The pricing components specific to this trade are first-class fields on every basket line rather than an adjustment applied at checkout, so the customer sees the true price of each item throughout — not a figure that changes at the end.
Build scanning into the operation, not alongside it. The staff application bundles its own scanner module rather than depending on an external component, giving direct control over scan behaviour and throughput in the operation where repetition makes that control matter.
Share the transaction pattern across operations. Inventory counting and parts receiving are different operations with the same underlying shape — authenticate, scan, accumulate, submit. A shared transaction abstraction implements that once, so both operations behave identically and new operations are inexpensive to add.
Match authentication to how staff actually work. Warehouse staff open the application repeatedly in short bursts on dedicated devices, so passcode entry replaces full credential entry — fast enough to use continuously while still gating access.
Separate feature APIs. The customer application uses distinct API modules for dealer information, availability and transactions rather than a single client, so each concern evolves without disturbing the others.
Type where correctness matters. The data and API layers were written in a typed language while screens remained untyped — incremental typing focused on the layers where a mistake is expensive rather than a rewrite for its own sake.
The Solution
Dealer directory. Customers browse dealers with individual dealer pages and integrated directions, so finding the right location is part of the app rather than a separate task.
Parts search and ordering. Parts are searched with autocomplete and filtering suited to the partial, alphanumeric part numbers the trade actually uses, with full descriptions, vendor information and weight available before ordering.
Offline-capable parts basket. The basket is held in an on-device database, so an order can be assembled across sessions and survives connectivity loss, with trade-specific pricing components shown per line throughout.
Equipment inventory browsing. Customers view equipment available from the dealer, turning a phone enquiry into a self-service lookup.
My Fleet. Customers see their own equipment in one place, which is the anchor for everything else — parts, service and rental all relate to machines they already own.
Service department access. Service information and requests handled in-app rather than by phone.
Rental services. Rental availability and enquiry, extending self-service to the third major dealership revenue line.
Special deals. Promotional offers surfaced to customers directly rather than depending on outbound contact.
Document viewing. In-app PDF rendering for the documentation this trade generates constantly.
Staff yard and warehouse inventory. Dealership staff count equipment units and parts stock on a mobile device with integrated barcode scanning, capturing directly rather than on paper.
Staff parts receiving. Inbound parts orders are received against the system on the device, at the point where the goods physically arrive.
Passcode-secured staff sessions. Short-form authentication matched to repeated warehouse use on dealership-owned devices.
Controlled enterprise distribution. The staff application is distributed outside public app stores with a verification mechanism governing authorised use.
Key Features
Offline-capable parts basket with local database persistence The basket lives in an on-device relational database behind a repository abstraction, so orders assembled over days or through connectivity loss are never lost.
Trade-specific pricing shown per line Pricing components particular to this trade are modelled as first-class fields on every basket line, so the price shown per item is the true price rather than one that changes at checkout.
Parts search built for trade part numbers Autocomplete and filtering designed around partial, alphanumeric identifiers rather than natural-language product names.
Integrated barcode scanning with a bundled scanner The staff application includes its own scanner module rather than depending on an external component, giving direct control over throughput in an operation repeated hundreds of times per count.
Shared transaction abstraction across staff operations Inventory counting and parts receiving share one underlying transaction pattern, so behaviour is consistent and new operations are inexpensive to add.
Legacy and modern interface consumption in one client The staff application consumes both current JSON services and an older interface format directly, so mobile capability was not blocked waiting for the core system to change.
My Fleet as the customer anchor Customers see their own equipment first, with parts, service and rental all relating back to machines they actually own.
Self-service across parts, service and rental All three major dealership revenue lines accessible without a phone call, during or outside business hours.
Passcode authentication for warehouse use Short-form session entry matched to how staff use dedicated devices — repeatedly, briefly, with hands busy.
Controlled enterprise distribution The staff application is distributed outside public app stores with a verification mechanism governing authorised use on dealership devices.
Technical Architecture
Customer application. A cross-platform application separating screens from a dedicated network layer, feature-specific API modules for dealer, availability and transaction concerns, a local database layer accessed through a repository abstraction, centralised state management, and a shared component library. Navigation composes stack, tab, drawer and swipeable top-tab navigators.
Local persistence layer. An on-device relational database holding the parts basket, wrapped in a repository so the rest of the application depends on an interface rather than on storage specifics.
Staff application. A native application built around a base transaction abstraction, with concrete implementations per operation — unit inventory, parts inventory and parts receiving — sharing scanning, session and submission behaviour. A barcode scanner module is bundled in-repository rather than taken as an external dependency.
Integration layer. Both applications reach the client's backend, which in turn integrates with the established core dealer management platform. The staff application additionally carries multiple serialisation formats so it can consume both current services and an older interface directly.
Licensing companion. A minimal separate application performing cryptographic verification, supporting controlled distribution of the staff application outside public app stores.
Supporting integrations. Mapping and directions, native mail composition, document rendering and file handling.
Flow: Customer app (local basket database) → Feature API modules → Backend → Core dealer management system ← Staff app (barcode scanning, transaction abstraction, dual-format interface consumption)
Technology Stack
| Category | Technology |
|---|---|
| Customer app framework | React Native with React 19 |
| Customer app state | Redux |
| Customer app navigation | React Navigation (stack, bottom tabs, drawer, material top tabs) |
| Customer app networking | Axios with a dedicated network layer and feature-specific API modules |
| Customer app local storage | On-device SQLite with a repository-pattern wrapper |
| Customer app typing | TypeScript in data and API layers |
| Customer app components | Autocomplete, search filtering, multi-select, range sliders, pickers, masked and confirmation-code inputs, collapsible sections, action sheets, image slider |
| Documents & files | PDF rendering, blob file handling, native mail composition |
| Maps | Directions integration via platform maps |
| Staff app language | Java (Android) |
| Staff app networking | Retrofit with JSON, scalar and XML converters; OkHttp logging |
| Staff app scanning | Bundled in-repository barcode scanner module |
| Staff app UI | AndroidX, Material Components, ConstraintLayout, density-independent sizing libraries, passcode entry component |
| Licensing app | Kotlin with public-key cryptographic verification |
| Build tooling | Gradle, with Kotlin DSL on the newer module |
Technical Challenges & Solutions
| Challenge | Our Approach |
|---|---|
| A core dealer management system that could not be replaced | Mobile capability built outward around it — consuming current services where they existed and an older interface format directly where they did not, so delivery was never blocked waiting for the core platform to change. |
| Inventory work happening in warehouses and yards with no signal | Operations designed not to assume live connectivity, with the customer basket held in an on-device database so order assembly and capture continue regardless of connection state. |
| Losing a parts order to a dropped connection or closed app | The basket persisted in a local relational database behind a repository abstraction, so an order assembled over several days survives everything short of uninstalling the application. |
| Trade pricing components that general e-commerce does not model | Those components modelled as first-class fields on every basket line rather than as a checkout adjustment, so the price shown per item is the price charged. |
| Scanning friction compounding across hundreds of repetitions | A scanner module bundled in-repository rather than taken as an external dependency, giving direct control over scan behaviour in the operation where repetition makes it decisive. |
| Two staff operations with the same underlying shape | A shared base transaction abstraction implementing authenticate–scan–accumulate–submit once, so inventory counting and parts receiving behave identically and new operations are cheap to add. |
| Warehouse staff needing fast, repeated access | Passcode entry rather than full credentials for staff sessions, matched to short repeated use on dealership-owned devices. |
| Distributing a staff application outside public app stores | A separate companion application performing cryptographic verification, keeping licensing concerns isolated from the operational application and allowing verification without a network round trip. |
| Searching large parts catalogues by trade part number | Autocomplete and filtering built around partial alphanumeric identifiers rather than natural-language product search. |
Security & Reliability
Authenticated API access. Token-based authorisation across both applications, with full credential flows — registration, password creation, reset and verification — in the customer application.
Passcode-gated staff sessions. The staff application uses short-form passcode entry appropriate to shared or dedicated dealership devices used in repeated short sessions.
Controlled distribution. The staff application is distributed outside public app stores with a cryptographic verification mechanism governing authorised use, kept in a separate companion application rather than embedded in the operational one.
Local data integrity. The basket database is accessed exclusively through a repository abstraction, so storage behaviour is consistent and centrally controlled rather than scattered across screens.
Resilience to connectivity loss. Local persistence means a lost connection degrades the experience rather than destroying work in progress — the central reliability property for both warehouse and customer use.
Scoped API surfaces. Feature-specific API modules keep each concern's contract separate, so a change in one area cannot inadvertently affect another.
Core system as system of record. The applications are channels over the core platform rather than parallel systems of record, so authoritative data remains in one place.
Scalability & Performance
Local-first basket operations. Cart reads and writes hit the on-device database rather than the network, so basket interaction is instant regardless of connection quality.
Scoped API modules. Feature-separated API clients keep payloads bounded to what a given area needs rather than fetching broadly.
Cached image loading. Image loading with caching on both applications, necessary for parts and equipment catalogues that are heavily illustrated.
Client-side filtering and autocomplete. Search narrows results incrementally rather than repeatedly round-tripping for every keystroke across a large catalogue.
Density-independent layout on the staff application. Scalable sizing libraries keep the interface consistent across the wide and often older device range found in warehouse deployments.
Bundled scanning. A locally controlled scanner module avoids the overhead and version dependency of an external scanning service in the highest-frequency operation.
Thin clients over the core system. With the core platform holding authoritative data, the applications stay light and scale with backend capacity.
Business Outcomes
- Customers serve themselves, checking parts, service, rental and their own fleet without phoning the counter during business hours.
- Routine enquiries stop occupying staff, freeing the parts counter for work that genuinely needs a person.
- Quoted prices are correct, because trade-specific pricing components are shown per line rather than appearing at checkout.
- Orders survive interruption, with the basket persisted locally across sessions and connectivity loss.
- Stock counts are captured once, at the point of counting, removing the paper-then-re-key step where transcription errors enter.
- Inventory work is possible where it happens, in warehouses and yards without reliable signal.
- Staff tooling is consistent, with counting and receiving sharing one transaction pattern rather than behaving differently.
- The core system was never at risk, because every capability was delivered as a channel over it rather than a replacement for it.
Why it worked
The instinct when a core business system cannot do something modern is to talk about replacing it. In sectors like this that conversation has been happening for a decade and the system is still running, because replacement is expensive, risky and disruptive to a business that cannot stop trading. The useful engineering question is different: what can be built around it, and how do you integrate with interfaces that predate current conventions without pretending they do not exist?
Our team works that way by default. Where a modern service existed we consumed it; where only an older interface was available we spoke it directly rather than blocking delivery on middleware that would have taken longer than the project. We built for the physical conditions the work actually happens in — local-first data so a warehouse with no signal is not a blocker, bundled scanning so the hundredth scan is as fast as the first, and passcode entry because staff open the app constantly with their hands full.
Our teams work across cross-platform and native mobile, local database design, legacy system integration including older serialisation formats, and controlled enterprise distribution. Just as importantly, we take the time to model a sector's real commercial structure — including the pricing components that only exist in that trade — because getting those wrong is how a new customer channel loses trust on its very first order.
Final Summary
Equipment and commercial vehicle dealerships run on core systems that predate mobile entirely. The cost shows at both ends: customers phone the parts counter to ask questions a screen could answer, and staff count stock on paper before re-keying it into a terminal. Both are long-standing problems, and both have been blocked by the assumption that fixing them means replacing the system the dealership runs on.
Our team built mobile capability around that core system instead. Customers get self-service access to parts, service, rental and their own fleet, with a parts basket held in an on-device database so an order assembled over several days survives connectivity loss — and with the trade's specific pricing components shown per line, so the price quoted is the price charged. Dealership staff get a native application for yard and warehouse work with integrated barcode scanning, capturing counts and receipts at the point the work happens rather than transcribing them afterwards.
The integration was pragmatic where it needed to be: the staff application consumes current services and an older interface format directly, so mobile delivery was never held up waiting for the core platform to change. A shared transaction abstraction gives counting and receiving identical behaviour, a bundled scanner module keeps throughput under direct control in the highest-frequency operation, passcode entry matches how warehouse staff actually use a device, and a separate companion application handles verification for distribution outside public app stores. The result extends a system the business depends on, without asking the business to risk it.