FIX: Import error and enhanced atomized address structure for organizations
This commit is contained in:
Binary file not shown.
@@ -1,9 +1,12 @@
|
|||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
from sqlalchemy import select
|
from sqlalchemy import select
|
||||||
|
from typing import List
|
||||||
from app.db.session import get_db
|
from app.db.session import get_db
|
||||||
from app.schemas.organization import CorpOnboardIn, CorpOnboardResponse
|
from app.schemas.organization import CorpOnboardIn, CorpOnboardResponse
|
||||||
from app.models.organization import Organization, OrgType
|
from app.models.organization import Organization, OrgType, OrganizationMember
|
||||||
|
# JAVÍTOTT IMPORT: A User modell helye a projektben
|
||||||
|
from app.models.user import User
|
||||||
from app.core.config import settings
|
from app.core.config import settings
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
@@ -18,18 +21,15 @@ async def onboard_organization(
|
|||||||
db: AsyncSession = Depends(get_db)
|
db: AsyncSession = Depends(get_db)
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
Új szervezet (cég/szerviz) rögzítése.
|
Új szervezet (cég/szerviz) rögzítése bővített névvel és atomizált címmel.
|
||||||
- Magyar adószám validáció (XXXXXXXX-Y-ZZ).
|
|
||||||
- Duplikáció ellenőrzés adószám alapján.
|
|
||||||
- NAS mappa és DB rekord létrehozása.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# 1. Magyar adószám validáció
|
# 1. Magyar adószám validáció (XXXXXXXX-Y-ZZ)
|
||||||
if org_in.country_code == "HU":
|
if org_in.country_code == "HU":
|
||||||
if not re.match(r"^\d{8}-\d-\d{2}$", org_in.tax_number):
|
if not re.match(r"^\d{8}-\d-\d{2}$", org_in.tax_number):
|
||||||
raise HTTPException(
|
raise HTTPException(
|
||||||
status_code=status.HTTP_400_BAD_REQUEST,
|
status_code=status.HTTP_400_BAD_REQUEST,
|
||||||
detail="Érvénytelen magyar adószám formátum! (Példa: 12345678-1-12)"
|
detail="Érvénytelen magyar adószám formátum!"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 2. Duplikáció ellenőrzés
|
# 2. Duplikáció ellenőrzés
|
||||||
@@ -41,30 +41,76 @@ async def onboard_organization(
|
|||||||
detail="Ezzel az adószámmal már regisztráltak céget!"
|
detail="Ezzel az adószámmal már regisztráltak céget!"
|
||||||
)
|
)
|
||||||
|
|
||||||
# 3. Mentés (Dinamikus státusszal és kisbetűs Enummal)
|
# 3. Biztosítunk egy tulajdonost (MVP fix: keresünk egy létező usert)
|
||||||
|
user_stmt = select(User).limit(1)
|
||||||
|
user_res = await db.execute(user_stmt)
|
||||||
|
test_user = user_res.scalar_one_or_none()
|
||||||
|
if not test_user:
|
||||||
|
raise HTTPException(status_code=400, detail="Nincs regisztrált felhasználó a rendszerben!")
|
||||||
|
|
||||||
|
# 4. Mentés (Szervezet létrehozása atomizált adatokkal és név-hierarchiával)
|
||||||
new_org = Organization(
|
new_org = Organization(
|
||||||
|
full_name=org_in.full_name,
|
||||||
name=org_in.name,
|
name=org_in.name,
|
||||||
|
display_name=org_in.display_name,
|
||||||
tax_number=org_in.tax_number,
|
tax_number=org_in.tax_number,
|
||||||
reg_number=org_in.reg_number,
|
reg_number=org_in.reg_number,
|
||||||
headquarters_address=org_in.headquarters_address,
|
address_zip=org_in.address_zip,
|
||||||
|
address_city=org_in.address_city,
|
||||||
|
address_street_name=org_in.address_street_name,
|
||||||
|
address_street_type=org_in.address_street_type,
|
||||||
|
address_house_number=org_in.address_house_number,
|
||||||
|
address_hrsz=org_in.address_hrsz,
|
||||||
|
address_stairwell=org_in.address_stairwell,
|
||||||
|
address_floor=org_in.address_floor,
|
||||||
|
address_door=org_in.address_door,
|
||||||
country_code=org_in.country_code,
|
country_code=org_in.country_code,
|
||||||
org_type=OrgType.business, # Most már kisbetűs 'business' kerül beküldésre
|
org_type=OrgType.business,
|
||||||
status="pending_verification"
|
status="pending_verification"
|
||||||
)
|
)
|
||||||
|
|
||||||
db.add(new_org)
|
db.add(new_org)
|
||||||
await db.flush() # ID generálás a NAS-hoz
|
await db.flush()
|
||||||
|
|
||||||
# 4. NAS Mappa létrehozása
|
# 5. TULAJDONOS RÖGZÍTÉSE (Membership lánc)
|
||||||
|
owner_member = OrganizationMember(
|
||||||
|
organization_id=new_org.id,
|
||||||
|
user_id=test_user.id,
|
||||||
|
role="owner"
|
||||||
|
)
|
||||||
|
db.add(owner_member)
|
||||||
|
|
||||||
|
# 6. NAS Mappa létrehozása (Org izoláció)
|
||||||
try:
|
try:
|
||||||
base_path = getattr(settings, "NAS_STORAGE_PATH", "/mnt/nas/app_data")
|
base_path = getattr(settings, "NAS_STORAGE_PATH", "/mnt/nas/app_data")
|
||||||
org_path = os.path.join(base_path, "organizations", str(new_org.id))
|
org_path = os.path.join(base_path, "organizations", str(new_org.id))
|
||||||
os.makedirs(org_path, exist_ok=True)
|
os.makedirs(os.path.join(org_path, "documents"), exist_ok=True)
|
||||||
logger.info(f"NAS mappa létrehozva szervezetnek: {org_path}")
|
logger.info(f"NAS mappa struktúra kész: {org_path}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"NAS hiba az onboardingnál: {e}")
|
logger.error(f"NAS hiba: {e}")
|
||||||
|
|
||||||
await db.commit()
|
await db.commit()
|
||||||
await db.refresh(new_org)
|
await db.refresh(new_org)
|
||||||
|
|
||||||
return {"organization_id": new_org.id, "status": new_org.status}
|
return {"organization_id": new_org.id, "status": new_org.status}
|
||||||
|
|
||||||
|
@router.get("/my", response_model=List[CorpOnboardResponse])
|
||||||
|
async def get_my_organizations(
|
||||||
|
db: AsyncSession = Depends(get_db)
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
A bejelentkezett felhasználóhoz tartozó összes cég/szervezet listázása.
|
||||||
|
"""
|
||||||
|
# MVP Teszt: Kézzel keresünk egy létező usert (később: current_user.id)
|
||||||
|
user_stmt = select(User).limit(1)
|
||||||
|
user_res = await db.execute(user_stmt)
|
||||||
|
test_user = user_res.scalar_one_or_none()
|
||||||
|
|
||||||
|
if not test_user:
|
||||||
|
return []
|
||||||
|
|
||||||
|
stmt = select(Organization).join(OrganizationMember).where(OrganizationMember.user_id == test_user.id)
|
||||||
|
result = await db.execute(stmt)
|
||||||
|
orgs = result.scalars().all()
|
||||||
|
|
||||||
|
return [{"organization_id": o.id, "status": o.status} for o in orgs]
|
||||||
Binary file not shown.
@@ -6,7 +6,6 @@ from sqlalchemy.sql import func
|
|||||||
from app.db.base import Base
|
from app.db.base import Base
|
||||||
|
|
||||||
class OrgType(str, enum.Enum):
|
class OrgType(str, enum.Enum):
|
||||||
# A tagok neveit kisbetűre állítjuk, hogy egyezzenek a Postgres Enum értékekkel
|
|
||||||
individual = "individual"
|
individual = "individual"
|
||||||
service = "service"
|
service = "service"
|
||||||
service_provider = "service_provider"
|
service_provider = "service_provider"
|
||||||
@@ -19,7 +18,27 @@ class Organization(Base):
|
|||||||
__table_args__ = {"schema": "data"}
|
__table_args__ = {"schema": "data"}
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
name = Column(String, nullable=False)
|
|
||||||
|
# --- NÉVKEZELÉS ---
|
||||||
|
full_name = Column(String, nullable=False) # Teljes hivatalos név
|
||||||
|
name = Column(String, nullable=False) # Rövidített cégnév (pl. ProfiBot Kft.)
|
||||||
|
display_name = Column(String(50)) # Alkalmazáson belüli rövidítés (pl. ProfiBot)
|
||||||
|
|
||||||
|
# --- ATOMIZÁLT CÍMKEZELÉS ---
|
||||||
|
address_zip = Column(String(10))
|
||||||
|
address_city = Column(String(100))
|
||||||
|
address_street_name = Column(String(150))
|
||||||
|
address_street_type = Column(String(50)) # utca, út, tér, dűlő, stb.
|
||||||
|
address_house_number = Column(String(20))
|
||||||
|
address_hrsz = Column(String(50)) # Helyrajzi szám
|
||||||
|
address_stairwell = Column(String(20))
|
||||||
|
address_floor = Column(String(20))
|
||||||
|
address_door = Column(String(20))
|
||||||
|
country_code = Column(String(2), default="HU")
|
||||||
|
|
||||||
|
# --- ÜZLETI ADATOK ---
|
||||||
|
tax_number = Column(String(20), unique=True, index=True)
|
||||||
|
reg_number = Column(String(50))
|
||||||
|
|
||||||
# PG_ENUM használata a Python Enum-mal szinkronizálva
|
# PG_ENUM használata a Python Enum-mal szinkronizálva
|
||||||
org_type = Column(
|
org_type = Column(
|
||||||
@@ -27,11 +46,6 @@ class Organization(Base):
|
|||||||
default=OrgType.individual
|
default=OrgType.individual
|
||||||
)
|
)
|
||||||
|
|
||||||
tax_number = Column(String(20), unique=True, index=True)
|
|
||||||
reg_number = Column(String(50))
|
|
||||||
headquarters_address = Column(String(255))
|
|
||||||
country_code = Column(String(2), default="HU")
|
|
||||||
|
|
||||||
status = Column(String(30), default="pending_verification")
|
status = Column(String(30), default="pending_verification")
|
||||||
is_deleted = Column(Boolean, default=False)
|
is_deleted = Column(Boolean, default=False)
|
||||||
|
|
||||||
@@ -52,6 +66,7 @@ class Organization(Base):
|
|||||||
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
created_at = Column(DateTime(timezone=True), server_default=func.now())
|
||||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
updated_at = Column(DateTime(timezone=True), onupdate=func.now())
|
||||||
|
|
||||||
|
# Kapcsolatok
|
||||||
assets = relationship("Asset", back_populates="organization", cascade="all, delete-orphan")
|
assets = relationship("Asset", back_populates="organization", cascade="all, delete-orphan")
|
||||||
members = relationship("OrganizationMember", back_populates="organization")
|
members = relationship("OrganizationMember", back_populates="organization")
|
||||||
owner = relationship("User", back_populates="owned_organizations")
|
owner = relationship("User", back_populates="owned_organizations")
|
||||||
|
|||||||
Binary file not shown.
@@ -4,17 +4,32 @@ from typing import Optional, List
|
|||||||
class ContactCreate(BaseModel):
|
class ContactCreate(BaseModel):
|
||||||
full_name: str
|
full_name: str
|
||||||
email: str
|
email: str
|
||||||
phone: Optional[str]
|
phone: Optional[str] = None
|
||||||
contact_type: str = "primary"
|
contact_type: str = "primary"
|
||||||
|
|
||||||
class CorpOnboardIn(BaseModel):
|
class CorpOnboardIn(BaseModel):
|
||||||
name: str
|
# Névkezelés
|
||||||
|
full_name: str = Field(..., description="Teljes hivatalos név")
|
||||||
|
name: str = Field(..., description="Rövidített cégnév (pl. ProfiBot Kft.)")
|
||||||
|
display_name: str = Field(..., description="Alkalmazáson belüli rövidítés (pl. ProfiBot)")
|
||||||
|
|
||||||
tax_number: str
|
tax_number: str
|
||||||
country_code: str = "HU"
|
country_code: str = "HU"
|
||||||
reg_number: Optional[str]
|
reg_number: Optional[str] = None
|
||||||
headquarters_address: str
|
|
||||||
contacts: Optional[List[ContactCreate]] = []
|
# Atomizált Címkezelés
|
||||||
|
address_zip: str
|
||||||
|
address_city: str
|
||||||
|
address_street_name: Optional[str] = None
|
||||||
|
address_street_type: Optional[str] = None # utca, út, tér, dűlő
|
||||||
|
address_house_number: Optional[str] = None
|
||||||
|
address_hrsz: Optional[str] = None # Helyrajzi szám (ha nincs utca/házszám)
|
||||||
|
address_stairwell: Optional[str] = None
|
||||||
|
address_floor: Optional[str] = None
|
||||||
|
address_door: Optional[str] = None
|
||||||
|
|
||||||
|
contacts: List[ContactCreate] = []
|
||||||
|
|
||||||
class CorpOnboardResponse(BaseModel):
|
class CorpOnboardResponse(BaseModel):
|
||||||
organization_id: int
|
organization_id: int
|
||||||
status: str = "pending_verification"
|
status: str
|
||||||
@@ -153,3 +153,19 @@ A fejlesztések rendben tartásához javaslom a **`17_DEVELOPER_NOTES_AND_PITFAL
|
|||||||
- **NAS Integration:** Automata mappastruktúra létrehozása az eszközöknek (`/assets/{uuid}`).
|
- **NAS Integration:** Automata mappastruktúra létrehozása az eszközöknek (`/assets/{uuid}`).
|
||||||
- **Data Model:** `privacy_level` és `status` mezők hozzáadva az Asset modellhez.
|
- **Data Model:** `privacy_level` és `status` mezők hozzáadva az Asset modellhez.
|
||||||
- **Bugfix:** SQLAlchemy `TypeError` javítva a modell és a séma szinkronizálásával.
|
- **Bugfix:** SQLAlchemy `TypeError` javítva a modell és a séma szinkronizálásával.
|
||||||
|
|
||||||
|
## [0.5.0] - 2026-02-07
|
||||||
|
### ✨ Corporate & CRM Foundation
|
||||||
|
- **Corporate Onboarding:** `POST /api/v1/organizations/onboard` végpont élesítve.
|
||||||
|
- **Validation:** Magyar adószám (HU) formátum ellenőrzés beépítve.
|
||||||
|
- **Status Management:** Bevezetve a `pending_verification` állapot a szervezetekhez.
|
||||||
|
- **Database:** PostgreSQL `orgtype` Enum szinkronizálva a Python modellel (kisbetűs mapping).
|
||||||
|
- **NAS:** Automata szervezeti mappa-izoláció (`/organizations/{id}`).
|
||||||
|
|
||||||
|
[0.5.1] - 2026-02-07
|
||||||
|
|
||||||
|
FIX: ModuleNotFoundError javítva az importok szinkronizálásával.
|
||||||
|
|
||||||
|
DATA: Atomizált címmezők hozzáadva a data.organizations táblához.
|
||||||
|
|
||||||
|
LOGIC: Háromszintű névkezelés (Hivatalos, Rövid, Display) bevezetve.
|
||||||
@@ -176,3 +176,31 @@ A rendszer az alábbi kategóriákat különbözteti meg az életút- és költs
|
|||||||
- **Construction:** Munkagépek (markolók, daruk).
|
- **Construction:** Munkagépek (markolók, daruk).
|
||||||
- **Agriculture:** Mezőgazdasági vontatók, kombájnok.
|
- **Agriculture:** Mezőgazdasági vontatók, kombájnok.
|
||||||
- **Micro-mobility:** E-roller, e-bike flották.
|
- **Micro-mobility:** E-roller, e-bike flották.
|
||||||
|
|
||||||
|
# 18. ASSET ÉS FLOTTA SPECIFIKÁCIÓ (v1.1)
|
||||||
|
|
||||||
|
## 1. Dokumentum Tárolási és Feldolgozási Stratégia
|
||||||
|
A rendszer a tárhelyköltségek optimalizálása és a gyors elérés érdekében hibrid tárolást alkalmaz:
|
||||||
|
|
||||||
|
### A) Tárolási típusok
|
||||||
|
- **Vault (Tartós):** Jogilag kritikus okmányok (Alapító okirat, Forgalmi, Adásvételi).
|
||||||
|
- Tárolás: NAS, hash-elt fájlnévvel.
|
||||||
|
- Elérhetőség: Korlátlan ideig, amíg az Asset/Szervezet aktív.
|
||||||
|
- **Ephemeral (Ideiglenes):** Napi bizonylatok (Parkolási jegy, Tankolási nyugta).
|
||||||
|
- Folyamat: Feltöltés -> OCR adatkinyerés -> Adatbázis rögzítés -> Kép törlése (90 nap után).
|
||||||
|
|
||||||
|
### B) Képoptimalizálási Motor
|
||||||
|
Minden feltöltött dokumentum (JPG/PNG) automata feldolgozáson esik át:
|
||||||
|
- Átmretezés: Max 1600px szélesség.
|
||||||
|
- Formátum konverzió: WebP (veszteségmentes tömörítés).
|
||||||
|
- Eredmény: ~80-90%-os tárhely megtakarítás olvashatóság vesztése nélkül.
|
||||||
|
|
||||||
|
## 2. Címkezelési Protokoll (Atomizált Adatok)
|
||||||
|
A pontos szűrés és a hivatalos iratok generálása érdekében a címeket az alábbi bontásban tároljuk:
|
||||||
|
- Irányítószám (IRSZ)
|
||||||
|
- Település (Város)
|
||||||
|
- Közterület neve
|
||||||
|
- Közterület jellege (utca, út, tér, stb. - választható listából)
|
||||||
|
- Házszám (emelet, ajtó, lépcsőház kiegészítéssel)
|
||||||
|
|
||||||
|
Címkezelés (v2.0): Minden magánszemély és szervezet címét atomizált formában tároljuk (IRSZ, Város, Utca, Házszám, HRSZ). Ez alapfeltétele a későbbi flotta-riportoknak és a pontos térképi megjelenítésnek.
|
||||||
Reference in New Issue
Block a user