RABAC felépítése megtörtént ill hirdetési portál alapok lerakva

This commit is contained in:
Roo
2026-06-19 06:06:34 +00:00
parent fe3c32597d
commit 9ba2d9180d
30 changed files with 4336 additions and 76 deletions

View File

@@ -6,7 +6,10 @@ A rules struktúra a seed_packages.py-ban definiált formátumot követi:
{
"type": "private" | "corporate",
"pricing": {"monthly_price": float, "yearly_price": float, "currency": "EUR"},
"pricing_zones": {
"HU": {"monthly_price": 3000, "yearly_price": 32500, "currency": "HUF"},
"DEFAULT": {"monthly_price": 12.99, "yearly_price": 129.99, "currency": "EUR"}
},
"allowances": {"max_vehicles": int, "max_garages": int, "monthly_free_credits": int},
"entitlements": ["SRV_..."],
"affiliate": {"commission_rate_percent": int, "referral_bonus_credits": int}
@@ -25,8 +28,16 @@ from pydantic import BaseModel, ConfigDict, Field, field_validator
# ── Belső (embedded) modellek a rules JSONB struktúrához ──────────────────────
class PricingZoneModel(BaseModel):
"""Egyetlen árazási zóna adatai (ország-specifikus ár)."""
monthly_price: float = Field(..., ge=0, description="Havi előfizetési díj")
yearly_price: float = Field(..., ge=0, description="Éves előfizetési díj")
currency: str = Field(default="EUR", min_length=3, max_length=3, description="Pénznem ISO 4217 kódja")
credit_price: Optional[int] = Field(default=None, ge=0, description="Kreditár (havi kredit költség a csomaghoz)")
class PricingModel(BaseModel):
"""Árazási információk egy előfizetési csomaghoz."""
"""Árazási információk egy előfizetési csomaghoz (legacy, egyzónás)."""
monthly_price: float = Field(..., ge=0, description="Havi előfizetési díj (EUR)")
yearly_price: float = Field(..., ge=0, description="Éves előfizetési díj (EUR)")
currency: str = Field(default="EUR", min_length=3, max_length=3, description="Pénznem ISO 4217 kódja")
@@ -40,6 +51,19 @@ class AllowancesModel(BaseModel):
monthly_free_credits: int = Field(..., ge=0, description="Havi ingyenes kreditek száma")
class DurationModel(BaseModel):
"""Előfizetés időtartamának beállításai (duration_days stacking)."""
days: int = Field(default=30, ge=1, description="Előfizetés időtartama napokban (pl. 30 = havi, 365 = éves)")
allow_stacking: bool = Field(default=True, description="Időhalmozás engedélyezése (stacking): ha True, a meglévő valid_until-hoz hozzáadódik")
class AdPolicyModel(BaseModel):
"""Hirdetési szabályok a csomaghoz."""
show_ads: bool = Field(default=True, description="Megjelenjenek-e hirdetések a felhasználónak")
ad_free_grace_days: int = Field(default=0, ge=0, description="Hány nap türelmi idő a reklámok eltűnéséig fizetés után")
max_daily_impressions: Optional[int] = Field(default=None, ge=0, description="Maximális napi hirdetésmegjelenítés (None = korlátlan)")
class LifecycleModel(BaseModel):
"""Életciklus állapotok egy előfizetési csomaghoz."""
is_public: bool = Field(default=True, description="Publikus-e a csomag (látszik-e a felhasználóknak)")
@@ -52,6 +76,13 @@ class AffiliateModel(BaseModel):
referral_bonus_credits: int = Field(..., ge=0, description="Ajánlási bónusz kreditek száma")
class MarketingModel(BaseModel):
"""Marketing információk egy előfizetési csomaghoz (kártya megjelenítéshez)."""
subtitle: Optional[str] = Field(default=None, description="Rövid leíró szöveg a kártyán (pl. 'Kisebb flotta esetén.')")
badge: Optional[str] = Field(default=None, description="Jelvény szöveg (pl. 'Legnépszerűbb', 'Pro')")
highlight_color: Optional[str] = Field(default=None, description="Kiemelő szín hex kódja (pl. '#70BC84')")
class SubscriptionRulesModel(BaseModel):
"""
A subscription_tiers.rules JSONB oszlopának erősen tipizált modellje.
@@ -59,11 +90,15 @@ class SubscriptionRulesModel(BaseModel):
Fields:
type: "private" (magánszemély) vagy "corporate" (vállalati)
display_name: Emberi olvasásra szánt megjelenítési név (pl. "Privát Ingyenes")
pricing: Árazási adatok (havi/éves díj, pénznem)
pricing: Árazási adatok (legacy, egyzónás) - deprecated, használd a pricing_zones-t
pricing_zones: Több régiós árazási adatok (országkód -> PricingZoneModel)
allowances: Korlátok (max jármű, garázs, kredit)
entitlements: Jogosultságok listája (pl. SRV_DATA_EXPORT)
affiliate: Partnerprogram beállítások
lifecycle: Életciklus állapotok (opcionális, pl. is_public)
duration: Előfizetés időtartamának beállításai (duration_days, stacking)
ad_policy: Hirdetési szabályok (show_ads, ad_free_grace_days)
marketing: Marketing adatok a kártya megjelenítéshez (subtitle, badge, highlight_color)
"""
type: str = Field(..., pattern=r"^(private|corporate)$", description="Csomag típusa")
display_name: Optional[str] = Field(
@@ -72,7 +107,11 @@ class SubscriptionRulesModel(BaseModel):
)
pricing: Optional[PricingModel] = Field(
default=None,
description="Árazási adatok (havi/éves díj, pénznem). Opcionális a hiányos seed adatok miatt."
description="Árazási adatok (legacy, egyzónás). Deprecated: használd a pricing_zones-t.",
)
pricing_zones: Optional[Dict[str, PricingZoneModel]] = Field(
default=None,
description="Több régiós árazás. Kulcs: országkód (pl. 'HU', 'DE', 'DEFAULT').",
)
allowances: Optional[AllowancesModel] = Field(
default=None,
@@ -87,6 +126,18 @@ class SubscriptionRulesModel(BaseModel):
default=None,
description="Életciklus állapotok (is_public, available_until)"
)
duration: Optional[DurationModel] = Field(
default=None,
description="Előfizetés időtartamának beállításai (duration_days, allow_stacking)"
)
ad_policy: Optional[AdPolicyModel] = Field(
default=None,
description="Hirdetési szabályok (show_ads, ad_free_grace_days, max_daily_impressions)"
)
marketing: Optional[MarketingModel] = Field(
default=None,
description="Marketing adatok a kártya megjelenítéshez (subtitle, badge, highlight_color)"
)
@field_validator("type")
@classmethod
@@ -109,6 +160,23 @@ class SubscriptionTierResponse(BaseModel):
model_config = ConfigDict(from_attributes=True)
class PublicSubscriptionTierResponse(BaseModel):
"""
Publikus előfizetési csomag válasz a frontend számára.
Tartalmazza a feloldott (resolved) árazást a felhasználó régiója alapján.
"""
id: int
name: str
rules: SubscriptionRulesModel
is_custom: bool
resolved_pricing: Optional[PricingZoneModel] = Field(
default=None,
description="A felhasználó régiójára feloldott árazás (egyetlen PricingZoneModel).",
)
model_config = ConfigDict(from_attributes=True)
class SubscriptionTierCreate(BaseModel):
"""Új előfizetési csomag létrehozása (POST /)."""
name: str = Field(..., min_length=2, max_length=100, description="Csomag neve (pl. private_pro_v2)")