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:
26
CLAUDE.md
26
CLAUDE.md
@@ -7,19 +7,19 @@ Handles attendance, invoicing, leave/trips, projects, vehicles, and HR operation
|
|||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
| Layer | Technology |
|
| Layer | Technology |
|
||||||
| -------------- | -------------------------------------------------------------- |
|
| -------------- | ---------------------------------------------------------------------------------------------------------------- |
|
||||||
| Runtime | Node.js, TypeScript 5.9.3 (strict) |
|
| Runtime | Node.js, TypeScript 5.9.3 (strict) |
|
||||||
| HTTP Framework | Fastify 5.8.2 |
|
| HTTP Framework | Fastify 5.8.2 |
|
||||||
| ORM | Prisma 6.19.2 → MySQL |
|
| 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 |
|
| Auth | JWT (HS256, 15 min) + TOTP 2FA (RFC 6238, otpauth) + bcryptjs |
|
||||||
| Validation | Zod 4.3.6 |
|
| Validation | Zod 4.3.6 |
|
||||||
| Frontend | React 18.3.1 + Vite 8.0.0 + Material UI v7 (Emotion) |
|
| Frontend | React 19.2.7 + Vite 8.0.0 + Material UI v7 (Emotion) |
|
||||||
| Testing | Vitest 4.1.0 + Supertest |
|
| Testing | Vitest 4.1.0 + Supertest |
|
||||||
| PDF | Puppeteer 24.x |
|
| PDF | Puppeteer 24.x |
|
||||||
| Email | nodemailer 8.x |
|
| Email | nodemailer 8.x |
|
||||||
| Cron | node-cron 4.x |
|
| Cron | node-cron 4.x |
|
||||||
| AI | @anthropic-ai/sdk 0.102 — Claude Sonnet 4.6 ("Odin" assistant) |
|
| AI | @anthropic-ai/sdk 0.102 — Claude Sonnet 4.6 ("Odin" assistant) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -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.
|
- **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).
|
- **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.
|
- **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`.
|
||||||
|
|||||||
@@ -4,6 +4,13 @@
|
|||||||
**Status:** Approved (brainstorm) → ready for implementation plan
|
**Status:** Approved (brainstorm) → ready for implementation plan
|
||||||
**Author:** brainstorm session
|
**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
|
## Goal
|
||||||
@@ -39,8 +46,12 @@ page already splits **Vydané | Přijaté**.
|
|||||||
used as a generic business partner. No new supplier/vendor master.
|
used as a generic business partner. No new supplier/vendor master.
|
||||||
3. **Linkage.** **Standalone document** in v1. No automatic links to warehouse or
|
3. **Linkage.** **Standalone document** in v1. No automatic links to warehouse or
|
||||||
received invoices.
|
received invoices.
|
||||||
4. **UI placement.** **Tabs on the Orders page** (`Přijaté | Vydané`), mirroring
|
4. **UI placement.** **Tabs on the Orders page**, mirroring the Invoices page.
|
||||||
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,
|
5. **Model shape.** A **dedicated `issued_orders` + `issued_order_items`** pair,
|
||||||
mirroring `invoices`/`invoice_items` — _not_ an extension of `orders` and _not_
|
mirroring `invoices`/`invoice_items` — _not_ an extension of `orders` and _not_
|
||||||
an upload-only record like `received_invoices`.
|
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
|
## PDF export
|
||||||
|
|
||||||
New route + template `src/routes/admin/issued-orders-pdf.ts`, modeled on
|
> **As-built (2026-06-09):** rather than the custom/compact layout this section
|
||||||
`invoices-pdf.ts`, reusing `htmlToPdf` (Puppeteer) from `src/utils/pdf.ts`:
|
> 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,
|
New route + template `src/routes/admin/issued-orders-pdf.ts`, reusing
|
||||||
logo as base64). **Recipient block** = the selected supplier (the `customers`
|
`htmlToPdf` (Puppeteer) from `src/utils/pdf.ts`:
|
||||||
record: name, IČO/`company_id`, DIČ/`vat_id`, address).
|
|
||||||
|
- **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**.
|
- Body: items table → VAT recap grouped by rate → **NET total + VAT + gross**.
|
||||||
- `cs` / `en` strings selected by `language`. Czech date/currency via the
|
- `cs` / `en` strings selected by `language`. Czech date/currency via the
|
||||||
existing formatters.
|
existing formatters.
|
||||||
@@ -253,11 +274,17 @@ New route + template `src/routes/admin/issued-orders-pdf.ts`, modeled on
|
|||||||
|
|
||||||
## Frontend
|
## 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**
|
MUI Tabs pattern as `Invoices.tsx`. The current orders list moves **verbatim**
|
||||||
into the **Přijaté** tab — every hook, mutation, `invalidate` array, validation,
|
into the **Přijaté** tab — every hook, mutation, `invalidate` array, validation,
|
||||||
and permission check preserved unchanged; only presentation is reorganized. The
|
and permission check preserved unchanged; only presentation is reorganized. The
|
||||||
**Vydané** tab renders the new list.
|
**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),
|
- **Vydané list** — `DataTable` columns: `po_number`, supplier (customer name),
|
||||||
status chip (color per status), `order_date`, total (currency), actions (open,
|
status chip (color per status), `order_date`, total (currency), actions (open,
|
||||||
**Export PDF**, delete). `FilterBar` with status + customer `Select`s and search
|
**Export PDF**, delete). `FilterBar` with status + customer `Select`s and search
|
||||||
|
|||||||
Reference in New Issue
Block a user