165 lines
6.7 KiB
Markdown
165 lines
6.7 KiB
Markdown
---
|
|
phase: 63-order-item-personalization
|
|
plan: 01
|
|
type: execute
|
|
wave: 1
|
|
depends_on: []
|
|
files_modified:
|
|
- database/migrations/20260401_000063_add_personalization_to_order_items.sql
|
|
- src/Modules/Settings/ShopproOrderMapper.php
|
|
- src/Modules/Orders/OrderImportRepository.php
|
|
- resources/views/orders/show.php
|
|
- resources/scss/orders/_show.scss
|
|
autonomous: true
|
|
---
|
|
|
|
<objective>
|
|
## Goal
|
|
Pobieranie danych personalizacji produktów z shopPRO i wyświetlanie ich w szczegółach zamówienia orderPRO.
|
|
|
|
## Purpose
|
|
Sprzedawcy widzą personalizacje klientów (np. tekst grawerunku, link Spotify, dedykacja) bezpośrednio w orderPRO bez konieczności logowania do shopPRO. Kluczowe dla realizacji zamówień z personalizowanymi produktami.
|
|
|
|
## Output
|
|
- Migracja DB: kolumna `personalization` w `order_items`
|
|
- Mapper: ekstrakcja `custom_fields` z odpowiedzi shopPRO API
|
|
- UI: wyświetlanie personalizacji pod nazwą produktu w widoku zamówienia
|
|
</objective>
|
|
|
|
<context>
|
|
## Project Context
|
|
@.paul/PROJECT.md
|
|
@.paul/ROADMAP.md
|
|
@.paul/STATE.md
|
|
|
|
## Source Files
|
|
@src/Modules/Settings/ShopproOrderMapper.php — mapItems() linia 527, mapuje produkty z API shopPRO
|
|
@src/Modules/Orders/OrderImportRepository.php — replaceItems() wstawia pozycje do DB
|
|
@resources/views/orders/show.php — widok szczegółów zamówienia, sekcja produktów
|
|
@database/migrations/20260302_000018_create_orders_tables_and_schedule.sql — schemat order_items
|
|
</context>
|
|
|
|
<skills>
|
|
No specialized flows configured.
|
|
</skills>
|
|
|
|
<acceptance_criteria>
|
|
|
|
## AC-1: Personalizacja zapisana w bazie danych
|
|
```gherkin
|
|
Given zamówienie z shopPRO zawiera produkty z custom_fields
|
|
When import zamówienia się wykonuje (cron lub ręczny)
|
|
Then kolumna personalization w order_items zawiera dane personalizacji jako czysty tekst (bez HTML)
|
|
```
|
|
|
|
## AC-2: Personalizacja wyświetlana w szczegółach zamówienia
|
|
```gherkin
|
|
Given zamówienie w orderPRO ma pozycje z wypełnioną personalizacją
|
|
When użytkownik otwiera szczegóły zamówienia (/orders/{id})
|
|
Then pod nazwą produktu wyświetla się sekcja personalizacji z etykietami i wartościami
|
|
```
|
|
|
|
## AC-3: Brak personalizacji nie powoduje błędów
|
|
```gherkin
|
|
Given zamówienie z shopPRO zawiera produkty BEZ custom_fields (puste lub null)
|
|
When import się wykonuje i użytkownik otwiera szczegóły
|
|
Then kolumna personalization jest NULL i w UI nie pojawia się sekcja personalizacji
|
|
```
|
|
|
|
</acceptance_criteria>
|
|
|
|
<tasks>
|
|
|
|
<task type="auto">
|
|
<name>Task 1: Migracja DB + mapping personalizacji</name>
|
|
<files>database/migrations/20260401_000063_add_personalization_to_order_items.sql, src/Modules/Settings/ShopproOrderMapper.php, src/Modules/Orders/OrderImportRepository.php</files>
|
|
<action>
|
|
1. Utworzyć migrację dodającą kolumnę `personalization TEXT NULL` do tabeli `order_items` (po kolumnie `payload_json`).
|
|
|
|
2. W `ShopproOrderMapper::mapItems()` (linia 561-580):
|
|
- Wyciągnąć `custom_fields` z `$row` za pomocą `$this->readPath($row, ['custom_fields'])`
|
|
- Przekonwertować HTML na czysty tekst: zamienić `<br>` na `\n`, usunąć tagi HTML (`strip_tags`), `html_entity_decode`, `trim`
|
|
- Zapisać jako klucz `personalization` w tablicy wynikowej (obok `payload_json`)
|
|
- Jeśli wynik jest pustym stringiem, ustawić `null`
|
|
|
|
3. W `OrderImportRepository::replaceItems()`:
|
|
- Dodać kolumnę `personalization` do INSERT query
|
|
- Bindować wartość z tablicy item
|
|
|
|
Avoid: Nie zmieniać formatu `payload_json` — surowe dane API muszą zostać nienaruszone.
|
|
Avoid: Nie parsować HTML do JSON — czysty tekst z zachowanymi newlines jest wystarczający.
|
|
</action>
|
|
<verify>
|
|
- Migracja wykonuje się bez błędów: `php database/migrate.php`
|
|
- Po re-imporcie zamówienia z shopPRO kolumna `personalization` zawiera tekst
|
|
- Zamówienia bez personalizacji mają NULL w kolumnie
|
|
</verify>
|
|
<done>AC-1 i AC-3 satisfied: personalizacja zapisywana w DB, brak personalizacji = NULL</done>
|
|
</task>
|
|
|
|
<task type="auto">
|
|
<name>Task 2: Wyświetlanie personalizacji w widoku zamówienia</name>
|
|
<files>resources/views/orders/show.php, resources/scss/orders/_show.scss</files>
|
|
<action>
|
|
1. W `resources/views/orders/show.php`, w sekcji renderującej produkty zamówienia:
|
|
- Pod nazwą produktu (`original_name`) dodać blok warunkowy: jeśli `$item['personalization']` nie jest puste
|
|
- Wyświetlić personalizację w `<div class="item-personalization">` z ikoną lub etykietą "Personalizacja:"
|
|
- Każda linia personalizacji (split po `\n`) jako osobna linia w UI
|
|
- Escape HTML: użyć `e()` helpera na każdej linii
|
|
|
|
2. W SCSS dodać style:
|
|
- `.item-personalization` — mały font (0.85em), kolor muted, lekki padding-top
|
|
- Etykiety (tekst przed `:`) mogą być pogrubione via CSS lub pozostawione jako plain text
|
|
- Kompaktowy układ, bez nadmiernych marginesów
|
|
|
|
Avoid: Nie renderować surowego HTML z custom_fields — zawsze escape.
|
|
Avoid: Nie dodawać nowych zależności JS — to statyczny tekst.
|
|
</action>
|
|
<verify>
|
|
- Otworzyć zamówienie z personalizacją w przeglądarce — personalizacja widoczna pod nazwą produktu
|
|
- Otworzyć zamówienie bez personalizacji — brak dodatkowej sekcji
|
|
- Sprawdzić XSS: wpisać `<script>alert(1)</script>` w custom_fields shopPRO — powinno być escaped
|
|
</verify>
|
|
<done>AC-2 satisfied: personalizacja wyświetlana w UI pod nazwą produktu</done>
|
|
</task>
|
|
|
|
</tasks>
|
|
|
|
<boundaries>
|
|
|
|
## DO NOT CHANGE
|
|
- shopPRO codebase (API już zwraca custom_fields — zero zmian po stronie shopPRO)
|
|
- payload_json format i zawartość w order_items
|
|
- Istniejące kolumny i indeksy order_items
|
|
- Logika importu zamówień poza mapowaniem items (OrderImportRepository::upsertOrderAggregate flow)
|
|
|
|
## SCOPE LIMITS
|
|
- Tylko personalizacja z shopPRO (nie Allegro, nie Erli)
|
|
- Tylko widok szczegółów zamówienia — bez zmian na liście zamówień
|
|
- Bez edycji personalizacji w orderPRO — read-only display
|
|
- Bez parsowania personalizacji do struktury klucz-wartość (JSON) — plain text wystarczy
|
|
|
|
</boundaries>
|
|
|
|
<verification>
|
|
Before declaring plan complete:
|
|
- [ ] Migracja wykonuje się bez błędów
|
|
- [ ] SCSS kompiluje się bez błędów
|
|
- [ ] Zamówienie z personalizacją — dane widoczne w UI
|
|
- [ ] Zamówienie bez personalizacji — brak sekcji w UI
|
|
- [ ] Brak regresji w istniejącym wyświetlaniu produktów
|
|
- [ ] XSS escape działa poprawnie
|
|
- [ ] Aktualizacja DOCS/DB_SCHEMA.md i DOCS/ARCHITECTURE.md
|
|
</verification>
|
|
|
|
<success_criteria>
|
|
- Wszystkie taski zakończone
|
|
- Wszystkie weryfikacje przechodzą
|
|
- Brak błędów PHP/SCSS
|
|
- Personalizacja widoczna w UI dla produktów z custom_fields
|
|
</success_criteria>
|
|
|
|
<output>
|
|
After completion, create `.paul/phases/63-order-item-personalization/63-01-SUMMARY.md`
|
|
</output>
|