From e370ca3021d90e26a30817b9ae576b7f111587b1 Mon Sep 17 00:00:00 2001 From: Kincses Date: Sat, 7 Feb 2026 14:37:39 +0000 Subject: [PATCH] FIX: Import error and enhanced atomized address structure for organizations --- .../__pycache__/organizations.cpython-312.pyc | Bin 3793 -> 6359 bytes backend/app/api/v1/endpoints/organizations.py | 78 ++++++++++++++---- .../__pycache__/organization.cpython-312.pyc | Bin 3510 -> 4097 bytes backend/app/models/organization.py | 29 +++++-- .../__pycache__/organization.cpython-312.pyc | Bin 1276 -> 1991 bytes backend/app/schemas/organization.py | 27 ++++-- docs/V01_gemini/15_Changelog.md | 18 +++- .../18_ASSET_AND_FLEET_SPECIFICATION.md | 28 +++++++ 8 files changed, 150 insertions(+), 30 deletions(-) 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 aa2ce1ddc4d2f9be1232c9182b048549e780a184..ebc04973cdf51fd45c14fd2f28d85d924de3cbe8 100644 GIT binary patch literal 6359 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 S&{-u&-{arWM23G36Z&6_Nx9wt delta 1910 zcmZuyTTC2P7(Qoqc4zj^vKN-i;?P222~ZYjx%6V8rCn1kP0NGbnydpez$|;gGqcc= zMXJ^)+N5pHgAY|B@ri(@2`_y!cGH3y6Ns86lOiU@#5b{RVq0H4v)f_Oc#?De@B7d9 z|MQ=7=FH?m>jB4icDn_Twt4J5smyZI;fMa4^`~bO_J|z<0SKnJBjJoV6D5(7JZIvr zL}{cnkJ-38;wBn4?umHv)*JCsK!l50M$#fa!o0DT0^2=`{zX{ek#f-&JFt6>;Pa#l zX3;Lvv4j7aB6P{JXef9|dP=nDsYak+6>Wk|G>y=L{i<=NjH=6Q(1E{%1K3El_$k#k zfJXVGG|ekgDycGr4PKrQ@hCN5HIu~ekp5_zUjCZ;2wGi*CjpQF_?X9o7Y)9q42V_f z?xH2$>LSw-o;CSe6;7wIJvu5{fmvb~nF61oGcZ<%m)un*2nejlKf5dNS8Qpk87M(L zMrqWMU>0_EW5%d7>Hb&?4zcdKkj{%XV-&1PyUsIotONhb>>V~K&ARXZ_B>+}Y)ke< zs-S^D>Do~r(oxY0j*RIR_=M6=Ccl*4=S%Uz<79m=U=!o4&>mk5(-HAn@axbs`!RJM zbb=f3TC5XXyT2DGhjmFUKznAGT~)lO&lIiT%rO71;sOwiGt8UJwRFKw!6|UbZ~;s~ zl3ybI+!ux^7<8q-Az3UOzGH5hZLx5qHFK}WS;#i72T;{RE5`ew9$G3$?X(95~q7)f{RLwFQF| zD#5Q=`YKefoPY0wj7sCbECH`)Oyq?((mYZ`Bu9BcK%y+;C2KV{ z+uCObgHUC~OOmWaQ{(AcvM}rWF~L2kk{FdjqH0X=3OziA{P=TQ5ZdtfwnJyj$RsM) zh6-)4Yg%?FElWvKv}z#LtHzOdS{_rGQBmO)1tEu~p;EF+Pw>i^O2<-CQZ>j5QYlHm zx9o>m7HPK}mEtXXbsduwr=p}Z)jY(@Vl*Ga5?(-1lv$$1gO1Z4?Ui$qE`LkDWxs$+u3v-+6Ul!(zMMFxT*5V~+N$(VlIS$=HqmC^=Ah zBm9~26WjEFbzHa3S=W3$ce?I0u6g=@WKZHVuFl!x5O`XjZtnv|@6*R6z~+4jps`z% z+vaR*W$m|nZ?~^`J9BK;I@|U4wt-AO+cp8C@5u%ocqS*w`rhvIhY!KK6);TGcPsnq z2)PGfn4#}MTEnK^j<6Zr?{O2dVu#^Qdc~ndtn7o~j&6FTzM7C#25O_(9PTizmioi{ z4Xcet&9mRAc{;RAtKFI=7{H(UUUoUi9cU4TT4Zvt^e@cFr3}kO0+yX~_07o{#{!I|r1T|aW)E4N*LH|qj g2Vpz>% 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 06b63af2427c62eeb4aed0f1127c7815d5db023f..468ac5b193fde71a6d42258c9731789d5720a909 100644 GIT binary patch delta 1580 zcmaJ>OK;Oy6t-Q*c{mP9oTuG}q>q%elt)`eFhU?k9nk`1Hn6}8I-&QrO_e4c$0?+n zWF(|v+0mVcs;~%D8WlAfX%@qh#b`8}*&xyd^&dc4j#P;y-0S4J5s$It@A!P*cg{V& zH_mhQ`=sw*N%AoG`g#6`TtWK7SLWE)=plEB`@Ij{_e`O;oLq%2AT*tO9Pu%X=HVFV zz_$yy_SgC zq=0b{M9~>OTphBQ0X*?h^Wjf$BVDd{h&18wJ&LPi_UL@i@9xB>9GJ_j-s((*{^r9Y zar+F$Kkr8`cy(T}ohPw#0;r%L3^WsTueDPadmQvPleY7;#ihVt4|k?Alo>`#j&Clu zC-ER{@;$WyfnNxa?P`;6$K+;jKVwZNfJ@nO-pFvKP%h-YD?@zEZg)xV6}_}+2B@iIWOMp`n9rLMrIzx= zVz+?!ft4*5%LPNPEG!lQG(AT40d4Hj;d1vGJ#Z$_E9X0>AHQ13&!N|@RP{fB2}HLx zb{+fvQ?tii>q;>=Qa2{!Jb)^*?u3hh_a(%odr)xJ3 zl;o4hZr_gDQYK#V(N4Zf7;a{c=@F7d3B(A@5||@E{y}t}z%}%nIF-0U9^(WQ z0xE$S3fItEaSg8uz2CBT&_CYWf+a1Zlr+j#(3~`$6j*Cu#6K5Fu+( delta 991 zcmZ{j&rcIU6vua#?a%G*TDFC@3#Bbk+>n@vct8^^XaEz`7~xV)uyz()0!5}R33>n% zqh2(aXJU-Tpk6TTK|rY?>jSZc6aW@K5Ft8 zN%C^^*-1S#zj?RhB9DH;ef|vpnupK)=qk?{UPHugER;Q^7UtwQL!1jWMs}9HCB6}- zB|6JNn;VNI)X=a%SMW7gNXwQ(YC4*ITCs9+3AS1yt4cG4kJvWxTQ8~w z)20RLT96fkg;@@(ZxIf>5^RpPhQs6zG&$W~7oC(L9E6l3xEii2Dm~M|sUwGI+CR-+ zL!h>WtkQ+D%X)v&!yHuWnoBj_ChR(bz3rAi{(nF0IJAo)%bB3gZXCuDtd%>g zuhCOiJ8`5OwVb_mtqaF&T5=_>Ct%rm8IUV}}B?7mM3|=Z`NWmca=?uoi$mc7*>|!xjAS=_^MZEpMgOJNlC*|)Vc;b!w z_C)`Ba^vK#sIQ%e$6^oU#ID4i7Jrl6RNwV~*N%M^LVIfXMRLRcQs121RR`84_9Ydz z#ZN8!QW);|exVV#DUFSvVx>DVF=^FArvd3?xXy5n;*zeAHWsuqu=_zq8O9jc#UTR2 zIQ*7QM9;Eem?6TTF^n{E8b;&=y6Xq|2D$)#Wy)DkU4wb04=unmr9b9Ekw@9}!RJFy YhiV)RYOP1n7+N2!ajdUR!Ve|!7tI3D(*OVf 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 4c34b25121fd826d477e11998d084bc00fdbd023..e564d1e1ac377daf247664b8239b974bf6741307 100644 GIT binary patch literal 1991 zcmZ`)J#5=X6h2C)UrUy3S&5n^jT}3+nbdNNEE$pljomiCPT@2Q7lJ0wHd~5hk5n2N zC?FsS>TdE-MA_2ONV|6Grm>3)d0=iyicSSO1hBhw>b)c7&<09CAK&}lyT`kGKl-g$ z%p?B%ed{~(2aM34pbVc>BbG4Y zXIWMi{;JCIs@imnmPNQ+P}Q$nngx-#mh^neN~)^aw&QDXKu=Za2;T&S4A3Hn9kkC8 z{aatqTvz-&eS`18TX5*QW}BO0UBhkDEGz-w*+kwnhbJf~jXzorMn)gq4BkE4`DE*I zck%nH1AI29O?GZ~r}pq9ofs}Va;Xplp8f06(MwJ6%QPe-@l*0eB6LwO;a3`7Wn@jo ztQlbyR~uO~F>6*>vm!|lB_|S+S8{?M5n4gXlM#{nqJ_YMcEZJ;$!E6085KW!C91Qh&!g z8bD?R!}MHBYllUj_qB(r-D<89$`aS_L$SAPpSD$_JsIh=;I``51SZzZa6KE3_~AA+o0Yi8yos~zGl*Ih-InM?R2P~4(ijLo84@$vRxeD={IRKULD|x0I(X! z^Sk(Dul9WE*_i>JKdxSA?BZH)?)jx>^8?%n&du4IFYe-M_u=#8_O}Ck zF_@a|-05EKEpGEcGJDkZ_4G{V`j*kX`uphQ9-avg4d*>FTMh>{+~WTX>=Yl)Vqo)e zj9DetH7mq(`Jg1M6d!=9X|Ob}1i$4i%jshYW=WG*t4OOz@Uetk;M<@8kXjFhE+3rL z+5p#rLh13X@DyNQmUArYwhhhpO`XCqMCq_JFIv-H59{M?-bJEC@S`7O*(>26!bkK2 y9)k~9K>T%3s7kXS^VdNmA(eabKZw7?zWxJNCHx}* delta 562 zcmXw$&ubGw6vtQb^?u>0 z`4vByE*cisc>1!s=7gp1sK|&zd0L$`IGs>g!wq>KZ=^f6F1vV7e#Q^0mP&myv9t3fwH}FHZ z7X}?WCrqU*@_+(h6;K4!02*LJzSg$y-Bw%WD`$+`QK2;6k1Fe^t${(tB)EmV={LMc4fdC!1@pH69Clw zYZdhqQL#CCeAZf!=D%`F?&`O&B|W{4x8$jQtw! 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