--- phase: 11-datalayer-ga4-fix plan: 01 type: execute wave: 1 depends_on: [] files_modified: - templates/shop-order/order-details.php - templates/shop-basket/summary-view.php - templates/shop-product/product.php - templates/shop-basket/basket.php autonomous: true --- ## Goal Naprawic wszystkie eventy dataLayer ecommerce (purchase, begin_checkout, view_item, add_to_cart) do formatu GA4 oraz dodac brakujacy event view_cart. Poprawki krytyczne dla remarketingu dynamicznego i konwersji. ## Purpose Bez tych poprawek remarketing dynamiczny Google Ads i konwersje GA4 nie dzialaja poprawnie — ceny produktow sa zerowe, klucze itemow niezgodne z GA4, brakuje walut i eventow. ## Output Poprawione 4 szablony PHP z prawidlowymi eventami dataLayer GA4. ## Project Context @.paul/PROJECT.md @.paul/ROADMAP.md @.paul/STATE.md ## Source Files @templates/shop-order/order-details.php (purchase event, lines 164-192) @templates/shop-basket/summary-view.php (begin_checkout event, lines 72-80, 175-187) @templates/shop-product/product.php (view_item lines 273-288, add_to_cart lines 607-625) @templates/shop-basket/basket.php (brak view_cart — do dodania) ## Technical Reference @poprawki_datalayer_projectpro.md (specyfikacja zmian z audytu) ## Data Model Order products (pp_shop_order_products) mają kolumny: product_id, name, price_brutto, price_brutto_promo, quantity. Basket products — surowa tablica z sesji, product data fetchowana przez ProductRepository::findCached(). No specialized flows configured — standard execute plan. ## AC-1: Purchase event — format GA4 z prawidlowa cena ```gherkin Given strona potwierdzenia zamowienia /zamowienie/* When dataLayer.push(purchase) jest wywolany Then items maja klucze item_id (string), item_name, price (number > 0), quantity (number), google_business_vertical: "retail" And ecommerce ma currency: "PLN" And nie ma zduplikowanego klucza value ani hardcoded wartosci ``` ## AC-2: Begin_checkout event — format GA4 ```gherkin Given strona /koszyk-podsumowanie z produktami w koszyku When dataLayer.push(begin_checkout) jest wywolany Then items maja klucze item_id (string), item_name (zamiast id, name), price (number), quantity (number), google_business_vertical: "retail" ``` ## AC-3: View_item event — kompletne dane ```gherkin Given strona produktu When dataLayer.push(view_item) jest wywolany Then ecommerce zawiera currency: "PLN" i value (number) And items maja price jako number (nie string), google_business_vertical: "retail" ``` ## AC-4: Add_to_cart event — poprawne typy ```gherkin Given klikniecie "dodaj do koszyka" na stronie produktu When dataLayer.push(add_to_cart) jest wywolany Then items maja google_business_vertical: "retail" And quantity jest number (nie string) ``` ## AC-5: View_cart event — nowy event na stronie koszyka ```gherkin Given strona /koszyk z produktami w koszyku When strona sie zaladuje Then dataLayer.push({event: "view_cart"}) jest wywolany z currency, value i items w formacie GA4 ``` Task 1: Naprawic istniejace eventy dataLayer (purchase, begin_checkout, view_item, add_to_cart) templates/shop-order/order-details.php, templates/shop-basket/summary-view.php, templates/shop-product/product.php **order-details.php (purchase event, linie 167-187):** 1. Usunac hardcoded `value: 25.42` (linia 172) — zostawic tylko dynamiczny `value` z linii 174 2. Zamienic `'id': ` na `item_id: ""` 3. Zamienic `'name': ''` na `item_name: ""` 4. Zamienic logike price na: `price: 0 && (float)$product['price_brutto_promo'] < (float)$product['price_brutto']) ? \Shared\Helpers\Helpers::normalize_decimal($product['price_brutto_promo']) : \Shared\Helpers\Helpers::normalize_decimal($product['price_brutto']);?>` 5. Dodac `google_business_vertical: "retail"` do kazdego itemu 6. Zamienic single quotes na double quotes w kluczach itemow (konsystencja) 7. Dodac `'quantity': ` (rzutowanie na int) **summary-view.php (begin_checkout items, linie 72-80):** 1. Zamienic `'"id": "' . $product['id']` na `'"item_id": "' . $product['id']` 2. Zamienic `'"name": "' . $product['language']['name']` na `'"item_name": "' . str_replace('"', '', $product['language']['name'])` 3. Dodac `'"google_business_vertical": "retail"'` do kazdego itemu **product.php (view_item, linie 273-287):** 1. Dodac `currency: "PLN",` do obiektu ecommerce (przed items) 2. Dodac `value: ,` do obiektu ecommerce (po currency) 3. Zmienic `price: ''` na `price: ` (usunac cudzyslow — number zamiast string) 4. Dodac `google_business_vertical: "retail"` do itemu **product.php (add_to_cart, linie 607-624):** 1. Dodac `google_business_vertical: "retail"` do itemu - quantity jest juz prawidlowo number (zmienna JS `quantity` pochodzi z parseInt/parseFloat lub .val() — sprawdzic i ewentualnie dodac parseInt) **Wazne:** Nie zmieniac struktury warunkow `if ($this->settings['google_tag_manager_id'])` — zostawic identycznie. **Wazne:** Uzywac normalize_decimal() dla cen (zapewnia format z kropka, nie przecinkiem). 1. Przegladnac wygenerowany HTML kazdego eventu — sprawdzic format kluczy, typy, obecnosc currency i google_business_vertical 2. Sprawdzic brak bledow skladni JS (cudzyslow, przecinki) 3. Testy PHPUnit nie powinny byc dotknięte (zmiany tylko w szablonach) AC-1, AC-2, AC-3, AC-4 satisfied: Wszystkie eventy uzywaja item_id/item_name, price jako number, currency PLN, google_business_vertical Task 2: Dodac event view_cart na stronie koszyka templates/shop-basket/basket.php Dodac dataLayer.push dla view_cart w sekcji ` ``` 2. Iterowac po `$this->basket`, dla kazdego elementu pobrac product data (ProductRepository::findCached) i zbudowac item z item_id, item_name, price, quantity, google_business_vertical. 3. Obliczyc value jako sume (price * quantity) wszystkich produktow. **Uwaga:** basket.php ma dostep do `$this->basket` (raw basket array). Kazdy element ma klucze: 'product-id', 'quantity', 'parent_id', 'attributes'. Product data nalezy pobrac przez: `(new \Domain\Product\ProductRepository($GLOBALS['mdb']))->findCached((int)$position['product-id'], $lang_id)` — identycznie jak robi basket-details.php. Uzyc `$GLOBALS['mdb']` i `(new \Domain\Languages\LanguagesRepository($GLOBALS['mdb']))->defaultLanguage()` dla lang_id (lub sprawdzic czy $this->lang_id jest dostepny — jesli nie, pobrac z sesji). **Wazne:** Dodac nowy `