InvoiceParser/README.md

92 lines
4.6 KiB
Markdown
Raw Normal View History

2026-09-16 20:09:19 +00:00
## Configuration (appsettings.json next to the exe)
Secrets live in `appsettings.local.json` (same shape, git-ignored, loaded on
top of `appsettings.json`). Put `ConnectionStrings:Pos` and the two `ApiKey`
values there; `appsettings.json` ships with those blank. Use a SQL login
limited to the posbdat/posinv tables rather than `sa`.
- `ConnectionStrings:Pos` - SQL Server connection string.
- `Llm:TextRoute` / `Llm:ImageRoute` - `{ Url, Model, ApiKey }` per customer.
- Local llama.cpp: `Url: "http://host:8080"`, empty ApiKey.
- DeepSeek/OpenAI-compatible: full base URL + ApiKey (sends Bearer header).
- Gemini: `Url: "https://generativelanguage.googleapis.com"`, Model
`gemini-...`, ApiKey (sends x-goog-api-key).
- `Llm:Prompt` - override the extraction prompt; empty = built-in default.
- `Llm:TimeoutSeconds`, `Llm:MaxImageDim` - LLM call timeout, vision image cap.
- `Llm:AttachPageImages` - text-layer PDFs: send each rendered page image with
its extracted text (default true). Fixes vendors whose text layer is garbled
by the form overlay. Needs a vision model on `TextRoute`; set false for
text-only models (DeepSeek, a text-only local llama.cpp).
- `Paths:Pending` / `Paths:Archive` - relative to the exe or absolute.
- `Port` - local UI port (falls back to a random port if taken).
- `Logging` - `Enabled`, `Directory` (default `logs`), `RetainDays` (14),
`MaxFileMB` (20), `LogRequests`. See "Log" below.
- `Batch` - `Enabled`, `NamePrefix` (`INV_`), `CreatedBy` (`INVOICE`),
`MinCostDelta` (0.01), `MarginThreshold` (15). See "Cost batches" below.
- `Tables:ProductBatchesHeaderTable` / `Tables:ProductBatchesTable` - the
posbdat batch tables a cost batch is written to.
## Log
`logs\invoiceparser-YYYYMMDD.log`, one file per day, pruned after
`Logging:RetainDays`. Copy the folder or read it over a share to see what a
machine did without sitting at it. One line per event:
2026-09-11 14:22:31.104 INFO [http] POST /api/save 200 412ms
Categories: `startup`, `shutdown`, `crash` (unhandled WPF/AppDomain/task
exceptions), `http` (every request; 4xx logs WARN, 5xx ERROR), `upload`,
`prep`, `worker` (queue and per-invoice stage changes), `llm` (endpoint, status,
elapsed, bytes; failures carry the response body), `match`, `db`, `catalog`,
`save`, `batch`, `product`, `pending`, `archive`. Connection-string passwords
and API keys are stripped before anything is written. Set
`Logging:LogRequests` to false to keep only warnings and errors.
## Cost batches
Saving an invoice also writes a posbdat price/cost batch for the items whose
cost moved. The invoice bills by the case, the product file costs by the unit,
so the comparison is `case cost / pack` against `Products.cost`; a gap of at
least `Batch:MinCostDelta` is a change, and an item with no cost on file counts
as one. Rows with no invoiced cost, and UPCs that are not in the product file,
are left out.
The batch is written inside the same transaction as InvoiceHeader and
InvoiceDetail - if it cannot be written, nothing is saved - and
`InvoiceHeader.Price_Cost_Change` is set to 1 when the invoice produced one.
It is named `INV_<invoice number>` (`_1`, `_2`... if that name is taken) and
carries the new cost only; retail is left alone, and items whose gross margin
at the new cost falls under `Batch:MarginThreshold` are flagged on the archive
screen for manual repricing. Apply it the usual way (`sp_ApplyBatch`).
The batch tables' column lists are read from INFORMATION_SCHEMA on first use and
whatever `Products` and `ProductBatches` genuinely have in common is copied
across, so a differently shaped install does not need a code change. If the
batch tables are missing or have no `cost` column the invoice still saves and
the reason is logged under `batch`.
## Build / run (dev)
dotnet build
bin\Debug\net8.0-windows\InvoiceParser.exe
## Deploy (copy-paste, no install)
dotnet publish -c Release -r win-x64 --self-contained -p:PublishSingleFile=true
Copy the publish folder (exe + appsettings.json + appsettings.local.json +
wwwroot) to the target machine and run the exe. Requirements on the target: the WebView2 Runtime
(preinstalled on updated Windows 10/11; otherwise the 2 MB Evergreen
bootstrapper from Microsoft), network access to SQL Server and the LLM host.
## Layout at runtime
InvoiceParser.exe
appsettings.json
appsettings.local.json connection string + API keys (not in git)
wwwroot\ UI (HTML/JS/CSS, local jQuery)
pending\{id}\ state.json, source.<ext>, pages\page_N.jpg
archive\ {vendorId}_{invoice}.<ext> + .preview cache
logs\ invoiceparser-YYYYMMDD.log
webview2_data\ WebView2 profile (created on first run)