feat(129): order user notes module
CRUD notatek autorskich operatora per zamowienie z badge [N] na liscie
zamowien. Reuse istniejacej tabeli `order_notes` przez nowy
`note_type='user'` z `user_id` (FK->users SET NULL) i `author_name`
(snapshot). Sekcja `#notes` w "Wiadomosci i zalaczniki" w
`/orders/{id}` z inline edit form + delete przez
`OrderProAlerts.confirm`. Autoryzacja DB-level
(`WHERE user_id = :user_id`, rowCount=0 ⇒ 403) — bez admin override
(brak systemu rol w aplikacji).
- Migracja `20260514_000116_*.sql` (ADD COLUMN user_id + author_name +
FK + indeks `idx_order_notes_type_order`); idempotentne z DDL
no-op fallback.
- `OrderNotesService` (CRUD + walidacja body ≤ 2000 znakow); subquery
`user_notes_count` w paginate; badge HTML w `toTableRow()`.
- 3 routy POST /orders/{id}/notes(/update|/delete).
- SCSS module `_order-notes.scss` + vanilla JS `order-notes.js`
(inline edit toggle + delete confirm; idempotent guard).
- 9 kluczy i18n PL; PROJECT.md + ROADMAP.md + tech_changelog.md +
db_schema.md zaktualizowane.
Follow-up: `php bin/migrate.php` + manualny smoke test (autor vs inny
user + badge na /orders/list).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -328,6 +328,26 @@ UNIQUE: `(integration_id, external_order_id)`
|
||||
|
||||
UNIQUE: `(order_id, source_payment_id)`
|
||||
|
||||
**order_notes** — Notatki przypisane do zamówienia (importowane ze źródła + autorskie operatora)
|
||||
| Column | Type | Nullable | Notes |
|
||||
|--------|------|----------|-------|
|
||||
| `id` | BIGINT UNSIGNED | NO | PK, AUTO_INCREMENT |
|
||||
| `order_id` | BIGINT UNSIGNED | NO | FK → orders(id) CASCADE |
|
||||
| `source_note_id` | VARCHAR(64) | YES | ID notatki ze źródła (shopPRO/Allegro); NULL dla notatek autorskich |
|
||||
| `note_type` | VARCHAR(32) | NO | `shoppro`/`allegro`/`message` (imported) lub `user` (Phase 129 — autorska notatka operatora) |
|
||||
| `user_id` | INT UNSIGNED | YES | FK → users(id) ON DELETE SET NULL (Phase 129); set tylko dla `note_type='user'` |
|
||||
| `author_name` | VARCHAR(190) | YES | Snapshot `users.name` w momencie tworzenia (Phase 129); chroni przed zmianą nazwy usera |
|
||||
| `created_at_external` | DATETIME | YES | Data ze źródła (import); NULL dla `note_type='user'` |
|
||||
| `comment` | TEXT | NO | Treść notatki (reuse dla `note_type='user'` jako body) |
|
||||
| `payload_json` | JSON | YES | Raw payload ze źródła; NULL dla `note_type='user'` |
|
||||
| `created_at` | DATETIME | NO | DEFAULT CURRENT_TIMESTAMP |
|
||||
| `updated_at` | DATETIME | NO | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|
||||
|
||||
UNIQUE: `(order_id, source_note_id)` — note: MySQL traktuje wiele NULL jako unique, więc nie blokuje wielu rekordów `note_type='user'` (source_note_id zawsze NULL).
|
||||
Indexes: `order_notes_order_idx (order_id)`, `idx_order_notes_type_order (note_type, order_id)` (Phase 129 — wspiera subquery `user_notes_count` na liście zamówień i `listUserNotes`).
|
||||
|
||||
> Note (Phase 129-01, 2026-05-14): Dodano `user_id`/`author_name` oraz `note_type='user'` dla notatek autorskich operatora. Edycja/usuwanie dozwolone tylko dla autora (`note.user_id === session.user_id`) — brak admin override (brak systemu ról w aplikacji). Importowane notatki ze źródła (`note_type IN ('shoppro','allegro','message')`) zachowują `user_id=NULL` i pozostają nieedytowalne.
|
||||
|
||||
---
|
||||
|
||||
## Order Statuses
|
||||
|
||||
Reference in New Issue
Block a user