Files
adsPRO/docs/memory.md
Jacek Pyziak b54a9a71b1 Add CLI script to fetch active Meta Ads insights for campaigns, adsets, and ads
- Implemented a new PHP script to retrieve insights for the last N days (default 30).
- Supports command-line options for token, account ID, days, API version, and output file.
- Fetches data at campaign, adset, and ad levels, with filtering for active statuses.
- Handles JSON output and optional file saving, including directory creation if necessary.
- Includes error handling for cURL requests and JSON responses.
2026-02-20 23:45:36 +01:00

21 KiB

2026-02-20 - Obsluga statusu ACTIVE dla klientow

Zmienione pliki

  • autoload/controls/class.Clients.php

    • save() zapisuje teraz pole active (domyslnie 1, gdy brak wartosci z formularza).
    • Dodana nowa akcja set_active() pod endpoint /clients/set_active do szybkiej zmiany statusu klienta AJAX-em.
    • force_sync() ma dodatkowa walidacje:
      • nie pozwala kolejkowac synchronizacji dla klienta nieaktywnego (active != 1),
      • nadal blokuje klienta usunietego (deleted = 1) i klienta bez wymaganych ID.
    • Kompatybilnosc schematu clients bez kolumny deleted:
      • helpery clients_has_deleted_column() i sql_clients_not_deleted(),
      • force_sync() i sync_status() nie wywalaja sie, gdy w bazie nie ma kolumny deleted.
  • templates/clients/main_view.php

    • Tabela klientow ma nowa kolumne Status (Aktywny/Nieaktywny).
    • Wiersz klienta trzyma data-active do obslugi UI i synchronizacji.
    • Dodany przycisk toggle (ikona fa-toggle-on/off) do natychmiastowej aktywacji/dezaktywacji.
    • Przyciski synchronizacji (kampanie/produkty/merchant) sa blokowane (disabled) dla nieaktywnego klienta i odblokowywane po aktywacji.
    • Formularz Dodaj/Edytuj klienta ma nowe pole Status klienta (active).
    • JS:
      • toggleClientActive() wysyla POST na /clients/set_active,
      • updateClientStatusUI() odswieza status i stan komorki Sync bez przeladowania strony,
      • loadSyncStatus() pomija paski postepu dla nieaktywnych klientow i pokazuje nieaktywny.

Gdzie to jest wykorzystywane

  • Zarzadzanie statusem klienta:
    • UI listy i formularza: templates/clients/main_view.php
    • Backend zapisu i toggle: autoload/controls/class.Clients.php
  • Ograniczenie recznego wymuszenia synchronizacji do klientow aktywnych:
    • autoload/controls/class.Clients.php (force_sync())

2026-02-20 - CRON kampanii (nowy przebieg, stare jako archiwum)

Zmienione pliki

  • autoload/controls/class.Cron.php
    • Dodany nowy cron_campaigns() jako glowny endpoint pod nowy przeplyw.
    • Stary kod zostal zachowany jako archiwum: cron_campaigns_archive().
    • Nowy przebieg:
      • bierze tylko aktywnych klientow (active = 1) z Google Ads Customer ID,
      • liczy okno dat na podstawie google_ads_conversion_window_days z config.php (z fallbackiem),
      • konczy okno na przedwczoraj (bez pobierania danych dzisiejszych),
      • przechodzi po datach dzien po dniu (rosnaco),
      • zapisuje/aktualizuje kampanie do campaigns,
      • zapisuje/aktualizuje historie dzienne do campaigns_history (upsert po campaign_id + date_add),
      • zapisuje grupy reklam / groupy PMAX do campaign_ad_groups.
    • po zakonczeniu kampanii + ad groups dla klienta, dla calego okna dat pobiera search terms dzienne do campaign_search_terms_history,
    • po pobraniu historii search terms wykonuje agregacje do campaign_search_terms (zanim przejdzie do kolejnego klienta).
    • Dodany krok syncu fraz dodanych i wykluczonych:
      • tabele docelowe: campaign_keywords i campaign_negative_keywords,
      • uruchamiany raz na cykl klienta (po ostatnim dniu okna), nie x razy dla kazdego dnia.
  • Kampanie produktowe / PMAX:
    • nie maja fraz dodanych, wiec w campaign_keywords moga miec 0 rekordow,
    • frazy wykluczone sa dalej synchronizowane do campaign_negative_keywords.

