Skip to content

Case study

OpsKwan

SaaS logistics for medical device distribution, deployed with Zimmer in 2011, tracking consignment, loaner sets, recalls and lots for implants.

Client
OpsKwan Technologies
Dates
2009 to 2024
of Canadian hospitals, for Zimmer Biomet's logistics
40%+
commits on the shared platform, all mine, 2011 to 2023
1,641

The problem

An orthopaedic implant distributor places its stock on consignment in hospitals: implants and instrument sets that sit in a hospital’s storeroom until a surgery uses them. Every item has a catalogue number, a lot and an expiry date, and the distributor has to know where each one is, what was used in which case, what’s on loan, and, when a manufacturer recalls a lot, which hospitals are holding it.

The work happens in storerooms. A sales rep counts stock with a barcode scanner, often with no connection, and the labels don’t agree with each other: each manufacturer encodes the same facts its own way.

My role

Director of Development at OpsKwan from 2009 to 2024. I wrote the platform’s shared code, every commit in it from 2011 to 2023: the models, the barcode handling, the manufacturer imports and the offline audits.

What I built

  • Consignment and loans. Hospitals, stock down to lot and expiry, transfers, loaner sets, surgery bookings and what each case used, with sterilisation and, later, biologics tracked back to their donors.
  • Manufacturer imports. Each manufacturer’s ERP export arrives in its own format. Importers for Zimmer, DePuy and Wright load them into the client’s database; Zimmer’s is turned into bulk SQL each day, with the results mailed out.
  • One database per client. Each distributor gets its own MySQL database beside a shared master database, and the platform builds a new client’s tables itself.
  • Recalls. A recall list is imported, by product and lot, and a recalled lot is caught at the moment of scanning, online or offline.

Architecture

A CakePHP application over MySQL, with its models, behaviors and drivers shared between the apps the company and its distributors used. The front end ran in the browser on tablets and phones, with scanners that type into the page; there was no native app.

OpsKwan architectureSales reps scan implant labels with hand-held scanners into a browser on a tablet or phone. The browser parses each HIBC or GS1 barcode itself, checks it against the product and recall lists the page loaded, and keeps the scans in IndexedDB, so an audit and its recall check work offline and sync later. Online, scans go to the CakePHP application, whose shared models parse the same barcodes on the server. Recall lists are imported into the application. Importers load manufacturers' ERP exports, Zimmer's as bulk SQL each day. The application uses a shared master database and one MySQL database per client, which the importers load.Scanner + browseroffline parser · IndexedDBRecall listsproduct or lot · CSVManufacturer ERPsZimmer · DePuy · WrightplatformCakePHP applicationshared models · server-side barcode parserImportersone per manufacturerMaster databaseMySQL · sharedOne database per clientMySQL · stock · lots · loans · recallslists
One shared code base, a master database and one database per client. Scans are parsed in the browser as well as on the server, so an audit and its recall check work without a connection.

The hard part

Reading implant labels in a storeroom with no signal

A hand-held scanner acts as a keyboard: it types the barcode into whatever field has focus. That’s simple to deploy and hard to get right, because the barcode is the data.

The labels come in several forms. HIBC, the health industry’s barcode, puts a labeller code, catalogue number and unit of use in a primary symbol, and the lot and expiry in a secondary one, either joined to the primary or printed as a second barcode. The expiry is a Julian date, year and day of the year. GS1 labels carry the product number first, then the lot and expiry under numbered identifiers, in either order. Some older labels followed neither standard, and OpsKwan printed its own.

The server’s parser handles all of them, and I wrote a second one in the browser, for HIBC, GS1 and the simplest layouts, so audits work without a connection.

  • Characters lost in transit. HIBC uses +, / and $, and a + in a form post arrives as a space. Each scan was sent as character codes and rebuilt on the server.
  • Two-symbol labels. A batch of scans is split by line and the symbols are paired, in either order. The secondary carries a link character that has to match the primary’s check character, and a scan that doesn’t match is rejected rather than guessed.
  • Finding the product. By catalogue number, then without leading zeros, then through a cross-reference table for numbers that differ between a manufacturer and a distributor.
  • Offline. In 2013 audits moved into the browser: each scan is parsed there, checked against a preloaded product list, and stored in IndexedDB until it syncs.
  • Recalls at the scanner. The recall list goes to the device too, and a recalled lot plays a warning sound instead of the usual one, online or not.

Two copies of a parser can drift. In 2019 I fixed a bug in the browser’s GS1 parser, where a lot read after the expiry kept the identifier’s digits; the server’s copy got the same fix eleven months later. It’s the clearest lesson in the code base: one parser, shared, beats two that agree most of the time.

Outcome

  • Deployed with Zimmer, now Zimmer Biomet, in 2011, it ran their logistics for over 40% of Canadian hospitals.
  • Thirteen years of the shared platform, from 2011 to 2023, every commit mine.
  • Offline audits with recall checks at the scanner, built in 2013.

Tell me what you're building