Loyalty & Ügyfélkezelő Rendszer — Tervezési Dokumentáció
Áttekintés
A Vecton platform moduláris hűség- és ügyfélkezelő rendszere, amely lehetővé teszi a tenantek számára, hogy saját igényeik szerint konfigurálják az ügyfélélményt. A rendszer építőkocka-elven működik: minden modul önállóan ki-be kapcsolható, és csak az aktív modulok jelennek meg az admin felületen és az API-ban.
Modulok
Loyalty & Ügyfélkezelő Engine
├── Customer Identity — Ügyfél-azonosítók, kártyaszámok
├── Customer Groups — Ügyfélcsoportok, árlisták
├── Points Program — Hűségpont gyűjtés & beváltás
├── Tier System — Törzsvásárlói szintrendszer
├── Store Credit — Kredit egyenleg, hitelkeret
├── Stamp Cards — Pecsétgyűjtő kártyák
└── Promotions — Kedvezmények, akciók, feltételes ajánlatok
Tenant szintű konfiguráció
Minden tenant maga dönti el, mely modulokat használja:
| Példa tenant | Aktív modulok |
|---|---|
| Kávézó | Azonosítók + Pecsétgyűjtő |
| B2B Nagyker | Azonosítók + Csoportok + Kredit |
| Divatwebshop | Azonosítók + Pontprogram + Szintek + Promóciók |
| Szerviz cég | Azonosítók + Kredit + Csoportok |
1. Customer Identity — Ügyfél-azonosítók
Cél
Rugalmas azonosítórendszer, ahol a tenant maga definiálja, milyen azonosítókat használ (törzskártya szám, adószám, szerződésszám, stb.).
Adatmodell
-- Tenant által definiált azonosító típusok
CREATE TABLE customer_identifier_types (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
name VARCHAR(255) NOT NULL, -- "Törzskártya", "Ügyfélszám", "Adószám"
slug VARCHAR(100) NOT NULL, -- "loyalty_card", "customer_number", "tax_id"
prefix VARCHAR(20) DEFAULT NULL, -- "VEC-", "LC-", stb.
format_regex VARCHAR(255) DEFAULT NULL, -- validációs minta
auto_generate BOOLEAN DEFAULT FALSE, -- automatikus generálás
next_sequence BIGINT DEFAULT 1, -- következő sorszám (ha auto_generate)
is_unique BOOLEAN DEFAULT TRUE, -- egyedi-e a tenant-en belül
is_required BOOLEAN DEFAULT FALSE, -- kötelező-e minden ügyfélnél
sort_order INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(tenant_id, slug)
);
-- Ügyfélhez rendelt azonosítók
CREATE TABLE customer_identifiers (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
identifier_type_id UUID NOT NULL REFERENCES customer_identifier_types(id),
value VARCHAR(255) NOT NULL,
issued_at DATE DEFAULT NULL,
expires_at DATE DEFAULT NULL,
is_active BOOLEAN DEFAULT TRUE,
metadata JSONB DEFAULT NULL, -- extra mezők, pl. kártya fizikai állapota
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(identifier_type_id, value)
);
Használati példák
| Tenant | Azonosító típus | Prefix | Auto-generált | Példa érték |
|---|---|---|---|---|
| Pékség | Törzskártya | TC- | Igen | TC-000142 |
| Nagyker | Ügyfélszám | VEC- | Igen | VEC-003891 |
| Nagyker | Adószám | — | Nem | 12345678-2-42 |
| Nagyker | Szerződésszám | SZ- | Nem | SZ-2026/0034 |
| Webshop | Hűségkártya | LY- | Igen | LY-982301 |
2. Customer Groups — Ügyfélcsoportok
Cél
Ügyfelek csoportosítása árazási, kedvezményes vagy belső szegmentációs célból. Egy ügyfél több csoporthoz is tartozhat.
Adatmodell
CREATE TABLE customer_groups (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
name VARCHAR(255) NOT NULL, -- "Nagyker", "VIP", "Alkalmazott"
slug VARCHAR(100) NOT NULL,
type VARCHAR(50) NOT NULL, -- 'pricing' | 'segment' | 'internal'
description TEXT DEFAULT NULL,
discount_percent DECIMAL(5,2) DEFAULT 0, -- alapértelmezett kedvezmény %
price_list_id UUID DEFAULT NULL, -- ha saját árlista tartozik hozzá
color VARCHAR(7) DEFAULT '#6366f1',
is_default BOOLEAN DEFAULT FALSE, -- új ügyfél automatikusan ide kerül
auto_assign_rules JSONB DEFAULT NULL, -- automatikus besorolási szabályok
sort_order INTEGER DEFAULT 0,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(tenant_id, slug)
);
CREATE TABLE customer_group_memberships (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
group_id UUID NOT NULL REFERENCES customer_groups(id) ON DELETE CASCADE,
valid_from DATE DEFAULT NULL,
valid_until DATE DEFAULT NULL,
notes TEXT DEFAULT NULL,
created_by INTEGER DEFAULT NULL,
created_at TIMESTAMP,
UNIQUE(customer_id, group_id)
);
Csoport típusok
- pricing — árazási csoport: saját árlista vagy alapértelmezett kedvezmény tartozik hozzá
- segment — szegmens: marketing célú csoportosítás (pl. "Visszatérő vásárló", "Új ügyfél")
- internal — belső: adminisztrációs célú (pl. "Tesztelők", "Partnercég dolgozói")
Automatikus besorolás (auto_assign_rules)
{
"trigger": "after_purchase",
"conditions": [
{"field": "total_purchases_count", "operator": "gte", "value": 5},
{"field": "total_purchases_amount", "operator": "gte", "value": 100000}
]
}
3. Points Program — Hűségpont rendszer
Cél
Konfigurálja a tenant a saját pontprogramját: hogyan gyűjthetők pontok, mire válthatók be, mikor járnak le.
Adatmodell
-- Pontprogram konfiguráció (tenant-enként 1 vagy több program)
CREATE TABLE loyalty_programs (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
name VARCHAR(255) NOT NULL, -- "Hűségpont Program"
type VARCHAR(50) NOT NULL, -- 'points' | 'stamps' | 'cashback'
currency_name VARCHAR(50) DEFAULT 'pont', -- "pont", "pecsét", "cashback Ft"
currency_symbol VARCHAR(10) DEFAULT 'P', -- "P", "⭐", "Ft"
expiry_days INTEGER DEFAULT NULL, -- NULL = soha nem jár le
expiry_type VARCHAR(50) DEFAULT 'from_earn', -- 'from_earn' | 'end_of_year' | 'rolling_window'
min_redeem INTEGER DEFAULT 0, -- minimum beváltható pont
is_active BOOLEAN DEFAULT TRUE,
settings JSONB DEFAULT '{}', -- extra beállítások
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Gyűjtési és beváltási szabályok
CREATE TABLE loyalty_rules (
id UUID PRIMARY KEY,
program_id UUID NOT NULL REFERENCES loyalty_programs(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL, -- "Alap pontgyűjtés", "Dupla pont hétvégén"
trigger VARCHAR(50) NOT NULL, -- 'purchase' | 'registration' | 'referral' | 'birthday' | 'manual' | 'review'
action VARCHAR(50) NOT NULL, -- 'earn' | 'redeem'
-- Mennyi pontot ad/von le
value DECIMAL(10,2) NOT NULL, -- 1, 2, 500, stb.
value_type VARCHAR(50) NOT NULL, -- 'per_amount' | 'fixed' | 'multiplier' | 'percent_of_cart'
value_unit DECIMAL(10,2) DEFAULT 1, -- per_amount esetén: "1 pont per 100 Ft" → value=1, value_unit=100
-- Beváltásnál: mennyit ér egy pont
redeem_value DECIMAL(10,2) DEFAULT NULL, -- 1 pont = 5 Ft kedvezmény
redeem_max_percent DECIMAL(5,2) DEFAULT NULL, -- max a kosár X%-a fedhető ponttal
-- Feltételek
conditions JSONB DEFAULT '{}', -- {min_amount, category_ids, day_of_week, channel, ...}
priority INTEGER DEFAULT 0, -- magasabb = előrébb értékelődik
is_active BOOLEAN DEFAULT TRUE,
valid_from TIMESTAMP DEFAULT NULL,
valid_until TIMESTAMP DEFAULT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
-- Ügyfél pontegyenlege
CREATE TABLE loyalty_balances (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
program_id UUID NOT NULL REFERENCES loyalty_programs(id) ON DELETE CASCADE,
balance BIGINT DEFAULT 0, -- aktuális egyenleg
lifetime_earned BIGINT DEFAULT 0, -- összesen szerzett (szint számításhoz)
lifetime_redeemed BIGINT DEFAULT 0, -- összesen beváltott
lifetime_expired BIGINT DEFAULT 0, -- összesen lejárt
last_activity_at TIMESTAMP DEFAULT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(customer_id, program_id)
);
-- Pont tranzakció napló (immutable)
CREATE TABLE loyalty_transactions (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
program_id UUID NOT NULL REFERENCES loyalty_programs(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL, -- 'earn' | 'redeem' | 'expire' | 'adjust' | 'bonus' | 'refund'
amount BIGINT NOT NULL, -- pozitív: jóváírás, negatív: terhelés
balance_after BIGINT NOT NULL, -- tranzakció utáni egyenleg
-- Mire vonatkozik
source_type VARCHAR(100) DEFAULT NULL, -- 'order' | 'manual' | 'campaign' | 'referral' | 'review'
source_id UUID DEFAULT NULL, -- polymorphic: order_id, campaign_id, stb.
rule_id UUID DEFAULT NULL REFERENCES loyalty_rules(id),
description VARCHAR(500) DEFAULT NULL, -- "Vásárlás #ORD-1234 után 150 pont"
metadata JSONB DEFAULT NULL, -- extra adat
expires_at TIMESTAMP DEFAULT NULL, -- mikor jár le ez a pont tétel
created_by INTEGER DEFAULT NULL, -- ki hozta létre (manuális jóváírásnál)
created_at TIMESTAMP NOT NULL
);
-- Indexek
CREATE INDEX idx_loyalty_transactions_customer ON loyalty_transactions(customer_id, program_id, created_at);
CREATE INDEX idx_loyalty_transactions_expires ON loyalty_transactions(expires_at) WHERE expires_at IS NOT NULL AND type = 'earn';
Szabálypéldák
Alap pontgyűjtés (1 pont / 100 Ft):
{
"name": "Alap pontgyűjtés",
"trigger": "purchase",
"action": "earn",
"value": 1,
"value_type": "per_amount",
"value_unit": 100
}
Dupla pont hétvégén:
{
"name": "Hétvégi dupla pont",
"trigger": "purchase",
"action": "earn",
"value": 2,
"value_type": "multiplier",
"conditions": {
"day_of_week": [6, 7]
},
"priority": 10
}
500 pont regisztrációkor:
{
"name": "Üdvözlő bónusz",
"trigger": "registration",
"action": "earn",
"value": 500,
"value_type": "fixed"
}
Pont beváltás (100 pont = 500 Ft):
{
"name": "Pont beváltás",
"trigger": "purchase",
"action": "redeem",
"value": 1,
"value_type": "per_amount",
"redeem_value": 5,
"redeem_max_percent": 30
}
Pont lejárat típusok
| Típus | Leírás | Példa |
|---|---|---|
from_earn | X nappal a szerzés után | 365 nap múlva lejár |
end_of_year | Az adott év végén | Dec 31-én minden pont lejár |
rolling_window | Utolsó aktivitástól X nap | 180 nap inaktivitás után lejár az összes |
4. Tier System — Szintrendszer
Cél
Automatikus és manuális törzsvásárlói szintek, szinthez kötött előnyökkel.
Adatmodell
CREATE TABLE loyalty_tiers (
id UUID PRIMARY KEY,
program_id UUID NOT NULL REFERENCES loyalty_programs(id) ON DELETE CASCADE,
name VARCHAR(255) NOT NULL, -- "Bronz", "Ezüst", "Arany", "Platina"
slug VARCHAR(100) NOT NULL,
-- Elérési feltétel (a tenant választja melyiket használja)
min_points BIGINT DEFAULT 0, -- lifetime_earned alapján
min_spend DECIMAL(12,2) DEFAULT 0, -- összesített vásárlás alapján
min_orders INTEGER DEFAULT 0, -- rendelések száma alapján
qualification_period_days INTEGER DEFAULT NULL, -- NULL = lifetime, 365 = éves újraértékelés
-- Szint előnyök
benefits JSONB DEFAULT '{}',
/*
{
"discount_percent": 10,
"free_shipping": true,
"free_shipping_min_amount": 0,
"point_multiplier": 1.5,
"priority_support": true,
"early_access_days": 3,
"birthday_bonus_points": 1000,
"exclusive_products": true
}
*/
color VARCHAR(7) DEFAULT '#6366f1',
icon VARCHAR(50) DEFAULT NULL, -- "tabler-award", "tabler-diamond"
sort_order INTEGER DEFAULT 0, -- Bronz=1, Ezüst=2, Arany=3, Platina=4
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(program_id, slug)
);
-- Ügyfél aktuális szintje és történet
CREATE TABLE customer_tiers (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
program_id UUID NOT NULL REFERENCES loyalty_programs(id) ON DELETE CASCADE,
tier_id UUID NOT NULL REFERENCES loyalty_tiers(id),
promoted_at TIMESTAMP NOT NULL,
expires_at TIMESTAMP DEFAULT NULL, -- mikor értékelődik újra
is_current BOOLEAN DEFAULT TRUE,
-- Történet
previous_tier_id UUID DEFAULT NULL REFERENCES loyalty_tiers(id),
reason VARCHAR(255) DEFAULT NULL, -- 'auto_promotion' | 'auto_demotion' | 'manual'
created_by INTEGER DEFAULT NULL,
created_at TIMESTAMP,
UNIQUE(customer_id, program_id, is_current) -- csak 1 aktuális szint per program
);
Szint példák
| Szint | Feltétel | Kedvezmény | Szállítás | Pont szorzó | Extra |
|---|---|---|---|---|---|
| Bronz | 0 pont | 0% | Normál | 1x | — |
| Ezüst | 5.000 pont | 5% | 15.000 Ft felett ingyen | 1.25x | — |
| Arany | 20.000 pont | 10% | Mindig ingyen | 1.5x | Korai hozzáférés 2 nap |
| Platina | 50.000 pont | 15% | Mindig ingyen | 2x | Korai hozzáférés 5 nap, prioritás support |
Visszasorolás logika
A tenant beállítja:
- Nincs visszasorolás — egyszer elért szint megmarad
- Éves újraértékelés — ha a qualification_period_days le, ha nem teljesíti a feltételt, visszasorolás 1 szinttel
- Azonnali — ha a pontegyenleg a szint alá esik (pont beváltás után)
5. Store Credit — Kredit egyenleg
Cél
Előre feltöltött egyenleg (B2B), visszáru jóváírás, ajándékkártya egyenleg, vagy hitelkeret.
Adatmodell
CREATE TABLE credit_accounts (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
tenant_id UUID NOT NULL,
balance DECIMAL(12,2) DEFAULT 0, -- aktuális egyenleg
credit_limit DECIMAL(12,2) DEFAULT 0, -- hitelkeret (0 = nincs hitel, csak előre töltött)
currency VARCHAR(3) DEFAULT 'HUF',
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(customer_id, currency)
);
CREATE TABLE credit_transactions (
id UUID PRIMARY KEY,
account_id UUID NOT NULL REFERENCES credit_accounts(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL, -- 'deposit' | 'purchase' | 'refund' | 'adjustment' | 'gift_card' | 'expiry'
amount DECIMAL(12,2) NOT NULL, -- pozitív: jóváírás, negatív: terhelés
balance_after DECIMAL(12,2) NOT NULL,
reference_type VARCHAR(100) DEFAULT NULL, -- 'order' | 'return' | 'gift_card' | 'manual'
reference_id UUID DEFAULT NULL,
description VARCHAR(500) DEFAULT NULL, -- "Előre fizetés 2026. márciusra"
note TEXT DEFAULT NULL, -- belső megjegyzés
created_by INTEGER DEFAULT NULL,
created_at TIMESTAMP NOT NULL
);
CREATE INDEX idx_credit_transactions_account ON credit_transactions(account_id, created_at);
Használati esetek
| Eset | Tranzakció típus | Összeg | Leírás |
|---|---|---|---|
| B2B előre fizet | deposit | +500.000 | "Előleg 2026 Q2" |
| Vásárlás egyenlegből | purchase | -34.500 | "Rendelés #ORD-4521" |
| Visszáru jóváírás | refund | +12.000 | "Visszáru #RET-089" |
| Ajándékkártya beváltás | gift_card | +25.000 | "GC-ABCD-1234-EFGH" |
| Admin korrekció | adjustment | +5.000 | "Kompenzáció késedelmes szállítás" |
Hitelkeret
Ha credit_limit > 0, az ügyfél a balance alá is mehet (negatív egyenleg), de maximum -credit_limit-ig:
Elérhető összeg = balance + credit_limit
Például:
- Balance: 0 Ft, Credit limit: 200.000 Ft → 200.000 Ft-ig vásárolhat
- Balance: -150.000 Ft, Credit limit: 200.000 Ft → még 50.000 Ft-ig vásárolhat
6. Stamp Cards — Pecsétgyűjtő
Cél
Egyszerű "10. kávé ingyen" típusú rendszer, kis üzleteknek.
Adatmodell
CREATE TABLE stamp_cards (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
name VARCHAR(255) NOT NULL, -- "Kávé pecsétgyűjtő"
total_stamps INTEGER NOT NULL DEFAULT 10, -- hány pecsét kell a jutalomhoz
reward_type VARCHAR(50) NOT NULL, -- 'free_product' | 'discount_percent' | 'discount_fixed' | 'points'
reward_value DECIMAL(10,2) DEFAULT NULL, -- 20 (%), 2000 (Ft), 500 (pont)
reward_product_id UUID DEFAULT NULL, -- ha free_product: melyik termék
-- Mire jár pecsét
qualifying_rules JSONB DEFAULT '{}', -- {category_ids, product_ids, min_amount}
is_active BOOLEAN DEFAULT TRUE,
valid_from DATE DEFAULT NULL,
valid_until DATE DEFAULT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE customer_stamp_cards (
id UUID PRIMARY KEY,
customer_id UUID NOT NULL REFERENCES customers(id) ON DELETE CASCADE,
stamp_card_id UUID NOT NULL REFERENCES stamp_cards(id) ON DELETE CASCADE,
stamps_collected INTEGER DEFAULT 0,
is_completed BOOLEAN DEFAULT FALSE,
reward_claimed BOOLEAN DEFAULT FALSE,
reward_claimed_at TIMESTAMP DEFAULT NULL,
started_at TIMESTAMP NOT NULL,
completed_at TIMESTAMP DEFAULT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP
);
CREATE TABLE stamp_events (
id UUID PRIMARY KEY,
customer_stamp_card_id UUID NOT NULL REFERENCES customer_stamp_cards(id) ON DELETE CASCADE,
type VARCHAR(50) NOT NULL, -- 'stamp' | 'reward_claimed' | 'reset'
source_type VARCHAR(100) DEFAULT NULL, -- 'order' | 'manual'
source_id UUID DEFAULT NULL,
created_by INTEGER DEFAULT NULL,
created_at TIMESTAMP NOT NULL
);
Működés
- Ügyfél vásárol (qualifying feltétel teljesül) → pecsét jóváírás
stamps_collected >= total_stamps→ kártya completed- Ügyfél beváltja a jutalmat →
reward_claimed = true - Új kártya indul automatikusan
7. Promotions — Kedvezmények & akciók
Cél
Univerzális promóciómotor, amellyel bármilyen kedvezménytípus konfigurálható feltételekkel. Kiváltja a hagyományos "kuponrendszert" is.
Adatmodell
CREATE TABLE promotions (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL,
name VARCHAR(255) NOT NULL, -- "Tavaszi akció 20%"
description TEXT DEFAULT NULL, -- adminnak szóló leírás
public_label VARCHAR(255) DEFAULT NULL, -- ügyfélnek látható szöveg: "Tavaszi vásár!"
-- Mit ad
type VARCHAR(50) NOT NULL,
/*
'discount_percent' — X% kedvezmény
'discount_fixed' — X Ft kedvezmény
'free_shipping' — ingyenes szállítás
'free_product' — ajándék termék
'fixed_price' — fix akciós ár
'cashback' — X% visszajár kreditre
'point_multiplier' — Nx pont szorzó
'point_bonus' — fix bónusz pont
*/
value DECIMAL(10,2) NOT NULL, -- 20 (%), 2000 (Ft), 1.5 (szorzó)
-- Kupon kód (opcionális — ha nincs, automatikus promóció)
code VARCHAR(50) DEFAULT NULL, -- "TAVASZ20", NULL = automatikus
-- Limitek
max_uses_total INTEGER DEFAULT NULL, -- összes felhasználás limit (NULL = korlátlan)
max_uses_per_customer INTEGER DEFAULT NULL, -- ügyfelenként limit
current_uses INTEGER DEFAULT 0,
-- Kombinálhatóság
is_combinable BOOLEAN DEFAULT TRUE, -- kombinálható-e más promócióval
priority INTEGER DEFAULT 0, -- ha nem kombinálható, melyik nyer (magasabb = erősebb)
is_active BOOLEAN DEFAULT TRUE,
created_by INTEGER DEFAULT NULL,
created_at TIMESTAMP,
updated_at TIMESTAMP,
UNIQUE(tenant_id, code) -- kuponkód egyedi tenant-en belül
);
-- Promóció feltételek (AND logika — minden feltételnek teljesülnie kell)
CREATE TABLE promotion_conditions (
id UUID PRIMARY KEY,
promotion_id UUID NOT NULL REFERENCES promotions(id) ON DELETE CASCADE,
condition_type VARCHAR(50) NOT NULL,
/*
-- Kosár feltételek
'min_cart_value' — minimum kosárérték
'min_quantity' — minimum darabszám
'max_cart_value' — maximum kosárérték
-- Termék feltételek
'category' — adott kategóriába tartozó termék(ek)re
'product' — konkrét termék(ek)re
'brand' — adott márká(k)ra
'exclude_category' — kivéve ezek a kategóriák
'exclude_product' — kivéve ezek a termékek
-- Ügyfél feltételek
'customer_group' — adott ügyfélcsoporthoz tartozik
'tier' — adott szinten van
'first_purchase' — első vásárlás
'days_since_last_order' — X napja nem rendelt
'min_lifetime_spend' — összesen ennyit költött
'birthday_month' — születésnapi hónap
'registration_days' — X napon belül regisztrált
-- Csatorna feltételek
'channel' — 'online' | 'in_store' | 'app'
'store' — konkrét bolt(ok)
-- Idő feltételek
'day_of_week' — hét napjai
'time_range' — napszak
*/
operator VARCHAR(20) NOT NULL, -- 'eq' | 'gt' | 'gte' | 'lt' | 'lte' | 'in' | 'not_in' | 'between'
value JSONB NOT NULL, -- érték vagy lista
created_at TIMESTAMP
);
-- Promóció érvényességi időszak
CREATE TABLE promotion_schedules (
id UUID PRIMARY KEY,
promotion_id UUID NOT NULL REFERENCES promotions(id) ON DELETE CASCADE,
valid_from TIMESTAMP NOT NULL,
valid_until TIMESTAMP NOT NULL,
-- Ismétlődő időszakok (opcionális)
recurring_days JSONB DEFAULT NULL, -- [1,2,3,4,5] = hétköznapok
recurring_hours JSONB DEFAULT NULL, -- {"from": "14:00", "to": "16:00"}
created_at TIMESTAMP
);
-- Promóció felhasználás napló
CREATE TABLE promotion_usages (
id UUID PRIMARY KEY,
promotion_id UUID NOT NULL REFERENCES promotions(id) ON DELETE CASCADE,
customer_id UUID NOT NULL REFERENCES customers(id),
order_id UUID DEFAULT NULL,
discount_amount DECIMAL(12,2) NOT NULL, -- ténylegesen adott kedvezmény
used_at TIMESTAMP NOT NULL,
created_at TIMESTAMP
);
CREATE INDEX idx_promotion_usages_customer ON promotion_usages(promotion_id, customer_id);
Promóció példák konfigurálva
"20% csak bolti vásárlásra"
promotion:
type: discount_percent
value: 20
code: NULL (automatikus)
conditions:
- condition_type: channel
operator: eq
value: "in_store"
"15% csak online rendelésre, min 10.000 Ft"
promotion:
type: discount_percent
value: 15
code: NULL
conditions:
- condition_type: channel
operator: eq
value: "online"
- condition_type: min_cart_value
operator: gte
value: 10000
"Ingyenes szállítás Arany tagoknak"
promotion:
type: free_shipping
value: 1
conditions:
- condition_type: tier
operator: in
value: ["gold", "platinum"]
"Happy hour: 30% kávéra, hétköznap 14-16h"
promotion:
type: discount_percent
value: 30
conditions:
- condition_type: category
operator: in
value: ["coffee"]
- condition_type: day_of_week
operator: in
value: [1, 2, 3, 4, 5]
- condition_type: time_range
operator: between
value: {"from": "14:00", "to": "16:00"}
"Visszacsábítás: 25% kuponkód, 1x beváltható"
promotion:
type: discount_percent
value: 25
code: "COMEBACK25"
max_uses_per_customer: 1
conditions:
- condition_type: days_since_last_order
operator: gte
value: 90
"B2B mennyiségi kedvezmény: 10 db-tól -20%"
promotion:
type: discount_percent
value: 20
conditions:
- condition_type: min_quantity
operator: gte
value: 10
- condition_type: customer_group
operator: in
value: ["wholesale"]
Tenant konfiguráció
Modul aktiválás
CREATE TABLE loyalty_module_settings (
id UUID PRIMARY KEY,
tenant_id UUID NOT NULL UNIQUE,
-- Modul kapcsolók
identifiers_enabled BOOLEAN DEFAULT FALSE,
groups_enabled BOOLEAN DEFAULT FALSE,
points_enabled BOOLEAN DEFAULT FALSE,
tiers_enabled BOOLEAN DEFAULT FALSE,
credit_enabled BOOLEAN DEFAULT FALSE,
stamps_enabled BOOLEAN DEFAULT FALSE,
promotions_enabled BOOLEAN DEFAULT FALSE,
-- Globális beállítások
default_currency VARCHAR(3) DEFAULT 'HUF',
created_at TIMESTAMP,
updated_at TIMESTAMP
);
Szolgáltatás architektúra
Melyik backend-be kerül?
Mivel ez szorosan kapcsolódik az e-commerce funkciókhoz (kosár, rendelés, termékek), de a CRM is használja (ügyfélkezelés), a javasolt elhelyezés:
tenant-backend-webshop — Promóciók, pont engine, kosár integráció
tenant-backend-crm — Ügyfélcsoportok, azonosítók (kiterjesztés)
Vagy (ha túl összekeveredik):
tenant-backend-loyalty (új!) — Önálló loyalty service az összes modullal
API végpontok (tervezett)
# Ügyfél-azonosítók
GET /api/loyalty/identifier-types
POST /api/loyalty/identifier-types
GET /api/loyalty/customers/{id}/identifiers
POST /api/loyalty/customers/{id}/identifiers
# Csoportok
GET /api/loyalty/groups
POST /api/loyalty/groups
PUT /api/loyalty/customers/{id}/groups
# Pontprogram
GET /api/loyalty/programs
GET /api/loyalty/customers/{id}/balance
GET /api/loyalty/customers/{id}/transactions
POST /api/loyalty/customers/{id}/points/adjust
POST /api/loyalty/customers/{id}/points/redeem
# Szintek
GET /api/loyalty/tiers
GET /api/loyalty/customers/{id}/tier
# Kredit
GET /api/loyalty/customers/{id}/credit
POST /api/loyalty/customers/{id}/credit/deposit
POST /api/loyalty/customers/{id}/credit/adjust
# Pecsétgyűjtő
GET /api/loyalty/stamp-cards
GET /api/loyalty/customers/{id}/stamps
POST /api/loyalty/customers/{id}/stamps/collect
# Promóciók
GET /api/loyalty/promotions
POST /api/loyalty/promotions
POST /api/loyalty/promotions/evaluate -- kosár kiértékelés
POST /api/loyalty/promotions/validate-code
# Tenant beállítások
GET /api/loyalty/settings
PUT /api/loyalty/settings
Implementációs sorrend (javasolt)
| Fázis | Modul | Függőség |
|---|---|---|
| 1 | Tenant konfiguráció (modul kapcsolók) | — |
| 1 | Ügyfél-azonosítók | Customer modell |
| 1 | Ügyfélcsoportok | Customer modell |
| 2 | Kredit egyenleg | Customer modell |
| 2 | Pecsétgyűjtő | Order/Purchase esemény |
| 3 | Pontprogram + szabályok | Order/Purchase esemény |
| 3 | Szintrendszer | Pontprogram |
| 4 | Promóciók | Kosár/rendelés rendszer |
Összefüggések
┌─────────────────┐
│ Tenant Config │
│ (modulok on/off)│
└────────┬────────┘
│
┌───────────────────┼───────────────────┐
│ │ │
┌────▼────┐ ┌─────▼─────┐ ┌─────▼─────┐
│Customer │ │ Points │ │Promotions │
│Identity │ │ Program │ │ Engine │
│& Groups │ │ │ │ │
└────┬────┘ └─────┬─────┘ └─────┬─────┘
│ │ │
│ ┌─────▼─────┐ │
│ │ Tier │ │
│ │ System │─────────────┘
│ └───────────┘ (szint = promó feltétel)
│
┌────▼────┐ ┌───────────┐
│ Credit │ │ Stamp │
│ Account │ │ Cards │
└─────────┘ └───────────┘