2026-02-20 - Produkty: przygotowanie schematu bazy

Zmienione pliki

  • migrations/016_products_model_unification.sql

    • Dodane kolumny produktowe bezposrednio do products:
      • custom_label_4, custom_label_3, title, description, google_product_category, product_url.
    • Backfill danych z products_data -> products (tylko gdy pole w products jest puste).
    • Dodana nowa tabela agregacyjna products_aggregate:
      • scope: product_id + campaign_id + ad_group_id (unikalne),
      • metryki *_30 i *_all_time,
      • date_sync (kiedy agregat byl przeliczony).
  • docs/database.sql

    • Zaktualizowana definicja products o nowe kolumny danych produktu.
    • Dodana definicja tabeli products_aggregate.

Ustalenie projektowe

  • products staje sie glowna tabela danych produktu.
  • products_data zostaje tymczasowo dla kompatybilnosci starego kodu; dane sa migrowane do products.
  • Agregaty dla widokow /products powinny docelowo byc czytane z products_aggregate zamiast liczenia w locie.

2026-02-20 - Produkty: przepiecie na products + agregaty

Zmienione pliki

  • autoload/factory/class.Products.php

    • get_product_data():
      • najpierw czyta pola produktowe z products (custom_label_4, custom_label_3, title, description, google_product_category, product_url),
      • fallback do products_data dla kompatybilnosci.
    • set_product_data():
      • zapisuje pole glownie do products,
      • rownolegle mirroruje zapis do products_data (kompatybilnosc starego kodu).
  • autoload/controls/class.Cron.php

    • sync_products_fetch_for_client():
      • import produktow zapisuje dane produktowe bezposrednio do products (w tym title, product_url),
      • usuniete poleganie na products_data podczas samego fetchu.
    • aggregate_products_history_30_for_client():
      • po przeliczeniu products_history_30 odpala przebudowe agregatow products_aggregate dla klienta i dnia.
    • Dodana metoda rebuild_products_aggregate_for_client( $client_id, $date_sync ):
      • liczy metryki *_30 i *_all_time z products_history,
      • zapisuje scope (product_id + campaign_id + ad_group_id) do products_aggregate.
    • rebuild_products_temp_for_client():
      • przestawione z liczenia bezposrednio po products_history na odczyt z products_aggregate,
      • zmniejsza liczenie "w locie" dla widoku /products.
    • cron_product_history_30_save():
      • products_history_30 przechowuje teraz srednie dzienne wartosci z okna do 30 dni (zamiast sumy okna),
      • nadal zapisuje roas_all_time dla danego dnia.
    • generate_custom_feed_for_client():
      • zrodlo danych produktowych przepiete na products (bez wymaganego INNER JOIN products_data).
    • diagnostyka i pobieranie brakujacych URL (cron_products_urls):
      • logika "ma URL / brak URL" bierze pod uwage products.product_url z fallbackiem do products_data.

