--- phase: 05-products-scope-history-delete plan: 01 subsystem: ui tags: [products, datatables, breakdown, delete, history, php, jquery, transactions] requires: - phase: 04-products-aggregate-breakdown provides: rozwijane podwiersze breakdown per kampania+grupa z payloadem row_meta.breakdown_rows provides: - Lokalne usuwanie wpisow statystyczno-historycznych per trojka product_id+campaign_id+ad_group_id z UI breakdown - Endpoint AJAX `/products/delete_product_scope_history/` - Factory method `\factory\Products::delete_product_scope_history()` (transakcja PDO) - Kolumna "Akcje" w tabeli rozbicia + dialog `$.confirm` z potwierdzeniem - Odswiezony, schludniejszy styl ikony rozwijania breakdown (CSS rotate, hover, stan open) affects: - autoload/factory/class.Products.php (delete API) - autoload/controls/class.Products.php (payload breakdown_for_view + nowa akcja) - templates/products/main_view.php (kolumna Akcje + handler + style) tech-stack: added: [] patterns: - "Hard delete scope-level: AGGREGATE + HISTORY + HISTORY_30 atomowo w transakcji PDO" - "Breakdown payload eksponuje pelna trojke ID (product, campaign, ad_group) dla operacji per-wiersz" - "Toggle expand stylowany CSS-em (rotate 90deg) zamiast swapu klas FA w JS" key-files: created: [] modified: - autoload/factory/class.Products.php - autoload/controls/class.Products.php - templates/products/main_view.php key-decisions: - "Hard delete lokalny w transakcji - bez Google Ads API, bez ruszania tabeli `products`" - "Eksponowanie product_id/campaign_id/ad_group_id w breakdown_for_view (poza zaplanowana edycja factory)" - "Style toggle: chevron rotowany przez CSS, JS nie podmienia klas FA" patterns-established: - "Operacje per-wiersz w child rows DataTables wymagaja eksponowania ID we payloadzie row_meta - nie polegaj na danych z parent row" - "Transakcja PDO przed serwisem Medoo: `$mdb->pdo->beginTransaction()` + `commit/rollBack` + `$pdo->inTransaction()` guard" duration: ~30min started: 2026-04-29T18:35:00Z completed: 2026-04-29T19:05:00Z --- # Phase 5 Plan 01: Products Scope History Delete (Summary) **Dodano w UI /products na rozwijanym podwierszu breakdown przycisk usuwania, ktory atomowo kasuje wpisy z `products_aggregate`, `products_history` i `products_history_30` dla konkretnej trojki product+campaign+ad_group, bez ruszania tabeli `products` i bez wywolan Google Ads API.** ## Performance | Metric | Value | |--------|-------| | Duration | ~30 min | | Started | 2026-04-29T18:35:00Z | | Completed | 2026-04-29T19:05:00Z | | Tasks | 4/4 (3 auto + 1 human-verify) + 1 unplanned bonus (toggle styling) | | Files modified | 3 | ## Acceptance Criteria Results | Criterion | Status | Notes | |-----------|--------|-------| | AC-1: Endpoint usuwa lokalne wpisy dla trojki product+campaign+ad_group | Pass | Factory uzywa `$mdb->delete()` z dokladnym WHERE per kazda z 3 tabel; transakcja PDO; inne trojki nietkniete (zweryfikowane przez usera) | | AC-2: Walidacja parametrow i bledow | Pass | Controller: `product_id <= 0 -> {status:error,message:...}` przed wywolaniem factory; factory tez waliduje (defense in depth) | | AC-3: UI kolumny Akcje + potwierdzenie | Pass | Nowa kolumna "Akcje" widoczna na koncu breakdown; ikona kosza `fa-solid fa-trash` w `btn btn-sm btn-danger`; dialog `$.confirm` z nazwa kampanii/grupy + przyciski "Usun"/"Anuluj" | | AC-4: UI po sukcesie aktualizuje sie bez utraty stanu | Pass | `$tr.remove()` + `products_table.ajax.reload(null,false)`; toast PL na success; `$.alert` z `res.message` na blad - human-verify approved | ## Accomplishments - Dodano scope-level usuwanie historii produktu z UI breakdown bez ryzyka skasowania samego produktu ani wplywu na inne trojki kampania/grupa. - Atomowa operacja DELETE-z-trzech-tabel w pojedynczej transakcji PDO z guardem `inTransaction()` i `rollBack` na throw. - Naprawiono nieoczywisty bug: breakdown_for_view nie eksponowal ID, przez co handler dostawal `undefined` i nic sie nie dzialo - dolozono trzy pola. - Bonus: ikona rozwijania breakdown przemodelowana na okragly button (hover, fioletowy stan open, CSS rotate chevron) - usunieto JS swap klas FA. ## Task Commits Brak commitu fazowego na tym etapie (working tree zawiera tez `.vscode/ftp-kr.sync.cache.json`, `.serena/project.yml` i `.paul/STATE.md` z innych watkow). ## Files Created/Modified | File | Change | Purpose | |------|--------|---------| | `autoload/factory/class.Products.php` | Modified (+51) | Nowa metoda `delete_product_scope_history($pid,$cid,$agid)` z transakcja PDO i 3-tabelowym DELETE | | `autoload/controls/class.Products.php` | Modified (+23 +3) | Nowa akcja AJAX `delete_product_scope_history()`; payload `breakdown_for_view` rozszerzony o `product_id`, `campaign_id`, `ad_group_id` | | `templates/products/main_view.php` | Modified (+86, ~30 styling) | Kolumna "Akcje" w `products_build_breakdown_html`, handler kliku z `$.confirm`, przebudowane style `.products-breakdown-toggle` (CSS rotate zamiast JS), CSS `td:last-child` szerokosci 56px | ## Decisions Made | Decision | Rationale | Impact | |----------|-----------|--------| | Eksponowac product/campaign/ad_group ID w `breakdown_for_view` | Bez tego JS dostawal `undefined` ID i handler ucinal sie na walidacji `<= 0` | Ustanawia wzorzec: jak chcesz akcje per-wiersz w child row, dodaj ID do payloadu row_meta | | Stylowac chevron rotacja CSS, nie podmiana klas FA w JS | Mniej kodu JS, plynniejsza animacja, jeden zrodlowy stan (`.products-breakdown-open`) | Pojedynczy CSS transition zarzadza wizualem; usunieto 2 linie JS | | Transakcja PDO + guard `inTransaction()` zamiast nested-transaction errora | Inny kod moze otworzyc transakcje wczesniej (np. w cron/migracji) | Bezpieczne uzycie metody w roznych kontekstach wywolania | ## Deviations from Plan ### Summary | Type | Count | Impact | |------|-------|--------| | Auto-fixed | 1 | Niezbedne - bez tej korekty UI nie dziala (bug: undefined ID) | | Scope additions | 1 | Maly bonus stylowania ikony toggle na prosbe usera, w obrebie tego samego pliku | | Deferred | 0 | Brak | **Total impact:** Plan zrealizowany zgodnie z zakresem; jedna konieczna korekta payloadu (Task 2) i jedno male rozszerzenie kosmetyczne na zyczenie usera. ### Auto-fixed Issues **1. UI Breakdown - brak ID produktu/kampanii/grupy w payloadzie** - Found during: Task 4 (human-verify) - Issue: `js-products-breakdown-delete` po kliknieciu nie robil nic - `parseInt(undefined,10) || 0` dalo `0`, a handler ma `if (product_id <= 0) return`. Powod: `breakdown_for_view` w `controls/class.Products.php:1097` mapowal tylko statystyki + nazwy, bez ID. - Fix: Dodano `product_id`, `campaign_id`, `ad_group_id` jako pierwsze pola mappingu (z fallbackami). - Files: `autoload/controls/class.Products.php` - Verification: Czesc human-verify - klik kosza po patchu wywoluje dialog z poprawnymi nazwami kampanii/grupy. - Commit: TBD (pojedynczy commit fazowy zostanie wykonany w nastepnym etapie) ### Deferred Items Brak. ## Issues Encountered | Issue | Resolution | |-------|------------| | Klik w kosz nie wywolywal dialogu (puste dane w `entry.product_id` itd.) | Zlokalizowano: payload breakdown filtrowany w controllerze. Dolozono 3 pola ID i pominieto cache state DataTables (force reload) | ## Next Phase Readiness **Ready:** - Wzorzec dla operacji per-wiersz w breakdown ustanowiony (ID w row_meta). - Wzorzec transakcyjnego DELETE w factory ustanowiony. - Style toggle bardziej spojne wizualnie - mozna wykorzystac wzorzec rotate-on-state w innych collapsible UI. **Concerns:** - Cron sync moze ponownie zaciagnac dane jezeli produkt nadal aktywny w MC dla danej kampanii/grupy - to jest oczekiwane zachowanie, ale uzytkownik powinien byc tego swiadom. - Working tree zawiera zmiany niepowiazane z planem (`.vscode/ftp-kr.sync.cache.json`, `.serena/project.yml`) - przy commit fazowym selektywne stage'owanie. **Blockers:** - Brak. --- *Phase: 05-products-scope-history-delete, Plan: 01* *Completed: 2026-04-29*