Files
adsPRO/.paul/phases/02-supplemental-feed-cl1/02-01-PLAN.md
2026-04-22 10:17:26 +02:00

7.8 KiB

phase, plan, type, wave, depends_on, files_modified, autonomous, delegation
phase plan type wave depends_on files_modified autonomous delegation
02-supplemental-feed-cl1 01 execute 1
autoload/services/class.SupplementalFeed.php
true off
## Goal Dodać kolumnę `custom_label_1` do generowanego supplemental feeda TSV (np. `feeds/supplemental_9.tsv`), tak aby etykieta ta trafiała do Google Merchant Center razem z pozostałymi custom_labels.

Purpose

Plan 01-01 (Phase 1) wprowadził kolumnę custom_label_1 w tabeli /products i w bazie danych (migracja 028), ale services\SupplementalFeed::generate_for_client() nie został zaktualizowany — feed TSV nadal eksportuje wyłącznie custom_label_3 i custom_label_4. Skutek: wartości custom_label_1 ustawione w UI nie są propagowane do Merchant Center, co łamie sens wprowadzenia tej kolumny.

Output

Zaktualizowany autoload/services/class.SupplementalFeed.php — feed feeds/supplemental_{client_id}.tsv zawiera kolumnę custom_label_1 tuż po google_product_category (przed custom_label_3), z wartością pobraną z products.custom_label_1.

## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md

Prior Work

@.paul/phases/01-products-cl1-column/01-01-SUMMARY.md

Phase 1 dodał products.custom_label_1 (migracja 028) i UI. Ten plan domyka lukę w feedzie TSV.

Source Files

@autoload/services/class.SupplementalFeed.php @migrations/028_products_custom_label_1.sql

<acceptance_criteria>

AC-1: Nagłówek TSV zawiera custom_label_1

Given klient o id = 9 ma wygenerowany feed przez SupplementalFeed::generate_for_client( 9 )
When otworzę pierwszy wiersz pliku feeds/supplemental_9.tsv
Then kolumny rozdzielone TAB to dokładnie: id, title, description, google_product_category, custom_label_1, custom_label_3, custom_label_4

AC-2: Wartość custom_label_1 trafia do wierszy

Given produkt klienta 9 ma w DB products.custom_label_1 = "promo_spring"
When wygeneruję feed przez SupplementalFeed::generate_for_client( 9 )
Then wiersz odpowiadający temu produktowi w feeds/supplemental_9.tsv ma w 5. kolumnie wartość "promo_spring"

AC-3: Pusty custom_label_1 nie wymusza eksportu produktu ani nie psuje pustej logiki

Given produkt ma wszystkie kolumny (title, description, google_product_category, custom_label_1, custom_label_3, custom_label_4) puste
When wygeneruję feed
Then ten produkt zostaje pominięty (nie pojawia się w TSV), zgodnie z dotychczasowym zachowaniem
And produkty, które mają tylko custom_label_1 ustawione (reszta pusta), są eksportowane z custom_label_1 wypełnionym a pozostałymi kolumnami pustymi

AC-4: Brak regresji dla custom_label_3 / custom_label_4 / bestseller

Given istniejąca logika refresh_bestseller_labels_for_client() nadal działa i ustawia products.custom_label_4
When wygeneruję feed
Then custom_label_4 nadal jest w 7. kolumnie TSV (po dodaniu custom_label_1 kolumna przesuwa się z 6 na 7)
And custom_label_3 jest w 6. kolumnie TSV
And zwracane statystyki (products_total, products_written, file, labels_updated) zachowują dotychczasową strukturę

</acceptance_criteria>

Task 1: Dodaj custom_label_1 do SELECT, nagłówka, warunku pustki i wiersza TSV autoload/services/class.SupplementalFeed.php W metodzie `generate_for_client( $client_id )`:
1. Rozszerz SELECT (ok. linia 166) o kolumnę `p.custom_label_1`:
   ```sql
   SELECT p.offer_id, p.title, p.description, p.google_product_category,
          p.custom_label_1, p.custom_label_3, p.custom_label_4
   ```

2. Rozszerz warunek WHERE o `p.custom_label_1 IS NOT NULL`:
   ```sql
   AND ( p.title IS NOT NULL
         OR p.description IS NOT NULL
         OR p.google_product_category IS NOT NULL
         OR p.custom_label_1 IS NOT NULL
         OR p.custom_label_3 IS NOT NULL
         OR p.custom_label_4 IS NOT NULL )
   ```

