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:
@@ -0,0 +1,57 @@
|
||||
-- Phase 129-01: extend order_notes o pola dla notatek autorskich (user-authored)
|
||||
-- Reuse istniejacej tabeli z nowym note_type='user', user_id (FK->users), author_name (snapshot).
|
||||
-- Idempotentna: guard przez information_schema; no-op po pierwszym uruchomieniu.
|
||||
-- Pattern z Key Decision 2026-05-10: migracje no-op zawsze jako DDL (ALTER TABLE COMMENT),
|
||||
-- nigdy SELECT 1 (PDO unbuffered + result set -> SQLSTATE 2014).
|
||||
|
||||
-- 1) ADD COLUMN user_id
|
||||
SET @col_user_id := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'order_notes'
|
||||
AND COLUMN_NAME = 'user_id'
|
||||
);
|
||||
SET @sql_user_id := IF(@col_user_id = 0,
|
||||
'ALTER TABLE order_notes ADD COLUMN user_id INT UNSIGNED NULL AFTER note_type',
|
||||
'ALTER TABLE order_notes COMMENT = ''phase-129 user_id no-op'''
|
||||
);
|
||||
PREPARE s1 FROM @sql_user_id; EXECUTE s1; DEALLOCATE PREPARE s1;
|
||||
|
||||
-- 2) ADD COLUMN author_name
|
||||
SET @col_author_name := (
|
||||
SELECT COUNT(*) FROM information_schema.COLUMNS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'order_notes'
|
||||
AND COLUMN_NAME = 'author_name'
|
||||
);
|
||||
SET @sql_author_name := IF(@col_author_name = 0,
|
||||
'ALTER TABLE order_notes ADD COLUMN author_name VARCHAR(190) NULL AFTER user_id',
|
||||
'ALTER TABLE order_notes COMMENT = ''phase-129 author_name no-op'''
|
||||
);
|
||||
PREPARE s2 FROM @sql_author_name; EXECUTE s2; DEALLOCATE PREPARE s2;
|
||||
|
||||
-- 3) ADD FOREIGN KEY user_id -> users(id) ON DELETE SET NULL
|
||||
SET @fk_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.TABLE_CONSTRAINTS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'order_notes'
|
||||
AND CONSTRAINT_NAME = 'order_notes_user_fk'
|
||||
);
|
||||
SET @sql_fk := IF(@fk_exists = 0,
|
||||
'ALTER TABLE order_notes ADD CONSTRAINT order_notes_user_fk FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL ON UPDATE CASCADE',
|
||||
'ALTER TABLE order_notes COMMENT = ''phase-129 fk no-op'''
|
||||
);
|
||||
PREPARE s3 FROM @sql_fk; EXECUTE s3; DEALLOCATE PREPARE s3;
|
||||
|
||||
-- 4) ADD INDEX (note_type, order_id) — wspiera subquery user_notes_count i listUserNotes
|
||||
SET @idx_exists := (
|
||||
SELECT COUNT(*) FROM information_schema.STATISTICS
|
||||
WHERE TABLE_SCHEMA = DATABASE()
|
||||
AND TABLE_NAME = 'order_notes'
|
||||
AND INDEX_NAME = 'idx_order_notes_type_order'
|
||||
);
|
||||
SET @sql_idx := IF(@idx_exists = 0,
|
||||
'ALTER TABLE order_notes ADD INDEX idx_order_notes_type_order (note_type, order_id)',
|
||||
'ALTER TABLE order_notes COMMENT = ''phase-129 idx no-op'''
|
||||
);
|
||||
PREPARE s4 FROM @sql_idx; EXECUTE s4; DEALLOCATE PREPARE s4;
|
||||
Reference in New Issue
Block a user