# Unit Pricing — specyfikacja rozszerzenia adsPRO Cel: dodać do adsPRO obsługę pól `unit_pricing_measure` i `unit_pricing_base_measure` (Google Merchant Center) tak, żeby moduł 14 audytu Google Ads (`analiza-produkty`) mógł je zapisywać z poziomu agenta AI, analogicznie do `title` / `google_product_category` / `custom_label_*`. Kontekst: Google Merchant Center wymaga `unit_pricing_measure` dla kategorii kosmetyki/Health & Beauty. Brak tego pola = 894/3455 SKU u klienta `aruba.rzeszow.pl` z warning `missing_potentially_required_attribute` (servability=unaffected, ale ranking ↓ i brak wyświetlania ceny per ml/g). --- ## 1. Model danych (DB) ### 1.1 Tabela `products` — nowe kolumny ```sql ALTER TABLE products ADD COLUMN unit_pricing_measure VARCHAR(64) NULL, -- np. "30 ml", "100 g", "1 szt" ADD COLUMN unit_pricing_base_measure VARCHAR(64) NULL, -- np. "100 ml", "1 kg", "1 szt" ADD COLUMN unit_pricing_changed_at DATETIME NULL; ``` **Walidacja wartości** (po stronie API i UI): - Format: `` — regex `^\d+(\.\d+)?\s+(ml|l|g|kg|szt|cm|m)$` - Dozwolone jednostki (Google specyfikacja): `ml`, `l`, `mg`, `g`, `kg`, `cl`, `m`, `cm`, `sqm`, `cbm`, `ct` (count = sztuki — Google używa `ct` lub osobnego pola; w PL przyjąć `szt`). - `unit_pricing_base_measure` musi być w **tej samej jednostce** co `measure` (np. measure=`30 ml` → base=`100 ml`, NIE `1 l`). - Standardowe wartości `base`: dla ml/g → `100`, dla l/kg → `1`, dla szt → `1`. ### 1.2 Migracja `migrations/YYYY-MM-DD_add_unit_pricing.sql` — patrz folder `migrations/` projektu. --- ## 2. API publiczne (rozszerzenie `api.php`) ### 2.1 `action=product_unit_pricing_set` — zapis Wzór: kopia `product_custom_label_4_set` z linii ~336 `api.php`. Parametry: - `api_key` (string, wymagany) - `client_id` (int, wymagany) - `offer_id` (string, wymagany) - `unit_pricing_measure` (string, opcjonalny — pusta wartość czyści pole) - `unit_pricing_base_measure` (string, opcjonalny — j.w.) Logika: - Walidacja regex (jw.) — jeśli zła forma → 422 `Invalid unit_pricing_measure format`. - Walidacja zgodności jednostek (measure unit == base unit) → 422 jeśli niezgodne. - Update `products.unit_pricing_measure` + `unit_pricing_base_measure` + `unit_pricing_changed_at = NOW()`. - Wpis w `products_comments`: `'Zmiana unit_pricing na / (API)'`. - HTTP 200 + JSON `{result: ok, product_id, offer_id, unit_pricing_measure, unit_pricing_base_measure}`. Przykład: ```bash curl -X POST https://adspro.projectpro.pl/api.php \ -d action=product_unit_pricing_set \ -d api_key=YOUR_KEY \ -d client_id=3 \ -d offer_id=7792 \ -d unit_pricing_measure="30 ml" \ -d unit_pricing_base_measure="100 ml" ``` ### 2.2 `action=product_unit_pricing_get` — odczyt Wzór: kopia `product_custom_label_4_get`. Parametry: `api_key`, `client_id`, `offer_id`. Odpowiedź: ```json { "result": "ok", "product_id": 987, "offer_id": "7792", "unit_pricing_measure": "30 ml", "unit_pricing_base_measure": "100 ml" } ``` ### 2.3 `action=products_get_missing_unit_pricing` — listing kandydatów (opcjonalne) Cel: agent może pobrać top N produktów które wymagają unit_pricing (filtr per kategoria) — zamiast skanować per-product. Parametry: `api_key`, `client_id`, opcjonalnie `top` (default 50), `category_filter` (np. `Skin Care`). Logika: SELECT z `products` WHERE `unit_pricing_measure IS NULL` AND (kategoria w listy beauty/cosmetics) ORDER BY `clicks_all_time DESC` LIMIT N. Odpowiedź: `{result: ok, count: N, products: [{offer_id, default_name, custom_title, google_product_category, clicks_30, ...}]}`. --- ## 3. Custom feed (zapis do GMC) Lokalizacja: `feeds/` w projekcie. Generator feedu produktowego musi dodać: ```xml 30 ml 100 ml ``` — **tylko** gdy oba pola niepuste w bazie. Pominąć tag jeśli null/empty (Google nie akceptuje pustych wartości). --- ## 4. Heurystyka AI (po stronie agenta — NIE w adsPRO) Agent (np. `set_product_unit_pricing.py` w projekcie `D:\google ads\scripts\actions\`) wyciąga `measure` z tytułu produktu: | Wzorzec w tytule | measure | base | |---|---|---| | `... 30ml` lub `... 30 ml` | `30 ml` | `100 ml` | | `... 1000ml` lub `... 1l` | `1000 ml` | `100 ml` | | `... 100g` | `100 g` | `100 g` | | `... 1kg` | `1 kg` | `1 kg` | | `... 10szt` | `10 szt` | `1 szt` | Edge cases (do flagowania jako "wątpliwości" — agent pyta usera): - Multi-component (zestaw 3 płynów × 200ml) — brak jednoznacznej miary - Sprzęt (fotel, taboret) — N/A jednostka - Nieznana pojemność (np. tylko `nr 3`) --- ## 5. Acceptance criteria - [ ] Migracja DB dodaje 3 kolumny + indeks na `unit_pricing_measure IS NULL` - [ ] `api.php` ma 3 nowe endpointy (`_set`, `_get`, `_get_missing` — ostatni opcjonalny) - [ ] Walidacja formatu regex + jednostek (testy unit) - [ ] Generator feedu zapisuje `` + `` gdy oba pola wypełnione - [ ] `product_unit_pricing_set` zapisuje wpis do `products_comments` - [ ] `product_unit_pricing_set` aktualizuje `unit_pricing_changed_at` - [ ] Endpoint `_get_missing` filtruje po kategorii (beauty/cosmetics) i sortuje po klikach - [ ] Skrypt `set_product_unit_pricing.py` po stronie agenta (`D:\google ads\scripts\actions\`) — wzorzec z `set_product_custom_label.py` - [ ] Skrypt `apply_unit_pricing_from_previews.py` — batch zapis z tabel preview w raportach M14 (`klienci//.analysis/reports/analiza-produkty-*.md` — etap 1.5) --- ## 6. Pilotaż Klient pilotażowy: `aruba.rzeszow.pl` (`client_id=3`). **Pierwsza partia (5 SKU z preview M14 z 2026-05-05):** | product_id | unit_pricing_measure | unit_pricing_base_measure | |---|---|---| | 7792 | 30 ml | 100 ml | | 305 | 10 ml | 100 ml | | 35 | 60 ml | 100 ml | | 281 | 100 ml | 100 ml | | 2288 | 1000 ml | 100 ml | Sukces pilotażu = 5/5 SKU widocznych w GMC z polami unit_pricing po 24h od pierwszego push feedu. --- ## 7. Dokumentacja publiczna Po wdrożeniu: dopisać sekcję 4.X do `docs/api-public-product-management.md` (analogicznie do sekcji 4.7 `product_custom_label_4_set`): - 4.X.1 `product_unit_pricing_set` - 4.X.2 `product_unit_pricing_get` - 4.X.3 (opc.) `products_get_missing_unit_pricing` --- ## 8. TODO procesowe (po wdrożeniu) 1. Backfill agentem AI wszystkich istniejących produktów z brakiem unit_pricing — heurystyka z sekcji 4 + flag wątpliwości na liście do user review. 2. Skrypt `apply_unit_pricing_from_previews.py` w projekcie agenta — batch zapis tabel preview z M14 raportów (etap 1.5) dla wszystkich klientów. 3. Monitoring w `analiza-feed` (M2): sprawdzanie liczby SKU bez unit_pricing — cel <5% per klient w branżach beauty/cosmetics. --- ## 9. Powiązane - Kontekst dla agenta: `D:\google ads\.claude\commands\analiza-produkty.md` — sekcja "Etap 1.5: Unit pricing (preview)". - Pierwsza preview tabela: `D:\google ads\klienci\aruba.rzeszow.pl\.analysis\reports\analiza-produkty-2026-05-05.md`.