RABC fejlesztése és beélesítése hibák kijavításával
This commit is contained in:
@@ -14,8 +14,11 @@ from sqlalchemy.orm import selectinload
|
||||
from pydantic import BaseModel, Field, ConfigDict, EmailStr
|
||||
|
||||
from app.db.session import get_db
|
||||
from app.api.deps import get_current_user
|
||||
from app.api.deps import get_current_user, RequireSystemCapability
|
||||
from app.schemas.organization import CorpOnboardIn, CorpOnboardResponse, OrganizationUpdate, OrganizationResponse
|
||||
from app.schemas.subscription import SubscriptionTierResponse
|
||||
from app.services.billing_engine import upgrade_org_subscription
|
||||
from app.core.capabilities import Capability
|
||||
from app.models.marketplace.organization import Organization, OrgType, OrganizationMember, Branch, OrgUserRole, OrgRole
|
||||
from app.models.identity import User, OneTimePassword
|
||||
from app.core.config import settings
|
||||
@@ -219,7 +222,14 @@ async def get_my_organizations(
|
||||
"subscription_plan": o.subscription_plan,
|
||||
"org_type": o.org_type.value if hasattr(o.org_type, 'value') else str(o.org_type),
|
||||
"visual_settings": o.visual_settings if hasattr(o, 'visual_settings') else None,
|
||||
"user_role": _get_user_role(o, current_user.id)
|
||||
"user_role": _get_user_role(o, current_user.id),
|
||||
# ── Cím adatok (Address fields) ──
|
||||
"address_zip": o.address_zip,
|
||||
"address_city": o.address_city,
|
||||
"address_street_name": o.address_street_name,
|
||||
"address_street_type": o.address_street_type,
|
||||
"address_house_number": o.address_house_number,
|
||||
"address_hrsz": o.address_hrsz,
|
||||
}
|
||||
for o in orgs
|
||||
]
|
||||
@@ -663,19 +673,7 @@ async def update_organization(
|
||||
Csak OWNER vagy ADMIN jogosultságú felhasználó módosíthatja.
|
||||
"""
|
||||
# 1. Jogosultság ellenőrzése
|
||||
stmt_member = select(OrganizationMember).where(
|
||||
(OrganizationMember.organization_id == org_id) &
|
||||
(OrganizationMember.user_id == current_user.id) &
|
||||
(OrganizationMember.role.in_(["OWNER", "ADMIN"]))
|
||||
)
|
||||
member = (await db.execute(stmt_member)).scalar_one_or_none()
|
||||
if not member:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=_("ORGANIZATION.ERROR.ACCESS_DENIED")
|
||||
)
|
||||
|
||||
# 2. Szervezet lekérése
|
||||
# Először lekérjük a szervezetet, hogy ellenőrizhessük az owner_id-t is
|
||||
stmt_org = select(Organization).where(
|
||||
Organization.id == org_id,
|
||||
Organization.is_deleted == False
|
||||
@@ -688,7 +686,23 @@ async def update_organization(
|
||||
detail=_("ORGANIZATION.ERROR.NOT_FOUND")
|
||||
)
|
||||
|
||||
# 3. JSONB merge visual_settings esetén
|
||||
# Jogosultság: OWNER/ADMIN a OrganizationMember táblában VAGY owner_id a Organization-ben
|
||||
is_owner_by_field = (org.owner_id == current_user.id)
|
||||
|
||||
stmt_member = select(OrganizationMember).where(
|
||||
(OrganizationMember.organization_id == org_id) &
|
||||
(OrganizationMember.user_id == current_user.id) &
|
||||
(OrganizationMember.role.in_(["OWNER", "ADMIN"]))
|
||||
)
|
||||
member = (await db.execute(stmt_member)).scalar_one_or_none()
|
||||
|
||||
if not member and not is_owner_by_field:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_403_FORBIDDEN,
|
||||
detail=_("ORGANIZATION.ERROR.ACCESS_DENIED")
|
||||
)
|
||||
|
||||
# 2. JSONB merge visual_settings esetén
|
||||
update_dict = update_data.model_dump(exclude_unset=True)
|
||||
|
||||
if "visual_settings" in update_dict and update_dict["visual_settings"] is not None:
|
||||
@@ -717,6 +731,63 @@ async def update_organization(
|
||||
return OrganizationResponse.model_validate(org)
|
||||
|
||||
|
||||
# ── P0 FEATURE: SUBSCRIPTION & PACKAGE ASSIGNMENT BRIDGE ──
|
||||
|
||||
class SubscriptionAssignIn(BaseModel):
|
||||
"""PUT body a szervezet előfizetési csomagjának beállításához."""
|
||||
tier_id: int = Field(..., description="SubscriptionTier ID a system.subscription_tiers táblából")
|
||||
|
||||
|
||||
@router.put("/{org_id}/subscription", response_model=dict)
|
||||
async def assign_organization_subscription(
|
||||
org_id: int,
|
||||
body: SubscriptionAssignIn,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user),
|
||||
_: bool = Depends(RequireSystemCapability(Capability.CAN_MANAGE_SUBSCRIPTIONS))
|
||||
):
|
||||
"""
|
||||
Szervezet előfizetési csomagjának beállítása (Subscription & Package Assignment Bridge).
|
||||
|
||||
P0 Feature: This endpoint assigns a subscription_tier to an organization.
|
||||
It requires the 'can_manage_subscriptions' system capability (SUPERADMIN or ADMIN).
|
||||
|
||||
The endpoint:
|
||||
1. Validates the tier exists in system.subscription_tiers
|
||||
2. Sets subscription_tier_id on the Organization record
|
||||
3. Updates subscription_plan and base_asset_limit from tier rules
|
||||
4. Creates/updates a finance.org_subscriptions audit record
|
||||
"""
|
||||
try:
|
||||
result = await upgrade_org_subscription(
|
||||
db=db,
|
||||
org_id=org_id,
|
||||
tier_id=body.tier_id,
|
||||
actor_user_id=current_user.id
|
||||
)
|
||||
await db.commit()
|
||||
|
||||
await security_service.log_event(
|
||||
db, user_id=current_user.id, action="ORG_SUBSCRIPTION_ASSIGNED",
|
||||
severity=LogSeverity.info, target_type="Organization", target_id=str(org_id),
|
||||
new_data={"tier_id": body.tier_id, "tier_name": result.get("tier_name")}
|
||||
)
|
||||
|
||||
return result
|
||||
except ValueError as e:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail=str(e)
|
||||
)
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Subscription assignment failed: org_id={org_id}, tier_id={body.tier_id}: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=_("ORGANIZATION.ERROR.DATABASE_ERROR", error=str(e))
|
||||
)
|
||||
|
||||
|
||||
# ── CÉG CSATLAKOZÁS ÉS ÁRVA CÉG ÁTVÉTEL ──
|
||||
|
||||
class JoinRequestIn(BaseModel):
|
||||
|
||||
Reference in New Issue
Block a user