This commit is contained in:
2026-04-22 13:01:47 +02:00
commit 803acf3da3
14 changed files with 1101 additions and 0 deletions

121
API-reference.md Normal file
View File

@@ -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 |

47
Admin.md Normal file
View File

@@ -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`.

69
Architektura.md Normal file
View File

@@ -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.

98
Automations.md Normal file
View File

@@ -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`.

67
Email-kampane.md Normal file
View File

@@ -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
```

43
Home.md Normal file
View File

@@ -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 ]
```

85
Instalace.md Normal file
View File

@@ -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
```

138
Integrace-Eshop-API.md Normal file
View File

@@ -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: <site-key>
# nebo
?site_key=<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',
]);
```

83
Integrace.md Normal file
View File

@@ -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.

75
Jobs-Cron.md Normal file
View File

@@ -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
```

69
Kontakty.md Normal file
View File

@@ -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)

76
Kosiky.md Normal file
View File

@@ -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`.

63
Logovani.md Normal file
View File

@@ -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', [...]);
```

67
Tracking.md Normal file
View File

@@ -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
<script src="https://app.domena.cz/collect.js"
data-site-key="abc123"
data-api-base="https://app.domena.cz">
</script>
```
## 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`.