Gdzie to jest wykorzystywane

  • Pipeline produktowy:

    • /cron/cron_products
    • etap fetch -> products_history,
    • etap agregacji -> products_history_30 + products_aggregate,
    • etap finalny -> products_temp budowane z products_aggregate.
  • Widok tabeli produktow /products:

    • dane nadal czytane z products_temp, ale products_temp jest teraz zasilane agregatami z products_aggregate.
    • Dodany helper sync_campaigns_snapshot_for_client() dla nowego przebiegu kampanii.
    • Dodany helper sync_campaign_terms_backfill_for_client() dla kroku fraz (history + agregacja).
    • Tryb wykonania nowego pipeline kampanii: 1 dzien = 1 wywolanie CRON.
      • Na jednym wywolaniu: kampanie + ad groups + search terms history + agregacja search terms dla jednego dnia.
      • Kolejne wywolanie przechodzi do kolejnego dnia dla tego samego klienta.
    • Tryb debug dla nowego CRON:
      • ?debug=true zwraca czytelny HTML (podsumowanie + pelny payload),
      • bez debug zwracany jest standardowy JSON.
    • Dodany helper cleanup_pipeline_rows_outside_window() aby pipeline kampanii trzymal tylko aktualne okno dat.
    • Filtry klientow w nowym CRON kampanii sa odporne na stare dane (NULL): COALESCE(active,0), COALESCE(deleted,0), TRIM(COALESCE(google_ads_customer_id,'')).
    • Dodana kompatybilnosc schematu clients bez kolumny deleted:
      • helpery: clients_has_column(), sql_clients_not_deleted(), sql_clients_deleted(),
      • nowy pipeline kampanii (cron_campaigns/cron_universal) nie wywala sie na bazie bez deleted.
    • get_conversion_window_days( $prefer_config = false ) uwzglednia teraz konfiguracje z config.php.
    • sync_campaign_ad_groups_for_client() dostal parametr as_of_date.
  • autoload/services/class.GoogleAdsApi.php

    • get_ad_groups_30_days() wspiera teraz parametr as_of_date i zakres dat [as_of_date-29, as_of_date].
    • get_ad_groups_all_time() wspiera teraz parametr as_of_date (filtr segments.date <= as_of_date z fallbackiem).

Gdzie to jest wykorzystywane

  • Głowny CRON kampanii: /cron/cron_campaigns -> \controls\Cron::cron_campaigns().
  • Uniwersalny CRON pipeline (zalecany endpoint): /cron/cron_universal -> \controls\Cron::cron_universal() (aktualnie deleguje do kroku kampanii).
  • Archiwalny CRON kampanii (stara logika): /cron/cron_campaigns_archive.
  • Dane do wykresow/tabel kampanii pozostaja pobierane z campaigns_history.

2026-02-20 - CRON uniwersalny jako glowny endpoint (1 dzien na wywolanie)

Zmienione pliki

  • autoload/controls/class.Cron.php
    • cron_universal() nie deleguje juz do cron_campaigns().
    • W jednym wywolaniu realizuje sekwencje:
      • kampanie (snapshot + ad groups + search terms + agregacja),
      • produkty (fetch + products_history_30 + products_aggregate + products_temp).
    • Tryb pracy pozostaje: 1 wywolanie = 1 klient + 1 dzien.
    • Status dnia jest zapisywany do cron_sync_status dla obu pipeline:
      • campaigns,
      • products.
    • Gdy krok kampanii zwroci blad, krok produktow dla tego dnia jest pomijany (products_sync_skipped_reason=campaigns_failed).

Gdzie to jest wykorzystywane

  • Docelowy adres CRON:
    • /cron/cron_universal?debug=true
  • Stare endpointy (/cron/cron_campaigns, /cron/cron_products) pozostaja w kodzie, ale nie sa docelowa sciezka wykonywania.

2026-02-20 - Poprawka niezaleznosci pipeline w cron_universal

Problem

  • campaigns mialo juz 100% (done) i cron_universal konczyl wykonanie, mimo ze products mial jeszcze zalegle daty.

Zmienione pliki

  • autoload/controls/class.Cron.php
    • cron_universal() wybiera teraz aktywnego klienta niezaleznie dla obu pipeline:
      • campaigns,
      • products.
    • Zakonczenie "wszyscy przetworzeni" następuje dopiero, gdy oba pipeline nie maja juz aktywnych pozycji.
    • Dodane osobne liczenie pozostalych dat:
      • campaigns_remaining_dates,
      • products_remaining_dates.
    • Statusy done/pending sa zapisywane osobno dla kazdego pipeline; produkty nie sa juz blokowane przez sam fakt, ze kampanie sa skonczone globalnie.
    • Ujednolicenie trybu client_id:
      • kampanie i produkty wykonują sie niezaleznie (w tym samym wywolaniu), a bledy sa laczone tylko w odpowiedzi.

