feat: Enhance user settings with cron URL plan display
- Added a new field to display the cron URL plan in user settings. - Updated JavaScript to handle the new plan data. refactor: Unify product model and migrate data - Migrated product data from `products_data` to `products` table. - Added new columns to `products` for better data organization. - Created `products_aggregate` table for storing aggregated product metrics. chore: Drop deprecated products_data table - Removed `products_data` table as data is now stored in `products`. feat: Add merchant URL flags to products - Introduced flags for tracking merchant URL status in `products` table. - Normalized product URLs to handle empty or invalid values. feat: Link campaign alerts to specific products - Added `product_id` column to `campaign_alerts` table for better tracking. - Created an index for efficient querying of alerts by product. chore: Add debug scripts for client data inspection - Created debug scripts to inspect client data from local and remote databases. - Included error handling and output formatting for better readability.
This commit is contained in:
280
docs/PLAN.md
280
docs/PLAN.md
@@ -1,280 +0,0 @@
|
||||
# adsPRO - System Zarządzania Reklamami Google ADS & Facebook ADS
|
||||
|
||||
## Opis projektu
|
||||
adsPRO to narzędzie webowe (PHP) do zarządzania i automatyzacji kampanii reklamowych Google ADS (priorytet) oraz Facebook ADS (planowane). System umożliwia monitorowanie kampanii, zarządzanie produktami, analizę wydajności (ROAS, CTR, CPC) oraz automatyczne etykietowanie produktów (bestsellery, zombie itp.).
|
||||
|
||||
**URL:** https://adspro.projectpro.pl
|
||||
**Hosting:** Hostido (shared hosting)
|
||||
|
||||
## Stack technologiczny
|
||||
- **PHP 8.x** - czyste PHP z własną strukturą MVC (bez frameworka)
|
||||
- **MySQL/MariaDB** - baza danych (Medoo ORM)
|
||||
- **Google ADS API** - pobieranie danych kampanii i produktów (CRON)
|
||||
- **Facebook ADS API** - planowane w przyszłości
|
||||
- **CRON** - automatyczna synchronizacja danych
|
||||
- **SCSS** - stylowanie (kompilacja do CSS)
|
||||
- **jQuery 3.6** - interaktywność frontend
|
||||
- **DataTables 2.1.7** - tabele z sortowaniem, filtrowaniem, paginacją
|
||||
- **Highcharts** - wykresy wydajności
|
||||
- **Select2** - zaawansowane selecty
|
||||
- **Font Awesome** - ikony
|
||||
|
||||
## Struktura katalogów (nowa)
|
||||
|
||||
```
|
||||
public_html/
|
||||
├── index.php # Front controller + nowy router
|
||||
├── .htaccess # Rewrite rules
|
||||
├── .env # Konfiguracja (przyszłość - migracja z config.php)
|
||||
├── config.php # Konfiguracja DB (obecna)
|
||||
├── ajax.php # Ajax handler
|
||||
├── api.php # API handler (Google ADS webhook)
|
||||
├── cron.php # CRON handler
|
||||
├── robots.txt
|
||||
├── layout/
|
||||
│ ├── favicon.png
|
||||
│ ├── style.scss # Główne style (SCSS)
|
||||
│ └── style.css # Skompilowane style
|
||||
├── libraries/ # Biblioteki zewnętrzne
|
||||
│ ├── medoo/
|
||||
│ ├── phpmailer/
|
||||
│ ├── select2/
|
||||
│ ├── jquery-confirm/
|
||||
│ ├── functions.js # Globalne funkcje JS
|
||||
│ └── framework/ # Framework UI (skin, pluginy)
|
||||
├── autoload/ # Kod PHP (MVC)
|
||||
│ ├── class.S.php # Helper: sesje, requesty, narzędzia
|
||||
│ ├── class.Tpl.php # Template engine
|
||||
│ ├── class.Cache.php # Cache
|
||||
│ ├── class.DbModel.php # Bazowy model DB
|
||||
│ ├── class.Html.php # HTML helper
|
||||
│ ├── controls/ # Kontrolery
|
||||
│ │ ├── class.Site.php # Router (do przebudowy)
|
||||
│ │ ├── class.Users.php # Logowanie, ustawienia
|
||||
│ │ ├── class.Dashboard.php # NOWY - Dashboard główny
|
||||
│ │ ├── class.Campaigns.php # Kampanie Google ADS
|
||||
│ │ ├── class.Products.php # Produkty
|
||||
│ │ ├── class.Allegro.php # Import Allegro
|
||||
│ │ ├── class.Reports.php # NOWY - Raporty i analityka
|
||||
│ │ ├── class.Api.php # Endpointy API
|
||||
│ │ └── class.Cron.php # CRON joby
|
||||
│ ├── factory/ # Modele danych (DB queries)
|
||||
│ │ ├── class.Users.php
|
||||
│ │ ├── class.Campaigns.php
|
||||
│ │ ├── class.Products.php
|
||||
│ │ └── class.Cron.php
|
||||
│ └── view/ # View helpers
|
||||
│ ├── class.Site.php # Renderer layoutu
|
||||
│ ├── class.Users.php
|
||||
│ └── class.Cron.php
|
||||
├── templates/ # Szablony PHP
|
||||
│ ├── site/
|
||||
│ │ ├── layout-logged.php # Layout z sidebar (PRZEBUDOWA)
|
||||
│ │ └── layout-unlogged.php # Layout logowania (PRZEBUDOWA)
|
||||
│ ├── auth/
|
||||
│ │ └── login.php # NOWY ekran logowania
|
||||
│ ├── dashboard/
|
||||
│ │ └── index.php # NOWY dashboard
|
||||
│ ├── campaigns/
|
||||
│ │ └── main_view.php # Widok kampanii
|
||||
│ ├── products/
|
||||
│ │ ├── main_view.php # Lista produktów
|
||||
│ │ └── product_history.php # Historia produktu
|
||||
│ ├── allegro/
|
||||
│ │ └── main_view.php # Import Allegro
|
||||
│ ├── reports/ # NOWE
|
||||
│ │ └── index.php # Raporty
|
||||
│ ├── users/
|
||||
│ │ ├── login-form.php # Stary login (do usunięcia)
|
||||
│ │ └── settings.php # Ustawienia użytkownika
|
||||
│ └── html/ # Komponenty HTML
|
||||
│ ├── button.php
|
||||
│ ├── input.php
|
||||
│ ├── select.php
|
||||
│ └── ...
|
||||
├── tools/
|
||||
│ └── google-taxonomy.php
|
||||
├── tmp/
|
||||
└── docs/
|
||||
└── PLAN.md
|
||||
```
|
||||
|
||||
## Nowy system routingu
|
||||
|
||||
### Zasada działania
|
||||
Zamiast obecnego `?module=X&action=Y` → czyste URLe obsługiwane przez `.htaccess` + nowy router w `class.Site.php`.
|
||||
|
||||
### Mapa URL
|
||||
|
||||
| URL | Kontroler | Metoda | Opis |
|
||||
|-----|-----------|--------|------|
|
||||
| `/login` | Users | login_form | Ekran logowania |
|
||||
| `/logout` | Users | logout | Wylogowanie |
|
||||
| `/` | Dashboard | index | Dashboard główny |
|
||||
| `/campaigns` | Campaigns | main_view | Lista kampanii |
|
||||
| `/campaigns/history/{id}` | Campaigns | history | Historia kampanii |
|
||||
| `/products` | Products | main_view | Lista produktów |
|
||||
| `/products/history/{id}` | Products | product_history | Historia produktu |
|
||||
| `/allegro` | Allegro | main_view | Import Allegro |
|
||||
| `/reports` | Reports | index | Raporty |
|
||||
| `/settings` | Users | settings | Ustawienia konta |
|
||||
| `/api/*` | Api | * | Endpointy API |
|
||||
| `/cron/*` | Cron | * | CRON joby |
|
||||
|
||||
### Nowy .htaccess
|
||||
```apache
|
||||
RewriteEngine On
|
||||
RewriteBase /
|
||||
|
||||
# Statyczne zasoby - pomijaj
|
||||
RewriteCond %{REQUEST_URI} ^/(libraries|layout|upload|temp)/ [NC]
|
||||
RewriteRule ^ - [L]
|
||||
|
||||
# Wszystko inne → index.php
|
||||
RewriteCond %{REQUEST_FILENAME} !-f
|
||||
RewriteCond %{REQUEST_FILENAME} !-d
|
||||
RewriteRule ^(.*)$ index.php [L,QSA]
|
||||
```
|
||||
|
||||
### Nowy router (class.Site.php)
|
||||
```php
|
||||
// Parsowanie URL z $_SERVER['REQUEST_URI']
|
||||
// Mapowanie: /segment1/segment2/segment3 → kontroler/akcja/parametry
|
||||
// Fallback na dashboard dla zalogowanych, login dla niezalogowanych
|
||||
```
|
||||
|
||||
## Główne funkcje
|
||||
|
||||
### 1. Nowy ekran logowania
|
||||
- Nowoczesny design: podzielony ekran (lewa strona - branding/grafika, prawa - formularz)
|
||||
- Logo "adsPRO" z subtitlem
|
||||
- Pola: email + hasło
|
||||
- Checkbox "Zapamiętaj mnie"
|
||||
- Walidacja AJAX
|
||||
- Animacje przejścia
|
||||
- Responsywność (mobile: tylko formularz)
|
||||
|
||||
### 2. Nowy layout z menu bocznym (sidebar)
|
||||
- **Sidebar (lewa strona, 260px):**
|
||||
- Logo "adsPRO" na górze
|
||||
- Menu nawigacyjne z ikonami Font Awesome:
|
||||
- 📊 Dashboard (`/`)
|
||||
- 📢 Kampanie (`/campaigns`)
|
||||
- 📦 Produkty (`/products`)
|
||||
- 📥 Allegro import (`/allegro`)
|
||||
- 📈 Raporty (`/reports`)
|
||||
- ⚙️ Ustawienia (`/settings`)
|
||||
- Aktywny element podświetlony
|
||||
- Możliwość zwijania sidebar (collapsed → same ikony, 60px)
|
||||
- Na dole: info o zalogowanym użytkowniku + przycisk wylogowania
|
||||
- **Top bar (nad contentem):**
|
||||
- Przycisk hamburger (toggle sidebar)
|
||||
- Breadcrumbs (ścieżka nawigacji)
|
||||
- Szybkie akcje / notyfikacje (przyszłość)
|
||||
- **Content area:**
|
||||
- Pełna szerokość minus sidebar
|
||||
- Padding 25px
|
||||
- Tło #F4F6F9 (jaśniejsze od obecnego)
|
||||
|
||||
### 3. Dashboard (NOWY)
|
||||
- Kafelki podsumowujące (karty):
|
||||
- Łączna liczba kampanii
|
||||
- Łączna liczba produktów
|
||||
- Średni ROAS (30 dni)
|
||||
- Łączne wydatki (30 dni)
|
||||
- Wykres trendu ROAS (ostatnie 30 dni)
|
||||
- Lista ostatnio zmodyfikowanych kampanii
|
||||
- Produkty wymagające uwagi (niski ROAS, zombie)
|
||||
|
||||
### 4. Zarządzanie kampaniami Google ADS
|
||||
- Wybór klienta (select)
|
||||
- Lista kampanii z metrykami (DataTables)
|
||||
- Historia kampanii z wykresem Highcharts
|
||||
- Metryki: ROAS, budżet, wydatki, wartość konwersji, strategia bidding
|
||||
- Usuwanie kampanii i wpisów historii
|
||||
- Komentarze do kampanii
|
||||
|
||||
### 5. Zarządzanie produktami
|
||||
- Wybór klienta
|
||||
- Konfiguracja min. ROAS dla bestsellerów
|
||||
- Tabela produktów z metrykami:
|
||||
- Wyświetlenia, kliknięcia, CTR, koszt, CPC
|
||||
- Konwersje, wartość konwersji, ROAS
|
||||
- Custom labels (bestseller/zombie/deleted/pla/paused)
|
||||
- Edycja inline (min_roas, custom_label)
|
||||
- Edycja produktu w modalu (tytuł, opis, kategoria Google)
|
||||
- Historia produktu z wykresem
|
||||
- Bulk delete zaznaczonych produktów
|
||||
|
||||
### 6. Import Allegro
|
||||
- Upload pliku CSV
|
||||
- Automatyczne mapowanie ofert
|
||||
- Raport importu (dodane, zaktualizowane)
|
||||
|
||||
### 7. Raporty (NOWY - przyszłość)
|
||||
- Raport wydajności kampanii
|
||||
- Raport produktów (bestsellery vs zombie)
|
||||
- Eksport do Excel
|
||||
- Porównanie okresów
|
||||
|
||||
### 8. Ustawienia
|
||||
- Dane konta (email)
|
||||
- Zmiana hasła
|
||||
- Konfiguracja Pushover (powiadomienia)
|
||||
- Klucze API (przyszłość: Google ADS, Facebook ADS)
|
||||
|
||||
### 9. CRON - synchronizacja danych
|
||||
- `cron_products` - synchronizacja produktów z Google ADS
|
||||
- `cron_products_history_30` - historia 30-dniowa produktów
|
||||
- `cron_xml` - generowanie XML
|
||||
- `cron_phrases` - synchronizacja fraz
|
||||
- `cron_phrases_history_30` - historia 30-dniowa fraz
|
||||
|
||||
## Plan implementacji
|
||||
|
||||
| Etap | Zakres | Priorytet | Pliki |
|
||||
|------|--------|-----------|-------|
|
||||
| **1. Nowy routing** | Przebudowa routera, nowy .htaccess, parsowanie czystych URL | 🔴 Wysoki | `.htaccess`, `index.php`, `controls/class.Site.php` |
|
||||
| **2. Nowy layout (sidebar)** | Layout z bocznym menu, top bar, responsywność | 🔴 Wysoki | `templates/site/layout-logged.php`, `layout/style.scss` |
|
||||
| **3. Nowy ekran logowania** | Nowoczesny split-screen login, nowy layout-unlogged | 🔴 Wysoki | `templates/site/layout-unlogged.php`, `templates/auth/login.php`, `layout/style.scss` |
|
||||
| **4. Dashboard** | Nowa strona startowa z podsumowaniem | 🟡 Średni | `controls/class.Dashboard.php`, `templates/dashboard/index.php` |
|
||||
| **5. Migracja kampanii** | Dostosowanie widoku kampanii do nowego routingu | 🟡 Średni | `controls/class.Campaigns.php`, `templates/campaigns/*` |
|
||||
| **6. Migracja produktów** | Dostosowanie widoku produktów do nowego routingu | 🟡 Średni | `controls/class.Products.php`, `templates/products/*` |
|
||||
| **7. Migracja Allegro** | Dostosowanie importu Allegro | 🟢 Niski | `controls/class.Allegro.php`, `templates/allegro/*` |
|
||||
| **8. Moduł raportów** | Nowy moduł analityczny | 🟢 Niski | `controls/class.Reports.php`, `templates/reports/*` |
|
||||
| **9. Facebook ADS** | Integracja z Facebook ADS API | 🔵 Przyszłość | Nowe kontrolery, factory, szablony |
|
||||
|
||||
## Kolorystyka i design
|
||||
|
||||
### Paleta kolorów
|
||||
- **Primary (akcent):** `#6690F4` (niebieski - obecny)
|
||||
- **Sidebar tło:** `#1E2A3A` (ciemny granat)
|
||||
- **Sidebar tekst:** `#A8B7C7` (jasny szary)
|
||||
- **Sidebar active:** `#6690F4` (primary)
|
||||
- **Content tło:** `#F4F6F9` (jasnoszary)
|
||||
- **Karty:** `#FFFFFF`
|
||||
- **Tekst:** `#4E5E6A` (obecny)
|
||||
- **Success:** `#57B951`
|
||||
- **Danger:** `#CC0000`
|
||||
- **Warning:** `#FF8C00`
|
||||
|
||||
### Typografia
|
||||
- Font: Open Sans (obecny - zachowany)
|
||||
- Rozmiar bazowy: 14px (sidebar), 15px (content)
|
||||
|
||||
## Bezpieczeństwo
|
||||
- Hasła hashowane MD5 (obecne) → **TODO: migracja na bcrypt**
|
||||
- Sesje PHP + cookie "zapamiętaj mnie"
|
||||
- Prepared statements (Medoo ORM)
|
||||
- htmlspecialchars() w szablonach
|
||||
- **TODO: CSRF tokeny w formularzach**
|
||||
- **TODO: migracja config.php → .env (z .htaccess deny)**
|
||||
|
||||
## Przyszłe rozszerzenia
|
||||
- Facebook ADS API - zarządzanie kampaniami FB
|
||||
- System powiadomień (Pushover + in-app)
|
||||
- Wielojęzyczność (PL/EN)
|
||||
- Role użytkowników (admin, manager, viewer)
|
||||
- Automatyczne reguły (np. "jeśli ROAS < X → zmień label na zombie")
|
||||
- Integracja z Google Merchant Center
|
||||
- API REST do integracji z innymi systemami
|
||||
@@ -1,44 +1,24 @@
|
||||
-- --------------------------------------------------------
|
||||
-- Host: host700513.hostido.net.pl
|
||||
-- Wersja serwera: 10.11.15-MariaDB-cll-lve - MariaDB Server
|
||||
-- Serwer OS: Linux
|
||||
-- HeidiSQL Wersja: 12.6.0.6765
|
||||
-- --------------------------------------------------------
|
||||
-- Zrzut struktury tabela host700513_adspro.clients
|
||||
CREATE TABLE IF NOT EXISTS `clients` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL DEFAULT '0',
|
||||
`google_ads_customer_id` varchar(20) DEFAULT NULL,
|
||||
`google_merchant_account_id` varchar(32) DEFAULT NULL,
|
||||
`google_ads_start_date` date DEFAULT NULL,
|
||||
`active` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||
/*!40101 SET NAMES utf8 */;
|
||||
/*!50503 SET NAMES utf8mb4 */;
|
||||
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
|
||||
/*!40103 SET TIME_ZONE='+00:00' */;
|
||||
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
|
||||
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
|
||||
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.campaigns
|
||||
CREATE TABLE IF NOT EXISTS `campaigns` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`client_id` int(11) NOT NULL DEFAULT 0,
|
||||
`campaign_id` bigint(20) NOT NULL DEFAULT 0,
|
||||
`campaign_name` varchar(255) NOT NULL DEFAULT '0',
|
||||
`advertising_channel_type` varchar(40) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `client_id` (`client_id`),
|
||||
CONSTRAINT `FK__clients` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=123 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.campaigns_comments
|
||||
CREATE TABLE IF NOT EXISTS `campaigns_comments` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`campaign_id` int(11) NOT NULL,
|
||||
`comment` text NOT NULL,
|
||||
`date_add` date NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `campaign_id` (`campaign_id`),
|
||||
CONSTRAINT `FK_campaigns_comments_campaigns` FOREIGN KEY (`campaign_id`) REFERENCES `campaigns` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=56 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.campaigns_history
|
||||
CREATE TABLE IF NOT EXISTS `campaigns_history` (
|
||||
@@ -54,235 +34,7 @@ CREATE TABLE IF NOT EXISTS `campaigns_history` (
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `offer_id` (`campaign_id`) USING BTREE,
|
||||
CONSTRAINT `FK_campaigns_history_campaigns` FOREIGN KEY (`campaign_id`) REFERENCES `campaigns` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=4400 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.clients
|
||||
CREATE TABLE IF NOT EXISTS `clients` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`name` varchar(255) NOT NULL DEFAULT '0',
|
||||
`google_ads_customer_id` varchar(20) DEFAULT NULL,
|
||||
`google_merchant_account_id` varchar(32) DEFAULT NULL,
|
||||
`google_ads_start_date` date DEFAULT NULL,
|
||||
`deleted` int(11) DEFAULT 0,
|
||||
`bestseller_min_roas` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=46 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.phrases
|
||||
CREATE TABLE IF NOT EXISTS `phrases` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`client_id` int(11) NOT NULL DEFAULT 0,
|
||||
`phrase` varchar(255) NOT NULL DEFAULT '0',
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `FK_phrases_clients` (`client_id`),
|
||||
CONSTRAINT `FK_phrases_clients` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5512 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.phrases_history
|
||||
CREATE TABLE IF NOT EXISTS `phrases_history` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`phrase_id` int(11) NOT NULL DEFAULT 0,
|
||||
`impressions` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks` int(11) NOT NULL DEFAULT 0,
|
||||
`cost` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_value` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`date_add` date NOT NULL DEFAULT '0000-00-00',
|
||||
`updated` int(11) NOT NULL DEFAULT 0,
|
||||
`deleted` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `offer_id` (`phrase_id`) USING BTREE,
|
||||
CONSTRAINT `FK_phrases_history_phrases` FOREIGN KEY (`phrase_id`) REFERENCES `phrases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=13088 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.phrases_history_30
|
||||
CREATE TABLE IF NOT EXISTS `phrases_history_30` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`phrase_id` int(11) NOT NULL,
|
||||
`impressions` int(11) NOT NULL,
|
||||
`clicks` int(11) NOT NULL,
|
||||
`cost` decimal(20,6) NOT NULL,
|
||||
`conversions` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_value` decimal(20,6) NOT NULL,
|
||||
`roas` decimal(20,6) NOT NULL,
|
||||
`date_add` date NOT NULL DEFAULT '0000-00-00',
|
||||
`deleted` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `offer_id` (`phrase_id`) USING BTREE,
|
||||
CONSTRAINT `FK_phrases_history_30_phrases` FOREIGN KEY (`phrase_id`) REFERENCES `phrases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=1795 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.phrases_temp
|
||||
CREATE TABLE IF NOT EXISTS `phrases_temp` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`phrase_id` int(11) DEFAULT NULL,
|
||||
`phrase` varchar(255) DEFAULT NULL,
|
||||
`impressions` int(11) DEFAULT NULL,
|
||||
`clicks` int(11) DEFAULT NULL,
|
||||
`cost` decimal(20,6) DEFAULT NULL,
|
||||
`conversions` decimal(20,6) DEFAULT NULL,
|
||||
`conversions_value` decimal(20,6) DEFAULT NULL,
|
||||
`cpc` decimal(20,6) DEFAULT NULL,
|
||||
`roas` decimal(20,0) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `offer_id` (`phrase_id`) USING BTREE,
|
||||
CONSTRAINT `FK_phrases_temp_phrases` FOREIGN KEY (`phrase_id`) REFERENCES `phrases` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=353973 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.products
|
||||
CREATE TABLE IF NOT EXISTS `products` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`client_id` int(11) NOT NULL DEFAULT 0,
|
||||
`offer_id` varchar(50) NOT NULL DEFAULT '0',
|
||||
`name` varchar(255) NOT NULL DEFAULT '0',
|
||||
`min_roas` int(11) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `FK_offers_clients` (`client_id`),
|
||||
CONSTRAINT `FK_offers_clients` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=5927 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.products_comments
|
||||
CREATE TABLE IF NOT EXISTS `products_comments` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) NOT NULL,
|
||||
`comment` text NOT NULL,
|
||||
`date_add` date NOT NULL DEFAULT current_timestamp(),
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `product_id` (`product_id`) USING BTREE,
|
||||
CONSTRAINT `FK_products_comments_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=118 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.products_data
|
||||
CREATE TABLE IF NOT EXISTS `products_data` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) DEFAULT NULL,
|
||||
`custom_label_4` varchar(255) DEFAULT NULL,
|
||||
`custom_label_3` varchar(255) DEFAULT NULL,
|
||||
`title` varchar(255) DEFAULT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`google_product_category` text DEFAULT NULL,
|
||||
`product_url` varchar(500) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `product_id` (`product_id`) USING BTREE,
|
||||
CONSTRAINT `FK_products_data_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=118 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.products_history
|
||||
CREATE TABLE IF NOT EXISTS `products_history` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) NOT NULL DEFAULT 0,
|
||||
`impressions` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks` int(11) NOT NULL DEFAULT 0,
|
||||
`ctr` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`cost` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_value` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`date_add` date NOT NULL DEFAULT '0000-00-00',
|
||||
`updated` int(11) NOT NULL DEFAULT 0,
|
||||
`deleted` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
KEY `product_id` (`product_id`) USING BTREE,
|
||||
CONSTRAINT `FK_products_history_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=63549 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.products_history_30
|
||||
CREATE TABLE IF NOT EXISTS `products_history_30` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) NOT NULL,
|
||||
`impressions` int(11) NOT NULL,
|
||||
`clicks` int(11) NOT NULL,
|
||||
`ctr` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`cost` decimal(20,6) NOT NULL,
|
||||
`conversions` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_value` decimal(20,6) NOT NULL,
|
||||
`roas` decimal(20,6) NOT NULL,
|
||||
`roas_all_time` decimal(20,6) NOT NULL,
|
||||
`date_add` date NOT NULL DEFAULT '0000-00-00',
|
||||
`deleted` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `product_id` (`product_id`) USING BTREE,
|
||||
CONSTRAINT `FK_products_history_30_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=27655 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.products_temp
|
||||
CREATE TABLE IF NOT EXISTS `products_temp` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) DEFAULT NULL,
|
||||
`name` varchar(255) DEFAULT NULL,
|
||||
`impressions` int(11) DEFAULT NULL,
|
||||
`impressions_30` int(11) DEFAULT NULL,
|
||||
`clicks` int(11) DEFAULT NULL,
|
||||
`clicks_30` int(11) DEFAULT NULL,
|
||||
`ctr` decimal(20,6) DEFAULT NULL,
|
||||
`cost` decimal(20,6) DEFAULT NULL,
|
||||
`conversions` decimal(20,6) DEFAULT NULL,
|
||||
`conversions_value` decimal(20,6) DEFAULT NULL,
|
||||
`cpc` decimal(20,6) DEFAULT NULL,
|
||||
`roas` decimal(20,0) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `product_id` (`product_id`) USING BTREE,
|
||||
CONSTRAINT `FK_products_temp_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=298845 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.settings
|
||||
CREATE TABLE IF NOT EXISTS `settings` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`setting_key` varchar(100) NOT NULL,
|
||||
`setting_value` text DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_setting_key` (`setting_key`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=15 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_general_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
-- Zrzut struktury tabela host700513_adspro.users
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`email` varchar(255) NOT NULL,
|
||||
`password` varchar(255) NOT NULL,
|
||||
`name` varchar(255) DEFAULT NULL,
|
||||
`surname` varchar(255) DEFAULT NULL,
|
||||
`default_project` int(11) DEFAULT NULL,
|
||||
`color` varchar(50) DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `email` (`email`)
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=11 DEFAULT CHARSET=utf8mb3 COLLATE=utf8mb3_polish_ci;
|
||||
|
||||
-- Eksport danych został odznaczony.
|
||||
|
||||
/*!40103 SET TIME_ZONE=IFNULL(@OLD_TIME_ZONE, 'system') */;
|
||||
/*!40101 SET SQL_MODE=IFNULL(@OLD_SQL_MODE, '') */;
|
||||
/*!40014 SET FOREIGN_KEY_CHECKS=IFNULL(@OLD_FOREIGN_KEY_CHECKS, 1) */;
|
||||
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
|
||||
/*!40111 SET SQL_NOTES=IFNULL(@OLD_SQL_NOTES, 1) */;
|
||||
|
||||
-- ================================
|
||||
-- DODANE: struktury kampanie > grupy/frazy
|
||||
-- ================================
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=381 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `campaign_ad_groups` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
@@ -304,67 +56,122 @@ CREATE TABLE IF NOT EXISTS `campaign_ad_groups` (
|
||||
`date_sync` date DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_campaign_ad_groups_campaign_ad_group` (`campaign_id`,`ad_group_id`),
|
||||
KEY `idx_campaign_ad_groups_campaign_id` (`campaign_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
KEY `idx_campaign_ad_groups_campaign_id` (`campaign_id`),
|
||||
CONSTRAINT `FK_campaign_ad_groups_campaigns` FOREIGN KEY (`campaign_id`) REFERENCES `campaigns` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=125 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `campaign_search_terms` (
|
||||
CREATE TABLE IF NOT EXISTS `campaign_alerts` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`campaign_id` int(11) NOT NULL,
|
||||
`ad_group_id` int(11) NOT NULL,
|
||||
`search_term` varchar(255) NOT NULL,
|
||||
`impressions_30` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks_30` int(11) NOT NULL DEFAULT 0,
|
||||
`cost_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversion_value_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`roas_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`impressions_all_time` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks_all_time` int(11) NOT NULL DEFAULT 0,
|
||||
`cost_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversion_value_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`roas_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`date_sync` date DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_campaign_search_terms` (`campaign_id`,`ad_group_id`,`search_term`),
|
||||
KEY `idx_campaign_search_terms_campaign_id` (`campaign_id`),
|
||||
KEY `idx_campaign_search_terms_ad_group_id` (`ad_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `campaign_keywords` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`campaign_id` int(11) NOT NULL,
|
||||
`ad_group_id` int(11) NOT NULL,
|
||||
`keyword_text` varchar(255) NOT NULL,
|
||||
`match_type` varchar(40) DEFAULT NULL,
|
||||
`impressions_30` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks_30` int(11) NOT NULL DEFAULT 0,
|
||||
`cost_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversion_value_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`roas_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`impressions_all_time` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks_all_time` int(11) NOT NULL DEFAULT 0,
|
||||
`cost_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversion_value_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`roas_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`date_sync` date DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_campaign_keywords` (`campaign_id`,`ad_group_id`,`keyword_text`(191),`match_type`),
|
||||
KEY `idx_campaign_keywords_campaign_id` (`campaign_id`),
|
||||
KEY `idx_campaign_keywords_ad_group_id` (`ad_group_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `campaign_negative_keywords` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`campaign_id` int(11) NOT NULL,
|
||||
`client_id` int(11) NOT NULL,
|
||||
`campaign_id` int(11) DEFAULT NULL,
|
||||
`campaign_external_id` bigint(20) DEFAULT NULL,
|
||||
`ad_group_id` int(11) DEFAULT NULL,
|
||||
`scope` varchar(20) NOT NULL DEFAULT 'campaign',
|
||||
`keyword_text` varchar(255) NOT NULL,
|
||||
`match_type` varchar(40) DEFAULT NULL,
|
||||
`date_sync` date DEFAULT NULL,
|
||||
`ad_group_external_id` bigint(20) DEFAULT NULL,
|
||||
`product_id` int(11) DEFAULT NULL,
|
||||
`alert_type` varchar(120) NOT NULL,
|
||||
`message` text NOT NULL,
|
||||
`meta_json` text DEFAULT NULL,
|
||||
`date_detected` date NOT NULL,
|
||||
`date_add` datetime NOT NULL DEFAULT current_timestamp(),
|
||||
`unseen` tinyint(1) NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `idx_campaign_negative_keywords_campaign_id` (`campaign_id`),
|
||||
KEY `idx_campaign_negative_keywords_ad_group_id` (`ad_group_id`)
|
||||
UNIQUE KEY `uniq_alert_daily` (`client_id`,`campaign_external_id`,`ad_group_external_id`,`alert_type`,`date_detected`),
|
||||
KEY `idx_alert_date` (`date_detected`),
|
||||
KEY `idx_alert_client` (`client_id`),
|
||||
KEY `idx_alert_campaign` (`campaign_id`),
|
||||
KEY `idx_alert_ad_group` (`ad_group_id`),
|
||||
KEY `idx_alert_unseen` (`unseen`),
|
||||
KEY `idx_alert_product` (`product_id`)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `products` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`client_id` int(11) NOT NULL DEFAULT 0,
|
||||
`offer_id` varchar(50) NOT NULL DEFAULT '0',
|
||||
`name` varchar(255) NOT NULL DEFAULT '0',
|
||||
`min_roas` int(11) DEFAULT NULL,
|
||||
`custom_label_4` varchar(255) DEFAULT NULL,
|
||||
`custom_label_3` varchar(255) DEFAULT NULL,
|
||||
`title` varchar(255) DEFAULT NULL,
|
||||
`description` text DEFAULT NULL,
|
||||
`google_product_category` text DEFAULT NULL,
|
||||
`product_url` varchar(500) DEFAULT NULL,
|
||||
`merchant_url_not_found` tinyint(1) NOT NULL DEFAULT 0,
|
||||
`merchant_url_last_check` datetime DEFAULT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
KEY `FK_offers_clients` (`client_id`),
|
||||
CONSTRAINT `FK_offers_clients` FOREIGN KEY (`client_id`) REFERENCES `clients` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=8482 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `products_history` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) NOT NULL DEFAULT 0,
|
||||
`campaign_id` int(11) NOT NULL DEFAULT 0,
|
||||
`ad_group_id` int(11) NOT NULL DEFAULT 0,
|
||||
`impressions` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks` int(11) NOT NULL DEFAULT 0,
|
||||
`ctr` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`cost` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_value` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`date_add` date NOT NULL DEFAULT '0000-00-00',
|
||||
`updated` int(11) NOT NULL DEFAULT 0,
|
||||
`deleted` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`) USING BTREE,
|
||||
UNIQUE KEY `uk_products_history_scope_day` (`product_id`,`campaign_id`,`ad_group_id`,`date_add`),
|
||||
KEY `product_id` (`product_id`) USING BTREE,
|
||||
KEY `idx_products_history_campaign_id` (`campaign_id`),
|
||||
KEY `idx_products_history_ad_group_id` (`ad_group_id`),
|
||||
CONSTRAINT `FK_products_history_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB AUTO_INCREMENT=37033 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci ROW_FORMAT=DYNAMIC;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `products_history_30` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) NOT NULL,
|
||||
`campaign_id` int(11) NOT NULL DEFAULT 0,
|
||||
`ad_group_id` int(11) NOT NULL DEFAULT 0,
|
||||
`impressions` int(11) NOT NULL,
|
||||
`clicks` int(11) NOT NULL,
|
||||
`ctr` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`cost` decimal(20,6) NOT NULL,
|
||||
`conversions` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_value` decimal(20,6) NOT NULL,
|
||||
`roas` decimal(20,6) NOT NULL,
|
||||
`roas_all_time` decimal(20,6) NOT NULL,
|
||||
`date_add` date NOT NULL DEFAULT '0000-00-00',
|
||||
`deleted` int(11) DEFAULT 0,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_products_history_30_scope_day` (`product_id`,`campaign_id`,`ad_group_id`,`date_add`),
|
||||
KEY `product_id` (`product_id`) USING BTREE,
|
||||
KEY `idx_products_history_30_campaign_id` (`campaign_id`),
|
||||
KEY `idx_products_history_30_ad_group_id` (`ad_group_id`),
|
||||
CONSTRAINT `FK_products_history_30_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `products_aggregate` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`product_id` int(11) NOT NULL,
|
||||
`campaign_id` int(11) NOT NULL DEFAULT 0,
|
||||
`ad_group_id` int(11) NOT NULL DEFAULT 0,
|
||||
`impressions_30` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks_30` int(11) NOT NULL DEFAULT 0,
|
||||
`ctr_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`cost_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversion_value_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`roas_30` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`impressions_all_time` int(11) NOT NULL DEFAULT 0,
|
||||
`clicks_all_time` int(11) NOT NULL DEFAULT 0,
|
||||
`ctr_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`cost_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversions_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`conversion_value_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`roas_all_time` decimal(20,6) NOT NULL DEFAULT 0.000000,
|
||||
`date_sync` date NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
UNIQUE KEY `uk_products_aggregate_scope` (`product_id`,`campaign_id`,`ad_group_id`),
|
||||
KEY `idx_products_aggregate_campaign_id` (`campaign_id`),
|
||||
KEY `idx_products_aggregate_ad_group_id` (`ad_group_id`),
|
||||
KEY `idx_products_aggregate_date_sync` (`date_sync`),
|
||||
CONSTRAINT `FK_products_aggregate_products` FOREIGN KEY (`product_id`) REFERENCES `products` (`id`) ON DELETE CASCADE ON UPDATE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
|
||||
426
docs/memory.md
426
docs/memory.md
@@ -1,55 +1,397 @@
|
||||
# adsPRO - Pamiec projektu
|
||||
# 2026-02-20 - Obsluga statusu ACTIVE dla klientow
|
||||
|
||||
Ten plik sluzy jako trwala pamiec dla Claude Code. Zapisuj tu wzorce, decyzje i ustalenia potwierdzone w trakcie pracy nad projektem.
|
||||
## Zmienione pliki
|
||||
|
||||
## Architektura
|
||||
- `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`.
|
||||
|
||||
- Custom MVC: Controllers (`\controls`) -> Factories (`\factory`) -> Medoo ORM (`$mdb`)
|
||||
- Autoload PSR-0: `\controls\Foo` -> `autoload/controls/class.Foo.php`
|
||||
- Routing w `index.php`: URL `/module/action/` -> `\controls\Module::action()`
|
||||
- Szablony w `templates/`, zmienne przez `$this->varName`
|
||||
- Serwisy API: `\services\GoogleAdsApi`, `\services\ClaudeApi`, `\services\OpenAiApi`
|
||||
- `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`.
|
||||
|
||||
## Styl kodu
|
||||
## Gdzie to jest wykorzystywane
|
||||
|
||||
- Spacje w nawiasach: `if ( $x )`, `function( $a, $b )`
|
||||
- Klamry w nowej linii
|
||||
- Wszystkie metody kontrolerow i fabryk: `static public function`
|
||||
- Endpointy JSON: `echo json_encode([...]); exit;`
|
||||
- Commity po polsku z prefixem: `feat:`, `fix:`, `update:`
|
||||
- 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()`)
|
||||
|
||||
## Frontend
|
||||
# 2026-02-20 - CRON kampanii (nowy przebieg, stare jako archiwum)
|
||||
|
||||
- jQuery 3.6, DataTables 2.1, Bootstrap 4, Select2 4.1, Highcharts
|
||||
- jquery-confirm do modali/dialogow
|
||||
- Font Awesome 6.5 do ikon
|
||||
- SASS: `layout/style.scss` -> auto-kompilacja przez Live Sass Compiler
|
||||
## Zmienione pliki
|
||||
|
||||
## Deployment
|
||||
- `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`.
|
||||
|
||||
- FTP auto-upload przez VS Code FTP-Kr
|
||||
- Brak kroku budowania - pliki laduja bezposrednio na serwer
|
||||
- Migracje: `php install.php` (idempotentne, sledzenie w `schema_migrations`)
|
||||
# 2026-02-20 - Produkty: przygotowanie schematu bazy
|
||||
|
||||
## Decyzje projektowe
|
||||
## Zmienione pliki
|
||||
|
||||
- Frazy wyszukiwane dodane do wykluczonych oznaczane czerwonym kolorem (klasa CSS `term-is-negative`)
|
||||
- Negatywne slowa kluczowe dodawane przez Google Ads API i zapisywane lokalnie w `campaign_negative_keywords`
|
||||
- Klucze API przechowywane w tabeli `settings` (key-value)
|
||||
- Frazy z Google Ads Keyword Planner dla URL produktu sa cachowane w `products_keyword_planner_terms` i ponownie uzywane przy generowaniu tytulu AI
|
||||
- Zmiany produktowe (`title`, `description`, `google_product_category`, `custom_label_4`) sa synchronizowane bezposrednio do Merchant API i logowane per pole w `products_merchant_sync_log`
|
||||
- CRON dziala w trybie **klient po kliencie** (client-first): konczy WSZYSTKIE daty jednego klienta, potem przechodzi do nastepnego. Dzieki temu paski postepu na `/clients` roznia sie miedzy klientami.
|
||||
- `cron_products` iteruje po datach per klient (`dates_per_run` z parametru `clients_per_run`), domyslnie `10` (max `100`); faza `aggregate_30` wywoluje `rebuild_products_temp` RAZ per klient
|
||||
- `cron_campaigns` iteruje po datach per klient (`dates_per_run` z parametru `clients_per_run`), domyslnie `2` (max `20`)
|
||||
- Helpery: `get_active_client($pipeline)` -> pierwszy klient z niezakonczona praca; `get_pending_dates_for_client()` -> daty do przetworzenia; `determine_client_products_phase()` -> faza per klient
|
||||
- Stan CRON przechowywany w tabeli `cron_sync_status` (wiersz = klient + pipeline + data + phase), zamiast JSON w `settings` (migracja 012)
|
||||
- Fazy produktow w `cron_sync_status`: pending -> fetch -> aggregate_30 -> done; kampanie: pending -> done
|
||||
- Force sync klienta = DELETE z `cron_sync_status` (wiersze odtwarzane przez `ensure_sync_rows` w nastepnym cyklu CRON)
|
||||
- Nowy klient/usuniety klient obslugiwany naturalnie: `ensure_sync_rows` dodaje brakujace, JOIN z `clients` pomija usunietych
|
||||
- `cleanup_old_sync_rows(30)` czysci zakonczone wiersze starsze niz 30 dni i wiersze usunietych klientow
|
||||
- `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).
|
||||
|
||||
## Preferencje uzytkownika
|
||||
- `docs/database.sql`
|
||||
- Zaktualizowana definicja `products` o nowe kolumny danych produktu.
|
||||
- Dodana definicja tabeli `products_aggregate`.
|
||||
|
||||
- Komunikacja po polsku
|
||||
- Zwiezle commity po polsku
|
||||
- Git push tylko na wyrazna prosbe
|
||||
## 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`.
|
||||
|
||||
Reference in New Issue
Block a user