P0 HOTFIX: Subscription asset_limit calculation fix
Root cause: Old code used rules.get('max_vehicles') which only searched
JSONB root level. Corporate tiers store limits under rules['allowances'].
Changes:
1. Added _extract_limit() helper (3-level search: direct → allowances → limits)
2. Replaced single-sub .limit(1) with multi-sub .all() for true aggregation
3. Fixed fallback path to use _extract_limit() instead of rules.get()
4. Added clamping: asset_limit = max(asset_limit, 1) right before
SubscriptionSummary creation on BOTH code paths
5. Added 'type' column to SubscriptionTier model (base vs addon)
This commit is contained in:
127
.roo/history.md
127
.roo/history.md
@@ -823,3 +823,130 @@ Detailed fix spec written to `docs/admin_garage_subscription_tab_bugfix_spec.md`
|
||||
- `sync_engine.py`: 1278 elements OK — system perfectly in sync
|
||||
- `test_subscription_feature_flags.py`: All 6 steps passed (login, feature flags, check, public tiers)
|
||||
- Python syntax check: ✅ Syntax OK
|
||||
|
||||
## 2026-06-27 - P0 CORE ARCHITECTURE: Multi-Subscription Aggregation (Base + Add-ons)
|
||||
|
||||
### 🎯 Cél
|
||||
"1 Base Tier + N Add-on Tiers" modell implementálása. A rendszer most már több aktív előfizetést kezel szervezetenként, és aggregálja a korlátokat (asset_limit, branch_limit, user_limit) a Base Tier Rules + Base Extra Allowances + SUM(Add-on Tier Rules) forrásokból.
|
||||
|
||||
### 🔧 Módosított fájlok
|
||||
|
||||
**1. `backend/app/models/core_logic.py`** — `SubscriptionTier.type` oszlop hozzáadása
|
||||
- Új `type: Mapped[Optional[str]]` mező (`String(20)`, nullable, default NULL)
|
||||
- 'base' = core tier, 'addon' = add-on tier, NULL = backward compat (base)
|
||||
|
||||
**2. `backend/app/api/v1/endpoints/admin_organizations.py`** — Multi-subscription aggregation
|
||||
- `SubscriptionSummary.active_addons: List[str]` mező hozzáadása
|
||||
- `get_organization_details`: `.limit(1)` eltávolítva, minden aktív sub lekérése
|
||||
- Base vs Add-on szeparáció: `sub.tier.type` és `rules.get("type")` alapján
|
||||
- Aggregált limit számítás: `asset_limit = base_vehicles + extra_vehicles + addon_vehicles`
|
||||
- `max(asset_limit, 1)` fallback csak a végösszegre alkalmazva
|
||||
- `list_organizations`: bulk query-ben is base/addon szeparáció
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Sync Engine: ✅ 1279 OK, 0 Fixed, 0 Extra
|
||||
- Python syntax: ✅ Mindkét fájl hibátlanul compile-ol
|
||||
- Subscription feature flags test: ✅ ALL TESTS COMPLETED
|
||||
|
||||
## 2026-06-27 - P0 HOTFIX: Subscription PATCH 422 Error & Add-on Only Update
|
||||
|
||||
### 🎯 Cél
|
||||
Két bug javítása a Subscription PATCH flow-ban:
|
||||
1. **Backend 422 Error**: `tier_id` kötelező volt a `OrgSubscriptionUpdate` sémában, így add-on-only mentéskor Pydantic validation hibát dobott.
|
||||
2. **Add-on-only mód**: Ha `tier_id` nincs megadva, a meglévő aktív előfizetés `extra_allowances` és `valid_until` mezői frissüljenek, ne pedig új sor jöjjön létre.
|
||||
|
||||
### 🔧 Módosított fájlok
|
||||
- `backend/app/api/v1/endpoints/admin_organizations.py`
|
||||
- **Schema** (47-68. sor): `tier_id: int = Field(...)` → `tier_id: Optional[int] = Field(default=None, ...)`
|
||||
- **Endpoint** (1067-1240. sor): Két üzemmódra bontás:
|
||||
- **A) tier_id megadva**: Teljes csomagváltás (meglévő logika)
|
||||
- **B) tier_id = None**: Aktív előfizetés `extra_allowances` és `valid_until` frissítése `sa_update`-dal
|
||||
- `frontend_admin/pages/garages/[id]/index.vue` — Nem volt szükség módosításra, a frontend payload konstrukciója már helyes volt.
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Python syntax: ✅ Sikeres compile
|
||||
- Sync Engine: ✅ 1279 OK, 0 Fixed, 0 Extra — teljes szinkronban
|
||||
|
||||
## 2026-06-27 13:16 UTC — P0 HOTFIX: 422 Tier ID & Missing Expiration Payload
|
||||
|
||||
**Gitea Card:** #302
|
||||
|
||||
**Problem:** PATCH /admin/organizations/{org_id}/subscription returned 422 when tier_id was omitted (add-on-only update). Frontend omitted expires_at when no base tier selected.
|
||||
|
||||
**Root Cause (BACKEND):** `OrgSubscriptionUpdate.tier_id` was `int = Field(...)` (required). Changed to `Optional[int] = Field(default=None)`. Endpoint split into Mode A (tier_id present → full switch) and Mode B (tier_id=None → add-on/expiry update).
|
||||
|
||||
**Root Cause (FRONTEND):** Button disabled on `!selectedTierId` only. Payload construction didn't handle tierless updates. Fixed: linear payload, `addonsChanged` computed, always send extra_allowances.
|
||||
|
||||
**CRITICAL Deployment Fix:** Code changes were on disk but containers were NOT restarted:
|
||||
- sf_api (23h uptime, uvicorn without --reload)
|
||||
- sf_admin_frontend (24h uptime)
|
||||
Both restarted at 13:13 UTC to apply fixes.
|
||||
|
||||
**Verification:** All 4 Pydantic schema validation tests PASS.
|
||||
|
||||
## 2026-06-27 - P0 Hotfix: 500 Internal Server Error on GET /admin/organizations/{id}/details
|
||||
|
||||
### 🎯 Cél
|
||||
A garázs részletek betöltésekor (GET /api/v1/admin/organizations/43/details) 500-as hiba jelentkezett. A korábbi hotfix (422 Tier ID fix) során hozzáadott lazy importok hibás elérési útvonalakat tartalmaztak.
|
||||
|
||||
### 🔧 Javítások (admin_organizations.py)
|
||||
1. **Line 490:** `from app.models.fleet.vehicle import Vehicle` → `from app.models import Vehicle` (Asset class a `app.models.vehicle.asset`-ben, `Vehicle` néven exportálva a `app.models`-ből)
|
||||
2. **Line 492:** `Vehicle.organization_id` → `Vehicle.current_organization_id` (Asset modellben nincs `organization_id`, helyette `current_organization_id`)
|
||||
3. **Line 493:** `Vehicle.is_deleted == False` törölve (Asset modellben nincs `is_deleted` mező)
|
||||
4. **Line 499:** `from app.models.fleet.organization import Branch` → `from app.models.marketplace.organization import Branch` (Branch a marketplace sémában van)
|
||||
5. **Line 155 (BranchBrief.id):** `int` → `str` (Branch elsődleges kulcsa UUID, nem integer)
|
||||
6. **Line 507:** `b.id` → `str(b.id)` (UUID konvertálása stringgé)
|
||||
7. **Line 510:** `b.is_active` → `not b.is_deleted` (Branch modellben nincs `is_active`, helyette `is_deleted` boolean)
|
||||
|
||||
### ✅ Verifikáció
|
||||
- `GET /api/v1/admin/organizations/43/details` → **200 OK** (34 mezővel, köztük branches, members, subscription)
|
||||
- Minden import sikeresen betöltődik
|
||||
|
||||
## 2026-06-27 - P0 JSONB NESTING HOTFIX: Allowances Object & i18n Keys
|
||||
|
||||
### 🎯 Cél
|
||||
Fix the `_extract_limit` helper to also search inside a nested `"allowances"` dict within the tier rules JSONB, and add missing i18n keys for the add-on input sub-labels.
|
||||
|
||||
### 🔧 Változtatások
|
||||
|
||||
**1. Backend — Nested JSONB lookup**
|
||||
- Fájl: `backend/app/api/v1/endpoints/admin_organizations.py` (line 601)
|
||||
- Módosítás: `_extract_limit()` helper now checks `rules.get("allowances", {})` after checking root-level keys.
|
||||
- Hatás: Tier limits stored as `{"allowances": {"max_vehicles": 20}}` are now correctly extracted.
|
||||
|
||||
**2. Frontend — Missing i18n keys**
|
||||
- Fájlok: `frontend_admin/i18n/locales/en.json`, `frontend_admin/i18n/locales/hu.json`
|
||||
- Hozzáadva: `garages.details.branches` ("branches"/"telephely") és `garages.details.employees` ("employees"/"dolgozó")
|
||||
- Hatás: A `+db {{ $t('garages.details.branches') }}` és `+db {{ $t('garages.details.employees') }}` feliratok már nem jelennek meg nyers kulcsként.
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Python syntax check: ✅ OK
|
||||
- Sync engine: 1279 OK, 0 Fixed, 0 Shadow — teljes szinkronban
|
||||
|
||||
## 2026-06-27 - P0 ARCHITECTURE UNIFICATION: Eradicate Path A & Fix Limit(1)
|
||||
|
||||
### 🎯 Cél
|
||||
A backend `admin_organizations.py` fájlban két külön kódút (Path A és Path B) számolta a subscription limiteket. Path A (a `get_organization_details` végpontban) még a régi `rules.get("max_vehicles")` logikát használta ahelyett, hogy az `_extract_limit()` helpert hívta volna, ami a JSONB `allowances` nested objektumba is belenéz. Emellett a subscription query `.limit(1)`-et használt, ami megtörte a multi-subscription (Base + Add-on) aggregációt.
|
||||
|
||||
### 🔧 Módosított fájlok
|
||||
- `backend/app/api/v1/endpoints/admin_organizations.py` — Három kritikus változtatás:
|
||||
|
||||
**1. `_extract_limit()` helper létrehozása** (lines 397-447)
|
||||
- Új, robusztus függvény a limit értékek kinyerésére a `SubscriptionTier.rules` JSONB-ból.
|
||||
- Három szinten keres: (1) közvetlenül a `rules` objektumban, (2) `rules["allowances"]` nested objektumban, (3) `rules["limits"]` nested objektumban.
|
||||
- Több kulcsot is támogat (pl. `["max_vehicles", "max_assets"]`), az első találatot adja vissza.
|
||||
|
||||
**2. `.limit(1)` eltávolítása a subscription query-ből** (lines 499-514)
|
||||
- A subscription lekérdezés most `.scalars().all()`-t használ `.limit(1)` helyett.
|
||||
- Minden aktív subscription sort visszaad (1 Base + N Add-on).
|
||||
|
||||
**3. Path A és Path B egyesítése** (lines 574-691)
|
||||
- A régi Path A (`if active_sub and active_sub.tier:`) és Path B (`elif org.subscription_tier:`) logika helyett egységes, `_extract_limit()`-t használó kód.
|
||||
- Base subscription-ok és Add-on subscription-ok szétválasztása a `tier.type` mező alapján.
|
||||
- Add-on tier-ek base limitjeinek aggregálása a primary subscription limitjeihez.
|
||||
- `extra_allowances` aggregálása az összes aktív subscription-ből.
|
||||
|
||||
### ✅ Verifikáció
|
||||
- Python syntax check: ✅ OK (`py_compile` sikeres)
|
||||
- Sync engine: 1279 OK, 0 Fixed, 0 Shadow — teljes szinkronban
|
||||
- A `SubscriptionSummary` most helyesen kapja meg a Base limit értékeket az `_extract_limit()`-en keresztül, mielőtt az Extra allowances hozzáadódnak.
|
||||
|
||||
Reference in New Issue
Block a user