From 2c331fda07607750208574e81d09220e27d13608 Mon Sep 17 00:00:00 2001 From: Jacek Pyziak Date: Thu, 19 Feb 2026 15:38:24 +0100 Subject: [PATCH] feat: Enhance cron product synchronization with fetch skipped reasons and campaign type validation --- .vscode/ftp-kr.sync.cache.json | 344 ++++++++----------------------- autoload/controls/class.Cron.php | 83 +++++++- 2 files changed, 160 insertions(+), 267 deletions(-) diff --git a/.vscode/ftp-kr.sync.cache.json b/.vscode/ftp-kr.sync.cache.json index 4edcbd8..705b992 100644 --- a/.vscode/ftp-kr.sync.cache.json +++ b/.vscode/ftp-kr.sync.cache.json @@ -4,11 +4,7 @@ "AGENTS.md": { "type": "-", "size": 2540, -<<<<<<< HEAD "lmtime": 0, -======= - "lmtime": 1771369610845, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "ajax.php": { @@ -34,61 +30,37 @@ "class.Api.php": { "type": "-", "size": 19358, -<<<<<<< HEAD "lmtime": 1744498273470, "modified": true -======= - "lmtime": 1771373591501, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a }, "class.Campaigns.php": { "type": "-", - "size": 4683, - "lmtime": 1769729268048, - "modified": true + "size": 5378, + "lmtime": 1771486264591, + "modified": false }, "class.CampaignTerms.php": { "type": "-", -<<<<<<< HEAD - "size": 20891, - "lmtime": 1771441276763, -======= "size": 21563, - "lmtime": 1771446539471, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a - "modified": false + "lmtime": 1771441276763, + "modified": true }, "class.Clients.php": { "type": "-", - "size": 1603, -<<<<<<< HEAD - "lmtime": 0, - "modified": true - }, - "class.Cron.php": { - "type": "-", - "size": 117327, - "lmtime": 1771446080190, -======= - "lmtime": 1771446539472, + "size": 3143, + "lmtime": 1771494823776, "modified": false }, "class.Cron.php": { "type": "-", - "size": 119789, - "lmtime": 1771446788002, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "size": 114130, + "lmtime": 1771496221250, "modified": false }, "class.Products.php": { "type": "-", "size": 39363, -<<<<<<< HEAD "lmtime": 1771440055487, -======= - "lmtime": 1771446539476, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "class.Site.php": { @@ -99,37 +71,22 @@ }, "class.Users.php": { "type": "-", -<<<<<<< HEAD - "size": 15829, - "lmtime": 1771198781919, - "modified": true - }, - "class.XmlFiles.php": { - "type": "-", - "size": 818, - "lmtime": 0, -======= - "size": 16302, - "lmtime": 1771446539477, + "size": 13549, + "lmtime": 1771493809548, "modified": false }, "class.XmlFiles.php": { "type": "-", "size": 856, - "lmtime": 1771446539477, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a - "modified": false + "lmtime": 0, + "modified": true } }, "factory": { "class.Campaigns.php": { "type": "-", - "size": 10634, -<<<<<<< HEAD - "lmtime": 1771441276763, -======= - "lmtime": 1771446539478, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "size": 10980, + "lmtime": 1771486281723, "modified": false }, "class.Clients.php": { @@ -147,13 +104,8 @@ "class.Products.php": { "type": "-", "size": 27192, -<<<<<<< HEAD "lmtime": 1771170224109, "modified": true -======= - "lmtime": 1771446539479, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a }, "class.Users.php": { "type": "-", @@ -163,64 +115,37 @@ }, "class.XmlFiles.php": { "type": "-", -<<<<<<< HEAD - "size": 1410, - "lmtime": 0, -======= "size": 1453, - "lmtime": 1771446539480, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a - "modified": false + "lmtime": 0, + "modified": true } }, "services": { "class.ClaudeApi.php": { "type": "-", "size": 12510, -<<<<<<< HEAD "lmtime": 1771198088093, "modified": true -======= - "lmtime": 1771446539481, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a }, "class.GoogleAdsApi.php": { "type": "-", "size": 99181, -<<<<<<< HEAD "lmtime": 1771444236566, -======= - "lmtime": 1771446539482, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "class.OpenAiApi.php": { "type": "-", "size": 18739, -<<<<<<< HEAD "lmtime": 1771171891986, "modified": true -======= - "lmtime": 1771446539484, - "modified": false - } - }, - "view": { - "class.Users.php": { - "type": "-", - "size": 415, - "lmtime": 1771446539484, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a } } }, ".claude": { "settings.local.json": { "type": "-", - "size": 528, - "lmtime": 1771368558172, + "size": 549, + "lmtime": 1771493868314, "modified": false } }, @@ -232,36 +157,21 @@ }, "config.php": { "type": "-", -<<<<<<< HEAD - "size": 515, - "lmtime": 1771444244763, -======= - "size": 571, - "lmtime": 1771449735250, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "size": 624, + "lmtime": 1771497460705, "modified": false }, "cron.php": { "type": "-", -<<<<<<< HEAD - "size": 1971, + "size": 1977, "lmtime": 0, "modified": true -======= - "size": 1977, - "lmtime": 1771446539486, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a }, "docs": { "database.sql": { "type": "-", "size": 17320, -<<<<<<< HEAD "lmtime": 1771440593718, -======= - "lmtime": 1771446539487, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "google_ads_api_design_doc.doc": { @@ -278,12 +188,8 @@ }, "memory.md": { "type": "-", - "size": 2680, -<<<<<<< HEAD - "lmtime": 1771443431441, -======= - "lmtime": 1771446539488, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "size": 3357, + "lmtime": 1771496247126, "modified": false }, "PLAN.md": { @@ -293,6 +199,12 @@ "modified": false } }, + "fix_cron_state.php": { + "type": "-", + "size": 4065, + "lmtime": 1771491681111, + "modified": false + }, ".gitignore": { "type": "-", "size": 16, @@ -307,15 +219,9 @@ }, "index.php": { "type": "-", -<<<<<<< HEAD - "size": 3885, + "size": 3891, "lmtime": 1771198110809, "modified": true -======= - "size": 3891, - "lmtime": 1771446539489, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a }, "install.php": { "type": "-", @@ -332,69 +238,40 @@ }, "style.css": { "type": "-", -<<<<<<< HEAD - "size": 48422, - "lmtime": 1771441569261, -======= - "size": 48423, - "lmtime": 1771446539490, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "size": 54205, + "lmtime": 1771494983015, "modified": false }, "style.css.map": { "type": "-", - "size": 12494, -<<<<<<< HEAD - "lmtime": 1771441569259, -======= - "lmtime": 1771446539491, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "size": 145847, + "lmtime": 1771488979745, "modified": false }, "style-old.css": { "type": "-", "size": 19791, -<<<<<<< HEAD "lmtime": 0, "modified": true -======= - "lmtime": 1771370224943, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a }, "style-old.scss": { "type": "-", "size": 25906, -<<<<<<< HEAD "lmtime": 0, "modified": true -======= - "lmtime": 1771370224941, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a }, "style.scss": { "type": "-", - "size": 59257, -<<<<<<< HEAD - "lmtime": 1771441540574, -======= - "lmtime": 1771446539492, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "size": 65819, + "lmtime": 1771494864189, "modified": false } }, "libraries": { - "adspro-dialog.css": { - "type": "-", - "size": 6932, - "lmtime": 1771446539493, - "modified": false - }, "adspro-dialog.js": { "type": "-", - "size": 10747, - "lmtime": 1771446539494, + "size": 9505, + "lmtime": 1771367545298, "modified": false }, "bootstrap": {}, @@ -453,6 +330,12 @@ "size": 48385, "lmtime": 0, "modified": false + }, + "adspro-dialog.css": { + "type": "-", + "size": 6801, + "lmtime": 1771367581706, + "modified": false } }, "migrations": { @@ -477,71 +360,49 @@ "004_campaigns_performance_max_flag.sql": { "type": "-", "size": 543, -<<<<<<< HEAD "lmtime": 0, -======= - "lmtime": 1771372240963, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "005_drop_is_performance_max_column.sql": { "type": "-", "size": 505, -<<<<<<< HEAD "lmtime": 0, -======= - "lmtime": 1771372246305, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "006_products_scope_dimensions.sql": { "type": "-", "size": 6350, -<<<<<<< HEAD "lmtime": 0, -======= - "lmtime": 1771373537593, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "007_clients_merchant_account_id.sql": { "type": "-", "size": 123, -<<<<<<< HEAD "lmtime": 0, -======= - "lmtime": 1771446539495, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "008_products_keyword_planner_terms.sql": { "type": "-", "size": 764, -<<<<<<< HEAD "lmtime": 0, -======= - "lmtime": 1771446539495, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "009_products_merchant_sync_log.sql": { "type": "-", "size": 880, -<<<<<<< HEAD "lmtime": 0, -======= - "lmtime": 1771446539496, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "010_campaign_keywords.sql": { "type": "-", "size": 1278, -<<<<<<< HEAD "lmtime": 1771440593714, -======= - "lmtime": 1771446539497, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "modified": false + }, + "011_clients_force_sync.sql": { + "type": "-", + "size": 220, + "lmtime": 1771489966129, "modified": false }, "demo_data.sql": { @@ -549,6 +410,12 @@ "size": 22351, "lmtime": 0, "modified": true + }, + "012_cron_sync_status.sql": { + "type": "-", + "size": 1830, + "lmtime": 1771493459924, + "modified": false } }, "robots.txt": { @@ -558,74 +425,45 @@ "modified": false }, "templates": { - "allegro": {}, - "campaigns": { - "main_view.php": { - "type": "-", - "size": 12379, - "lmtime": 1771371901368, - "modified": false - } - }, - "campaign_terms": { - "main_view.php": { - "type": "-", - "size": 81281, - "lmtime": 1771446539498, - "modified": false - } - }, - "clients": { - "main_view.php": { - "type": "-", - "size": 7024, - "lmtime": 1771446539499, - "modified": false - } - }, - "cron": {}, - "html": {}, "products": { "main_view.php": { "type": "-", "size": 49238, -<<<<<<< HEAD "lmtime": 1771441425576, -======= - "lmtime": 1771446539500, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "product_history.php": { "type": "-", -<<<<<<< HEAD "size": 13214, "lmtime": 1769467103988, "modified": true -======= - "size": 13302, - "lmtime": 1771446539501, - "modified": false ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a } }, "site": { "layout-cron.php": { "type": "-", - "size": 5763, - "lmtime": 1771370224949, + "size": 5764, + "lmtime": 1771367592957, "modified": false }, "layout-logged.php": { "type": "-", - "size": 8625, - "lmtime": 1771446539502, + "size": 7746, + "lmtime": 1771367592216, "modified": false }, "layout-unlogged.php": { "type": "-", - "size": 2023, - "lmtime": 1771370224947, + "size": 2024, + "lmtime": 0, + "modified": true + } + }, + "campaigns": { + "main_view.php": { + "type": "-", + "size": 17073, + "lmtime": 1771497969444, "modified": false } }, @@ -638,51 +476,33 @@ }, "settings.php": { "type": "-", - "size": 21626, - "lmtime": 1771446539503, + "size": 11754, + "lmtime": 1771198773079, "modified": false } }, - "xml_files": { + "campaign_terms": { "main_view.php": { "type": "-", -<<<<<<< HEAD "size": 81281, "lmtime": 1771441788473, -======= - "size": 2849, - "lmtime": 1771446539504, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a + "modified": false + } + }, + "clients": { + "main_view.php": { + "type": "-", + "size": 9867, + "lmtime": 1771494851645, "modified": false } } }, -<<<<<<< HEAD "tmp": {}, "TODO.md": { "type": "-", "size": 0, "lmtime": 0, -======= - "tmp": { - "debug_products_urls.php": { - "type": "-", - "size": 2766, - "lmtime": 1771446539505, - "modified": false - }, - "products_data_save.txt": { - "type": "-", - "size": 46550, - "lmtime": 0, - "modified": false - } - }, - "TODO.md": { - "type": "-", - "size": 0, - "lmtime": 1771446539470, ->>>>>>> ed59af4a5ab471bdf2eeef28e0335416285baa3a "modified": false }, "tools": {}, diff --git a/autoload/controls/class.Cron.php b/autoload/controls/class.Cron.php index 4f7fe69..c51e296 100644 --- a/autoload/controls/class.Cron.php +++ b/autoload/controls/class.Cron.php @@ -45,6 +45,7 @@ class Cron 'phase' => 'single_full', 'processed_products' => (int) $sync['processed_products'], 'skipped' => (int) $sync['skipped'], + 'fetch_skipped_reason' => isset( $sync['fetch_skipped_reason'] ) ? (string) $sync['fetch_skipped_reason'] : '', 'history_30_products' => (int) $history_30, 'products_temp_rows' => (int) $temp_rows, 'errors' => $sync['errors'] @@ -54,7 +55,22 @@ class Cron self::cleanup_old_sync_rows( 30 ); - $client_ids = $mdb -> query( "SELECT id FROM clients WHERE deleted = 0 AND google_ads_customer_id IS NOT NULL AND google_ads_customer_id <> '' ORDER BY id ASC" ) -> fetchAll( \PDO::FETCH_COLUMN ); + $client_ids = $mdb -> query( "SELECT c.id + FROM clients c + WHERE c.deleted = 0 + AND c.google_ads_customer_id IS NOT NULL + AND c.google_ads_customer_id <> '' + AND ( + NOT EXISTS ( SELECT 1 FROM campaigns cp WHERE cp.client_id = c.id ) + OR EXISTS ( + SELECT 1 + FROM campaigns cp + WHERE cp.client_id = c.id + AND cp.campaign_id > 0 + AND ( cp.campaign_name IS NULL OR cp.campaign_name <> '--- konto ---' ) + ) + ) + ORDER BY c.id ASC" ) -> fetchAll( \PDO::FETCH_COLUMN ); $client_ids = array_values( array_unique( array_map( 'intval', $client_ids ) ) ); if ( empty( $client_ids ) ) @@ -144,6 +160,7 @@ class Cron $errors = []; $processed_products_total = 0; $skipped_total = 0; + $fetch_skipped_reasons = []; $history_30_products_total = 0; $products_temp_rows_total = 0; @@ -168,6 +185,11 @@ class Cron $sync = self::sync_products_fetch_for_client( $selected_client, $api, $active_date ); $processed_products_total += (int) ( $sync['processed_products'] ?? 0 ); $skipped_total += (int) ( $sync['skipped'] ?? 0 ); + $fetch_skipped_reason = trim( (string) ( $sync['fetch_skipped_reason'] ?? '' ) ); + if ( $fetch_skipped_reason !== '' ) + { + $fetch_skipped_reasons[ $fetch_skipped_reason ] = true; + } if ( !empty( $sync['errors'] ) ) { $errors = array_merge( $errors, (array) $sync['errors'] ); @@ -238,6 +260,7 @@ class Cron 'total_clients' => count( $client_ids ), 'processed_products' => $processed_products_total, 'skipped' => $skipped_total, + 'fetch_skipped_reasons' => array_keys( $fetch_skipped_reasons ), 'history_30_products' => $history_30_products_total, 'products_temp_rows' => $products_temp_rows_total, 'errors' => $errors @@ -508,6 +531,38 @@ class Cron $customer_id = trim( (string) ( $client['google_ads_customer_id'] ?? '' ) ); $date = date( 'Y-m-d', strtotime( $date ) ); + $known_campaign_types = $mdb -> query( + 'SELECT DISTINCT UPPER( TRIM( advertising_channel_type ) ) AS channel_type + FROM campaigns + WHERE client_id = :client_id + AND campaign_id > 0 + AND advertising_channel_type IS NOT NULL + AND TRIM( advertising_channel_type ) <> ""', + [ ':client_id' => $client_id ] + ) -> fetchAll( \PDO::FETCH_COLUMN ); + + $known_campaign_types = array_values( array_unique( array_filter( array_map( function( $item ) + { + return strtoupper( trim( (string) $item ) ); + }, (array) $known_campaign_types ) ) ) ); + + $product_campaign_types = [ 'SHOPPING', 'PERFORMANCE_MAX' ]; + $has_product_campaign_type = count( array_intersect( $known_campaign_types, $product_campaign_types ) ) > 0; + + if ( !empty( $known_campaign_types ) && !$has_product_campaign_type ) + { + return [ + 'date' => $date, + 'processed_products' => 0, + 'skipped' => 0, + 'history_30_products' => 0, + 'products_temp_rows' => 0, + 'touched_products' => 0, + 'fetch_skipped_reason' => 'non_product_campaign_types', + 'errors' => [] + ]; + } + $products = $api -> get_products_for_date( $customer_id, $date ); if ( $products === false ) { @@ -576,7 +631,8 @@ class Cron $existing_campaigns_rows = $mdb -> query( 'SELECT id, campaign_id, campaign_name FROM campaigns - WHERE client_id = :client_id', + WHERE client_id = :client_id + AND campaign_id > 0', [ ':client_id' => $client_id ] ) -> fetchAll( \PDO::FETCH_ASSOC ); @@ -687,6 +743,11 @@ class Cron $ad_group_external_id = (int) $ad_group_external_id; $ad_group_name = trim( (string) $ad_group_name ); + if ( $campaign_external_id <= 0 ) + { + return [ 'campaign_id' => 0, 'ad_group_id' => 0 ]; + } + $campaign_data = $campaigns_by_external_id[ $campaign_external_id ] ?? null; if ( !$campaign_data ) { @@ -891,6 +952,12 @@ class Cron } $campaign_external_id = (int) ( $offer['CampaignId'] ?? 0 ); + if ( $campaign_external_id <= 0 ) + { + $skipped++; + continue; + } + $campaign_name = trim( (string) ( $offer['CampaignName'] ?? '' ) ); $ad_group_external_id = (int) ( $offer['AdGroupId'] ?? 0 ); $ad_group_name = trim( (string) ( $offer['AdGroupName'] ?? '' ) ); @@ -1149,6 +1216,7 @@ class Cron FROM products_history AS ph INNER JOIN products AS p ON p.id = ph.product_id WHERE p.client_id = :client_id + AND ph.campaign_id > 0 AND ph.updated = 1'; if ( $date ) @@ -1216,7 +1284,7 @@ class Cron COALESCE( SUM( ph.conversions ), 0 ) AS conversions, COALESCE( SUM( ph.conversions_value ), 0 ) AS conversions_value FROM products AS p - LEFT JOIN products_history AS ph ON p.id = ph.product_id + LEFT JOIN products_history AS ph ON p.id = ph.product_id AND ph.campaign_id > 0 WHERE p.client_id = :client_id GROUP BY p.id, p.name, ph.campaign_id, ph.ad_group_id', [ ':client_id' => $client_id ] @@ -1240,7 +1308,7 @@ class Cron COALESCE( SUM( ph.conversions ), 0 ) AS conversions, COALESCE( SUM( ph.conversions_value ), 0 ) AS conversions_value FROM products AS p - LEFT JOIN products_history AS ph ON p.id = ph.product_id + LEFT JOIN products_history AS ph ON p.id = ph.product_id AND ph.campaign_id > 0 WHERE p.client_id = :client_id GROUP BY p.id', [ ':client_id' => $client_id ] @@ -1394,7 +1462,7 @@ class Cron $products = $mdb -> select( 'products', 'id', [ 'client_id' => $client_id ] ); foreach ( $products as $product ) { - $scopes = $mdb -> query( 'SELECT DISTINCT campaign_id, ad_group_id, date_add FROM products_history WHERE product_id = ' . $product . ' AND updated = 1 ORDER BY date_add DESC' ) -> fetchAll( \PDO::FETCH_ASSOC ); + $scopes = $mdb -> query( 'SELECT DISTINCT campaign_id, ad_group_id, date_add FROM products_history WHERE product_id = ' . $product . ' AND campaign_id > 0 AND updated = 1 ORDER BY date_add DESC' ) -> fetchAll( \PDO::FETCH_ASSOC ); foreach ( $scopes as $scope ) { $campaign_id = (int) ( $scope['campaign_id'] ?? 0 ); @@ -1449,6 +1517,11 @@ class Cron $campaign_id = (int) $campaign_id; $ad_group_id = (int) $ad_group_id; + if ( $campaign_id <= 0 ) + { + return; + } + $data = $mdb -> query( 'SELECT date_add,