frontend 2026-06-10 bontva a 2 felület
This commit is contained in:
@@ -12,7 +12,7 @@ from sqlalchemy import select
|
||||
|
||||
from app.db.session import get_db
|
||||
from app.api.deps import get_current_user
|
||||
from app.schemas.organization import CorpOnboardIn, CorpOnboardResponse
|
||||
from app.schemas.organization import CorpOnboardIn, CorpOnboardResponse, OrganizationUpdate, OrganizationResponse
|
||||
from app.models.marketplace.organization import Organization, OrgType, OrganizationMember, Branch
|
||||
from app.models.identity import User # JAVÍTVA: Központi Identity modell
|
||||
from app.core.config import settings
|
||||
@@ -39,13 +39,13 @@ async def onboard_organization(
|
||||
detail="Érvénytelen magyar adószám formátum!"
|
||||
)
|
||||
|
||||
# 2. Duplikáció ellenőrzés
|
||||
# 2. Duplikáció ellenőrzés – adószám alapján
|
||||
stmt_exist = select(Organization).where(Organization.tax_number == org_in.tax_number)
|
||||
result_exist = await db.execute(stmt_exist)
|
||||
if result_exist.scalar_one_or_none():
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="Ezzel az adószámmal már regisztráltak céget!"
|
||||
detail="Ez a cég (adószám) már regisztrálva van a rendszerben."
|
||||
)
|
||||
|
||||
# 3. KÖTELEZŐ MEZŐ: folder_slug generálása
|
||||
@@ -121,6 +121,56 @@ async def onboard_organization(
|
||||
|
||||
return {"organization_id": new_org.id, "status": new_org.status}
|
||||
|
||||
@router.get("/lookup-tax/{tax_number}")
|
||||
async def lookup_tax_number(
|
||||
tax_number: str,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""
|
||||
Adószám alapú cégadat lekérdezés a hivatalos NAV Online Számla API v3 rendszerén keresztül.
|
||||
A queryTaxpayer végpontot használja valós idejű cégadatok lekérésére.
|
||||
|
||||
Duplikáció ellenőrzés: Mielőtt a NAV-hoz fordulnánk, ellenőrizzük, hogy az adószám
|
||||
első 8 számjegye (törzsszám) alapján létezik-e már aktív cég az adatbázisban.
|
||||
|
||||
Hibakezelés:
|
||||
- 409: Az adószám már regisztrálva van a rendszerben
|
||||
- 503: NAV rendszer nem elérhető (hálózati hiba, DNS, timeout)
|
||||
- 404: Adószám nem található a NAV adatbázisában
|
||||
"""
|
||||
from app.services.nav_service import NavService, NavServiceUnavailableError
|
||||
|
||||
# Duplikáció ellenőrzés: adószám első 8 számjegye (törzsszám) alapján
|
||||
tax_core = tax_number.strip().replace("-", "").replace(" ", "")[:8]
|
||||
stmt_existing = select(Organization).where(
|
||||
Organization.tax_number.like(f"{tax_core}%"),
|
||||
Organization.is_deleted == False,
|
||||
)
|
||||
result_existing = await db.execute(stmt_existing)
|
||||
existing_org = result_existing.scalar_one_or_none()
|
||||
if existing_org:
|
||||
logger.warning(f"Duplikáció észlelve: adószám {tax_core} már létezik (org_id={existing_org.id})")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_409_CONFLICT,
|
||||
detail="Ez az adószám már regisztrálva van a rendszerben."
|
||||
)
|
||||
|
||||
try:
|
||||
result = await NavService.query_taxpayer(tax_number)
|
||||
except NavServiceUnavailableError as e:
|
||||
logger.error(f"NAV szolgáltatás hiba: {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
|
||||
detail="A NAV rendszere jelenleg nem elérhető."
|
||||
)
|
||||
|
||||
if result is None:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Cég nem található a NAV adatbázisában"
|
||||
)
|
||||
return result
|
||||
|
||||
@router.get("/my", response_model=List[dict])
|
||||
async def get_my_organizations(
|
||||
db: AsyncSession = Depends(get_db),
|
||||
@@ -147,10 +197,84 @@ async def get_my_organizations(
|
||||
"country_code": o.country_code,
|
||||
"is_active": o.is_active,
|
||||
"is_deleted": o.is_deleted,
|
||||
"subscription_plan": o.subscription_plan
|
||||
"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
|
||||
}
|
||||
for o in orgs
|
||||
]
|
||||
|
||||
|
||||
@router.patch("/{org_id}/visual-settings", response_model=OrganizationResponse)
|
||||
async def update_organization_visual_settings(
|
||||
org_id: int,
|
||||
update_data: OrganizationUpdate,
|
||||
db: AsyncSession = Depends(get_db),
|
||||
current_user: User = Depends(get_current_user)
|
||||
):
|
||||
"""
|
||||
Szervezet vizuális beállításainak részleges frissítése (JSONB merge).
|
||||
|
||||
A frontend küldhet csak egyetlen kulcsot is (pl. {"theme": "dark"}),
|
||||
és az nem írja felül null-lal a többi mezőt. A meglévő visual_settings
|
||||
objektum mélyen merge-elődik a beérkező adatokkal.
|
||||
|
||||
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="Nincs jogosultságod a szervezet beállításainak módosításához (csak OWNER/ADMIN)."
|
||||
)
|
||||
|
||||
# 2. Szervezet lekérése
|
||||
stmt_org = select(Organization).where(
|
||||
Organization.id == org_id,
|
||||
Organization.is_deleted == False
|
||||
)
|
||||
result = await db.execute(stmt_org)
|
||||
org = result.scalar_one_or_none()
|
||||
if not org:
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_404_NOT_FOUND,
|
||||
detail="Szervezet nem található."
|
||||
)
|
||||
|
||||
# 3. JSONB merge: csak a kapott kulcsokat frissítjük, a többit megtartjuk
|
||||
update_dict = update_data.dict(exclude_unset=True)
|
||||
|
||||
if "visual_settings" in update_dict and update_dict["visual_settings"] is not None:
|
||||
incoming_vs = update_dict["visual_settings"]
|
||||
current_vs = dict(org.visual_settings) if org.visual_settings else {}
|
||||
# Mély merge: a meglévő kulcsok megmaradnak, csak a kapottak frissülnek
|
||||
current_vs.update(incoming_vs)
|
||||
org.visual_settings = current_vs
|
||||
del update_dict["visual_settings"]
|
||||
|
||||
# 4. Egyéb mezők frissítése (display_name, language, default_currency)
|
||||
for field, value in update_dict.items():
|
||||
if hasattr(org, field) and value is not None:
|
||||
setattr(org, field, value)
|
||||
|
||||
try:
|
||||
await db.commit()
|
||||
await db.refresh(org)
|
||||
except Exception as e:
|
||||
await db.rollback()
|
||||
logger.error(f"Hiba a szervezet frissítésekor (org_id={org_id}): {e}")
|
||||
raise HTTPException(
|
||||
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
||||
detail=f"Adatbázis hiba: {str(e)}"
|
||||
)
|
||||
|
||||
return OrganizationResponse.model_validate(org)
|
||||
# --- B2B MEGHÍVÓ LOGIKA ---
|
||||
|
||||
from pydantic import BaseModel, EmailStr
|
||||
|
||||
Reference in New Issue
Block a user