10 KiB
🔧 Javítási Terv: Admin Garázs Előfizetés Fül — Hibás Csomagadatok
Gitea Issue: #301 Hatáskör: Backend (admin_organizations.py) + Frontend (index.vue) Típus: Bug Prioritás: Magas
📋 Összefoglaló
A frontend admin frontend_admin/pages/garages/[id]/index.vue garázs részletek oldal Előfizetés (Subscription) fülén a kiválasztott csomaghoz tartozó adatok (asset_limit, branch_limit, asset_count) nem a valóságot tükrözik. A backend SubscriptionSummary Pydantic modellje hiányos, és a limit-ek kinyerése törékeny.
🔍 Feltárt Hibák (Root Causes)
🐛 #1: branch_limit mező hiányzik a SubscriptionSummary-ból
Hely: backend/app/api/v1/endpoints/admin_organizations.py:116-124
A SubscriptionSummary Pydantic modell jelenleg nem tartalmaz branch_limit mezőt. A frontend template azonban használja:
// frontend_admin/pages/garages/[id]/index.vue:526
{{ garage.branches?.length || 0 }} / {{ garage.subscription?.branch_limit || '∞' }}
Mivel branch_limit sosem érkezik a backend válaszban, a mező undefined, és a frontend mindig '∞'-t (végtelen) jelenít meg.
🐛 #2: asset_limit kinyerése logikai hibás
Hely: backend/app/api/v1/endpoints/admin_organizations.py:462-465
asset_limit = (
rules.get("max_vehicles")
or active_sub.tier.feature_capabilities.get("max_vehicles")
or 1
)
Problémák:
- Python
oroperátor falsy-nak tekinti a0-t — ha a tiermax_vehicles: 0, azormiattfeature_capabilities-re ugrik tovább. - Ha
feature_capabilities-ben sincsmax_vehicles, vagy az is0, azasset_limit = 1lesz. - Ha a tier más kulcsnevet használ (pl.
vehicle_limit,max_assets), az érték hibásan1-re esik vissza.
🐛 #3: GarageDetailsResponse nem tartalmaz branches mezőt
Hely: backend/app/api/v1/endpoints/admin_organizations.py:127-175
A GarageDetailsResponse modellben nincs branches lista. A frontend garage.branches?.length használ, de a backend nem ad vissza branch adatokat, így a kihasználtsági mutató mindig 0 / ∞.
🐛 #4: asset_count hardkódolt (mindig 0)
Hely: backend/app/api/v1/endpoints/admin_organizations.py:473
asset_count=0,
Az asset_count mindig 0, sosem számolja le a szervezet tényleges járműveit.
🐛 #5: Lejárt előfizetés is aktívként jelenhet meg
Hely: backend/app/api/v1/endpoints/admin_organizations.py:414-425
.where(
OrganizationSubscription.org_id == org_id,
OrganizationSubscription.is_active == True,
)
A lekérdezés nem ellenőrzi a valid_until >= now() feltételt. Ha egy subscription is_active=True de valid_until már lejárt, a backend továbbra is aktívként kezeli.
🐛 #6: Fallback path hiányos adatokkal
Hely: backend/app/api/v1/endpoints/admin_organizations.py:476-488
Ha nincs aktív OrganizationSubscription, a kód a org.subscription_tier denormalizált kapcsolatra esik vissza. Ez a path nem adja vissza a valid_from és is_active mezőket, csak a defaultokat használja.
🛠️ Javítási Terv
1. Backend: SubscriptionSummary bővítése
Fájl: backend/app/api/v1/endpoints/admin_organizations.py
1/a. branch_limit mező hozzáadása (sor 116-124 körül)
A SubscriptionSummary osztály bővítése:
class SubscriptionSummary(BaseModel):
tier_name: str = "Free/Fallback"
tier_level: int = 0
valid_from: Optional[str] = None
expires_at: Optional[str] = None
is_active: bool = True
asset_count: int = 0
asset_limit: int = 1
branch_limit: int = 0 # 🆕 HIÁNYZÓ MEZŐ
1/b. asset_limit kinyerés javítása (sor 462-465)
Az or helyett használj explicit max() logikát:
# JAVÍTVA: explicit max() használata az or helyett
rules = active_sub.tier.rules or {}
fc = active_sub.tier.feature_capabilities or {}
# Kompatibilitás: több kulcsnév támogatása
vehicle_limit_keys = ["max_vehicles", "vehicle_limit", "max_assets"]
asset_limit = max(
(rules.get(k, 0) for k in vehicle_limit_keys),
default=0
)
asset_limit = max(
asset_limit,
max((fc.get(k, 0) for k in vehicle_limit_keys), default=0),
)
asset_limit = max(asset_limit, 1) # minimum 1
Ugyanezt a javítást kell elvégezni a fallback path-on is (sor 477-482).
1/c. branch_limit kitermelése a rules-ből (sor 462-488 között)
# 🆕 HIÁNYZÓ: branch_limit kinyerése
branch_limit_keys = ["max_branches", "branch_limit", "max_garages"]
branch_limit = max(
(rules.get(k, 0) for k in branch_limit_keys),
default=0
)
branch_limit = max(
branch_limit,
max((fc.get(k, 0) for k in branch_limit_keys), default=0),
)
Ezt add hozzá mindkét path-hoz (aktív sub + fallback):
subscription_summary = SubscriptionSummary(
...
asset_limit=asset_limit,
branch_limit=branch_limit, # 🆕
)
1/d. asset_count dinamikus számolása
Az asset_count-ot ne hardkódold. Számold meg a szervezet járműveit. Ehhez be kell tölteni az Organization.assets relationship-et a SELECT-nél:
org_stmt = (
select(Organization)
.options(
selectinload(Organization.subscription_tier),
selectinload(Organization.owner).selectinload(User.person),
selectinload(Organization.assets), # 🆕
)
.where(Organization.id == org_id)
)
Majd a subscription summary építésénél:
asset_count = len(org.assets) if hasattr(org, 'assets') and org.assets else 0
VAGY használj explicit lekérdezést:
from sqlalchemy import func
from app.models.asset import AssetAssignment
asset_count_stmt = select(func.count(AssetAssignment.id)).where(
AssetAssignment.organization_id == org_id
)
asset_count_result = await db.execute(asset_count_stmt)
asset_count = asset_count_result.scalar() or 0
1/e. Lejárt előfizetés szűrés (sor 414-425)
from sqlalchemy import or_ as sa_or
sub_stmt = (
select(OrganizationSubscription)
.options(selectinload(OrganizationSubscription.tier))
.where(
OrganizationSubscription.org_id == org_id,
OrganizationSubscription.is_active == True,
# 🆕 HIÁNYZÓ: lejárati dátum ellenőrzése
sa_or_(
OrganizationSubscription.valid_until.is_(None),
OrganizationSubscription.valid_until >= func.now(),
),
)
.order_by(OrganizationSubscription.id.desc())
.limit(1)
)
2. Backend: GarageDetailsResponse bővítése branches listával
Fájl: backend/app/api/v1/endpoints/admin_organizations.py
2/a. BranchBrief Pydantic modell létrehozása (a SubscriptionSummary után)
class BranchBrief(BaseModel):
"""Fióktelep rövid adatai."""
id: int
name: str
city: Optional[str] = None
is_active: bool = True
2/b. GarageDetailsResponse bővítése branches-szel
class GarageDetailsResponse(BaseModel):
...
subscription: Optional[SubscriptionSummary] = None
branches: List[BranchBrief] = [] # 🆕 HIÁNYZÓ MEZŐ
primary_contact: Optional[ContactPersonInfo] = None
...
2/c. Branch adatok lekérdezése a végpontban
# 🆕 HIÁNYZÓ: branch adatok lekérése
from app.models.location import Branch
branches_stmt = select(Branch).where(Branch.organization_id == org_id)
branches_result = await db.execute(branches_stmt)
branches_data = branches_result.scalars().all()
branch_list = [
BranchBrief(
id=b.id,
name=b.name,
city=b.city,
is_active=b.is_active,
)
for b in branches_data
]
3. Frontend: Ellenőrző módosítások
Fájl: frontend_admin/pages/garages/[id]/index.vue
3/a. branchQuotaPercent computed property ellenőrzése
Ellenőrizd, hogy a számított property helyesen működik a nem-undefined branch_limit-nel:
const branchQuotaPercent = computed(() => {
if (!garage.value?.subscription?.branch_limit) return 0
const branchCount = garage.value?.branches?.length || 0
return (branchCount / garage.value.subscription.branch_limit) * 100
})
✅ Elfogadási Kritériák (Acceptance Criteria)
- Branch limit megjelenítése: A branches utilization sorban a backend által küldött
branch_limitérték jelenjen meg (ne mindig∞). - Asset limit helyes értéke: A kiválasztott csomag
rulesJSONB-jében lévőmax_vehicles(vagy alternatív kulcs) értéke jelenjen meg. - Asset count valós adat: A járművek száma ne
0legyen, hanem a tényleges asset-ek száma. - Branch count valós adat: A branch-ek száma ne mindig
0legyen, hanem a tényleges branch-ek száma abrancheslistából. - Lejárt előfizetés: Ne jelenjen meg aktív előfizetésként, ha a
valid_untilmár lejárt. - Fallback: Ha nincs aktív subscription, a
org.subscription_tierdenormalizált mezőből származó adatok legyenek teljesek (valid_from, is_active).
📦 Módosítandó Fájlok
| # | Fájl | Változtatás |
|---|---|---|
| 1 | backend/app/api/v1/endpoints/admin_organizations.py |
SubscriptionSummary bővítése branch_limit-lel, asset_limit logika javítása, asset_count dinamikus számolása, GarageDetailsResponse bővítése branches-szel + BranchBrief, lejárati dátum szűrés |
| 2 | frontend_admin/pages/garages/[id]/index.vue |
branchQuotaPercent számítás ellenőrzése |
🧪 Tesztelési Terv
- Létező garázs megnyitása: Ellenőrizd, hogy az Előfizetés fül a tényleges csomag adatait mutatja.
- Csomagváltás: Válts csomagot, és ellenőrizd, hogy a frissítés után a helyes adatok jelennek meg.
- Lejárt előfizetés: Állíts be egy lejárt
valid_untildátumot, és ellenőrizd, hogy a garázs fallback csomag adatait mutatja. - Több branch-es garázs: Ellenőrizd, hogy a branch count és limit helyesen jelenik meg.
- API teszt:
GET /api/v1/admin/organizations/{id}/details- ellenőrizd, hogy a válasz tartalmazza asubscription.branch_limitésbranchesmezőket.
⚡ Végrehajtási Sorrend
- Code mód váltása
- Backend módosítások elvégzése (
admin_organizations.py) - Sync engine futtatása:
docker compose exec sf_api python3 /app/backend/app/scripts/sync_engine.py - API konténer újraindítása
- Frontend (ha szükséges) módosítása
- Manuális tesztelés a böngészőből