fix(giellie): correct the stock tool actor, audit and quantity pointers

This commit is contained in:
Dave 2026-11-06 05:35:26 +02:00
parent e66340db4f
commit a3b5996d15
3 changed files with 8 additions and 5 deletions

View file

@ -258,8 +258,8 @@ REST mirror at `/api/v1/quote_items` (GET list + POST create) and `/api/v1/quote
| Tool | Purpose |
|------|---------|
| `stock_items.list`, `stock_items.get`, `stock_items.create`, `stock_items.adjust` | Inventory CRUD + manual adjustment. |
| `stock_items.update`, `stock_items.set_pricing` | Patch an item's catalogue fields, or set cost / selling price / markup in a batch. `set_pricing` audits the change in `stock_ledger` with the `actor`. Neither one touches the quantity. |
| `stock_items.set_quantities` | Set or change the stock quantity on up to 100 pasted lines. `mode: "set"` (new quantity on hand) or `mode: "add"` (change to add). Each line resolves by exact SKU, then by a description match (flagged). Unresolved lines are reported, never guessed. `dry_run: true` previews the before/after plan. Quantities may be negative — the till oversells on purpose. Writes a `stock_ledger` row with the acting user and mirrors `price_list.stock_qty`. **Required:** `actor.{id,name}` — `actor.id` must be a 15-char WFDASH user id (see `users.lookup`); it is stamped on every ledger row. A repeated SKU rejects the whole batch — split the paste. |
| `stock_items.update`, `stock_items.set_pricing` | Patch an item's catalogue fields, or set cost / selling price / markup in a batch. Neither one touches the quantity. `set_pricing` writes a `stock_ledger` row **only for a `cost_price` change on an item that already had a cost** — a selling-price or markup edit writes no ledger row, and neither does a first-time cost. |
| `stock_items.set_quantities` | Set or change the stock quantity on up to 100 pasted lines. `mode: "set"` (new quantity on hand) or `mode: "add"` (change to add). Each line resolves by exact SKU, then by a description match (flagged). Unresolved lines are reported, never guessed. `dry_run: true` previews the before/after plan. Quantities may be negative — the till oversells on purpose. Writes a `stock_ledger` row stamped with the acting user and mirrors `price_list.stock_qty`. **Actor:** the route requires `actor.id` (a 15-char WFDASH user id); `actor.name` is optional and defaults to the id. Giellie injects the signed-in operator's own actor after the model's arguments, so it wins over anything the model passed — never supply an actor yourself and never go looking for one. Every ledger row is already attributed. A repeated SKU rejects the whole batch — split the paste. |
| `stock_purchases.list`, `stock_purchases.get`, `stock_purchases.create`, `stock_purchases.next_number` | Stock-PO CRUD. |
| `stock_purchases.send`, `stock_purchases.receive` | Send to supplier / receive goods. |
| `stock_purchases.send_email`, `stock_purchases.retry_email` | Outbound email for stock POs. |

View file

@ -2025,7 +2025,10 @@ const toolNames = body.result.tools.map((t: { name: string }) => t.name);
expect(tool.inputSchema.properties.actor).toBeTruthy();
expect(tool.description).toMatch(/selling_price/);
expect(tool.description).toMatch(/stock_ledger/);
expect(tool.description).toMatch(/stock_items\.adjust/);
// Quantities are NOT changed by this tool — the description must point at
// the tool that does change them, not the superseded stock_items.adjust.
expect(tool.description).toMatch(/stock_items\.set_quantities/);
expect(tool.description).not.toMatch(/stock_items\.adjust/);
});
it("maps snake_case to the service's camelCase and forwards the actor", async () => {

View file

@ -2147,7 +2147,7 @@ const tools = [
{
name: "stock_items.update",
description:
"Patch catalogue fields on a master stock_item: sku, description, notes, unit, selling_price, markup. For inventory qty use stock_items.adjust. Returns {ok: true, item, previous} on success or {ok: false, reason, message, field?} on rejection. reason is one of: not_found (404), forbidden_field (400), validation_error (400), pb_error (422).",
"Patch catalogue fields on a master stock_item: sku, description, notes, unit, selling_price, markup. For inventory qty use stock_items.set_quantities. Returns {ok: true, item, previous} on success or {ok: false, reason, message, field?} on rejection. reason is one of: not_found (404), forbidden_field (400), validation_error (400), pb_error (422).",
inputSchema: {
type: "object",
required: ["id", "patch"],
@ -2172,7 +2172,7 @@ const tools = [
{
name: "stock_items.set_pricing",
description:
"Set cost price / selling price / markup on existing stock_items in one batch (max 100 per call). The POS till reads `selling_price` (NOT markup), so set both consistently: selling = cost × (1 + markup/100). Cost changes are audited in stock_ledger with the `actor`. Quantities are NOT changed here (use stock_items.adjust). Returns {results: [...], summary: {total, succeeded, failed}}; each row is {id, ok: true, item, previous} or {id, ok: false, reason, message} (reason: not_found | validation_error | forbidden_field | pb_error) — one bad row does not stop the others.",
"Set cost price / selling price / markup on existing stock_items in one batch (max 100 per call). The POS till reads `selling_price` (NOT markup), so set both consistently: selling = cost × (1 + markup/100). Cost changes are audited in stock_ledger with the `actor`. Quantities are NOT changed here (use stock_items.set_quantities). Returns {results: [...], summary: {total, succeeded, failed}}; each row is {id, ok: true, item, previous} or {id, ok: false, reason, message} (reason: not_found | validation_error | forbidden_field | pb_error) — one bad row does not stop the others.",
inputSchema: {
type: "object",
required: ["updates"],