feat(issued-orders): counterparty is now a supplier (dodavatel), not a customer

Issued orders are purchase orders WE send - they must pick from suppliers
(sklad_suppliers), not from customers. Per user decision customer_id was
REPLACED (not kept alongside): migration drops issued_orders.customer_id and
adds supplier_id FK -> sklad_suppliers (existing rows lose their counterparty
- the feature is days old; re-point them in the UI).

- service: input/filters/search (suppliers.name + ico)/enrichment/detail all
  supplier-based; create validates the supplier inside the transaction and
  update before write -> Czech 400 'Dodavatel nenalezen' instead of P2003 500;
  detail returns a minimal supplier field set (no internal notes leak)
- routes: supplier_id on list + stats; new GET /issued-orders/suppliers
  lookup (orders.view/create/edit guard - orders users lack warehouse.manage
  which guards the warehouse suppliers CRUD), active suppliers only,
  name+id ordering
- PDF: Dodavatel block now renders the supplier (name, newline-split address,
  IC/DIC), layout and both language label sets unchanged
- frontend: new SupplierPicker kit component (CustomerPicker untouched),
  IssuedOrderDetail/IssuedOrders switched to supplier_id/supplier_name; the
  picker keeps a fallback option for orders whose supplier was later
  deactivated; WarehouseSuppliers CRUD now also invalidates issued-orders so
  the picker can't go stale
- tests: issued-orders suite switched to supplier fixtures + new coverage
  (lookup shape + 403, nonexistent supplier 400, PDF supplier block)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-10 11:29:06 +02:00
parent 74ce24e3fa
commit 6a22195c7d
15 changed files with 598 additions and 112 deletions

View File

@@ -165,6 +165,7 @@ describe("getIssuedOrderTotals (issued orders) per-currency aggregation", () =>
{ description: "X", quantity: qty, unit_price: price, vat_rate: 21 },
],
});
if ("error" in o) throw new Error(`createIssuedOrder failed: ${o.error}`);
createdIssuedIds.push(o.id);
return o.id;
};
@@ -195,6 +196,8 @@ describe("getIssuedOrderTotals (issued orders) per-currency aggregation", () =>
{ description: "X", quantity: 1, unit_price: 1000, vat_rate: 21 },
],
});
if ("error" in draft)
throw new Error(`createIssuedOrder failed: ${draft.error}`);
createdIssuedIds.push(draft.id);
const sent = await createIssuedOrder({
@@ -207,6 +210,8 @@ describe("getIssuedOrderTotals (issued orders) per-currency aggregation", () =>
{ description: "X", quantity: 1, unit_price: 1000, vat_rate: 21 },
],
});
if ("error" in sent)
throw new Error(`createIssuedOrder failed: ${sent.error}`);
createdIssuedIds.push(sent.id);
const next = await createIssuedOrder({
@@ -218,6 +223,8 @@ describe("getIssuedOrderTotals (issued orders) per-currency aggregation", () =>
{ description: "X", quantity: 1, unit_price: 1000, vat_rate: 21 },
],
});
if ("error" in next)
throw new Error(`createIssuedOrder failed: ${next.error}`);
createdIssuedIds.push(next.id);
// Whole target month: both count -> 2 x 1210 = 2420.