docs(orders): sync spec/plan/CLAUDE.md to as-built (Vydané-first tabs, month filter, shared PDF template, Prisma 7 / React 19)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
BOHA
2026-06-09 13:50:41 +02:00
parent 62f50d31af
commit afef0e6759
3 changed files with 79 additions and 21 deletions

View File

@@ -7,19 +7,19 @@ Handles attendance, invoicing, leave/trips, projects, vehicles, and HR operation
## Tech Stack
| Layer | Technology |
| -------------- | -------------------------------------------------------------- |
| Runtime | Node.js, TypeScript 5.9.3 (strict) |
| HTTP Framework | Fastify 5.8.2 |
| ORM | Prisma 6.19.2 → MySQL |
| Auth | JWT (HS256, 15 min) + TOTP 2FA (RFC 6238, otpauth) + bcryptjs |
| Validation | Zod 4.3.6 |
| Frontend | React 18.3.1 + Vite 8.0.0 + Material UI v7 (Emotion) |
| Testing | Vitest 4.1.0 + Supertest |
| PDF | Puppeteer 24.x |
| Email | nodemailer 8.x |
| Cron | node-cron 4.x |
| AI | @anthropic-ai/sdk 0.102 — Claude Sonnet 4.6 ("Odin" assistant) |
| Layer | Technology |
| -------------- | ---------------------------------------------------------------------------------------------------------------- |
| Runtime | Node.js, TypeScript 5.9.3 (strict) |
| HTTP Framework | Fastify 5.8.2 |
| ORM | Prisma 7.8.0 (Rust-free client + @prisma/adapter-mariadb driver adapter; datasource in prisma.config.ts) → MySQL |
| Auth | JWT (HS256, 15 min) + TOTP 2FA (RFC 6238, otpauth) + bcryptjs |
| Validation | Zod 4.3.6 |
| Frontend | React 19.2.7 + Vite 8.0.0 + Material UI v7 (Emotion) |
| Testing | Vitest 4.1.0 + Supertest |
| PDF | Puppeteer 24.x |
| Email | nodemailer 8.x |
| Cron | node-cron 4.x |
| AI | @anthropic-ai/sdk 0.102 — Claude Sonnet 4.6 ("Odin" assistant) |
---

View File

@@ -2412,3 +2412,34 @@ If the smoke test surfaced fixes, commit them. Then use **superpowers:finishing-
- **Permissions:** reuse `orders.view/create/edit/delete/export` (verified to exist in `seed.ts`) — no permission migration, matching the spec.
- **VAT regime:** NET + VAT-on-top in `computeIssuedOrderTotals`, the PDF, and the form's live totals — consistent across all three, and deliberately opposite to `received_invoices` (gross).
- **Naming consistency:** `issued_orders` / `issued_order_items` / `po_number` / `issued_orders_status` / `generateIssuedOrderNumber` / `issuedOrderListOptions` / `entityType: "issued_order"` are used identically in every task.
---
## Post-plan consistency pass (as-built, 2026-06-09)
All tasks above executed as written. The following changes landed **after** the
plan ran, to align the feature with the existing Invoices page and shared
document suite (these supersede the corresponding sketches above):
- **Vydané-first tabs + shared month filter.** Tabs ship as **`Vydané | Přijaté`**
(Vydané default, mirroring Invoices; tab values `vydane`/`prijate`, persisted in
`?tab=`). An **Invoices-style chevron month/year selector** sits above the tabs
and filters **both** lists: Vydané by `order_date`, Přijaté by `created_at`. The
pre-existing **Přijaté search bug** (search term sent but ignored by the backend)
was also fixed.
- **Shared-template PDF.** The issued-order PDF was **rebuilt on the shared
invoice / order-confirmation red-accent template** (titled **OBJEDNÁVKA**)
instead of the custom layout in Task 7. PO direction is flipped vs an invoice:
the **customer = "Dodavatel" (supplier)** and the **company = "Odběratel" (buyer)**.
Still on-demand `application/pdf`, no NAS persistence.
- **Orders.tsx parent shell.** `Orders.tsx` was restructured into a **parent shell**
owning the `PageHeader` + month/year selector + tabs; `IssuedOrders.tsx` and
`OrdersReceived.tsx` became **content-only children** taking `month`/`year` props.
`OrdersReceived`'s create modal was **hoisted** to the shell (`createOpen`/
`setCreateOpen`, the ReceivedInvoices pattern). **No KPI/stat cards** on the
landing (deliberate — orders have no paid/overdue semantics).
- **Two review-fix commits** also landed: cross-year number release (release the
PO number against the correct sequence year, not always the current year);
authenticated PDF open from the list (open the PDF via the token-aware fetch
rather than a bare link); and the detail-page back button routing to
`/orders?tab=vydane`.

View File

