Two-step workflow:
- IDP — PDF invoices → Excel workbooks (
process_invoices.py) - Human review —
Receipts - Ready/review_dashboard.txt - Aspire import — Excel → purchase receipts (
import_purchase_receipt.py)
Receipts - Ready/
PR Template.xlsx # Template (excluded from import scan)
review_dashboard.txt # Low-confidence extractions for review
*.xlsx # Generated import files (one receipt each)
Invoices - Ready/ # Drop PDF invoices here
Invoices - Processed/
Complete/ # PDFs after IDP (default processed folder)
exports/
vendors.csv # Vendor list for IDP matching
catalog_items.csv # Catalog for IDP (A–F: ID, Code, Name, AlternateName, Type, Active)
| Cell / range | Field |
|---|---|
| B1 | Invoice date |
| B2 | Vendor (exact Aspire vendor name) |
| B3 | Branch (default from ASPIRE_DEFAULT_BRANCH) |
| B4 | (ignored) |
| B5 | Invoice number (IDP appends -INV) |
| B6 | Receipt note (Idaho Sod variance / manual split instructions) |
| B10+ | Item code (optional if C has name) |
| C10+ | Item name |
| D10+ | Quantity |
| E10+ | Unit cost (vendor profile may apply tax, 3 decimal places) |
| F6 | Column heading: Total Cost |
| F10+ | Line total (= D × E); Fowler profile nudges E so ΣF matches invoice grand total |
Per-vendor rules for tax and total reconciliation. process_invoices.py stays generic; profiles are selected from the matched Aspire vendor name.
| Profile | Vendor | Tax on unit cost | Reconcile ΣF to invoice total | Skip import consolidation |
|---|---|---|---|---|
hd_fowler |
H.D. Fowler Company {Turf} (and names containing “fowler”) | ×1.065 | Yes | No |
HD Fowler freight: INBOUND FRT / BILLABLE lines are not imported as catalog items. Taxed freight (×1.065) is summed and added per material unit to each line’s unit cost (total freight ÷ total material quantity).
| idaho_sod | Idaho Sod (Vendor 136) | None (×1.0) | Yes | No |
| cedron_sod | Cedron Sod | None (×1.0) | Yes | No |
| default | Everyone else | None (×1.0) | No | No |
Idaho Sod: Invoices use Total Due ÷ square feet for unit cost (delivery, pallet deposit, and fuel surcharge are included in Total Due, not separate Aspire lines). Kentucky → Bluegrass Sod; RTF → Rhizomatous Tall Fescue Sod. IDP writes one import line; when 3 decimals cannot match Total Due exactly, B6 documents the variance and a manual two-line split for Aspire UI. Import sends B6 as ReceiptNote.
Cedron Sod: Invoices use the bottom-left invoice total ÷ total sod square feet when there is a single grass type (pallet credit, pallet charge, and tax are included in the total, not separate Aspire lines). Multiple grass types are left for review. Same B6 variance / ReceiptNote behavior as Idaho Sod.
Catalog matching treats tee, elbow, coupler, adapter, plug as product families with strict size rules.
Add vendors by extending HD_FOWLER_PROFILE / DEFAULT_PROFILE or calling register_vendor_profile() in idp_vendor_profiles.py.
HD Fowler Turf vs Waterworks: IDP always maps Fowler invoices to H.D. Fowler Company {Turf} (override with IDP_HD_FOWLER_VENDOR_NAME). Waterworks is omitted from the LLM vendor list and remapped on import if Excel still has the wrong Fowler variant.
cd "c:\Users\ryanc\OneDrive\repos\Aspire Project"
copy .env.example .env
# Edit .env: ASPIRE_CLIENT_ID, ASPIRE_SECRET, ANTHROPIC_API_KEY
py -m pip install -r requirements.txt
py scripts/ensure_pr_template.pyRefresh exports from Aspire before IDP (recommended when new catalog items are added):
py scripts/export_reference_data.py --out exportsPlace PDFs in Receipts - Ready\Invoices - Ready\.
py scripts/process_invoices.py --fresh-dashboard
py scripts/process_invoices.py --no-catalog-prompt --no-dashboard path/to/invoice.pdf
# Model: interactive 1/2/3 prompt, or --model 2, or --no-model-prompt to use ANTHROPIC_MODEL from .env
# Extraction audit JSON saved to Receipts - Ready/review/JSONs (use --no-extraction-json to skip)- Uses Anthropic vision + structured JSON (
exports/vendors.csv,exports/catalog_items.csv). - Writes
{Vendor}_{Invoice-INV}.xlsxintoReceipts - Ready\. - Moves PDFs to
Invoices - Processed\Complete\. - HD Fowler only: appends each matched line to
exports/HD Fowler Item Match Log.xlsx(auto path, or on Streamlit approve after review). Uses xlwings so your Excel formatting is preserved; Microsoft Excel must be installed and the log file should be closed while IDP runs. - Backfill archived Fowler PDFs:
py scripts/backfill_fowler_match_log.py(no Aspire import).
Open Receipts - Ready\review_dashboard.txt and fix any low-confidence vendor/line matches in the generated .xlsx before importing.
py scripts/import_purchase_receipt.py --dry-run
py scripts/import_purchase_receipt.py- Skips
*Template*.xlsxand*-imported.xlsxfiles. - After a successful import, renames the workbook to
{name}-imported.xlsx. - Resolves catalog items by item code (B) first, then item name (C) if code is empty.
- MD Internal Vendor (347): prompts to refresh
exports/catalog_items.csvfrom Aspire before matching (--yes-refresh-catalog/--no-refresh-catalog/--no-catalog-promptto control). - Creates receipts only (
POST /Receipts) — does not approve or receive. - After each successful create, prompts Y/N to upload the matching PDF from
Invoices - Processed\Complete(--yes-attach/--no-attachskip the prompt). PDFs are namedAspire-{Vendor}-{Invoice-INV}__{MMDDYYYY}.pdf(e.g.Aspire-HD Fowler-I7331698-INV__06042026.pdf). - Optionally prompts Y/N to mark the receipt received via
POST /Receipts/Receive(--yes-receive/--no-receiveskip the prompt). Receive date is set by Aspire to today when you run import (not approved). - Bulk import (2+ files): attach and receive are asked once for the whole run (
Y= all,N= ask for each receipt). Use--per-receipt-promptto skip the batch question. Use--no-attach/--no-receiveto skip entirely.
See .env.example for Aspire, IDP, and path overrides.
- Aspire API guide
- Production Swagger
- Attachments API probe notes — receipt PDF attach flow (WIP)