2026-02-20 - Usuniecie products_data

Zmienione pliki

  • migrations/017_drop_products_data.sql

    • Dodana migracja usuwajaca tabele products_data.
  • autoload/factory/class.Products.php

    • get_product_data() czyta dane tylko z products.
    • set_product_data() zapisuje dane tylko do products.
  • autoload/controls/class.Cron.php

    • diagnostyka URL i wybieranie produktow bez URL opiera sie juz tylko o products.product_url.
  • docs/database.sql

    • usunieta definicja tabeli products_data.
  • migrations/demo_data.sql

    • usuniete operacje INSERT/DELETE na products_data,
    • etykiety demo (custom_label_4) sa ustawiane bezposrednio w products.

Gdzie to jest wykorzystywane

  • Dane produktowe (title, description, google_product_category, custom_label_3, custom_label_4, product_url) sa trzymane tylko w products.

2026-02-20 - Ostatni krok cron_universal: URL z Merchant + alerty brakow

Zmienione pliki

  • autoload/controls/class.Cron.php

    • Dodany helper sync_products_urls_and_alerts_for_client().
    • Na koncu przebiegu cron_universal (zarowno tryb automatyczny, jak i client_id) wykonywany jest krok:
      • pobranie URL produktow z Google Merchant Center dla produktow bez URL,
      • zapis URL do products.product_url.
    • Gdy offer_id nie istnieje w Merchant Center, tworzony/aktualizowany jest alert w campaign_alerts:
      • alert_type = products_missing_in_merchant_center,
      • scope techniczny: campaign_external_id = 0, ad_group_external_id = 0,
      • meta_json zawiera m.in. listy missing_offer_ids i missing_product_ids.
    • Gdy w danym dniu brak brakujacych produktow, dzienny alert tego typu jest czyszczony.
    • Do odpowiedzi cron dodane pola diagnostyczne:
      • merchant_urls_checked,
      • merchant_urls_updated,
      • merchant_missing_in_mc_count,
      • merchant_missing_offer_ids.
    • cron_universal ma dodatkowy fallback niezalezny od pipeline campaigns/products:
      • gdy oba pipeline sa zakonczone, ale sa jeszcze produkty bez URL, uruchamia sam krok Merchant URL + alerty (merchant_only=1),
      • dopiero brak takich produktow daje komunikat "Wszyscy aktywni klienci zostali przetworzeni...".
    • Krok Merchant URL nie jest wykonywany dla kazdego dnia okna; dziala jako osobny etap po zakonczeniu campaigns/products.
    • Do zapytan do GMC trafiaja tylko produkty z products.product_url IS NULL i merchant_url_not_found = 0.
    • Na jedno wywolanie wykonywana jest jedna paczka sprawdzen (limit z config.php: cron_products_urls_limit_per_client, ustawiony na 100).
    • Produkty, ktorych GMC nie zwraca (brak URL), sa oznaczane:
      • products.merchant_url_not_found = 1,
      • products.merchant_url_last_check = NOW(),
      • dzieki temu nie sa wysylane ponownie w nieskonczonosc.
    • Alert products_missing_in_merchant_center jest liczony na podstawie calej aktualnej puli merchant_url_not_found = 1 (nie tylko bieżącej paczki), wiec nie znika przy checked_products = 0.
    • Alerty sa per produkt (1 alert = 1 produkt):
      • dla kazdego produktu bez URL i z merchant_url_not_found = 1 tworzony jest osobny wpis w campaign_alerts,
      • tresc alertu zawiera nazwe produktu (fallback: name, dalej offer_id) i offer_id,
      • technicznie: campaign_external_id = products.id, co stabilizuje unikalnosc wpisu.
  • migrations/018_products_merchant_url_flags.sql

    • Dodane kolumny w products:
      • merchant_url_not_found (TINYINT, domyslnie 0),
      • merchant_url_last_check (DATETIME).
    • Normalizacja: puste/sztuczne product_url ('', '0', '-', 'null') ustawiane na NULL.
  • autoload/factory/class.Products.php

    • Przy zapisie product_url:
      • ustawiany jest merchant_url_last_check,
      • dla poprawnego URL resetowane jest merchant_url_not_found = 0.

