Aplicar lógica de validación de tenant y compañía a todos los endpoints relevantes en los módulos: client_and_provider, classes, country_rule_oct, exchange_rate, fraction_rule_octave, package, parts, permission_rule_oct, seal
This commit is contained in:
@@ -35,6 +35,13 @@ async def list_classes(
|
||||
"""
|
||||
List classes with optional filters and pagination
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClassService(db)
|
||||
search_params = ClassSearchDTO(
|
||||
client_id=client_id,
|
||||
@@ -58,6 +65,13 @@ async def get_classes_by_client(
|
||||
"""
|
||||
Get all classes for a specific client
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClassService(db)
|
||||
return service.search_by_client(client_id, skip, limit)
|
||||
|
||||
|
||||
31
backend/api/v1/modules/a76/classes/test_classes.py
Normal file
31
backend/api/v1/modules/a76/classes/test_classes.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_classes(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/classes/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_class_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/classes/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_class_forbidden():
|
||||
response = client.post("/classes/", json={"name": "Test Class"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_class_forbidden():
|
||||
response = client.put("/classes/1", json={"name": "Updated Class"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -28,6 +28,17 @@ async def create_client_provider(
|
||||
"""
|
||||
Create a new client or provider in the system
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
# Ensure the client_data is associated with the correct tenant and company
|
||||
if client_data.tenant_id != tenant_id or client_data.company_id != company_id:
|
||||
raise HTTPException(status_code=400, detail="Mismatch in tenant or company association")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
return service.create_client_provider(client_data)
|
||||
|
||||
@@ -45,6 +56,13 @@ async def list_clients_providers(
|
||||
"""
|
||||
List clients and providers with optional filters and pagination
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
return service.list_clients_providers(skip, limit, search, client_or_provider, enabled_only)
|
||||
|
||||
@@ -59,6 +77,13 @@ async def get_clients_only(
|
||||
"""
|
||||
Get only clients (client_or_provider = 'C')
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
return service.get_clients_only(skip, limit)
|
||||
|
||||
@@ -73,6 +98,13 @@ async def get_providers_only(
|
||||
"""
|
||||
Get only providers (client_or_provider = 'P')
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
return service.get_providers_only(skip, limit)
|
||||
|
||||
@@ -86,6 +118,13 @@ async def search_by_rfc(
|
||||
"""
|
||||
Search clients/providers by RFC
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
return service.search_by_rfc(rfc)
|
||||
|
||||
@@ -99,6 +138,13 @@ async def get_client_provider(
|
||||
"""
|
||||
Get client/provider by ID with all related information
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
client = service.get_client_provider(client_id)
|
||||
if not client:
|
||||
@@ -116,6 +162,13 @@ async def update_client_provider(
|
||||
"""
|
||||
Update client/provider information
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
client = service.update_client_provider(client_id, client_data)
|
||||
if not client:
|
||||
@@ -134,6 +187,13 @@ async def delete_client_provider(
|
||||
|
||||
Note: This will completely remove the client/provider and all related data.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
if not service.delete_client_provider(client_id):
|
||||
raise HTTPException(status_code=404, detail=f"Client/Provider with ID '{client_id}' not found")
|
||||
@@ -148,6 +208,13 @@ async def toggle_client_provider_status(
|
||||
"""
|
||||
Toggle client/provider enabled/disabled status
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
client = service.toggle_status(client_id)
|
||||
if not client:
|
||||
@@ -165,6 +232,13 @@ async def get_client_provider_address(
|
||||
"""
|
||||
Get only address information for a client/provider
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
client = service.get_client_provider(client_id)
|
||||
if not client:
|
||||
@@ -185,6 +259,13 @@ async def get_client_provider_programs(
|
||||
"""
|
||||
Get only programs information for a client/provider
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
client = service.get_client_provider(client_id)
|
||||
if not client:
|
||||
@@ -205,6 +286,13 @@ async def get_client_provider_basic_info(
|
||||
"""
|
||||
Get basic information for a client/provider (without address and programs)
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = ClientProviderService(db)
|
||||
client = service.get_client_provider(client_id)
|
||||
if not client:
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_clients_and_providers(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/client_and_provider/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_client_or_provider_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/client_and_provider/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_client_or_provider_forbidden():
|
||||
response = client.post("/client_and_provider/", json={"name": "Test Client/Provider"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_client_or_provider_forbidden():
|
||||
response = client.put("/client_and_provider/1", json={"name": "Updated Client/Provider"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
31
backend/api/v1/modules/a76/company/test_company.py
Normal file
31
backend/api/v1/modules/a76/company/test_company.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_companies(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/company/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_company_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/company/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_company_forbidden():
|
||||
response = client.post("/company/", json={"name": "Test Company"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_company_forbidden():
|
||||
response = client.put("/company/1", json={"name": "Updated Company"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -18,6 +18,13 @@ async def list_countries(
|
||||
"""
|
||||
List all CountryRuleOct entries.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return db.query(CountryRuleOctService).all()
|
||||
|
||||
|
||||
@@ -33,6 +40,13 @@ async def read_country_rule(
|
||||
"""
|
||||
Get a specific CountryRuleOct by its composite key.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
country = CountryRuleOctService.get_country_by_keys(db, permission, line, fraction, country_code)
|
||||
if not country:
|
||||
raise HTTPException(status_code=404, detail="CountryRuleOct not found")
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_country_rules(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/country-rule-oct/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_country_rule_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/country-rule-oct/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_country_rule_forbidden():
|
||||
response = client.post("/country-rule-oct/", json={"rule": "Test Rule"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_country_rule_forbidden():
|
||||
response = client.put("/country-rule-oct/1", json={"rule": "Updated Rule"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -18,6 +18,13 @@ async def list_exchange_rates(
|
||||
"""
|
||||
List all ExchangeRate entries.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return db.query(ExchangeRateService).all()
|
||||
|
||||
|
||||
@@ -30,6 +37,13 @@ async def read_exchange_rate(
|
||||
"""
|
||||
Get a specific ExchangeRate by its date.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
exchange_rate = ExchangeRateService.get_exchange_rate_by_date(db, date)
|
||||
if not exchange_rate:
|
||||
raise HTTPException(status_code=404, detail="ExchangeRate not found")
|
||||
@@ -45,6 +59,13 @@ async def create_exchange_rate(
|
||||
"""
|
||||
Create a new ExchangeRate entry.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return ExchangeRateService.create_exchange_rate(db, exchange_rate_data)
|
||||
|
||||
|
||||
@@ -57,6 +78,13 @@ async def delete_exchange_rate(
|
||||
"""
|
||||
Delete an ExchangeRate by its date.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
exchange_rate = ExchangeRateService.delete_exchange_rate(db, date)
|
||||
if not exchange_rate:
|
||||
raise HTTPException(status_code=404, detail="ExchangeRate not found")
|
||||
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_exchange_rates(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/exchange-rate/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_exchange_rate_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/exchange-rate/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_exchange_rate_forbidden():
|
||||
response = client.post("/exchange-rate/", json={"rate": 1.23})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_exchange_rate_forbidden():
|
||||
response = client.put("/exchange-rate/1", json={"rate": 1.45})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -18,6 +18,13 @@ async def list_fractions(
|
||||
"""
|
||||
List all FractionRuleOctave entries.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return db.query(FractionRuleOctaveService).all()
|
||||
|
||||
|
||||
@@ -32,6 +39,13 @@ async def read_fraction(
|
||||
"""
|
||||
Get a specific FractionRuleOctave by its composite key.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
frac = FractionRuleOctaveService.get_fraction_by_permission_line(db, permission, line, fraction)
|
||||
if not frac:
|
||||
raise HTTPException(status_code=404, detail="FractionRuleOctave not found")
|
||||
@@ -47,6 +61,13 @@ async def create_frac(
|
||||
"""
|
||||
Create a new FractionRuleOctave entry.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return FractionRuleOctaveService.create_frac(db, frac_data)
|
||||
|
||||
|
||||
@@ -61,6 +82,13 @@ async def delete_fraction(
|
||||
"""
|
||||
Delete a FractionRuleOctave by its composite key.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
frac = FractionRuleOctaveService.delete_fraction(db, permission, line, fraction)
|
||||
if not frac:
|
||||
raise HTTPException(status_code=404, detail="FractionRuleOctave not found")
|
||||
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_fraction_rules(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/fraction_rule_octave/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_fraction_rule_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/fraction_rule_octave/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_fraction_rule_forbidden():
|
||||
response = client.post("/fraction_rule_octave/", json={"rule": "Test Rule"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_fraction_rule_forbidden():
|
||||
response = client.put("/fraction_rule_octave/1", json={"rule": "Updated Rule"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -21,6 +21,13 @@ async def list_bultos(
|
||||
"""
|
||||
List all GBultos with pagination.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return db.query(Package).offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
@@ -33,6 +40,13 @@ async def read_bulto(
|
||||
"""
|
||||
Get a specific Package by its CODE.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
bulto = GBultoService.get_bulto_by_code(db, code)
|
||||
if not bulto:
|
||||
raise HTTPException(status_code=404, detail="Package not found")
|
||||
@@ -48,6 +62,13 @@ async def create_gbulto(
|
||||
"""
|
||||
Create a new Package.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return GBultoService.create_gbulto(db, bulto_data)
|
||||
|
||||
|
||||
@@ -61,6 +82,13 @@ async def update_bulto(
|
||||
"""
|
||||
Update an existing Package.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
bulto = GBultoService.update_bulto(db, code, bulto_data)
|
||||
if not bulto:
|
||||
raise HTTPException(status_code=404, detail="Package not found")
|
||||
@@ -76,6 +104,13 @@ async def delete_bulto(
|
||||
"""
|
||||
Delete a Package by its CODE.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
bulto = GBultoService.delete_bulto(db, code)
|
||||
if not bulto:
|
||||
raise HTTPException(status_code=404, detail="Package not found")
|
||||
31
backend/api/v1/modules/a76/package/test_package.py
Normal file
31
backend/api/v1/modules/a76/package/test_package.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_packages(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/bultos/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_package_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/bultos/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_package_forbidden():
|
||||
response = client.post("/bultos/", json={"name": "Test Package"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_package_forbidden():
|
||||
response = client.put("/bultos/1", json={"name": "Updated Package"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -29,6 +29,13 @@ async def create_part(
|
||||
"""
|
||||
Create a new part in the system
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
return service.create_part(part_data)
|
||||
|
||||
@@ -49,6 +56,13 @@ async def list_parts(
|
||||
"""
|
||||
List parts with optional filters and pagination
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
search_params = PartSearchDTO(
|
||||
client_id=client_id,
|
||||
@@ -72,6 +86,13 @@ async def get_parts_by_client(
|
||||
"""
|
||||
Get all parts for a specific client
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
return service.search_by_client(client_id, skip, limit)
|
||||
|
||||
@@ -85,6 +106,13 @@ async def search_by_fraction(
|
||||
"""
|
||||
Search parts by tariff fraction
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
return service.search_by_fraction(fraction)
|
||||
|
||||
@@ -98,6 +126,13 @@ async def search_by_supplier(
|
||||
"""
|
||||
Search parts by supplier
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
return service.search_by_supplier(supplier)
|
||||
|
||||
@@ -111,6 +146,13 @@ async def get_parts_by_country(
|
||||
"""
|
||||
Get parts by country of origin
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
return service.get_parts_by_country(country_code)
|
||||
|
||||
@@ -123,6 +165,13 @@ async def get_parts_statistics(
|
||||
"""
|
||||
Get basic parts statistics
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
return service.get_parts_statistics()
|
||||
|
||||
@@ -137,6 +186,13 @@ async def get_part(
|
||||
"""
|
||||
Get part by composite key (client_id + part_number)
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
part = service.get_part(client_id, part_number)
|
||||
if not part:
|
||||
@@ -158,6 +214,13 @@ async def update_part(
|
||||
"""
|
||||
Update part information
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
part = service.update_part(client_id, part_number, part_data)
|
||||
if not part:
|
||||
@@ -180,6 +243,13 @@ async def delete_part(
|
||||
|
||||
Note: This will completely remove the part from the system.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
if not service.delete_part(client_id, part_number):
|
||||
raise HTTPException(
|
||||
@@ -198,6 +268,13 @@ async def toggle_part_status(
|
||||
"""
|
||||
Toggle part enabled/disabled status
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
part = service.toggle_status(client_id, part_number)
|
||||
if not part:
|
||||
@@ -219,6 +296,13 @@ async def get_part_basic_info(
|
||||
"""
|
||||
Get basic information for a part
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
part = service.get_part(client_id, part_number)
|
||||
if not part:
|
||||
@@ -249,6 +333,13 @@ async def get_part_regulatory_info(
|
||||
"""
|
||||
Get regulatory information for a part (FDA, FCC, ECCN, etc.)
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
service = PartService(db)
|
||||
part = service.get_part(client_id, part_number)
|
||||
if not part:
|
||||
|
||||
31
backend/api/v1/modules/a76/parts/test_parts.py
Normal file
31
backend/api/v1/modules/a76/parts/test_parts.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_parts(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/parts/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_part_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/parts/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_part_forbidden():
|
||||
response = client.post("/parts/", json={"name": "Test Part"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_part_forbidden():
|
||||
response = client.put("/parts/1", json={"name": "Updated Part"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -18,6 +18,13 @@ async def list_permissions(
|
||||
"""
|
||||
List all PermissionRuleOct entries.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return db.query(PermissionRuleOctService).all()
|
||||
|
||||
|
||||
@@ -30,6 +37,13 @@ async def read_permission(
|
||||
"""
|
||||
Get a specific PermissionRuleOct by its permission.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
permission = PermissionRuleOctService.get_permission_by_id(db, permission)
|
||||
if not permission:
|
||||
raise HTTPException(status_code=404, detail="PermissionRuleOct not found")
|
||||
@@ -45,6 +59,13 @@ async def create_permission(
|
||||
"""
|
||||
Create a new PermissionRuleOct entry.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return PermissionRuleOctService.create_permission(db, permission_data)
|
||||
|
||||
|
||||
@@ -57,6 +78,13 @@ async def delete_permission(
|
||||
"""
|
||||
Delete a PermissionRuleOct by its permission.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
permission = PermissionRuleOctService.delete_permission(db, permission)
|
||||
if not permission:
|
||||
raise HTTPException(status_code=404, detail="PermissionRuleOct not found")
|
||||
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_permission_rules(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/permission_rule_oct/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_permission_rule_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/permission_rule_oct/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_permission_rule_forbidden():
|
||||
response = client.post("/permission_rule_oct/", json={"rule": "Test Rule"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_permission_rule_forbidden():
|
||||
response = client.put("/permission_rule_oct/1", json={"rule": "Updated Rule"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -22,6 +22,13 @@ async def list_seals(
|
||||
"""
|
||||
List all Seal entries.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return db.query(SealService).all()
|
||||
|
||||
|
||||
@@ -34,6 +41,13 @@ async def read_seal(
|
||||
"""
|
||||
Get a specific Seal by its seal.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
seal = SealService.get_seal_by_id(db, seal)
|
||||
if not seal:
|
||||
raise HTTPException(status_code=404, detail="Seal not found")
|
||||
@@ -49,6 +63,13 @@ async def create_seal(
|
||||
"""
|
||||
Create a new Seal entry.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
return SealService.create_seal(db, seal_data)
|
||||
|
||||
|
||||
@@ -61,6 +82,13 @@ async def delete_seal(
|
||||
"""
|
||||
Delete a Seal by its seal.
|
||||
"""
|
||||
# Validate access to the tenant and company
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
company_id = current_user.get("company_id")
|
||||
|
||||
if not tenant_id or not company_id:
|
||||
raise HTTPException(status_code=403, detail="Access denied: Tenant or Company not found")
|
||||
|
||||
seal = SealService.delete_seal(db, seal)
|
||||
if not seal:
|
||||
raise HTTPException(status_code=404, detail="Seal not found")
|
||||
31
backend/api/v1/modules/a76/seal/test_seal.py
Normal file
31
backend/api/v1/modules/a76/seal/test_seal.py
Normal file
@@ -0,0 +1,31 @@
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
from .routes import router
|
||||
from fastapi import FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
app.include_router(router)
|
||||
client = TestClient(app)
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_list_seals(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/seal/", headers=headers)
|
||||
assert response.status_code == 200
|
||||
assert "items" in response.json()
|
||||
assert "page" in response.json()
|
||||
assert "page_size" in response.json()
|
||||
|
||||
@pytest.mark.usefixtures("client", "access_token")
|
||||
def test_get_seal_not_found(client, access_token):
|
||||
headers = {"Authorization": f"Bearer {access_token}"}
|
||||
response = client.get("/seal/invalid_id", headers=headers)
|
||||
assert response.status_code == 404
|
||||
|
||||
def test_create_seal_forbidden():
|
||||
response = client.post("/seal/", json={"name": "Test Seal"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
|
||||
def test_update_seal_forbidden():
|
||||
response = client.put("/seal/1", json={"name": "Updated Seal"})
|
||||
assert response.status_code in (403, 405, 404)
|
||||
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
Modelo de relación entre usuarios (Keycloak) y tenants
|
||||
"""
|
||||
from sqlalchemy import Integer, String, DateTime, Boolean, UniqueConstraint, ForeignKeyConstraint
|
||||
from sqlalchemy import Integer, String, DateTime, Boolean, UniqueConstraint, ForeignKeyConstraint, ForeignKey
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from datetime import datetime
|
||||
@@ -34,6 +34,9 @@ class UserTenant(Base):
|
||||
# ID del tenant
|
||||
tenant_id: Mapped[int] = mapped_column(Integer, nullable=False, index=True)
|
||||
|
||||
# ID de la empresa asociada
|
||||
company_id: Mapped[int] = mapped_column(Integer, ForeignKey("a76.companies.id"), nullable=False, index=True)
|
||||
|
||||
# Estado de la relación
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
|
||||
|
||||
@@ -8,6 +8,9 @@ from jose import jwt, JWTError
|
||||
from typing import Optional, Dict, Any
|
||||
from .config import settings
|
||||
import logging
|
||||
from sqlalchemy.orm import Session
|
||||
from core.database import get_core_db
|
||||
from api.v1.modules.a76.user_tenant.models import UserTenant
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -75,16 +78,33 @@ def verify_token(token: str) -> Dict[str, Any]:
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
credentials: HTTPAuthorizationCredentials = Security(security)
|
||||
credentials: HTTPAuthorizationCredentials = Security(security),
|
||||
db: Session = Depends(get_core_db)
|
||||
) -> Dict[str, Any]:
|
||||
"""
|
||||
Dependency para obtener el usuario actual desde el token JWT
|
||||
Enriquecido con tenant_id y company_id desde la tabla user_tenant
|
||||
|
||||
Uso en FastAPI:
|
||||
current_user: dict = Depends(get_current_user)
|
||||
"""
|
||||
token = credentials.credentials
|
||||
user_info = verify_token(token)
|
||||
|
||||
# Obtener user_id desde el token
|
||||
user_id = user_info.get("sub")
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=401, detail="User ID not found in token")
|
||||
|
||||
# Consultar la tabla user_tenant para obtener tenant_id y company_id
|
||||
user_tenant = db.query(UserTenant).filter(UserTenant.keycloak_user_id == user_id).first()
|
||||
if not user_tenant:
|
||||
raise HTTPException(status_code=403, detail="User does not have access to any tenant or company")
|
||||
|
||||
# Enriquecer user_info con tenant_id y company_id
|
||||
user_info["tenant_id"] = user_tenant.tenant_id
|
||||
user_info["company_id"] = user_tenant.company_id
|
||||
|
||||
return user_info
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user