feat: update font to Roboto across templates and add campaign/ad group filters in product views

- Changed font from Open Sans to Roboto in layout files.
- Added campaign and ad group filters in products main view.
- Enhanced product history to include campaign and ad group IDs.
- Updated migrations to support new campaign and ad group dimensions in product statistics.
- Introduced new migration files for managing campaign types and dropping obsolete columns.
This commit is contained in:
2026-02-18 01:21:22 +01:00
parent 1cff9ba0eb
commit 4635cefcbb
23 changed files with 2444 additions and 410 deletions

View File

@@ -295,7 +295,7 @@ class Api
$processed = 0;
$skipped = 0;
$touched_product_ids = [];
$touched_scopes = [];
foreach ( $data['data'] as $offer )
{
@@ -338,6 +338,23 @@ class Api
continue;
}
$campaign_external_id = (int) self::normalize_number( $offer['CampaignId'] ?? ( $offer['campaign_id'] ?? 0 ) );
$campaign_name = trim( (string) ( $offer['CampaignName'] ?? ( $offer['campaign_name'] ?? '' ) ) );
$ad_group_external_id = (int) self::normalize_number( $offer['AdGroupId'] ?? ( $offer['ad_group_id'] ?? 0 ) );
$ad_group_name = trim( (string) ( $offer['AdGroupName'] ?? ( $offer['ad_group_name'] ?? '' ) ) );
$scope = \controls\Cron::resolve_products_scope_ids(
$client_id,
$campaign_external_id,
$campaign_name,
$ad_group_external_id,
$ad_group_name,
$date
);
$db_campaign_id = (int) ( $scope['campaign_id'] ?? 0 );
$db_ad_group_id = (int) ( $scope['ad_group_id'] ?? 0 );
$impressions = (int) round( self::normalize_number( $offer['Impressions'] ?? 0 ) );
$clicks = (int) round( self::normalize_number( $offer['Clicks'] ?? 0 ) );
$cost = self::normalize_number( $offer['Cost'] ?? 0 );
@@ -352,12 +369,24 @@ class Api
'cost' => $cost,
'conversions' => $conversions,
'conversions_value' => $conversion_value,
'updated' => 1
'updated' => 1,
'campaign_id' => $db_campaign_id,
'ad_group_id' => $db_ad_group_id
];
if ( $mdb -> count( 'products_history', [ 'AND' => [ 'product_id' => $product_id, 'date_add' => $date ] ] ) )
if ( $mdb -> count( 'products_history', [ 'AND' => [
'product_id' => $product_id,
'campaign_id' => $db_campaign_id,
'ad_group_id' => $db_ad_group_id,
'date_add' => $date
] ] ) )
{
$offer_data_old = $mdb -> get( 'products_history', '*', [ 'AND' => [ 'product_id' => $product_id, 'date_add' => $date ] ] );
$offer_data_old = $mdb -> get( 'products_history', '*', [ 'AND' => [
'product_id' => $product_id,
'campaign_id' => $db_campaign_id,
'ad_group_id' => $db_ad_group_id,
'date_add' => $date
] ] );
if (
$offer_data_old['impressions'] == $offer_data['impressions']
@@ -367,13 +396,23 @@ class Api
and number_format( (float) str_replace( ',', '.', $offer_data_old['conversions_value'] ), 5 ) == number_format( (float) $offer_data['conversions_value'], 5 )
)
{
$touched_product_ids[ $product_id ] = true;
$scope_key = (int) $product_id . '|' . $db_campaign_id . '|' . $db_ad_group_id;
$touched_scopes[ $scope_key ] = [
'product_id' => (int) $product_id,
'campaign_id' => $db_campaign_id,
'ad_group_id' => $db_ad_group_id
];
$processed++;
continue;
}
$mdb -> update( 'products_history', $offer_data, [
'AND' => [ 'product_id' => $product_id, 'date_add' => $date ]
'AND' => [
'product_id' => $product_id,
'campaign_id' => $db_campaign_id,
'ad_group_id' => $db_ad_group_id,
'date_add' => $date
]
] );
}
else
@@ -383,19 +422,33 @@ class Api
$mdb -> insert( 'products_history', $offer_data );
}
$touched_product_ids[ $product_id ] = true;
$scope_key = (int) $product_id . '|' . $db_campaign_id . '|' . $db_ad_group_id;
$touched_scopes[ $scope_key ] = [
'product_id' => (int) $product_id,
'campaign_id' => $db_campaign_id,
'ad_group_id' => $db_ad_group_id
];
$processed++;
}
$history_30_rows = 0;
foreach ( array_keys( $touched_product_ids ) as $product_id )
foreach ( $touched_scopes as $scope )
{
\controls\Cron::cron_product_history_30_save( (int) $product_id, $date );
$mdb -> update( 'products_history', [ 'updated' => 0 ], [ 'AND' => [ 'product_id' => (int) $product_id, 'date_add' => $date ] ] );
$product_id = (int) ( $scope['product_id'] ?? 0 );
$campaign_id = (int) ( $scope['campaign_id'] ?? 0 );
$ad_group_id = (int) ( $scope['ad_group_id'] ?? 0 );
\controls\Cron::cron_product_history_30_save( $product_id, $date, $campaign_id, $ad_group_id );
$mdb -> update( 'products_history', [ 'updated' => 0 ], [ 'AND' => [
'product_id' => $product_id,
'campaign_id' => $campaign_id,
'ad_group_id' => $ad_group_id,
'date_add' => $date
] ] );
$history_30_rows++;
}
$temp_rows = self::rebuild_products_temp_for_client( $client_id );
$temp_rows = \controls\Cron::rebuild_products_temp_for_client( $client_id );
echo json_encode( [
'status' => 'ok',
@@ -411,67 +464,7 @@ class Api
static private function rebuild_products_temp_for_client( $client_id )
{
global $mdb;
$client_id = (int) $client_id;
if ( $client_id <= 0 )
{
return 0;
}
$product_ids = $mdb -> select( 'products', 'id', [ 'client_id' => $client_id ] );
if ( empty( $product_ids ) )
{
return 0;
}
$mdb -> delete( 'products_temp', [ 'product_id' => $product_ids ] );
$rows = $mdb -> query(
'SELECT p.id AS product_id, p.name,
COALESCE( SUM( ph.impressions ), 0 ) AS impressions,
COALESCE( SUM( ph.clicks ), 0 ) AS clicks,
COALESCE( SUM( ph.cost ), 0 ) AS cost,
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
WHERE p.client_id = :client_id
GROUP BY p.id, p.name',
[ ':client_id' => $client_id ]
) -> fetchAll( \PDO::FETCH_ASSOC );
$inserted = 0;
foreach ( $rows as $row )
{
$impressions = (int) $row['impressions'];
$clicks = (int) $row['clicks'];
$cost = (float) $row['cost'];
$conversions = (float) $row['conversions'];
$conversion_value = (float) $row['conversions_value'];
$ctr = ( $impressions > 0 ) ? round( $clicks / $impressions, 4 ) * 100 : 0;
$cpc = ( $clicks > 0 ) ? round( $cost / $clicks, 6 ) : 0;
$roas = ( $cost > 0 ) ? round( $conversion_value / $cost, 2 ) * 100 : 0;
$mdb -> insert( 'products_temp', [
'product_id' => (int) $row['product_id'],
'name' => $row['name'],
'impressions' => $impressions,
'impressions_30' => (int) \factory\Products::get_impressions_30( (int) $row['product_id'] ),
'clicks' => $clicks,
'clicks_30' => (int) \factory\Products::get_clicks_30( (int) $row['product_id'] ),
'ctr' => $ctr,
'cost' => $cost,
'conversions' => $conversions,
'conversions_value' => $conversion_value,
'cpc' => $cpc,
'roas' => $roas
] );
$inserted++;
}
return $inserted;
return \controls\Cron::rebuild_products_temp_for_client( $client_id );
}
static private function normalize_number( $value )