91 lines
4.6 KiB
Markdown
91 lines
4.6 KiB
Markdown
## 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)
|