diff --git a/CLAUDE.md b/CLAUDE.md index 75efc36..559c6c4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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) | --- diff --git a/docs/superpowers/plans/2026-06-09-issued-orders.md b/docs/superpowers/plans/2026-06-09-issued-orders.md index 81eb64c..6444d10 100644 --- a/docs/superpowers/plans/2026-06-09-issued-orders.md +++ b/docs/superpowers/plans/2026-06-09-issued-orders.md @@ -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`. diff --git a/docs/superpowers/specs/2026-06-09-issued-orders-design.md b/docs/superpowers/specs/2026-06-09-issued-orders-design.md index 2d6a9b3..459d73b 100644 --- a/docs/superpowers/specs/2026-06-09-issued-orders-design.md +++ b/docs/superpowers/specs/2026-06-09-issued-orders-design.md @@ -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