2026-02-20 - Alerty na stronie /products dla klient + kampania

Zmienione pliki

  • autoload/factory/class.Products.php

    • get_scope_alerts() nie wymaga juz wybranej grupy reklam:
      • minimalny scope: client_id + campaign_id,
      • filtr ad_group_id jest stosowany tylko opcjonalnie (gdy grupa jest wybrana).
  • templates/products/main_view.php

    • load_scope_alerts() pobiera alerty juz dla kombinacji klient + kampania.
    • Sekcja alertow ma zaktualizowany opis: kampania + opcjonalna grupa reklam.

Gdzie to jest wykorzystywane

  • /products
    • Panel alertow pod filtrami pokazuje alerty:
      • dla calej kampanii (gdy grupa reklam nie jest wybrana),
      • lub zawezone do konkretnej grupy (gdy grupa reklam jest wybrana).

2026-02-20 - Etykietowanie alertow Merchant (bez falszywej kampanii)

Zmienione pliki

  • autoload/controls/class.Cron.php

    • Dla alertu products_missing_in_merchant_center nie jest juz zapisywany product_id w campaign_external_id.
    • Pola scope kampanii/grupy sa zapisywane jako 0 (alert produktowy, bez przypisania do kampanii).
  • templates/campaign_alerts/main_view.php

    • Dla alertu products_missing_in_merchant_center tabela alertow pokazuje:
      • Kampania: Produkt (Merchant Center),
      • Grupa reklam: ---.
    • Dla pozostalych alertow fallback Kampania #... / Grupa reklam #... dziala tylko dla dodatnich external_id; dla 0 pokazuje neutralne etykiety.

2026-02-20 - Powiazanie campaign_alerts z products

Zmienione pliki

  • migrations/019_campaign_alerts_product_id.sql

    • Dodana kolumna campaign_alerts.product_id (NULL) oraz indeks idx_alert_product.
  • autoload/controls/class.Cron.php

    • Alerty products_missing_in_merchant_center zapisuja product_id w tabeli campaign_alerts.
    • Dla zachowania unikalnosci dziennej per produkt, techniczny campaign_external_id pozostaje rowny product_id.
  • autoload/factory/class.CampaignAlerts.php

    • get_alerts() zwraca teraz rowniez pole product_id.
  • docs/database.sql

    • Dodana aktualna definicja tabeli campaign_alerts z kolumna product_id.

2026-02-20 - CRON produktow: title nie jest uzupelniany automatycznie

Zmienione pliki

  • autoload/controls/class.Cron.php
    • W syncu produktow do tabeli products CRON nie zapisuje juz pola title.
    • Dla nowych produktow CRON zapisuje tylko name (bez title).
    • Dla istniejacych produktow usunieto automatyczne uzupelnianie pustego title.

Gdzie to jest wykorzystywane

  • /cron/cron_universal
    • automatyczny import produktow nie nadpisuje ani nie uzupelnia products.title,
    • title pozostaje polem do recznej edycji i wysylki do GMC.

2026-02-20 - Lista produktow z 0 wyswietlen (30 dni) na /products

Zmienione pliki

  • autoload/factory/class.Products.php

    • Dodana metoda get_products_without_impressions_30( $client_id, $campaign_id, $limit ).
    • Zwraca produkty z wybranej kampanii, ktore maja sume impressions_30 = 0 na podstawie products_aggregate.
    • Dodatkowy filtr ad_group_id (opcjonalny), aby lista byla zgodna z aktualnym filtrem grupy reklam na widoku.
  • autoload/controls/class.Products.php

    • Dodany endpoint get_products_without_impressions_30().
    • Zwraca JSON: status, products[], count i przyjmuje opcjonalnie ad_group_id.
  • templates/products/main_view.php

    • Dodana sekcja nad tabela produktow:
      • "Produkty do sprawdzenia (0 wyswietlen w ostatnich 30 dniach)".
    • Sekcja pojawia sie dla wybranego klient + kampania.
    • Lista odswieza sie przy zmianie klienta/kampanii/grupy oraz po zaladowaniu strony.

