L1L2 gen1Gen2 kialakítása
This commit is contained in:
184
backend/app/models/marketplace/commission.py
Normal file
184
backend/app/models/marketplace/commission.py
Normal file
@@ -0,0 +1,184 @@
|
||||
# /opt/docker/dev/service_finder/backend/app/models/marketplace/commission.py
|
||||
"""
|
||||
CommissionRule: Dynamic, tiered, time-bound commission and reward rules.
|
||||
|
||||
Supports:
|
||||
- L1 rewards (XP/credits for individual referrals)
|
||||
- L2 commissions (% for company-to-company purchases)
|
||||
- Tier-based multipliers (Standard, VIP, Platinum)
|
||||
- Regional overrides (HU, Global, etc.)
|
||||
- Time-bound promotional campaigns
|
||||
|
||||
THOUGHT PROCESS:
|
||||
- Single table for both L1 and L2 avoids join complexity; nullable fields
|
||||
differentiate type-specific values.
|
||||
- region_code as simple string (ISO 3166-1 alpha-2) follows existing pattern
|
||||
(see InsuranceProvider.country_code).
|
||||
- start_date/end_date as Date (day-granular campaigns, no timezone issues).
|
||||
- is_campaign boolean enables quick filtering without date parsing.
|
||||
- UniqueConstraint on 5 columns prevents duplicate rules for the same
|
||||
type/tier/region/date combination.
|
||||
- metadata_json JSONB for future-proof UI fields (colors, icons, tooltips).
|
||||
"""
|
||||
|
||||
import enum
|
||||
from datetime import datetime, date
|
||||
from typing import Optional
|
||||
from sqlalchemy import (
|
||||
String, Integer, Float, Boolean, DateTime, Date,
|
||||
Text, Enum as SQLEnum, Numeric, UniqueConstraint, text
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
from sqlalchemy.dialects.postgresql import UUID as PG_UUID, JSONB
|
||||
from sqlalchemy.sql import func
|
||||
from app.database import Base
|
||||
|
||||
|
||||
class CommissionRuleType(str, enum.Enum):
|
||||
"""
|
||||
Két elsődleges jutalom típus:
|
||||
- L1_REWARD: XP/kredit jutalom egyéni ajánlóknak (referral)
|
||||
- L2_COMMISSION: Százalékos jutalék céges vásárlások után
|
||||
"""
|
||||
L1_REWARD = "L1_REWARD" # XP/credits for individual referrers
|
||||
L2_COMMISSION = "L2_COMMISSION" # % commission for company purchases
|
||||
|
||||
|
||||
class CommissionTier(str, enum.Enum):
|
||||
"""Jutalék szintek / rétegek."""
|
||||
STANDARD = "STANDARD"
|
||||
VIP = "VIP"
|
||||
PLATINUM = "PLATINUM"
|
||||
ENTERPRISE = "ENTERPRISE"
|
||||
CONTRACTED = "CONTRACTED"
|
||||
|
||||
|
||||
class CommissionRule(Base):
|
||||
"""
|
||||
Dinamikus jutalék/jutalom szabályok.
|
||||
|
||||
Minden szabály egy adott típushoz (L1/L2), szinthez (tier),
|
||||
régióhoz és opcionális időablakhoz tartozik.
|
||||
|
||||
A lekérdező motor a tranzakció időpontjában érvényes,
|
||||
legspecifikusabb szabályt alkalmazza.
|
||||
"""
|
||||
__tablename__ = "commission_rules"
|
||||
__table_args__ = (
|
||||
UniqueConstraint(
|
||||
'rule_type', 'tier', 'region_code',
|
||||
'start_date', 'end_date',
|
||||
name='uix_commission_rule_unique'
|
||||
),
|
||||
{"schema": "marketplace", "extend_existing": True}
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True)
|
||||
|
||||
# --- Rule Classification ---
|
||||
rule_type: Mapped[CommissionRuleType] = mapped_column(
|
||||
SQLEnum(CommissionRuleType, name="commission_rule_type", schema="marketplace"),
|
||||
nullable=False,
|
||||
index=True,
|
||||
comment="L1_REWARD = XP/kredit jutalom, L2_COMMISSION = % jutalék"
|
||||
)
|
||||
|
||||
tier: Mapped[CommissionTier] = mapped_column(
|
||||
SQLEnum(CommissionTier, name="commission_tier", schema="marketplace"),
|
||||
nullable=False,
|
||||
default=CommissionTier.STANDARD,
|
||||
index=True,
|
||||
comment="Jutalék szint (STANDARD, VIP, PLATINUM, ENTERPRISE)"
|
||||
)
|
||||
|
||||
# --- Regional Scope ---
|
||||
region_code: Mapped[str] = mapped_column(
|
||||
String(10),
|
||||
nullable=False,
|
||||
default="GLOBAL",
|
||||
index=True,
|
||||
comment="ISO 3166-1 alpha-2 országkód (pl. 'HU', 'DE') vagy 'GLOBAL'"
|
||||
)
|
||||
|
||||
# --- Reward / Commission Values ---
|
||||
# L1_REWARD fields
|
||||
xp_reward: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True, default=0,
|
||||
comment="L1: XP jutalom értéke"
|
||||
)
|
||||
credit_reward: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True, default=0,
|
||||
comment="L1: Kredit jutalom értéke"
|
||||
)
|
||||
|
||||
# L2_COMMISSION fields
|
||||
commission_percent: Mapped[Optional[float]] = mapped_column(
|
||||
Numeric(5, 2), nullable=True, default=0.00,
|
||||
comment="L2: Jutalék százalék (pl. 5.00 = 5%)"
|
||||
)
|
||||
upline_commission_percent: Mapped[Optional[float]] = mapped_column(
|
||||
Numeric(5, 2), nullable=True, default=0.00,
|
||||
comment="L2: Upline (Gen2) jutalék százalék — a közvetlen ajánló feletti szintnek járó jutalék (pl. 2.00 = 2%)"
|
||||
)
|
||||
renewal_commission_percent: Mapped[Optional[float]] = mapped_column(
|
||||
Numeric(5, 2), nullable=True, default=0.00,
|
||||
comment="L2: Megújítási jutalék százalék (pl. 2.50 = 2.5%) - havi/éves előfizetés megújításakor járó jutalék"
|
||||
)
|
||||
commission_max_amount: Mapped[Optional[float]] = mapped_column(
|
||||
Numeric(18, 2), nullable=True,
|
||||
comment="L2: Maximális jutalék összeg (opcionális felső korlát)"
|
||||
)
|
||||
|
||||
# --- Time-Bound Campaign ---
|
||||
is_campaign: Mapped[bool] = mapped_column(
|
||||
Boolean, default=False, server_default=text("false"),
|
||||
comment="TRUE = időkorlátos kampány, FALSE = állandó szabály"
|
||||
)
|
||||
start_date: Mapped[Optional[date]] = mapped_column(
|
||||
Date, nullable=True, index=True,
|
||||
comment="Kampány kezdő dátuma (NULL = azonnal érvényes)"
|
||||
)
|
||||
end_date: Mapped[Optional[date]] = mapped_column(
|
||||
Date, nullable=True, index=True,
|
||||
comment="Kampány záró dátuma (NULL = nincs lejárat)"
|
||||
)
|
||||
|
||||
# --- Metadata ---
|
||||
name: Mapped[str] = mapped_column(
|
||||
String(200), nullable=False,
|
||||
comment="Emberi olvasható szabály név (pl. 'HU VIP Nyári Kampány')"
|
||||
)
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
Text, nullable=True,
|
||||
comment="Részletes leírás a szabályról"
|
||||
)
|
||||
is_active: Mapped[bool] = mapped_column(
|
||||
Boolean, default=True, server_default=text("true"), index=True,
|
||||
comment="TRUE = aktív és használható, FALSE = letiltva"
|
||||
)
|
||||
|
||||
# --- Audit Trail ---
|
||||
created_by: Mapped[Optional[int]] = mapped_column(
|
||||
Integer, nullable=True,
|
||||
comment="Létrehozó admin felhasználó ID"
|
||||
)
|
||||
created_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now()
|
||||
)
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(),
|
||||
onupdate=func.now()
|
||||
)
|
||||
|
||||
# --- Extensibility ---
|
||||
metadata_json: Mapped[Optional[dict]] = mapped_column(
|
||||
JSONB, nullable=True, server_default=text("'{}'::jsonb"),
|
||||
comment="Bővíthető metaadatok (pl. campaign banner URL, notes)"
|
||||
)
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return (
|
||||
f"<CommissionRule(id={self.id}, type='{self.rule_type}', "
|
||||
f"tier='{self.tier}', region='{self.region_code}', "
|
||||
f"active={self.is_active})>"
|
||||
)
|
||||
Reference in New Issue
Block a user