Skip to content

Extracting Structured Fields From a Purchase Requisition Form

9 min read · updated August 11, 2026

A purchase order commits the company to a vendor. A requisition commits nobody to anything — it is an internal request for permission to spend. Extracting one as though it were the other loses the fields the document exists for, and produces an approval status that is a guess.

A requisition is not a small purchase order

The two documents overlap on line items, quantities and prices, which is why they get modelled together and why that modelling fails. The difference is direction: a requisition faces inward, from a requester to their own finance function, and a purchase order faces outward, from the company to a supplier.

Everything that follows comes from that. A requisition’s vendor field is a suggestion — “suggested supplier” or “preferred vendor” — and procurement may source elsewhere; a purchase order’s vendor is the counterparty. A requisition’s prices are estimates, frequently drawn from a web page or a verbal quote, and the resulting PO price legitimately differs. A requisition has a need-by date, which is a request; a PO has a delivery date, which is a term.

One more distinction is worth stating because it is a persistent source of confusion: the requisition is not part of the three-way match. Invoice, purchase order and goods receipt are matched to authorise payment; the requisition sits upstream of all three and authorises the PO. A pipeline that pulls requisitions into a matching process is matching against a document with no external force.

The fields that only exist here

  • Requester and requesting department. A named person with a cost centre, distinct from the buyer who will later raise the PO. Both appear on the form and they are different roles.
  • Cost centre and GL account, per line. Not per document. A single requisition routinely splits across accounts, and a header-level cost centre field will be wrong for at least one line on any requisition that mixes capital and expense items.
  • Budget reference and available balance. Some forms carry a budget check performed before submission, with a remaining balance printed on the form. That figure is a point-in-time snapshot and is stale by the time you read it; extract it as an assertion with its own date rather than as a fact.
  • Business justification. Free text, and the only field on the form that explains why. It is what an audit sample asks for and it is routinely dropped as unstructured noise.
  • Need-by date and delivery location. Internal requirements, not commitments.
  • Approval chain. Typically a row of boxes, each with a name, a title, a signature and a date, in escalating order.

Capital-versus-expense classification deserves its own field where the form supports it, because it determines which approval path applies and often which threshold. A form with a “capex / opex” checkbox is telling you something the line items alone do not.

Approval is a state, not a signature

The tempting model is a boolean: signature present, therefore approved. It is wrong in both directions and the failures are not symmetric.

A signature is evidence that one named person signed at one level on one date. Approval is a state of the request: draft, submitted, partially_approved, approved, rejected, returned_for_information, or cancelled. Deriving that state needs the whole chain plus a rule about how many levels this request required, which is the delegation of authority.

So model the chain as an ordered list of approval events, each with the approver’s name, their title as printed, the level, the date, and whether a signature mark is present:

{
  "requisition_no": "REQ-2026-004871",
  "requester": { "name": "A. Okonjo", "cost_center": "CC-4420" },
  "total": { "amount": 47500.00, "currency": "USD" },
  "capex": false,
  "approvals": [
    { "level": 1, "role": "manager", "name": "R. Silva",
      "signed": true,  "date": "2026-02-03" },
    { "level": 2, "role": "director", "name": "M. Haas",
      "signed": true,  "date": "2026-02-05" },
    { "level": 3, "role": "vp", "name": null,
      "signed": false, "date": null }
  ],
  "status": "derived"
}

An unsigned level-3 box is not the same as no level-3 box. The first means the form contemplated a VP approval and did not get one; the second means none was required. That distinction is visible on the page and is destroyed by any schema that stores only the signatures it found.

Approval dates are worth validating for order. A level-2 signature dated before the level-1 signature is either a transcription error or a process exception, and both are worth surfacing. A signature dated before the requisition date is one or the other with more certainty.

Checking against the delegation of authority

A delegation of authority is a table of spend thresholds by role. It lives outside the document, it is the thing that makes the extracted chain meaningful, and it turns approval status into arithmetic. Take a schedule of: manager up to $10,000; director up to $50,000; VP up to $250,000; CFO above that.

requisition total          $47,500.00
required level             director   (10,000 < 47,500 <= 50,000)
highest level signed       director
derived status             approved

-- then a freight line of $4,800.00 is added --

revised total              $52,300.00
required level             vp         (50,000 < 52,300 <= 250,000)
highest level signed       director
derived status             partially_approved  (insufficient authority)

The second case is the one worth building for. The document looks identical — same signatures, same boxes, nothing crossed out — and the approval is no longer sufficient because the amount moved past a threshold after it was given. Any change to the total after an approval date invalidates approvals below the new required level, and detecting it needs the extracted line items, the extracted approval dates, and the threshold table together.

Two arithmetic checks belong alongside it. Line extensions should foot: quantity times unit price equals the line total, and the line totals plus tax and freight equal the document total. And the currency must be consistent across lines; a mixed-currency requisition compared against a threshold table denominated in one currency is being compared against nothing.

Patterns worth detecting

Split requisitions. Three requisitions from the same requester to the same suggested vendor in the same week, each just under a threshold, is the classic pattern for avoiding an approval level. It is detectable only across documents, which means the extraction has to emit a record shaped for that query — requester, vendor, date, total, threshold band — and somebody has to run it. Whether any given cluster is deliberate is not for the pipeline to decide; flagging it is.

Retroactive requisitions. A requisition dated after the invoice it is meant to authorise, raised to regularise a purchase somebody already made. Comparing the requisition date to the earliest related document date catches it, and it is one of the most common findings in a procurement audit.

Self-approval. An approver name matching the requester name at any level. A simple string comparison catches the obvious case; matching on employee identifier catches the rest.

Stale or delegated authority. A signature from an approver whose printed title does not match the level box they signed, or a “p.p.” or “on behalf of” annotation next to a signature. The delegation may be entirely proper; it needs to be recorded rather than flattened into the delegating approver’s name, which is what a model asked simply for “the approver” will do.

A blanket or standing requisition. One request covering repeated purchases up to a ceiling over a period. Its total is a limit rather than an amount, so threshold checks apply to the ceiling and consumption has to be tracked against it — a different lifecycle from a one-off request, and worth a distinct document subtype rather than a flag discovered later.