From 803acf3da3f1ff1f41524ccd1314fb924eb28af6 Mon Sep 17 00:00:00 2001 From: michal Date: Wed, 22 Apr 2026 13:01:47 +0200 Subject: [PATCH] Wiki --- API-reference.md | 121 ++++++++++++++++++++++++++++++++++++ Admin.md | 47 ++++++++++++++ Architektura.md | 69 +++++++++++++++++++++ Automations.md | 98 +++++++++++++++++++++++++++++ Email-kampane.md | 67 ++++++++++++++++++++ Home.md | 43 +++++++++++++ Instalace.md | 85 +++++++++++++++++++++++++ Integrace-Eshop-API.md | 138 +++++++++++++++++++++++++++++++++++++++++ Integrace.md | 83 +++++++++++++++++++++++++ Jobs-Cron.md | 75 ++++++++++++++++++++++ Kontakty.md | 69 +++++++++++++++++++++ Kosiky.md | 76 +++++++++++++++++++++++ Logovani.md | 63 +++++++++++++++++++ Tracking.md | 67 ++++++++++++++++++++ 14 files changed, 1101 insertions(+) create mode 100644 API-reference.md create mode 100644 Admin.md create mode 100644 Architektura.md create mode 100644 Automations.md create mode 100644 Email-kampane.md create mode 100644 Home.md create mode 100644 Instalace.md create mode 100644 Integrace-Eshop-API.md create mode 100644 Integrace.md create mode 100644 Jobs-Cron.md create mode 100644 Kontakty.md create mode 100644 Kosiky.md create mode 100644 Logovani.md create mode 100644 Tracking.md diff --git a/API-reference.md b/API-reference.md new file mode 100644 index 0000000..06f13f3 --- /dev/null +++ b/API-reference.md @@ -0,0 +1,121 @@ +# API Reference + +Všechny API endpointy vyžadují `X-Site-Key` header nebo `?site_key=` parametr (ověření přes `ResolveTrackingSite` middleware). + +--- + +## Browser Tracking API + +Endpointy používané `collect.js` z prohlížeče. + +### `POST /api/collect` +Obecný tracking event (page_view, newsletter_subscribe, gallery_config_submit...). + +```json +{ + "visitor_id": "string (required)", + "event": "string (required)", + "payload": {}, + "occurred_at": "ISO 8601" +} +``` + +### `POST /api/identify` +Identifikace návštěvníka (propojení visitor_id s emailem nebo ftclid tokenem). + +```json +{ + "visitor_id": "string", + "email": "string (nebo ftclid)", + "ftclid": "string", + "name": "string", + "user_id": "string" +} +``` + +### `GET /api/me` +Vrátí informace o aktuálním návštěvníkovi (kontakt propojený s visitor_id). + +### `POST /api/cart` +Upsert košíku z prohlížeče. + +```json +{ + "visitor_id": "string", + "event": "cart_upsert", + "payload": { + "cart_id": null, + "cart": { + "items": [{ "code": "...", "qty": 1, "unit_no_tax": 99 }], + "totals": { "currency": "CZK", "total_no_tax": 99 } + } + } +} +``` + +### `POST /api/cart/complete` +Dokončení košíku z prohlížeče. + +```json +{ + "visitor_id": "string", + "event": "cart_completed", + "payload": { + "cart_id": 42, + "order_id": "ORD-001", + "total": 198.00, + "currency": "CZK", + "checkout": { + "shipping_no_tax": 89, + "shipping_label": "PPL", + "cod_fee_no_tax": 29 + } + } +} +``` + +--- + +## Eshop API (server-to-server) + +Viz [Integrace Eshop API](Integrace-Eshop-API). + +### `POST /api/eshop/cart` +Košík z eshopu – párování podle emailu. Logováno do `eshop-YYYY-MM-DD.log`. + +### `POST /api/eshop/order` +Objednávka z eshopu – párování podle emailu. + +--- + +## Integrace Webhooky + +### `POST /api/integrations/smartemailing/webhook` +Příjem webhooků ze SmartEmailing (odhlášení, události). + +--- + +## Veřejné endpointy + +### `GET /feeds/products.xml` +XML produktový feed (`ProductFeedController`). + +### `GET /e/o/{token}` +Email open tracking pixel. + +### `GET /email/u/{token}` +Unsubscribe stránka z emailu. + +### `GET /unsubscribe/{contact}` *(signed URL)* +Globální odhlášení kontaktu. + +--- + +## Chybové kódy + +| HTTP | Situace | +|------|---------| +| 401 | Chybějící nebo neplatný Site Key | +| 404 | Záznam nenalezen (cart_not_found...) | +| 422 | Validační chyba – chybí povinné pole | +| 500 | Serverová chyba | diff --git a/Admin.md b/Admin.md new file mode 100644 index 0000000..a56e654 --- /dev/null +++ b/Admin.md @@ -0,0 +1,47 @@ +# Admin rozhraní + +Admin UI je dostupné po přihlášení pod doménou `APP_MAIN_DOMAIN`. Postaveno na **Livewire 3** + **Tailwind CSS**. + +## Navigace + +| URL | Livewire komponenta / Controller | Popis | +|-----|----------------------------------|-------| +| `/dashboard` | `Admin\Dashboard` | Hlavní dashboard | +| `/admin/live` | – | Live tracking | +| `/admin/liveTracking` | `Admin\LiveTracking` | Real-time přehled návštěvníků | +| `/admin/tracking-sites/{site}` | `Admin\TrackingSiteShow` | Nastavení tracking webu | +| `/admin/siteCarts/{siteId}` | `Admin\CartsTable` | Přehled košíků | +| `/admin/contacts` | `Admin\ContactsIndex` | Seznam kontaktů | +| `/admin/contacts/{contact}` | `Admin\ContactShow` | Detail kontaktu | +| `/admin/contacts/import` | `Admin\ContactsImportCsv` | Import CSV | +| `/admin/companies` | `Admin\CompaniesIndex` | Seznam firem | +| `/admin/companies/{company}` | `Admin\CompanyShow` | Detail firmy | +| `/admin/phonebooks` | `Admin\PhonebooksBrowser` | Telefonní seznamy | +| `/admin/funnel/segments` | `Admin\SegmentsIndex` | Správa segmentů | +| `/admin/funnel/segments/{id}/columns` | `Admin\SegmentColumns` | Sloupce segmentu | +| `/admin/funnel/segments/{id}/rules` | `Admin\SegmentRules` | Pravidla segmentu | +| `/admin/funnel/segments/{id}/board` | `Admin\SegmentBoard` | Kanban board kontaktů | +| `/admin/funnel/actions` | `Admin\ActionsIndex` | Seznam akcí | +| `/admin/funnel/action-runs` | `Admin\ActionRunsIndex` | Historie spuštění akcí | +| `/admin/funnel/automations` | `Admin\FunnelAutomations` | Editor automací | +| `/admin/automations/{siteId}` | `Admin\AutomationsEditor` | Editor pro konkrétní web | +| `/admin/funnel/email-campaigns` | `Admin\EmailCampaignsIndex` | Seznam kampaní | +| `/admin/funnel/email-campaigns/{id}` | `Admin\EmailCampaignShow` | Detail kampaně | +| `/admin/funnel/email-categories` | `Admin\EmailCategories` | Kategorie emailů | +| `/admin/email-templates/editor/{id}` | `AdminEmailTemplateEditorController` | Editor šablon | +| `/admin/integrations` | `Admin\IntegrationsIndex` | Přehled integrací | +| `/admin/funnel/integrations/smtp` | `Admin\IntegrationsSmtp` | SMTP nastavení | +| `/admin/mostVisitedCategories` | `Admin\MostVisitedCategories` | Nejnavštěvovanější kategorie | +| `/admin/userFlow` | `Admin\UserFlow` | Tok uživatelů | +| `/admin/configuratorFlow` | `Admin\ConfiguratorFlow` | Tok konfigurátoru | +| `/admin/products/clicks` | `Admin\ProductClicks` | Kliky na produkty | +| `/admin/vario/doklady` | `Admin\VarioDokladyController` | Vario doklady | +| `/admin/ai-assistant` | `Admin\AdminAiConsole` | AI asistent (OpenAI) | + +## Přihlášení a týmy + +Autentizace přes **Laravel Jetstream**. Jeden uživatel může být členem více týmů (`Membership` model). Každý tým má vlastní data (multi-tenancy přes `team_id`). + +## Notifikace + +Interní notifikace přes `Notification` model. Zobrazeny v `Admin\NotificationsPanel`. diff --git a/Architektura.md b/Architektura.md new file mode 100644 index 0000000..6135c57 --- /dev/null +++ b/Architektura.md @@ -0,0 +1,69 @@ +# Architektura + +## Technologický stack + +| Vrstva | Technologie | +|--------|-------------| +| Framework | Laravel 12, PHP 8.2+ | +| Frontend | Livewire 3, Tailwind CSS, Vite | +| Autentizace | Laravel Jetstream + Sanctum | +| Queue | Laravel Queue (databáze nebo Redis) | +| Debugging | Laravel Telescope, Laravel Pulse | +| Email | SMTP / Microsoft Graph | +| Externí integrace | SmartEmailing API, Vario SOAP, PBX | + +## Adresářová struktura + +``` +app/ +├── Console/Commands/ # Artisan příkazy (sync, rebuild, backfill...) +├── Http/ +│ ├── Controllers/ +│ │ ├── Api/Tracking/ # Browser tracking endpointy +│ │ ├── Api/Eshop/ # Server-to-server eshop endpointy +│ │ └── Admin/ # Web admin controllery +│ └── Middleware/ +│ ├── ResolveTrackingSite # Ověření X-Site-Key +│ ├── CorsForTracking # CORS pro tracking API +│ └── LogEshopRequest # Logování eshop requestů +├── Jobs/ # Background joby (queue) +├── Livewire/Admin/ # Livewire komponenty admin UI +├── Models/ # Eloquent modely +├── Services/ +│ ├── Actions/ # ActionRunner + drivery akcí +│ ├── Ai/ # OpenAI integrace +│ ├── Funnel/ # RulesEngine, FunnelAssigner +│ ├── GeoIP/ # Geo-lookup +│ ├── Ingest/ # IngestEvent pipeline +│ ├── Integrations/ # SmartEmailing klient +│ ├── Tracking/ # IdentityLinker, CategoryTree, BotDetector... +│ └── Vario/ # Vario SOAP sync +public/ +└── collect.js # Tracking skript pro weby/eshopy +routes/ +├── api.php # API endpointy +└── web.php # Admin web rozhraní +``` + +## Klíčové modely + +| Model | Tabulka | Popis | +|-------|---------|-------| +| `Contact` | `contacts` | Identifikovaný zákazník/návštěvník | +| `FunnelCart` | `funnel_carts` | Nákupní košík | +| `FunnelEvent` | `funnel_events` | Každá trackovací událost | +| `FunnelRule` | `funnel_rules` | Pravidlo automace (trigger + podmínky) | +| `FunnelAction` | `funnel_actions` | Akce spouštěná pravidlem | +| `FunnelState` | `funnel_states` | Aktuální stav kontaktu v segmentu/sloupci | +| `Segment` | `segments` | Skupina kontaktů s vlastními pravidly | +| `FunnelColumn` | `funnel_columns` | Sloupec (stav) v segmentu | +| `EmailCampaign` | `email_campaigns` | Emailová kampaň | +| `EmailCampaignSend` | `email_campaign_sends` | Jeden plánovaný email send | +| `EmailTemplate` | `email_templates` | Šablona emailu | +| `TrackingSite` | `tracking_sites` | Web/eshop identifikovaný Site Key | +| `TrackingProduct` | `tracking_products` | Produkt zaznamenaný z košíků | +| `VisitorIdentity` | `visitor_identities` | Mapping visitor_id → contact_id | + +## Multi-tenancy + +Aplikace je multi-tenant přes `team_id`. Každý tým má vlastní kontakty, segmenty, kampaně a tracking sites. Autentizace týmů probíhá přes Laravel Jetstream. diff --git a/Automations.md b/Automations.md new file mode 100644 index 0000000..d1ec589 --- /dev/null +++ b/Automations.md @@ -0,0 +1,98 @@ +# Automations a Funnel + +## Přehled + +Automační systém reaguje na eventy a pohybuje kontakty mezi stavy (sloupci) v segmentu. Při přechodu do nového sloupce se spouštějí akce (email, webhook, tag...). + +``` +FunnelEvent + │ + ▼ +RulesEngine.evaluate() + │ + ├─ Projde FunnelRule záznamy pro aktivní segmenty kontaktu + ├─ Vyhodnotí podmínky (when_event, conditions) + └─ Pokud pravidlo platí → FunnelState přesune kontakt do target_column + │ + ▼ + RunFunnelActionsJob + │ + ▼ + FunnelAction → ActionRunner → Driver +``` + +## Klíčové třídy + +| Třída | Popis | +|-------|-------| +| `RulesEngine` | Vyhodnocuje pravidla pro daný event a kontakt | +| `FunnelAssigner` | Přiřazuje kontakt do segmentu (vytváří FunnelState) | +| `ActionRunner` | Spouští akce pro daný FunnelState | +| `DriverFactory` | Vrací správný driver podle typu akce | + +## Modely + +### FunnelRule +Pravidlo triggeru a přechodu. + +| Pole | Popis | +|------|-------| +| `segment_id` | Ke kterému segmentu patří | +| `when_event` | Event který pravidlo triggeruje (např. `cart_upsert`) | +| `conditions` | JSON podmínky (payload hodnoty, tagy...) | +| `target_column_id` | Cílový sloupec po splnění pravidla | +| `apply_once` | Aplikovat jen jednou na kontakt | +| `priority` | Pořadí vyhodnocení | + +### FunnelAction +Akce spouštěná při přechodu do sloupce. + +| Pole | Popis | +|------|-------| +| `segment_id` | Příslušný segment | +| `column_id` | Sloupec který spouští akci | +| `trigger` | `on_enter` / `on_exit` / `scheduled` | +| `type` | Typ akce (viz níže) | +| `config` | JSON konfigurace akce | +| `throttle` | Omezení frekvence spuštění | + +### FunnelState +Aktuální pozice kontaktu v segmentu. + +| Pole | Popis | +|------|-------| +| `contact_id` | Kontakt | +| `segment_id` | Segment | +| `column_id` | Aktuální sloupec | + +## Typy akcí (Drivers) + +| Typ | Driver | Popis | +|-----|--------|-------| +| `send_mail` | `SendMailDriver` | Odeslání emailu | +| `set_tag` | `SetTagDriver` | Přidání tagu kontaktu | +| `set_flag` | `SetFlagDriver` | Nastavení příznaku | +| `move_to_column` | `MoveToColumnDriver` | Přesun do jiného sloupce | +| `webhook` | `WebhookDriver` | HTTP webhook | +| `slack` | `SlackDriver` | Slack notifikace | +| `notify` | `NotifyDriver` | Interní notifikace | +| `internal_note` | `InternalNoteDriver` | Poznámka ke kontaktu | +| `smartemailing_sync` | `SmartEmailingAddToListDriver` | Přidání do SmartEmailing listu | +| `smartemailing_add_tag` | `SmartEmailingAddTagDriver` | Tag v SmartEmailing | +| `smartemailing_unsubscribe` | `SmartEmailingUnsubscribeDriver` | Odhlášení v SmartEmailing | + +## Placeholders v akcích + +`PlaceholderReplacer` nahrazuje `{{contact.email}}`, `{{contact.name}}` apod. v textech akcí. + +## Replay + +Command `FunnelReplaySegment` / job `ReplaySegmentAutomationsJob` přehraje eventy pro všechny kontakty v segmentu – užitečné při změně pravidel. + +## Inaktivita + +Command `FunnelEvaluateInactivity` / job `FunnelInactivityJob` spouští pravidla pro kontakty které dlouho nevykazovaly aktivitu. + +## Naplánované akce + +`FunnelScheduledAction` – akce naplánované do budoucnosti (např. "pošli email za 3 dny"). Zpracovává `RunScheduledFunnelActionJob`. diff --git a/Email-kampane.md b/Email-kampane.md new file mode 100644 index 0000000..4cdcc6e --- /dev/null +++ b/Email-kampane.md @@ -0,0 +1,67 @@ +# Email kampaně + +## Přehled + +Systém podporuje hromadné emailové kampaně s plánováním, schvalováním, sledováním otevření a kliků. + +## Klíčové modely + +| Model | Tabulka | Popis | +|-------|---------|-------| +| `EmailCampaign` | `email_campaigns` | Definice kampaně | +| `EmailCampaignSend` | `email_campaign_sends` | Jeden naplánovaný email jednomu příjemci | +| `EmailTemplate` | `email_templates` | HTML šablona emailu | +| `EmailCategory` | `email_categories` | Kategorie (pro odhlašování per kategorie) | +| `EmailCategoryUnsubscribe` | – | Odhlášení z konkrétní kategorie | +| `ContactCampaignStat` | – | Statistiky kampaně per kontakt | +| `CampaignEventStat` | – | Agregované statistiky eventů | +| `CampaignCartDailyStat` | – | Denní statistiky košíků per kampaň | + +## Životní cyklus kampaně + +``` +1. Vytvoření kampaně (EmailCampaign) +2. Výběr segmentu + šablony +3. Naplánování (schedule) nebo odeslání ihned (send-now) +4. PrepareEmailCampaignSendsJob → vytvoří EmailCampaignSend záznamy +5. Schválení (approval workflow) +6. BatchSendEmailCampaignSendsJob → SendEmailCampaignSendJob → odeslání +7. Tracking otevření (pixel /e/o/{token}) +8. Tracking kliků (ftclid parametr v odkazech) +``` + +## Odesílání + +Kampaně odesílá command `SendDueEmailCampaigns` (cron). Selhat může přes `RetryFailedEmailCampaignSends`. + +Podporované mail drivery: +- **SMTP** – standardní Laravel mailer +- **Microsoft Graph** – `MicrosoftGraphMailService` (přes Microsoft 365 API) + +## Šablony + +Editor šablon je dostupný na `/admin/email-templates/editor/{id}`. Šablony podporují: +- HTML s inline CSS (`tijsverkoyen/css-to-inline-styles`) +- Placeholdery (`{{contact.email}}`, `{{contact.name}}` apod.) +- Unsubscribe link + +## Tracking + +- **Otevření** – 1×1px tracking pixel `/e/o/{token}` → `EmailOpenTrackingController` +- **Kliky** – `ftclid` parametr v odkazech; při kliknutí `collect.js` automaticky identifikuje návštěvníka a propojí ho s kontaktem + +## Schvalování + +Před odesláním může být vyžadováno schválení přes `EmailSendApprovalController`: +- `GET /email-sends/pending` – seznam čekajících +- `POST /email-sends/{send}/approve` – schválení +- `POST /email-sends/{send}/reject` – zamítnutí + +## Statistiky + +Rebuild statistik: +```bash +php artisan rebuild:campaign-event-stats +php artisan rebuild:campaign-cart-daily-stats +php artisan rebuild:contact-campaign-stats +``` diff --git a/Home.md b/Home.md new file mode 100644 index 0000000..64a319a --- /dev/null +++ b/Home.md @@ -0,0 +1,43 @@ +# Home – Funnel-1 Wiki + +**Funnel-1** je interní marketing automation & CRM platforma postavená na Laravel 12. Slouží ke sledování chování návštěvníků na webech a eshopech, správě kontaktů, segmentaci, automatizaci emailových kampaní a integraci s externími systémy. + +--- + +## Obsah wiki + +| Stránka | Popis | +|-----------------------------------------------|-------| +| [Architektura.](Architektura.md) | Přehled technologií, vrstev a klíčových modulů | +| [Instalace a spuštění](Instalace.md) | Jak projekt lokálně rozběhnout | +| [Tracking systém](Tracking.md) | Jak funguje sledování návštěvníků přes `collect.js` | +| [Košíky](Kosiky.md) | FunnelCart – sledování a konverze košíků | +| [Integrace eshop API](Integrace-Eshop-API.md) | Server-to-server API pro eshopy (email-based) | +| [Kontakty a CRM](Kontakty.md) | Contact model, identifikace, segmenty | +| [Automations a Funnel](Automations.md) | RulesEngine, FunnelRule, FunnelAction | +| [Email kampaně](Email-kampane.md) | Tvorba, odesílání a tracking emailů | +| [Integrace](Integrace.md) | SmartEmailing, Vario, PBX, Microsoft Graph | +| [API reference](API-reference.md) | Přehled všech API endpointů | +| [Admin rozhraní](Admin.md) | Přehled Livewire admin sekcí | +| [Jobs a Cron](Jobs-Cron.md) | Background joby a scheduled tasky | +| [Logování](Logovani.md) | Log kanály a co se kde loguje | + +--- + +## Rychlý přehled + +``` +Eshop / web + │ + ├─ collect.js (browser tracking) + │ └─ POST /api/collect, /api/cart, /api/identify + │ + └─ Server-to-server (PHP backend eshopu) + └─ POST /api/eshop/cart, /api/eshop/order + │ + ▼ + [ FunnelEvent ] ──► [ RulesEngine ] ──► [ FunnelAction ] + │ + ▼ + [ Contact ] ──► [ Segment ] ──► [ EmailCampaign ] +``` diff --git a/Instalace.md b/Instalace.md new file mode 100644 index 0000000..34e2f7a --- /dev/null +++ b/Instalace.md @@ -0,0 +1,85 @@ +# Instalace a spuštění + +## Požadavky + +- PHP 8.2+ +- Composer +- Node.js 18+ +- MySQL / PostgreSQL +- (volitelně) Redis pro queue + +## Rychlá instalace + +```bash +composer install +cp .env.example .env +php artisan key:generate +php artisan migrate +npm install +npm run build +``` + +Nebo přes composer script: + +```bash +composer setup +``` + +## Vývoj (dev server) + +Spustí paralelně: PHP server, queue worker, Pail log viewer a Vite hot-reload: + +```bash +composer dev +``` + +## Klíčové .env proměnné + +```env +APP_URL=https://app.domena.cz +APP_MAIN_DOMAIN=app.domena.cz +APP_TOR_UNSUB_DOMAIN=unsub.domena.cz + +DB_CONNECTION=mysql +DB_HOST=127.0.0.1 +DB_DATABASE=funnel + +QUEUE_CONNECTION=database # nebo redis + +# Email +MAIL_MAILER=smtp +MAIL_HOST=... +MAIL_FROM_ADDRESS=... + +# Microsoft Graph (alternativní mailer) +MICROSOFT_GRAPH_TENANT_ID=... +MICROSOFT_GRAPH_CLIENT_ID=... +MICROSOFT_GRAPH_CLIENT_SECRET=... + +# SmartEmailing +SMARTEMAILING_API_URL=... +SMARTEMAILING_API_KEY=... + +# GeoIP +GEOIP_DATABASE_PATH=... + +# Logging +LOG_CHANNEL=stack +LOG_LEVEL=info +``` + +## Spuštění queue workera + +```bash +php artisan queue:listen --tries=1 +# nebo +php artisan queue:work +``` + +## Scheduled tasks (cron) + +Přidat do crontabu serveru: + +```cron +* * * * * cd /path/to/project && php artisan schedule:run >> /dev/null 2>&1 +``` diff --git a/Integrace-Eshop-API.md b/Integrace-Eshop-API.md new file mode 100644 index 0000000..6a64751 --- /dev/null +++ b/Integrace-Eshop-API.md @@ -0,0 +1,138 @@ +# Integrace Eshop API + +Server-to-server integrace pro eshopy. Eshop posílá data přímo ze svého backendu – párování probíhá podle e-mailu zákazníka, **žádný browser tracking není potřeba**. + +Podrobná dokumentace pro programátora eshopu je v [`docs/integrace-eshop.md`](../docs/integrace-eshop.md). + +--- + +## Autentizace + +Každý request musí obsahovat Site Key: + +``` +X-Site-Key: +# nebo +?site_key= +``` + +--- + +## Endpointy + +### `POST /api/eshop/cart` – Stav košíku + +Volejte při každé změně košíku. Párování probíhá podle `email`. + +```json +{ + "email": "zakaznik@example.cz", + "currency": "CZK", + "total_no_tax": 198.00, + "cart_id": null, + "items": [ + { + "code": "PROD-001", + "name": "Název produktu", + "qty": 2, + "unit_no_tax": 99.00, + "total_no_tax": 198.00, + "url": "https://eshop.cz/produkt" + } + ] +} +``` + +**Odpověď:** +```json +{ "ok": true, "cart_id": 42, "status": "open", "changed": true } +``` + +Logika controlleru `EshopCartController`: +1. Najde nebo vytvoří `Contact` podle emailu +2. Hledá poslední otevřený košík pro daný kontakt +3. Porovná obsah (hash z items + currency + total) – pokud se nezměnil, jen obnoví `last_seen_at` +4. Uloží/aktualizuje `FunnelCart`, upsertuje `TrackingProduct` záznamy +5. Zapiše `FunnelEvent` (event `cart_upsert`, source `eshop`) + +--- + +### `POST /api/eshop/order` – Dokončení objednávky + +Volejte jednorázově při úspěšném dokončení. + +```json +{ + "email": "zakaznik@example.cz", + "cart_id": 42, + "order_id": "ORD-2026-0042", + "total_no_tax": 198.00, + "currency": "CZK", + "checkout": { + "shipping_no_tax": 89.00, + "shipping_label": "PPL", + "cod_fee_no_tax": 29.00 + } +} +``` + +**Odpověď:** +```json +{ "ok": true, "cart_id": 42, "order_id": "ORD-2026-0042", "status": "converted" } +``` + +Logika controlleru `EshopOrderController`: +1. Najde kontakt podle emailu +2. Hledá košík – podle `cart_id`, nebo poslední `open`/`empty` pro daný kontakt +3. Pokud košík nenajde → `404 cart_not_found` +4. Uloží data objednávky, status → `converted` +5. Zapiše `FunnelEvent` (event `cart_completed`, source `eshop`) + +--- + +## Logování + +Všechny requesty na `/api/eshop/*` jsou logovány do `storage/logs/eshop-YYYY-MM-DD.log` přes middleware `LogEshopRequest`. E-mail je maskován (`za***@example.cz`). + +--- + +## Chybové stavy + +| HTTP | Kód | Řešení | +|------|-----|--------| +| 401 | – | Chybný nebo chybějící Site Key | +| 422 | – | Chybí povinné pole | +| 404 | `cart_not_found` | Žádný otevřený košík pro daný email | + +--- + +## PHP příklad + +```php +// Při změně košíku +$res = Http::withHeaders(['X-Site-Key' => config('tracking.site_key')]) + ->post('https://app.domena.cz/api/eshop/cart', [ + 'email' => $customer->email, + 'currency' => 'CZK', + 'total_no_tax' => $cart->totalNoTax(), + 'cart_id' => session('ft_cart_id'), + 'items' => $cart->items->map(fn($i) => [ + 'code' => $i->sku, + 'name' => $i->name, + 'qty' => $i->qty, + 'unit_no_tax' => $i->unitPriceNoTax, + ])->toArray(), + ]); + +session(['ft_cart_id' => $res->json('cart_id')]); + +// Při dokončení +Http::withHeaders(['X-Site-Key' => config('tracking.site_key')]) + ->post('https://app.domena.cz/api/eshop/order', [ + 'email' => $customer->email, + 'cart_id' => session('ft_cart_id'), + 'order_id' => $order->id, + 'total_no_tax' => $order->totalNoTax(), + 'currency' => 'CZK', + ]); +``` diff --git a/Integrace.md b/Integrace.md new file mode 100644 index 0000000..1189a60 --- /dev/null +++ b/Integrace.md @@ -0,0 +1,83 @@ +# Integrace + +## SmartEmailing + +CRM/mailing systém SmartEmailing. + +**Klient:** `App\Services\Integrations\SmartEmailingClient` + +**Synchronizace kontaktů:** `SmartEmailingContactsSyncService` + +**Akce z automací:** +- `SmartEmailingAddToListDriver` – přidání kontaktu do listu +- `SmartEmailingAddTagDriver` – přidání tagu +- `SmartEmailingUnsubscribeDriver` – odhlášení kontaktu + +**Webhook příjem:** `POST /api/integrations/smartemailing/webhook` → `SmartEmailingWebhookController` + +**Healthcheck:** `SmartEmailingHealthcheck` – kontrola dostupnosti API + +--- + +## Vario ERP + +ERP systém pro správu firem, kontaktů, kategorií a produktů. Komunikace přes **SOAP**. + +**Klient:** `App\Services\Vario\VarioSoapClient` + +**Sync commandy:** + +| Command | Popis | +|---------|-------| +| `vario:sync` | Master sync (spouští ostatní) | +| `vario:sync-contacts` | Sync kontaktů | +| `vario:sync-companies` | Sync firem | +| `vario:sync-categories` | Sync kategorií produktů | +| `vario:sync-phonebooks` | Sync telefonních seznamů | +| `vario:sync-product` | Sync jednotlivého produktu | + +**Klíčové třídy:** +- `VarioImporter` – orchestrace importu +- `VarioContactMapper` / `VarioCompanyMapper` – mapování polí +- `VarioSnapshotBuilder` – snapshot dat pro porovnání změn +- `VarioDatapackParser` – parsování datapaku ze SOAP odpovědi + +**Admin UI:** `/admin/vario/doklady` – prohlížení a parsování Vario dokladů + +--- + +## PBX (telefonní ústředna) + +**Klient:** `App\Services\Pbx\PbxClient` + +**Telefonní seznamy:** `Phonebook`, `PhonebookEntry`, `PhonebookContact` modely + +**Sync:** `PushPhonebookToPbxJob` – nahraje telefonní seznam do PBX + +**Admin UI:** `/admin/phonebooks` – prohlížeč telefonních seznamů + +--- + +## Microsoft Graph (Email) + +Alternativní odesílač emailů přes Microsoft 365 API. + +**Service:** `App\Services\MicrosoftGraph\MicrosoftGraphMailService` + +**Konfigurace v .env:** +```env +MICROSOFT_GRAPH_TENANT_ID= +MICROSOFT_GRAPH_CLIENT_ID= +MICROSOFT_GRAPH_CLIENT_SECRET= +MICROSOFT_GRAPH_FROM_ADDRESS= +``` + +--- + +## Logování integrací + +Všechny integrace logují výsledky do `IntegrationLog` (`integration_logs` tabulka). Přehled dostupný v admin UI na `/admin/integrations/logs/{log}`. + +`IntegrationLogger` je helper pro jednotné logování úspěchů a chyb. + +Stav integrace (poslední sync, chyby) ukládá `IntegrationState` model. diff --git a/Jobs-Cron.md b/Jobs-Cron.md new file mode 100644 index 0000000..78a1c37 --- /dev/null +++ b/Jobs-Cron.md @@ -0,0 +1,75 @@ +# Jobs a Cron + +## Artisan Commands (Scheduled Tasks) + +Příkazy spouštěné přes `php artisan schedule:run` (cron každou minutu). + +| Command | Popis | +|---------|-------| +| `SendDueEmailCampaigns` | Odesílá naplánované emailové kampaně | +| `RetryFailedEmailCampaignSends` | Opakuje selhaná odeslání emailů | +| `ExpireFunnelCarts` | Označí staré otevřené košíky jako `expired` | +| `FunnelEvaluateInactivity` | Vyhodnotí pravidla pro neaktivní kontakty | +| `FunnelReplaySegment` | Přehraje eventy pro kontakty v segmentu | +| `VarioSync` | Master sync z Vario ERP | +| `VarioSyncContacts` | Sync kontaktů z Vario | +| `VarioSyncCompanies` | Sync firem z Vario | +| `VarioSyncCategories` | Sync kategorií z Vario | +| `VarioSyncPhonebooks` | Sync telefonních seznamů z Vario | +| `VarioSyncProduct` | Sync jednoho produktu z Vario | +| `SyncFunnelEventTypes` | Synchronizuje typy funnel eventů | +| `DashboardIngestEvents` | Ingestuje events pro dashboard | +| `DashboardBackfill` | Backfill dat pro dashboard | + +## Rebuild Commands (manuální) + +| Command | Popis | +|---------|-------| +| `RebuildCampaignCartDailyStats` | Přepočítá denní košíkové statistiky per kampaň | +| `RebuildProductGroupCartDailyStats` | Přepočítá košíkové statistiky per produktová skupina | +| `RebuildCampaignEventStats` | Přepočítá event statistiky kampaní | +| `RebuildContactCampaignStats` | Přepočítá statistiky kampaní per kontakt | +| `BackfillFunnelCarts` | Backfill chybějících dat košíků | +| `BackfillFunnelEventContacts` | Backfill `contact_id` u starých eventů | +| `BackfillFunnelEventBots` | Backfill bot flag u starých eventů | +| `GenerateDataBuilderConfig` | Generuje konfiguraci pro DataBuilder (AI) | + +## Queue Jobs + +Joby zpracovávané na pozadí přes Laravel Queue. + +| Job | Popis | +|-----|-------| +| `RunFunnelActionsJob` | Spustí akce pro daný FunnelState | +| `RunFunnelActionByIdJob` | Spustí konkrétní akci | +| `RunActionByIdJob` | Spustí akci podle ID | +| `RunScheduledFunnelActionJob` | Spustí naplánovanou akci | +| `FunnelInactivityJob` | Zpracuje inaktivitu kontaktu | +| `ReplaySegmentAutomationsJob` | Přehraje automace pro segment | +| `ExpireFunnelCartsJob` | Expiruje košíky | +| `PrepareEmailCampaignSendsJob` | Připraví send záznamy pro kampaň | +| `PrepareEmailCampaignSendJob` | Připraví jeden send | +| `BatchSendEmailCampaignSendsJob` | Hromadné odesílání emailů | +| `SendEmailCampaignSendJob` | Odeslání jednoho emailu | +| `RetryFailedEmailCampaignSendsJob` | Retry selhaných emailů | +| `QueueEmailCampaignSendsFromCsvJob` | Načte příjemce z CSV do fronty | +| `SyncVarioContactsJob` | Async sync Vario kontaktů | +| `SyncVarioProductCategoryJob` | Async sync Vario kategorií | +| `VarioSyncJob` | Async master Vario sync | +| `VarioHealthcheckJob` | Healthcheck Vario připojení | +| `PushPhonebookToPbxJob` | Nahrání telefonního seznamu do PBX | +| `BackfillFunnelEventContactsJob` | Async backfill contact_id | +| `RebuildCampaignEventStatsJob` | Async rebuild statistik | +| `RebuildCampaignCartDailyStatsJob` | Async rebuild košíkových statistik | +| `RebuildContactCampaignStatsJob` | Async rebuild statistik per kontakt | +| `RebuildProductGroupCartDailyStatsJob` | Async rebuild produktových statistik | + +## Spuštění queue workera + +```bash +# vývoj +php artisan queue:listen --tries=1 + +# produkce +php artisan queue:work --tries=3 --sleep=3 --timeout=90 +``` diff --git a/Kontakty.md b/Kontakty.md new file mode 100644 index 0000000..c836a92 --- /dev/null +++ b/Kontakty.md @@ -0,0 +1,69 @@ +# Kontakty a CRM + +## Model Contact + +`App\Models\Contact` – tabulka `contacts` + +| Sloupec | Typ | Popis | +|---------|-----|-------| +| `id` | bigint | PK | +| `team_id` | bigint | Příslušnost k týmu | +| `email` | string | E-mail (unikátní per team) | +| `name` | string | Celé jméno | +| `phone` | string | Telefon | +| `source` | string | Zdroj (`web`, `eshop`, `import`...) | +| `user_id` | string | ID zákazníka v externím systému | +| `tags` | json | Pole tagů | +| `flags` | json | Slovník příznaků (user_id, kampaně...) | +| `traits` | json | Vlastnosti kontaktu | +| `meta` | json | Libovolná metadata | +| `identify_token` | string | Token pro identifikaci z emailového odkazu | +| `last_activity_at` | timestamp | Poslední aktivita | +| `email_opt_out_at` | timestamp | Datum odhlášení z emailů | +| `internal` | boolean | Interní kontakt (nezapočítává se do statistik) | +| `smartemailing_id` | int | ID v SmartEmailing | +| `vario_ids` | json | ID záznamu v Vario ERP | + +## Identifikace (IdentityLinker) + +`App\Services\Tracking\IdentityLinker` zajišťuje propojení anonymního `visitor_id` s `Contact`: + +``` +visitor_id ──► visitor_identities ──► contact_id +``` + +### Metody + +- **`identify($teamId, $data)`** – párování podle emailu; vytvoří kontakt pokud neexistuje; backfilluje `contact_id` ke starším eventům +- **`identifyByToken($teamId, $data)`** – párování přes `ftclid` (click token z emailové kampaně); dohledá `EmailCampaignSend` → `Contact` +- **`contactIdForVisitor($teamId, $visitorId)`** – rychlý lookup mapping + +## Segmenty + +`Segment` sdružuje kontakty do skupiny. Každý segment má: +- **`FunnelColumn`** – sloupce (stavy), ve kterých se kontakt nachází (Kanban-style) +- **`FunnelRule`** – pravidla pro přechod mezi sloupci +- **`FunnelState`** – aktuální stav konkrétního kontaktu v segmentu + +## Tagy + +Kontakty lze tagovat. Tagy jsou uloženy jako JSON pole. Přidávání tagů probíhá přes: +- `SetTagDriver` (akce automace) +- `SmartEmailingAddTagDriver` (sync do SmartEmailing) +- Automaticky při kliknutí na kampaňový email (`email_campaign:{id}`) + +## Import kontaktů + +- **CSV import** – `ContactsImportCsv` Livewire + `JsonContactImporter` +- **Vario sync** – `VarioSyncContacts` command / `SyncVarioContactsJob` +- **SmartEmailing webhook** – `SmartEmailingWebhookController` + +## Firmy (Company) + +Kontakty mohou být přiřazeny k firmám (`Company` model). Firmy se synchronizují z Vario ERP přes `VarioSyncCompanies`. + +## Odhlášení z emailů + +- `email_opt_out_at` – globální odhlášení +- `EmailCategoryUnsubscribe` – odhlášení z konkrétní kategorie emailů +- Unsubscribe link vede na `UnsubscribeController` nebo `EmailUnsubscribeController` (TOR-friendly doména) diff --git a/Kosiky.md b/Kosiky.md new file mode 100644 index 0000000..8cd27aa --- /dev/null +++ b/Kosiky.md @@ -0,0 +1,76 @@ +# Košíky (FunnelCart) + +## Model + +`App\Models\FunnelCart` – tabulka `funnel_carts` + +| Sloupec | Typ | Popis | +|---------|-----|-------| +| `id` | bigint | PK | +| `team_id` | bigint | Příslušnost k týmu | +| `tracking_site_id` | bigint | Příslušný web/eshop | +| `visitor_id` | string | ID anonymního návštěvníka | +| `contact_id` | bigint | Propojený kontakt (nullable) | +| `status` | string | `open` / `empty` / `converted` / `expired` | +| `items` | json | Pole položek košíku | +| `currency` | string | ISO kód měny | +| `total_no_tax` | decimal | Celková suma bez DPH | +| `shipping_fee_no_tax` | decimal | Cena dopravy bez DPH | +| `cod_fee_no_tax` | decimal | Poplatek za dobírku bez DPH | +| `shipping_label` | string | Název dopravce | +| `first_seen_at` | timestamp | Kdy košík vznikl | +| `last_seen_at` | timestamp | Poslední aktivita | +| `completed_at` | timestamp | Kdy byl dokončen | +| `is_completed` | boolean | Flag dokončení | +| `is_expired` | boolean | Flag expirování | + +## Formát položky košíku (`items[]`) + +```json +{ + "code": "PROD-001", + "name": "Název produktu", + "qty": 2, + "unit_no_tax": 99.00, + "total_no_tax": 198.00, + "url": "https://eshop.cz/produkt", + "package": {} +} +``` + +## Stavy košíku + +``` +open – košík má položky, zákazník nakupuje +empty – košík byl vyprázdněn +converted – objednávka dokončena +expired – košík expiroval (job ExpireFunnelCarts) +``` + +## Browser tracking (collect.js) + +Košíky z prohlížeče jdou přes dva endpointy: + +- **`POST /api/cart`** – `CartController` – upsert košíku (event `cart_upsert`) +- **`POST /api/cart/complete`** – `CartCompleteController` – dokončení (event `cart_completed`) + +Deduplikace přes `external_event_id` ve formátu `cart:{id}:cart_upsert:{hash}` kde hash je SHA1 z obsahu (kódy produktů, množství, ceny, měna). + +## Server-to-server (eshop API) + +Viz samostatná stránka [Integrace Eshop API](Integrace-Eshop-API). + +- **`POST /api/eshop/cart`** – `EshopCartController` – párování podle emailu +- **`POST /api/eshop/order`** – `EshopOrderController` – dokončení podle emailu + +## Expirování košíků + +Job `ExpireFunnelCartsJob` (command `ExpireFunnelCarts`) pravidelně označuje staré otevřené košíky jako `expired`. + +## Statistiky + +Denní statistiky košíků se agregují do: +- `campaign_cart_daily_stats` – košíky per kampaň +- `product_group_cart_daily_stats` – košíky per produktová skupina + +Rebuild přes command `RebuildCampaignCartDailyStats` / `RebuildProductGroupCartDailyStats`. diff --git a/Logovani.md b/Logovani.md new file mode 100644 index 0000000..277be84 --- /dev/null +++ b/Logovani.md @@ -0,0 +1,63 @@ +# Logování + +Projekt používá více log kanálů pro oddělení různých typů událostí. + +## Kanály + +| Kanál | Soubor | Driver | Rotace | Popis | +|-------|--------|--------|--------|-------| +| `stack` | *(dle konfigurace)* | stack | – | Výchozí kanál | +| `single` | `storage/logs/laravel.log` | single | ne | Obecné logy Laravelu | +| `daily` | `storage/logs/laravel-YYYY-MM-DD.log` | daily | 14 dní | Daily logy | +| `funnel` | `storage/logs/funnel.log` | single | ne | Funnel/automace logy | +| `eshop` | `storage/logs/eshop-YYYY-MM-DD.log` | daily | 30 dní | Eshop API requesty | + +## Co se loguje kde + +### `funnel.log` +- `RulesEngine` – vyhodnocování pravidel (`rules_engine.evaluate:*`) +- Debug konkrétních kontaktů při průchodu automacemi + +### `eshop-YYYY-MM-DD.log` +Každý request na `/api/eshop/cart` a `/api/eshop/order`: + +```json +{ + "message": "api/eshop/cart", + "context": { + "method": "POST", + "ip": "1.2.3.4", + "site_key": "abc123", + "body": { + "email": "za***@example.cz", + "currency": "CZK", + "items": [...] + }, + "status": 200, + "response": { "ok": true, "cart_id": 42, ... } + } +} +``` + +> E-mail je automaticky maskován (`za***@example.cz`) z důvodu GDPR. + +### Laravel Telescope +Dostupný na `/telescope` – loguje HTTP requesty, queue joby, SQL dotazy, výjimky, emaily. Pouze ve vývojovém prostředí. + +### Laravel Pulse +Dostupný na `/pulse` – real-time monitoring výkonu (slow queries, failed jobs, queue depth...). + +## Rotace logů + +Pro produkci doporučujeme `logrotate` nebo nastavit `daily` driver s odpovídajícím `days` limitem. Aktuální nastavení: +- `eshop`: 30 dní +- `daily` (Laravel): 14 dní + +## Přidání vlastního logu + +```php +use Illuminate\Support\Facades\Log; + +Log::channel('funnel')->info('my_event', ['data' => $data]); +Log::channel('eshop')->warning('something_wrong', [...]); +``` diff --git a/Tracking.md b/Tracking.md new file mode 100644 index 0000000..aa9a944 --- /dev/null +++ b/Tracking.md @@ -0,0 +1,67 @@ +# Tracking systém + +## Jak to funguje + +Tracking probíhá přes JavaScript soubor `public/collect.js`, který se vloží na sledovaný web. Každý web/eshop má přiřazený **Site Key** (`TrackingSite.public_key`), který se předává jako `data-site-key` atribut scriptu. + +```html + +``` + +## Visitor ID + +- Každý anonymní návštěvník dostane náhodné `visitor_id` (UUID), které se ukládá do cookie `ft_vid` a localStorage. +- Po identifikaci (přihlášení, vyplnění emailu) se `visitor_id` propojí s `Contact` přes tabulku `visitor_identities` – viz `IdentityLinker`. + +## IngestEvent pipeline + +Všechny eventy (browser i server-side) prochází přes `App\Services\Ingest\IngestEvent::ingest()`: + +1. Parsuje a normalizuje data (visitor_id, contact_id, occurred_at...) +2. Volitelně provede identify (pokud přišel `identity.email`) +3. Pokud chybí `visitor_id`, generuje náhradní (`contact:{id}` nebo `integration:{source}`) +4. Uloží `FunnelEvent` do databáze (idempotentně přes `external_event_id`) +5. Vrátí uložený `FunnelEvent` + +Po ingestion volají controllery `RulesEngine::evaluate()` – viz [Automations](Automations). + +## Události (event typy) + +| Event | Kdy se posílá | +|-------|--------------| +| `page_view` | Každé zobrazení stránky | +| `cart_upsert` | Změna obsahu košíku | +| `cart_completed` | Dokončení objednávky | +| `newsletter_subscribe` | Přihlášení k newsletteru | +| `gallery_config_submit` | Odeslání konfigurátoru | +| `identify` | Identifikace návštěvníka | + +## Identifikace návštěvníka + +Třída `IdentityLinker` zajišťuje propojení `visitor_id → Contact`: + +- **`identify()`** – podle emailu (přihlášení, formulář) +- **`identifyByToken()`** – podle `ftclid` (klik z emailové kampaně) +- Mapping ukládá do `visitor_identities` +- Backfilluje `contact_id` ke starším eventům pro stejné `visitor_id` + +## Automatická identifikace v collect.js + +1. Přes `ftclid` v URL (z emailového odkazu) +2. Přes `gident` / `tag` UTM parametr +3. Přes email v payloadu (při odeslání formuláře) + +## Bot detekce + +`BotDetector` flaguje requesty od crawlerů a botů. `FunnelEvent.bot = 1` pro botí eventy – tyto se do statistik nezapočítávají. + +## GTM / dataLayer integrace + +`collect.js` naslouchá na `window.dataLayer.push()` a zachytává standardní ecommerce eventy (`purchase`, `order_completed`, `transaction`, `ecommerce_purchase`) – automaticky je převede na `cart_completed`. + +## Cross-domain tracking + +Pokud zákazník přechází mezi doménami, `collect.js` automaticky doplňuje `visitor_id` jako URL parametr do odkazů podle konfigurace `data-propagate-link-domains`.