fix(144): count imported order notes in list badge
Phase 144 complete: - Count all order_notes rows in the /orders/list notes badge - Add regression coverage for imported plus operator notes - Document the combined notes badge contract Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
@@ -13,8 +13,8 @@ Sprzedawca może obsługiwać zamówienia ze wszystkich kanałów
|
|||||||
| Attribute | Value |
|
| Attribute | Value |
|
||||||
|-----------|-------|
|
|-----------|-------|
|
||||||
| Version | 3.9.0-dev |
|
| Version | 3.9.0-dev |
|
||||||
| Status | v3.12 Orders List Sidebar UI Hotfix complete - Phase 143 closed |
|
| Status | v3.13 Imported Notes Badge Count Hotfix complete - Phase 144 closed |
|
||||||
| Last Updated | 2026-05-18 (Phase 143 unified) |
|
| Last Updated | 2026-05-18 (Phase 144 unified) |
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
@@ -143,9 +143,10 @@ Sprzedawca może obsługiwać zamówienia ze wszystkich kanałów
|
|||||||
- [x] Integrations Hub Grouped Sections: `/settings/integrations` pokazuje lekkie sekcje dla marketplace, kurierow i pozostalych integracji, bez starego naglowka/opisu wspolnego panelu — Phase 141
|
- [x] Integrations Hub Grouped Sections: `/settings/integrations` pokazuje lekkie sekcje dla marketplace, kurierow i pozostalych integracji, bez starego naglowka/opisu wspolnego panelu — Phase 141
|
||||||
- [x] Polkurier Shipment Prepare Prefill: `/orders/{id}/shipment/prepare` rozpoznaje mapowania shopPRO z `provider='polkurier'`, preselectuje przewoznika i usluge oraz nie fallbackuje do Allegro — Phase 142
|
- [x] Polkurier Shipment Prepare Prefill: `/orders/{id}/shipment/prepare` rozpoznaje mapowania shopPRO z `provider='polkurier'`, preselectuje przewoznika i usluge oraz nie fallbackuje do Allegro — Phase 142
|
||||||
- [x] Orders List Sidebar UI Hotfix: `/orders/list` startuje bez opisowego boksu "Zamowienia", a zapisany zwiniety sidebar jest stosowany przed pierwszym renderem strony — Phase 143
|
- [x] Orders List Sidebar UI Hotfix: `/orders/list` startuje bez opisowego boksu "Zamowienia", a zapisany zwiniety sidebar jest stosowany przed pierwszym renderem strony — Phase 143
|
||||||
|
- [x] Imported Notes Badge Count Hotfix: badge `[N]` na `/orders/list` zlicza wszystkie `order_notes` zamowienia, lacznie z notatkami importowanymi z shopPRO i notatkami autorskimi operatora — Phase 144
|
||||||
- [x] Integracja polkurier.pl (fundament): pojedyncza globalna konfiguracja w `/settings/integrations/polkurier`, szyfrowany Token API + login, karta w hubie integracji obok Apaczki i realny test polaczenia przez `apimetod=test_auth_api` zweryfikowany na zywym koncie operatora; `ShipmentProviderRegistry` netkniety — `PolkurierShipmentService/TrackingService` w kolejnych fazach — Phase 127
|
- [x] Integracja polkurier.pl (fundament): pojedyncza globalna konfiguracja w `/settings/integrations/polkurier`, szyfrowany Token API + login, karta w hubie integracji obok Apaczki i realny test polaczenia przez `apimetod=test_auth_api` zweryfikowany na zywym koncie operatora; `ShipmentProviderRegistry` netkniety — `PolkurierShipmentService/TrackingService` w kolejnych fazach — Phase 127
|
||||||
- [x] polkurier ShipmentService + TrackingService + UI prepare panel: pelen kontrakt API (createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers), `PolkurierShipmentService` implementujacy `ShipmentProviderInterface` z normalizacja shipmenttype (lowercase) i splitem ulicy na street/housenumber/flatnumber, `PolkurierTrackingService` mapujacy statusy O/P/A/WP/D/Z/W na znormalizowane, panel "polkurier" w `prepare.php` z dynamiczna lista uslug z `available_carriers`, seed migracja `delivery_status_mappings(provider='polkurier')` z 7 wpisami z PDF v1.11; live test na #114/#115 zakonczony sukcesem po 4 iteracjach (ReferenceError → uppercase shipmenttype → orderno parsing → A4/A6); rozmiar etykiety sterowany w panelu klienta polkurier.pl (Ustawienia konta → Preferencje etykiet), NIE przez API — Phase 128
|
- [x] polkurier ShipmentService + TrackingService + UI prepare panel: pelen kontrakt API (createShipment/getLabel/getStatus/cancelOrder/getAvailableCarriers), `PolkurierShipmentService` implementujacy `ShipmentProviderInterface` z normalizacja shipmenttype (lowercase) i splitem ulicy na street/housenumber/flatnumber, `PolkurierTrackingService` mapujacy statusy O/P/A/WP/D/Z/W na znormalizowane, panel "polkurier" w `prepare.php` z dynamiczna lista uslug z `available_carriers`, seed migracja `delivery_status_mappings(provider='polkurier')` z 7 wpisami z PDF v1.11; live test na #114/#115 zakonczony sukcesem po 4 iteracjach (ReferenceError → uppercase shipmenttype → orderno parsing → A4/A6); rozmiar etykiety sterowany w panelu klienta polkurier.pl (Ustawienia konta → Preferencje etykiet), NIE przez API — Phase 128
|
||||||
- [x] Order User Notes module (Phase 129): pelen CRUD notatek autorskich operatora per zamowienie. Reuse `order_notes` przez nowy `note_type='user'` z `user_id` (FK→users SET NULL) + `author_name` (snapshot) + indeks `idx_order_notes_type_order`. `OrderNotesService` z autoryzacja DB-level (`WHERE user_id = :user_id`, rowCount=0 ⇒ 403). Sekcja `#notes` w "Wiadomosci i zalaczniki" w `/orders/{id}` z inline edit form + delete przez `OrderProAlerts.confirm`. Badge `[N]` (indigo neutralny) przy nr zamowienia na `/orders/list` (subquery `user_notes_count` w paginate). Brak admin override (brak systemu rol w aplikacji) — edit/delete tylko dla autora — Phase 129
|
- [x] Order User Notes module (Phase 129): pelen CRUD notatek autorskich operatora per zamowienie. Reuse `order_notes` przez nowy `note_type='user'` z `user_id` (FK→users SET NULL) + `author_name` (snapshot) + indeks `idx_order_notes_type_order`. `OrderNotesService` z autoryzacja DB-level (`WHERE user_id = :user_id`, rowCount=0 ⇒ 403). Sekcja `#notes` w "Wiadomosci i zalaczniki" w `/orders/{id}` z inline edit form + delete przez `OrderProAlerts.confirm`. Badge `[N]` (indigo neutralny) przy nr zamowienia na `/orders/list`; od Phase 144 badge korzysta z `notes_count` i liczy wszystkie rekordy `order_notes`, takze importowane ze zrodla. Brak admin override (brak systemu rol w aplikacji) — edit/delete tylko dla autora — Phase 129/144
|
||||||
- [x] polkurier delivery status mappings UI (Phase 130): polkurier jako 4. provider w dropdownie `/settings/delivery-statuses?tab=mapping`. `POLKURIER_MAP` + `POLKURIER_DESCRIPTIONS` w `DeliveryStatus.php` (7 wpisow O/P/A/WP/D/Z/W z oficjalnej dokumentacji v1.11, identyczne z migracja Phase 128 — DB seed staje sie no-op). `PROVIDERS` rozszerzone w `DeliveryStatusesController` + `DeliveryStatusMappingController`. `countAllUnmappedForBadge()` zlicza polkurier. Zero zmian w widoku (`_delivery-status-mappings-content.php` auto-iteruje po providerach z controllera) — Phase 130
|
- [x] polkurier delivery status mappings UI (Phase 130): polkurier jako 4. provider w dropdownie `/settings/delivery-statuses?tab=mapping`. `POLKURIER_MAP` + `POLKURIER_DESCRIPTIONS` w `DeliveryStatus.php` (7 wpisow O/P/A/WP/D/Z/W z oficjalnej dokumentacji v1.11, identyczne z migracja Phase 128 — DB seed staje sie no-op). `PROVIDERS` rozszerzone w `DeliveryStatusesController` + `DeliveryStatusMappingController`. `countAllUnmappedForBadge()` zlicza polkurier. Zero zmian w widoku (`_delivery-status-mappings-content.php` auto-iteruje po providerach z controllera) — Phase 130
|
||||||
|
|
||||||
### Deferred
|
### Deferred
|
||||||
@@ -325,6 +326,6 @@ Quick Reference:
|
|||||||
|
|
||||||
---
|
---
|
||||||
*PROJECT.md — Updated when requirements or context change*
|
*PROJECT.md — Updated when requirements or context change*
|
||||||
*Last updated: 2026-05-18 after Phase 143 closure*
|
*Last updated: 2026-05-18 after Phase 144 closure*
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,23 @@ orderPRO to narzedzie do wielokanalowego zarzadzania sprzedaza. Projekt przechod
|
|||||||
|
|
||||||
## Current Milestone
|
## Current Milestone
|
||||||
|
|
||||||
|
v3.13 Imported Notes Badge Count Hotfix - Complete
|
||||||
|
|
||||||
|
Pilny hotfix dla listy zamowien: notatki zaimportowane ze zrodla, np. shopPRO, maja byc zliczane razem z notatkami autorskimi w badge `[N]` przy numerze zamowienia.
|
||||||
|
|
||||||
|
Progress: 1 of 1 phases complete (100%).
|
||||||
|
|
||||||
|
| Phase | Name | Plans | Status |
|
||||||
|
|-------|------|-------|--------|
|
||||||
|
| 144 | Imported Notes Badge Count Hotfix | 1/1 | Complete (2026-05-18; PHPUnit/Sonar env gaps documented) |
|
||||||
|
|
||||||
|
### Phase 144: Imported Notes Badge Count Hotfix
|
||||||
|
|
||||||
|
Focus: Zmienic licznik badge notatek na `/orders/list`, aby uzywal wszystkich rekordow `order_notes` dla zamowienia, a nie tylko `note_type='user'`. Zamowienie `1034` z importowana notatka shopPRO powinno pokazac cyfre na liscie.
|
||||||
|
Plans: 144-01 (complete; `.paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md`)
|
||||||
|
|
||||||
|
## Previous Milestone
|
||||||
|
|
||||||
v3.12 Orders List Sidebar UI Hotfix - Complete
|
v3.12 Orders List Sidebar UI Hotfix - Complete
|
||||||
|
|
||||||
Maly hotfix UX dla ekranu operacyjnego: usuniecie opisowego boksu z listy zamowien oraz likwidacja widocznego "pokaz i schowaj" przy odswiezaniu strony ze zwinietym sidebarem.
|
Maly hotfix UX dla ekranu operacyjnego: usuniecie opisowego boksu z listy zamowien oraz likwidacja widocznego "pokaz i schowaj" przy odswiezaniu strony ze zwinietym sidebarem.
|
||||||
@@ -21,7 +38,7 @@ Progress: 1 of 1 phases complete (100%).
|
|||||||
Focus: Usunac boks "Zamowienia / Kompaktowa lista zamowien oparta o lokalna baze orderPRO." z `/orders/list` i zastosowac zapisany stan zwinietego sidebaru przed pierwszym renderem strony.
|
Focus: Usunac boks "Zamowienia / Kompaktowa lista zamowien oparta o lokalna baze orderPRO." z `/orders/list` i zastosowac zapisany stan zwinietego sidebaru przed pierwszym renderem strony.
|
||||||
Plans: 143-01 (complete; `.paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-SUMMARY.md`)
|
Plans: 143-01 (complete; `.paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-SUMMARY.md`)
|
||||||
|
|
||||||
## Previous Milestone
|
## Earlier Milestone
|
||||||
|
|
||||||
v3.11 Polkurier Shipment Prepare Hotfix - Complete
|
v3.11 Polkurier Shipment Prepare Hotfix - Complete
|
||||||
|
|
||||||
@@ -670,4 +687,4 @@ Archive: `.paul/milestones/v0.1-ROADMAP.md`
|
|||||||
|
|
||||||
---
|
---
|
||||||
*Roadmap created: 2026-03-12*
|
*Roadmap created: 2026-03-12*
|
||||||
*Last updated: 2026-05-18 - Phase 143 complete; v3.12 Orders List Sidebar UI Hotfix complete*
|
*Last updated: 2026-05-18 - Phase 144 complete; v3.13 Imported Notes Badge Count Hotfix complete*
|
||||||
|
|||||||
@@ -5,19 +5,19 @@
|
|||||||
See: .paul/PROJECT.md (updated 2026-05-18)
|
See: .paul/PROJECT.md (updated 2026-05-18)
|
||||||
|
|
||||||
**Core value:** Sprzedawca moze obslugiwac zamowienia ze wszystkich kanalow sprzedazy i nadawac przesylki bez przelaczania sie miedzy platformami.
|
**Core value:** Sprzedawca moze obslugiwac zamowienia ze wszystkich kanalow sprzedazy i nadawac przesylki bez przelaczania sie miedzy platformami.
|
||||||
**Current focus:** v3.12 Orders List Sidebar UI Hotfix complete; Phase 143 unified.
|
**Current focus:** v3.13 Imported Notes Badge Count Hotfix complete; Phase 144 unified.
|
||||||
|
|
||||||
## Current Position
|
## Current Position
|
||||||
|
|
||||||
Milestone: v3.12 Orders List Sidebar UI Hotfix
|
Milestone: v3.13 Imported Notes Badge Count Hotfix
|
||||||
Phase: 143 of 143 (Orders List Sidebar UI Hotfix) - Complete
|
Phase: 144 of 144 (Imported Notes Badge Count Hotfix) - Complete
|
||||||
Plan: 143-01 complete
|
Plan: 144-01 complete
|
||||||
Status: Milestone complete, ready for next milestone or release decision
|
Status: Milestone complete, ready for next milestone or release decision
|
||||||
Last activity: 2026-05-18 11:50 - Unified .paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-PLAN.md
|
Last activity: 2026-05-18 12:39 - Unified .paul/phases/144-imported-notes-badge-count/144-01-PLAN.md
|
||||||
|
|
||||||
Progress:
|
Progress:
|
||||||
- Milestone v3.12: [##########] 100% (1 of 1 phases complete)
|
- Milestone v3.13: [##########] 100% (1 of 1 phases complete)
|
||||||
- Phase 143: [##########] 100% (complete)
|
- Phase 144: [##########] 100% (complete)
|
||||||
|
|
||||||
## Loop Position
|
## Loop Position
|
||||||
|
|
||||||
@@ -29,19 +29,19 @@ PLAN -> APPLY -> UNIFY
|
|||||||
|
|
||||||
## Session Continuity
|
## Session Continuity
|
||||||
|
|
||||||
Last session: 2026-05-18 11:50
|
Last session: 2026-05-18 12:39
|
||||||
Stopped at: Phase 143 complete; v3.12 milestone complete
|
Stopped at: Phase 144 complete; v3.13 milestone complete
|
||||||
Next action: Run $paul-complete-milestone or start next milestone planning
|
Next action: Run $paul-complete-milestone or start next milestone planning
|
||||||
Resume file: .paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-SUMMARY.md
|
Resume file: .paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md
|
||||||
|
|
||||||
## Pending parallel work
|
## Pending parallel work
|
||||||
- None — Phase 118, 121, 122 wszystkie zacommitowane (8f14851, 360eef1).
|
- None — Phase 118, 121, 122 wszystkie zacommitowane (8f14851, 360eef1).
|
||||||
|
|
||||||
## Git State
|
## Git State
|
||||||
|
|
||||||
Last commit: HEAD fix(143): polish orders list and sidebar first paint
|
Last commit: HEAD fix(144): count imported order notes in list badge
|
||||||
Last phase commit: HEAD fix(143): polish orders list and sidebar first paint
|
Last phase commit: HEAD fix(144): count imported order notes in list badge
|
||||||
Previous: fix(142): preselect polkurier shipment mapping
|
Previous: fix(143): polish orders list and sidebar first paint
|
||||||
Branch: main
|
Branch: main
|
||||||
|
|
||||||
### Skill Audit (Phase 139)
|
### Skill Audit (Phase 139)
|
||||||
@@ -75,6 +75,12 @@ Branch: main
|
|||||||
|----------|---------|-------|
|
|----------|---------|-------|
|
||||||
| `sonar-scanner` | gap documented | Attempted after APPLY with `sonar-scanner --version`; CLI is not available in PATH. |
|
| `sonar-scanner` | gap documented | Attempted after APPLY with `sonar-scanner --version`; CLI is not available in PATH. |
|
||||||
|
|
||||||
|
### Skill Audit (Phase 144)
|
||||||
|
|
||||||
|
| Expected | Invoked | Notes |
|
||||||
|
|----------|---------|-------|
|
||||||
|
| `sonar-scanner` | gap documented | Attempted after APPLY with `sonar-scanner --version`; CLI is not available in PATH. |
|
||||||
|
|
||||||
### Skill Audit (Phase 129)
|
### Skill Audit (Phase 129)
|
||||||
|
|
||||||
| Expected | Invoked | Notes |
|
| Expected | Invoked | Notes |
|
||||||
@@ -133,6 +139,7 @@ Branch: main
|
|||||||
|
|
||||||
### Recent Decisions
|
### Recent Decisions
|
||||||
|
|
||||||
|
- Phase 144 changed the `/orders/list` notes badge contract from operator-only notes to all `order_notes` rows; detail view grouping and note CRUD/import behavior remain unchanged.
|
||||||
- Phase 134 is documentation-only: no runtime code or schema changes were made.
|
- Phase 134 is documentation-only: no runtime code or schema changes were made.
|
||||||
- Backlog entries are annotated, not deleted; stale/implemented cleanup is deferred to later phases.
|
- Backlog entries are annotated, not deleted; stale/implemented cleanup is deferred to later phases.
|
||||||
- Phase 135 completed confirmed accounting net issues: `RECEIPT-NET-FIX` and `STAT-NET` are resolved for new/runtime behavior.
|
- Phase 135 completed confirmed accounting net issues: `RECEIPT-NET-FIX` and `STAT-NET` are resolved for new/runtime behavior.
|
||||||
@@ -152,6 +159,7 @@ Branch: main
|
|||||||
|
|
||||||
### Blockers / Concerns
|
### Blockers / Concerns
|
||||||
|
|
||||||
|
- Phase 144 APPLY: `vendor/bin/phpunit` is missing, so `tests/Unit/OrdersRepositoryNotesCountTest.php` was linted and covered by an ad-hoc SQLite runtime smoke instead of PHPUnit; `sonar-scanner` is unavailable in PATH.
|
||||||
- Phase 134: `sonar-scanner` is still unavailable in PATH.
|
- Phase 134: `sonar-scanner` is still unavailable in PATH.
|
||||||
- Phase 135: `vendor/bin/phpunit` and `sonar-scanner` are unavailable in PATH/checkout; syntax checks and ad-hoc SQLite/runtime smoke passed.
|
- Phase 135: `vendor/bin/phpunit` and `sonar-scanner` are unavailable in PATH/checkout; syntax checks and ad-hoc SQLite/runtime smoke passed.
|
||||||
- Phase 136: Fakturownia idempotency strategy implemented and UNIFY complete; runtime migration still needs local MySQL online.
|
- Phase 136: Fakturownia idempotency strategy implemented and UNIFY complete; runtime migration still needs local MySQL online.
|
||||||
|
|||||||
@@ -20,6 +20,10 @@
|
|||||||
- Dodano pre-hydration klasy `html.has-collapsed-sidebar`, aby zapisany zwiniety sidebar byl schowany od pierwszego renderu.
|
- Dodano pre-hydration klasy `html.has-collapsed-sidebar`, aby zapisany zwiniety sidebar byl schowany od pierwszego renderu.
|
||||||
- Zachowano runtime toggle sidebaru i mobilne slide-in menu; pre-hydration compact state jest ograniczony do desktopu.
|
- Zachowano runtime toggle sidebaru i mobilne slide-in menu; pre-hydration compact state jest ograniczony do desktopu.
|
||||||
- Gap: manualny smoke UI i SonarQube scan Phase 143 pozostaja do wykonania po uruchomieniu app/browser session i przywroceniu `sonar-scanner`.
|
- Gap: manualny smoke UI i SonarQube scan Phase 143 pozostaja do wykonania po uruchomieniu app/browser session i przywroceniu `sonar-scanner`.
|
||||||
|
- [Phase 144, Plan 01] Naprawiono licznik badge notatek na `/orders/list`, aby obejmowal notatki autorskie i importowane ze zrodla.
|
||||||
|
- `OrdersRepository` zwraca `notes_count` liczony ze wszystkich rekordow `order_notes` dla zamowienia.
|
||||||
|
- Dodano test regresyjny `OrdersRepositoryNotesCountTest` i wykonano SQLite smoke `notes_count=2` dla notatki `message` + `user`.
|
||||||
|
- Gap: PHPUnit i SonarQube scan Phase 144 pozostaja do wykonania po przywroceniu `vendor/` i `sonar-scanner`.
|
||||||
|
|
||||||
## Zmienione pliki
|
## Zmienione pliki
|
||||||
|
|
||||||
@@ -45,9 +49,15 @@
|
|||||||
- `.paul/phases/142-polkurier-shipment-prepare-prefill/142-01-SUMMARY.md`
|
- `.paul/phases/142-polkurier-shipment-prepare-prefill/142-01-SUMMARY.md`
|
||||||
- `.paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-PLAN.md`
|
- `.paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-PLAN.md`
|
||||||
- `.paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-SUMMARY.md`
|
- `.paul/phases/143-orders-list-sidebar-ui-hotfix/143-01-SUMMARY.md`
|
||||||
|
- `.paul/phases/144-imported-notes-badge-count/144-01-PLAN.md`
|
||||||
|
- `.paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md`
|
||||||
- `resources/views/layouts/app.php`
|
- `resources/views/layouts/app.php`
|
||||||
- `resources/views/orders/list.php`
|
- `resources/views/orders/list.php`
|
||||||
- `resources/views/shipments/prepare.php`
|
- `resources/views/shipments/prepare.php`
|
||||||
- `resources/scss/app.scss`
|
- `resources/scss/app.scss`
|
||||||
- `public/assets/css/app.css`
|
- `public/assets/css/app.css`
|
||||||
- `tests/Unit/ShipmentPreparePolkurierMappingTest.php`
|
- `tests/Unit/ShipmentPreparePolkurierMappingTest.php`
|
||||||
|
- `src/Modules/Orders/OrdersRepository.php`
|
||||||
|
- `src/Modules/Orders/OrdersController.php`
|
||||||
|
- `tests/Unit/OrdersRepositoryNotesCountTest.php`
|
||||||
|
- `DOCS/DB_SCHEMA.md`
|
||||||
|
|||||||
179
.paul/phases/144-imported-notes-badge-count/144-01-PLAN.md
Normal file
179
.paul/phases/144-imported-notes-badge-count/144-01-PLAN.md
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
---
|
||||||
|
phase: 144-imported-notes-badge-count
|
||||||
|
plan: 01
|
||||||
|
type: execute
|
||||||
|
wave: 1
|
||||||
|
depends_on: []
|
||||||
|
files_modified:
|
||||||
|
- src/Modules/Orders/OrdersRepository.php
|
||||||
|
- src/Modules/Orders/OrdersController.php
|
||||||
|
- tests/Unit/OrdersRepositoryNotesCountTest.php
|
||||||
|
- DOCS/ARCHITECTURE.md
|
||||||
|
- DOCS/DB_SCHEMA.md
|
||||||
|
- DOCS/TECH_CHANGELOG.md
|
||||||
|
autonomous: true
|
||||||
|
delegation: off
|
||||||
|
---
|
||||||
|
|
||||||
|
<objective>
|
||||||
|
## Goal
|
||||||
|
Naprawic licznik notatek na `/orders/list`, aby badge `[N]` przy numerze zamowienia zliczal wszystkie notatki z `order_notes` dla zamowienia: autorskie `note_type='user'` oraz importowane ze zrodla, np. shopPRO `note_type='message'`.
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
Operator widzi na liscie zamowien, ze zamowienie wymaga uwagi lub zawiera wiadomosc klienta, bez wchodzenia w szczegoly. Obecnie importowana notatka z shopPRO w zamowieniu `1034` jest widoczna w szczegolach, ale nie podbija liczby na liscie.
|
||||||
|
|
||||||
|
## Output
|
||||||
|
Behavior-preserving hotfix w module Orders: zmieniony count subquery i nazewnictwo pola w row/controller, test regresyjny dla importowanych notatek oraz aktualizacja dokumentacji technicznej.
|
||||||
|
</objective>
|
||||||
|
|
||||||
|
<context>
|
||||||
|
<clarifications>
|
||||||
|
- No clarifications needed - proceeding. Uzytkownik jednoznacznie wskazal oczekiwane zachowanie: importowane notatki z shopPRO maja byc zliczane do notatek orderPRO i widoczne jako cyfra na liscie zamowien.
|
||||||
|
</clarifications>
|
||||||
|
|
||||||
|
## Project Context
|
||||||
|
@.paul/PROJECT.md
|
||||||
|
@.paul/ROADMAP.md
|
||||||
|
@.paul/STATE.md
|
||||||
|
@.paul/codebase/architecture.md
|
||||||
|
@.paul/codebase/db_schema.md
|
||||||
|
@AGENTS.md
|
||||||
|
|
||||||
|
## Source Files
|
||||||
|
@src/Modules/Orders/OrdersRepository.php
|
||||||
|
@src/Modules/Orders/OrdersController.php
|
||||||
|
@src/Modules/Orders/OrderNotesService.php
|
||||||
|
@src/Modules/Orders/OrderImportRepository.php
|
||||||
|
@resources/views/orders/show.php
|
||||||
|
@resources/scss/modules/_order-notes.scss
|
||||||
|
@tests/bootstrap.php
|
||||||
|
@DOCS/ARCHITECTURE.md
|
||||||
|
@DOCS/DB_SCHEMA.md
|
||||||
|
@DOCS/TECH_CHANGELOG.md
|
||||||
|
</context>
|
||||||
|
|
||||||
|
<skills>
|
||||||
|
## Required Skills / Checks
|
||||||
|
|
||||||
|
| Skill | Priority | When to Invoke | Loaded? |
|
||||||
|
|-------|----------|----------------|---------|
|
||||||
|
| sonar-scanner | required | Po APPLY, przed UNIFY, jezeli CLI jest dostepny | ○ |
|
||||||
|
|
||||||
|
Note: `.paul/SPECIAL-FLOWS.md` wymaga skanu Sonar po APPLY. Historycznie CLI bywa niedostepny; jezeli nadal go nie ma, udokumentowac gap w SUMMARY/STATE.
|
||||||
|
</skills>
|
||||||
|
|
||||||
|
<acceptance_criteria>
|
||||||
|
|
||||||
|
## AC-1: Importowana notatka podbija badge
|
||||||
|
```gherkin
|
||||||
|
Given zamowienie ma co najmniej jedna notatke w order_notes z note_type innym niz "user"
|
||||||
|
When operator otwiera /orders/list
|
||||||
|
Then przy numerze zamowienia widzi badge [N], gdzie N obejmuje te importowane notatki
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-2: Notatki autorskie nadal sa liczone
|
||||||
|
```gherkin
|
||||||
|
Given zamowienie ma notatki autorskie note_type="user"
|
||||||
|
When operator otwiera /orders/list
|
||||||
|
Then badge [N] nadal obejmuje notatki autorskie i linkuje do /orders/{id}#notes
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-3: Licznik jest suma wszystkich notatek zamowienia
|
||||||
|
```gherkin
|
||||||
|
Given zamowienie ma jedna notatke autorska i jedna importowana
|
||||||
|
When lista zamowien renderuje kolumne numeru
|
||||||
|
Then wyswietlany badge pokazuje [2], a title odmienia liczbe jak dotychczas
|
||||||
|
```
|
||||||
|
|
||||||
|
## AC-4: Brak zmian schematu
|
||||||
|
```gherkin
|
||||||
|
Given obecny schemat ma tabele order_notes i indeks order_notes_order_idx
|
||||||
|
When hotfix jest wdrazany
|
||||||
|
Then nie powstaje nowa migracja ani nowa kolumna, bo problem dotyczy tylko zapytania listy
|
||||||
|
```
|
||||||
|
|
||||||
|
</acceptance_criteria>
|
||||||
|
|
||||||
|
<tasks>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 1: Zmienic licznik listy na wszystkie notatki</name>
|
||||||
|
<files>src/Modules/Orders/OrdersRepository.php, src/Modules/Orders/OrdersController.php</files>
|
||||||
|
<action>
|
||||||
|
W `OrdersRepository::buildListSql()` zastapic `user_notes_count` licznikiem wszystkich rekordow `order_notes` danego zamowienia.
|
||||||
|
- Zmienic prywatny helper z semantyki `userNotesCountSubquerySql()` na neutralne `notesCountSubquerySql()`.
|
||||||
|
- Query ma liczyc `COUNT(*) FROM order_notes WHERE order_id = o.id`, bez filtra `note_type`.
|
||||||
|
- W `transformOrderRow()` zwracac `notes_count`.
|
||||||
|
- W `OrdersController::buildRow()` czytac `notes_count`; title i link badge pozostaja takie jak dotychczas.
|
||||||
|
- Unikac zmiany sekcji szczegolow zamowienia: importowane i autorskie notatki nadal sa tam renderowane osobno.
|
||||||
|
</action>
|
||||||
|
<verify>php -l src/Modules/Orders/OrdersRepository.php && php -l src/Modules/Orders/OrdersController.php</verify>
|
||||||
|
<done>AC-1, AC-2 i AC-3 spelnione: badge bazuje na lacznej liczbie notatek.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 2: Dodac test regresyjny licznika notatek</name>
|
||||||
|
<files>tests/Unit/OrdersRepositoryNotesCountTest.php</files>
|
||||||
|
<action>
|
||||||
|
Dodac test jednostkowy pokrywajacy nowa semantyke licznika.
|
||||||
|
- Utworzyc minimalna baze SQLite in-memory z tabelami wymaganymi przez `OrdersRepository::paginate()`.
|
||||||
|
- Zaseedowac zamowienie z jedna notatka `note_type='message'` i jedna `note_type='user'`.
|
||||||
|
- Wywolac `paginate(['page' => 1, 'per_page' => 20])` i potwierdzic, ze pierwszy wiersz ma `notes_count === 2`.
|
||||||
|
- Dodac drugi przypadek lub asercje dla zamowienia bez notatek: `notes_count === 0`.
|
||||||
|
- Nie testowac widoku szczegolow ani CRUD notatek autorskich, bo to istnieje poza zakresem tego hotfixa.
|
||||||
|
</action>
|
||||||
|
<verify>vendor/bin/phpunit tests/Unit/OrdersRepositoryNotesCountTest.php albo, jezeli vendor/ nadal nie istnieje, php -l tests/Unit/OrdersRepositoryNotesCountTest.php</verify>
|
||||||
|
<done>AC-1, AC-2 i AC-3 maja automatyczna regresje na poziomie repozytorium.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
<task type="auto">
|
||||||
|
<name>Task 3: Zaktualizowac dokumentacje techniczna</name>
|
||||||
|
<files>DOCS/ARCHITECTURE.md, DOCS/DB_SCHEMA.md, DOCS/TECH_CHANGELOG.md</files>
|
||||||
|
<action>
|
||||||
|
Udokumentowac hotfix bez sugerowania zmiany schematu.
|
||||||
|
- `DOCS/ARCHITECTURE.md`: dopisac przy module Orders/list, ze badge notatek zlicza wszystkie `order_notes`, a szczegoly nadal rozdzielaja autorskie i importowane notatki.
|
||||||
|
- `DOCS/DB_SCHEMA.md`: przy `order_notes` doprecyzowac, ze indeks `order_notes_order_idx` wspiera laczny licznik notatek na liscie; `idx_order_notes_type_order` zostaje dla list user/import.
|
||||||
|
- `DOCS/TECH_CHANGELOG.md`: dodac wpis 2026-05-18 z opisem co i dlaczego.
|
||||||
|
</action>
|
||||||
|
<verify>rg -n "notes_count|badge|order_notes" DOCS/ARCHITECTURE.md DOCS/DB_SCHEMA.md DOCS/TECH_CHANGELOG.md</verify>
|
||||||
|
<done>AC-4 spelnione i dokumentacja zgadza sie z nowa semantyka.</done>
|
||||||
|
</task>
|
||||||
|
|
||||||
|
</tasks>
|
||||||
|
|
||||||
|
<boundaries>
|
||||||
|
|
||||||
|
## DO NOT CHANGE
|
||||||
|
- database/migrations/* - brak migracji w tym hotfixie.
|
||||||
|
- src/Modules/Orders/OrderImportRepository.php - import notatek dziala; problem jest w liczniku listy.
|
||||||
|
- src/Modules/Orders/OrderNotesService.php - CRUD notatek autorskich bez zmian.
|
||||||
|
- resources/views/orders/show.php - szczegoly rozdzielaja notatki autorskie/importowane i to zostaje.
|
||||||
|
|
||||||
|
## SCOPE LIMITS
|
||||||
|
- Nie zmieniac wygladu badge, kolorow ani SCSS.
|
||||||
|
- Nie laczyc UI sekcji notatek w szczegolach zamowienia.
|
||||||
|
- Nie dodawac filtrow po notatkach ani nowych endpointow.
|
||||||
|
- Nie wykonywac operacji na zdalnej bazie poza ewentualna read-only weryfikacja po akceptacji.
|
||||||
|
|
||||||
|
</boundaries>
|
||||||
|
|
||||||
|
<verification>
|
||||||
|
Before declaring plan complete:
|
||||||
|
- [ ] `php -l src/Modules/Orders/OrdersRepository.php`
|
||||||
|
- [ ] `php -l src/Modules/Orders/OrdersController.php`
|
||||||
|
- [ ] `php -l tests/Unit/OrdersRepositoryNotesCountTest.php`
|
||||||
|
- [ ] `vendor/bin/phpunit tests/Unit/OrdersRepositoryNotesCountTest.php` albo udokumentowany brak `vendor/`
|
||||||
|
- [ ] `rg -n "user_notes_count|notes_count" src/Modules/Orders tests DOCS`
|
||||||
|
- [ ] All acceptance criteria met
|
||||||
|
</verification>
|
||||||
|
|
||||||
|
<success_criteria>
|
||||||
|
- Lista zamowien pokazuje badge dla zamowien majacych tylko importowana notatke.
|
||||||
|
- Zamowienia z mieszanka notatek autorskich i importowanych pokazuja sume.
|
||||||
|
- Brak migracji DB i brak zmian w import flow.
|
||||||
|
- Dokumentacja techniczna opisuje nowy kontrakt licznika.
|
||||||
|
</success_criteria>
|
||||||
|
|
||||||
|
<output>
|
||||||
|
After completion, create `.paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md`.
|
||||||
|
</output>
|
||||||
158
.paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md
Normal file
158
.paul/phases/144-imported-notes-badge-count/144-01-SUMMARY.md
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
---
|
||||||
|
phase: 144-imported-notes-badge-count
|
||||||
|
plan: 01
|
||||||
|
subsystem: orders
|
||||||
|
tags: [orders-list, order-notes, badge, shoppro]
|
||||||
|
requires:
|
||||||
|
- phase: 129-order-user-notes
|
||||||
|
provides: shared `order_notes` table for imported and operator notes
|
||||||
|
provides:
|
||||||
|
- Combined `/orders/list` notes badge count for imported and operator notes
|
||||||
|
- Regression coverage for `OrdersRepository::paginate()` notes count
|
||||||
|
affects: [orders-list, order-notes, shoppro-import]
|
||||||
|
tech-stack:
|
||||||
|
added: []
|
||||||
|
patterns: [correlated count subquery, sqlite repository smoke]
|
||||||
|
key-files:
|
||||||
|
created:
|
||||||
|
- tests/Unit/OrdersRepositoryNotesCountTest.php
|
||||||
|
modified:
|
||||||
|
- src/Modules/Orders/OrdersRepository.php
|
||||||
|
- src/Modules/Orders/OrdersController.php
|
||||||
|
- DOCS/ARCHITECTURE.md
|
||||||
|
- DOCS/DB_SCHEMA.md
|
||||||
|
- DOCS/TECH_CHANGELOG.md
|
||||||
|
key-decisions:
|
||||||
|
- "Notes badge counts every `order_notes` row; details view still separates user/imported notes."
|
||||||
|
patterns-established:
|
||||||
|
- "Use `notes_count` for combined order notes counters; reserve note_type filters for detail sections and CRUD ownership."
|
||||||
|
duration: 20min
|
||||||
|
started: 2026-05-18T12:19:00+02:00
|
||||||
|
completed: 2026-05-18T12:39:00+02:00
|
||||||
|
---
|
||||||
|
|
||||||
|
# Phase 144 Plan 01: Imported Notes Badge Count Hotfix Summary
|
||||||
|
|
||||||
|
`/orders/list` now shows the notes badge for orders that only have imported source notes, including shopPRO messages.
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
| Metric | Value |
|
||||||
|
|--------|-------|
|
||||||
|
| Duration | ~20 minutes |
|
||||||
|
| Started | 2026-05-18T12:19:00+02:00 |
|
||||||
|
| Completed | 2026-05-18T12:39:00+02:00 |
|
||||||
|
| Tasks | 3 completed |
|
||||||
|
| Files modified | 8 planned files plus PAUL state/changelog |
|
||||||
|
|
||||||
|
## Acceptance Criteria Results
|
||||||
|
|
||||||
|
| Criterion | Status | Notes |
|
||||||
|
|-----------|--------|-------|
|
||||||
|
| AC-1: Importowana notatka podbija badge | Pass | `OrdersRepository::notesCountSubquerySql()` counts all `order_notes` rows without filtering `note_type`. |
|
||||||
|
| AC-2: Notatki autorskie nadal sa liczone | Pass | Operator notes remain rows in `order_notes`, so they are included in the same count. |
|
||||||
|
| AC-3: Licznik jest suma wszystkich notatek zamowienia | Pass | Added repository regression test and ad-hoc SQLite smoke where `message + user = notes_count 2`. |
|
||||||
|
| AC-4: Brak zmian schematu | Pass | No migration added; existing `order_notes` table and `order_notes_order_idx` are used. |
|
||||||
|
|
||||||
|
## Accomplishments
|
||||||
|
|
||||||
|
- Replaced list-row `user_notes_count` semantics with combined `notes_count`.
|
||||||
|
- Kept the existing badge markup, link to `#notes`, and title pluralization behavior.
|
||||||
|
- Added `OrdersRepositoryNotesCountTest` for combined imported/operator notes and zero-note orders.
|
||||||
|
- Updated architecture, schema documentation, and technical changelog for the new list badge contract.
|
||||||
|
|
||||||
|
## Task Commits
|
||||||
|
|
||||||
|
Commits are handled at phase transition time.
|
||||||
|
|
||||||
|
| Task | Commit | Type | Description |
|
||||||
|
|------|--------|------|-------------|
|
||||||
|
| Task 1: Zmienic licznik listy na wszystkie notatki | pending | fix | `OrdersRepository` now selects `notes_count`; `OrdersController` renders it. |
|
||||||
|
| Task 2: Dodac test regresyjny licznika notatek | pending | test | Added SQLite repository test for imported plus user notes. |
|
||||||
|
| Task 3: Zaktualizowac dokumentacje techniczna | pending | docs | Documented combined badge count and no-schema-change scope. |
|
||||||
|
|
||||||
|
## Files Created/Modified
|
||||||
|
|
||||||
|
| File | Change | Purpose |
|
||||||
|
|------|--------|---------|
|
||||||
|
| `src/Modules/Orders/OrdersRepository.php` | Modified | Count all notes for list rows via `notesCountSubquerySql()`. |
|
||||||
|
| `src/Modules/Orders/OrdersController.php` | Modified | Render badge from `notes_count`. |
|
||||||
|
| `tests/Unit/OrdersRepositoryNotesCountTest.php` | Created | Regression coverage for combined notes count. |
|
||||||
|
| `DOCS/ARCHITECTURE.md` | Modified | Documented `/orders/list` notes badge contract. |
|
||||||
|
| `DOCS/DB_SCHEMA.md` | Modified | Documented `order_notes` and indexes relevant to the badge. |
|
||||||
|
| `DOCS/TECH_CHANGELOG.md` | Modified | Added Phase 144 technical changelog entry. |
|
||||||
|
| `.paul/STATE.md` | Modified | Recorded APPLY and verification gaps. |
|
||||||
|
| `.paul/ROADMAP.md` | Modified | Added Phase 144 status. |
|
||||||
|
|
||||||
|
## Decisions Made
|
||||||
|
|
||||||
|
| Decision | Rationale | Impact |
|
||||||
|
|----------|-----------|--------|
|
||||||
|
| Count all `order_notes` rows for the badge | User expectation is that imported shopPRO notes are operationally visible on the list just like operator notes. | Orders with only imported notes now display `[N]` on `/orders/list`. |
|
||||||
|
| Keep details view grouping unchanged | User asked only for list count; existing detail UI separates operator notes from source messages clearly. | No UI/SCSS changes and no behavior change for note CRUD/import. |
|
||||||
|
|
||||||
|
## Deviations from Plan
|
||||||
|
|
||||||
|
### Summary
|
||||||
|
|
||||||
|
| Type | Count | Impact |
|
||||||
|
|------|-------|--------|
|
||||||
|
| Auto-fixed | 1 | Renamed one leftover variable reference during Task 1; no behavior risk. |
|
||||||
|
| Scope additions | 0 | None. |
|
||||||
|
| Deferred | 2 | Environment gaps only. |
|
||||||
|
|
||||||
|
### Auto-fixed Issues
|
||||||
|
|
||||||
|
**1. Variable rename cleanup**
|
||||||
|
- **Found during:** Task 1
|
||||||
|
- **Issue:** After renaming `$userNotesCount` to `$notesCount`, the badge title still referenced the old variable.
|
||||||
|
- **Fix:** Updated title construction to use `$notesCount`.
|
||||||
|
- **Files:** `src/Modules/Orders/OrdersController.php`
|
||||||
|
- **Verification:** PHP lint and `rg` confirmed no old source references remain.
|
||||||
|
|
||||||
|
### Deferred Items
|
||||||
|
|
||||||
|
- `PHPUNIT-MISSING-144`: `vendor/bin/phpunit` is absent in the checkout, so the PHPUnit test was linted and an equivalent SQLite runtime smoke was executed.
|
||||||
|
- `SONAR-MISSING-144`: `sonar-scanner` is not available in PATH, so the required scan could not run.
|
||||||
|
|
||||||
|
## Issues Encountered
|
||||||
|
|
||||||
|
| Issue | Resolution |
|
||||||
|
|-------|------------|
|
||||||
|
| `vendor/bin/phpunit` missing | Ran `php -l` for the test and an ad-hoc SQLite smoke through `OrdersRepository::paginate()`. |
|
||||||
|
| `sonar-scanner` missing in PATH | Documented gap in STATE/SUMMARY. |
|
||||||
|
|
||||||
|
## Verification Results
|
||||||
|
|
||||||
|
| Command | Result |
|
||||||
|
|---------|--------|
|
||||||
|
| `C:\xampp\php\php.exe -l src\Modules\Orders\OrdersRepository.php` | Pass |
|
||||||
|
| `C:\xampp\php\php.exe -l src\Modules\Orders\OrdersController.php` | Pass |
|
||||||
|
| `C:\xampp\php\php.exe -l tests\Unit\OrdersRepositoryNotesCountTest.php` | Pass |
|
||||||
|
| `vendor/bin/phpunit tests/Unit/OrdersRepositoryNotesCountTest.php` | Not run: `vendor/bin/phpunit` missing |
|
||||||
|
| Ad-hoc SQLite smoke for `message + user` notes | Pass: `notes_count smoke OK: 2` |
|
||||||
|
| `git diff --check` | Pass |
|
||||||
|
| `sonar-scanner --version` | Not run: command unavailable |
|
||||||
|
|
||||||
|
## Skill Audit
|
||||||
|
|
||||||
|
| Expected | Invoked | Notes |
|
||||||
|
|----------|---------|-------|
|
||||||
|
| `sonar-scanner` | gap documented | CLI is unavailable in PATH. |
|
||||||
|
|
||||||
|
## Next Phase Readiness
|
||||||
|
|
||||||
|
**Ready:**
|
||||||
|
- The Phase 144 hotfix is complete and documented.
|
||||||
|
- Future order list counters should use `notes_count` when they mean all notes.
|
||||||
|
|
||||||
|
**Concerns:**
|
||||||
|
- PHPUnit still requires restoring `vendor/` or project dependencies.
|
||||||
|
- Sonar still requires restoring `sonar-scanner` in PATH or a fallback scanner.
|
||||||
|
|
||||||
|
**Blockers:**
|
||||||
|
- None for this hotfix.
|
||||||
|
|
||||||
|
---
|
||||||
|
*Phase: 144-imported-notes-badge-count, Plan: 01*
|
||||||
|
*Completed: 2026-05-18*
|
||||||
@@ -109,6 +109,7 @@ HTTP Request
|
|||||||
|
|
||||||
### Orders List and Sidebar State
|
### Orders List and Sidebar State
|
||||||
- `/orders/list` renders the reusable order status panel and shared `components/table-list` directly; the previous descriptive intro card was removed so the operational table starts higher on the screen.
|
- `/orders/list` renders the reusable order status panel and shared `components/table-list` directly; the previous descriptive intro card was removed so the operational table starts higher on the screen.
|
||||||
|
- The order number cell shows the notes badge from `OrdersRepository::notesCountSubquerySql()`, counting every `order_notes` row for the order. Imported source notes (`note_type <> 'user'`) and operator notes (`note_type='user'`) both contribute to the same `[N]` badge, while order details still render those groups separately.
|
||||||
- The desktop sidebar collapse state is stored in `localStorage.sidebarCollapsed`.
|
- The desktop sidebar collapse state is stored in `localStorage.sidebarCollapsed`.
|
||||||
- `resources/views/layouts/app.php` reads that value in the document head and adds `html.has-collapsed-sidebar` before CSS loads. `resources/scss/app.scss` mirrors the compact sidebar rules for that pre-hydration class, so a saved collapsed sidebar is collapsed on first paint instead of animating after the footer script runs.
|
- `resources/views/layouts/app.php` reads that value in the document head and adds `html.has-collapsed-sidebar` before CSS loads. `resources/scss/app.scss` mirrors the compact sidebar rules for that pre-hydration class, so a saved collapsed sidebar is collapsed on first paint instead of animating after the footer script runs.
|
||||||
- The footer sidebar script still owns runtime interactions: it applies/removes `.sidebar.is-collapsed`, updates the button labels, closes open `details`, persists the state, and then removes the temporary HTML pre-hydration class.
|
- The footer sidebar script still owns runtime interactions: it applies/removes `.sidebar.is-collapsed`, updates the button labels, closes open `details`, persists the state, and then removes the temporary HTML pre-hydration class.
|
||||||
|
|||||||
@@ -324,6 +324,24 @@ UNIQUE: `(integration_id, external_order_id)`
|
|||||||
|
|
||||||
UNIQUE: `(order_id, source_payment_id)`
|
UNIQUE: `(order_id, source_payment_id)`
|
||||||
|
|
||||||
|
**order_notes** — Notes attached to orders, both imported from source systems and written by operators
|
||||||
|
| 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 | Source note ID; NULL for operator notes |
|
||||||
|
| `note_type` | VARCHAR(32) | NO | Imported types such as `shoppro`/`allegro`/`message`, or `user` for operator notes |
|
||||||
|
| `user_id` | INT UNSIGNED | YES | FK -> users(id) SET NULL; set for operator notes |
|
||||||
|
| `author_name` | VARCHAR(190) | YES | Snapshot of user name for operator notes |
|
||||||
|
| `created_at_external` | DATETIME | YES | Source timestamp for imported notes |
|
||||||
|
| `comment` | TEXT | NO | Note body |
|
||||||
|
| `payload_json` | JSON | YES | Raw source payload for imported notes |
|
||||||
|
| `created_at` | DATETIME | NO | DEFAULT CURRENT_TIMESTAMP |
|
||||||
|
| `updated_at` | DATETIME | NO | DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP |
|
||||||
|
|
||||||
|
UNIQUE: `(order_id, source_note_id)`; MySQL allows multiple NULL `source_note_id` values for operator notes.
|
||||||
|
Indexes: `order_notes_order_idx (order_id)` supports the combined notes badge on `/orders/list`; `idx_order_notes_type_order (note_type, order_id)` supports user/imported note lists.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Order Statuses
|
## Order Statuses
|
||||||
|
|||||||
@@ -1,5 +1,19 @@
|
|||||||
# Technical Changelog
|
# Technical Changelog
|
||||||
|
|
||||||
|
## 2026-05-18 - Phase 144 Plan 01: Imported Notes Badge Count Hotfix
|
||||||
|
|
||||||
|
**Co zrobiono:**
|
||||||
|
- Zmieniono licznik badge notatek na `/orders/list` z `user_notes_count` na `notes_count`.
|
||||||
|
- `OrdersRepository` liczy teraz wszystkie rekordy `order_notes` dla zamowienia, bez filtra `note_type='user'`.
|
||||||
|
- `OrdersController` renderuje ten sam badge `[N]` i link do `#notes`, ale liczba obejmuje notatki autorskie oraz importowane ze zrodla, np. shopPRO `message`.
|
||||||
|
- Dodano test regresyjny `OrdersRepositoryNotesCountTest` dla sumy notatki importowanej i autorskiej oraz zamowienia bez notatek.
|
||||||
|
|
||||||
|
**Dlaczego:**
|
||||||
|
- Zamowienie z notatka zaimportowana z shopPRO bylo poprawnie widoczne w szczegolach, ale lista zamowien nie pokazywala cyfry, bo licznik obejmowal tylko notatki operatora.
|
||||||
|
|
||||||
|
**BREAKING / migracja:**
|
||||||
|
- Brak migracji DB i brak zmian breaking. Hotfix zmienia tylko zapytanie listy zamowien i dokumentuje istniejaca tabele `order_notes`.
|
||||||
|
|
||||||
## 2026-05-18 - Phase 143 Plan 01: Orders List Sidebar UI Hotfix
|
## 2026-05-18 - Phase 143 Plan 01: Orders List Sidebar UI Hotfix
|
||||||
|
|
||||||
**Co zrobiono:**
|
**Co zrobiono:**
|
||||||
|
|||||||
@@ -795,11 +795,11 @@ final class OrdersController
|
|||||||
? ' <span class="risk-return-badge" title="Klient nie odebral ' . $returnedCount . ' przesylek w historii">zwroty: ' . $returnedCount . '</span>'
|
? ' <span class="risk-return-badge" title="Klient nie odebral ' . $returnedCount . ' przesylek w historii">zwroty: ' . $returnedCount . '</span>'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
$userNotesCount = max(0, (int) ($row['user_notes_count'] ?? 0));
|
$notesCount = max(0, (int) ($row['notes_count'] ?? 0));
|
||||||
$notesBadge = $userNotesCount >= 1
|
$notesBadge = $notesCount >= 1
|
||||||
? ' <a href="/orders/' . (int) ($row['id'] ?? 0) . '#notes" class="order-notes-badge" title="' . $userNotesCount . ' '
|
? ' <a href="/orders/' . (int) ($row['id'] ?? 0) . '#notes" class="order-notes-badge" title="' . $notesCount . ' '
|
||||||
. ($userNotesCount === 1 ? 'notatka' : ($userNotesCount < 5 ? 'notatki' : 'notatek')) . '">['
|
. ($notesCount === 1 ? 'notatka' : ($notesCount < 5 ? 'notatki' : 'notatek')) . '">['
|
||||||
. $userNotesCount . ']</a>'
|
. $notesCount . ']</a>'
|
||||||
: '';
|
: '';
|
||||||
|
|
||||||
$previewBtn = '<button type="button" class="btn-icon js-order-preview-btn" data-order-id="' . (int) ($row['id'] ?? 0) . '" title="Podglad">'
|
$previewBtn = '<button type="button" class="btn-icon js-order-preview-btn" data-order-id="' . (int) ($row['id'] ?? 0) . '" title="Podglad">'
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ final class OrdersRepository
|
|||||||
COALESCE(od_agg.documents_count, 0) AS documents_count,
|
COALESCE(od_agg.documents_count, 0) AS documents_count,
|
||||||
ig.name AS integration_name,
|
ig.name AS integration_name,
|
||||||
' . $this->customerReturnedCountSubquerySql('o', 'a') . ' AS customer_returned_count,
|
' . $this->customerReturnedCountSubquerySql('o', 'a') . ' AS customer_returned_count,
|
||||||
' . $this->userNotesCountSubquerySql('o') . ' AS user_notes_count
|
' . $this->notesCountSubquerySql('o') . ' AS notes_count
|
||||||
FROM orders o
|
FROM orders o
|
||||||
LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer"
|
LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = "customer"
|
||||||
LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.status_code) = asm.allegro_status_code
|
LEFT JOIN allegro_order_status_mappings asm ON o.source = "allegro" AND LOWER(o.status_code) = asm.allegro_status_code
|
||||||
@@ -247,7 +247,7 @@ final class OrdersRepository
|
|||||||
'projects_done' => (int) ($row['projects_done'] ?? 0),
|
'projects_done' => (int) ($row['projects_done'] ?? 0),
|
||||||
'projects_total' => (int) ($row['projects_total'] ?? 0),
|
'projects_total' => (int) ($row['projects_total'] ?? 0),
|
||||||
'customer_returned_count' => max(0, (int) ($row['customer_returned_count'] ?? 0)),
|
'customer_returned_count' => max(0, (int) ($row['customer_returned_count'] ?? 0)),
|
||||||
'user_notes_count' => max(0, (int) ($row['user_notes_count'] ?? 0)),
|
'notes_count' => max(0, (int) ($row['notes_count'] ?? 0)),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -714,13 +714,13 @@ final class OrdersRepository
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Phase 129-01: subquery liczby notatek autorskich (note_type='user') dla zamowienia.
|
* Subquery liczby wszystkich notatek zamowienia, autorskich i importowanych.
|
||||||
* Wspierane indeksem idx_order_notes_type_order (note_type, order_id).
|
* Wspierane indeksem order_notes_order_idx (order_id).
|
||||||
*/
|
*/
|
||||||
private function userNotesCountSubquerySql(string $orderAlias): string
|
private function notesCountSubquerySql(string $orderAlias): string
|
||||||
{
|
{
|
||||||
return '(SELECT COUNT(*) FROM order_notes
|
return '(SELECT COUNT(*) FROM order_notes
|
||||||
WHERE order_id = ' . $orderAlias . '.id AND note_type = "user")';
|
WHERE order_id = ' . $orderAlias . '.id)';
|
||||||
}
|
}
|
||||||
|
|
||||||
private function effectiveStatusSql(string $orderAlias, string $mappingAlias): string
|
private function effectiveStatusSql(string $orderAlias, string $mappingAlias): string
|
||||||
|
|||||||
174
tests/Unit/OrdersRepositoryNotesCountTest.php
Normal file
174
tests/Unit/OrdersRepositoryNotesCountTest.php
Normal file
@@ -0,0 +1,174 @@
|
|||||||
|
<?php
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use App\Modules\Orders\OrdersRepository;
|
||||||
|
use PDO;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use ReflectionProperty;
|
||||||
|
|
||||||
|
final class OrdersRepositoryNotesCountTest extends TestCase
|
||||||
|
{
|
||||||
|
private PDO $pdo;
|
||||||
|
private OrdersRepository $repository;
|
||||||
|
|
||||||
|
protected function setUp(): void
|
||||||
|
{
|
||||||
|
$this->pdo = new PDO('sqlite::memory:');
|
||||||
|
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$this->pdo->sqliteCreateFunction('REGEXP_REPLACE', static fn (string $value): string => preg_replace('/[^0-9]+/', '', $value) ?? '');
|
||||||
|
|
||||||
|
$this->createSchema();
|
||||||
|
$this->repository = new OrdersRepository($this->pdo);
|
||||||
|
|
||||||
|
$supportsMappedMedia = new ReflectionProperty(OrdersRepository::class, 'supportsMappedMedia');
|
||||||
|
$supportsMappedMedia->setAccessible(true);
|
||||||
|
$supportsMappedMedia->setValue(null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testImportedAndUserNotesAreCountedTogether(): void
|
||||||
|
{
|
||||||
|
$this->seedOrder(1, 'OP/1');
|
||||||
|
$this->seedNote(1, 'message', 'Prosze o szybka wysylke');
|
||||||
|
$this->seedNote(1, 'user', 'Sprawdzic przed pakowaniem');
|
||||||
|
|
||||||
|
$result = $this->repository->paginate(['page' => 1, 'per_page' => 20]);
|
||||||
|
|
||||||
|
self::assertSame('', $result['error']);
|
||||||
|
self::assertSame(1, $result['total']);
|
||||||
|
self::assertSame(2, $result['items'][0]['notes_count'] ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testOrderWithoutNotesReturnsZeroCount(): void
|
||||||
|
{
|
||||||
|
$this->seedOrder(2, 'OP/2');
|
||||||
|
|
||||||
|
$result = $this->repository->paginate(['page' => 1, 'per_page' => 20]);
|
||||||
|
|
||||||
|
self::assertSame('', $result['error']);
|
||||||
|
self::assertSame(1, $result['total']);
|
||||||
|
self::assertSame(0, $result['items'][0]['notes_count'] ?? null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function createSchema(): void
|
||||||
|
{
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE orders (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
internal_order_number TEXT,
|
||||||
|
source TEXT,
|
||||||
|
source_order_id TEXT,
|
||||||
|
external_order_id TEXT,
|
||||||
|
status_code TEXT,
|
||||||
|
payment_status INTEGER,
|
||||||
|
currency TEXT,
|
||||||
|
total_with_tax REAL,
|
||||||
|
total_paid REAL,
|
||||||
|
ordered_at TEXT,
|
||||||
|
source_created_at TEXT,
|
||||||
|
source_updated_at TEXT,
|
||||||
|
fetched_at TEXT,
|
||||||
|
is_canceled_by_buyer INTEGER DEFAULT 0,
|
||||||
|
external_carrier_id TEXT,
|
||||||
|
external_payment_type_id TEXT,
|
||||||
|
integration_id INTEGER,
|
||||||
|
customer_login TEXT
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE order_addresses (
|
||||||
|
order_id INTEGER,
|
||||||
|
address_type TEXT,
|
||||||
|
name TEXT,
|
||||||
|
email TEXT,
|
||||||
|
phone TEXT,
|
||||||
|
city TEXT
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE allegro_order_status_mappings (
|
||||||
|
allegro_status_code TEXT,
|
||||||
|
orderpro_status_code TEXT
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE integrations (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
name TEXT
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE order_items (
|
||||||
|
id INTEGER PRIMARY KEY,
|
||||||
|
order_id INTEGER,
|
||||||
|
original_name TEXT,
|
||||||
|
quantity REAL,
|
||||||
|
media_url TEXT,
|
||||||
|
sort_order INTEGER,
|
||||||
|
project_generated INTEGER DEFAULT 0
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE order_shipments (
|
||||||
|
order_id INTEGER
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE order_documents (
|
||||||
|
order_id INTEGER
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE shipment_packages (
|
||||||
|
order_id INTEGER,
|
||||||
|
delivery_status TEXT
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
$this->pdo->exec(
|
||||||
|
'CREATE TABLE order_notes (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
order_id INTEGER,
|
||||||
|
note_type TEXT,
|
||||||
|
comment TEXT
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function seedOrder(int $id, string $number): void
|
||||||
|
{
|
||||||
|
$this->pdo->prepare(
|
||||||
|
'INSERT INTO orders (
|
||||||
|
id, internal_order_number, source, source_order_id, external_order_id, status_code,
|
||||||
|
payment_status, currency, total_with_tax, total_paid, ordered_at, source_created_at,
|
||||||
|
source_updated_at, fetched_at, integration_id
|
||||||
|
) VALUES (
|
||||||
|
:id, :internal_order_number, "shoppro", :source_order_id, :external_order_id, "nowe",
|
||||||
|
2, "PLN", 100.00, 100.00, "2026-05-18 10:00:00", "2026-05-18 10:00:00",
|
||||||
|
"2026-05-18 10:00:00", "2026-05-18 10:00:00", 1
|
||||||
|
)'
|
||||||
|
)->execute([
|
||||||
|
'id' => $id,
|
||||||
|
'internal_order_number' => $number,
|
||||||
|
'source_order_id' => 'SP/' . $id,
|
||||||
|
'external_order_id' => 'EXT/' . $id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->pdo->prepare(
|
||||||
|
'INSERT INTO order_addresses (order_id, address_type, name, email, phone, city)
|
||||||
|
VALUES (:order_id, "customer", "Jan Testowy", "jan@example.test", "500600700", "Rzeszow")'
|
||||||
|
)->execute(['order_id' => $id]);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function seedNote(int $orderId, string $noteType, string $comment): void
|
||||||
|
{
|
||||||
|
$this->pdo->prepare(
|
||||||
|
'INSERT INTO order_notes (order_id, note_type, comment)
|
||||||
|
VALUES (:order_id, :note_type, :comment)'
|
||||||
|
)->execute([
|
||||||
|
'order_id' => $orderId,
|
||||||
|
'note_type' => $noteType,
|
||||||
|
'comment' => $comment,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user