3. Nagłówek TSV (ok. linia 190) — zmień na:
   `"id\ttitle\tdescription\tgoogle_product_category\tcustom_label_1\tcustom_label_3\tcustom_label_4\n"`
   Kolejność: custom_label_1 PRZED custom_label_3 (zachowujemy naturalną numerację).

4. Pętla `foreach`:
   - Dodaj `$custom_label_1 = trim( (string) ( $row['custom_label_1'] ?? '' ) );`
   - Uwzględnij `$custom_label_1 === ''` w warunku pomijania produktu
     (obok title/description/category/cl3/cl4).
   - Dodaj `$custom_label_1` do tablicy przekazywanej do `implode( "\t", [...] )` — pozycja PRZED `$custom_label_3`.

Unikaj:
- zmiany kolejności pozostałych kolumn (tylko DODAJEMY cl_1 w naturalnym miejscu)
- dotykania logiki `refresh_bestseller_labels_for_client()` ani `is_below_exit_for_cooldown()` (poza tym planem)
- zmiany sanitize_for_tsv / normalize_feed_offer_id
1. Lokalnie (lub po wgraniu na serwer) uruchom PHP: `php -r "require 'index.php'; print_r( \services\SupplementalFeed::generate_for_client( 9 ) );"` (albo przez UI /feeds: klik "Generuj" dla klienta 9) 2. `head -1 feeds/supplemental_9.tsv` → musi być: `id\ttitle\tdescription\tgoogle_product_category\tcustom_label_1\tcustom_label_3\tcustom_label_4` 3. Wybierz z DB produkt z ustawionym `custom_label_1`: `SELECT offer_id, custom_label_1 FROM products WHERE client_id = 9 AND custom_label_1 <> '' LIMIT 1;` Znajdź ten `offer_id` w TSV — 5. kolumna musi zawierać wartość z DB. 4. Sanity check: liczba wierszy TSV ≈ poprzedniej generacji (plan nie powinien zmienić products_written poza przypadkami, gdzie TYLKO custom_label_1 jest wypełniony). AC-1, AC-2, AC-3, AC-4 spełnione: feed dla klienta 9 zawiera custom_label_1 w nagłówku i wierszach, pozostałe kolumny bez regresji.

DO NOT CHANGE

  • refresh_bestseller_labels_for_client(), is_below_exit_for_cooldown(), get_client_bestseller_settings() — logika bestsellera jest poza scope.
  • sanitize_for_tsv(), normalize_feed_offer_id() — bez zmian.
  • migrations/028_products_custom_label_1.sql — migracja już wdrożona w Phase 1.
  • autoload/controls/class.Products.php, templates/products/main_view.php — UI produktów jest poza scope.
  • Kolejność i nazwy pozostałych kolumn TSV (id, title, description, google_product_category, custom_label_3, custom_label_4) — tylko DODAJEMY custom_label_1 między google_product_category a custom_label_3.

SCOPE LIMITS

  • Plan dotyczy WYŁĄCZNIE pliku autoload/services/class.SupplementalFeed.php.
  • Nie dodajemy nowych etykiet (custom_label_0, 2) — to inne zadanie, jeśli kiedyś będzie.
  • Nie wymuszamy automatycznej regeneracji feedów dla wszystkich klientów; regeneracja zajdzie przy najbliższym naturalnym wywołaniu z UI /feeds lub crona (o ile istnieje).
  • Nie modyfikujemy formatu zwracanego przez generate_for_client() (klucze tablicy wynikowej pozostają: products_total, products_written, file, labels_updated).
Przed zamknięciem planu: - [ ] `php -l autoload/services/class.SupplementalFeed.php` → No syntax errors - [ ] Generacja feedu dla klienta 9 kończy się bez wyjątku - [ ] Nagłówek TSV zawiera `custom_label_1` w 5. kolumnie - [ ] Przynajmniej jeden wiersz TSV ma wypełnione `custom_label_1` (produkt z ustawioną etykietą w DB) - [ ] Wszystkie AC spełnione

<success_criteria>

  • Zmodyfikowany jeden plik: autoload/services/class.SupplementalFeed.php
  • Feed feeds/supplemental_9.tsv zawiera kolumnę custom_label_1 z poprawnymi wartościami
  • Brak błędów PHP (lint + runtime)
  • Brak regresji w dotychczasowych kolumnach / statystykach zwrotnych </success_criteria>
Po ukończeniu utwórz `.paul/phases/02-supplemental-feed-cl1/02-01-SUMMARY.md`.