Case study · Web application

Two thirds of orders placed without a phone call

A national industrial supplier was taking most of its orders by email and phone into a shared mailbox, where two administrators re-keyed them into the ERP by hand. We built an authenticated portal where each customer prices, places and tracks orders against their own contract, with credit limits enforced server-side and every approval written to an audit trail. The re-keying queue is gone, and order status is no longer something customers have to ask for.

Client · National industrial supplier Sector · Industrial supply & distribution Engagement · Discovery, build, integration, support Duration · Nine months to first release Status · In production since mid-2024 Delivery model · Fixed-price build, monthly support

Before the portal, ordering was an inbox. Requests arrived as emails with a PDF order form attached, as phone calls to whichever branch the customer had always dealt with, and as a handful of faxes from accounts that had never been asked to change. Everything landed in one shared mailbox that two administrators worked through in arrival order, re-keying each line into the ERP: account code, part number, quantity, then a price looked up in a contract schedule that lived in a spreadsheet maintained by the pricing team. Roughly one order in sixteen carried a line error, and the error usually surfaced at pick time rather than at entry.

We delivered a customer-facing portal built over the existing ERP rather than around it. Customers see their own catalogue with their own contract pricing, place and amend orders, route them through their internal approval chain, download statements, invoices and compliance certificates, and follow a delivery from depot to site. Orders are validated against live credit position at submission and written back to the ERP as native sales orders, so the branch keeps working in the system it already knows. Site administrators manage their own users without raising a ticket with us or with the supplier.

Engagement facts

Customer accounts412 onboarded
Contract-priced SKUs18,600
Identity providers3 (SAML / OIDC)
ERP integrationRead replica + write-back
ERP write-backp95 under 90 seconds
Accessibility targetWCAG 2.1 AA
Data residencyAWS ap-southeast-2
Release cadenceFortnightly
SupportAWST business hours + on-call

01The problem

Customers could not answer basic questions themselves. A request for a statement copy, an invoice from four months ago, or confirmation that a pallet had left the depot all became a phone call to a branch administrator who then went looking through the ERP or a shared drive. Delivery status was reconstructed by ringing the freight provider and relaying the answer by email. During busy periods the same questions arrived several times a day from the same accounts.

Credit-limit approvals were the slowest part of the process. An order above a customer's remaining limit was held, then chased by email across sales, credit and sometimes the customer's own finance team. Approvals were given verbally or in reply to a thread, and once the order was released there was no durable record of who had authorised it, when, or against what credit position. When a customer later disputed a release, the reconstruction was done from memory and mailbox search.

  • Four intake channels, all converging on one shared mailbox
  • Two administrators re-keying every order line into the ERP
  • Contract pricing looked up manually from a spreadsheet schedule
  • Statements and certificates sent on request, one email at a time
  • Credit-limit releases with no auditable approval chain
  • No self-service view of order or delivery status

02Constraints and non-negotiables

The ERP stayed. The supplier had no appetite and no budget to replace it, and the vendor's position was explicit: we could have a read-only replica of the transactional database, and we could write back through a supported API, but we could not write to the tables directly. Every design decision downstream followed from that, including the fact that the portal had to tolerate a replica that was minutes behind rather than seconds.

Three of the largest customers mandated single sign-on against their own identity providers as a condition of continued business, which removed password-only authentication as an option. Credit limits had to be enforced on the server, never in the browser, because the portal would be the first channel where a customer could place an order unattended. Accessibility was contractual: WCAG 2.1 AA, verified rather than asserted. All customer data had to remain in Australia. And the existing phone and email channels had to keep working throughout, because a failed portal launch could not be allowed to stop orders arriving.

  • No direct writes to the ERP database; supported API only
  • Read-only replica with variable replication lag
  • Server-side credit enforcement, no client-side trust
  • SSO required by three enterprise customers
  • WCAG 2.1 AA as a contracted acceptance criterion
  • Australian data residency for all customer records
  • Telephone and email ordering retained in parallel

03What we built

The portal is a single authenticated application with four working surfaces. Ordering presents each customer's own catalogue with their contract pricing, quantity breaks and substitution rules, and accepts orders with a delivery address drawn from their account rather than typed freehand. Approvals route an order through the customer's own chain before it reaches the supplier, with limits and delegates configured by the customer's administrator. The document centre serves statements, invoices, credit notes and product compliance certificates, generated on demand and downloadable in bulk. Delivery tracking consolidates consignment events from the freight provider into one status per order line.

Behind those surfaces sits an integration service that validates, prices and writes the order into the ERP as a native sales order, then reports the ERP order number and any credit hold back to the portal. Customer-side user administration means an account administrator can add a colleague, set a spend limit and assign an approval role without contacting the supplier or us. Everything a user does to an order — create, amend, approve, decline, cancel — is written to an append-only audit record with actor, timestamp, source IP and the credit position that applied at the time.

04The hard parts and how we solved them