@@ -4,6 +4,13 @@
**Status:** Approved (brainstorm) → ready for implementation plan
**Author:** brainstorm session
> **Updated 2026-06-09 (as-built):** Shipped with three deviations from this
> brainstorm — **Vydané-first tabs** (Vydané | Přijaté, mirroring Invoices), a
> **shared chevron month/year filter** above the tabs that filters both lists,
> and a PDF **rebuilt on the shared invoice/order-confirmation template** rather
> than the custom layout sketched below. See the affected sections (UI placement,
> PDF export, Frontend) and the plan's "Post-plan consistency pass" note.
---
## Goal
@@ -39,8 +46,12 @@ page already splits **Vydané | Přijaté**.
used as a generic business partner. No new supplier/vendor master.
3. **Linkage.** **Standalone document** in v1. No automatic links to warehouse or
received invoices.
4. **UI placement.** **Tabs on the Orders page** (`Přijaté | Vydané`), mirroring
the Invoices page.
4. **UI placement.** **Tabs on the Orders page**, mirroring the Invoices page.
_(As-built: **Vydané first / Přijaté second**`Vydané | Přijaté`, tab values
`vydane`/`prijate`, persisted in `?tab=` — matching the Invoices page order. A
shared **chevron month/year selector** sits above the tabs and filters BOTH
lists: Vydané by `order_date`, Přijaté by `created_at`. The pre-existing Přijaté
search bug — search term sent but ignored by the backend — was also fixed.)_
5. **Model shape.** A **dedicated `issued_orders` + `issued_order_items`** pair,
mirroring `invoices`/`invoice_items`_not_ an extension of `orders` and _not_
an upload-only record like `received_invoices`.
@@ -232,12 +243,22 @@ its Czech label in `src/admin/lib/entityTypeLabels.ts` (keyed to the server's
## PDF export
New route + template `src/routes/admin/issued-orders-pdf.ts`, modeled on
`invoices-pdf.ts`, reusing `htmlToPdf` (Puppeteer) from `src/utils/pdf.ts`:
> **As-built (2026-06-09):** rather than the custom/compact layout this section
> originally sketched, the issued-order PDF was **rebuilt on the shared
> invoice / order-confirmation red-accent (#de3a3a) template** (`src/routes/admin/issued-orders-pdf.ts`),
> titled **OBJEDNÁVKA**, for visual consistency with the rest of the document
> suite. Because a PO is a buying document, the direction is **flipped vs an
> invoice**: the selected **customer record renders as "Dodavatel" (supplier)**
> and **your company as "Odběratel" (buyer)**. It is generated **on demand** and
> streamed as `application/pdf` — **no NAS persistence**. Sanitization,
> on-demand/no-NAS behavior, and the NET+VAT-on-top totals below are unchanged.
- **Sender block** = your company (`company_settings`: name, IČO, DIČ, address,
logo as base64). **Recipient block** = the selected supplier (the `customers`
record: name, IČO/`company_id`, DIČ/`vat_id`, address).
New route + template `src/routes/admin/issued-orders-pdf.ts`, reusing
`htmlToPdf` (Puppeteer) from `src/utils/pdf.ts`:
- **Dodavatel (supplier) block** = the selected `customers` record (name,
IČO/`company_id`, DIČ/`vat_id`, address). **Odběratel (buyer) block** = your
company (`company_settings`: name, IČO, DIČ, address, logo as base64).
- Body: items table → VAT recap grouped by rate → **NET total + VAT + gross**.
- `cs` / `en` strings selected by `language`. Czech date/currency via the
existing formatters.
@@ -253,11 +274,17 @@ New route + template `src/routes/admin/issued-orders-pdf.ts`, modeled on
## Frontend
- **`src/admin/pages/Orders.tsx` — add `Přijaté | Vydané` tabs** using the same
- **`src/admin/pages/Orders.tsx` — add `Vydané | Přijaté` tabs** using the same
MUI Tabs pattern as `Invoices.tsx`. The current orders list moves **verbatim**
into the **Přijaté** tab — every hook, mutation, `invalidate` array, validation,
and permission check preserved unchanged; only presentation is reorganized. The
**Vydané** tab renders the new list.
_(As-built: `Orders.tsx` became a **parent shell** that owns the `PageHeader` +
the shared month/year selector + the tabs; `IssuedOrders.tsx` (Vydané) and
`OrdersReceived.tsx` (Přijaté) are **content-only children** receiving `month`/`year`
props. `OrdersReceived`'s create modal is **hoisted** to the shell via
`createOpen`/`setCreateOpen`, the ReceivedInvoices pattern. **No KPI/stat cards**
on the Orders landing — deliberate, since orders have no paid/overdue semantics.)_
- **Vydané list** — `DataTable` columns: `po_number`, supplier (customer name),
status chip (color per status), `order_date`, total (currency), actions (open,
**Export PDF**, delete). `FilterBar` with status + customer `Select`s and search