How a draft PO reaches the owner
The bot picked a move — reorder or urgent reorder. Now the draft-PO Lambda has to figure out which supplier to order from, how many to order, when to send the draft, and what context to attach. Get any of those wrong and the draft is worse than no draft: an order for the wrong amount, sent to the wrong supplier, landing at 2am, with no way to see why. Four small guardrails sit between the move and the draft that lands in front of the owner.
Key takeaways
- Supplier resolution: per-item override beats per-category default beats the configured fallback supplier.
- Slack DMs are the default; email is the fallback if no Slack ID is configured for the owner.
- Quiet hours defer a draft to the next business hour so nothing lands overnight.
- The order quantity is rounded up to pack size, meets the minimum, and is capped by the rules doc.
- Every draft ships with the item, suggested quantity, unit cost, supplier, and Approve/Edit/Skip.
Four guardrails on every draft
Gate 1: resolve the supplier
Three places the draft-PO Lambda looks for the supplier of an item, in order. First, the stock sheet’s per-item supplier column — if a row names a specific supplier, that’s who the order goes to regardless of any default. Second, the per-category default in the rules doc (“all cleaning supplies default to Acme Wholesale”). Third, the configured fallback supplier — a catch-all so no item is ever stranded without somewhere to order from. The fallback should rarely fire; if it does, the weekly digest names every item that hit it so the supplier doc can be updated.
Once the draft knows the supplier, it pulls that supplier’s ordering email, pack size, and minimum order from the supplier doc — the numbers Gate 3 needs to size the order, and the address Approve will use later. It also looks up where to send the draft to the owner: the rules doc maps the owner to a Slack member ID if one is set, otherwise to an email address. Slack is preferred because a DM with action buttons is faster to act on than an email; email is the fallback so nothing falls through the cracks.
Gate 2: quiet hours
The bot runs early in the morning, so the first time a move fires it’s already near business hours. But an urgent draft that results from a sudden POS-driven dip can fire later in the day, and one-off deferred drafts can land outside the configured window. A purchase-order draft at 11pm helps nobody — it just sits unread until morning and risks an approval tap from a half-asleep owner.
Gate 2 reads the rules doc’s quiet-hours setting (default 6pm to 8am, configurable per business). If the current local time is in the quiet window, the draft creates a one-off EventBridge Scheduler rule that fires at the next business-hour minute and exits without sending. The Scheduler invokes the same draft-PO Lambda with the same payload at the deferred time, where Gate 2 will let it through. Urgent drafts can be configured to ignore quiet hours if a shop genuinely wants to be woken for a stockout risk — but the default is to wait.
Gate 3: a sensible order quantity
How many to order is its own small decision, and the bot makes it the same way every time. Start from how much you need to comfortably clear the reorder point and cover the next stretch of sales — a target stock level the rules doc can set as a number of days of cover (say, 30 days). Subtract what’s on hand. That’s the raw quantity. Then three adjustments, all from the supplier doc: round up to the supplier’s pack size (you can’t order half a case), raise to the supplier’s minimum order if the raw number is below it, and finally cap the result at the rules-doc order cap so a typo in the sales rate can never produce a five-figure order.
The cap is the important guardrail. Every other gate makes the draft more right; the cap is the one that makes a wrong draft small. If a sales rate gets fat-fingered to 800 instead of 8, the cap means the worst the bot can propose is the capped quantity — a number the owner will immediately see is off, rather than a catastrophic auto-order. And because nothing sends without the owner’s tap anyway, even a capped-but-wrong draft is caught before any money moves.
Gate 4: compose with full context, then send
The supplier doc has one purchase-order template per supplier: a short order with placeholders for the item name, SKU, quantity, unit cost, line total, and any standing instructions (delivery address, account number, PO prefix). The draft-PO Lambda fills the placeholders, attaches the on-hand count and sales rate so the owner has the “why” at a glance, adds Approve, Edit, and Skip buttons, and sends the message to the owner via the Slack API. The supplier’s ordering email isn’t touched yet — that only happens on Approve, in Part 5.
For email fallback, the same draft is wrapped in a small HTML email with the same fields and three links that, when clicked, hit a Function URL that records the choice — the email equivalent of the Slack buttons.
An urgent draft is composed slightly differently: it carries an “Urgent” tag, sorts to the top of the owner’s queue, and includes the days-of-cover-left figure (“~2 days at current sales”) so the owner knows exactly how much runway is left. If the rules doc names a faster backup supplier for the item, the urgent draft offers it as a one-tap alternative in the Edit modal.
Every draft — Slack or email, reorder or urgent — writes a pending row to ir-orders in DynamoDB. The next day’s check reads that row and knows not to draft the same item again while it’s awaiting a decision.
Why the guardrails exist
None of these gates are exotic. They’re the kind of small care a thoughtful buyer would take if they were placing the order themselves — check who we actually buy this from, don’t draft at 11pm, order a round case rather than an odd number, and never let a typo turn into a giant order. Putting them in code as four small sequential gates makes them part of the design, not a feature you’re trusting the person on shift to remember.
Next post: how a reorder gets approved once the owner has seen the draft — how Approve sends the PO and marks the item on-order, how Edit changes the order first, and how Skip steps back without losing the thread.
All posts