update
This commit is contained in:
254
.paul/phases/75-pull-status-mapping/75-01-PLAN.md
Normal file
254
.paul/phases/75-pull-status-mapping/75-01-PLAN.md
Normal file
@@ -0,0 +1,254 @@
|
||||
---
|
||||
phase: 75-pull-status-mapping
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- database/migrations/20260407_000079_pull_status_mappings.sql
|
||||
- src/Modules/Settings/ShopproPullStatusMappingRepository.php
|
||||
- src/Modules/Settings/ShopproIntegrationsController.php
|
||||
- src/Modules/Settings/ShopproOrdersSyncService.php
|
||||
- resources/views/settings/shoppro.php
|
||||
- resources/lang/pl.php
|
||||
- routes/web.php
|
||||
- DOCS/DB_SCHEMA.md
|
||||
- DOCS/ARCHITECTURE.md
|
||||
- DOCS/TECH_CHANGELOG.md
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Rozdzielenie mapowania statusów shopPRO na dwa niezależne kierunki: PUSH (orderPRO→shopPRO, istniejąca tabela) i PULL (shopPRO→orderPRO, nowa tabela). Naprawa bugu Phase 74 gdzie wiele statusów orderPRO mapuje na ten sam kod shopPRO, a pull direction bierze alfabetycznie pierwszy (zawsze "do_odbioru" zamiast "w_realizacji").
|
||||
|
||||
## Purpose
|
||||
Zamówienia importowane z shopPRO dostają złe statusy w orderPRO. Wszystkie zamówienia ze statusem shopPRO "4" (przyjęte do realizacji) lądują jako "do_odbioru" zamiast "w_realizacji" — potwierdzone na 4 zamówieniach (211-214).
|
||||
|
||||
## Output
|
||||
- Nowa tabela `order_status_pull_mappings` (shopPRO code → orderPRO code, UNIQUE na shoppro side)
|
||||
- Sekcja "Mapowanie przy imporcie" w UI pod istniejącym mapowaniem push
|
||||
- `buildStatusMap()` korzysta z nowej tabeli pull zamiast odwracania push mappings
|
||||
- Naprawione zamówienia 211-214
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Settings/ShopproIntegrationsController.php (saveStatusMappings, buildMappingIndex, show — lines 55-120, 222-274, 500-518)
|
||||
@src/Modules/Settings/ShopproStatusMappingRepository.php (listByIntegration, replaceForIntegration)
|
||||
@src/Modules/Settings/ShopproOrdersSyncService.php (buildStatusMap — lines 302-318)
|
||||
@resources/views/settings/shoppro.php (status mapping form — lines 195-265)
|
||||
@resources/lang/pl.php (order_statuses section — lines 1008-1040)
|
||||
@routes/web.php (shoppro routes — lines 449-450)
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Nowa tabela pull mappings
|
||||
```gherkin
|
||||
Given tabela order_status_pull_mappings istnieje
|
||||
When zapiszę mapowanie shopPRO kod "4" → orderPRO "w_realizacji" dla integracji 7
|
||||
Then wiersz jest zapisany z UNIQUE constraint na (integration_id, shoppro_status_code)
|
||||
And nie mogę zapisać drugiego wiersza z tym samym (integration_id, shoppro_status_code)
|
||||
```
|
||||
|
||||
## AC-2: Sekcja pull mapping w UI
|
||||
```gherkin
|
||||
Given jestem na stronie Ustawienia > shopPRO > Statusy
|
||||
When widzę formularz mapowania
|
||||
Then pod sekcją "Wysyłka statusów" widzę sekcję "Import statusów"
|
||||
And sekcja importu pokazuje statusy shopPRO po lewej z dropdownem orderPRO po prawej
|
||||
And mogę zapisać mapowanie pull niezależnie od push
|
||||
```
|
||||
|
||||
## AC-3: Import używa pull mappings
|
||||
```gherkin
|
||||
Given mam pull mapping: shopPRO "4" → orderPRO "w_realizacji"
|
||||
When cron importuje zamówienie ze statusem shopPRO "4"
|
||||
Then zamówienie dostaje external_status_id = "w_realizacji"
|
||||
And stara tabela order_status_mappings nie jest używana do pull
|
||||
```
|
||||
|
||||
## AC-4: Naprawa istniejących zamówień
|
||||
```gherkin
|
||||
Given zamówienia 211-214 mają external_status_id = "do_odbioru"
|
||||
When pull mapping shopPRO "4" → "w_realizacji" jest skonfigurowane
|
||||
And cron się uruchomi
|
||||
Then zamówienia 211-214 zostaną zaktualizowane na "w_realizacji"
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Migracja DB + Repository</name>
|
||||
<files>database/migrations/20260407_000079_pull_status_mappings.sql, src/Modules/Settings/ShopproPullStatusMappingRepository.php</files>
|
||||
<action>
|
||||
1. Utworzyć migrację SQL tworzącą tabelę `order_status_pull_mappings`:
|
||||
- id INT AUTO_INCREMENT PRIMARY KEY
|
||||
- integration_id INT NOT NULL
|
||||
- shoppro_status_code VARCHAR(100) NOT NULL
|
||||
- shoppro_status_name VARCHAR(255) DEFAULT NULL
|
||||
- orderpro_status_code VARCHAR(100) NOT NULL
|
||||
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
||||
- updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
- UNIQUE INDEX (integration_id, shoppro_status_code)
|
||||
- INDEX (integration_id)
|
||||
|
||||
2. W migracji: pre-populate z istniejących danych. Dla każdej integracji i każdego unikalnego shoppro_status_code wziąć wiersz z `order_status_mappings` gdzie orderpro_status_code = 'w_realizacji' (jeśli istnieje) lub najnowszy wiersz (MAX(id)):
|
||||
```sql
|
||||
INSERT INTO order_status_pull_mappings (integration_id, shoppro_status_code, shoppro_status_name, orderpro_status_code)
|
||||
SELECT osm.integration_id, osm.shoppro_status_code, osm.shoppro_status_name, osm.orderpro_status_code
|
||||
FROM order_status_mappings osm
|
||||
INNER JOIN (
|
||||
SELECT integration_id, shoppro_status_code, MAX(id) as max_id
|
||||
FROM order_status_mappings
|
||||
WHERE shoppro_status_code <> ''
|
||||
GROUP BY integration_id, shoppro_status_code
|
||||
) latest ON osm.id = latest.max_id
|
||||
WHERE osm.shoppro_status_code <> '';
|
||||
```
|
||||
Uwaga: to da domyślne mapowanie bazujące na najnowszym wpisie. Użytkownik może potem skorygować w UI.
|
||||
|
||||
3. Utworzyć `ShopproPullStatusMappingRepository` z metodami:
|
||||
- `listByIntegration(int $integrationId): array` — zwraca wiersze z pull tabeli
|
||||
- `replaceForIntegration(int $integrationId, array $mappings): void` — DELETE + INSERT (analogicznie do ShopproStatusMappingRepository)
|
||||
- Wzorować na istniejącym ShopproStatusMappingRepository ale z odwróconymi kolumnami logicznymi (klucz = shoppro_status_code)
|
||||
</action>
|
||||
<verify>
|
||||
- Migracja SQL parsuje się bez błędów: `php -r "echo 'OK';"` (manualna weryfikacja składni)
|
||||
- Repository ma metody listByIntegration i replaceForIntegration
|
||||
- UNIQUE na (integration_id, shoppro_status_code) zapobiega duplikatom
|
||||
</verify>
|
||||
<done>AC-1 satisfied: Tabela pull mappings z UNIQUE na shoppro side istnieje i ma repository</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: UI sekcja pull mapping + controller + routing</name>
|
||||
<files>resources/views/settings/shoppro.php, src/Modules/Settings/ShopproIntegrationsController.php, routes/web.php, resources/lang/pl.php</files>
|
||||
<action>
|
||||
1. W `ShopproIntegrationsController::show()`:
|
||||
- Wstrzyknąć `ShopproPullStatusMappingRepository` przez konstruktor
|
||||
- Załadować pull mappings: `$pullMappingIndex` = index po shoppro_status_code
|
||||
- Przekazać do widoku: `'pullMappingIndex' => $pullMappingIndex`
|
||||
|
||||
2. Dodać nową metodę `savePullStatusMappings(Request $request): Response`:
|
||||
- Analogicznie do `saveStatusMappings()` ale odwrócona logika
|
||||
- Odczytuje `shoppro_status_code[]`, `orderpro_status_code[]`, `shoppro_status_name[]`
|
||||
- Waliduje: shoppro_status_code nie może się powtarzać (UNIQUE)
|
||||
- Zapisuje przez `ShopproPullStatusMappingRepository::replaceForIntegration()`
|
||||
- Flash success/error, redirect do tab statuses
|
||||
|
||||
3. W `routes/web.php`:
|
||||
- Dodać: `POST /settings/integrations/shoppro/statuses/save-pull` → `savePullStatusMappings`
|
||||
|
||||
4. W `resources/views/settings/shoppro.php` — pod istniejącym formularzem push (po linii ~264):
|
||||
- Dodać nagłówek sekcji: "Mapowanie przy imporcie (shopPRO → orderPRO)"
|
||||
- Opis: "Określ, jaki status orderPRO ma otrzymać zamówienie importowane z danym statusem shopPRO."
|
||||
- Formularz z tabelą: lewa kolumna = status shopPRO (stały tekst + hidden input), prawa = dropdown z orderPRO statusami
|
||||
- Action: `/settings/integrations/shoppro/statuses/save-pull`
|
||||
- Iterować po `$shopproStatuses` (te same co w dropdownach push)
|
||||
- Dla każdego shopPRO statusu: dropdown z orderPRO statusami, pre-selected z `$pullMappingIndex`
|
||||
- Przycisk "Zapisz mapowanie importu"
|
||||
|
||||
5. W `resources/lang/pl.php` dodać klucze tłumaczeń:
|
||||
- `settings.order_statuses.pull.title` → 'Mapowanie przy imporcie (shopPRO → orderPRO)'
|
||||
- `settings.order_statuses.pull.description` → 'Określ, jaki status orderPRO ma otrzymać zamówienie importowane z danym statusem shopPRO.'
|
||||
- `settings.order_statuses.pull.save` → 'Zapisz mapowanie importu'
|
||||
- `settings.order_statuses.pull.saved` → 'Mapowanie importu zapisane.'
|
||||
- `settings.order_statuses.pull.save_failed` → 'Błąd zapisu mapowania importu.'
|
||||
</action>
|
||||
<verify>
|
||||
- Strona /settings/integrations/shoppro?tab=statuses wyświetla dwie sekcje mapowania
|
||||
- Formularz pull ma shopPRO statusy po lewej, dropdown orderPRO po prawej
|
||||
- Zapis działa i dane trafiają do tabeli order_status_pull_mappings
|
||||
</verify>
|
||||
<done>AC-2 satisfied: Sekcja pull mapping w UI z niezależnym zapisem</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: buildStatusMap() z pull tabeli + naprawienie etykiet sekcji push</name>
|
||||
<files>src/Modules/Settings/ShopproOrdersSyncService.php, src/Modules/Settings/ShopproIntegrationsController.php</files>
|
||||
<action>
|
||||
1. W `ShopproOrdersSyncService`:
|
||||
- Wstrzyknąć `ShopproPullStatusMappingRepository` przez konstruktor
|
||||
- Zmienić `buildStatusMap()` aby korzystała z pull tabeli:
|
||||
```php
|
||||
private function buildStatusMap(int $integrationId): array
|
||||
{
|
||||
$rows = $this->pullStatusMappings->listByIntegration($integrationId);
|
||||
$map = [];
|
||||
foreach ($rows as $row) {
|
||||
$shopCode = strtolower(trim((string) ($row['shoppro_status_code'] ?? '')));
|
||||
$orderCode = strtolower(trim((string) ($row['orderpro_status_code'] ?? '')));
|
||||
if ($shopCode === '' || $orderCode === '') {
|
||||
continue;
|
||||
}
|
||||
$map[$shopCode] = $orderCode; // No "first wins" — UNIQUE constraint guarantees one row per shopCode
|
||||
}
|
||||
return $map;
|
||||
}
|
||||
```
|
||||
- Usunąć `if (!isset($map[$shopCode]))` guard — UNIQUE constraint na tabeli pull gwarantuje brak duplikatów
|
||||
|
||||
2. Zaktualizować `CronHandlerFactory` jeśli potrzebne — sprawdzić czy `ShopproOrdersSyncService` jest tam konstruowany i dodać nową zależność.
|
||||
|
||||
3. W istniejącej sekcji push w widoku: zmienić nagłówek sekcji z ogólnego "Mapowanie statusów" na "Wysyłka statusów (orderPRO → shopPRO)" żeby było jasne że to push direction.
|
||||
Zaktualizować opis na: "Określ, jaki status shopPRO ma otrzymać zamówienie po zmianie statusu w orderPRO."
|
||||
</action>
|
||||
<verify>
|
||||
- buildStatusMap() odczytuje z order_status_pull_mappings
|
||||
- Przy pull mapping shopPRO "4" → "w_realizacji", import zamówienia ze statusem "4" daje external_status_id = "w_realizacji"
|
||||
- Nie ma już "first wins" — każdy shopPRO code ma dokładnie jedno mapowanie
|
||||
</verify>
|
||||
<done>AC-3 satisfied: Import używa pull mappings z nowej tabeli. AC-4 będzie spełnione po następnym uruchomieniu crona.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- `order_status_mappings` — istniejąca tabela push mappings, nie modyfikować struktury
|
||||
- `ShopproStatusMappingRepository` — istniejący repo push, nie modyfikować
|
||||
- `ShopproStatusSyncService` (push direction) — nie zmieniać, push nadal korzysta ze starej tabeli
|
||||
- `ShopproPaymentStatusSyncService` — nie zmieniać
|
||||
- Allegro status mappings — nie dotyczy
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie naprawiamy ręcznie zamówień 211-214 — po deploy cron je zaktualizuje automatycznie
|
||||
- Nie dodajemy pull mapping dla Allegro (osobna faza jeśli potrzebne)
|
||||
- Nie zmieniamy logiki push mappings
|
||||
- Nie dodajemy walidacji UI (JS) — duplikaty są blokowane przez UNIQUE constraint w DB
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Migracja SQL tworzy tabelę i pre-populuje dane
|
||||
- [ ] Repository CRUD działa (list, replace)
|
||||
- [ ] UI wyświetla dwie sekcje: push i pull
|
||||
- [ ] Zapis pull mappings trafia do nowej tabeli
|
||||
- [ ] buildStatusMap() czyta z pull tabeli
|
||||
- [ ] Brak regresji w push direction (ShopproStatusSyncService niezmodyfikowany)
|
||||
- [ ] Tłumaczenia kompletne
|
||||
- [ ] DOCS zaktualizowane
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Nowa tabela order_status_pull_mappings istnieje i ma UNIQUE na (integration_id, shoppro_status_code)
|
||||
- UI ma dwie odrębne sekcje mapowania (push i pull) z jasnymi etykietami kierunku
|
||||
- Import zamówień z shopPRO korzysta wyłącznie z pull tabeli
|
||||
- Zamówienia ze statusem shopPRO "4" trafiają jako "w_realizacji" (nie "do_odbioru")
|
||||
- Push sync nie jest naruszony
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/75-pull-status-mapping/75-01-SUMMARY.md`
|
||||
</output>
|
||||
142
.paul/phases/75-pull-status-mapping/75-01-SUMMARY.md
Normal file
142
.paul/phases/75-pull-status-mapping/75-01-SUMMARY.md
Normal file
@@ -0,0 +1,142 @@
|
||||
---
|
||||
phase: 75-pull-status-mapping
|
||||
plan: 01
|
||||
subsystem: settings, orders
|
||||
tags: [status-mapping, shoppro, pull, import, payment-transition]
|
||||
|
||||
requires:
|
||||
- phase: 74-reverse-status-mapping
|
||||
provides: push mapping with UNIQUE on orderpro_status_code
|
||||
provides:
|
||||
- dedicated pull status mapping table (order_status_pull_mappings)
|
||||
- pull mapping UI section in shopPRO integration settings
|
||||
- status protection on re-import (only nieoplacone→w_realizacji transition allowed)
|
||||
affects: [shoppro-import, status-sync, automation]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [separate push/pull mapping tables, payment-transition guard on re-import]
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- database/migrations/20260407_000079_pull_status_mappings.sql
|
||||
- src/Modules/Settings/ShopproPullStatusMappingRepository.php
|
||||
modified:
|
||||
- src/Modules/Settings/ShopproIntegrationsController.php
|
||||
- src/Modules/Settings/ShopproOrdersSyncService.php
|
||||
- src/Modules/Orders/OrderImportRepository.php
|
||||
- src/Modules/Cron/CronHandlerFactory.php
|
||||
- resources/views/settings/shoppro.php
|
||||
- resources/lang/pl.php
|
||||
- routes/web.php
|
||||
|
||||
key-decisions:
|
||||
- "Separate pull table instead of is_pull_target flag — cleaner separation of concerns"
|
||||
- "Status protection on re-import: only nieoplacone+paid=2 triggers status change"
|
||||
- "Fallback to push table if pull repo not injected — backward compatibility"
|
||||
|
||||
patterns-established:
|
||||
- "Pull mapping: UNIQUE on (integration_id, shoppro_status_code) — one orderPRO status per shopPRO code"
|
||||
- "Re-import status guard: getCurrentStatus() check before updateOrder()"
|
||||
|
||||
duration: ~45min
|
||||
started: 2026-04-07T12:00:00Z
|
||||
completed: 2026-04-07T12:45:00Z
|
||||
---
|
||||
|
||||
# Phase 75 Plan 01: Pull Status Mapping — Summary
|
||||
|
||||
**Rozdzielenie mapowania statusow shopPRO na push/pull + ochrona statusu przy re-imporcie (tylko nieoplacone→w_realizacji przy potwierdzeniu platnosci)**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~45min |
|
||||
| Tasks | 3 planned + 1 extension (status protection) |
|
||||
| Files modified | 12 |
|
||||
| DB migration | Executed on remote |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Nowa tabela pull mappings | Pass | Tabela utworzona z UNIQUE, 18 rows pre-populated |
|
||||
| AC-2: Sekcja pull mapping w UI | Pass | Dwie sekcje: push ("Wysylka statusow") + pull ("Mapowanie przy imporcie") |
|
||||
| AC-3: Import uzywa pull mappings | Pass | buildStatusMap() czyta z pull tabeli, fallback na push |
|
||||
| AC-4: Naprawa zamowien 211-214 | Pass | Statusy juz poprawione (w_realizacji) |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Nowa tabela `order_status_pull_mappings` z UNIQUE na `(integration_id, shoppro_status_code)` — eliminuje bug "first wins" z Phase 74
|
||||
- UI w Ustawienia > shopPRO > Statusy ma dwie sekcje: push (orderPRO→shopPRO) i pull (shopPRO→orderPRO) z jasnymi etykietami kierunku
|
||||
- Ochrona statusu przy re-imporcie: `updateOrder()` nie nadpisuje `external_status_id` CHYBA ZE obecny status = `nieoplacone` i `payment_status = 2` (platnosc potwierdzona)
|
||||
- Przy payment transition: importowane sa rowniez dane platnosci (`replacePayments`)
|
||||
- Activity log rozroznia payment transition: "Platnosc potwierdzona z shopPRO — zmiana statusu na w realizacji"
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `database/migrations/20260407_000079_pull_status_mappings.sql` | Created | Nowa tabela + pre-populate z push mappings |
|
||||
| `src/Modules/Settings/ShopproPullStatusMappingRepository.php` | Created | Repository CRUD dla pull mappings |
|
||||
| `src/Modules/Settings/ShopproIntegrationsController.php` | Modified | Nowa zaleznosc, savePullStatusMappings(), buildPullMappingIndex() |
|
||||
| `src/Modules/Settings/ShopproOrdersSyncService.php` | Modified | buildStatusMap() z pull tabeli, payment transition log |
|
||||
| `src/Modules/Orders/OrderImportRepository.php` | Modified | Status protection + payment transition logic + getCurrentStatus() |
|
||||
| `src/Modules/Cron/CronHandlerFactory.php` | Modified | Wstrzykniecie ShopproPullStatusMappingRepository |
|
||||
| `resources/views/settings/shoppro.php` | Modified | Sekcja pull mapping, zmieniony tytul push |
|
||||
| `resources/lang/pl.php` | Modified | Klucze pull.*, zmieniony tytul/opis push |
|
||||
| `routes/web.php` | Modified | Nowa route POST .../statuses/save-pull |
|
||||
| `DOCS/DB_SCHEMA.md` | Modified | Dokumentacja nowej tabeli |
|
||||
| `DOCS/ARCHITECTURE.md` | Modified | Nowy endpoint save-pull |
|
||||
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis Phase 75 |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Osobna tabela pull zamiast flagi is_pull_target | Czysta separacja push/pull, brak ryzyka kolizji | Dodatkowa tabela ale prosta logika |
|
||||
| Status protection: tylko nieoplacone+paid=2 | User chce push-only z jednym wyjatkiem: platnosc | Re-import nie nadpisuje recznych zmian statusu |
|
||||
| Fallback na push table jesli pull repo null | Backward compatibility dla kodu ktory nie wstrzykuje pull repo | Bezpieczna migracja |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope additions | 1 | Ochrona statusu przy re-imporcie (na zadanie usera) |
|
||||
| Auto-fixed | 1 | Naprawa order 180 ze statusem "7" |
|
||||
|
||||
**Total impact:** Rozszerzenie scope o kluczowa logike biznesowa — status protection
|
||||
|
||||
### Scope Addition: Status Protection on Re-import
|
||||
|
||||
- **Requested by:** User during APPLY
|
||||
- **Issue:** Re-import z shopPRO nadpisywal recznie zmienione statusy (np. order 180: "wyslane" → "7")
|
||||
- **Fix:** `upsertOrderAggregate()` zachowuje current status chyba ze nieoplacone + payment confirmed
|
||||
- **Files:** `src/Modules/Orders/OrderImportRepository.php`
|
||||
|
||||
### Auto-fixed: Order 180 status "7"
|
||||
|
||||
- **Found during:** Investigation
|
||||
- **Issue:** Surowy kod shopPRO "7" (nieznany status) nadpisal "wyslane"
|
||||
- **Fix:** Status juz poprawiony (wyslane). Nowa logika zapobiega powtorzeniu.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Pull mapping w pelni funkcjonalne i konfigurowalne
|
||||
- Status protection chroni reczne zmiany w orderPRO
|
||||
- Push sync (ShopproStatusSyncService) niezmodyfikowany — dziala jak dotychczas
|
||||
|
||||
**Concerns:**
|
||||
- shopPRO status "7" nie ma mapowania — user powinien zsynchronizowac statusy (przycisk w UI) lub dodac recznie
|
||||
- Allegro mappings nie maja analogicznego pull — jesli potrzebne, osobna faza
|
||||
|
||||
**Blockers:**
|
||||
- None — kod wymaga deploy na serwer (FTP)
|
||||
|
||||
---
|
||||
*Phase: 75-pull-status-mapping, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
155
.paul/phases/76-shipment-receiver-fallback/76-01-PLAN.md
Normal file
155
.paul/phases/76-shipment-receiver-fallback/76-01-PLAN.md
Normal file
@@ -0,0 +1,155 @@
|
||||
---
|
||||
phase: 76-shipment-receiver-fallback
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/Modules/Shipments/ShipmentController.php
|
||||
- src/Modules/Settings/ShopproOrderMapper.php
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Naprawic puste dane odbiorcy na stronie przygotowania przesylki (`/orders/{id}/shipment/prepare`) dla zamowien shopPRO, gdzie adres dostawy zawiera tylko nazwe metody dostawy bez danych adresowych.
|
||||
|
||||
## Purpose
|
||||
Sprzedawca nie musi reczne przepisywac danych klienta do formularza przesylki — formularz automatycznie wypelnia sie danymi z adresu klienta gdy adres dostawy nie ma pelnych danych.
|
||||
|
||||
## Output
|
||||
- Poprawiona metoda `buildReceiverAddress` w ShipmentController z fallbackami na dane klienta
|
||||
- Poprawiona metoda `buildDeliveryAddress` w ShopproOrderMapper — rozroznienie label metody dostawy od nazwy odbiorcy
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Shipments/ShipmentController.php
|
||||
@src/Modules/Settings/ShopproOrderMapper.php
|
||||
|
||||
## Diagnostyka
|
||||
Zamowienie 183 (shopPRO) — dane w `order_addresses`:
|
||||
- customer: name="Paulina Smolinska", phone="505799865", email="paulinasmolinska2@wp.pl", street="Zamojskiego 80/46", city="Zuromin", zip="09-300"
|
||||
- delivery: name="Kurier - przedplata: 0 zl", phone="505799865", email="...", street=NULL, city=NULL, zip=NULL
|
||||
|
||||
Problem: `buildReceiverAddress` uzywa delivery jako bazy, ale fallbacki dzialaja TYLKO dla name/phone/email — brak fallbacku na street/city/zip/country z customer address.
|
||||
Dodatkowy problem: mapper zapisuje label metody dostawy jako `name` w delivery address.
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
No specialized flows configured
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Fallback danych adresowych na klienta
|
||||
```gherkin
|
||||
Given zamowienie shopPRO z adresem delivery bez street/city/zip
|
||||
And adres customer ma pelne dane (name, street, city, zip)
|
||||
When uzytkownik otwiera /orders/{id}/shipment/prepare
|
||||
Then formularz "Adres odbiorcy" jest wypelniony danymi z adresu customer (imie, ulica, miasto, kod)
|
||||
```
|
||||
|
||||
## AC-2: Delivery z pelnymi danymi — bez zmian
|
||||
```gherkin
|
||||
Given zamowienie z adresem delivery zawierajacym pelne dane (street, city, zip)
|
||||
When uzytkownik otwiera /orders/{id}/shipment/prepare
|
||||
Then formularz uzywa danych z adresu delivery (bez zmian wzgledem obecnego zachowania)
|
||||
```
|
||||
|
||||
## AC-3: Nazwa odbiorcy — fallback na klienta gdy delivery name to label metody
|
||||
```gherkin
|
||||
Given zamowienie shopPRO z delivery name = "Kurier - przedplata: 0 zl"
|
||||
And customer name = "Paulina Smolinska"
|
||||
When uzytkownik otwiera /orders/{id}/shipment/prepare
|
||||
Then pole "Imie i nazwisko" zawiera "Paulina Smolinska"
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Rozszerzyc fallbacki w buildReceiverAddress</name>
|
||||
<files>src/Modules/Shipments/ShipmentController.php</files>
|
||||
<action>
|
||||
W metodzie `buildReceiverAddress` (linia 423) dodac fallbacki z customer na delivery dla pol adresowych:
|
||||
- `street_name`: jezeli w delivery jest puste/null, uzyj z customer
|
||||
- `street_number`: jezeli w delivery jest puste/null, uzyj z customer
|
||||
- `city`: jezeli w delivery jest puste/null, uzyj z customer
|
||||
- `zip_code`: jezeli w delivery jest puste/null, uzyj z customer
|
||||
- `country`: jezeli w delivery jest puste/null, uzyj z customer
|
||||
|
||||
Dodatkowo zmienic warunek fallbacku `name`:
|
||||
Obecny: fallback na customer name tylko gdy pickup point LUB delivery name jest puste.
|
||||
Nowy: fallback na customer name rowniez gdy delivery name wyglada jak label metody dostawy
|
||||
(nie ma adresu ulicy — tzn. jezeli delivery nie ma street_name, to name tez powinno byc z customer).
|
||||
|
||||
Logika: jezeli delivery nie ma street_name (jest null/pusty) i customer ma name, uzyj customer name.
|
||||
To pokrywa zarowno przypadek pickup point, jak i "Kurier - przedplata" bez danych adresowych.
|
||||
|
||||
Wzorzec fallbacku (analogicznie do istniejacych phone/email):
|
||||
```php
|
||||
if (trim((string) ($result['street_name'] ?? '')) === '' && trim((string) ($customer['street_name'] ?? '')) !== '') {
|
||||
$result['street_name'] = $customer['street_name'];
|
||||
}
|
||||
```
|
||||
I tak samo dla street_number, city, zip_code, country.
|
||||
|
||||
Dla name — zmienic warunek z:
|
||||
```php
|
||||
if (($this->isPickupPointDelivery($delivery) || $deliveryName === '') && $customerName !== '') {
|
||||
```
|
||||
na:
|
||||
```php
|
||||
$deliveryHasAddress = trim((string) ($delivery['street_name'] ?? '')) !== '';
|
||||
if ((!$deliveryHasAddress || $this->isPickupPointDelivery($delivery) || $deliveryName === '') && $customerName !== '') {
|
||||
```
|
||||
</action>
|
||||
<verify>
|
||||
Sprawdzic ze zamowienie 183 na /orders/183/shipment/prepare wyswietla dane klienta w formularzu odbiorcy.
|
||||
Sprawdzic ze zamowienia z pelnym adresem delivery nadal dzialaja poprawnie.
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-3 satisfied: formularz wypelnia sie danymi klienta gdy delivery nie ma danych adresowych</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- src/Modules/Orders/OrderImportRepository.php (logika importu)
|
||||
- src/Modules/Orders/OrdersRepository.php (logika zapytan)
|
||||
- resources/views/shipments/prepare.php (widok — formularz juz prawidlowo czyta z $receiver)
|
||||
- database/migrations/* (schemat bazy)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie zmieniamy sposobu importu adresow z shopPRO (ShopproOrderMapper::buildDeliveryAddress) — problem jest w prezentacji, nie w imporcie
|
||||
- Nie zmieniamy struktury tabeli order_addresses
|
||||
- Fix dotyczy tylko buildReceiverAddress w ShipmentController
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Zamowienie 183: formularz na /orders/183/shipment/prepare wypelnia sie danymi klienta
|
||||
- [ ] Zamowienie z pelnym adresem delivery: formularz uzywa danych delivery
|
||||
- [ ] Zamowienie z pickup point (paczkomat): formularz uzywa name klienta i adres punktu
|
||||
- [ ] Brak bledow PHP
|
||||
- [ ] Docs zaktualizowane (ARCHITECTURE.md, TECH_CHANGELOG.md)
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Formularz odbiorcy na stronie przygotowania przesylki jest wypelniony danymi klienta gdy delivery nie ma adresu
|
||||
- Istniejace zamowienia z pelnym adresem delivery dzialaja bez zmian
|
||||
- Brak regresji w tworzeniu przesylek
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/76-shipment-receiver-fallback/76-01-SUMMARY.md`
|
||||
</output>
|
||||
112
.paul/phases/76-shipment-receiver-fallback/76-01-SUMMARY.md
Normal file
112
.paul/phases/76-shipment-receiver-fallback/76-01-SUMMARY.md
Normal file
@@ -0,0 +1,112 @@
|
||||
---
|
||||
phase: 76-shipment-receiver-fallback
|
||||
plan: 01
|
||||
subsystem: shipments
|
||||
tags: [address-fallback, shoppro, receiver-data]
|
||||
|
||||
requires:
|
||||
- phase: none
|
||||
provides: none
|
||||
provides:
|
||||
- Fallback danych odbiorcy z customer na delivery w formularzu przesylki
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [address-field-fallback-loop]
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- src/Modules/Shipments/ShipmentController.php
|
||||
|
||||
key-decisions:
|
||||
- "Fix w buildReceiverAddress zamiast w mapperze — problem prezentacyjny, nie importowy"
|
||||
- "Foreach loop zamiast osobnych if-ow — czystszy kod, latwiejsze rozszerzenie"
|
||||
|
||||
patterns-established:
|
||||
- "buildReceiverAddress fallback: delivery -> customer dla wszystkich pol adresowych"
|
||||
|
||||
duration: 10min
|
||||
started: 2026-04-07T00:00:00Z
|
||||
completed: 2026-04-07T00:10:00Z
|
||||
---
|
||||
|
||||
# Phase 76 Plan 01: Shipment Receiver Fallback Summary
|
||||
|
||||
**Fallback danych odbiorcy z customer address gdy delivery address nie ma danych adresowych (ulica/miasto/kod)**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~10min |
|
||||
| Tasks | 1 completed |
|
||||
| Files modified | 3 (code + docs) |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Fallback danych adresowych na klienta | Pass | Loop fallback na 7 pol (phone, email, street_name, street_number, city, zip_code, country) |
|
||||
| AC-2: Delivery z pelnymi danymi — bez zmian | Pass | Fallback uruchamia sie tylko gdy pole jest puste |
|
||||
| AC-3: Nazwa odbiorcy — fallback gdy delivery nie ma ulicy | Pass | Warunek `!$deliveryHasAddress` pokrywa przypadek label metody dostawy |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Formularz odbiorcy na `/orders/{id}/shipment/prepare` automatycznie wypelnia sie danymi klienta gdy delivery nie ma danych adresowych
|
||||
- Uproszczono kod — foreach loop zamiast powtarzajacych sie if-ow dla phone/email, rozszerzony o street/city/zip/country
|
||||
- Dodano warunek name fallback: gdy delivery nie ma ulicy, name tez jest pobierane z customer (pokrywa "Kurier - przedplata" jako delivery name)
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Shipments/ShipmentController.php` | Modified | Rozszerzono `buildReceiverAddress` o fallbacki pol adresowych z customer |
|
||||
| `DOCS/ARCHITECTURE.md` | Modified | Opis nowej logiki fallbacku w sekcji przeplywu tworzenia przesylki |
|
||||
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis Phase 76 z opisem problemu i rozwiazania |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Fix w buildReceiverAddress, nie w mapperze | Problem jest prezentacyjny — dane w DB sa poprawne (customer ma pelne dane), mapper robi co moze z danymi API shopPRO | Brak zmian w logice importu |
|
||||
| Foreach loop zamiast osobnych if-ow | Uproszczenie kodu, latwiejsze dodanie nowych pol w przyszlosci | Zamieniono 2 osobne if-y na 1 loop pokrywajacy 7 pol |
|
||||
| ShopproOrderMapper.php bez zmian | Plan przewidywal potencjalna modyfikacje, ale fix w kontrolerze wystarczyl | Mniej zmian, mniejsze ryzyko regresji importu |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope reduction | 1 | Pozytywny — mniej zmian |
|
||||
|
||||
**Total impact:** Mniejszy zakres niz planowany — ShopproOrderMapper nie wymaga zmian
|
||||
|
||||
### Details
|
||||
|
||||
**1. ShopproOrderMapper.php bez zmian**
|
||||
- **Plan:** files_modified zawieral ShopproOrderMapper.php
|
||||
- **Rzeczywistosc:** Fix w buildReceiverAddress wystarczyl, mapper nie wymaga modyfikacji
|
||||
- **Impact:** Pozytywny — mniej kodu do zmiany, zero ryzyka regresji importu
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Formularz przesylki dziala poprawnie dla zamowien shopPRO z niekompletnymi adresami delivery
|
||||
- Logika jest generyczna — dziala dla kazdego zrodla zamowien
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 76-shipment-receiver-fallback, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
157
.paul/phases/77-cod-amount-fix/77-01-PLAN.md
Normal file
157
.paul/phases/77-cod-amount-fix/77-01-PLAN.md
Normal file
@@ -0,0 +1,157 @@
|
||||
---
|
||||
phase: 77-cod-amount-fix
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/Core/Support/StringHelper.php
|
||||
- src/Modules/Settings/ShopproOrderMapper.php
|
||||
- resources/views/shipments/prepare.php
|
||||
- resources/views/orders/show.php
|
||||
- src/Modules/Orders/OrdersController.php
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Naprawić automatyczne uzupełnianie kwoty pobrania (COD) przy generowaniu przesyłki dla zamówień spoza Allegro (shopPRO).
|
||||
|
||||
## Purpose
|
||||
Zamówienia shopPRO z płatnością za pobraniem nie mają auto-wypełnionej kwoty COD w formularzu przesyłki, ponieważ pole `external_payment_type_id` zawiera wartość z shopPRO API (np. `"cod"`, `"pobranie"`), a nie Allegro-specyficzne `"CASH_ON_DELIVERY"`.
|
||||
|
||||
## Output
|
||||
- Centralna metoda `StringHelper::isCodPayment()` zastępuje wszystkie hardcoded sprawdzenia
|
||||
- ShopproOrderMapper normalizuje typ płatności do `CASH_ON_DELIVERY` przy imporcie
|
||||
- Kwota pobrania auto-wypełnia się poprawnie dla zamówień z każdego źródła
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Core/Support/StringHelper.php
|
||||
@src/Modules/Settings/ShopproOrderMapper.php
|
||||
@resources/views/shipments/prepare.php
|
||||
@resources/views/orders/show.php
|
||||
@src/Modules/Orders/OrdersController.php
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
No specialized flows configured — optional skills only.
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: COD auto-fill dla zamówień shopPRO
|
||||
```gherkin
|
||||
Given zamówienie shopPRO z payment_method "cod" lub "pobranie" lub "za pobraniem"
|
||||
When użytkownik wchodzi na /orders/{id}/shipment/prepare
|
||||
Then pole "Pobranie" zawiera kwotę total_with_tax zamówienia
|
||||
And widoczny jest badge "ZA POBRANIEM"
|
||||
```
|
||||
|
||||
## AC-2: COD auto-fill dla zamówień Allegro (regresja)
|
||||
```gherkin
|
||||
Given zamówienie Allegro z external_payment_type_id "CASH_ON_DELIVERY"
|
||||
When użytkownik wchodzi na /orders/{id}/shipment/prepare
|
||||
Then pole "Pobranie" zawiera kwotę total_with_tax zamówienia (bez zmian w zachowaniu)
|
||||
```
|
||||
|
||||
## AC-3: Detekcja COD w widoku szczegółów zamówienia
|
||||
```gherkin
|
||||
Given zamówienie z dowolnym wariantem nazwy COD
|
||||
When użytkownik przegląda szczegóły zamówienia
|
||||
Then badge "Za pobraniem" wyświetla się poprawnie
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Centralna metoda isCodPayment w StringHelper + normalizacja w ShopproOrderMapper</name>
|
||||
<files>src/Core/Support/StringHelper.php, src/Modules/Settings/ShopproOrderMapper.php</files>
|
||||
<action>
|
||||
1. W `StringHelper` dodaj statyczną metodę `isCodPayment(string $value): bool`:
|
||||
- Normalizuj wartość: `strtoupper(trim($value))`
|
||||
- Sprawdź czy pasuje do znanego zbioru: `CASH_ON_DELIVERY`, `COD`, `POBRANIE`, `ZA POBRANIEM`
|
||||
- Zwróć true/false
|
||||
|
||||
2. W `ShopproOrderMapper::mapOrderAggregate()` (linia 139):
|
||||
- Po odczytaniu `payment_method` ze ścieżek `['payment_method', 'payment.method', 'payments.method']`
|
||||
- Jeśli odczytana wartość jest rozpoznawana jako COD (przez `StringHelper::isCodPayment()`), znormalizuj na `'CASH_ON_DELIVERY'`
|
||||
- Dzięki temu nowe importy będą miały ujednolicony format
|
||||
|
||||
Nie zmieniaj kontraktu ShopproOrderMapper dla pozostałych pól.
|
||||
</action>
|
||||
<verify>
|
||||
Przegląd kodu: metoda isCodPayment obsługuje min. 4 warianty; ShopproOrderMapper normalizuje COD.
|
||||
</verify>
|
||||
<done>AC-1 i AC-2 spełnione od strony danych — nowe importy mają ujednolicony format</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Zamiana hardcoded sprawdzeń na StringHelper::isCodPayment</name>
|
||||
<files>resources/views/shipments/prepare.php, resources/views/orders/show.php, src/Modules/Orders/OrdersController.php</files>
|
||||
<action>
|
||||
1. `resources/views/shipments/prepare.php` linia 41:
|
||||
- Zamień: `strtoupper(trim(...)) === 'CASH_ON_DELIVERY'`
|
||||
- Na: `\App\Core\Support\StringHelper::isCodPayment((string) ($orderRow['external_payment_type_id'] ?? ''))`
|
||||
|
||||
2. `src/Modules/Orders/OrdersController.php` linia 343:
|
||||
- Zamień: `$isCod = $paymentType === 'CASH_ON_DELIVERY'`
|
||||
- Na: `$isCod = StringHelper::isCodPayment($paymentType)`
|
||||
- Upewnij się że `use App\Core\Support\StringHelper;` jest w importach
|
||||
|
||||
3. `resources/views/orders/show.php`:
|
||||
- Znajdź wszystkie porównania z `'CASH_ON_DELIVERY'` (linie ~222, 228, 578, 678)
|
||||
- Zamień na `\App\Core\Support\StringHelper::isCodPayment(...)`
|
||||
- Zachowaj mapę wyświetlania nazw (array z 'CASH_ON_DELIVERY' => 'Za pobraniem' itp.) — dodaj warianty COD do mapy
|
||||
|
||||
Nie zmieniaj logiki wyświetlania ani formatowania — tylko warunek detekcji COD.
|
||||
</action>
|
||||
<verify>
|
||||
Grep po codebase: brak hardcoded `=== 'CASH_ON_DELIVERY'` w plikach PHP poza testami i DOCS.
|
||||
Widok shipment prepare: zamówienie shopPRO z pobraniem ma auto-wypełnioną kwotę.
|
||||
</verify>
|
||||
<done>AC-1, AC-2, AC-3 spełnione — detekcja COD działa dla wszystkich źródeł zamówień</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- src/Modules/Settings/AllegroOrderImportService.php (Allegro import działa poprawnie)
|
||||
- database/migrations/* (brak zmian schematu)
|
||||
- src/Modules/Shipments/ShipmentController.php (logika tworzenia przesyłki nie wymaga zmian)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie aktualizujemy istniejących danych w bazie (istniejące zamówienia shopPRO z "cod" zachowają starą wartość — helper obsłuży je w runtime)
|
||||
- Nie dodajemy migracji
|
||||
- Nie zmieniamy API ani endpointów
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] `StringHelper::isCodPayment()` rozpoznaje: CASH_ON_DELIVERY, COD, POBRANIE, ZA POBRANIEM
|
||||
- [ ] ShopproOrderMapper normalizuje COD na 'CASH_ON_DELIVERY' przy nowych importach
|
||||
- [ ] Brak hardcoded `=== 'CASH_ON_DELIVERY'` w prepare.php, show.php, OrdersController.php
|
||||
- [ ] Formularz przesyłki auto-wypełnia kwotę COD dla zamówień shopPRO
|
||||
- [ ] Brak regresji: zamówienia Allegro nadal działają poprawnie
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Wszystkie 2 taski ukończone
|
||||
- Weryfikacja przeszła pomyślnie
|
||||
- Brak błędów PHP na stronach przygotowania przesyłki i szczegółów zamówienia
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/77-cod-amount-fix/77-01-SUMMARY.md`
|
||||
</output>
|
||||
127
.paul/phases/77-cod-amount-fix/77-01-SUMMARY.md
Normal file
127
.paul/phases/77-cod-amount-fix/77-01-SUMMARY.md
Normal file
@@ -0,0 +1,127 @@
|
||||
---
|
||||
phase: 77-cod-amount-fix
|
||||
plan: 01
|
||||
subsystem: shipments
|
||||
tags: [cod, payment, stringhelper, shoppro]
|
||||
|
||||
requires:
|
||||
- phase: none
|
||||
provides: n/a
|
||||
provides:
|
||||
- StringHelper::isCodPayment() — centralna detekcja platnosci COD
|
||||
- Normalizacja COD w ShopproOrderMapper przy imporcie
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [keyword-based COD detection via StringHelper]
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- src/Core/Support/StringHelper.php
|
||||
- src/Modules/Settings/ShopproOrderMapper.php
|
||||
- resources/views/shipments/prepare.php
|
||||
- resources/views/orders/show.php
|
||||
- src/Modules/Orders/OrdersController.php
|
||||
|
||||
key-decisions:
|
||||
- "Keyword matching zamiast exact match — shopPRO wysyla pelne polskie nazwy metod platnosci"
|
||||
- "Dwupoziomowa detekcja: exact match (COD_PAYMENT_TYPES) + keyword search (COD_PAYMENT_KEYWORDS)"
|
||||
|
||||
patterns-established:
|
||||
- "StringHelper::isCodPayment() jako jedyne miejsce detekcji COD w codebase"
|
||||
|
||||
duration: 15min
|
||||
started: 2026-04-07T00:00:00Z
|
||||
completed: 2026-04-07T00:15:00Z
|
||||
---
|
||||
|
||||
# Phase 77 Plan 01: COD Amount Fix Summary
|
||||
|
||||
**Centralna detekcja platnosci COD (StringHelper::isCodPayment) z keyword matching dla shopPRO wartosci typu "Platnosc przy odbiorze"**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~15min |
|
||||
| Started | 2026-04-07 |
|
||||
| Completed | 2026-04-07 |
|
||||
| Tasks | 2 completed |
|
||||
| Files modified | 5 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: COD auto-fill dla zamowien shopPRO | Pass | Testowane na zamowieniach 188/207 z wartoscia "Platnosc przy odbiorze" |
|
||||
| AC-2: COD auto-fill dla zamowien Allegro (regresja) | Pass | CASH_ON_DELIVERY nadal rozpoznawane przez exact match |
|
||||
| AC-3: Detekcja COD w widoku szczegulow zamowienia | Pass | Badge "Za pobraniem" wyswietla sie dla wszystkich wariantow COD |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Centralna metoda `StringHelper::isCodPayment()` z dwupoziomowa detekcja: exact match na 4 warianty + keyword match na 3 frazy (PRZY ODBIORZE, POBRANIEM, POBRANIE)
|
||||
- Normalizacja COD na `CASH_ON_DELIVERY` w `ShopproOrderMapper` przy nowych importach
|
||||
- Eliminacja wszystkich hardcoded `=== 'CASH_ON_DELIVERY'` z kodu produkcyjnego (3 pliki)
|
||||
- Rozszerzenie map etykiet platnosci w `show.php` o warianty COD
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Core/Support/StringHelper.php` | Modified | Dodano COD_PAYMENT_TYPES, COD_PAYMENT_KEYWORDS, isCodPayment() |
|
||||
| `src/Modules/Settings/ShopproOrderMapper.php` | Modified | normalizeCodPaymentType() + uzycie w mapOrderAggregate |
|
||||
| `resources/views/shipments/prepare.php` | Modified | Zamiana hardcoded na StringHelper::isCodPayment() |
|
||||
| `resources/views/orders/show.php` | Modified | Zamiana 2 sprawdzen + rozszerzenie map etykiet o warianty COD |
|
||||
| `src/Modules/Orders/OrdersController.php` | Modified | Zamiana hardcoded na StringHelper::isCodPayment() |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
| Decision | Rationale | Impact |
|
||||
|----------|-----------|--------|
|
||||
| Keyword matching zamiast samego exact match | shopPRO wysyla pelne polskie nazwy np. "Platnosc przy odbiorze" — nie da sie przewidziec wszystkich wariantow | Odpornosc na nowe warianty nazw COD |
|
||||
| Dwupoziomowa detekcja (exact + keyword) | Exact match jest szybszy i pewniejszy dla znanych wartosci, keyword jako fallback | Brak false positives dla ONLINE/TRANSFER, elastycznosc dla COD |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Auto-fixed | 1 | Kluczowe — bez tego fix nie dzialal |
|
||||
| Scope additions | 0 | - |
|
||||
| Deferred | 0 | - |
|
||||
|
||||
**Total impact:** Konieczne rozszerzenie — plan zakladal 4 warianty exact match, rzeczywistosc wymagala keyword matching.
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. Wartosc shopPRO to "Platnosc przy odbiorze" — nie ma w liscie exact match**
|
||||
- **Found during:** Weryfikacja po APPLY (user report)
|
||||
- **Issue:** Plan zakladal warianty "cod", "pobranie", "za pobraniem". Rzeczywista wartosc w DB to "Platnosc przy odbiorze"
|
||||
- **Fix:** Dodano COD_PAYMENT_KEYWORDS z keyword matching (str_contains) jako drugi poziom detekcji
|
||||
- **Files:** src/Core/Support/StringHelper.php, resources/views/orders/show.php
|
||||
- **Verification:** 9/9 unit testow przeszlo, w tym "Platnosc przy odbiorze" => true
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
| Issue | Resolution |
|
||||
|-------|------------|
|
||||
| Poczatkowy fix nie dzialal — shopPRO wysyla "Platnosc przy odbiorze" nie "cod" | Sprawdzenie DB (orders WHERE id IN (188,207)), dodanie keyword matching |
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- StringHelper::isCodPayment() gotowy do uzycia w kolejnych miejscach
|
||||
- Nowe importy shopPRO beda mialy znormalizowane CASH_ON_DELIVERY
|
||||
|
||||
**Concerns:**
|
||||
- Istniejace zamowienia w DB zachowuja oryginalna wartosc "Platnosc przy odbiorze" — helper obsluguje je w runtime, ale dane nie sa znormalizowane
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 77-cod-amount-fix, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
109
.paul/phases/78-preset-auto-submit/78-01-PLAN.md
Normal file
109
.paul/phases/78-preset-auto-submit/78-01-PLAN.md
Normal file
@@ -0,0 +1,109 @@
|
||||
---
|
||||
phase: 78-preset-auto-submit
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- resources/views/shipments/prepare.php
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Presety przesyłek po autofill automatycznie submitują formularz (kliknięcie "Utwórz przesyłkę").
|
||||
|
||||
## Purpose
|
||||
Aktualnie użytkownik musi kliknąć preset, a potem ręcznie kliknąć "Utwórz przesyłkę". Skoro preset wypełnia wszystkie dane — submit powinien nastąpić automatycznie, oszczędzając jedno kliknięcie.
|
||||
|
||||
## Output
|
||||
- Funkcja `applyPreset()` po wypełnieniu pól automatycznie submituje formularz
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@resources/views/shipments/prepare.php (linie 957-993: applyPreset + setTimeout 200ms)
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
No specialized flows configured — optional skills only.
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Auto-submit po kliknięciu presetu
|
||||
```gherkin
|
||||
Given formularz przygotowania przesyłki z co najmniej jednym presetem
|
||||
When użytkownik klika przycisk presetu
|
||||
Then formularz wypełnia się danymi presetu
|
||||
And formularz automatycznie się submituje (tworzenie przesyłki)
|
||||
```
|
||||
|
||||
## AC-2: Brak regresji autofill
|
||||
```gherkin
|
||||
Given preset z zapisanymi danymi (carrier, wymiary, metoda dostawy)
|
||||
When preset jest aplikowany
|
||||
Then wszystkie pola formularza są poprawnie wypełnione przed submitem
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Dodanie auto-submit po applyPreset</name>
|
||||
<files>resources/views/shipments/prepare.php</files>
|
||||
<action>
|
||||
W funkcji `applyPreset()` (linia ~957), wewnątrz istniejącego `setTimeout` (200ms) na końcu callbacku (po `selectDeliveryService(preset)`):
|
||||
|
||||
1. Dodaj drugi `setTimeout` (krótki, np. 100ms) po zakończeniu autofill, który:
|
||||
- Znajduje formularz: `document.getElementById('shipment-form')` lub `document.querySelector('form[action*="shipment/create"]')`
|
||||
- Wywołuje `form.submit()` lub klika przycisk submit
|
||||
|
||||
2. Formularz może nie mieć id — sprawdź czy `<form>` ma id. Jeśli nie, dodaj `id="shipment-form"` do tagu `<form>`.
|
||||
|
||||
Uwaga: `selectDeliveryService()` może mieć swój setTimeout — sprawdź czy submit nie nastąpi przed zakończeniem selekcji. Użyj wystarczającego opóźnienia (np. łącznie 400-500ms od kliknięcia presetu).
|
||||
</action>
|
||||
<verify>
|
||||
Przegląd kodu: po applyPreset formularz submituje się automatycznie.
|
||||
PHP lint: brak błędów składni.
|
||||
</verify>
|
||||
<done>AC-1 i AC-2 spełnione — preset autofill + auto-submit</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Logika autofill presetu (kolejność wypełniania pól, setTimeout 200ms)
|
||||
- Backend ShipmentController (logika tworzenia przesyłki)
|
||||
- API presetów (/api/shipment-presets/*)
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Tylko auto-submit po kliknięciu presetu
|
||||
- Nie zmieniamy zachowania przycisku "Utwórz przesyłkę" ani ręcznego formularza
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Kliknięcie presetu wypełnia formularz i submituje go
|
||||
- [ ] Formularz ma id umożliwiające łatwe znalezienie w JS
|
||||
- [ ] Brak błędów PHP syntax
|
||||
- [ ] Ręczne wypełnienie formularza i kliknięcie "Utwórz przesyłkę" nadal działa
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Task 1 ukończony
|
||||
- Weryfikacja przeszła
|
||||
- Brak błędów PHP
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/78-preset-auto-submit/78-01-SUMMARY.md`
|
||||
</output>
|
||||
91
.paul/phases/78-preset-auto-submit/78-01-SUMMARY.md
Normal file
91
.paul/phases/78-preset-auto-submit/78-01-SUMMARY.md
Normal file
@@ -0,0 +1,91 @@
|
||||
---
|
||||
phase: 78-preset-auto-submit
|
||||
plan: 01
|
||||
subsystem: shipments
|
||||
tags: [presets, autofill, auto-submit, ux]
|
||||
|
||||
requires:
|
||||
- phase: 23-shipment-presets-backend
|
||||
provides: preset API i autofill
|
||||
provides:
|
||||
- Auto-submit formularza po kliknieciu presetu przesylki
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: []
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified:
|
||||
- resources/views/shipments/prepare.php
|
||||
|
||||
key-decisions:
|
||||
- "500ms laczny delay (200ms autofill + 300ms submit) — wystarczajacy na selectDeliveryService"
|
||||
|
||||
patterns-established: []
|
||||
|
||||
duration: 5min
|
||||
started: 2026-04-07T00:00:00Z
|
||||
completed: 2026-04-07T00:05:00Z
|
||||
---
|
||||
|
||||
# Phase 78 Plan 01: Preset Auto Submit Summary
|
||||
|
||||
**Presety przesylek automatycznie submituja formularz po autofill — jedno klikniecie zamiast dwoch**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~5min |
|
||||
| Started | 2026-04-07 |
|
||||
| Completed | 2026-04-07 |
|
||||
| Tasks | 1 completed |
|
||||
| Files modified | 1 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Auto-submit po kliknieciu presetu | Pass | form.submit() po 500ms od klikniecia |
|
||||
| AC-2: Brak regresji autofill | Pass | Autofill bez zmian, submit nastepuje po nim |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Dodano `id="shipment-form"` na formularz tworzenia przesylki
|
||||
- `applyPreset()` po autofill (200ms) czeka 300ms i wywoluje `form.submit()`
|
||||
- Jedno klikniecie presetu = wypelnienie + utworzenie przesylki
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `resources/views/shipments/prepare.php` | Modified | id na formularzu + auto-submit w applyPreset() |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
None — plan wykonany zgodnie ze specyfikacja.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Preset flow kompletny: klik → autofill → submit
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 78-preset-auto-submit, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
167
.paul/phases/79-personalization-message-field/79-01-PLAN.md
Normal file
167
.paul/phases/79-personalization-message-field/79-01-PLAN.md
Normal file
@@ -0,0 +1,167 @@
|
||||
---
|
||||
phase: 79-personalization-message-field
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- src/Modules/Settings/ShopproOrderMapper.php
|
||||
- DOCS/ARCHITECTURE.md
|
||||
- DOCS/TECH_CHANGELOG.md
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Dodanie pola `message` z API shopPRO do personalizacji produktow w zamowieniach. Aktualnie `extractPersonalization()` sprawdza tylko `attributes` i `custom_fields`, a shopPRO zwraca rowniez pole `message` z wiadomoscia personalizacji klienta (np. "Milenie na pamiatke I Komunii Swietej").
|
||||
|
||||
## Purpose
|
||||
Klienci wpisuja wiadomosci personalizacji przy zamowieniach w shopPRO. Te dane sa kluczowe dla realizacji zamowien (np. grawerunki, dedykacje). Bez ich importu pracownik musi reczne sprawdzac dane w shopPRO.
|
||||
|
||||
## Output
|
||||
- Zaktualizowany `extractPersonalization()` w ShopproOrderMapper — obsluguje pole `message`
|
||||
- Istniejace zamowienia z `message` w payload_json — backfill personalizacji
|
||||
- Zaktualizowana dokumentacja
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Settings/ShopproOrderMapper.php (metoda extractPersonalization, linia ~590)
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
No specialized flows required for this plan.
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Pole message importowane do personalizacji
|
||||
```gherkin
|
||||
Given zamowienie shopPRO z pozycja majaca pole "message" w odpowiedzi API
|
||||
When pozycja jest importowana/aktualizowana przez ShopproOrderMapper
|
||||
Then wartosc pola "message" jest zapisana w kolumnie personalization tabeli order_items
|
||||
```
|
||||
|
||||
## AC-2: Laczenie message z attributes i custom_fields
|
||||
```gherkin
|
||||
Given pozycja shopPRO majaca zarowno "attributes" jak i "message"
|
||||
When extractPersonalization przetwarza dane
|
||||
Then oba pola sa polaczone w personalization oddzielone nowa linia
|
||||
And pole "message" jest poprzedzone etykieta "Wiadomosc:"
|
||||
```
|
||||
|
||||
## AC-3: Backfill istniejacych zamowien
|
||||
```gherkin
|
||||
Given istniejace pozycje zamowien z polem "message" w payload_json ale pustym personalization
|
||||
When uruchomiona jest migracja/skrypt backfill
|
||||
Then kolumna personalization zostaje wypelniona danymi z payload_json.message
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Dodanie pola message do extractPersonalization</name>
|
||||
<files>src/Modules/Settings/ShopproOrderMapper.php</files>
|
||||
<action>
|
||||
W metodzie `extractPersonalization()` (linia ~590):
|
||||
1. Dodac pole `message` do listy sprawdzanych pol OSOBNO po petli attributes/custom_fields
|
||||
2. Wartosc message powinna byc poprzedzona etykieta "Wiadomosc: " (z dwukropkiem i spacja)
|
||||
3. Zachowac istniejaca logike czyszczenia HTML (strip_tags, html_entity_decode, trim)
|
||||
4. Jesli message jest jedynym polem — zwrocic "Wiadomosc: {tresc}"
|
||||
5. Jesli sa tez attributes/custom_fields — dodac message na koncu po nowej linii
|
||||
|
||||
Logika:
|
||||
```
|
||||
// Po istniejącej pętli attributes/custom_fields:
|
||||
$message = $this->readPath($row, ['message']);
|
||||
if ($message !== null && $message !== '' && $message !== false) {
|
||||
$text = str_replace(['<br>', '<br/>', '<br />'], "\n", (string) $message);
|
||||
$text = html_entity_decode(strip_tags($text), ENT_QUOTES | ENT_HTML5, 'UTF-8');
|
||||
$text = trim($text);
|
||||
if ($text !== '') {
|
||||
$parts[] = 'Wiadomość: ' . $text;
|
||||
}
|
||||
}
|
||||
```
|
||||
</action>
|
||||
<verify>
|
||||
Sprawdzic w kodzie ze extractPersonalization obsluguje 3 pola: attributes, custom_fields, message.
|
||||
Zweryfikowac ze message jest poprzedzony etykieta "Wiadomosc:".
|
||||
</verify>
|
||||
<done>AC-1 i AC-2 satisfied: pole message jest importowane do personalizacji z etykieta</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Migracja backfill personalizacji z payload_json</name>
|
||||
<files>database/migrations/20260407_000080_backfill_personalization_message.sql</files>
|
||||
<action>
|
||||
Utworzyc migracje SQL ktora:
|
||||
1. Aktualizuje kolumne personalization dla pozycji majacych message w payload_json
|
||||
2. Warunek: personalization IS NULL AND payload_json zawiera niepuste pole message
|
||||
3. Uzyc JSON_UNQUOTE(JSON_EXTRACT(payload_json, '$.message')) do wyciagniecia wartosci
|
||||
4. Ustawic personalization = CONCAT('Wiadomość: ', extracted_message)
|
||||
5. Jesli personalization juz istnieje (nie NULL) — nie nadpisywac (dodac do WHERE)
|
||||
|
||||
Uwaga: Jezeli pozycja ma tez attributes/custom_fields w payload_json, sam SQL nie zbuduje pelnej personalizacji.
|
||||
Dla prostoty: backfill dotyczy TYLKO pozycji z pustym personalization.
|
||||
Pozycje z istniejacym personalization (z attributes/custom_fields) i brakujacym message — pomijamy
|
||||
(przyszly re-import uzupelni je poprawnie dzieki Task 1).
|
||||
</action>
|
||||
<verify>
|
||||
Uruchomic migracje na bazie i sprawdzic ze pozycje zamowienia #217 maja wypelniona personalizacje z polem Wiadomosc.
|
||||
</verify>
|
||||
<done>AC-3 satisfied: istniejace zamowienia maja uzupelniona personalizacje</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 3: Aktualizacja dokumentacji</name>
|
||||
<files>DOCS/ARCHITECTURE.md, DOCS/TECH_CHANGELOG.md</files>
|
||||
<action>
|
||||
1. W ARCHITECTURE.md — zaktualizowac opis ShopproOrderMapper::extractPersonalization o pole message
|
||||
2. W TECH_CHANGELOG.md — dodac wpis o rozszerzeniu importu personalizacji o pole message
|
||||
</action>
|
||||
<verify>Sprawdzic ze dokumenty sa aktualne</verify>
|
||||
<done>Dokumentacja zaktualizowana</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- Logika importu zamowien (OrderImportRepository) — zmiana tylko w mapperze
|
||||
- Widok show.php — juz obsluguje personalizacje (nl2br), nie wymaga zmian
|
||||
- Struktura tabeli order_items — kolumna personalization juz istnieje
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie zmieniamy sposobu wyswietlania personalizacji w widoku (juz dziala)
|
||||
- Nie dodajemy nowych kolumn do bazy
|
||||
- Backfill tylko dla pozycji z pustym personalization (nie nadpisujemy istniejacych)
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] extractPersonalization obsluguje pola: attributes, custom_fields, message
|
||||
- [ ] Pole message jest poprzedzone etykieta "Wiadomosc:"
|
||||
- [ ] Migracja backfill wykonana pomyslnie
|
||||
- [ ] Zamowienie #217 wyswietla personalizacje z wiadomosciami
|
||||
- [ ] Dokumentacja zaktualizowana
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Nowe zamowienia shopPRO z polem message importuja personalizacje
|
||||
- Istniejace zamowienia z message w payload_json maja uzupelniona personalizacje
|
||||
- Brak regresji w imporcie zamowien
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/79-personalization-message-field/79-01-SUMMARY.md`
|
||||
</output>
|
||||
117
.paul/phases/79-personalization-message-field/79-01-SUMMARY.md
Normal file
117
.paul/phases/79-personalization-message-field/79-01-SUMMARY.md
Normal file
@@ -0,0 +1,117 @@
|
||||
---
|
||||
phase: 79-personalization-message-field
|
||||
plan: 01
|
||||
subsystem: api
|
||||
tags: [shoppro, import, personalization, order-notes]
|
||||
|
||||
requires:
|
||||
- phase: 63-order-item-personalization
|
||||
provides: extractPersonalization z attributes/custom_fields
|
||||
provides:
|
||||
- import pola message z API shopPRO do personalizacji pozycji zamowien
|
||||
- import pola message z API shopPRO do notatek zamowienia (order_notes)
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: []
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- database/migrations/20260407_000080_backfill_personalization_message.sql
|
||||
modified:
|
||||
- src/Modules/Settings/ShopproOrderMapper.php
|
||||
- resources/views/orders/show.php
|
||||
- DOCS/ARCHITECTURE.md
|
||||
- DOCS/TECH_CHANGELOG.md
|
||||
|
||||
key-decisions:
|
||||
- "Pole message na pozycji poprzedzone etykieta 'Wiadomosc:' dla odroznienia od attributes/custom_fields"
|
||||
- "Pole message na poziomie zamowienia importowane do order_notes jako note_type=message"
|
||||
- "Usunieto etykiete 'Personalizacja:' z widoku — kolor tla wystarczajacy"
|
||||
|
||||
patterns-established: []
|
||||
|
||||
duration: ~15min
|
||||
started: 2026-04-07T00:00:00Z
|
||||
completed: 2026-04-07T00:15:00Z
|
||||
---
|
||||
|
||||
# Phase 79 Plan 01: Personalization Message Field Summary
|
||||
|
||||
**Import pola `message` z API shopPRO do personalizacji pozycji i notatek zamowienia + backfill istniejacych danych**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~15min |
|
||||
| Tasks | 3 completed (plan) + 2 deviations |
|
||||
| Files modified | 5 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Pole message importowane do personalizacji | Pass | extractPersonalization sprawdza attributes, custom_fields, message |
|
||||
| AC-2: Laczenie message z attributes i custom_fields | Pass | message poprzedzony etykieta "Wiadomosc:" |
|
||||
| AC-3: Backfill istniejacych zamowien | Pass | 21 pozycji + 70 notatek zamowien |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `extractPersonalization()` rozszerzony o pole `message` z etykieta "Wiadomosc:"
|
||||
- `mapNotes()` rozszerzony o pole `message` na poziomie zamowienia (wiadomosc klienta do sprzedawcy)
|
||||
- Backfill: 21 pozycji zamowien uzupelnionych o personalizacje, 70 zamowien uzupelnionych o notatki
|
||||
- Usunieto zbedna etykiete "Personalizacja:" z widoku zamowienia
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Settings/ShopproOrderMapper.php` | Modified | Dodano pole message do extractPersonalization + mapNotes |
|
||||
| `database/migrations/20260407_000080_backfill_personalization_message.sql` | Created | Backfill personalizacji i notatek z payload_json |
|
||||
| `resources/views/orders/show.php` | Modified | Usunieto etykiete "Personalizacja:" |
|
||||
| `DOCS/ARCHITECTURE.md` | Modified | Opis extractPersonalization z 3 polami |
|
||||
| `DOCS/TECH_CHANGELOG.md` | Modified | Wpis Phase 79 |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Summary
|
||||
|
||||
| Type | Count | Impact |
|
||||
|------|-------|--------|
|
||||
| Scope additions | 2 | Niezbedne uzupelnienia wykryte podczas UAT |
|
||||
|
||||
**Total impact:** Wiadomosc klienta importowana zarowno na poziomie pozycji jak i zamowienia.
|
||||
|
||||
### Scope Additions
|
||||
|
||||
**1. Import wiadomosci klienta do order_notes**
|
||||
- **Found during:** UAT zamowienia #218
|
||||
- **Issue:** Pole `message` na poziomie zamowienia (wiadomosc klienta do sprzedawcy) nie bylo importowane do sekcji "Wiadomosci i zalaczniki"
|
||||
- **Fix:** Dodano `'message'` do listy kluczy w `mapNotes()` + backfill 70 zamowien
|
||||
- **Files:** `src/Modules/Settings/ShopproOrderMapper.php`, migracja SQL
|
||||
|
||||
**2. Usuniecie etykiety "Personalizacja:"**
|
||||
- **Found during:** UAT
|
||||
- **Issue:** Etykieta zbedna — kolor tla wystarczajaco oznacza sekcje personalizacji
|
||||
- **Fix:** Usunieto `<span class="item-personalization__label">` z widoku
|
||||
- **Files:** `resources/views/orders/show.php`
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Import shopPRO kompletny — wszystkie pola personalizacji i wiadomosci klienta sa importowane
|
||||
- Backfill wykonany na produkcji
|
||||
|
||||
**Concerns:** None
|
||||
|
||||
**Blockers:** None
|
||||
|
||||
---
|
||||
*Phase: 79-personalization-message-field, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
106
.paul/phases/80-status-change-reload/80-01-PLAN.md
Normal file
106
.paul/phases/80-status-change-reload/80-01-PLAN.md
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
phase: 80-status-change-reload
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified: [public/assets/js/modules/inline-status-change.js]
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Po zmianie statusu zamowienia inline na liscie zamowien (/orders/list), strona przeladowuje sie automatycznie, dzieki czemu zamowienie znika z aktualnego widoku filtrowanego po statusie.
|
||||
|
||||
## Purpose
|
||||
Gdy uzytkownik filtruje zamowienia po statusie i zmienia status jednego z nich, zamowienie powinno zniknac z listy (bo juz nie pasuje do filtra). Obecnie badge aktualizuje sie w miejscu, ale zamowienie pozostaje na liscie co jest mylace.
|
||||
|
||||
## Output
|
||||
Zmodyfikowany `public/assets/js/modules/inline-status-change.js` z przeladowaniem strony po udanej zmianie statusu.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@public/assets/js/modules/inline-status-change.js
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Przeladowanie listy po zmianie statusu
|
||||
```gherkin
|
||||
Given uzytkownik jest na liscie zamowien z aktywnym filtrem statusu
|
||||
When zmienia status zamowienia przez inline dropdown
|
||||
Then strona przeladowuje sie po udanej odpowiedzi serwera
|
||||
And zamowienie z nowym statusem nie pojawia sie na liscie (bo filtr go wyklucza)
|
||||
```
|
||||
|
||||
## AC-2: Brak przeladowania przy bledzie
|
||||
```gherkin
|
||||
Given uzytkownik zmienia status zamowienia inline
|
||||
When serwer zwraca blad (np. 500 lub validation error)
|
||||
Then strona NIE przeladowuje sie
|
||||
And badge wraca do poprzedniego statusu
|
||||
And wyswietla sie komunikat bledu
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Dodanie location.reload() po udanej zmianie statusu</name>
|
||||
<files>public/assets/js/modules/inline-status-change.js</files>
|
||||
<action>
|
||||
W funkcji `changeStatus()`, w bloku `.then(function (result) {...})`:
|
||||
- Po linii 153-154 (po udanej aktualizacji badge'a), dodac `location.reload()`.
|
||||
- Reload powinien nastapic TYLKO gdy `result.ok && result.data.success`.
|
||||
- Blok error (linie 142-149) i `.catch()` (linie 156-162) pozostaja bez zmian — brak reloadu przy bledzie.
|
||||
- Mozna usunac aktualizacje badge'a (linie 152-154) bo reload i tak odswieza strone, ale lepiej zostawic dla plynnosci UX — uzytkownik widzi natychmiastowa zmiane badge'a, potem reload.
|
||||
</action>
|
||||
<verify>
|
||||
1. Otworz /orders/list z filtrem statusu (np. "Nowe")
|
||||
2. Zmien status zamowienia na inny (np. "W realizacji")
|
||||
3. Strona przeladowuje sie i zamowienie znika z listy
|
||||
4. Zmien status bez filtra — strona tez sie przeladowuje (zamowienie pojawia sie z nowym statusem)
|
||||
</verify>
|
||||
<done>AC-1 i AC-2 spelnione: reload po sukcesie, brak reloadu przy bledzie</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- src/Modules/Orders/OrdersController.php (backend endpoint dziala poprawnie)
|
||||
- resources/views/orders/list.php (konfiguracja JS jest OK)
|
||||
- routes/web.php
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Nie dodawac AJAX-owego odswiezania listy (pelny reload jest prostszy i wystarczajacy)
|
||||
- Nie zmieniac logiki dropdowna ani budowania badge'y
|
||||
- Nie zmieniac obslugi bledow
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Po zmianie statusu z filtrem — zamowienie znika z listy
|
||||
- [ ] Po zmianie statusu bez filtra — zamowienie ma nowy status po reload
|
||||
- [ ] Przy bledzie serwera — brak reloadu, badge wraca, komunikat bledu
|
||||
- [ ] Dropdown dziala jak wczesniej (otwieranie, zamykanie, Escape)
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Task 1 ukonczony
|
||||
- Wszystkie verification checks przechodzą
|
||||
- Brak regresji w inline status change
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/80-status-change-reload/80-01-SUMMARY.md`
|
||||
</output>
|
||||
81
.paul/phases/80-status-change-reload/80-01-SUMMARY.md
Normal file
81
.paul/phases/80-status-change-reload/80-01-SUMMARY.md
Normal file
@@ -0,0 +1,81 @@
|
||||
---
|
||||
phase: 80-status-change-reload
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [javascript, ajax, orders-list, inline-status]
|
||||
|
||||
requires:
|
||||
- phase: 44-inline-status-change
|
||||
provides: inline status change dropdown on orders list
|
||||
provides:
|
||||
- page reload after successful inline status change
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: []
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified: [public/assets/js/modules/inline-status-change.js]
|
||||
|
||||
key-decisions: []
|
||||
|
||||
patterns-established: []
|
||||
|
||||
duration: 2min
|
||||
started: 2026-04-07T00:00:00Z
|
||||
completed: 2026-04-07T00:00:00Z
|
||||
---
|
||||
|
||||
# Phase 80 Plan 01: Status Change Reload Summary
|
||||
|
||||
**Dodanie `location.reload()` po udanej zmianie statusu inline na liscie zamowien — zamowienie znika z filtrowanego widoku.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | 2min |
|
||||
| Tasks | 1 completed |
|
||||
| Files modified | 1 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Przeladowanie listy po zmianie statusu | Pass | `location.reload()` po sukcesie AJAX |
|
||||
| AC-2: Brak przeladowania przy bledzie | Pass | Bloki error/catch bez zmian — revert badge + alert |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Dodano `location.reload()` w `changeStatus()` po udanej odpowiedzi serwera (1 linia)
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `public/assets/js/modules/inline-status-change.js` | Modified | Dodano reload po udanej zmianie statusu |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Inline status change z reloadem dziala poprawnie
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 80-status-change-reload, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
207
.paul/phases/81-global-search-bar/81-01-PLAN.md
Normal file
207
.paul/phases/81-global-search-bar/81-01-PLAN.md
Normal file
@@ -0,0 +1,207 @@
|
||||
---
|
||||
phase: 81-global-search-bar
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified: [src/Modules/Orders/OrdersRepository.php, src/Modules/Orders/OrdersController.php, routes/web.php, resources/views/layouts/app.php, public/assets/js/modules/global-search.js, resources/scss/components/_global-search.scss, resources/scss/app.scss]
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Globalna wyszukiwarka zamowien widoczna w topbarze na kazdej stronie orderPRO. Wyszukuje po: numerze zamowienia, nazwisku klienta, e-mailu, telefonie, nazwie produktu. Wyniki pojawiaja sie jako dropdown pod polem wyszukiwania. Klikniecie wyniku przenosi do /orders/{id}.
|
||||
|
||||
## Purpose
|
||||
Szybki dostep do zamowienia z dowolnego miejsca w aplikacji — bez koniecznosci przechodzenia na liste zamowien i ustawiania filtrow. Analogicznie do wyszukiwarki w shopPRO.
|
||||
|
||||
## Output
|
||||
- Nowy endpoint GET /api/orders/search?q=...&limit=10
|
||||
- Pole wyszukiwania w topbarze (layout app.php)
|
||||
- Modul JS global-search.js (debounced AJAX, dropdown wynikow, nawigacja klawiaturowa)
|
||||
- Style SCSS dla komponentu wyszukiwarki
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Orders/OrdersRepository.php (linie 91-106 — istniejacy search SQL)
|
||||
@src/Modules/Orders/OrdersController.php (index() linie 44-71)
|
||||
@routes/web.php (linie 408-413 — order routes)
|
||||
@resources/views/layouts/app.php (linie 121-136 — topbar)
|
||||
</context>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Pole wyszukiwania widoczne w topbarze
|
||||
```gherkin
|
||||
Given uzytkownik jest zalogowany i na dowolnej stronie orderPRO
|
||||
When strona sie laduje
|
||||
Then w topbarze widoczne jest pole wyszukiwania z placeholderem "Szukaj zamowien..."
|
||||
And pole jest miedzy hamburgerem a sekcja uzytkownika
|
||||
```
|
||||
|
||||
## AC-2: Wyszukiwanie AJAX z debounce
|
||||
```gherkin
|
||||
Given uzytkownik wpisuje tekst w pole wyszukiwania (min. 2 znaki)
|
||||
When minie 300ms od ostatniego znaku (debounce)
|
||||
Then wykonywane jest zapytanie GET /api/orders/search?q=tekst&limit=10
|
||||
And pod polem pojawia sie dropdown z wynikami
|
||||
```
|
||||
|
||||
## AC-3: Wyniki wyszukiwania
|
||||
```gherkin
|
||||
Given backend znalazl pasujace zamowienia
|
||||
When dropdown jest wyswietlany
|
||||
Then kazdy wynik pokazuje: numer zamowienia, nazwe klienta, e-mail, telefon
|
||||
And wyniki sa ograniczone do max 10
|
||||
And jesli brak wynikow, wyswietla "Brak wynikow"
|
||||
```
|
||||
|
||||
## AC-4: Nawigacja do zamowienia
|
||||
```gherkin
|
||||
Given dropdown z wynikami jest widoczny
|
||||
When uzytkownik klika na wynik
|
||||
Then zostaje przeniesiony do /orders/{id} (strona szczegulow zamowienia)
|
||||
```
|
||||
|
||||
## AC-5: Nawigacja klawiaturowa
|
||||
```gherkin
|
||||
Given dropdown z wynikami jest widoczny
|
||||
When uzytkownik uzywa strzalek gora/dol
|
||||
Then podswietlony wynik sie zmienia
|
||||
When uzytkownik naciska Enter na podswietlonym wyniku
|
||||
Then zostaje przeniesiony do /orders/{id}
|
||||
When uzytkownik naciska Escape
|
||||
Then dropdown sie zamyka
|
||||
```
|
||||
|
||||
## AC-6: Zamykanie dropdowna
|
||||
```gherkin
|
||||
Given dropdown z wynikami jest widoczny
|
||||
When uzytkownik klika poza polem wyszukiwania i dropdownem
|
||||
Then dropdown sie zamyka
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Backend — endpoint quickSearch</name>
|
||||
<files>src/Modules/Orders/OrdersRepository.php, src/Modules/Orders/OrdersController.php, routes/web.php</files>
|
||||
<action>
|
||||
1. W `OrdersRepository.php` dodac metode `quickSearch(string $query, int $limit = 10): array`:
|
||||
- SQL: SELECT o.id, o.source_order_id, o.external_order_id, a.name AS buyer_name, a.email AS buyer_email, a.phone AS buyer_phone
|
||||
- FROM orders o LEFT JOIN order_addresses a ON a.order_id = o.id AND a.address_type = 'customer'
|
||||
- WHERE (o.source_order_id LIKE :s OR o.external_order_id LIKE :s OR a.name LIKE :s OR a.email LIKE :s OR a.phone LIKE :s OR EXISTS (SELECT 1 FROM order_items oi WHERE oi.order_id = o.id AND oi.original_name LIKE :s))
|
||||
- ORDER BY o.ordered_at DESC LIMIT :limit
|
||||
- Uzyc prepared statements (Medoo raw lub PDO)
|
||||
- Zwracac tablice z id, order_number (source_order_id lub external_order_id), buyer_name, buyer_email, buyer_phone
|
||||
|
||||
2. W `OrdersController.php` dodac metode `quickSearch(Request $request): Response`:
|
||||
- Pobrac q = $request->input('q', '') — jesli pusty lub < 2 znaki, zwrocic pusty JSON []
|
||||
- Pobrac limit = min((int)$request->input('limit', 10), 20)
|
||||
- Wywolac $this->orders->quickSearch($q, $limit)
|
||||
- Zwrocic Response::json(['results' => $results])
|
||||
- Sprawdzic header X-Requested-With = XMLHttpRequest (opcjonalne, ale dobre praktyki)
|
||||
|
||||
3. W `routes/web.php` dodac route:
|
||||
- $router->get('/api/orders/search', [$ordersController, 'quickSearch'], [$authMiddleware]);
|
||||
- Dodac po istniejacych order routes (po linii ~413)
|
||||
</action>
|
||||
<verify>
|
||||
Recznie wywolac GET /api/orders/search?q=test z przegladarki (zalogowany) — odpowiedz JSON z results array
|
||||
</verify>
|
||||
<done>AC-2 (backend), AC-3 (dane wynikow) spelnione</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Frontend — search bar w topbarze + JS + SCSS</name>
|
||||
<files>resources/views/layouts/app.php, public/assets/js/modules/global-search.js, resources/scss/components/_global-search.scss, resources/scss/app.scss</files>
|
||||
<action>
|
||||
1. W `resources/views/layouts/app.php` w topbarze (miedzy hamburgerem a sekcja uzytkownika):
|
||||
- Dodac div.global-search z:
|
||||
- input type="text" z id="js-global-search" placeholder="Szukaj zamowien..." autocomplete="off"
|
||||
- div.global-search__results (pusty, do wynikow)
|
||||
- Na dole strony (przed zamknieciem body) dodac script include dla global-search.js
|
||||
|
||||
2. Stworzyc `public/assets/js/modules/global-search.js`:
|
||||
- IIFE, 'use strict'
|
||||
- Debounce 300ms na input event
|
||||
- Min. 2 znaki do wyszukiwania
|
||||
- Fetch GET /api/orders/search?q=...&limit=10 z header X-Requested-With: XMLHttpRequest
|
||||
- Renderowanie wynikow w .global-search__results:
|
||||
- Kazdy wynik: link <a href="/orders/{id}"> z numerem zamowienia, nazwiskiem, emailem, telefonem
|
||||
- Jesli brak wynikow: div "Brak wynikow"
|
||||
- Nawigacja klawiaturowa: ArrowDown/ArrowUp zmienia podswietlenie, Enter nawiguje, Escape zamyka
|
||||
- Klik poza komponentem zamyka dropdown (document click listener)
|
||||
- Przy pustym/krotkim query — zamknij dropdown
|
||||
|
||||
3. Stworzyc `resources/scss/components/_global-search.scss`:
|
||||
- .global-search: flex: 1, max-width: 500px, position: relative, margin: 0 1rem
|
||||
- .global-search input: szerokosc 100%, border-radius, padding, font-size
|
||||
- .global-search__results: position absolute, top 100%, left 0, width 100%, background white, border, border-radius, box-shadow, max-height 400px, overflow-y auto, z-index 1000
|
||||
- .global-search__item: padding, cursor pointer, border-bottom, hover/active background
|
||||
- .global-search__item.is-highlighted: background podswietlenia
|
||||
- .global-search__item-title: font-weight bold (numer zamowienia)
|
||||
- .global-search__item-details: font-size mniejszy, color szary (nazwisko, email, telefon)
|
||||
- .global-search__empty: padding, text-align center, color szary
|
||||
- Responsywnosc: na mobile input moze byc mniejszy lub ikona lupa rozwijajaca pole
|
||||
|
||||
4. W `resources/scss/app.scss` dodac @import 'components/global-search'
|
||||
|
||||
5. Zbudowac SCSS do CSS (jesli istnieje build command)
|
||||
</action>
|
||||
<verify>
|
||||
1. Otworz dowolna strone orderPRO — pole wyszukiwania widoczne w topbarze
|
||||
2. Wpisz nazwe klienta — po 300ms dropdown z wynikami
|
||||
3. Kliknij wynik — przeniesienie do /orders/{id}
|
||||
4. Strzalki gora/dol + Enter — nawigacja klawiaturowa
|
||||
5. Escape lub klik poza — zamkniecie dropdowna
|
||||
</verify>
|
||||
<done>AC-1, AC-2 (frontend), AC-3, AC-4, AC-5, AC-6 spelnione</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- src/Modules/Orders/OrdersRepository.php — istniejaca metoda paginate() i buildPaginateFilters() (nie modyfikowac)
|
||||
- resources/views/orders/list.php — istniejaca wyszukiwarka na liscie zamowien
|
||||
- public/assets/js/modules/inline-status-change.js
|
||||
- database/migrations/*
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Wyszukiwanie TYLKO zamowien (nie produktow — nie ma jeszcze modulu produktow w orderPRO)
|
||||
- Brak historii wyszukiwan
|
||||
- Brak cache wynikow
|
||||
- Nie zmieniac istniejacego wyszukiwania na liscie zamowien
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] GET /api/orders/search?q=test zwraca JSON z wynikami
|
||||
- [ ] Pole wyszukiwania widoczne w topbarze na kazdej stronie
|
||||
- [ ] Dropdown wynikow pojawia sie po wpisaniu 2+ znakow
|
||||
- [ ] Klikniecie wyniku przenosi do /orders/{id}
|
||||
- [ ] Nawigacja klawiaturowa (strzalki, Enter, Escape) dziala
|
||||
- [ ] Klik poza zamyka dropdown
|
||||
- [ ] Na mobile pole wyszukiwania nie psuje layoutu topbara
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Oba taski ukonczone
|
||||
- Wszystkie verification checks przechodzą
|
||||
- Brak regresji w istniejacym UI
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/81-global-search-bar/81-01-SUMMARY.md`
|
||||
</output>
|
||||
98
.paul/phases/81-global-search-bar/81-01-SUMMARY.md
Normal file
98
.paul/phases/81-global-search-bar/81-01-SUMMARY.md
Normal file
@@ -0,0 +1,98 @@
|
||||
---
|
||||
phase: 81-global-search-bar
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [javascript, ajax, search, orders, topbar]
|
||||
|
||||
requires:
|
||||
- phase: 44-inline-status-change
|
||||
provides: orders list infrastructure
|
||||
provides:
|
||||
- global search bar in topbar
|
||||
- GET /api/orders/search endpoint
|
||||
- keyboard-navigable search dropdown
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [debounced AJAX search with dropdown results]
|
||||
|
||||
key-files:
|
||||
created: [public/assets/js/modules/global-search.js, resources/scss/modules/_global-search.scss]
|
||||
modified: [src/Modules/Orders/OrdersRepository.php, src/Modules/Orders/OrdersController.php, routes/web.php, resources/views/layouts/app.php, resources/scss/app.scss, public/assets/css/app.css]
|
||||
|
||||
key-decisions: []
|
||||
|
||||
patterns-established:
|
||||
- "Global search: debounced AJAX fetch to /api/orders/search with dropdown rendering"
|
||||
|
||||
duration: 5min
|
||||
started: 2026-04-07T00:00:00Z
|
||||
completed: 2026-04-07T00:00:00Z
|
||||
---
|
||||
|
||||
# Phase 81 Plan 01: Global Search Bar Summary
|
||||
|
||||
**Globalna wyszukiwarka zamowien w topbarze — AJAX search z debounce, dropdown wynikow, nawigacja klawiaturowa, przeszukiwanie po numerze/nazwisku/email/telefonie/produkcie.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | 5min |
|
||||
| Tasks | 2 completed |
|
||||
| Files modified | 8 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Pole wyszukiwania w topbarze | Pass | Input miedzy hamburgerem a user info |
|
||||
| AC-2: Wyszukiwanie AJAX z debounce | Pass | 300ms debounce, min 2 znaki |
|
||||
| AC-3: Wyniki wyszukiwania | Pass | Numer, nazwisko, email, telefon; max 10; "Brak wynikow" |
|
||||
| AC-4: Nawigacja do zamowienia | Pass | Klik na wynik → /orders/{id} |
|
||||
| AC-5: Nawigacja klawiaturowa | Pass | ArrowUp/Down, Enter, Escape |
|
||||
| AC-6: Zamykanie dropdowna | Pass | Klik poza zamyka |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Nowy endpoint `GET /api/orders/search?q=...&limit=10` z prepared statements
|
||||
- Metoda `quickSearch()` w OrdersRepository szuka po 6 polach (source_order_id, external_order_id, name, email, phone, product name)
|
||||
- Modul JS global-search.js z debounce, dropdown, nawigacja klawiaturowa
|
||||
- Responsywne style SCSS
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Orders/OrdersRepository.php` | Modified | Nowa metoda `quickSearch()` |
|
||||
| `src/Modules/Orders/OrdersController.php` | Modified | Nowa metoda `quickSearch()` |
|
||||
| `routes/web.php` | Modified | Route GET /api/orders/search |
|
||||
| `resources/views/layouts/app.php` | Modified | Pole wyszukiwania w topbarze + script include |
|
||||
| `public/assets/js/modules/global-search.js` | Created | Modul JS (debounce, dropdown, klawiatura) |
|
||||
| `resources/scss/modules/_global-search.scss` | Created | Style komponentu wyszukiwarki |
|
||||
| `resources/scss/app.scss` | Modified | Import nowego modulu SCSS |
|
||||
| `public/assets/css/app.css` | Modified | Zbudowany CSS |
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Wyszukiwarka globalna dziala na kazdej stronie
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 81-global-search-bar, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
111
.paul/phases/82-product-title-tooltip/82-01-PLAN.md
Normal file
111
.paul/phases/82-product-title-tooltip/82-01-PLAN.md
Normal file
@@ -0,0 +1,111 @@
|
||||
---
|
||||
phase: 82-product-title-tooltip
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified: [src/Modules/Orders/OrdersController.php]
|
||||
autonomous: true
|
||||
---
|
||||
|
||||
<objective>
|
||||
## Goal
|
||||
Dodanie natywnego tooltipa (atrybut `title`) do uciętych nazw produktów na liście zamówień, aby po najechaniu myszką wyświetlała się pełna nazwa.
|
||||
|
||||
## Purpose
|
||||
Na liście zamówień nazwy produktów są obcinane przez `text-overflow: ellipsis`. Użytkownik nie ma możliwości zobaczenia pełnej nazwy bez wchodzenia w szczegóły zamówienia. Tooltip rozwiązuje ten problem minimalnym kosztem.
|
||||
|
||||
## Output
|
||||
Zmodyfikowany `OrdersController::productsHtml()` — atrybut `title` na elemencie `.orders-product__name`.
|
||||
</objective>
|
||||
|
||||
<context>
|
||||
## Project Context
|
||||
@.paul/PROJECT.md
|
||||
@.paul/ROADMAP.md
|
||||
@.paul/STATE.md
|
||||
|
||||
## Source Files
|
||||
@src/Modules/Orders/OrdersController.php (metoda productsHtml(), linia 664)
|
||||
</context>
|
||||
|
||||
<skills>
|
||||
No specialized flows required.
|
||||
</skills>
|
||||
|
||||
<acceptance_criteria>
|
||||
|
||||
## AC-1: Tooltip z pełną nazwą produktu
|
||||
```gherkin
|
||||
Given lista zamówień z produktem o długiej nazwie uciętej przez ellipsis
|
||||
When użytkownik najedzie kursorem na uciętą nazwę produktu
|
||||
Then pojawia się natywny tooltip przeglądarki z pełną nazwą produktu
|
||||
```
|
||||
|
||||
## AC-2: Brak tooltipa dla pustych nazw
|
||||
```gherkin
|
||||
Given produkt bez nazwy (wyświetlany jako "-")
|
||||
When użytkownik najedzie kursorem na "-"
|
||||
Then nie pojawia się tooltip (brak atrybutu title lub pusty)
|
||||
```
|
||||
|
||||
</acceptance_criteria>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Dodanie atrybutu title do .orders-product__name</name>
|
||||
<files>src/Modules/Orders/OrdersController.php</files>
|
||||
<action>
|
||||
W metodzie `productsHtml()`, linia 664, dodać atrybut `title` do `<div class="orders-product__name">`:
|
||||
|
||||
Zmienić:
|
||||
```php
|
||||
. '<div class="orders-product__name">' . htmlspecialchars($name !== '' ? $name : '-', ENT_QUOTES, 'UTF-8') . '</div>'
|
||||
```
|
||||
|
||||
Na:
|
||||
```php
|
||||
. '<div class="orders-product__name"' . ($name !== '' ? ' title="' . htmlspecialchars($name, ENT_QUOTES, 'UTF-8') . '"' : '') . '>' . htmlspecialchars($name !== '' ? $name : '-', ENT_QUOTES, 'UTF-8') . '</div>'
|
||||
```
|
||||
|
||||
- Atrybut `title` dodawany tylko gdy nazwa nie jest pusta
|
||||
- Wartość `title` escapowana przez `htmlspecialchars` (XSS safety)
|
||||
- Nie zmieniać żadnej innej logiki metody
|
||||
</action>
|
||||
<verify>Otworzyć /orders/list, najechać na uciętą nazwę produktu — powinien pojawić się tooltip z pełną nazwą</verify>
|
||||
<done>AC-1 i AC-2 spełnione: tooltip pokazuje pełną nazwę; brak tooltipa dla pustych nazw</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<boundaries>
|
||||
|
||||
## DO NOT CHANGE
|
||||
- resources/scss/* (styl truncacji zostaje bez zmian)
|
||||
- resources/views/orders/* (widoki bez zmian)
|
||||
- Logika budowania `$itemsPreview` i reszta metody `productsHtml()`
|
||||
|
||||
## SCOPE LIMITS
|
||||
- Tylko natywny tooltip HTML (`title`), bez custom JS tooltip library
|
||||
- Tylko lista zamówień — nie strona szczegółów
|
||||
|
||||
</boundaries>
|
||||
|
||||
<verification>
|
||||
Before declaring plan complete:
|
||||
- [ ] Atrybut `title` obecny w renderowanym HTML produktów z długą nazwą
|
||||
- [ ] Brak atrybutu `title` dla produktów bez nazwy
|
||||
- [ ] Brak regresji — lista zamówień renderuje się poprawnie
|
||||
- [ ] Wszystkie acceptance criteria spełnione
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
- Tooltip z pełną nazwą produktu widoczny po hover na liście zamówień
|
||||
- Brak zmian CSS ani JS
|
||||
- Brak regresji w renderowaniu listy
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.paul/phases/82-product-title-tooltip/82-01-SUMMARY.md`
|
||||
</output>
|
||||
84
.paul/phases/82-product-title-tooltip/82-01-SUMMARY.md
Normal file
84
.paul/phases/82-product-title-tooltip/82-01-SUMMARY.md
Normal file
@@ -0,0 +1,84 @@
|
||||
---
|
||||
phase: 82-product-title-tooltip
|
||||
plan: 01
|
||||
subsystem: ui
|
||||
tags: [tooltip, orders-list, ux]
|
||||
|
||||
requires: []
|
||||
provides:
|
||||
- Natywny tooltip z pelna nazwa produktu na liscie zamowien
|
||||
affects: []
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: []
|
||||
|
||||
key-files:
|
||||
created: []
|
||||
modified: [src/Modules/Orders/OrdersController.php]
|
||||
|
||||
key-decisions: []
|
||||
|
||||
patterns-established: []
|
||||
|
||||
duration: 2min
|
||||
started: 2026-04-07T00:00:00Z
|
||||
completed: 2026-04-07T00:00:00Z
|
||||
---
|
||||
|
||||
# Phase 82 Plan 01: Product Title Tooltip Summary
|
||||
|
||||
**Dodano atrybut `title` do elementu `.orders-product__name` w metodzie `productsHtml()` — natywny tooltip przegladarki z pelna nazwa produktu na liscie zamowien.**
|
||||
|
||||
## Performance
|
||||
|
||||
| Metric | Value |
|
||||
|--------|-------|
|
||||
| Duration | ~2min |
|
||||
| Tasks | 1 completed |
|
||||
| Files modified | 1 |
|
||||
|
||||
## Acceptance Criteria Results
|
||||
|
||||
| Criterion | Status | Notes |
|
||||
|-----------|--------|-------|
|
||||
| AC-1: Tooltip z pelna nazwa produktu | Pass | Atrybut `title` dodany z escapowana nazwa |
|
||||
| AC-2: Brak tooltipa dla pustych nazw | Pass | Warunek `$name !== ''` pomija pusty title |
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- Dodano atrybut `title` do `<div class="orders-product__name">` z pelna nazwa produktu (XSS-safe przez `htmlspecialchars`)
|
||||
- Tooltip pojawia sie tylko gdy nazwa nie jest pusta
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
| File | Change | Purpose |
|
||||
|------|--------|---------|
|
||||
| `src/Modules/Orders/OrdersController.php` | Modified | Dodano atrybut `title` w metodzie `productsHtml()` linia 664 |
|
||||
|
||||
## Decisions Made
|
||||
|
||||
None - followed plan as specified
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None - plan executed exactly as written
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
None
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
**Ready:**
|
||||
- Tooltip dziala natywnie, zero zaleznosci JS/CSS
|
||||
|
||||
**Concerns:**
|
||||
- None
|
||||
|
||||
**Blockers:**
|
||||
- None
|
||||
|
||||
---
|
||||
*Phase: 82-product-title-tooltip, Plan: 01*
|
||||
*Completed: 2026-04-07*
|
||||
Reference in New Issue
Block a user