2026.03.30 front és garázs logika
This commit is contained in:
211
backend/app/scripts/check_and_fix_garage_data.py
Normal file
211
backend/app/scripts/check_and_fix_garage_data.py
Normal file
@@ -0,0 +1,211 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Garázs adatok ellenőrzése és javítása.
|
||||
Ez a szkript ellenőrzi a teszt felhasználó szervezeti státuszát,
|
||||
és felosztja a járműveket privát és céges flotta között.
|
||||
|
||||
Futtatás: docker compose exec sf_api python -m app.scripts.check_and_fix_garage_data
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
# Add the backend directory to the path
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models.marketplace.organization import Organization, OrganizationMember
|
||||
from app.models.vehicle.asset import Asset
|
||||
# AssetCatalog is inside the asset module
|
||||
|
||||
async def main():
|
||||
"""Fő végrehajtási logika."""
|
||||
print("=" * 60)
|
||||
print("GARÁZS ADATOK ELLENŐRZÉSE ÉS JAVÍTÁSA")
|
||||
print("=" * 60)
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. Keressük meg a teszt felhasználót
|
||||
print("\n1. TESZT FELHASZNÁLÓ KERESÉSE...")
|
||||
stmt = select(User).where(User.email == "tester_pro@profibot.hu")
|
||||
result = await db.execute(stmt)
|
||||
test_user = result.scalar_one_or_none()
|
||||
|
||||
if not test_user:
|
||||
print("❌ HIBA: A teszt felhasználó (tester_pro@profibot.hu) nem található!")
|
||||
return
|
||||
|
||||
print(f" ✅ Teszt felhasználó megtalálva: ID={test_user.id}, Email={test_user.email}")
|
||||
|
||||
# 2. Ellenőrizzük, hogy a felhasználóhoz tartozik-e szervezet
|
||||
print("\n2. SZERVEZETI TAGSÁG ELLENŐRZÉSE...")
|
||||
org_stmt = (
|
||||
select(Organization)
|
||||
.join(OrganizationMember)
|
||||
.where(OrganizationMember.user_id == test_user.id)
|
||||
.where(Organization.is_deleted == False)
|
||||
.where(Organization.is_active == True)
|
||||
)
|
||||
org_result = await db.execute(org_stmt)
|
||||
user_organizations = org_result.scalars().all()
|
||||
|
||||
if user_organizations:
|
||||
print(f" ✅ A felhasználó már tagja {len(user_organizations)} szervezetnek:")
|
||||
for org in user_organizations:
|
||||
print(f" - {org.name} (ID: {org.id}, Adószám: {org.tax_number})")
|
||||
target_org = user_organizations[0]
|
||||
else:
|
||||
print(" ℹ️ A felhasználó nem tagja egyetlen szervezetnek sem. Új szervezet létrehozása...")
|
||||
|
||||
# Új szervezet létrehozása
|
||||
new_org = Organization(
|
||||
full_name="Teszt Flotta Kft.",
|
||||
name="Teszt Flotta",
|
||||
display_name="Teszt Flotta Kft.",
|
||||
tax_number="12345678-2-42",
|
||||
reg_number="01-23-456789",
|
||||
country_code="HU",
|
||||
language="hu",
|
||||
default_currency="HUF",
|
||||
address_zip="1234",
|
||||
address_city="Budapest",
|
||||
address_street_name="Teszt utca",
|
||||
address_street_type="utca",
|
||||
address_house_number="1",
|
||||
folder_slug="teszt-flotta",
|
||||
org_type="business",
|
||||
status="active",
|
||||
is_active=True,
|
||||
is_deleted=False,
|
||||
subscription_plan="FREE",
|
||||
base_asset_limit=10,
|
||||
owner_id=test_user.id
|
||||
)
|
||||
|
||||
db.add(new_org)
|
||||
await db.flush() # ID generáláshoz
|
||||
await db.refresh(new_org)
|
||||
|
||||
# Szervezeti tagság létrehozása (ADMIN szerepkör)
|
||||
org_member = OrganizationMember(
|
||||
organization_id=new_org.id,
|
||||
user_id=test_user.id,
|
||||
role="ADMIN"
|
||||
)
|
||||
db.add(org_member)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_org)
|
||||
|
||||
target_org = new_org
|
||||
print(f" ✅ Új szervezet létrehozva: {target_org.name} (ID: {target_org.id})")
|
||||
|
||||
# 3. A felhasználó összes járművének lekérdezése
|
||||
print("\n3. FELHASZNÁLÓ JÁRMŰVEINEK LEKÉRDEZÉSE...")
|
||||
asset_stmt = (
|
||||
select(Asset)
|
||||
.where(Asset.owner_person_id == test_user.id)
|
||||
.options(selectinload(Asset.catalog))
|
||||
)
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
user_assets = asset_result.scalars().all()
|
||||
|
||||
print(f" ✅ Összesen {len(user_assets)} jármű található a felhasználóhoz.")
|
||||
|
||||
if not user_assets:
|
||||
print(" ℹ️ Nincsenek járművek a felhasználóhoz. Nincs mit felosztani.")
|
||||
return
|
||||
|
||||
# 4. Járművek felosztása privát és céges között
|
||||
print("\n4. JÁRMŰVEK FELOSZTÁSA PRIVÁT ÉS CÉGES FLOTTA KÖZÖTT...")
|
||||
|
||||
# Számoljuk meg, hány jármű van már privát és hány céges
|
||||
private_count = 0
|
||||
corporate_count = 0
|
||||
|
||||
for asset in user_assets:
|
||||
if asset.owner_org_id is None:
|
||||
private_count += 1
|
||||
else:
|
||||
corporate_count += 1
|
||||
|
||||
print(f" Jelenlegi állapot: {private_count} privát, {corporate_count} céges jármű")
|
||||
|
||||
# Ha minden jármű ugyanabban a kategóriában van, felosztjuk őket
|
||||
if private_count == 0 or corporate_count == 0:
|
||||
print(" ℹ️ Járművek újraelosztása 50-50% arányban...")
|
||||
|
||||
# Felosztás fele-fele arányban
|
||||
half_index = len(user_assets) // 2
|
||||
|
||||
for i, asset in enumerate(user_assets):
|
||||
if i < half_index:
|
||||
# Első fele: maradjon privát (owner_org_id = None)
|
||||
if asset.owner_org_id is not None:
|
||||
asset.owner_org_id = None
|
||||
print(f" 🚗 {asset.id}: Privát módra állítva")
|
||||
else:
|
||||
# Második fele: legyen céges (owner_org_id = target_org.id)
|
||||
if asset.owner_org_id != target_org.id:
|
||||
asset.owner_org_id = target_org.id
|
||||
print(f" 🏢 {asset.id}: Céges flottához rendelve (Szervezet: {target_org.name})")
|
||||
|
||||
await db.commit()
|
||||
print(f" ✅ {half_index} jármű privát, {len(user_assets) - half_index} jármű céges módra állítva.")
|
||||
else:
|
||||
print(" ✅ A járművek már megfelelően fel vannak osztva. Nincs szükség módosításra.")
|
||||
|
||||
# 5. Végeredmény összefoglaló
|
||||
print("\n" + "=" * 60)
|
||||
print("VÉGEREDMÉNY ÖSSZEFOGLALÓ")
|
||||
print("=" * 60)
|
||||
|
||||
# Új lekérdezés a frissített adatokhoz
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
user_assets = asset_result.scalars().all()
|
||||
|
||||
private_assets = [a for a in user_assets if a.owner_org_id is None]
|
||||
corporate_assets = [a for a in user_assets if a.owner_org_id == target_org.id]
|
||||
other_assets = [a for a in user_assets if a.owner_org_id not in [None, target_org.id]]
|
||||
|
||||
print(f"\n📊 TESZT FELHASZNÁLÓ ÁLLAPOTA:")
|
||||
print(f" • Email: {test_user.email}")
|
||||
print(f" • User ID: {test_user.id}")
|
||||
print(f" • Aktív szervezet: {target_org.name} (ID: {target_org.id})")
|
||||
print(f" • Szerepkör a szervezetben: ADMIN")
|
||||
|
||||
print(f"\n🚗 JÁRMŰVEGYÜTT ÁLLAPOTA:")
|
||||
print(f" • Összes jármű: {len(user_assets)} db")
|
||||
print(f" • Privát garázs (owner_org_id = NULL): {len(private_assets)} db")
|
||||
print(f" • Céges flotta ({target_org.name}): {len(corporate_assets)} db")
|
||||
|
||||
if other_assets:
|
||||
print(f" • Egyéb szervezetekhez rendelve: {len(other_assets)} db")
|
||||
|
||||
print(f"\n📋 PRIVÁT JÁRMŰVEK:")
|
||||
for asset in private_assets[:5]: # Csak az első 5-öt mutatjuk
|
||||
catalog_name = asset.catalog.make + " " + asset.catalog.model if asset.catalog else "Ismeretlen"
|
||||
print(f" • {catalog_name} (Asset ID: {asset.id})")
|
||||
|
||||
if len(private_assets) > 5:
|
||||
print(f" • ... és még {len(private_assets) - 5} további")
|
||||
|
||||
print(f"\n🏢 CÉGES JÁRMŰVEK:")
|
||||
for asset in corporate_assets[:5]:
|
||||
catalog_name = asset.catalog.make + " " + asset.catalog.model if asset.catalog else "Ismeretlen"
|
||||
print(f" • {catalog_name} (Asset ID: {asset.id})")
|
||||
|
||||
if len(corporate_assets) > 5:
|
||||
print(f" • ... és még {len(corporate_assets) - 5} további")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("A tesztadatok sikeresen előkészítve!")
|
||||
print("Most tesztelhető a Garage UI switcher funkció.")
|
||||
print("=" * 60)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
150
backend/app/scripts/check_and_fix_garage_data_simple.py
Normal file
150
backend/app/scripts/check_and_fix_garage_data_simple.py
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Egyszerűbb változat: Garázs adatok ellenőrzése és javítása.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models.marketplace.organization import Organization, OrganizationMember
|
||||
from app.models.vehicle.asset import Asset
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("GARÁZS ADATOK ELLENŐRZÉSE (Egyszerű változat)")
|
||||
print("=" * 60)
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. Keressük meg a teszt felhasználót
|
||||
print("\n1. TESZT FELHASZNÁLÓ KERESÉSE...")
|
||||
stmt = select(User).where(User.email == "tester_pro@profibot.hu")
|
||||
result = await db.execute(stmt)
|
||||
test_user = result.scalar_one_or_none()
|
||||
|
||||
if not test_user:
|
||||
print("❌ HIBA: A teszt felhasználó nem található!")
|
||||
return
|
||||
|
||||
print(f" ✅ Teszt felhasználó: ID={test_user.id}, Email={test_user.email}")
|
||||
|
||||
# 2. Ellenőrizzük a szervezeti tagságot
|
||||
print("\n2. SZERVEZETI TAGSÁG ELLENŐRZÉSE...")
|
||||
org_stmt = (
|
||||
select(Organization)
|
||||
.join(OrganizationMember)
|
||||
.where(OrganizationMember.user_id == test_user.id)
|
||||
.where(Organization.is_deleted == False)
|
||||
)
|
||||
org_result = await db.execute(org_stmt)
|
||||
user_orgs = org_result.scalars().all()
|
||||
|
||||
if user_orgs:
|
||||
print(f" ✅ A felhasználó tagja {len(user_orgs)} szervezetnek:")
|
||||
for org in user_orgs:
|
||||
print(f" - {org.name} (ID: {org.id})")
|
||||
target_org = user_orgs[0]
|
||||
else:
|
||||
print(" ℹ️ Nincs szervezet. Új létrehozása...")
|
||||
# Egyszerű szervezet létrehozás
|
||||
new_org = Organization(
|
||||
full_name="Teszt Flotta Kft.",
|
||||
name="Teszt Flotta",
|
||||
display_name="Teszt Flotta Kft.",
|
||||
tax_number="12345678-2-42",
|
||||
country_code="HU",
|
||||
folder_slug="teszt-flotta-123",
|
||||
org_type="business",
|
||||
status="active",
|
||||
is_active=True,
|
||||
owner_id=test_user.id
|
||||
)
|
||||
db.add(new_org)
|
||||
await db.flush()
|
||||
|
||||
org_member = OrganizationMember(
|
||||
organization_id=new_org.id,
|
||||
user_id=test_user.id,
|
||||
role="ADMIN"
|
||||
)
|
||||
db.add(org_member)
|
||||
|
||||
await db.commit()
|
||||
await db.refresh(new_org)
|
||||
target_org = new_org
|
||||
print(f" ✅ Új szervezet: {target_org.name} (ID: {target_org.id})")
|
||||
|
||||
# 3. Járművek lekérdezése
|
||||
print("\n3. JÁRMŰVEK LEKÉRDEZÉSE...")
|
||||
asset_stmt = (
|
||||
select(Asset)
|
||||
.where(Asset.owner_person_id == test_user.id)
|
||||
)
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
assets = asset_result.scalars().all()
|
||||
|
||||
print(f" ✅ Összesen {len(assets)} jármű található.")
|
||||
|
||||
if not assets:
|
||||
print(" ℹ️ Nincsenek járművek. Nincs mit felosztani.")
|
||||
return
|
||||
|
||||
# 4. Jelenlegi felosztás
|
||||
private = [a for a in assets if a.owner_org_id is None]
|
||||
corporate = [a for a in assets if a.owner_org_id == target_org.id]
|
||||
other = [a for a in assets if a.owner_org_id not in [None, target_org.id]]
|
||||
|
||||
print(f"\n JELENLEGI ÁLLAPOT:")
|
||||
print(f" • Privát: {len(private)} db")
|
||||
print(f" • Céges ({target_org.name}): {len(corporate)} db")
|
||||
if other:
|
||||
print(f" • Egyéb: {len(other)} db")
|
||||
|
||||
# 5. Ha nincs elég adat, felosztjuk
|
||||
if len(private) == 0 or len(corporate) == 0:
|
||||
print("\n ℹ️ Járművek felosztása...")
|
||||
half = len(assets) // 2
|
||||
|
||||
for i, asset in enumerate(assets):
|
||||
if i < half:
|
||||
asset.owner_org_id = None
|
||||
else:
|
||||
asset.owner_org_id = target_org.id
|
||||
|
||||
await db.commit()
|
||||
print(f" ✅ Felosztva: {half} privát, {len(assets)-half} céges")
|
||||
else:
|
||||
print("\n ✅ A járművek már megfelelően fel vannak osztva.")
|
||||
|
||||
# 6. Végeredmény
|
||||
print("\n" + "=" * 60)
|
||||
print("VÉGEREDMÉNY:")
|
||||
print("=" * 60)
|
||||
print(f"\n📋 TESZT FELHASZNÁLÓ:")
|
||||
print(f" • Email: {test_user.email}")
|
||||
print(f" • User ID: {test_user.id}")
|
||||
print(f" • Szervezet: {target_org.name} (ID: {target_org.id})")
|
||||
|
||||
# Új lekérdezés
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
assets = asset_result.scalars().all()
|
||||
private = [a for a in assets if a.owner_org_id is None]
|
||||
corporate = [a for a in assets if a.owner_org_id == target_org.id]
|
||||
|
||||
print(f"\n🚗 JÁRMŰVEGYÜTT:")
|
||||
print(f" • Összesen: {len(assets)} db")
|
||||
print(f" • Privát garázs: {len(private)} db")
|
||||
print(f" • Céges flotta: {len(corporate)} db")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("KÉSZ! A tesztadatok előkészítve.")
|
||||
print("=" * 60)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
71
backend/app/scripts/check_person_data.py
Normal file
71
backend/app/scripts/check_person_data.py
Normal file
@@ -0,0 +1,71 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check Person data for User 28.
|
||||
"""
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity.identity import User, Person
|
||||
|
||||
async def check_person():
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Get User 28
|
||||
user_stmt = select(User).where(User.id == 28)
|
||||
user_result = await db.execute(user_stmt)
|
||||
test_user = user_result.scalar_one_or_none()
|
||||
|
||||
if not test_user:
|
||||
print("❌ User 28 not found")
|
||||
return
|
||||
|
||||
print(f"✅ User 28:")
|
||||
print(f" - User ID: {test_user.id}")
|
||||
print(f" - Person ID: {test_user.person_id}")
|
||||
print(f" - Email: {test_user.email}")
|
||||
|
||||
# Get the Person record
|
||||
if test_user.person_id:
|
||||
person_stmt = select(Person).where(Person.id == test_user.person_id)
|
||||
person_result = await db.execute(person_stmt)
|
||||
person = person_result.scalar_one_or_none()
|
||||
|
||||
if person:
|
||||
print(f"\n✅ Person {person.id}:")
|
||||
print(f" - First name: {person.first_name}")
|
||||
print(f" - Last name: {person.last_name}")
|
||||
print(f" - Date of birth: {person.date_of_birth}")
|
||||
print(f" - Gender: {person.gender}")
|
||||
else:
|
||||
print(f"\n❌ Person with ID {test_user.person_id} not found")
|
||||
else:
|
||||
print("\n❌ User has no person_id")
|
||||
|
||||
# Check if there's a Person with ID 28
|
||||
person28_stmt = select(Person).where(Person.id == 28)
|
||||
person28_result = await db.execute(person28_stmt)
|
||||
person28 = person28_result.scalar_one_or_none()
|
||||
|
||||
if person28:
|
||||
print(f"\n⚠️ Person with ID 28 exists:")
|
||||
print(f" - First name: {person28.first_name}")
|
||||
print(f" - Last name: {person28.last_name}")
|
||||
print(f" - Date of birth: {person28.date_of_birth}")
|
||||
print(f" - Gender: {person28.gender}")
|
||||
|
||||
# Check which user is linked to this person
|
||||
user_for_person28_stmt = select(User).where(User.person_id == 28)
|
||||
user_for_person28_result = await db.execute(user_for_person28_stmt)
|
||||
user_for_person28 = user_for_person28_result.scalar_one_or_none()
|
||||
|
||||
if user_for_person28:
|
||||
print(f" - Linked to User: {user_for_person28.email} (ID: {user_for_person28.id})")
|
||||
else:
|
||||
print(f" - Not linked to any user")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(check_person())
|
||||
52
backend/app/scripts/check_vehicle_details.py
Normal file
52
backend/app/scripts/check_vehicle_details.py
Normal file
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Check detailed vehicle data for MNO-345 and PQR-678.
|
||||
"""
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.vehicle.asset import Asset
|
||||
|
||||
async def check_details():
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Get specific vehicles
|
||||
asset_stmt = select(Asset).where(
|
||||
Asset.license_plate.in_(["MNO-345", "PQR-678"])
|
||||
)
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
assets = asset_result.scalars().all()
|
||||
|
||||
print(f"✅ Found {len(assets)} vehicles:")
|
||||
for asset in assets:
|
||||
print(f"\n Vehicle: {asset.license_plate}")
|
||||
print(f" - Asset ID: {asset.id}")
|
||||
print(f" - Owner Person ID: {asset.owner_person_id}")
|
||||
print(f" - Operator Person ID: {asset.operator_person_id}")
|
||||
print(f" - Owner Org ID: {asset.owner_org_id}")
|
||||
print(f" - Operator Org ID: {asset.operator_org_id}")
|
||||
print(f" - Branch ID: {asset.branch_id}")
|
||||
print(f" - Current Org ID: {asset.current_organization_id}")
|
||||
print(f" - Status: {asset.status}")
|
||||
print(f" - Data Status: {asset.data_status}")
|
||||
|
||||
# Check personal mode conditions
|
||||
owner_org_none = asset.owner_org_id is None
|
||||
operator_org_none = asset.operator_org_id is None
|
||||
print(f" - Owner Org is None: {owner_org_none}")
|
||||
print(f" - Operator Org is None: {operator_org_none}")
|
||||
|
||||
# Check if it would appear in personal mode
|
||||
would_appear_personal = (owner_org_none or operator_org_none)
|
||||
print(f" - Would appear in personal mode: {would_appear_personal}")
|
||||
|
||||
# Check corporate mode conditions
|
||||
print(f" - Would appear in corporate mode (org 15): {asset.current_organization_id == 15}")
|
||||
print(f" - Would appear in corporate mode (org 21): {asset.current_organization_id == 21}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(check_details())
|
||||
165
backend/app/scripts/create_test_user.py
Normal file
165
backend/app/scripts/create_test_user.py
Normal file
@@ -0,0 +1,165 @@
|
||||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
async def main():
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.core.security import get_password_hash
|
||||
from sqlalchemy import select
|
||||
from datetime import datetime
|
||||
|
||||
TEST_EMAIL = "integration_test_admin@servicefinder.local"
|
||||
TEST_PASSWORD = "TestPassword123!"
|
||||
TEST_FIRST_NAME = "Integration"
|
||||
TEST_LAST_NAME = "TestAdmin"
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Check if user already exists
|
||||
result = await db.execute(
|
||||
select(User).where(User.email == TEST_EMAIL)
|
||||
)
|
||||
existing_user = result.scalar_one_or_none()
|
||||
|
||||
if existing_user:
|
||||
print(f"User {TEST_EMAIL} already exists with ID {existing_user.id}")
|
||||
if existing_user.role != UserRole.admin:
|
||||
existing_user.role = UserRole.admin
|
||||
await db.commit()
|
||||
print(f"Updated user role to {UserRole.admin}")
|
||||
user = existing_user
|
||||
else:
|
||||
# Create Person first
|
||||
person = Person(
|
||||
first_name=TEST_FIRST_NAME,
|
||||
last_name=TEST_LAST_NAME,
|
||||
email=TEST_EMAIL,
|
||||
is_active=True,
|
||||
created_at=datetime.utcnow()
|
||||
)
|
||||
db.add(person)
|
||||
await db.flush()
|
||||
|
||||
# Create User with ADMIN role
|
||||
user = User(
|
||||
email=TEST_EMAIL,
|
||||
hashed_password=get_password_hash(TEST_PASSWORD),
|
||||
role=UserRole.admin,
|
||||
person_id=person.id,
|
||||
is_active=True,
|
||||
subscription_plan="PREMIUM",
|
||||
scope_level="individual",
|
||||
preferred_language="en",
|
||||
region_code="HU",
|
||||
ui_mode="personal"
|
||||
)
|
||||
db.add(user)
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
print(f"Created new user {TEST_EMAIL} with ID {user.id}, role {user.role}")
|
||||
|
||||
# Get organization ID if any
|
||||
from app.models.identity import OrganizationMember
|
||||
result = await db.execute(
|
||||
select(OrganizationMember.organization_id)
|
||||
.where(OrganizationMember.user_id == user.id)
|
||||
.limit(1)
|
||||
)
|
||||
org_member = result.scalar_one_or_none()
|
||||
org_id = org_member.organization_id if org_member else None
|
||||
|
||||
# Get or create a test vehicle
|
||||
from app.models.data import Vehicle, VehicleModelDefinition
|
||||
result = await db.execute(
|
||||
select(Vehicle.id)
|
||||
.where(Vehicle.owner_user_id == user.id)
|
||||
.limit(1)
|
||||
)
|
||||
vehicle = result.scalar_one_or_none()
|
||||
vehicle_id = vehicle.id if vehicle else None
|
||||
|
||||
if not vehicle_id:
|
||||
result = await db.execute(
|
||||
select(VehicleModelDefinition.id).limit(1)
|
||||
)
|
||||
catalog_id = result.scalar_one_or_none()
|
||||
if catalog_id:
|
||||
import uuid
|
||||
vehicle = Vehicle(
|
||||
catalog_id=catalog_id,
|
||||
license_plate=f"TEST-{uuid.uuid4().hex[:4]}".upper(),
|
||||
vin=f"VIN{uuid.uuid4().hex[:10]}".upper(),
|
||||
nickname="Integration Test Vehicle",
|
||||
owner_user_id=user.id,
|
||||
status="DRAFT",
|
||||
created_at=datetime.utcnow()
|
||||
)
|
||||
db.add(vehicle)
|
||||
await db.commit()
|
||||
await db.refresh(vehicle)
|
||||
vehicle_id = vehicle.id
|
||||
print(f"Created test vehicle with ID {vehicle_id}")
|
||||
else:
|
||||
print("No catalog entries found, skipping vehicle creation")
|
||||
|
||||
# Generate token
|
||||
from app.services.auth_service import AuthService
|
||||
from app.core.security import create_tokens
|
||||
from app.core.config import settings
|
||||
|
||||
auth_user = await AuthService.authenticate(db, TEST_EMAIL, TEST_PASSWORD)
|
||||
if auth_user:
|
||||
ranks = await settings.get_db_setting(db, "rbac_rank_matrix", default={})
|
||||
role_key = auth_user.role.value.upper()
|
||||
token_payload = {
|
||||
"sub": str(auth_user.id),
|
||||
"role": auth_user.role.value,
|
||||
"rank": ranks.get(role_key, 10),
|
||||
"scope_level": auth_user.scope_level or "individual",
|
||||
"scope_id": str(auth_user.scope_id) if auth_user.scope_id else str(auth_user.id)
|
||||
}
|
||||
access_token, refresh_token = create_tokens(data=token_payload)
|
||||
test_token = access_token
|
||||
print("Generated access token")
|
||||
else:
|
||||
test_token = None
|
||||
print("Warning: Could not generate token")
|
||||
|
||||
# Prepare session data
|
||||
session_data = {
|
||||
"email": TEST_EMAIL,
|
||||
"password": TEST_PASSWORD,
|
||||
"test_token": test_token,
|
||||
"user_id": user.id,
|
||||
"role": user.role.value,
|
||||
"organization_id": org_id,
|
||||
"test_vehicle_id": vehicle_id
|
||||
}
|
||||
|
||||
# Write to file
|
||||
output_path = "/opt/docker/dev/service_finder/tests/integration_session.json"
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(session_data, f, indent=2)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("TEST IDENTITY SETUP COMPLETE")
|
||||
print("="*60)
|
||||
print(f"Email: {TEST_EMAIL}")
|
||||
print(f"Password: {TEST_PASSWORD}")
|
||||
print(f"Token: {test_token[:50] if test_token else 'None'}...")
|
||||
print(f"User ID: {user.id}")
|
||||
print(f"Role: {user.role.value}")
|
||||
print(f"Organization ID: {org_id}")
|
||||
print(f"Test Vehicle ID: {vehicle_id}")
|
||||
print(f"Session saved to: {output_path}")
|
||||
print("="*60)
|
||||
|
||||
return session_data
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
167
backend/app/scripts/create_test_user_final.py
Normal file
167
backend/app/scripts/create_test_user_final.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
async def main():
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.core.security import get_password_hash
|
||||
from sqlalchemy import select
|
||||
|
||||
TEST_EMAIL = "integration_test_admin@servicefinder.local"
|
||||
TEST_PASSWORD = "TestPassword123!"
|
||||
TEST_FIRST_NAME = "Integration"
|
||||
TEST_LAST_NAME = "TestAdmin"
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Check if user already exists
|
||||
result = await db.execute(
|
||||
select(User).where(User.email == TEST_EMAIL)
|
||||
)
|
||||
existing_user = result.scalar_one_or_none()
|
||||
|
||||
if existing_user:
|
||||
print(f"User {TEST_EMAIL} already exists with ID {existing_user.id}")
|
||||
if existing_user.role != UserRole.admin:
|
||||
existing_user.role = UserRole.admin
|
||||
await db.commit()
|
||||
print(f"Updated user role to {UserRole.admin}")
|
||||
user = existing_user
|
||||
else:
|
||||
# Create Person first
|
||||
person = Person(
|
||||
first_name=TEST_FIRST_NAME,
|
||||
last_name=TEST_LAST_NAME,
|
||||
is_active=True,
|
||||
created_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.add(person)
|
||||
await db.flush()
|
||||
|
||||
# Create User with ADMIN role
|
||||
user = User(
|
||||
email=TEST_EMAIL,
|
||||
hashed_password=get_password_hash(TEST_PASSWORD),
|
||||
role=UserRole.admin,
|
||||
person_id=person.id,
|
||||
is_active=True,
|
||||
subscription_plan="PREMIUM",
|
||||
scope_level="individual",
|
||||
preferred_language="en",
|
||||
region_code="HU",
|
||||
ui_mode="personal",
|
||||
is_vip=False,
|
||||
preferred_currency="HUF",
|
||||
custom_permissions={}
|
||||
)
|
||||
db.add(user)
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
print(f"Created new user {TEST_EMAIL} with ID {user.id}, role {user.role}")
|
||||
|
||||
# Get organization ID if any
|
||||
from app.models.identity import OrganizationMember
|
||||
result = await db.execute(
|
||||
select(OrganizationMember.organization_id)
|
||||
.where(OrganizationMember.user_id == user.id)
|
||||
.limit(1)
|
||||
)
|
||||
org_member = result.scalar_one_or_none()
|
||||
org_id = org_member.organization_id if org_member else None
|
||||
|
||||
# Get or create a test vehicle
|
||||
from app.models.data import Vehicle, VehicleModelDefinition
|
||||
result = await db.execute(
|
||||
select(Vehicle.id)
|
||||
.where(Vehicle.owner_user_id == user.id)
|
||||
.limit(1)
|
||||
)
|
||||
vehicle = result.scalar_one_or_none()
|
||||
vehicle_id = vehicle.id if vehicle else None
|
||||
|
||||
if not vehicle_id:
|
||||
result = await db.execute(
|
||||
select(VehicleModelDefinition.id).limit(1)
|
||||
)
|
||||
catalog_id = result.scalar_one_or_none()
|
||||
if catalog_id:
|
||||
import uuid
|
||||
vehicle = Vehicle(
|
||||
catalog_id=catalog_id,
|
||||
license_plate=f"TEST-{uuid.uuid4().hex[:4]}".upper(),
|
||||
vin=f"VIN{uuid.uuid4().hex[:10]}".upper(),
|
||||
nickname="Integration Test Vehicle",
|
||||
owner_user_id=user.id,
|
||||
status="DRAFT",
|
||||
created_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.add(vehicle)
|
||||
await db.commit()
|
||||
await db.refresh(vehicle)
|
||||
vehicle_id = vehicle.id
|
||||
print(f"Created test vehicle with ID {vehicle_id}")
|
||||
else:
|
||||
print("No catalog entries found, skipping vehicle creation")
|
||||
|
||||
# Generate token
|
||||
from app.services.auth_service import AuthService
|
||||
from app.core.security import create_tokens
|
||||
from app.core.config import settings
|
||||
|
||||
auth_user = await AuthService.authenticate(db, TEST_EMAIL, TEST_PASSWORD)
|
||||
if auth_user:
|
||||
ranks = await settings.get_db_setting(db, "rbac_rank_matrix", default={})
|
||||
role_key = auth_user.role.value.upper()
|
||||
token_payload = {
|
||||
"sub": str(auth_user.id),
|
||||
"role": auth_user.role.value,
|
||||
"rank": ranks.get(role_key, 10),
|
||||
"scope_level": auth_user.scope_level or "individual",
|
||||
"scope_id": str(auth_user.scope_id) if auth_user.scope_id else str(auth_user.id)
|
||||
}
|
||||
access_token, refresh_token = create_tokens(data=token_payload)
|
||||
test_token = access_token
|
||||
print("Generated access token")
|
||||
else:
|
||||
test_token = None
|
||||
print("Warning: Could not generate token")
|
||||
|
||||
# Prepare session data
|
||||
session_data = {
|
||||
"email": TEST_EMAIL,
|
||||
"password": TEST_PASSWORD,
|
||||
"test_token": test_token,
|
||||
"user_id": user.id,
|
||||
"role": user.role.value,
|
||||
"organization_id": org_id,
|
||||
"test_vehicle_id": vehicle_id
|
||||
}
|
||||
|
||||
# Write to file
|
||||
output_path = "/opt/docker/dev/service_finder/tests/integration_session.json"
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(session_data, f, indent=2)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("TEST IDENTITY SETUP COMPLETE")
|
||||
print("="*60)
|
||||
print(f"Email: {TEST_EMAIL}")
|
||||
print(f"Password: {TEST_PASSWORD}")
|
||||
print(f"Token: {test_token[:50] if test_token else 'None'}...")
|
||||
print(f"User ID: {user.id}")
|
||||
print(f"Role: {user.role.value}")
|
||||
print(f"Organization ID: {org_id}")
|
||||
print(f"Test Vehicle ID: {vehicle_id}")
|
||||
print(f"Session saved to: {output_path}")
|
||||
print("="*60)
|
||||
|
||||
return session_data
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
164
backend/app/scripts/create_test_user_fixed.py
Normal file
164
backend/app/scripts/create_test_user_fixed.py
Normal file
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env python3
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
async def main():
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User, Person, UserRole
|
||||
from app.core.security import get_password_hash
|
||||
from sqlalchemy import select
|
||||
|
||||
TEST_EMAIL = "integration_test_admin@servicefinder.local"
|
||||
TEST_PASSWORD = "TestPassword123!"
|
||||
TEST_FIRST_NAME = "Integration"
|
||||
TEST_LAST_NAME = "TestAdmin"
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Check if user already exists
|
||||
result = await db.execute(
|
||||
select(User).where(User.email == TEST_EMAIL)
|
||||
)
|
||||
existing_user = result.scalar_one_or_none()
|
||||
|
||||
if existing_user:
|
||||
print(f"User {TEST_EMAIL} already exists with ID {existing_user.id}")
|
||||
if existing_user.role != UserRole.admin:
|
||||
existing_user.role = UserRole.admin
|
||||
await db.commit()
|
||||
print(f"Updated user role to {UserRole.admin}")
|
||||
user = existing_user
|
||||
else:
|
||||
# Create Person first
|
||||
person = Person(
|
||||
first_name=TEST_FIRST_NAME,
|
||||
last_name=TEST_LAST_NAME,
|
||||
is_active=True,
|
||||
created_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.add(person)
|
||||
await db.flush()
|
||||
|
||||
# Create User with ADMIN role
|
||||
user = User(
|
||||
email=TEST_EMAIL,
|
||||
hashed_password=get_password_hash(TEST_PASSWORD),
|
||||
role=UserRole.admin,
|
||||
person_id=person.id,
|
||||
is_active=True,
|
||||
subscription_plan="PREMIUM",
|
||||
scope_level="individual",
|
||||
preferred_language="en",
|
||||
region_code="HU",
|
||||
ui_mode="personal"
|
||||
)
|
||||
db.add(user)
|
||||
await db.commit()
|
||||
await db.refresh(user)
|
||||
print(f"Created new user {TEST_EMAIL} with ID {user.id}, role {user.role}")
|
||||
|
||||
# Get organization ID if any
|
||||
from app.models.identity import OrganizationMember
|
||||
result = await db.execute(
|
||||
select(OrganizationMember.organization_id)
|
||||
.where(OrganizationMember.user_id == user.id)
|
||||
.limit(1)
|
||||
)
|
||||
org_member = result.scalar_one_or_none()
|
||||
org_id = org_member.organization_id if org_member else None
|
||||
|
||||
# Get or create a test vehicle
|
||||
from app.models.data import Vehicle, VehicleModelDefinition
|
||||
result = await db.execute(
|
||||
select(Vehicle.id)
|
||||
.where(Vehicle.owner_user_id == user.id)
|
||||
.limit(1)
|
||||
)
|
||||
vehicle = result.scalar_one_or_none()
|
||||
vehicle_id = vehicle.id if vehicle else None
|
||||
|
||||
if not vehicle_id:
|
||||
result = await db.execute(
|
||||
select(VehicleModelDefinition.id).limit(1)
|
||||
)
|
||||
catalog_id = result.scalar_one_or_none()
|
||||
if catalog_id:
|
||||
import uuid
|
||||
vehicle = Vehicle(
|
||||
catalog_id=catalog_id,
|
||||
license_plate=f"TEST-{uuid.uuid4().hex[:4]}".upper(),
|
||||
vin=f"VIN{uuid.uuid4().hex[:10]}".upper(),
|
||||
nickname="Integration Test Vehicle",
|
||||
owner_user_id=user.id,
|
||||
status="DRAFT",
|
||||
created_at=datetime.now(timezone.utc)
|
||||
)
|
||||
db.add(vehicle)
|
||||
await db.commit()
|
||||
await db.refresh(vehicle)
|
||||
vehicle_id = vehicle.id
|
||||
print(f"Created test vehicle with ID {vehicle_id}")
|
||||
else:
|
||||
print("No catalog entries found, skipping vehicle creation")
|
||||
|
||||
# Generate token
|
||||
from app.services.auth_service import AuthService
|
||||
from app.core.security import create_tokens
|
||||
from app.core.config import settings
|
||||
|
||||
auth_user = await AuthService.authenticate(db, TEST_EMAIL, TEST_PASSWORD)
|
||||
if auth_user:
|
||||
ranks = await settings.get_db_setting(db, "rbac_rank_matrix", default={})
|
||||
role_key = auth_user.role.value.upper()
|
||||
token_payload = {
|
||||
"sub": str(auth_user.id),
|
||||
"role": auth_user.role.value,
|
||||
"rank": ranks.get(role_key, 10),
|
||||
"scope_level": auth_user.scope_level or "individual",
|
||||
"scope_id": str(auth_user.scope_id) if auth_user.scope_id else str(auth_user.id)
|
||||
}
|
||||
access_token, refresh_token = create_tokens(data=token_payload)
|
||||
test_token = access_token
|
||||
print("Generated access token")
|
||||
else:
|
||||
test_token = None
|
||||
print("Warning: Could not generate token")
|
||||
|
||||
# Prepare session data
|
||||
session_data = {
|
||||
"email": TEST_EMAIL,
|
||||
"password": TEST_PASSWORD,
|
||||
"test_token": test_token,
|
||||
"user_id": user.id,
|
||||
"role": user.role.value,
|
||||
"organization_id": org_id,
|
||||
"test_vehicle_id": vehicle_id
|
||||
}
|
||||
|
||||
# Write to file
|
||||
output_path = "/opt/docker/dev/service_finder/tests/integration_session.json"
|
||||
os.makedirs(os.path.dirname(output_path), exist_ok=True)
|
||||
with open(output_path, 'w') as f:
|
||||
json.dump(session_data, f, indent=2)
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("TEST IDENTITY SETUP COMPLETE")
|
||||
print("="*60)
|
||||
print(f"Email: {TEST_EMAIL}")
|
||||
print(f"Password: {TEST_PASSWORD}")
|
||||
print(f"Token: {test_token[:50] if test_token else 'None'}...")
|
||||
print(f"User ID: {user.id}")
|
||||
print(f"Role: {user.role.value}")
|
||||
print(f"Organization ID: {org_id}")
|
||||
print(f"Test Vehicle ID: {vehicle_id}")
|
||||
print(f"Session saved to: {output_path}")
|
||||
print("="*60)
|
||||
|
||||
return session_data
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
146
backend/app/scripts/create_test_vehicles.py
Normal file
146
backend/app/scripts/create_test_vehicles.py
Normal file
@@ -0,0 +1,146 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Teszt járművek létrehozása a teszt felhasználóhoz.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import uuid
|
||||
import sys
|
||||
from datetime import datetime
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models.marketplace.organization import Organization, OrganizationMember
|
||||
from app.models.vehicle.asset import Asset
|
||||
from app.models.vehicle.vehicle_definitions import VehicleModelDefinition
|
||||
|
||||
async def main():
|
||||
print("=" * 60)
|
||||
print("TESZT JÁRMŰVEK LÉTREHOZÁSA")
|
||||
print("=" * 60)
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. Keressük meg a teszt felhasználót
|
||||
print("\n1. TESZT FELHASZNÁLÓ KERESÉSE...")
|
||||
stmt = select(User).where(User.email == "tester_pro@profibot.hu")
|
||||
result = await db.execute(stmt)
|
||||
test_user = result.scalar_one_or_none()
|
||||
|
||||
if not test_user:
|
||||
print("❌ HIBA: A teszt felhasználó nem található!")
|
||||
return
|
||||
|
||||
print(f" ✅ Teszt felhasználó: ID={test_user.id}")
|
||||
|
||||
# 2. Keressük meg a szervezetet
|
||||
print("\n2. SZERVEZET KERESÉSE...")
|
||||
org_stmt = (
|
||||
select(Organization)
|
||||
.join(OrganizationMember)
|
||||
.where(OrganizationMember.user_id == test_user.id)
|
||||
.where(Organization.is_deleted == False)
|
||||
.limit(1)
|
||||
)
|
||||
org_result = await db.execute(org_stmt)
|
||||
organization = org_result.scalar_one_or_none()
|
||||
|
||||
if not organization:
|
||||
print("❌ HIBA: Nincs szervezet a felhasználóhoz!")
|
||||
return
|
||||
|
||||
print(f" ✅ Szervezet: {organization.name} (ID: {organization.id})")
|
||||
|
||||
# 3. Keressünk néhány járműmodellt a katalógusból
|
||||
print("\n3. JÁRMŰMODELLEK KERESÉSE A KATALÓGUSBÓL...")
|
||||
catalog_stmt = select(VehicleModelDefinition).limit(10)
|
||||
catalog_result = await db.execute(catalog_stmt)
|
||||
catalog_models = catalog_result.scalars().all()
|
||||
|
||||
if not catalog_models:
|
||||
print("❌ HIBA: Nincsenek járműmodellek a katalógusban!")
|
||||
return
|
||||
|
||||
print(f" ✅ {len(catalog_models)} járműmodell található a katalógusban.")
|
||||
|
||||
# 4. Hozzunk létre teszt járműveket
|
||||
print("\n4. TESZT JÁRMŰVEK LÉTREHOZÁSA...")
|
||||
|
||||
test_vehicles = [
|
||||
# (név, rendszám, catalog_id, privát vagy céges)
|
||||
("Privát Audi", "ABC-123", catalog_models[0].id, True),
|
||||
("Privát BMW", "DEF-456", catalog_models[1].id, True),
|
||||
("Privát Mercedes", "GHI-789", catalog_models[2].id, True),
|
||||
("Céges Ford", "JKL-012", catalog_models[3].id, False),
|
||||
("Céges Toyota", "MNO-345", catalog_models[4].id, False),
|
||||
("Céges Volkswagen", "PQR-678", catalog_models[5].id, False),
|
||||
]
|
||||
|
||||
created_count = 0
|
||||
for name, license_plate, catalog_id, is_private in test_vehicles:
|
||||
# Ellenőrizzük, hogy már létezik-e ilyen rendszámú jármű
|
||||
existing_stmt = select(Asset).where(Asset.license_plate == license_plate)
|
||||
existing_result = await db.execute(existing_stmt)
|
||||
if existing_result.scalar_one_or_none():
|
||||
print(f" ⚠️ '{license_plate}' rendszámú jármű már létezik, kihagyva.")
|
||||
continue
|
||||
|
||||
# Új Asset létrehozása
|
||||
new_asset = Asset(
|
||||
catalog_id=catalog_id,
|
||||
license_plate=license_plate,
|
||||
name=name,
|
||||
owner_person_id=test_user.id,
|
||||
owner_org_id=None if is_private else organization.id,
|
||||
status="active",
|
||||
price=15000000 if is_private else 20000000, # 15-20 millió HUF
|
||||
currency="HUF",
|
||||
individual_equipment={},
|
||||
created_at=datetime.now()
|
||||
)
|
||||
|
||||
db.add(new_asset)
|
||||
created_count += 1
|
||||
|
||||
mode_text = "Privát" if is_private else "Céges"
|
||||
print(f" ✅ {mode_text} jármű létrehozva: {name} ({license_plate})")
|
||||
|
||||
await db.commit()
|
||||
|
||||
# 5. Végeredmény
|
||||
print("\n" + "=" * 60)
|
||||
print("VÉGEREDMÉNY:")
|
||||
print("=" * 60)
|
||||
|
||||
# Járművek számolása
|
||||
private_stmt = select(Asset).where(
|
||||
Asset.owner_person_id == test_user.id,
|
||||
Asset.owner_org_id.is_(None)
|
||||
)
|
||||
private_result = await db.execute(private_stmt)
|
||||
private_count = len(private_result.scalars().all())
|
||||
|
||||
corporate_stmt = select(Asset).where(
|
||||
Asset.owner_person_id == test_user.id,
|
||||
Asset.owner_org_id == organization.id
|
||||
)
|
||||
corporate_result = await db.execute(corporate_stmt)
|
||||
corporate_count = len(corporate_result.scalars().all())
|
||||
|
||||
print(f"\n📊 ÖSSZEFOGLALÓ:")
|
||||
print(f" • Felhasználó: {test_user.email}")
|
||||
print(f" • Szervezet: {organization.name}")
|
||||
print(f" • Új járművek létrehozva: {created_count}")
|
||||
print(f" • Összes privát jármű: {private_count} db")
|
||||
print(f" • Összes céges jármű: {corporate_count} db")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("KÉSZ! A teszt járművek létrehozva.")
|
||||
print("Most már tesztelhető a Garage UI switcher.")
|
||||
print("=" * 60)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
61
backend/app/scripts/debug_scope_issue.py
Normal file
61
backend/app/scripts/debug_scope_issue.py
Normal file
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Debug the scope_id issue.
|
||||
"""
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity.identity import User
|
||||
from app.core.security import decode_token
|
||||
|
||||
# Token from verification test (hardcoded for now, we'll get it dynamically)
|
||||
TEST_TOKEN = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIyOCIsInJvbGUiOiJhZG1pbiIsInJhbmsiOjEsInNjb3BlX2xldmVsIjoib3JnYW5pemF0aW9uIiwic2NvcGVfaWQiOiIyOCIsImV4cCI6MTc3NDg2MjQwMCwiaWF0IjoxNzQzMzI2NDAwLCJ0eXBlIjoiYWNjZXNzIn0.4Q9n2vQ8q3V7X6Y5Z8A9B0C1D2E3F4G5H6I7J8K9L0"
|
||||
|
||||
async def debug():
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Decode token
|
||||
payload = decode_token(TEST_TOKEN)
|
||||
print(f"✅ Token payload:")
|
||||
print(f" - sub: {payload.get('sub')}")
|
||||
print(f" - scope_id: {payload.get('scope_id')}")
|
||||
print(f" - scope_level: {payload.get('scope_level')}")
|
||||
print(f" - role: {payload.get('role')}")
|
||||
|
||||
# Get user from database
|
||||
user_id = payload.get('sub')
|
||||
if user_id:
|
||||
user_stmt = select(User).where(User.id == int(user_id))
|
||||
user_result = await db.execute(user_stmt)
|
||||
user = user_result.scalar_one_or_none()
|
||||
|
||||
if user:
|
||||
print(f"\n✅ User from database (ID: {user.id}):")
|
||||
print(f" - scope_id: {user.scope_id}")
|
||||
print(f" - scope_level: {user.scope_level}")
|
||||
print(f" - person_id: {user.person_id}")
|
||||
|
||||
# Check what the assets endpoint would see
|
||||
print(f"\n🔍 Assets endpoint logic:")
|
||||
print(f" - current_user.scope_id: {user.scope_id}")
|
||||
print(f" - Type: {type(user.scope_id)}")
|
||||
print(f" - Is None? {user.scope_id is None}")
|
||||
print(f" - == 'None'? {user.scope_id == 'None'}")
|
||||
print(f" - == ''? {user.scope_id == ''}")
|
||||
|
||||
if user.scope_id is None:
|
||||
print(" → Would go to PERSONAL mode")
|
||||
else:
|
||||
print(" → Would go to CORPORATE mode")
|
||||
try:
|
||||
scope_org_id = int(user.scope_id)
|
||||
print(f" → scope_org_id: {scope_org_id}")
|
||||
except (ValueError, TypeError):
|
||||
print(f" → scope_org_id: None (invalid)")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(debug())
|
||||
104
backend/app/scripts/diagnose_vehicle_filtering.py
Normal file
104
backend/app/scripts/diagnose_vehicle_filtering.py
Normal file
@@ -0,0 +1,104 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Diagnostic script to understand why vehicle filtering isn't working correctly.
|
||||
"""
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from sqlalchemy import select, or_
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.vehicle.asset import Asset
|
||||
from app.models.identity.identity import User
|
||||
|
||||
async def diagnose():
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Get User 28
|
||||
user_stmt = select(User).where(User.id == 28)
|
||||
user_result = await db.execute(user_stmt)
|
||||
test_user = user_result.scalar_one_or_none()
|
||||
|
||||
if not test_user:
|
||||
print("❌ User 28 not found")
|
||||
return
|
||||
|
||||
print(f"✅ Found User 28:")
|
||||
print(f" - User ID: {test_user.id}")
|
||||
print(f" - Person ID: {test_user.person_id}")
|
||||
print(f" - Email: {test_user.email}")
|
||||
|
||||
# Get all vehicles
|
||||
asset_stmt = select(Asset).where(
|
||||
or_(
|
||||
Asset.license_plate == "MNO-345",
|
||||
Asset.license_plate == "PQR-678"
|
||||
)
|
||||
)
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
assets = asset_result.scalars().all()
|
||||
|
||||
print(f"\n✅ Found {len(assets)} vehicles:")
|
||||
for asset in assets:
|
||||
print(f"\n Vehicle: {asset.license_plate}")
|
||||
print(f" - Asset ID: {asset.id}")
|
||||
print(f" - Owner Person ID: {asset.owner_person_id}")
|
||||
print(f" - Operator Person ID: {asset.operator_person_id}")
|
||||
print(f" - Owner Org ID: {asset.owner_org_id}")
|
||||
print(f" - Operator Org ID: {asset.operator_org_id}")
|
||||
print(f" - Branch ID: {asset.branch_id}")
|
||||
print(f" - Current Org ID: {asset.current_organization_id}")
|
||||
|
||||
# Check if it matches user's person_id
|
||||
matches_owner = asset.owner_person_id == test_user.person_id
|
||||
matches_operator = asset.operator_person_id == test_user.person_id
|
||||
print(f" - Matches owner_person_id ({test_user.person_id}): {matches_owner}")
|
||||
print(f" - Matches operator_person_id ({test_user.person_id}): {matches_operator}")
|
||||
|
||||
# Now test the actual query logic from assets.py
|
||||
print(f"\n🔍 Testing the actual query logic:")
|
||||
|
||||
# Personal mode query (from assets.py lines 41-57)
|
||||
stmt = (
|
||||
select(Asset)
|
||||
.where(
|
||||
or_(
|
||||
Asset.owner_org_id.is_(None),
|
||||
Asset.operator_org_id.is_(None)
|
||||
),
|
||||
or_(
|
||||
Asset.owner_person_id == test_user.person_id,
|
||||
Asset.operator_person_id == test_user.person_id
|
||||
)
|
||||
)
|
||||
.order_by(Asset.created_at.desc())
|
||||
)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
personal_assets = result.scalars().all()
|
||||
|
||||
print(f" Personal mode query returns {len(personal_assets)} vehicles:")
|
||||
for asset in personal_assets:
|
||||
print(f" - {asset.license_plate}")
|
||||
|
||||
# Corporate mode query (for organization 15)
|
||||
print(f"\n🔍 Testing corporate mode query (org_id = 15):")
|
||||
stmt = (
|
||||
select(Asset)
|
||||
.where(
|
||||
Asset.current_organization_id == 15,
|
||||
Asset.branch_id.is_not(None)
|
||||
)
|
||||
.order_by(Asset.created_at.desc())
|
||||
)
|
||||
|
||||
result = await db.execute(stmt)
|
||||
corporate_assets = result.scalars().all()
|
||||
|
||||
print(f" Corporate mode query returns {len(corporate_assets)} vehicles:")
|
||||
for asset in corporate_assets:
|
||||
print(f" - {asset.license_plate}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(diagnose())
|
||||
100
backend/app/scripts/fix_asset_person_ids.py
Normal file
100
backend/app/scripts/fix_asset_person_ids.py
Normal file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fix asset person IDs - update owner_person_id from User ID to Person ID.
|
||||
"""
|
||||
import asyncio
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.vehicle.asset import Asset
|
||||
from app.models.identity.identity import User
|
||||
|
||||
async def fix_asset_person_ids():
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Get User 28
|
||||
user_stmt = select(User).where(User.id == 28)
|
||||
user_result = await db.execute(user_stmt)
|
||||
test_user = user_result.scalar_one_or_none()
|
||||
|
||||
if not test_user:
|
||||
print("❌ User 28 not found")
|
||||
return
|
||||
|
||||
print(f"✅ Found User 28:")
|
||||
print(f" - User ID: {test_user.id}")
|
||||
print(f" - Person ID: {test_user.person_id}")
|
||||
print(f" - Email: {test_user.email}")
|
||||
|
||||
if not test_user.person_id:
|
||||
print("❌ User has no person_id")
|
||||
return
|
||||
|
||||
# Find assets with owner_person_id = 28 (User ID)
|
||||
asset_stmt = select(Asset).where(
|
||||
Asset.owner_person_id == 28
|
||||
)
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
assets = asset_result.scalars().all()
|
||||
|
||||
print(f"\n✅ Found {len(assets)} assets with owner_person_id = 28:")
|
||||
for asset in assets:
|
||||
print(f" - {asset.license_plate} (Asset ID: {asset.id})")
|
||||
|
||||
if assets:
|
||||
# Update them to have person_id = 29
|
||||
update_stmt = (
|
||||
update(Asset)
|
||||
.where(Asset.owner_person_id == 28)
|
||||
.values(owner_person_id=test_user.person_id)
|
||||
)
|
||||
|
||||
result = await db.execute(update_stmt)
|
||||
await db.commit()
|
||||
|
||||
print(f"\n✅ Updated {result.rowcount} assets:")
|
||||
print(f" - Changed owner_person_id from 28 to {test_user.person_id}")
|
||||
|
||||
# Verify the update
|
||||
asset_stmt = select(Asset).where(
|
||||
Asset.owner_person_id == test_user.person_id
|
||||
)
|
||||
asset_result = await db.execute(asset_stmt)
|
||||
updated_assets = asset_result.scalars().all()
|
||||
|
||||
print(f"\n✅ Verification - Found {len(updated_assets)} assets with owner_person_id = {test_user.person_id}:")
|
||||
for asset in updated_assets:
|
||||
print(f" - {asset.license_plate}")
|
||||
else:
|
||||
print("\nℹ️ No assets found with owner_person_id = 28")
|
||||
|
||||
# Also check for operator_person_id = 28
|
||||
operator_asset_stmt = select(Asset).where(
|
||||
Asset.operator_person_id == 28
|
||||
)
|
||||
operator_asset_result = await db.execute(operator_asset_stmt)
|
||||
operator_assets = operator_asset_result.scalars().all()
|
||||
|
||||
print(f"\n✅ Found {len(operator_assets)} assets with operator_person_id = 28:")
|
||||
for asset in operator_assets:
|
||||
print(f" - {asset.license_plate} (Asset ID: {asset.id})")
|
||||
|
||||
if operator_assets:
|
||||
# Update them to have person_id = 29
|
||||
update_stmt = (
|
||||
update(Asset)
|
||||
.where(Asset.operator_person_id == 28)
|
||||
.values(operator_person_id=test_user.person_id)
|
||||
)
|
||||
|
||||
result = await db.execute(update_stmt)
|
||||
await db.commit()
|
||||
|
||||
print(f"\n✅ Updated {result.rowcount} operator assets:")
|
||||
print(f" - Changed operator_person_id from 28 to {test_user.person_id}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(fix_asset_person_ids())
|
||||
227
backend/app/scripts/fix_test_user_garage.py
Normal file
227
backend/app/scripts/fix_test_user_garage.py
Normal file
@@ -0,0 +1,227 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Fix test user garage data and split vehicles between private and org branches.
|
||||
|
||||
This script:
|
||||
1. Fetches User 28 (tester_pro@profibot.hu)
|
||||
2. Creates a Private Organization for User 28 (if missing)
|
||||
3. Creates a Private Branch (Garage) named "Teszt Pro - Saját Garázs"
|
||||
4. Fetches the 2 vehicles owned by User 28 (MNO-345 and PQR-678)
|
||||
5. Splits them: Assigns MNO-345 to the newly created Private Branch
|
||||
6. Keeps PQR-678 in the Org 15 Branch (b2060e1d...)
|
||||
7. Commits changes
|
||||
|
||||
Run inside sf_api container:
|
||||
docker compose exec sf_api python -m app.scripts.fix_test_user_garage
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import uuid
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from sqlalchemy.orm import selectinload
|
||||
|
||||
# Add the backend directory to the path
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.models.marketplace.organization import Organization, OrganizationMember, Branch, OrgType, OrgUserRole
|
||||
from app.models.vehicle.asset import Asset
|
||||
|
||||
async def main():
|
||||
"""Main execution logic."""
|
||||
print("=" * 60)
|
||||
print("FIX TEST USER GARAGE DATA SCRIPT")
|
||||
print("=" * 60)
|
||||
|
||||
async with AsyncSessionLocal() as db:
|
||||
# 1. Fetch User 28 (tester_pro@profibot.hu)
|
||||
print("\n1. FETCHING TEST USER...")
|
||||
stmt = select(User).where(User.email == "tester_pro@profibot.hu")
|
||||
result = await db.execute(stmt)
|
||||
test_user = result.scalar_one_or_none()
|
||||
|
||||
if not test_user:
|
||||
print("❌ ERROR: Test user (tester_pro@profibot.hu) not found!")
|
||||
return
|
||||
|
||||
print(f" ✅ Test user found: ID={test_user.id}, Email={test_user.email}")
|
||||
|
||||
# 2. Check if user already has a private organization
|
||||
print("\n2. CHECKING FOR PRIVATE ORGANIZATION...")
|
||||
org_stmt = (
|
||||
select(Organization)
|
||||
.join(OrganizationMember)
|
||||
.where(OrganizationMember.user_id == test_user.id)
|
||||
.where(Organization.org_type == OrgType.individual)
|
||||
.where(Organization.is_deleted == False)
|
||||
)
|
||||
result = await db.execute(org_stmt)
|
||||
private_org = result.scalar_one_or_none()
|
||||
|
||||
if not private_org:
|
||||
print(" ⚠️ No private organization found, creating one...")
|
||||
|
||||
# Create private organization
|
||||
from datetime import datetime, timezone
|
||||
now = datetime.now(timezone.utc)
|
||||
private_org = Organization(
|
||||
full_name=f"Private Organization for {test_user.email}",
|
||||
name=f"Private_{test_user.id}",
|
||||
display_name=f"Teszt Pro - Privát",
|
||||
folder_slug=f"priv_{test_user.id}",
|
||||
org_type=OrgType.individual,
|
||||
status="active",
|
||||
is_active=True,
|
||||
is_deleted=False,
|
||||
country_code="HU",
|
||||
language="hu",
|
||||
default_currency="HUF",
|
||||
first_registered_at=now,
|
||||
current_lifecycle_started_at=now,
|
||||
subscription_plan="FREE",
|
||||
base_asset_limit=1,
|
||||
purchased_extra_slots=0,
|
||||
notification_settings={"notify_owner": True, "alert_days_before": [30, 15, 7, 1]},
|
||||
external_integration_config={},
|
||||
created_at=now,
|
||||
is_ownership_transferable=True
|
||||
)
|
||||
db.add(private_org)
|
||||
await db.flush() # Get the ID
|
||||
|
||||
# Add user as owner of the organization
|
||||
org_member = OrganizationMember(
|
||||
organization_id=private_org.id,
|
||||
user_id=test_user.id,
|
||||
person_id=test_user.person_id,
|
||||
role=OrgUserRole.OWNER,
|
||||
is_verified=True
|
||||
)
|
||||
db.add(org_member)
|
||||
|
||||
await db.commit()
|
||||
print(f" ✅ Created private organization: ID={private_org.id}, Name={private_org.name}")
|
||||
else:
|
||||
print(f" ✅ Private organization already exists: ID={private_org.id}, Name={private_org.name}")
|
||||
|
||||
# 3. Check/create private branch (garage)
|
||||
print("\n3. CHECKING/CREATING PRIVATE BRANCH (GARAGE)...")
|
||||
branch_stmt = select(Branch).where(
|
||||
Branch.organization_id == private_org.id,
|
||||
Branch.name.ilike("%Teszt Pro - Saját Garázs%"),
|
||||
Branch.is_deleted == False
|
||||
)
|
||||
result = await db.execute(branch_stmt)
|
||||
private_branch = result.scalar_one_or_none()
|
||||
|
||||
if not private_branch:
|
||||
private_branch = Branch(
|
||||
id=uuid.uuid4(),
|
||||
organization_id=private_org.id,
|
||||
name="Teszt Pro - Saját Garázs",
|
||||
is_main=True,
|
||||
status="active",
|
||||
is_deleted=False,
|
||||
postal_code="1234",
|
||||
city="Budapest",
|
||||
street_name="Teszt utca",
|
||||
house_number="1"
|
||||
)
|
||||
db.add(private_branch)
|
||||
await db.commit()
|
||||
print(f" ✅ Created private branch: ID={private_branch.id}, Name={private_branch.name}")
|
||||
else:
|
||||
print(f" ✅ Private branch already exists: ID={private_branch.id}, Name={private_branch.name}")
|
||||
|
||||
# 4. Fetch the 2 vehicles by license plate (regardless of owner)
|
||||
print("\n4. FETCHING VEHICLES BY LICENSE PLATE...")
|
||||
|
||||
# Find vehicles by license plate
|
||||
vehicles_stmt = select(Asset).where(
|
||||
Asset.license_plate.in_(["MNO-345", "PQR-678"])
|
||||
)
|
||||
result = await db.execute(vehicles_stmt)
|
||||
vehicles = result.scalars().all()
|
||||
|
||||
print(f" Found {len(vehicles)} vehicles with plates MNO-345 or PQR-678")
|
||||
|
||||
# 5. Find Org 15 branch (b2060e1d...)
|
||||
print("\n5. FINDING ORG 15 BRANCH...")
|
||||
# First find Org 15
|
||||
org15_stmt = select(Organization).where(
|
||||
Organization.id == 15,
|
||||
Organization.is_deleted == False
|
||||
)
|
||||
result = await db.execute(org15_stmt)
|
||||
org15 = result.scalar_one_or_none()
|
||||
|
||||
if not org15:
|
||||
print("❌ ERROR: Organization 15 not found!")
|
||||
return
|
||||
|
||||
print(f" ✅ Organization 15 found: ID={org15.id}, Name={org15.name}")
|
||||
|
||||
# Find a branch in Org 15
|
||||
org15_branch_stmt = select(Branch).where(
|
||||
Branch.organization_id == 15,
|
||||
Branch.is_deleted == False
|
||||
).limit(1)
|
||||
result = await db.execute(org15_branch_stmt)
|
||||
org15_branch = result.scalar_one_or_none()
|
||||
|
||||
if not org15_branch:
|
||||
print("❌ ERROR: No branch found in Organization 15!")
|
||||
return
|
||||
|
||||
print(f" ✅ Org 15 branch found: ID={org15_branch.id}, Name={org15_branch.name}")
|
||||
|
||||
# 6. Split vehicles
|
||||
print("\n6. SPLITTING VEHICLES BETWEEN BRANCHES...")
|
||||
updated_count = 0
|
||||
|
||||
for vehicle in vehicles:
|
||||
if vehicle.license_plate == "MNO-345":
|
||||
# Assign to private branch
|
||||
vehicle.branch_id = private_branch.id
|
||||
vehicle.current_organization_id = private_org.id
|
||||
print(f" ✅ Assigned MNO-345 to private branch: {private_branch.name}")
|
||||
updated_count += 1
|
||||
elif vehicle.license_plate == "PQR-678":
|
||||
# Keep in Org 15 branch
|
||||
vehicle.branch_id = org15_branch.id
|
||||
vehicle.current_organization_id = 15
|
||||
print(f" ✅ Kept PQR-678 in Org 15 branch: {org15_branch.name}")
|
||||
updated_count += 1
|
||||
|
||||
if updated_count > 0:
|
||||
await db.commit()
|
||||
print(f"\n✅ Successfully updated {updated_count} vehicles")
|
||||
else:
|
||||
print("\n⚠️ No vehicles needed updating")
|
||||
|
||||
# 7. Verify the split
|
||||
print("\n7. VERIFICATION...")
|
||||
for plate in ["MNO-345", "PQR-678"]:
|
||||
verify_stmt = select(Asset).where(Asset.license_plate == plate).options(selectinload(Asset.catalog))
|
||||
result = await db.execute(verify_stmt)
|
||||
vehicle = result.scalar_one_or_none()
|
||||
if vehicle:
|
||||
branch_name = "Unknown"
|
||||
if vehicle.branch_id == private_branch.id:
|
||||
branch_name = "Private Branch"
|
||||
elif vehicle.branch_id == org15_branch.id:
|
||||
branch_name = "Org 15 Branch"
|
||||
|
||||
print(f" {plate}: Branch ID={vehicle.branch_id} ({branch_name}), Org ID={vehicle.current_organization_id}")
|
||||
else:
|
||||
print(f" {plate}: Not found")
|
||||
|
||||
print("\n" + "=" * 60)
|
||||
print("SCRIPT COMPLETED SUCCESSFULLY")
|
||||
print("=" * 60)
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
190
backend/app/scripts/migrate_vehicles_to_garages.py
Normal file
190
backend/app/scripts/migrate_vehicles_to_garages.py
Normal file
@@ -0,0 +1,190 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Migration script to assign existing vehicles to their organization's default garage (branch).
|
||||
This fixes the issue where existing vehicles have branch_id = NULL after the column was added.
|
||||
|
||||
Logic:
|
||||
1. For each Asset with owner_org_id or operator_org_id
|
||||
2. Find the default Branch (Garage) for that Organization (is_main = True)
|
||||
3. Update Asset.branch_id to that Branch's UUID
|
||||
4. If no default branch exists, create one
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
import sys
|
||||
from sqlalchemy import select, update
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.database import AsyncSessionLocal
|
||||
from app.models.vehicle.asset import Asset
|
||||
from app.models.marketplace.organization import Branch, Organization
|
||||
from app.models.identity import User
|
||||
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
async def get_or_create_default_branch(session: AsyncSession, organization_id: int) -> Branch:
|
||||
"""Get the default branch (is_main = True) for an organization, create if doesn't exist."""
|
||||
# Try to find existing default branch
|
||||
stmt = select(Branch).where(
|
||||
Branch.organization_id == organization_id,
|
||||
Branch.is_main == True,
|
||||
Branch.is_deleted == False
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
branch = result.scalar_one_or_none()
|
||||
|
||||
if branch:
|
||||
logger.info(f"Found default branch {branch.id} for organization {organization_id}")
|
||||
return branch
|
||||
|
||||
# If no default branch exists, create one
|
||||
logger.warning(f"No default branch found for organization {organization_id}, creating one...")
|
||||
|
||||
# Get organization name for branch naming
|
||||
org_stmt = select(Organization).where(Organization.id == organization_id)
|
||||
org_result = await session.execute(org_stmt)
|
||||
organization = org_result.scalar_one_or_none()
|
||||
|
||||
org_name = organization.name if organization else f"Organization {organization_id}"
|
||||
|
||||
# Create default branch
|
||||
import uuid
|
||||
from datetime import datetime
|
||||
from sqlalchemy.sql import func
|
||||
|
||||
new_branch = Branch(
|
||||
id=uuid.uuid4(),
|
||||
organization_id=organization_id,
|
||||
name=f"{org_name} - Main Garage",
|
||||
is_main=True,
|
||||
status="active",
|
||||
is_deleted=False,
|
||||
created_at=datetime.utcnow()
|
||||
)
|
||||
|
||||
session.add(new_branch)
|
||||
await session.flush()
|
||||
logger.info(f"Created default branch {new_branch.id} for organization {organization_id}")
|
||||
|
||||
return new_branch
|
||||
|
||||
async def migrate_vehicles_to_garages():
|
||||
"""Main migration function."""
|
||||
async with AsyncSessionLocal() as session:
|
||||
try:
|
||||
# Get all assets that have organization ownership but no branch_id
|
||||
stmt = select(Asset).where(
|
||||
(Asset.owner_org_id.is_not(None) | Asset.operator_org_id.is_not(None)),
|
||||
Asset.branch_id.is_(None)
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
assets = result.scalars().all()
|
||||
|
||||
logger.info(f"Found {len(assets)} assets without branch assignment")
|
||||
|
||||
updated_count = 0
|
||||
skipped_count = 0
|
||||
|
||||
for asset in assets:
|
||||
# Determine which organization to use (prefer owner, fallback to operator)
|
||||
org_id = asset.owner_org_id or asset.operator_org_id
|
||||
|
||||
if not org_id:
|
||||
logger.warning(f"Asset {asset.id} has no organization reference, skipping")
|
||||
skipped_count += 1
|
||||
continue
|
||||
|
||||
# Get or create default branch for the organization
|
||||
branch = await get_or_create_default_branch(session, org_id)
|
||||
|
||||
# Update the asset
|
||||
update_stmt = (
|
||||
update(Asset)
|
||||
.where(Asset.id == asset.id)
|
||||
.values(branch_id=branch.id, relocation_performed=True)
|
||||
)
|
||||
await session.execute(update_stmt)
|
||||
|
||||
logger.info(f"Updated asset {asset.id} with branch {branch.id} (org {org_id})")
|
||||
updated_count += 1
|
||||
|
||||
# Commit all changes
|
||||
await session.commit()
|
||||
|
||||
logger.info(f"Migration completed: {updated_count} assets updated, {skipped_count} skipped")
|
||||
|
||||
# Also update assets that already have branch_id but need relocation_performed flag
|
||||
if updated_count > 0:
|
||||
stmt = select(Asset).where(
|
||||
Asset.branch_id.is_not(None),
|
||||
Asset.relocation_performed == False
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
assets_without_flag = result.scalars().all()
|
||||
|
||||
for asset in assets_without_flag:
|
||||
update_stmt = (
|
||||
update(Asset)
|
||||
.where(Asset.id == asset.id)
|
||||
.values(relocation_performed=True)
|
||||
)
|
||||
await session.execute(update_stmt)
|
||||
|
||||
await session.commit()
|
||||
logger.info(f"Updated relocation_performed flag for {len(assets_without_flag)} assets")
|
||||
|
||||
return updated_count
|
||||
|
||||
except Exception as e:
|
||||
await session.rollback()
|
||||
logger.error(f"Migration failed: {e}")
|
||||
raise
|
||||
|
||||
async def verify_migration():
|
||||
"""Verify the migration results."""
|
||||
async with AsyncSessionLocal() as session:
|
||||
# Count assets with branch_id
|
||||
stmt = select(Asset).where(Asset.branch_id.is_not(None))
|
||||
result = await session.execute(stmt)
|
||||
assets_with_branch = result.scalars().all()
|
||||
|
||||
# Count assets without branch_id but with organizations
|
||||
stmt = select(Asset).where(
|
||||
(Asset.owner_org_id.is_not(None) | Asset.operator_org_id.is_not(None)),
|
||||
Asset.branch_id.is_(None)
|
||||
)
|
||||
result = await session.execute(stmt)
|
||||
assets_still_missing = result.scalars().all()
|
||||
|
||||
logger.info(f"Verification:")
|
||||
logger.info(f" - Assets with branch_id: {len(assets_with_branch)}")
|
||||
logger.info(f" - Assets still missing branch_id: {len(assets_still_missing)}")
|
||||
|
||||
if assets_still_missing:
|
||||
logger.warning("Some assets still missing branch_id:")
|
||||
for asset in assets_still_missing[:5]: # Show first 5
|
||||
logger.warning(f" Asset {asset.id}: owner_org={asset.owner_org_id}, operator_org={asset.operator_org_id}")
|
||||
|
||||
return len(assets_with_branch), len(assets_still_missing)
|
||||
|
||||
if __name__ == "__main__":
|
||||
logger.info("Starting vehicle-to-garage migration...")
|
||||
|
||||
try:
|
||||
# Run migration
|
||||
updated = asyncio.run(migrate_vehicles_to_garages())
|
||||
|
||||
# Verify
|
||||
with_branch, missing = asyncio.run(verify_migration())
|
||||
|
||||
if missing == 0:
|
||||
logger.info("✅ Migration successful! All organizational vehicles now have branch assignments.")
|
||||
else:
|
||||
logger.warning(f"⚠️ Migration incomplete: {missing} assets still lack branch_id")
|
||||
sys.exit(1)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"❌ Migration failed: {e}")
|
||||
sys.exit(1)
|
||||
62
backend/app/scripts/reset_test_user_password.py
Normal file
62
backend/app/scripts/reset_test_user_password.py
Normal file
@@ -0,0 +1,62 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Reset password for tester_pro@profibot.hu to 'Password123!'
|
||||
"""
|
||||
import sys
|
||||
import os
|
||||
sys.path.insert(0, '/app/backend')
|
||||
|
||||
from app.core.security import get_password_hash
|
||||
from sqlalchemy import create_engine, text
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
# Database URL from environment
|
||||
DATABASE_URL = "postgresql+psycopg2://kincses:MiskociA74@shared-postgres:5432/service_finder"
|
||||
|
||||
def reset_password():
|
||||
"""Reset password for tester_pro@profibot.hu"""
|
||||
engine = create_engine(DATABASE_URL)
|
||||
Session = sessionmaker(bind=engine)
|
||||
session = Session()
|
||||
|
||||
try:
|
||||
# Get password hash for 'Password123!'
|
||||
password_hash = get_password_hash("Password123!")
|
||||
print(f"Password hash for 'Password123!': {password_hash}")
|
||||
|
||||
# Update the user
|
||||
update_stmt = text("""
|
||||
UPDATE identity.users
|
||||
SET hashed_password = :password_hash
|
||||
WHERE email = :email
|
||||
""")
|
||||
|
||||
result = session.execute(
|
||||
update_stmt,
|
||||
{"password_hash": password_hash, "email": "tester_pro@profibot.hu"}
|
||||
)
|
||||
|
||||
session.commit()
|
||||
|
||||
if result.rowcount > 0:
|
||||
print(f"Successfully updated password for tester_pro@profibot.hu")
|
||||
return True
|
||||
else:
|
||||
print(f"User not found: tester_pro@profibot.hu")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
session.rollback()
|
||||
return False
|
||||
finally:
|
||||
session.close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("Resetting password for tester_pro@profibot.hu...")
|
||||
if reset_password():
|
||||
print("Password reset successful")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print("Password reset failed")
|
||||
sys.exit(1)
|
||||
108
backend/app/scripts/test_assets_api_directly.py
Normal file
108
backend/app/scripts/test_assets_api_directly.py
Normal file
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Test the assets API endpoint directly.
|
||||
"""
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import sys
|
||||
import os
|
||||
sys.path.append(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))))
|
||||
|
||||
async def test_assets_api():
|
||||
base_url = "http://sf_api:8000"
|
||||
|
||||
# First, login to get token
|
||||
async with aiohttp.ClientSession() as session:
|
||||
# Login
|
||||
login_data = {
|
||||
"username": "tester_pro@profibot.hu",
|
||||
"password": "Test123!"
|
||||
}
|
||||
|
||||
print("1. Logging in...")
|
||||
async with session.post(f"{base_url}/api/v1/auth/login", data=login_data) as resp:
|
||||
if resp.status != 200:
|
||||
print(f"❌ Login failed: {resp.status}")
|
||||
text = await resp.text()
|
||||
print(f"Response: {text}")
|
||||
return
|
||||
login_result = await resp.json()
|
||||
token = login_result["access_token"]
|
||||
print(f"✅ Login successful, token: {token[:50]}...")
|
||||
|
||||
# Set personal mode (scope_id = null)
|
||||
print("\n2. Setting personal mode (scope_id = null)...")
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
patch_data = {"organization_id": None}
|
||||
|
||||
async with session.patch(
|
||||
f"{base_url}/api/v1/users/me/active-organization",
|
||||
json=patch_data,
|
||||
headers=headers
|
||||
) as resp:
|
||||
if resp.status != 200:
|
||||
print(f"❌ PATCH failed: {resp.status}")
|
||||
text = await resp.text()
|
||||
print(f"Response: {text}")
|
||||
return
|
||||
patch_result = await resp.json()
|
||||
print(f"✅ PATCH successful")
|
||||
print(f" scope_id: {patch_result.get('scope_id')}")
|
||||
print(f" scope_level: {patch_result.get('scope_level')}")
|
||||
|
||||
# Get vehicles in personal mode
|
||||
print("\n3. Getting vehicles in personal mode...")
|
||||
async with session.get(
|
||||
f"{base_url}/api/v1/assets/vehicles",
|
||||
headers=headers
|
||||
) as resp:
|
||||
if resp.status != 200:
|
||||
print(f"❌ GET vehicles failed: {resp.status}")
|
||||
text = await resp.text()
|
||||
print(f"Response: {text}")
|
||||
return
|
||||
vehicles_result = await resp.json()
|
||||
print(f"✅ GET vehicles successful")
|
||||
print(f" Found {len(vehicles_result)} vehicles")
|
||||
for i, vehicle in enumerate(vehicles_result[:5]): # Show first 5
|
||||
print(f" {i+1}. {vehicle.get('license_plate')} (ID: {vehicle.get('id')})")
|
||||
if len(vehicles_result) > 5:
|
||||
print(f" ... and {len(vehicles_result) - 5} more")
|
||||
|
||||
# Now test corporate mode (org_id = 15)
|
||||
print("\n4. Setting corporate mode (org_id = 15)...")
|
||||
patch_data_corp = {"organization_id": "15"}
|
||||
|
||||
async with session.patch(
|
||||
f"{base_url}/api/v1/users/me/active-organization",
|
||||
json=patch_data_corp,
|
||||
headers=headers
|
||||
) as resp:
|
||||
if resp.status != 200:
|
||||
print(f"❌ PATCH corporate failed: {resp.status}")
|
||||
text = await resp.text()
|
||||
print(f"Response: {text}")
|
||||
return
|
||||
patch_corp_result = await resp.json()
|
||||
print(f"✅ PATCH corporate successful")
|
||||
print(f" scope_id: {patch_corp_result.get('scope_id')}")
|
||||
|
||||
# Get vehicles in corporate mode
|
||||
print("\n5. Getting vehicles in corporate mode...")
|
||||
async with session.get(
|
||||
f"{base_url}/api/v1/assets/vehicles",
|
||||
headers=headers
|
||||
) as resp:
|
||||
if resp.status != 200:
|
||||
print(f"❌ GET corporate vehicles failed: {resp.status}")
|
||||
text = await resp.text()
|
||||
print(f"Response: {text}")
|
||||
return
|
||||
vehicles_corp_result = await resp.json()
|
||||
print(f"✅ GET corporate vehicles successful")
|
||||
print(f" Found {len(vehicles_corp_result)} vehicles")
|
||||
for i, vehicle in enumerate(vehicles_corp_result[:5]):
|
||||
print(f" {i+1}. {vehicle.get('license_plate')}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(test_assets_api())
|
||||
175
backend/app/scripts/verify_scope_switcher.py
Normal file
175
backend/app/scripts/verify_scope_switcher.py
Normal file
@@ -0,0 +1,175 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Backend verification test for scope switcher functionality.
|
||||
|
||||
Simulates the frontend:
|
||||
1. Login as User 28 (tester_pro@profibot.hu) - get JWT token
|
||||
2. Call PATCH /active-organization with organization_id = null
|
||||
3. Call GET /vehicles - assert it returns exactly 1 car (MNO-345)
|
||||
4. Call PATCH /active-organization with organization_id = 15
|
||||
5. Call GET /vehicles - assert it returns exactly 1 car (PQR-678)
|
||||
|
||||
Run inside sf_api container:
|
||||
docker compose exec sf_api python -m app.scripts.verify_scope_switcher
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import httpx
|
||||
import json
|
||||
from typing import Dict, Any
|
||||
|
||||
# Add the backend directory to the path
|
||||
sys.path.insert(0, '/app')
|
||||
|
||||
from app.db.session import AsyncSessionLocal
|
||||
from app.models.identity import User
|
||||
from app.core.security import create_tokens
|
||||
from app.core.config import settings
|
||||
|
||||
async def get_auth_token() -> str:
|
||||
"""Get JWT token for test user by simulating login."""
|
||||
async with AsyncSessionLocal() as db:
|
||||
# Find the test user
|
||||
from sqlalchemy import select
|
||||
stmt = select(User).where(User.email == "tester_pro@profibot.hu")
|
||||
result = await db.execute(stmt)
|
||||
user = result.scalar_one_or_none()
|
||||
|
||||
if not user:
|
||||
raise Exception("Test user not found")
|
||||
|
||||
# Create token directly (simulating login)
|
||||
from app.core.security import DEFAULT_RANK_MAP
|
||||
ranks = DEFAULT_RANK_MAP # Simplified
|
||||
|
||||
token_data = {
|
||||
"sub": str(user.id),
|
||||
"role": user.role.value if hasattr(user.role, 'value') else str(user.role),
|
||||
"rank": ranks.get(user.role.value.upper(), 10),
|
||||
"scope_level": user.scope_level or "individual",
|
||||
"scope_id": str(user.scope_id) if user.scope_id else str(user.id)
|
||||
}
|
||||
|
||||
access_token, _ = create_tokens(data=token_data)
|
||||
return access_token
|
||||
|
||||
async def make_api_request(method: str, endpoint: str, token: str, data: Dict = None) -> Dict[str, Any]:
|
||||
"""Make HTTP request to the API."""
|
||||
base_url = "http://localhost:8000/api/v1"
|
||||
headers = {
|
||||
"Authorization": f"Bearer {token}",
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
|
||||
async with httpx.AsyncClient(timeout=30.0) as client:
|
||||
url = f"{base_url}{endpoint}"
|
||||
|
||||
if method == "GET":
|
||||
response = await client.get(url, headers=headers)
|
||||
elif method == "PATCH":
|
||||
response = await client.patch(url, headers=headers, json=data)
|
||||
else:
|
||||
raise ValueError(f"Unsupported method: {method}")
|
||||
|
||||
response.raise_for_status()
|
||||
return response.json()
|
||||
|
||||
async def main():
|
||||
"""Main verification test."""
|
||||
print("=" * 60)
|
||||
print("SCOPE SWITCHER VERIFICATION TEST")
|
||||
print("=" * 60)
|
||||
|
||||
try:
|
||||
# 1. Get auth token
|
||||
print("\n1. AUTHENTICATING AS TEST USER...")
|
||||
token = await get_auth_token()
|
||||
print(f" ✅ Token obtained (length: {len(token)})")
|
||||
|
||||
# 2. Test personal mode (organization_id = null)
|
||||
print("\n2. TESTING PERSONAL MODE (organization_id = null)...")
|
||||
patch_data = {"organization_id": None}
|
||||
patch_response = await make_api_request("PATCH", "/users/me/active-organization", token, patch_data)
|
||||
print(f" ✅ PATCH response: {json.dumps(patch_response, indent=2)}")
|
||||
|
||||
# Check if scope_id was updated
|
||||
if patch_response.get("scope_id") is not None:
|
||||
print(" ❌ ERROR: scope_id should be null in personal mode!")
|
||||
return
|
||||
|
||||
# 3. Get vehicles in personal mode
|
||||
print("\n3. GETTING VEHICLES IN PERSONAL MODE...")
|
||||
vehicles_response = await make_api_request("GET", "/assets/vehicles", token)
|
||||
vehicles_count = len(vehicles_response)
|
||||
print(f" ✅ Found {vehicles_count} vehicles")
|
||||
|
||||
# Check license plates
|
||||
license_plates = [v.get("license_plate") for v in vehicles_response if v.get("license_plate")]
|
||||
print(f" ✅ License plates: {license_plates}")
|
||||
|
||||
# Should have exactly 1 car (MNO-345)
|
||||
if vehicles_count != 1:
|
||||
print(f" ❌ ERROR: Expected 1 vehicle in personal mode, got {vehicles_count}")
|
||||
return
|
||||
|
||||
if "MNO-345" not in license_plates:
|
||||
print(f" ❌ ERROR: Expected MNO-345 in personal mode, got {license_plates}")
|
||||
return
|
||||
|
||||
print(" ✅ PERSONAL MODE TEST PASSED: Exactly 1 car (MNO-345)")
|
||||
|
||||
# 4. Test corporate mode (organization_id = 15)
|
||||
print("\n4. TESTING CORPORATE MODE (organization_id = 15)...")
|
||||
patch_data = {"organization_id": 15}
|
||||
patch_response = await make_api_request("PATCH", "/users/me/active-organization", token, patch_data)
|
||||
print(f" ✅ PATCH response: {json.dumps(patch_response, indent=2)}")
|
||||
|
||||
# Check if scope_id was updated
|
||||
if patch_response.get("scope_id") != "15":
|
||||
print(f" ❌ ERROR: scope_id should be '15', got {patch_response.get('scope_id')}")
|
||||
return
|
||||
|
||||
# 5. Get vehicles in corporate mode
|
||||
print("\n5. GETTING VEHICLES IN CORPORATE MODE...")
|
||||
vehicles_response = await make_api_request("GET", "/assets/vehicles", token)
|
||||
vehicles_count = len(vehicles_response)
|
||||
print(f" ✅ Found {vehicles_count} vehicles")
|
||||
|
||||
# Check license plates
|
||||
license_plates = [v.get("license_plate") for v in vehicles_response if v.get("license_plate")]
|
||||
print(f" ✅ License plates: {license_plates}")
|
||||
|
||||
# Should have exactly 1 car (PQR-678)
|
||||
if vehicles_count != 1:
|
||||
print(f" ❌ ERROR: Expected 1 vehicle in corporate mode, got {vehicles_count}")
|
||||
return
|
||||
|
||||
if "PQR-678" not in license_plates:
|
||||
print(f" ❌ ERROR: Expected PQR-678 in corporate mode, got {license_plates}")
|
||||
return
|
||||
|
||||
print(" ✅ CORPORATE MODE TEST PASSED: Exactly 1 car (PQR-678)")
|
||||
|
||||
# 6. Final verification
|
||||
print("\n" + "=" * 60)
|
||||
print("✅ ALL TESTS PASSED!")
|
||||
print("=" * 60)
|
||||
print("\nSUMMARY:")
|
||||
print("- Personal mode (scope_id = null): Shows 1 vehicle (MNO-345)")
|
||||
print("- Corporate mode (scope_id = 15): Shows 1 vehicle (PQR-678)")
|
||||
print("- Scope switcher endpoint works correctly")
|
||||
print("- Vehicle filtering by branch/organization works correctly")
|
||||
|
||||
except httpx.HTTPStatusError as e:
|
||||
print(f"\n❌ HTTP ERROR: {e}")
|
||||
print(f"Response: {e.response.text}")
|
||||
return
|
||||
except Exception as e:
|
||||
print(f"\n❌ ERROR: {type(e).__name__}: {e}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user