Developer ToolsFree generatorPDF, Excel, Word export

Invoice Field Regex Generator in Python for OCR Text

For suppliers with stable layouts, a few well tested regular expressions beat a heavy model: they are fast, explainable and easy to audit. This generator builds a Python parser for the four fields every AP team needs, with a basic total check.

Fill in

Alternatives separated by |

Generated code
Download:

Patterns

import re
from datetime import datetime

CUR = re.escape("$")

INVOICE_NO = re.compile(r"(?:Invoice No|Invoice #|Inv No)\s*[:#.]?\s*([A-Z0-9][A-Z0-9\-/]{3,24})", re.I)
PO_NO = re.compile(r"(?:PO Number|Purchase Order|P.O. No)\s*[:#.]?\s*([A-Z0-9][A-Z0-9\-/]{3,24})", re.I)
DATE_TXT = re.compile(r"(?:Invoice Date|Date)\s*[:.]?\s*(\d{4}-\d{2}-\d{2}|\d{1,2}[/.-]\d{1,2}[/.-]\d{2,4})", re.I)
TOTAL = re.compile(r"(?:Total Due|Amount Due|Grand Total|Total)\s*[:.]?\s*" + CUR + r"?\s*([0-9]{1,3}(?:,[0-9]{3})*(?:\.[0-9]{2})|[0-9]+(?:\.[0-9]{2})?)", re.I)
LINE_AMT = re.compile(r"^.*?" + CUR + r"?\s*([0-9]{1,3}(?:,[0-9]{3})*\.[0-9]{2})\s*$", re.M)

Parser

def first(pattern, text):
    m = pattern.search(text)
    return m.group(1).strip() if m else None

def parse_date(raw):
    if not raw:
        return None
    for fmt in ("%Y-%m-%d", "%d/%m/%Y", "%d-%m-%Y", "%d.%m.%Y", "%m/%d/%Y", "%d/%m/%y"):
        try:
            return datetime.strptime(raw, fmt).date().isoformat()
        except ValueError:
            continue
    return None

def parse_amount(raw):
    return float(raw.replace(",", "")) if raw else None

def parse_invoice(text):
    result = {
        "invoice_number": first(INVOICE_NO, text),
        "po_number": first(PO_NO, text),
        "invoice_date": parse_date(first(DATE_TXT, text)),
        "total": parse_amount(first(TOTAL, text)),
    }
    result["flags"] = []
    for key in ("invoice_number", "invoice_date", "total"):
        if result[key] is None:
            result["flags"].append("missing_" + key)
    return result

Total check against line amounts

def check_total(text, parsed, tolerance=0.01):
    amounts = [parse_amount(a) for a in LINE_AMT.findall(text)]
    if not amounts or parsed["total"] is None:
        return "cannot_check"
    # the last amount on a page is usually the total, so exclude it from the sum
    body = amounts[:-1]
    if abs(sum(body) - parsed["total"]) <= tolerance:
        return "ok"
    return "review"

if __name__ == "__main__":
    sample = open("invoice.txt", encoding="utf-8").read()
    data = parse_invoice(sample)
    data["total_check"] = check_total(sample, data)
    print(data)

This is a starting point for text-based extraction, not a complete product. It assumes decimal point amounts, so adapt the amount pattern and parse_amount for decimal comma locales. The total check is a heuristic that ignores tax and freight lines; replace it with line level logic that matches your invoice layouts. Test on real supplier samples before relying on it.

Outgrowing regex?

Optional: an expert can assess your invoice layouts and suggest where regex ends and a model should start.

Optional. The tool and every export work without this. No spam.

How to use the generator

List the labels your suppliers print, separated by a vertical bar, then set the currency symbol and total tolerance. Copy the script, save OCR text from your engine into invoice.txt, and run it. The output is a dictionary with the invoice number, PO number, ISO date and total, plus flags for missing mandatory fields. Extend the label lists as you meet new supplier layouts, and keep one test text file per supplier so changes do not break old ones.

Design of the patterns

Each pattern looks for a label, an optional separator such as a colon or hash, and then a value shaped like the field. Invoice and PO numbers allow letters, digits, hyphens and slashes, with a minimum length to avoid matching words. The date pattern accepts ISO dates and numeric day, month and year forms. The amount pattern accepts thousands commas and two decimals.

  • Labels are case-insensitive alternatives
  • Longer labels should come before shorter ones, so Total Due matches before Total
  • Dates are normalized to ISO format
  • Missing mandatory fields raise flags rather than silent blanks

Where regex stops working

Regex works when the label and value sit on the same line. It struggles with tables, labels above values, multi column layouts, handwritten notes and scans with heavy noise. Ambiguous dates such as 04/05/2026 cannot be resolved by pattern alone, so decide the order per supplier or country. When the number of layouts grows, move to a layout-aware extraction approach and keep regex for validation.

Common mistakes

A pattern that matches the first Total in the text may pick up a subtotal. Prefer the last match on the page, or anchor to the words Amount Due. Another mistake is skipping validation: pair every extracted value with a check such as date range, duplicate invoice detection by supplier and number, and the total comparison.

Frequently Asked Questions

Can regex extract data from any invoice?

No. It suits invoices where the label and value appear on one line in a consistent form. For varied layouts, tables and noisy scans you need layout-aware extraction, with regex kept for validating the results.

How do I handle dates like 04/05/2026?

Pattern matching cannot tell whether that is April 5 or May 4. Store the expected date order per supplier or country, and apply that order first. Reject dates that fall outside a plausible range around the receipt date.

How do I support decimal comma amounts?

Change the amount pattern to use dots for thousands and a comma for decimals, and update parse_amount to remove dots and replace the comma with a dot. Select the pattern per supplier locale rather than guessing from the text.

Why is my total picking up the subtotal?

The word Total also appears inside Subtotal. List the more specific labels first, use word boundaries, and prefer the last match in the text, since final totals usually appear at the end of the page.

If you need extraction that handles many layouts and writes validated results back to your ERP, Netray can build it as a private service on your infrastructure.