Photo of Danny Berger
Barcoding Inventory with QR Codes
Post |

Most decently-sized stores will have barcodes on their products. For the store, it makes the checkout process extremely easy and accurate. For the consumer, barcodes might be useful with a phone app to scan them. I needed to make the inventory scannable at the shop, and I really wanted to do it in a more meaningful way than 1D barcodes could support.

Barcodes: 1D vs 2D

There are two different kinds of barcodes: 1 dimensional and 2 dimensional. The 1D allows for a purely linear scan of simple, UPC-like barcodes. While 1D barcodes are extremely commonplace on many products, I dislike them because they can't provide any context.

For example, if I were shopping in Target and scanned a UPC barcode with a regular phone app, it might take me to the Amazon listing first - not necessarily great for Target's business, but it also becomes a completely separate brand channel distracting my thoughts. Another example is when UPCs aren't registered on a product - different retail stores will make up their own internal barcode which isn't helpful at all if I try to scan it.

On the otherhand, 2D barcodes require complex parsing but they can hold much more data. QR codes are one extremely common form of 2D barcodes and they typically encode URLs. With my goal of providing more context, URLs provide just that - not only with a domain name, but an arbitrary path. If somebody scanned an item at our shop, they'd at least get redirected through the shop's website.

One disadvantage that QR codes have compared to 1D barcodes is their size and resolution requirements. All 1D barcodes could theoretically be 1 pixel high, but QR codes must be square. To help ensure a reasonable QR codes, most people will use a URL shortener service - shorter URLs mean simpler QR designs, simpler designs mean the QR code can be read more easily and doesn't need to be large.

Another disadvantage to QR codes is that 2D handheld scanners are significantly more expensive than 1D. Fortunately, many previously-used 2D scanners can be found on eBay for very reasonable prices. Unfortunately, I found that some of the used ones would quickly turn unreliable after a period of time.

Mapping URLs to retail "things"

While inventory was the primary target of barcoding, I really wanted to barcode most things involved with retail workflows (like order receipts). With that in mind I figured I needed to store three properties:

  • insignia - the unique, short identifier (e.g. EyV3chYax)
  • target_ref - the type of "thing" (e.g. inventory or order)
  • target_id - the ID of the "thing" (e.g. 010035EA-9F6D-41A2-97C4-EEB5A3F3034A)

I created a manager which supports three basic operations (internally it uses a map of the different types of "things"):

  • getInsignia($target) - which returns the short identifier/insignia
  • getTarget($insignia) - which returns the application object
  • getResponse($insignia) - which returns an appropriate HTTP response

I created a couple of HTTP endpoints which utilize the manager:

  • /io/{insignia} - which returns the result of getResponse (typically a redirect)
  • /io/{insignia}.png - which returns the QR code image

Then, whenever I want to print a QR code on a document, I just have to do:

<img src="{% raw %}{{ web_insignia_png(transaction) }}{% endraw %}" style="float:right;" />
Your Receipt for Order #{% raw %}{{ }}{% endraw %}

Further, the QR code can be used with a redirecting short domain for even simpler codes:

QR Code

Adding More Context

One of the reasons I wanted to use QR codes was context. Aside from scans now landing on the shop's website, they can be even more context-aware through security roles. For example, if a customer scans the QR code above, they'll end up on the product page for the shop as you would expect; but if an admin scans it they'll end up on the main inventory page to see current quantities and recent transactions.

Or, a better example is with order receipts. If somebody scans their order receipt, they'll be required to login and then will be taken to their order details page, assuming the order was on their account. If an admin scans the receipt (perhaps while packing it) they'll be taken to the administrative, detailed view of the order.

The idea of context doesn't only apply to where a user might end up, it also applies to how they get there. For example, sometimes there will be more than one bolt of a single fabric pattern, and, since each bolt is a different "thing" in the system, they each have a different QR code. If a customer scans either of the bolts, they would get taken to the exact same public product page. However, when an admin scans a bolt they'll get taken to the detailed view showing which orders were cut on that specific bolt and how much yardage the bolt still has.

Integrated Context

At this point, the barcodes were extremely accessible for one-off scans, but I also wanted to integrate the barcodes into specific points of the system. For the computers we're using USB 2D barcode scanners which are capable of acting like a keyboard device (the computer sees it "typing" whatever it scans, followed by an Enter). The most useful integration point was the POS for handling in-store shoppers.

For the POS, I created a new UI component which auto-focused itself. Once something gets scanned, it sends the scanned data to the server so it can figure out what should happen. For QR code scans, it performs the insignia lookup to find the actual inventory item. Then, for simple inventory items it can just add the scanned item to the order. For fabric on the bolt, it comes back with a dialog about how much to cut. For complex items, it shows a dialog for further specifications. Or there might just be a discrepancy and it needs to come back and show a message. Once the item is added it provides visual feedback, the scan field is re-focused and the cycle continues. It works something like the following in a browser...

    dotty.set('tween', { link : 'cancel', duration : 200 });

            function () {
                talkr.set('text', 'Ready to Scan');
                dotty.tween('background-color', '#66FF66');
            function () {
                talkr.set('text', 'Click to Scan');
                dotty.tween('background-color', '#CC0000');
            function (e) {
                if ('enter' != e.key) {
                } else if (!this.value) {

                prompt('Seems like you scanned...', this.value);

                this.value = '';

            function (e) {



I feel like the shop is able to better grow both technically and logistically by having used QR codes as opposed to a classic barcode system. A few techy customers have tried the QR codes, but it's not really something we've been promoting. Once the website has a proper mobile-friendly version we'll have a better opportunity and reason to try and impress customers with the QR codes. In the meantime, the QR codes have been an immense time-saver for both staff and shoppers checking out at the shop.