Skip to content

Part 3 of 7 · Purchase order approver series ~6 min read

How a purchase request gets checked

The model read the request. From here on, nothing is a judgement call. The checker asks four questions with yes-or-no answers, in a fixed order, against a sheet the business already maintains — and it attaches the number behind every answer, so a decision can be read back six months later without anybody guessing what the system was thinking.

Key takeaways

  • Four checks, always in the same order: line exists, room in it, vendor approved, not a repeat.
  • Every check is plain arithmetic against the sheet. The model never decides whether to spend.
  • “No room” always ships with two numbers: what is left, and what was asked for.
  • Committed is not spent. The line moves when the purchase order is issued, not when it is paid.
  • A failed check is a specific sentence back to the requester, never a generic rejection.

Four questions, in order

The order matters, because a later answer is meaningless if an earlier one failed. There is no point telling somebody there is no room in a budget line that does not exist.

The four budget checks, in the order they runA vertical chain of five steps inside the AWS account, entered from the left by a box labelled Complete request, five fields, from Part 2. Step one asks whether the budget line exists, matching against the budget sheet shown to the right; if there is no match it exits to Ask which line, listing the options. Step two asks whether there is room, computing the limit minus the committed total read from a DynamoDB ledger; if there is no room it exits to Over budget, sent to a person with the numbers. Step three asks whether the vendor is approved, matching loosely against the vendor tab; an unknown name exits to New vendor, which needs a person. Step four asks whether something very similar has already been committed on the same line this week, exiting to Possible repeat, which shows both and asks. Step five is Four yeses, which hands the request to the router. A note says every exit is a sentence to a human with the numbers attached, and none of them is a dead end.AWS ACCOUNTComplete requestfive fields, from Part 2Does the line exist?match against the sheetBudget sheetlines and limitsAsk which linelist the optionsno matchIs there room?limit minus committedDynamoDB ledgercommitted to dateOver budgetsend with the numbersno roomVendor approved?fuzzy name matchVendor tabapproved suppliersNew vendorneeds a personunknownAlready committed?same line, same weekPossible repeatshow both, asksimilarFour yeseshand to the routerEvery exit is a sentence to a human, with the numbers attached. None of them is a dead end.
Fig 1. The four checks in order, each reading one source and each with its own exit. Nothing is rejected outright — every failed check becomes a specific question to a specific person.
  • Database
  • App integration
  • Machine learning
  • Security & identity
  • Analytics
  • Front-end & mobile
  • People

Does the budget line exist?

The reader put a budget line in the record, or left it blank and the intake already asked. Either way, the checker now matches that string against the rows in the sheet. Exact match first, then a loose one — “workshop consumables” finds “Workshop Consumables”, and “consumables” finds it too if there is only one row containing the word. Two candidate rows is not a match; it is a question, and the requester gets both to pick from.

Is there room?

Room is limit - committed - this request. Three numbers, one subtraction. The limit comes from the sheet. The committed total comes from the ledger, not the sheet, because the sheet is edited by people and the ledger is written by the system. If they disagree, the ledger wins for the check and the disagreement is flagged.

The word committed is doing real work there. A purchase order that has been issued but not yet invoiced is money that is already spoken for. A system that only counted paid invoices would happily approve four orders against a line with room for one, and all four would be perfectly correct at the moment they were approved.

Is the vendor approved?

Matched against the vendor tab by name, loosely enough to survive how people actually type: case, punctuation, a trailing “Ltd”, a domain instead of a name. An unmatched vendor is never a rejection — new suppliers are normal. It routes to a person with the name as typed and a note that this vendor is not on the list yet, and approving it adds the row.

Is this already committed?

This is the softest of the four and the only one that can be wrong in a useful direction. It looks for a commitment on the same budget line, to the same vendor, within the same week, for an amount within ten per cent. That is not proof of a duplicate — weekly consumables orders look exactly like that — so it never blocks. It attaches both records to the message and lets the person look.

The shape of an answer

Each check returns three things: the answer, the numbers behind it, and the sentence a human would write. That third one is why the approval message in Part 4 reads like a person wrote it without a model being involved at that stage.

The five values the checker produces for one requestA horizontal row of five boxes. Line: matched to Workshop consumables. Room: 1,150 left in the line against 640 asked for. Vendor: Medline, known. Repeat: nothing similar found. Verdict: over the auto-approve line, so ask a person. A note says four answers and their numbers, and that the verdict is arithmetic rather than judgement.WHAT THE CHECKER HANDS ONLineWorkshop consumablesRoom1,150 left, 640 askedVendorMedline, knownRepeatnothing similarVerdictover the line, askFour answers and their numbers. The verdict is arithmetic, not judgement.
Fig 2. The checker’s output for one real request. Four answers, each carrying the number that produced it, and a verdict that follows from them mechanically.
  • App integration
  • Machine learning
  • Analytics

Why none of this is the model’s job

  • Subtraction has one right answer. A model gets it right almost always, and almost always is the wrong reliability for money.
  • A check has to be explainable in one sentence with a number in it. Code produces that for free; a model has to be asked for it and can be asked twice and answer differently.
  • The rules change often — a new limit, a new vendor, a new line. Those are edits in a sheet. Nothing about them should require touching a prompt.
  • The model already did the only thing it is better at than code: turning a sentence a person typed into five fields.

Next: what happens when the answer is “ask a person” — who gets asked, what they see, and what happens if they never reply.

All posts