diff --git a/.paul/ROADMAP.md b/.paul/ROADMAP.md index d564006..2cc48dd 100644 --- a/.paul/ROADMAP.md +++ b/.paul/ROADMAP.md @@ -42,6 +42,7 @@ Status: Planning | Phase | Name | Plans | Status | Completed | |-------|------|-------|--------|-----------| | 10 | Edycja personalizacji produktu w koszyku | 1 | Done | 2026-03-19 | +| 11 | DataLayer GA4 analytics fix | 1 | Done | 2026-03-25 | ## Phase Details @@ -73,4 +74,13 @@ Status: Planning --- *Roadmap created: 2026-03-12* -*Last updated: 2026-03-19* +### Phase 11 — DataLayer GA4 analytics fix + +**Problem:** Eventy dataLayer ecommerce (purchase, begin_checkout, view_item, add_to_cart) używają starego formatu UA (id/name zamiast item_id/item_name), brak currency w view_item, price:0 w purchase, brak eventu view_cart. Remarketing dynamiczny i konwersje GA4 nie działają poprawnie. + +**Scope:** Poprawka 4 istniejących eventów do formatu GA4 + dodanie nowego eventu view_cart na stronie koszyka. + +**Reference:** `poprawki_datalayer_projectpro.md` — audyt analityki z pomysloweprezenty.pl + +--- +*Last updated: 2026-03-25* diff --git a/.paul/STATE.md b/.paul/STATE.md index 1607a10..71888ba 100644 --- a/.paul/STATE.md +++ b/.paul/STATE.md @@ -5,25 +5,25 @@ See: .paul/PROJECT.md (updated 2026-03-12) **Core value:** Właściciel sklepu ma pełną kontrolę nad sprzedażą online w jednym systemie pisanym od podstaw, bez narzutów zewnętrznych platform. -**Current focus:** Phase 10 complete — Edycja personalizacji produktu w koszyku +**Current focus:** Phase 11 complete — DataLayer GA4 analytics fix ## Current Position Milestone: Feature -Phase: 10 — Edycja personalizacji produktu w koszyku — Complete -Plan: 10-01 complete (phase done) -Status: UNIFY complete, phase 10 finished -Last activity: 2026-03-19 — 10-01 UNIFY complete +Phase: 11 — DataLayer GA4 analytics fix — Complete +Plan: 11-01 complete (phase done) +Status: UNIFY complete, phase 11 finished +Last activity: 2026-03-25 — 11-01 UNIFY complete Progress: -- Phase 10: [██████████] 100% (COMPLETE) +- Phase 11: [██████████] 100% (COMPLETE) ## Loop Position -Current loop state (phase 10, plan 01): +Current loop state (phase 11, plan 01): ``` PLAN ──▶ APPLY ──▶ UNIFY - ✓ ✓ ✓ [Phase 10 complete] + ✓ ✓ ✓ [Phase 11 complete] ``` Previous phases: @@ -35,6 +35,7 @@ Phase 7: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-0 Phase 8: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-16] Phase 9: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-19] Phase 10: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-19] +Phase 11: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026-03-25] ``` ## Accumulated Context @@ -49,6 +50,9 @@ Phase 10: PLAN ──▶ APPLY ──▶ UNIFY ✓ ✓ ✓ [COMPLETE — 2026- - 2026-03-19: Cleanup stuck sync_payment/sync_status jobów po udanym wysłaniu - 2026-03-19: Edycja custom fields w koszyku — product_code przeliczany po zmianie, merge duplikatów przy identycznym hashu - 2026-03-19: JS handlery koszyka w basket.php (nie basket-details.php) bo basket-details jest AJAX-replaceable +- 2026-03-25: view_cart event w basket.php (nie basket-details.php) — ten sam powód +- 2026-03-25: GA4 item format standard: item_id (string), item_name, price (number), quantity (int), google_business_vertical: "retail" +- 2026-03-25: Brak user_data w purchase — wymaga analizy RODO ### Deferred Issues None. @@ -58,10 +62,10 @@ None. ## Session Continuity -Last session: 2026-03-19 -Stopped at: Phase 10 UNIFY complete +Last session: 2026-03-25 +Stopped at: Phase 11 UNIFY complete Next action: /koniec-pracy or next feature -Resume file: .paul/phases/10-basket-edit-custom-fields/10-01-SUMMARY.md +Resume file: .paul/phases/11-datalayer-ga4-fix/11-01-SUMMARY.md --- *STATE.md — Updated after every significant action* diff --git a/.paul/phases/11-datalayer-ga4-fix/11-01-PLAN.md b/.paul/phases/11-datalayer-ga4-fix/11-01-PLAN.md new file mode 100644 index 0000000..1d0a38f --- /dev/null +++ b/.paul/phases/11-datalayer-ga4-fix/11-01-PLAN.md @@ -0,0 +1,225 @@ +--- +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 ` +
basket_details; ?> diff --git a/templates/shop-basket/summary-view.php b/templates/shop-basket/summary-view.php index 78acbe5..26e373d 100644 --- a/templates/shop-basket/summary-view.php +++ b/templates/shop-basket/summary-view.php @@ -73,10 +73,11 @@ $begin_checkout_items .= ','; $begin_checkout_items .= '{'; - $begin_checkout_items .= '"id": "' . $product['id'] . '",'; - $begin_checkout_items .= '"name": "' . $product['language']['name'] . '",'; + $begin_checkout_items .= '"item_id": "' . $product['id'] . '",'; + $begin_checkout_items .= '"item_name": "' . str_replace( '"', '', $product['language']['name'] ) . '",'; $begin_checkout_items .= '"price": ' . \Shared\Helpers\Helpers::normalize_decimal( $price_product['price_new'] ) . ','; - $begin_checkout_items .= '"quantity": ' . $position['quantity']; + $begin_checkout_items .= '"quantity": ' . (int)$position['quantity'] . ','; + $begin_checkout_items .= '"google_business_vertical": "retail"'; $begin_checkout_items .= '}'; ?> diff --git a/templates/shop-order/order-details.php b/templates/shop-order/order-details.php index d32eef6..fb09f05 100644 --- a/templates/shop-order/order-details.php +++ b/templates/shop-order/order-details.php @@ -169,17 +169,17 @@ event: "purchase", ecommerce: { transaction_id: " order['id'];?>", - value: 25.42, currency: "PLN", value: order['summary'], 2 ) ) - str_replace( ',', '.', round( $this -> order['transport_cost'], 2 ) );?>, shipping: order['transport_cost'] );?>, items: [ order['products'] as $product ):?> { - 'id': , - 'name': '', - 'quantity': , - 'price': 0 && (float)$product['price_brutto_promo'] < (float)$product['price_brutto']) ? (float)$product['price_brutto_promo'] : (float)$product['price_brutto'];?> + item_id: "", + item_name: "", + quantity: , + 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'] );?>, + google_business_vertical: "retail" } order['products'] ) ) echo ',';?> ] diff --git a/templates/shop-product/product.php b/templates/shop-product/product.php index 3d0ce7c..7cb7468 100644 --- a/templates/shop-product/product.php +++ b/templates/shop-product/product.php @@ -275,12 +275,15 @@ dataLayer.push({ event: "view_item", ecommerce: { + currency: "PLN", + value: product['price_brutto_promo'] ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] ); endif;?>, items: [ { item_id: " product['id'];?>", item_name: " product['language']['name'] );?>", - price: ' product['price_brutto_promo'] ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] ); endif;?>', - quantity: 1 + price: product['price_brutto_promo'] ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] ); endif;?>, + quantity: 1, + google_business_vertical: "retail" } ] } @@ -617,7 +620,8 @@ item_id: " product['id'];?>", item_name: " product['language']['name'] );?>", price: product['price_brutto_promo'] ): echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto_promo'] ); else: echo \Shared\Helpers\Helpers::normalize_decimal( $this -> product['price_brutto'] ); endif;?>, - quantity: quantity + quantity: parseInt(quantity), + google_business_vertical: "retail" } ] }