Pricing was the first serious problem. Contract terms were not a single discount: they combined a schedule discount, product-group overrides, volume breaks and promotional prices with expiry dates, and the ERP evaluated them in an order nobody had written down. Rather than re-implement that logic, we built a price resolution service that calls the ERP's own pricing routine per line, caches the result against a version key, and stores the resolved price as a snapshot on the order at submission. The portal never computes a price the ERP would disagree with, and if the ERP later re-prices differently, the discrepancy is reported rather than silently absorbed.

The approval workflow needed to be a real state machine, not a status column. Orders can be split, so part of an order can be approved while the remainder waits; approvers go on leave, so delegation had to be time-bounded and revocable; and an approval given against one credit position is not valid against another. We modelled it as explicit transitions with guards, and made every transition conditional on a re-check of credit at the moment of release. Double submission was handled with an idempotency key generated at cart level, so a resubmitted form returns the original order rather than creating a second one.

Two further problems were less visible but mattered more in production. One identity provider issued a transient NameID, which broke the link between sessions and portal users after each re-authentication; we mapped on a persistent attribute instead and stored the federation identifier separately from the local account. And because the portal reads from a replication-lagged replica, displaying a balance that was two minutes stale was unacceptable for credit decisions, so credit-affecting reads go to a lag-aware path that either returns a confirmed value or says plainly that the figure is not current.

05Migration, cutover and change management

Nothing was switched off. We onboarded in cohorts, starting with twelve accounts that had volunteered for a pilot and placing high-spend, low-complexity customers first so that the largest share of volume moved through the portal early without the most complicated contracts being involved. Each cohort received a short onboarding pack, a thirty-minute session with their administrator, and four weeks in which phone and email ordering remained fully available while portal ordering was reconciled against it.

Documents were backfilled before any login was issued: five years of statements and invoices were extracted from the ERP and the document archive, matched to accounts, and indexed, so the first thing a new portal user saw was their own history rather than an empty screen. Access was migrated account by account, with administrators confirming their own user lists. The two administrators who had been re-keying orders moved to exception handling and contract maintenance, and for the first two months sat next to the queue that had replaced their old mailbox.

06Results, and what we would do differently

Twelve months after the first release, 61 per cent of order value is placed through the portal without anyone at the supplier typing it in. Around 27 staff hours a week that previously went to re-keying now go to exception handling and pricing maintenance. The median time from order submission to credit release fell from 3.1 days to under six hours, mostly because approvals are now routed to a named person with a delegate rather than to a mailbox. Inbound calls asking for order or delivery status dropped by 44 per cent.

One thing went wrong. Six weeks after launch, two large accounts intermittently saw "pricing unavailable" on submission. The cause was our own price cache: it was invalidated using an updated-at timestamp read from the replica, and during a failover that timestamp moved backwards, so fresh prices were treated as stale and discarded. We replaced timestamp invalidation with a monotonic version counter, added a guard that refuses to serve credit and pricing figures when replica lag exceeds a threshold, and added a synthetic check that places a priced order against a test account every fifteen minutes. That check has caught two regressions since.

What we would do differently is mostly about sequencing. We treated contract pricing as configuration late in the build and should have modelled it as data from week one, because the pricing exceptions were the single largest source of rework. We would also have built the customer onboarding pack before the pilot rather than during it, since the first twelve accounts all asked the same four questions.

61%
Of order value self-served
27 hrs
Order entry effort removed weekly
5.8 hrs
Median credit release, was 3.1 days
44%
Fewer inbound status calls
Customer operations team reviewing portal order queues on a shared screen
We stopped being a call centre for order status. The part I did not expect to value most is the audit trail — when a release is disputed we can show who approved it, at what time, and against which credit position, without opening the mailbox.
IT Manager, national industrial supplier
Delivery notes

Stack and delivery notes

The portal runs on AWS in the Sydney region, deployed from Terraform with separate staging and production accounts, and releases every second Wednesday in a window agreed with the supplier.

.NET 8 ASP.NET Core React TypeScript PostgreSQL Redis SAML 2.0 OpenID Connect Hangfire Terraform AWS ECS CloudWatch Playwright
Related work

Other delivered systems

Each of these ran as a separate engagement with its own scope and constraints. If your problem resembles one of them, the detail page explains what actually happened. Request a quote if you would rather talk about your own situation first.

Field Service Operations Platform

Scheduling, job capture and asset history for a WA maintenance contractor replacing paper dockets and three spreadsheets.

ERP & CRM Integration Hub

Fourteen point-to-point connectors consolidated into one governed integration layer with reconciliation reporting.

Legacy Platform Cloud Migration

A fifteen-year-old on-premise application moved to AWS in four stages with parallel running and a tested restore.

Inventory & Warehouse System

Real-time stock control across four distribution sites, built on an append-only movement ledger with offline scanning.

Have a process that still runs through an inbox?

A 45-minute scoping call, no obligation. We will tell you honestly whether a portal is the right answer.

Request a Quote