---
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
---

<objective>
## 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.
</objective>

<context>
## 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().
</context>

<skills>
No specialized flows configured — standard execute plan.
</skills>

<acceptance_criteria>

## 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
```

</acceptance_criteria>

<tasks>

<task type="auto">
  <name>Task 1: Naprawic istniejace eventy dataLayer (purchase, begin_checkout, view_item, add_to_cart)</name>
  <files>templates/shop-order/order-details.php, templates/shop-basket/summary-view.php, templates/shop-product/product.php</files>
  <action>
    **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': <?= (int)$product['product_id'];?>` na `item_id: "<?= $product['product_id'];?>"`
    3. Zamienic `'name': '<?= $product['name'];?>'` na `item_name: "<?= str_replace('"', '', $product['name']);?>"`
    4. Zamienic logike price na: `price: <?= ((float)$product['price_brutto_promo'] > 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': <?= (int)$product['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: <cena>,` do obiektu ecommerce (po currency)
    3. Zmienic `price: '<cena>'` na `price: <cena>` (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).
  </action>
  <verify>
    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)
  </verify>
  <done>AC-1, AC-2, AC-3, AC-4 satisfied: Wszystkie eventy uzywaja item_id/item_name, price jako number, currency PLN, google_business_vertical</done>
</task>

<task type="auto">
  <name>Task 2: Dodac event view_cart na stronie koszyka</name>
  <files>templates/shop-basket/basket.php</files>
  <action>
    Dodac dataLayer.push dla view_cart w sekcji `<script>` na poczatku bloku `$(function() {` w basket.php (linia 209).

    Implementacja:
    1. Dodac blok PHP+JS wewnatrz istniejacego `<script>` (po linii 50, w nowym `<script>` z warunkiem GTM):
    ```
    <? if ( $this -> settings['google_tag_manager_id'] ?? false ): ?>
    <? if ( is_array( $this -> basket ) and count( $this -> basket ) ): ?>
    <script type="text/javascript">
      dataLayer.push({ ecommerce: null });
      dataLayer.push({
        event: "view_cart",
        ecommerce: {
          currency: "PLN",
          value: [obliczona suma],
          items: [
            // iteracja po $this->basket z fetchem produktu przez ProductRepository
          ]
        }
      });
    </script>
    <? endif; ?>
    <? endif; ?>
    ```

    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 `<script>` blok PRZED istniejacym blokiem `<script>` (przed linia 36), nie wewnatrz istniejacego — zeby uniknac konfliktow z jQuery ready i AJAX reload.
    **Wazne:** Warunek `settings['google_tag_manager_id']` — uzyc `$settings` (global) lub `$this->settings` — sprawdzic ktore jest dostepne w basket.php (linia 1: `global $settings` sugeruje ze $settings jest dostepny).
  </action>
  <verify>
    1. Otworzyc /koszyk z produktami — sprawdzic w konsoli przegladarki dataLayer na obecnosc view_cart
    2. Sprawdzic czy items maja poprawne pola: item_id (string), item_name, price (number), quantity (number), google_business_vertical
    3. Sprawdzic czy value = suma cen * ilosci
    4. Sprawdzic czy event NIE odapla sie ponownie po AJAX reload koszyka (bo jest w osobnym script poza basket-details)
  </verify>
  <done>AC-5 satisfied: Event view_cart jest pushowany na stronie /koszyk z pelnym zestawem danych GA4</done>
</task>

</tasks>

<boundaries>

## DO NOT CHANGE
- autoload/Domain/* (warstwa domenowa — bez zmian)
- autoload/front/Controllers/* (kontrolery — bez zmian)
- templates/shop-basket/basket-details.php (AJAX-replaceable — nie dodawac tam skryptow)
- Logika sesji google-analytics-purchase (purchase dedup)
- Warunki `if ($this->settings['google_tag_manager_id'])` — zachowac identycznie

## SCOPE LIMITS
- Tylko eventy dataLayer — nie dodawac/zmieniac Facebook Pixel, gtag, ani innych trackerow
- Nie zmieniac struktury HTML szablonow
- Nie dodawac user_data do purchase (opcjonalne w specyfikacji, wymaga osobnej analizy RODO)
- Nie usuwac/przenosic kodu GADS conversion (nie znaleziono w kodzie — prawdopodobnie w GTM)
- Nie dodawac nowych eventow poza view_cart (np. remove_from_cart — poza zakresem)

</boundaries>

<verification>
Before declaring plan complete:
- [ ] Wszystkie eventy uzywaja item_id (string) i item_name zamiast id/name
- [ ] price jest zawsze number (nie string, nie 0 dla prawidlowych produktow)
- [ ] currency: "PLN" obecne we wszystkich eventach ecommerce
- [ ] google_business_vertical: "retail" w kazdym item
- [ ] quantity jest zawsze number
- [ ] Nowy event view_cart dziala na /koszyk
- [ ] Brak hardcoded value: 25.42 w purchase
- [ ] Brak bledow skladni JS w wygenerowanym HTML
- [ ] PHPUnit testy przechodzą (./test.ps1)
</verification>

<success_criteria>
- All tasks completed
- All verification checks pass
- No errors or warnings introduced
- DataLayer eventy zgodne z formatem GA4 (item_id, item_name, currency, google_business_vertical)
- Remarketing dynamiczny Google Ads ma prawidlowe ceny produktow
</success_criteria>

<output>
After completion, create `.paul/phases/11-datalayer-ga4-fix/11-01-SUMMARY.md`
</output>
