Advanced40 min

Project: Invoice Date Extractor

Every failure mode at once: dates, numbers, and large state. Extract with Jev, compute in code.

This is the capstone, and it is deliberately the ugliest problem in the course. An invoice arrives as a scanned PDF that some upstream OCR step has turned into a wall of text. You need the issue date, the due date, and the total. Every documented failure mode is present at once: dates that Jev reads as text rather than ordered quantities, amounts that Jev cannot add, and a state stuffed with remittance boilerplate, terms and conditions, and a footer about the supplier’s environmental policy.

The discipline that makes it work is one sentence. Jev extracts, code computes. Every time you feel the pull to ask the model for a number, a comparison, or a sum, that is the failure mode announcing itself.

Cut the state down first

Before a single question is asked, the invoice text gets shrunk. Not because of the 32k limit, which a one-page invoice will not approach, but because context rot starts biting long before the ceiling and every line of boilerplate is a distractor competing for the judgment.

import re

BOILERPLATE = re.compile(
    r"(terms and conditions|remittance advice|privacy policy"
    r"|please do not reply|registered office|environmental)",
    re.IGNORECASE,
)

AMOUNT = re.compile(r"[$£€]\s?\d[\d,]*\.\d{2}")
DATE_LINE = re.compile(r"(date|due|issued|invoice)", re.IGNORECASE)


def build_state(raw: str) -> dict:
    lines = [ln.strip() for ln in raw.splitlines() if ln.strip()]
    kept = [ln for ln in lines if not BOILERPLATE.search(ln)]

    # Code finds the candidates. Jev never generates a number or a date string.
    amount_candidates = sorted({m.group(0) for ln in kept for m in AMOUNT.finditer(ln)})
    date_lines = [ln for ln in kept if DATE_LINE.search(ln)]

    return {
        "invoice": {
            "header": kept[:25],
            "date_lines": date_lines,
            "amount_candidates": amount_candidates,
        }
    }

Look at what the regex did to the shape of the problem. Amounts are no longer something the model has to find, read, or transcribe. They are a numbered list of strings, and the only thing left for Jev is the one genuinely semantic question: which of these candidates is the invoice total, as opposed to a line item, a subtotal, a tax figure, or the previous balance carried forward.

Full lesson

Keep reading Project: Invoice Date Extractor

The rest of this lesson — including the interactive exercises and the worked project — is part of Jagged Edges.

  • Every lesson in all three courses
  • Interactive builders, graders, and playgrounds
  • 1,000 live Jev credits, for the courses and the playground
  • Project source you can run yourself
  • Free updates as Jev ships new versions
Browse free lessons

Already bought them? Sign in to unlock — no need to buy twice.