diff --git a/backend/app/api/v1/endpoints/__pycache__/organizations.cpython-312.pyc b/backend/app/api/v1/endpoints/__pycache__/organizations.cpython-312.pyc index aa2ce1d..ebc0497 100644 Binary files a/backend/app/api/v1/endpoints/__pycache__/organizations.cpython-312.pyc and b/backend/app/api/v1/endpoints/__pycache__/organizations.cpython-312.pyc differ diff --git a/backend/app/api/v1/endpoints/organizations.py b/backend/app/api/v1/endpoints/organizations.py index 5419ca3..d204a34 100644 --- a/backend/app/api/v1/endpoints/organizations.py +++ b/backend/app/api/v1/endpoints/organizations.py @@ -1,9 +1,12 @@ from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy import select +from typing import List from app.db.session import get_db 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 import os import re @@ -18,18 +21,15 @@ async def onboard_organization( db: AsyncSession = Depends(get_db) ): """ - Új szervezet (cég/szerviz) rögzítése. - - 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. + Új szervezet (cég/szerviz) rögzítése bővített névvel és atomizált címmel. """ - # 1. Magyar adószám validáció + # 1. Magyar adószám validáció (XXXXXXXX-Y-ZZ) if org_in.country_code == "HU": if not re.match(r"^\d{8}-\d-\d{2}$", org_in.tax_number): raise HTTPException( 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 @@ -41,30 +41,76 @@ async def onboard_organization( 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( + full_name=org_in.full_name, name=org_in.name, + display_name=org_in.display_name, tax_number=org_in.tax_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, - org_type=OrgType.business, # Most már kisbetűs 'business' kerül beküldésre + org_type=OrgType.business, status="pending_verification" ) 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: base_path = getattr(settings, "NAS_STORAGE_PATH", "/mnt/nas/app_data") org_path = os.path.join(base_path, "organizations", str(new_org.id)) - os.makedirs(org_path, exist_ok=True) - logger.info(f"NAS mappa létrehozva szervezetnek: {org_path}") + os.makedirs(os.path.join(org_path, "documents"), exist_ok=True) + logger.info(f"NAS mappa struktúra kész: {org_path}") except Exception as e: - logger.error(f"NAS hiba az onboardingnál: {e}") + logger.error(f"NAS hiba: {e}") await db.commit() await db.refresh(new_org) - return {"organization_id": new_org.id, "status": new_org.status} \ No newline at end of file + 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] \ No newline at end of file diff --git a/backend/app/models/__pycache__/organization.cpython-312.pyc b/backend/app/models/__pycache__/organization.cpython-312.pyc index 06b63af..468ac5b 100644 Binary files a/backend/app/models/__pycache__/organization.cpython-312.pyc and b/backend/app/models/__pycache__/organization.cpython-312.pyc differ diff --git a/backend/app/models/organization.py b/backend/app/models/organization.py index f5c9e30..25e17d4 100755 --- a/backend/app/models/organization.py +++ b/backend/app/models/organization.py @@ -6,7 +6,6 @@ from sqlalchemy.sql import func from app.db.base import Base class OrgType(str, enum.Enum): - # A tagok neveit kisbetűre állítjuk, hogy egyezzenek a Postgres Enum értékekkel individual = "individual" service = "service" service_provider = "service_provider" @@ -19,7 +18,27 @@ class Organization(Base): __table_args__ = {"schema": "data"} 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 org_type = Column( @@ -27,11 +46,6 @@ class Organization(Base): 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") is_deleted = Column(Boolean, default=False) @@ -52,6 +66,7 @@ class Organization(Base): created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column(DateTime(timezone=True), onupdate=func.now()) + # Kapcsolatok assets = relationship("Asset", back_populates="organization", cascade="all, delete-orphan") members = relationship("OrganizationMember", back_populates="organization") owner = relationship("User", back_populates="owned_organizations") diff --git a/backend/app/schemas/__pycache__/organization.cpython-312.pyc b/backend/app/schemas/__pycache__/organization.cpython-312.pyc index 4c34b25..e564d1e 100644 Binary files a/backend/app/schemas/__pycache__/organization.cpython-312.pyc and b/backend/app/schemas/__pycache__/organization.cpython-312.pyc differ diff --git a/backend/app/schemas/organization.py b/backend/app/schemas/organization.py index a0d8c76..a32ab77 100644 --- a/backend/app/schemas/organization.py +++ b/backend/app/schemas/organization.py @@ -4,17 +4,32 @@ from typing import Optional, List class ContactCreate(BaseModel): full_name: str email: str - phone: Optional[str] + phone: Optional[str] = None contact_type: str = "primary" 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 country_code: str = "HU" - reg_number: Optional[str] - headquarters_address: str - contacts: Optional[List[ContactCreate]] = [] + reg_number: Optional[str] = None + + # 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): organization_id: int - status: str = "pending_verification" \ No newline at end of file + status: str \ No newline at end of file diff --git a/docs/V01_gemini/15_Changelog.md b/docs/V01_gemini/15_Changelog.md index 4ea211e..f2488d9 100644 --- a/docs/V01_gemini/15_Changelog.md +++ b/docs/V01_gemini/15_Changelog.md @@ -152,4 +152,20 @@ A fejlesztések rendben tartásához javaslom a **`17_DEVELOPER_NOTES_AND_PITFAL - **Asset Endpoint:** `POST /api/v1/assets/` élesítve VIN validációval. - **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. -- **Bugfix:** SQLAlchemy `TypeError` javítva a modell és a séma szinkronizálásával. \ No newline at end of file +- **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. \ No newline at end of file diff --git a/docs/V01_gemini/18_ASSET_AND_FLEET_SPECIFICATION.md b/docs/V01_gemini/18_ASSET_AND_FLEET_SPECIFICATION.md index 34098b9..3eaeedb 100644 --- a/docs/V01_gemini/18_ASSET_AND_FLEET_SPECIFICATION.md +++ b/docs/V01_gemini/18_ASSET_AND_FLEET_SPECIFICATION.md @@ -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). - **Agriculture:** Mezőgazdasági vontatók, kombájnok. - **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. \ No newline at end of file