Gdzie to jest wykorzystywane

  • /products
    • pomocnicza lista produktow potencjalnie nieistniejacych / wymagajacych weryfikacji (0 wyswietlen w 30 dni dla wybranej kampanii).

2026-02-20 - Ustawienia CRON: poprawka licznika klientow + usuniecie "Krok 1/Krok 2"

Zmienione pliki

  • autoload/controls/class.Users.php

    • Licznik Klienci z Google Ads ID liczy teraz klientow z:
      • COALESCE(active, 0) = 1,
      • TRIM(COALESCE(google_ads_customer_id, '')) <> ''.
    • Analogicznie poprawione filtry dla klientow Merchant i zapytan pomocniczych (wg active).
    • Harmonogram krokow (Krok 1, Krok 2) w danych dashboardu CRON jest pusty.
  • templates/users/settings.php

    • Usunieta sekcja wizualna harmonogramu krokow CRON (Krok 1 / Krok 2).
    • Usunieta obsluga renderowania tej sekcji w JS odswiezajacym status CRON.

Gdzie to jest wykorzystywane

  • /settings?settings_tab=cron
    • licznik klientow z Google Ads ID pokazuje poprawna wartosc na podstawie aktywnych klientow (active = 1),
    • brak sekcji "Krok 1 / Krok 2".

2026-02-20 - /products czyta bezposrednio z products_aggregate

Zmienione pliki

  • autoload/factory/class.Products.php
    • Zapytania dla listy produktow i licznikow zostaly przepiete z products_temp na products_aggregate:
      • get_products(),
      • get_roas_bounds(),
      • get_records_total_products(),
      • get_product_full_context().
    • Metryki all-time sa liczone z pol:
      • impressions_all_time, clicks_all_time, cost_all_time, conversions_all_time, conversion_value_all_time.
    • Metryki 30d sa czytane z:
      • impressions_30, clicks_30.

Gdzie to jest wykorzystywane

  • /products
    • tabela i liczniki nie zaleza juz od products_temp; biora dane bezposrednio z products_aggregate.

2026-02-20 - custom_label_4 tylko z tabeli products

Ustalenie

  • Etykieta custom_label_4 jest czytana i zapisywana z tabeli products.
  • Agregaty (products_aggregate) nie sa zrodlem dla pola custom_label_4.

2026-02-20 - Usuniecie funkcjonalnosci bestseller_min_roas

Zmienione pliki

  • templates/products/main_view.php

    • Usuniety filtr UI: pole Bestseller min ROAS (#bestseller_min_roas).
    • Usuniety frontendowy loader wartosci progu (load_client_bestseller_min_roas).
    • Usuniete wywolania loadera przy zmianie klienta i przy inicjalizacji strony.
    • Usuniety zapis AJAX progu klienta na blur (/products/save_client_bestseller_min_roas/).
  • autoload/controls/class.Products.php

    • Usuniete endpointy:
      • get_client_bestseller_min_roas()
      • save_client_bestseller_min_roas()
  • autoload/factory/class.Products.php

    • Usuniete metody dostepu do progu ROAS klienta:
      • get_client_bestseller_min_roas( $client_id )
      • save_client_bestseller_min_roas( $client_id, $min_roas )
  • autoload/controls/class.Cron.php

    • W rebuild_products_temp_for_client() usunieta logika automatycznej zmiany custom_label_4 oparta o prog bestseller_min_roas.
    • Funkcja pozostaje jako krok diagnostyczny zwracajacy liczbe scope z products_aggregate.

Efekt

  • Aplikacja nie odczytuje, nie zapisuje i nie wykorzystuje juz bestseller_min_roas w UI, endpointach ani w CRON.
  • Automatyczne oznaczanie custom_label_4 = bestseller na podstawie tego progu zostalo wycofane.