Invoice Audit
Invoice line-item audit API: a chain of computed columns from subtotal to tax, total and risk band
One app.py with 2 tables, 10 computed columns, 5 HTTP routes. Needs pixeltable[serve]>=0.7.14. Updated 2026-09-29.
app.py
"""Invoice Audit API built with Pixeltable.pxt schema update app.py auditpxt service run app.py audit"""import pixeltable as pxtimport pixeltable.functions as pxtffrom pixeltable.serving import FastAPIRouterfrom udfs import (apply_discount,excerpt_note,grand_total,invoice_ref,line_subtotal,risk_band,risk_score,tax_amount,)# ---- tables ----TableModel = pxt.model_base()class Vendors(TableModel, name='vendors'):id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True)code: pxt.Stringname: pxt.Stringtier: pxt.Stringactive: pxt.Boolcode_upper = pxtf.string.upper(code)tier_upper = pxtf.string.upper(tier)class LineItems(TableModel, name='line_items'):id = pxt.Column(value=pxtf.uuid.uuid7(), primary_key=True)vendor_code: pxt.Stringsku: pxt.Stringqty: pxt.Intunit_price: pxt.Floatdiscount_pct: pxt.Floattax_rate: pxt.Floatvendor_tier: pxt.Stringnote: pxt.String | Noneauditor: pxt.String | Noneref = invoice_ref(vendor_code, sku)subtotal = line_subtotal(qty, unit_price)net = apply_discount(subtotal, discount_pct)tax = tax_amount(net, tax_rate)total = grand_total(net, tax)risk = risk_score(qty, unit_price, discount_pct, vendor_tier)band = risk_band(risk)note_excerpt = excerpt_note(note, n=48)# ---- queries ----@pxt.querydef flagged(vendor_code: str):"""Lines for a vendor that are not 'clear', riskiest first."""return LineItems.where((LineItems.vendor_code == vendor_code) & (LineItems.band != 'clear')).select(LineItems.ref, LineItems.total, LineItems.risk, LineItems.band, LineItems.note_excerpt).order_by(LineItems.risk, asc=False)# ---- routes ----audit = FastAPIRouter(name='audit')audit.add_insert_route(LineItems, path='/lines',inputs=[LineItems.vendor_code, LineItems.sku, LineItems.qty, LineItems.unit_price, LineItems.discount_pct,LineItems.tax_rate, LineItems.vendor_tier, LineItems.note, LineItems.auditor],outputs=[LineItems.id, LineItems.ref, LineItems.subtotal, LineItems.net, LineItems.tax, LineItems.total,LineItems.risk, LineItems.band, LineItems.note_excerpt],)audit.add_update_route(LineItems, path='/lines/adjust', inputs=[LineItems.discount_pct, LineItems.auditor],outputs=[LineItems.id, LineItems.net, LineItems.total, LineItems.risk, LineItems.band])audit.add_insert_route(Vendors, path='/vendors', inputs=[Vendors.code, Vendors.name, Vendors.tier, Vendors.active],outputs=[Vendors.id, Vendors.code_upper, Vendors.tier_upper])audit.add_compute_route(LineItems, path='/risk',inputs=[LineItems.qty, LineItems.unit_price, LineItems.discount_pct, LineItems.vendor_tier],outputs=[LineItems.risk, LineItems.band])audit.add_query_route(path='/flagged', query=flagged, method='get')
What app.py declares
Each row links to its line.
- runs on insert
- stored
- index
- route
- idvendor_codeskuqtyunit_pricediscount_pcttax_ratevendor_tiernoteauditor
- ref = invoice_ref(vendor_code, sku)L48
- subtotal = line_subtotal(qty, unit_price)L49
- net = apply_discount(subtotal, discount_pct)L50
- tax = tax_amount(net, tax_rate)L51
- total = grand_total(net, tax)L52
- risk = risk_score(qty, unit_price, discount_pct, vendor_tier)L53
- band = risk_band(risk)L54
- note_excerpt = excerpt_note(note, n=48)L55
serves audit
- POST/linesinsert, line_itemstakes vendor_code, sku, qty, unit_price, discount_pct, tax_rate, vendor_tier, note, auditor; returns id, ref, subtotal, net, tax, total, risk, band, note_excerptL69
- POST/lines/adjustupdate, line_itemstakes discount_pct, auditor; returns id, net, total, risk, bandL76
- POST/vendorsinsert, vendorstakes code, name, tier, active; returns id, code_upper, tier_upperL78
- POST/riskcompute, line_itemstakes qty, unit_price, discount_pct, vendor_tier; returns risk, bandL80
- GET/flaggedquery, flaggedtakes vendor_codeL83
Run it
Needs uv. The routes then answer on port 8000.
terminal
git clone https://github.com/pierrebrunelle/pixeltable-invoice-auditcd pixeltable-invoice-audituv syncuv run pxt schema update app.py audituv run python seed.py audituv run pxt service run app.py audit --port 8000
Related apps
- Forge Heat CyclesForge heat-cycle registry API: B-tree indexes, chained risk UDFs, and parallel shop-floor writersShares: UDFs
- Climbing Route ToposClimbing-route topo registry API: photo uploads with thumbnails, grayscale copies, grade bands and index-backed crag lookupsShares: UDFs
- Warehouse Pick ListsWarehouse pick-list REST API: units, pick zones, waves and tote labels as incremental computed columns over Json order linesShares: UDFs