- 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.
21 KiB
2026-02-20 - Obsluga statusu ACTIVE dla klientow
Zmienione pliki
-
autoload/controls/class.Clients.phpsave()zapisuje teraz poleactive(domyslnie1, gdy brak wartosci z formularza).- Dodana nowa akcja
set_active()pod endpoint/clients/set_activedo 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.
- nie pozwala kolejkowac synchronizacji dla klienta nieaktywnego (
- Kompatybilnosc schematu
clientsbez kolumnydeleted:- helpery
clients_has_deleted_column()isql_clients_not_deleted(), force_sync()isync_status()nie wywalaja sie, gdy w bazie nie ma kolumnydeleted.
- helpery
-
templates/clients/main_view.php- Tabela klientow ma nowa kolumne
Status(Aktywny/Nieaktywny). - Wiersz klienta trzyma
data-activedo 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 pokazujenieaktywny.
- Tabela klientow ma nowa kolumne
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
- UI listy i formularza:
- 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_dayszconfig.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 pocampaign_id + date_add), - zapisuje grupy reklam / groupy PMAX do
campaign_ad_groups.
- bierze tylko aktywnych klientow (
- 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_keywordsicampaign_negative_keywords, - uruchamiany raz na cykl klienta (po ostatnim dniu okna), nie x razy dla kazdego dnia.
- tabele docelowe:
- Dodany nowy
- Kampanie produktowe / PMAX:
- nie maja fraz dodanych, wiec w
campaign_keywordsmoga miec 0 rekordow, - frazy wykluczone sa dalej synchronizowane do
campaign_negative_keywords.
- nie maja fraz dodanych, wiec w
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 wproductsjest puste). - Dodana nowa tabela agregacyjna
products_aggregate:- scope:
product_id + campaign_id + ad_group_id(unikalne), - metryki
*_30i*_all_time, date_sync(kiedy agregat byl przeliczony).
- scope:
- Dodane kolumny produktowe bezposrednio do
-
docs/database.sql- Zaktualizowana definicja
productso nowe kolumny danych produktu. - Dodana definicja tabeli
products_aggregate.
- Zaktualizowana definicja
Ustalenie projektowe
productsstaje sie glowna tabela danych produktu.products_datazostaje tymczasowo dla kompatybilnosci starego kodu; dane sa migrowane doproducts.- Agregaty dla widokow
/productspowinny docelowo byc czytane zproducts_aggregatezamiast liczenia w locie.
2026-02-20 - Produkty: przepiecie na products + agregaty
Zmienione pliki
-
autoload/factory/class.Products.phpget_product_data():- najpierw czyta pola produktowe z
products(custom_label_4,custom_label_3,title,description,google_product_category,product_url), - fallback do
products_datadla kompatybilnosci.
- najpierw czyta pola produktowe z
set_product_data():- zapisuje pole glownie do
products, - rownolegle mirroruje zapis do
products_data(kompatybilnosc starego kodu).
- zapisuje pole glownie do
-
autoload/controls/class.Cron.phpsync_products_fetch_for_client():- import produktow zapisuje dane produktowe bezposrednio do
products(w tymtitle,product_url), - usuniete poleganie na
products_datapodczas samego fetchu.
- import produktow zapisuje dane produktowe bezposrednio do
aggregate_products_history_30_for_client():- po przeliczeniu
products_history_30odpala przebudowe agregatowproducts_aggregatedla klienta i dnia.
- po przeliczeniu
- Dodana metoda
rebuild_products_aggregate_for_client( $client_id, $date_sync ):- liczy metryki
*_30i*_all_timezproducts_history, - zapisuje scope (
product_id + campaign_id + ad_group_id) doproducts_aggregate.
- liczy metryki
rebuild_products_temp_for_client():- przestawione z liczenia bezposrednio po
products_historyna odczyt zproducts_aggregate, - zmniejsza liczenie "w locie" dla widoku
/products.
- przestawione z liczenia bezposrednio po
cron_product_history_30_save():products_history_30przechowuje teraz srednie dzienne wartosci z okna do 30 dni (zamiast sumy okna),- nadal zapisuje
roas_all_timedla danego dnia.
generate_custom_feed_for_client():- zrodlo danych produktowych przepiete na
products(bez wymaganegoINNER JOIN products_data).
- zrodlo danych produktowych przepiete na
- diagnostyka i pobieranie brakujacych URL (
cron_products_urls):- logika "ma URL / brak URL" bierze pod uwage
products.product_urlz fallbackiem doproducts_data.
- logika "ma URL / brak URL" bierze pod uwage
Gdzie to jest wykorzystywane
-
Pipeline produktowy:
/cron/cron_products- etap
fetch->products_history, - etap agregacji ->
products_history_30+products_aggregate, - etap finalny ->
products_tempbudowane zproducts_aggregate.
-
Widok tabeli produktow
/products:- dane nadal czytane z
products_temp, aleproducts_tempjest teraz zasilane agregatami zproducts_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=truezwraca 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
clientsbez kolumnydeleted:- helpery:
clients_has_column(),sql_clients_not_deleted(),sql_clients_deleted(), - nowy pipeline kampanii (
cron_campaigns/cron_universal) nie wywala sie na bazie bezdeleted.
- helpery:
get_conversion_window_days( $prefer_config = false )uwzglednia teraz konfiguracje zconfig.php.sync_campaign_ad_groups_for_client()dostal parametras_of_date.
- dane nadal czytane z
-
autoload/services/class.GoogleAdsApi.phpget_ad_groups_30_days()wspiera teraz parametras_of_datei zakres dat[as_of_date-29, as_of_date].get_ad_groups_all_time()wspiera teraz parametras_of_date(filtrsegments.date <= as_of_datez 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.phpcron_universal()nie deleguje juz docron_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_statusdla 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
campaignsmialo juz 100% (done) icron_universalkonczyl wykonanie, mimo zeproductsmial jeszcze zalegle daty.
Zmienione pliki
autoload/controls/class.Cron.phpcron_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/pendingsa 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.
- Dodana migracja usuwajaca tabele
-
autoload/factory/class.Products.phpget_product_data()czyta dane tylko zproducts.set_product_data()zapisuje dane tylko doproducts.
-
autoload/controls/class.Cron.php- diagnostyka URL i wybieranie produktow bez URL opiera sie juz tylko o
products.product_url.
- diagnostyka URL i wybieranie produktow bez URL opiera sie juz tylko o
-
docs/database.sql- usunieta definicja tabeli
products_data.
- usunieta definicja tabeli
-
migrations/demo_data.sql- usuniete operacje
INSERT/DELETEnaproducts_data, - etykiety demo (
custom_label_4) sa ustawiane bezposrednio wproducts.
- usuniete operacje
Gdzie to jest wykorzystywane
- Dane produktowe (
title,description,google_product_category,custom_label_3,custom_label_4,product_url) sa trzymane tylko wproducts.
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 iclient_id) wykonywany jest krok:- pobranie URL produktow z Google Merchant Center dla produktow bez URL,
- zapis URL do
products.product_url.
- Gdy
offer_idnie istnieje w Merchant Center, tworzony/aktualizowany jest alert wcampaign_alerts:alert_type = products_missing_in_merchant_center,- scope techniczny:
campaign_external_id = 0,ad_group_external_id = 0, meta_jsonzawiera m.in. listymissing_offer_idsimissing_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_universalma dodatkowy fallback niezalezny od pipelinecampaigns/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...".
- gdy oba pipeline sa zakonczone, ale sa jeszcze produkty bez URL, uruchamia sam krok Merchant URL + alerty (
- 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 NULLimerchant_url_not_found = 0. - Na jedno wywolanie wykonywana jest jedna paczka sprawdzen (limit z
config.php:cron_products_urls_limit_per_client, ustawiony na100). - 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_centerjest liczony na podstawie calej aktualnej pulimerchant_url_not_found = 1(nie tylko bieżącej paczki), wiec nie znika przychecked_products = 0. - Alerty sa per produkt (1 alert = 1 produkt):
- dla kazdego produktu bez URL i z
merchant_url_not_found = 1tworzony jest osobny wpis wcampaign_alerts, - tresc alertu zawiera nazwe produktu (fallback:
name, dalejoffer_id) ioffer_id, - technicznie:
campaign_external_id = products.id, co stabilizuje unikalnosc wpisu.
- dla kazdego produktu bez URL i z
- Dodany helper
-
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 naNULL.
- Dodane kolumny w
-
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.
- ustawiany jest
- Przy zapisie
2026-02-20 - Alerty na stronie /products dla klient + kampania
Zmienione pliki
-
autoload/factory/class.Products.phpget_scope_alerts()nie wymaga juz wybranej grupy reklam:- minimalny scope:
client_id + campaign_id, - filtr
ad_group_idjest stosowany tylko opcjonalnie (gdy grupa jest wybrana).
- minimalny scope:
-
templates/products/main_view.phpload_scope_alerts()pobiera alerty juz dla kombinacjiklient + 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).
- Panel alertow pod filtrami pokazuje alerty:
2026-02-20 - Etykietowanie alertow Merchant (bez falszywej kampanii)
Zmienione pliki
-
autoload/controls/class.Cron.php- Dla alertu
products_missing_in_merchant_centernie jest juz zapisywanyproduct_idwcampaign_external_id. - Pola scope kampanii/grupy sa zapisywane jako
0(alert produktowy, bez przypisania do kampanii).
- Dla alertu
-
templates/campaign_alerts/main_view.php- Dla alertu
products_missing_in_merchant_centertabela alertow pokazuje:- Kampania:
Produkt (Merchant Center), - Grupa reklam:
---.
- Kampania:
- Dla pozostalych alertow fallback
Kampania #.../Grupa reklam #...dziala tylko dla dodatnich external_id; dla0pokazuje neutralne etykiety.
- Dla alertu
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 indeksidx_alert_product.
- Dodana kolumna
-
autoload/controls/class.Cron.php- Alerty
products_missing_in_merchant_centerzapisujaproduct_idw tabelicampaign_alerts. - Dla zachowania unikalnosci dziennej per produkt, techniczny
campaign_external_idpozostaje rownyproduct_id.
- Alerty
-
autoload/factory/class.CampaignAlerts.phpget_alerts()zwraca teraz rowniez poleproduct_id.
-
docs/database.sql- Dodana aktualna definicja tabeli
campaign_alertsz kolumnaproduct_id.
- Dodana aktualna definicja tabeli
2026-02-20 - CRON produktow: title nie jest uzupelniany automatycznie
Zmienione pliki
autoload/controls/class.Cron.php- W syncu produktow do tabeli
productsCRON nie zapisuje juz polatitle. - Dla nowych produktow CRON zapisuje tylko
name(beztitle). - Dla istniejacych produktow usunieto automatyczne uzupelnianie pustego
title.
- W syncu produktow do tabeli
Gdzie to jest wykorzystywane
/cron/cron_universal- automatyczny import produktow nie nadpisuje ani nie uzupelnia
products.title, titlepozostaje polem do recznej edycji i wysylki do GMC.
- automatyczny import produktow nie nadpisuje ani nie uzupelnia
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 = 0na podstawieproducts_aggregate. - Dodatkowy filtr
ad_group_id(opcjonalny), aby lista byla zgodna z aktualnym filtrem grupy reklam na widoku.
- Dodana metoda
-
autoload/controls/class.Products.php- Dodany endpoint
get_products_without_impressions_30(). - Zwraca JSON:
status,products[],counti przyjmuje opcjonalniead_group_id.
- Dodany endpoint
-
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.
- Dodana sekcja nad tabela produktow:
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 IDliczy 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.
- Licznik
-
templates/users/settings.php- Usunieta sekcja wizualna harmonogramu krokow CRON (
Krok 1/Krok 2). - Usunieta obsluga renderowania tej sekcji w JS odswiezajacym status CRON.
- Usunieta sekcja wizualna harmonogramu krokow 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".
- licznik klientow z Google Ads ID pokazuje poprawna wartosc na podstawie aktywnych klientow (
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_tempnaproducts_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.
- Zapytania dla listy produktow i licznikow zostaly przepiete z
Gdzie to jest wykorzystywane
/products- tabela i liczniki nie zaleza juz od
products_temp; biora dane bezposrednio zproducts_aggregate.
- tabela i liczniki nie zaleza juz od
2026-02-20 - custom_label_4 tylko z tabeli products
Ustalenie
- Etykieta
custom_label_4jest czytana i zapisywana z tabeliproducts. - Agregaty (
products_aggregate) nie sa zrodlem dla polacustom_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/).
- Usuniety filtr UI: pole
-
autoload/controls/class.Products.php- Usuniete endpointy:
get_client_bestseller_min_roas()save_client_bestseller_min_roas()
- Usuniete endpointy:
-
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 )
- Usuniete metody dostepu do progu ROAS klienta:
-
autoload/controls/class.Cron.php- W
rebuild_products_temp_for_client()usunieta logika automatycznej zmianycustom_label_4oparta o progbestseller_min_roas. - Funkcja pozostaje jako krok diagnostyczny zwracajacy liczbe scope z
products_aggregate.
- W
Efekt
- Aplikacja nie odczytuje, nie zapisuje i nie wykorzystuje juz
bestseller_min_roasw UI, endpointach ani w CRON. - Automatyczne oznaczanie
custom_label_4 = bestsellerna podstawie tego progu zostalo wycofane.