Niko McGrathSelected work, 2021–2026
← All work

No. 07 · Business · 2026

Inventory Tracker

SalmonCat's order and inventory workbook, and the Google Apps Script that now files every Shopify order into it and opens a Discord ticket for fulfillment, so nobody copies a CSV by hand.

Role
Builder
Runs on
Google Apps Script
Live since
June 2026

The spreadsheet that held it together

SalmonCat’s inventory lived in spreadsheets far longer than it should have. One workbook tracked every order, every kit and every part through the launch and the years after it. It was the load-bearing piece of the whole operation, held together with the flimsiest tool we had, and a proper system from day one would have saved real money and real stress. I’d build that first next time.

By 2026 the problem had changed shape. I was in Japan, and my brother was packing and shipping orders in the US. The workbook itself still worked: an Orders Master tab took the raw Shopify export, and a formula-driven Order sheet fanned each order out into lines and removed duplicate order numbers. But every order still went through three steps by hand before anyone could ship it.

What every order went through

The first three steps were copying and messaging. The tracker took those over; buying the label stays a person's job.

  1. 01Now automated Download the CSV Export the new orders from Shopify
  2. 02Now automated Paste into Orders Master The formulas take it from there
  3. 03Now automated Message my brother Which order, what to pack
  4. 04 Buy the label In Shopify
  5. 05 Ship it From the US

Hand-kept numbers drift, too. When I counted stock in April 2026, the master list said there were no Aizome bodies left. The shelf had five.

Orders that file themselves

A Google Apps Script bound to the order sheet now does the middle of the job. Every ten minutes it asks Shopify for any order newer than the last one it saw, writes it into Orders Master, and posts one Discord ticket per order to the fulfillment channel. There’s no server: it runs on Google’s infrastructure, for free.

Where an order goes now

Nothing is copied by hand between Shopify and the person packing the box.

StoreShopifyAdmin API, with read-only access to orders
Google Apps ScriptThe order syncNew orders since the last one it saw, then any order that changed since its last run
Sheet tabOrders MasterRaw rows in Shopify’s own export format
Sheet tabOrder sheetFormulas fan each order out and drop duplicate order numbers
Discord forumOne ticket per orderItems, ship-to address, method and total
FulfillmentLabel PDF in the threadMeans clear to ship

Writing rows the way a person would

Multi-item orders are written the way Shopify’s own CSV export writes them: the first row carries the order’s fields and its first item, and each further item gets a continuation row. That makes the new rows exactly what the sheet already expected, so none of the formulas downstream had to change. Columns are found by their header names at run time, so reordering the sheet can’t break it.

A three-item order in Orders Master

E2fx=IF(D2="","",DATEVALUE(LEFT(D2&"",10)))
A B C D E ⋯
1 NameEmailFinancial StatusPaid atPaid Date (Clean)Lineitem quantityLineitem nameLineitem skuLineitem priceTotal
2 #SCS2041 [email protected] paid 2026-07-14T09:12:44-07:00 2026-07-14 1 Aozora Base Kit - Hakushi / Brass AZ-001 130.00 159.68
3 #SCS2041 [email protected] 1 Extra Plate - Polycarbonate PL-003 12.99
4 #SCS2041 [email protected] 1 PCB Foam FM-001 1.69
A sample order with invented customer details. Row 2 carries the order’s fields and its first item; rows 3 and 4 carry only what Shopify’s export repeats for each item. E2 is a live formula that turns the payment timestamp into a date for tracking handling time.

A ticket for the person shipping

Each order opens its own thread in a Discord forum channel, @-mentioning my brother, with everything he needs to pack it and a link straight to buying the label. Right after checkout, Shopify sometimes returns a new order before its line items have landed. The script waits three seconds and asks again; if the order is still empty, it stays out of the sheet and its ticket is flagged instead of passing for a normal one.

#orders › #SCS2041 — Jordan Lee

🧾 #SCS2041 • Jordan Lee

Items

  • 1× Aozora Base Kit - Hakushi / Brass AZ-001
  • 1× Extra Plate - Polycarbonate PL-003
  • 1× PCB Foam FM-001

Ship to

Jordan Lee100 Example StPortland, OR, 97201US

Method: USPS Priority • Total: USD 159.68

⏳ AWAITING LABEL — @brother buy it, drop the label PDF in this thread, then it's clear to ship.

A normal ticket.

#orders › #SCS2042 — Sam Ortiz

🚨 #SCS2042 • Sam Ortiz

NO LINE ITEMS FROM SHOPIFY — NOT recorded in Orders Master. Check this order manually.

Items

none

Ship to

Sam Ortiz200 Sample AveAustin, TX, 78701US

Method: USPS Priority • Total: USD 130.00

⏳ AWAITING LABEL — @brother buy it, drop the label PDF in this thread, then it's clear to ship.

The flag, when Shopify still has no line items after the retry. Both are sample orders with invented details.

What stays manual, on purpose

Buying the shipping label stays in Shopify, and the label PDF goes into the order’s Discord thread. A PDF in the thread means clear to ship.

Shipped

I built it on 2026-06-29 and checked it against real past orders with a local dry-run harness. Shopify had retired the old custom-app tokens in January 2026, so it authenticates through a Partners app. It went live on 2026-06-30 and processed four real orders on its first run.

Rows that stay true

Writing an order once isn’t enough: it gets paid, shipped, tagged, and sometimes cancelled. So every run ends with a second pass. The script asks Shopify for every order changed since its last run, finds that order’s rows by their Shopify ID, and brings them up to date: fulfillment status, ship date, payment status and tags on the order’s first row, and each item’s own fulfillment status on its row. The ship date gets the same live formula as the payment date, so the time from payment to shipment now tracks itself. A cancelled order’s rows are deleted outright.

A one-off repair job covers the past. It scans the last 30 days of orders, matches them against the sheet by ID, and adds any that are missing, without posting their tickets a second time.

Next: stock that counts itself

The last manual piece is the stock itself. A sold base kit should take its parts off the inventory list, but a kit isn’t one item. It’s a bill of materials, and its parts depend on each other.

What one base kit takes

BodyAZ-001 – AZ-005 · five colorways
PlatePL-001 brass · PL-002 aluminum
BadgeBG-001 – BG-004 · A and B grade
Hotswap PCBPB-001
Base kit1 sold

RuleThe badge has to be the same metal as the plate.

That rule is what a part-by-part count hides. In April 2026 there were plates for 178 kits and bodies and PCBs for 80, but only 68 matching badges, 40 brass and 28 aluminum.

Kits the April 2026 stock could make, part by part

A kit needs one of each part, in matching metal. Whichever runs out first sets the limit.

Plates, brass + aluminum
178
Bodies
80
Hotswap PCBs
80
Badges, A + B grade
68

SalmonCat inventory count, 16 April 2026.

See the numbers
MeasureValue
Plates, brass + aluminum178
Bodies80
Hotswap PCBs80
Badges, A + B grade68
Kit limit 6868

So the badges ran out first, and 12 bodies had no matching badge set. That’s the number a tracker should show without anyone working it out by hand, and it’s the next thing to build: decrement each part as an order comes in, and report how many complete kits are left.