Merge pull request 'feature/sec' (#14) from feature/sec into development

Reviewed-on: ADUANASOFT/anexo76#14
This commit is contained in:
2025-11-11 20:04:35 +00:00
40 changed files with 1024 additions and 422 deletions

View File

@@ -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)

View 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)

View File

@@ -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:

View 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_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)

View 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)

View File

@@ -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")

View 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_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)

View File

@@ -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")

View 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_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)

View File

@@ -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")

View 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_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)

View File

@@ -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")

View 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)

View File

@@ -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:

View 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)

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoConfigAdditional CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_config_additional import PedimentoConfigAdditionalService
from ..dtos.pedimento_config_additional import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/config-additional")
@router.get("/", response_model=PedimentoConfigAdditionalResponse)
async def get_config_additional(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get config additional by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigAdditionalService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigAdditionalService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
raise HTTPException(status_code=404, detail="Config additional not found")
@@ -39,21 +37,17 @@ async def get_config_additional(
async def create_config_additional(
pedimento_id: int,
data: PedimentoConfigAdditionalCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create config additional"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id and tenant_id match
# Ensure pedimento_id and company_id match
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
if data.tenant_id != tenant_id:
raise HTTPException(status_code=403, detail="Tenant ID mismatch")
config = PedimentoConfigAdditionalService.create(db, data)
config = PedimentoConfigAdditionalService.create(db, data, tenant_id, company_id)
return config
@@ -61,15 +55,13 @@ async def create_config_additional(
async def update_config_additional(
pedimento_id: int,
data: PedimentoConfigAdditionalUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update config additional"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigAdditionalService.update(db, pedimento_id, tenant_id, data)
config = PedimentoConfigAdditionalService.update(db, pedimento_id, tenant_id, company_id, data)
if not config:
raise HTTPException(status_code=404, detail="Config additional not found")
@@ -79,15 +71,13 @@ async def update_config_additional(
@router.delete("/", status_code=204)
async def delete_config_additional(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete config additional"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoConfigAdditionalService.delete(db, pedimento_id, tenant_id)
success = PedimentoConfigAdditionalService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Config additional not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoConfigCalculations CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_config_calculations import PedimentoConfigCalculationsService
from ..dtos.pedimento_config_calculations import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/config-calculations")
@router.get("/", response_model=PedimentoConfigCalculationsResponse)
async def get_config_calculations(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get config calculations by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigCalculationsService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigCalculationsService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
raise HTTPException(status_code=404, detail="Config calculations not found")
@@ -39,19 +37,17 @@ async def get_config_calculations(
async def create_config_calculations(
pedimento_id: int,
data: PedimentoConfigCalculationsCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create config calculations"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
config = PedimentoConfigCalculationsService.create(db, data, tenant_id)
config = PedimentoConfigCalculationsService.create(db, data, tenant_id, company_id)
return config
@@ -59,15 +55,13 @@ async def create_config_calculations(
async def update_config_calculations(
pedimento_id: int,
data: PedimentoConfigCalculationsUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update config calculations"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigCalculationsService.update(db, pedimento_id, tenant_id, data)
config = PedimentoConfigCalculationsService.update(db, pedimento_id, tenant_id, company_id, data)
if not config:
raise HTTPException(status_code=404, detail="Config calculations not found")
@@ -77,15 +71,13 @@ async def update_config_calculations(
@router.delete("/", status_code=204)
async def delete_config_calculations(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete config calculations"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoConfigCalculationsService.delete(db, pedimento_id, tenant_id)
success = PedimentoConfigCalculationsService.delete(db, pedimento_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Config calculations not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoConfigParameters CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_config_parameters import PedimentoConfigParametersService
from ..dtos.pedimento_config_parameters import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/config-parameters")
@router.get("/", response_model=PedimentoConfigParametersResponse)
async def get_config_parameters(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get config parameters by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigParametersService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigParametersService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
raise HTTPException(status_code=404, detail="Config parameters not found")
@@ -39,19 +37,17 @@ async def get_config_parameters(
async def create_config_parameters(
pedimento_id: int,
data: PedimentoConfigParametersCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create config parameters"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
config = PedimentoConfigParametersService.create(db, data, tenant_id)
config = PedimentoConfigParametersService.create(db, data, tenant_id, company_id)
return config
@@ -59,15 +55,13 @@ async def create_config_parameters(
async def update_config_parameters(
pedimento_id: int,
data: PedimentoConfigParametersUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update config parameters"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigParametersService.update(db, pedimento_id, tenant_id, data)
config = PedimentoConfigParametersService.update(db, pedimento_id, tenant_id, company_id, data)
if not config:
raise HTTPException(status_code=404, detail="Config parameters not found")
@@ -77,15 +71,13 @@ async def update_config_parameters(
@router.delete("/", status_code=204)
async def delete_config_parameters(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete config parameters"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoConfigParametersService.delete(db, pedimento_id, tenant_id)
success = PedimentoConfigParametersService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Config parameters not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoConfigSurcharges CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_config_surcharges import PedimentoConfigSurchargesService
from ..dtos.pedimento_config_surcharges import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/config-surcharges")
@router.get("/", response_model=PedimentoConfigSurchargesResponse)
async def get_config_surcharges(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get config surcharges by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigSurchargesService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigSurchargesService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
raise HTTPException(status_code=404, detail="Config surcharges not found")
@@ -39,19 +37,17 @@ async def get_config_surcharges(
async def create_config_surcharges(
pedimento_id: int,
data: PedimentoConfigSurchargesCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create config surcharges"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
config = PedimentoConfigSurchargesService.create(db, data, tenant_id)
config = PedimentoConfigSurchargesService.create(db, data, tenant_id, company_id)
return config
@@ -59,15 +55,13 @@ async def create_config_surcharges(
async def update_config_surcharges(
pedimento_id: int,
data: PedimentoConfigSurchargesUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update config surcharges"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigSurchargesService.update(db, pedimento_id, tenant_id, data)
config = PedimentoConfigSurchargesService.update(db, pedimento_id, tenant_id, company_id, data)
if not config:
raise HTTPException(status_code=404, detail="Config surcharges not found")
@@ -77,15 +71,13 @@ async def update_config_surcharges(
@router.delete("/", status_code=204)
async def delete_config_surcharges(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete config surcharges"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoConfigSurchargesService.delete(db, pedimento_id, tenant_id)
success = PedimentoConfigSurchargesService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Config surcharges not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoConfigUpdateRectification CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_config_update_rectification import PedimentoConfigUpdateRectificationService
from ..dtos.pedimento_config_update_rectification import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/config-update-rectification")
@router.get("/", response_model=PedimentoConfigUpdateRectificationResponse)
async def get_config_update_rectification(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get config update rectification by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigUpdateRectificationService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigUpdateRectificationService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
raise HTTPException(status_code=404, detail="Config update rectification not found")
@@ -39,19 +37,17 @@ async def get_config_update_rectification(
async def create_config_update_rectification(
pedimento_id: int,
data: PedimentoConfigUpdateRectificationCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create config update rectification"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
config = PedimentoConfigUpdateRectificationService.create(db, data, tenant_id)
config = PedimentoConfigUpdateRectificationService.create(db, data, tenant_id, company_id)
return config
@@ -59,15 +55,13 @@ async def create_config_update_rectification(
async def update_config_update_rectification(
pedimento_id: int,
data: PedimentoConfigUpdateRectificationUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update config update rectification"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigUpdateRectificationService.update(db, pedimento_id, tenant_id, data)
config = PedimentoConfigUpdateRectificationService.update(db, pedimento_id, tenant_id, company_id, data)
if not config:
raise HTTPException(status_code=404, detail="Config update rectification not found")
@@ -77,15 +71,13 @@ async def update_config_update_rectification(
@router.delete("/", status_code=204)
async def delete_config_update_rectification(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete config update rectification"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoConfigUpdateRectificationService.delete(db, pedimento_id, tenant_id)
success = PedimentoConfigUpdateRectificationService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Config update rectification not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoConfigUpdates CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_config_updates import PedimentoConfigUpdatesService
from ..dtos.pedimento_config_updates import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/config-updates")
@router.get("/", response_model=PedimentoConfigUpdatesResponse)
async def get_config_updates(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get config updates by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigUpdatesService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigUpdatesService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
raise HTTPException(status_code=404, detail="Config updates not found")
@@ -39,19 +37,17 @@ async def get_config_updates(
async def create_config_updates(
pedimento_id: int,
data: PedimentoConfigUpdatesCreate,
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db)
):
"""Create config updates"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
config = PedimentoConfigUpdatesService.create(db, data, tenant_id)
config = PedimentoConfigUpdatesService.create(db, data, tenant_id, company_id)
return config
@@ -59,15 +55,13 @@ async def create_config_updates(
async def update_config_updates(
pedimento_id: int,
data: PedimentoConfigUpdatesUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update config updates"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
config = PedimentoConfigUpdatesService.update(db, pedimento_id, tenant_id, data)
config = PedimentoConfigUpdatesService.update(db, pedimento_id, tenant_id, company_id, data)
if not config:
raise HTTPException(status_code=404, detail="Config updates not found")
@@ -77,15 +71,13 @@ async def update_config_updates(
@router.delete("/", status_code=204)
async def delete_config_updates(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete config updates"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoConfigUpdatesService.delete(db, pedimento_id, tenant_id)
success = PedimentoConfigUpdatesService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Config updates not found")

View File

@@ -1,12 +1,11 @@
"""
Routes for PedimentoCustomsOffices CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_customs_offices import PedimentoCustomsOfficesService
from ..dtos.pedimento_customs_offices import (
PedimentoCustomsOfficesCreate,
@@ -21,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/customs-offices")
@router.get("/", response_model=List[PedimentoCustomsOfficesResponse])
async def list_customs_offices(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get all customs offices for a pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
offices = PedimentoCustomsOfficesService.get_by_pedimento_id(db, pedimento_id, tenant_id)
offices = PedimentoCustomsOfficesService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
return offices
@@ -37,15 +34,13 @@ async def list_customs_offices(
async def get_customs_office(
pedimento_id: int,
office_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get a specific customs office by ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
office = PedimentoCustomsOfficesService.get_by_id(db, office_id, pedimento_id, tenant_id)
office = PedimentoCustomsOfficesService.get_by_id(db, office_id, pedimento_id, tenant_id, company_id)
if not office:
raise HTTPException(status_code=404, detail="Customs office not found")
@@ -56,19 +51,17 @@ async def get_customs_office(
async def create_customs_office(
pedimento_id: int,
data: PedimentoCustomsOfficesCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new customs office"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
office = PedimentoCustomsOfficesService.create(db, data, tenant_id)
office = PedimentoCustomsOfficesService.create(db, data, tenant_id, company_id)
return office
@@ -77,15 +70,13 @@ async def update_customs_office(
pedimento_id: int,
office_id: int,
data: PedimentoCustomsOfficesUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update a customs office"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
office = PedimentoCustomsOfficesService.update(db, office_id, pedimento_id, tenant_id, data)
office = PedimentoCustomsOfficesService.update(db, office_id, pedimento_id, tenant_id, company_id, data)
if not office:
raise HTTPException(status_code=404, detail="Customs office not found")
@@ -96,15 +87,13 @@ async def update_customs_office(
async def delete_customs_office(
pedimento_id: int,
office_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete a customs office"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoCustomsOfficesService.delete(db, office_id, pedimento_id, tenant_id)
success = PedimentoCustomsOfficesService.delete(db, office_id, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Customs office not found")

View File

@@ -2,10 +2,10 @@
Routes for PedimentoDates CRUD operations
"""
import logging
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_dates import PedimentoDatesService
from ..dtos.pedimento_dates import (
@@ -21,15 +21,13 @@ logger = logging.getLogger(__name__)
@router.get("/", response_model=PedimentoDatesResponse)
async def get_dates(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get dates by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
dates = PedimentoDatesService.get_by_pedimento_id(db, pedimento_id, tenant_id)
dates = PedimentoDatesService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not dates:
raise HTTPException(status_code=404, detail="Pedimento dates not found")
@@ -39,15 +37,13 @@ async def get_dates(
@router.post("/", response_model=PedimentoDatesResponse, status_code=201)
async def create_dates(
data: PedimentoDatesCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create pedimento dates"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
dates = PedimentoDatesService.create(db, data, tenant_id)
dates = PedimentoDatesService.create(db, data, tenant_id, company_id)
return dates
@@ -55,15 +51,13 @@ async def create_dates(
async def update_dates(
pedimento_id: int,
data: PedimentoDatesUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update pedimento dates"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
dates = PedimentoDatesService.update(db, pedimento_id, tenant_id, data)
dates = PedimentoDatesService.update(db, pedimento_id, tenant_id, company_id, data)
if not dates:
raise HTTPException(status_code=404, detail="Pedimento dates not found")
@@ -73,15 +67,13 @@ async def update_dates(
@router.delete("/", status_code=204)
async def delete_dates(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete pedimento dates"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoDatesService.delete(db, pedimento_id, tenant_id)
success = PedimentoDatesService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Pedimento dates not found")

View File

@@ -1,11 +1,11 @@
"""
Routes for PedimentoDecrementables CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_decrementables import PedimentoDecrementablesService
from ..dtos.pedimento_decrementables import (
@@ -21,15 +21,13 @@ router = APIRouter(prefix="/{pedimento_id}/decrementables")
@router.get("/", response_model=List[PedimentoDecrementablesResponse])
async def list_decrementables(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get all decrementables for a pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
decrementables = PedimentoDecrementablesService.get_by_pedimento_id(db, pedimento_id, tenant_id)
decrementables = PedimentoDecrementablesService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
return decrementables
@@ -37,15 +35,13 @@ async def list_decrementables(
async def get_decrementable(
pedimento_id: int,
decrementable_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get a specific decrementable by ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
decrementable = PedimentoDecrementablesService.get_by_id(db, decrementable_id, pedimento_id, tenant_id)
decrementable = PedimentoDecrementablesService.get_by_id(db, decrementable_id, pedimento_id, tenant_id, company_id)
if not decrementable:
raise HTTPException(status_code=404, detail="Decrementable not found")
@@ -56,19 +52,17 @@ async def get_decrementable(
async def create_decrementable(
pedimento_id: int,
data: PedimentoDecrementablesCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new decrementable"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
decrementable = PedimentoDecrementablesService.create(db, data, tenant_id)
decrementable = PedimentoDecrementablesService.create(db, data, tenant_id, company_id)
return decrementable
@@ -77,15 +71,13 @@ async def update_decrementable(
pedimento_id: int,
decrementable_id: int,
data: PedimentoDecrementablesUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update a decrementable"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
decrementable = PedimentoDecrementablesService.update(db, decrementable_id, pedimento_id, tenant_id, data)
decrementable = PedimentoDecrementablesService.update(db, decrementable_id, pedimento_id, tenant_id, company_id, data)
if not decrementable:
raise HTTPException(status_code=404, detail="Decrementable not found")
@@ -96,15 +88,13 @@ async def update_decrementable(
async def delete_decrementable(
pedimento_id: int,
decrementable_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete a decrementable"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoDecrementablesService.delete(db, decrementable_id, pedimento_id, tenant_id)
success = PedimentoDecrementablesService.delete(db, decrementable_id, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Decrementable not found")

View File

@@ -1,11 +1,11 @@
"""
Routes for PedimentoIncrementables CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_incrementables import PedimentoIncrementablesService
from ..dtos.pedimento_incrementables import (
@@ -21,15 +21,13 @@ router = APIRouter(prefix="/{pedimento_id}/incrementables")
@router.get("/", response_model=List[PedimentoIncrementablesResponse])
async def list_incrementables(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get all incrementables for a pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
incrementables = PedimentoIncrementablesService.get_by_pedimento_id(db, pedimento_id, tenant_id)
incrementables = PedimentoIncrementablesService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
return incrementables
@@ -37,15 +35,13 @@ async def list_incrementables(
async def get_incrementable(
pedimento_id: int,
incrementable_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get a specific incrementable by ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
incrementable = PedimentoIncrementablesService.get_by_id(db, incrementable_id, pedimento_id, tenant_id)
incrementable = PedimentoIncrementablesService.get_by_id(db, incrementable_id, pedimento_id, tenant_id, company_id)
if not incrementable:
raise HTTPException(status_code=404, detail="Incrementable not found")
@@ -56,19 +52,17 @@ async def get_incrementable(
async def create_incrementable(
pedimento_id: int,
data: PedimentoIncrementablesCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new incrementable"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
incrementable = PedimentoIncrementablesService.create(db, data, tenant_id)
incrementable = PedimentoIncrementablesService.create(db, data, tenant_id, company_id)
return incrementable
@@ -77,15 +71,13 @@ async def update_incrementable(
pedimento_id: int,
incrementable_id: int,
data: PedimentoIncrementablesUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update an incrementable"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
incrementable = PedimentoIncrementablesService.update(db, incrementable_id, pedimento_id, tenant_id, data)
incrementable = PedimentoIncrementablesService.update(db, incrementable_id, pedimento_id, tenant_id, company_id, data)
if not incrementable:
raise HTTPException(status_code=404, detail="Incrementable not found")
@@ -96,15 +88,13 @@ async def update_incrementable(
async def delete_incrementable(
pedimento_id: int,
incrementable_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete an incrementable"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoIncrementablesService.delete(db, incrementable_id, pedimento_id, tenant_id)
success = PedimentoIncrementablesService.delete(db, incrementable_id, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Incrementable not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoIndexes CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_indexes import PedimentoIndexesService
from ..dtos.pedimento_indexes import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/indexes")
@router.get("/", response_model=PedimentoIndexesResponse)
async def get_indexes(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get indexes by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
indexes = PedimentoIndexesService.get_by_pedimento_id(db, pedimento_id, tenant_id)
indexes = PedimentoIndexesService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not indexes:
raise HTTPException(status_code=404, detail="Pedimento indexes not found")
@@ -39,19 +37,17 @@ async def get_indexes(
async def create_indexes(
pedimento_id: int,
data: PedimentoIndexesCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create pedimento indexes"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
indexes = PedimentoIndexesService.create(db, data, tenant_id)
indexes = PedimentoIndexesService.create(db, data, tenant_id, company_id)
return indexes
@@ -59,15 +55,13 @@ async def create_indexes(
async def update_indexes(
pedimento_id: int,
data: PedimentoIndexesUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update pedimento indexes"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
indexes = PedimentoIndexesService.update(db, pedimento_id, tenant_id, data)
indexes = PedimentoIndexesService.update(db, pedimento_id, tenant_id, company_id, data)
if not indexes:
raise HTTPException(status_code=404, detail="Pedimento indexes not found")
@@ -77,15 +71,13 @@ async def update_indexes(
@router.delete("/", status_code=204)
async def delete_indexes(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete pedimento indexes"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoIndexesService.delete(db, pedimento_id, tenant_id)
success = PedimentoIndexesService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Pedimento indexes not found")

View File

@@ -1,11 +1,11 @@
"""
Routes for PedimentoPayments CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_payments import PedimentoPaymentsService
from ..dtos.pedimento_payments import (
@@ -21,15 +21,13 @@ router = APIRouter(prefix="/{pedimento_id}/payments")
@router.get("/", response_model=List[PedimentoPaymentsResponse])
async def list_payments(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get all payments for a pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
payments = PedimentoPaymentsService.get_by_pedimento_id(db, pedimento_id, tenant_id)
payments = PedimentoPaymentsService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
return payments
@@ -37,15 +35,13 @@ async def list_payments(
async def get_payment(
pedimento_id: int,
payment_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get a specific payment by ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
payment = PedimentoPaymentsService.get_by_id(db, payment_id, pedimento_id, tenant_id)
payment = PedimentoPaymentsService.get_by_id(db, payment_id, pedimento_id, tenant_id, company_id)
if not payment:
raise HTTPException(status_code=404, detail="Payment not found")
@@ -56,19 +52,17 @@ async def get_payment(
async def create_payment(
pedimento_id: int,
data: PedimentoPaymentsCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new payment"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
payment = PedimentoPaymentsService.create(db, data, tenant_id)
payment = PedimentoPaymentsService.create(db, data, tenant_id, company_id)
return payment
@@ -77,15 +71,13 @@ async def update_payment(
pedimento_id: int,
payment_id: int,
data: PedimentoPaymentsUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update a payment"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
payment = PedimentoPaymentsService.update(db, payment_id, pedimento_id, tenant_id, data)
payment = PedimentoPaymentsService.update(db, payment_id, pedimento_id, tenant_id, company_id, data)
if not payment:
raise HTTPException(status_code=404, detail="Payment not found")
@@ -96,15 +88,13 @@ async def update_payment(
async def delete_payment(
pedimento_id: int,
payment_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete a payment"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoPaymentsService.delete(db, payment_id, pedimento_id, tenant_id)
success = PedimentoPaymentsService.delete(db, payment_id, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Payment not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoRectificationDestination CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_rectification_destination import PedimentoRectificationDestinationService
from ..dtos.pedimento_rectification_destination import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/rectification-destination")
@router.get("/", response_model=PedimentoRectificationDestinationResponse)
async def get_rectification_destination(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get rectification destination by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
rectification = PedimentoRectificationDestinationService.get_by_pedimento_id(db, pedimento_id, tenant_id)
rectification = PedimentoRectificationDestinationService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not rectification:
raise HTTPException(status_code=404, detail="Rectification destination not found")
@@ -39,19 +37,17 @@ async def get_rectification_destination(
async def create_rectification_destination(
pedimento_id: int,
data: PedimentoRectificationDestinationCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create rectification destination"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
rectification = PedimentoRectificationDestinationService.create(db, data, tenant_id)
rectification = PedimentoRectificationDestinationService.create(db, data, tenant_id, company_id)
return rectification
@@ -59,15 +55,13 @@ async def create_rectification_destination(
async def update_rectification_destination(
pedimento_id: int,
data: PedimentoRectificationDestinationUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update rectification destination"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
rectification = PedimentoRectificationDestinationService.update(db, pedimento_id, tenant_id, data)
rectification = PedimentoRectificationDestinationService.update(db, pedimento_id, tenant_id, company_id, data)
if not rectification:
raise HTTPException(status_code=404, detail="Rectification destination not found")
@@ -77,15 +71,13 @@ async def update_rectification_destination(
@router.delete("/", status_code=204)
async def delete_rectification_destination(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete rectification destination"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoRectificationDestinationService.delete(db, pedimento_id, tenant_id)
success = PedimentoRectificationDestinationService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Rectification destination not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoRectificationOrigin CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_rectification_origin import PedimentoRectificationOriginService
from ..dtos.pedimento_rectification_origin import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/rectification-origin")
@router.get("/", response_model=PedimentoRectificationOriginResponse)
async def get_rectification_origin(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get rectification origin by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
rectification = PedimentoRectificationOriginService.get_by_pedimento_id(db, pedimento_id, tenant_id)
rectification = PedimentoRectificationOriginService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not rectification:
raise HTTPException(status_code=404, detail="Rectification origin not found")
@@ -39,19 +37,17 @@ async def get_rectification_origin(
async def create_rectification_origin(
pedimento_id: int,
data: PedimentoRectificationOriginCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create rectification origin"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
rectification = PedimentoRectificationOriginService.create(db, data, tenant_id)
rectification = PedimentoRectificationOriginService.create(db, data, tenant_id, company_id)
return rectification
@@ -59,15 +55,13 @@ async def create_rectification_origin(
async def update_rectification_origin(
pedimento_id: int,
data: PedimentoRectificationOriginUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update rectification origin"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
rectification = PedimentoRectificationOriginService.update(db, pedimento_id, tenant_id, data)
rectification = PedimentoRectificationOriginService.update(db, pedimento_id, tenant_id, company_id, data)
if not rectification:
raise HTTPException(status_code=404, detail="Rectification origin not found")
@@ -77,15 +71,13 @@ async def update_rectification_origin(
@router.delete("/", status_code=204)
async def delete_rectification_origin(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete rectification origin"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoRectificationOriginService.delete(db, pedimento_id, tenant_id)
success = PedimentoRectificationOriginService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Rectification origin not found")

View File

@@ -1,11 +1,11 @@
"""
Routes for PedimentoTransportMeans CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import List
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_transport_means import PedimentoTransportMeansService
from ..dtos.pedimento_transport_means import (
@@ -21,15 +21,13 @@ router = APIRouter(prefix="/{pedimento_id}/transport-means")
@router.get("/", response_model=List[PedimentoTransportMeansResponse])
async def list_transport_means(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get all transport means for a pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
transport_means = PedimentoTransportMeansService.get_by_pedimento_id(db, pedimento_id, tenant_id)
transport_means = PedimentoTransportMeansService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
return transport_means
@@ -37,15 +35,13 @@ async def list_transport_means(
async def get_transport_mean(
pedimento_id: int,
transport_mean_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get a specific transport mean by ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
transport_mean = PedimentoTransportMeansService.get_by_id(db, transport_mean_id, pedimento_id, tenant_id)
transport_mean = PedimentoTransportMeansService.get_by_id(db, transport_mean_id, pedimento_id, tenant_id, company_id)
if not transport_mean:
raise HTTPException(status_code=404, detail="Transport mean not found")
@@ -56,19 +52,17 @@ async def get_transport_mean(
async def create_transport_mean(
pedimento_id: int,
data: PedimentoTransportMeansCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new transport mean"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
transport_mean = PedimentoTransportMeansService.create(db, data, tenant_id)
transport_mean = PedimentoTransportMeansService.create(db, data, tenant_id, company_id)
return transport_mean
@@ -77,15 +71,13 @@ async def update_transport_mean(
pedimento_id: int,
transport_mean_id: int,
data: PedimentoTransportMeansUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update a transport mean"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
transport_mean = PedimentoTransportMeansService.update(db, transport_mean_id, pedimento_id, tenant_id, data)
transport_mean = PedimentoTransportMeansService.update(db, transport_mean_id, pedimento_id, tenant_id, company_id, data)
if not transport_mean:
raise HTTPException(status_code=404, detail="Transport mean not found")
@@ -96,15 +88,13 @@ async def update_transport_mean(
async def delete_transport_mean(
pedimento_id: int,
transport_mean_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete a transport mean"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoTransportMeansService.delete(db, transport_mean_id, pedimento_id, tenant_id)
success = PedimentoTransportMeansService.delete(db, transport_mean_id, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Transport mean not found")

View File

@@ -1,10 +1,10 @@
"""
Routes for PedimentoValidation CRUD operations
"""
from fastapi import APIRouter, Depends, HTTPException
from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimento_validation import PedimentoValidationService
from ..dtos.pedimento_validation import (
@@ -20,15 +20,13 @@ router = APIRouter(prefix="/{pedimento_id}/validation")
@router.get("/", response_model=PedimentoValidationResponse)
async def get_validation(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get validation by pedimento ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
validation = PedimentoValidationService.get_by_pedimento_id(db, pedimento_id, tenant_id)
validation = PedimentoValidationService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not validation:
raise HTTPException(status_code=404, detail="Pedimento validation not found")
@@ -39,19 +37,17 @@ async def get_validation(
async def create_validation(
pedimento_id: int,
data: PedimentoValidationCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create pedimento validation"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
# Ensure pedimento_id matches
if data.pedimento_id != pedimento_id:
raise HTTPException(status_code=400, detail="Pedimento ID mismatch")
validation = PedimentoValidationService.create(db, data, tenant_id)
validation = PedimentoValidationService.create(db, data, tenant_id, company_id)
return validation
@@ -59,15 +55,13 @@ async def create_validation(
async def update_validation(
pedimento_id: int,
data: PedimentoValidationUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update pedimento validation"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
validation = PedimentoValidationService.update(db, pedimento_id, tenant_id, data)
validation = PedimentoValidationService.update(db, pedimento_id, tenant_id, company_id, data)
if not validation:
raise HTTPException(status_code=404, detail="Pedimento validation not found")
@@ -77,15 +71,13 @@ async def update_validation(
@router.delete("/", status_code=204)
async def delete_validation(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete pedimento validation"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentoValidationService.delete(db, pedimento_id, tenant_id)
success = PedimentoValidationService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Pedimento validation not found")

View File

@@ -5,7 +5,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from typing import Dict, Any, Optional
from core.database import get_core_db
from core.security import get_current_user, get_tenant_from_token
from core.security import validate_access_to_resource
from ..services.pedimentos import PedimentosService
from ..dtos.pedimentos import PedimentosCreate, PedimentosUpdate, PedimentosResponse
@@ -16,18 +16,16 @@ router = APIRouter()
@router.get("/", response_model=Dict[str, Any])
async def list_pedimentos(
company_id: int = Query(..., description="Company ID"),
page: int = Query(1, ge=1, description="Page number"),
page_size: int = Query(50, ge=1, le=100, description="Page size"),
status: Optional[str] = Query(None, description="Filter by status"),
client_id: Optional[int] = Query(None, description="Filter by client ID"),
year: Optional[str] = Query(None, description="Filter by year"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get all pedimentos with pagination and filters"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
filters = {}
if status:
@@ -38,7 +36,7 @@ async def list_pedimentos(
filters["year"] = year
skip = (page - 1) * page_size
items, total = PedimentosService.get_all(db, tenant_id, skip, page_size, filters)
items, total = PedimentosService.get_all(db, tenant_id, company_id, skip, page_size, filters)
return {
"items": [PedimentosResponse.model_validate(item) for item in items],
@@ -51,15 +49,13 @@ async def list_pedimentos(
@router.get("/{pedimento_id}", response_model=PedimentosResponse)
async def get_pedimento(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Get a pedimento by ID"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
pedimento = PedimentosService.get_by_id(db, pedimento_id, tenant_id)
pedimento = PedimentosService.get_by_id(db, pedimento_id, tenant_id, company_id)
if not pedimento:
raise HTTPException(status_code=404, detail="Pedimento not found")
@@ -69,15 +65,13 @@ async def get_pedimento(
@router.post("/", response_model=PedimentosResponse, status_code=201)
async def create_pedimento(
data: PedimentosCreate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Create a new pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
pedimento = PedimentosService.create(db, data, tenant_id)
pedimento = PedimentosService.create(db, data, tenant_id, company_id)
return pedimento
@@ -85,15 +79,13 @@ async def create_pedimento(
async def update_pedimento(
pedimento_id: int,
data: PedimentosUpdate,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Update a pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
pedimento = PedimentosService.update(db, pedimento_id, tenant_id, data)
pedimento = PedimentosService.update(db, pedimento_id, tenant_id, company_id, data)
if not pedimento:
raise HTTPException(status_code=404, detail="Pedimento not found")
@@ -103,15 +95,13 @@ async def update_pedimento(
@router.delete("/{pedimento_id}", status_code=204)
async def delete_pedimento(
pedimento_id: int,
company_id: int = Query(..., description="Company ID"),
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user)
):
"""Delete a pedimento"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
tenant_id = validate_access_to_resource(company_id)
success = PedimentosService.delete(db, pedimento_id, tenant_id)
success = PedimentosService.delete(db, pedimento_id, tenant_id, company_id)
if not success:
raise HTTPException(status_code=404, detail="Pedimento not found")

View File

@@ -12,17 +12,21 @@ class PedimentoConfigAdditionalService:
"""Service class for PedimentoConfigAdditional business logic"""
@staticmethod
def get_by_pedimento_id(db: Session, pedimento_id: int, tenant_id: int) -> Optional[PedimentoConfigAdditional]:
def get_by_pedimento_id(db: Session, pedimento_id: int, tenant_id: int, company_id: int) -> Optional[PedimentoConfigAdditional]:
"""Get config by pedimento ID"""
return db.query(PedimentoConfigAdditional).filter(
PedimentoConfigAdditional.pedimento_id == pedimento_id,
PedimentoConfigAdditional.tenant_id == tenant_id
PedimentoConfigAdditional.tenant_id == tenant_id,
PedimentoConfigAdditional.company_id == company_id
).first()
@staticmethod
def create(db: Session, config_data: PedimentoConfigAdditionalCreate) -> PedimentoConfigAdditional:
def create(db: Session, config_data: PedimentoConfigAdditionalCreate, tenant_id: int, company_id: int) -> PedimentoConfigAdditional:
"""Create a new config"""
config = PedimentoConfigAdditional(**config_data.model_dump())
config.tenant_id = tenant_id
config.company_id = company_id
db.add(config)
db.commit()
db.refresh(config)
@@ -33,10 +37,11 @@ class PedimentoConfigAdditionalService:
db: Session,
pedimento_id: int,
tenant_id: int,
company_id: int,
config_data: PedimentoConfigAdditionalUpdate
) -> Optional[PedimentoConfigAdditional]:
"""Update config"""
config = PedimentoConfigAdditionalService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigAdditionalService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
return None
@@ -49,9 +54,9 @@ class PedimentoConfigAdditionalService:
return config
@staticmethod
def delete(db: Session, pedimento_id: int, tenant_id: int) -> bool:
def delete(db: Session, pedimento_id: int, tenant_id: int, company_id: int) -> bool:
"""Delete config"""
config = PedimentoConfigAdditionalService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigAdditionalService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
return False

View File

@@ -12,17 +12,21 @@ class PedimentoConfigCalculationsService:
"""Service class for PedimentoConfigCalculations business logic"""
@staticmethod
def get_by_pedimento_id(db: Session, pedimento_id: int, tenant_id: int) -> Optional[PedimentoConfigCalculations]:
def get_by_pedimento_id(db: Session, pedimento_id: int, tenant_id: int, company_id: int) -> Optional[PedimentoConfigCalculations]:
"""Get config by pedimento ID"""
return db.query(PedimentoConfigCalculations).filter(
PedimentoConfigCalculations.pedimento_id == pedimento_id,
PedimentoConfigCalculations.tenant_id == tenant_id
PedimentoConfigCalculations.tenant_id == tenant_id,
PedimentoConfigCalculations.company_id == company_id
).first()
@staticmethod
def create(db: Session, config_data: PedimentoConfigCalculationsCreate) -> PedimentoConfigCalculations:
def create(db: Session, config_data: PedimentoConfigCalculationsCreate, tenant_id: int, company_id: int) -> PedimentoConfigCalculations:
"""Create a new config"""
config = PedimentoConfigCalculations(**config_data.model_dump())
config.tenant_id = tenant_id
config.company_id = company_id
db.add(config)
db.commit()
db.refresh(config)
@@ -33,10 +37,11 @@ class PedimentoConfigCalculationsService:
db: Session,
pedimento_id: int,
tenant_id: int,
company_id: int,
config_data: PedimentoConfigCalculationsUpdate
) -> Optional[PedimentoConfigCalculations]:
"""Update config"""
config = PedimentoConfigCalculationsService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigCalculationsService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
return None
@@ -49,9 +54,9 @@ class PedimentoConfigCalculationsService:
return config
@staticmethod
def delete(db: Session, pedimento_id: int, tenant_id: int) -> bool:
def delete(db: Session, pedimento_id: int, tenant_id: int, company_id: int) -> bool:
"""Delete config"""
config = PedimentoConfigCalculationsService.get_by_pedimento_id(db, pedimento_id, tenant_id)
config = PedimentoConfigCalculationsService.get_by_pedimento_id(db, pedimento_id, tenant_id, company_id)
if not config:
return False

View File

@@ -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")

View 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_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)

View File

@@ -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")

View 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)

View File

@@ -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)

View File

@@ -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
@@ -146,6 +166,62 @@ def get_tenant_from_token(user_info: Dict[str, Any]) -> Optional[int]:
return None
def validate_company_access(
company_id: int,
current_user: Dict[str, Any]
) -> bool:
"""
Valida que el usuario tenga acceso a la compañía solicitada
Args:
company_id: ID de la compañía a la que se quiere acceder
current_user: Información del usuario actual desde el token
Returns:
True si el usuario tiene acceso, False en caso contrario
Nota:
Por ahora solo verifica que el tenant_id del usuario coincida con el company_id.
Se puede extender para validar permisos específicos por compañía.
"""
tenant_id = get_tenant_from_token(current_user)
# Si no hay tenant_id en el token, denegar acceso
if not tenant_id:
return False
# Validar que el company_id pertenezca al tenant del usuario
# Por ahora asumimos que company_id == tenant_id
# Esto se puede modificar si hay una tabla de relación tenant-company
return tenant_id == company_id
def validate_access_to_resource(
company_id: int,
current_user: dict = Depends(get_current_user)
) -> bool:
"""
Valida que el usuario tenga acceso a un recurso específico basado en company_id
y regresa el tenant_id
Args:
company_id: company_id asociado al recurso
current_user: Información del usuario actual desde el token
Returns:
True si el usuario tiene acceso, False en caso contrario
"""
tenant_id = get_tenant_from_token(current_user)
if not tenant_id:
raise HTTPException(status_code=400, detail="Tenant ID not found in token")
if not validate_company_access(company_id, current_user):
raise HTTPException(status_code=403, detail="Access denied to this company")
# Validar que el tenant_id del usuario coincida con el del recurso
return tenant_id
class KeycloakClient:
"""Cliente para interactuar con Keycloak Admin API"""