diff --git a/backend/api/v1/modules/a76/doc_types_dig/routes.py b/backend/api/v1/modules/a76/doc_types_dig/routes.py
index ca229671..5dc43488 100644
--- a/backend/api/v1/modules/a76/doc_types_dig/routes.py
+++ b/backend/api/v1/modules/a76/doc_types_dig/routes.py
@@ -10,6 +10,7 @@ from .dto import DocumentTypeDigitizationResponse
from .service import DocumentTypeDigitizationService
router = APIRouter(prefix='/document-types-digitization', tags=['Document Types Digitization'])
+VIEW_PERMISSION = ['ref_invoice_types.view']
@router.get('/', response_model=Dict[str, Any])
@@ -24,7 +25,7 @@ async def list_document_types(
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
- tenant_id = validate_access_to_resource(db, company_id, current_user)
+ tenant_id = validate_access_to_resource(db, company_id, current_user, VIEW_PERMISSION)
skip = (page - 1) * page_size
filters: Dict[str, Any] = {}
@@ -59,7 +60,7 @@ async def get_document_type(
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
- tenant_id = validate_access_to_resource(db, company_id, current_user)
+ tenant_id = validate_access_to_resource(db, company_id, current_user, VIEW_PERMISSION)
document_type = DocumentTypeDigitizationService.get_by_id(
db,
document_type_id,
@@ -83,7 +84,7 @@ async def get_document_type_by_code(
db: Session = Depends(get_core_db),
current_user: dict = Depends(get_current_user),
):
- tenant_id = validate_access_to_resource(db, company_id, current_user)
+ tenant_id = validate_access_to_resource(db, company_id, current_user, VIEW_PERMISSION)
document_type = DocumentTypeDigitizationService.get_by_code(
db,
code,
diff --git a/backend/api/v1/modules/a76/general_catalogs/sectors/routes.py b/backend/api/v1/modules/a76/general_catalogs/sectors/routes.py
index 60398a89..331a7455 100644
--- a/backend/api/v1/modules/a76/general_catalogs/sectors/routes.py
+++ b/backend/api/v1/modules/a76/general_catalogs/sectors/routes.py
@@ -20,8 +20,8 @@ router = TenantCRUDRoutes(
enable_filters=True,
default_page_size=50,
max_page_size=100,
- list_permissions=["cat_sectors.view"],
- get_permissions=["cat_sectors.view"],
+ list_permissions=["ref_sectors.view"],
+ get_permissions=["ref_sectors.view"],
create_permissions=["cat_sectors.create"],
update_permissions=["cat_sectors.edit"],
delete_permissions=["cat_sectors.delete"],
diff --git a/backend/api/v1/modules/public/reference_data/agency_tariff_codes/routes.py b/backend/api/v1/modules/public/reference_data/agency_tariff_codes/routes.py
index 89cb4220..a4300667 100644
--- a/backend/api/v1/modules/public/reference_data/agency_tariff_codes/routes.py
+++ b/backend/api/v1/modules/public/reference_data/agency_tariff_codes/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy.orm import Session
from .dto import AgencyTariffCodeDTO
from .models import AgencyTariffCode
+from ..rbac import require_ref_view
router = APIRouter(prefix="/agency-tariff-codes")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_pedimento_codes.view"))],
+)
async def list_agency_tariff_codes(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"),
@@ -61,7 +66,11 @@ async def list_agency_tariff_codes(
}
-@router.get("/{id}", response_model=AgencyTariffCodeDTO)
+@router.get(
+ "/{id}",
+ response_model=AgencyTariffCodeDTO,
+ dependencies=[Depends(require_ref_view("ref_pedimento_codes.view"))],
+)
async def get_agency_tariff_code(
id: int,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/carta_porte_codes/routes.py b/backend/api/v1/modules/public/reference_data/carta_porte_codes/routes.py
index 38a2f213..c95bb0cf 100644
--- a/backend/api/v1/modules/public/reference_data/carta_porte_codes/routes.py
+++ b/backend/api/v1/modules/public/reference_data/carta_porte_codes/routes.py
@@ -9,11 +9,16 @@ from sqlalchemy.orm import Session
from .dto import CartaPorteDTO
from .models import CartaPorte
+from ..rbac import require_ref_view
router = APIRouter(prefix="/carta-porte")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_transport_types.view"))],
+)
async def list_carta_porte(
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=100),
@@ -55,7 +60,11 @@ async def list_carta_porte(
}
-@router.get("/{id}", response_model=CartaPorteDTO)
+@router.get(
+ "/{id}",
+ response_model=CartaPorteDTO,
+ dependencies=[Depends(require_ref_view("ref_transport_types.view"))],
+)
async def get_carta_porte(
id: int,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/code_pedimento_regimens/routes.py b/backend/api/v1/modules/public/reference_data/code_pedimento_regimens/routes.py
index 03f5efd7..c8ca4618 100644
--- a/backend/api/v1/modules/public/reference_data/code_pedimento_regimens/routes.py
+++ b/backend/api/v1/modules/public/reference_data/code_pedimento_regimens/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import CodePedimentoRegimenDTO
from .models import CodePedimentoRegimen
+from ..rbac import require_ref_view
router = APIRouter(prefix="/code-pedimento-regimens")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_pedimento_regimens.view"))],
+)
def list_code_pedimento_regimens(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"),
@@ -51,7 +56,11 @@ def list_code_pedimento_regimens(
}
-@router.get("/{id}", response_model=CodePedimentoRegimenDTO)
+@router.get(
+ "/{id}",
+ response_model=CodePedimentoRegimenDTO,
+ dependencies=[Depends(require_ref_view("ref_pedimento_regimens.view"))],
+)
def get_code_pedimento_regimen(
id: int,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/containers/routes.py b/backend/api/v1/modules/public/reference_data/containers/routes.py
index 7182f848..be6659f0 100644
--- a/backend/api/v1/modules/public/reference_data/containers/routes.py
+++ b/backend/api/v1/modules/public/reference_data/containers/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import ContainerDTO
from .models import Container
+from ..rbac import require_ref_view
router = APIRouter(prefix="/containers")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_containers.view"))],
+)
async def list_containers(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -41,7 +46,11 @@ async def list_containers(
}
-@router.get("/{key}", response_model=ContainerDTO)
+@router.get(
+ "/{key}",
+ response_model=ContainerDTO,
+ dependencies=[Depends(require_ref_view("ref_containers.view"))],
+)
async def get_container(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/countries/routes.py b/backend/api/v1/modules/public/reference_data/countries/routes.py
index 45d76eea..28550fa6 100644
--- a/backend/api/v1/modules/public/reference_data/countries/routes.py
+++ b/backend/api/v1/modules/public/reference_data/countries/routes.py
@@ -7,18 +7,23 @@ from sqlalchemy.orm import Session
from .dto import CountryDTO
from .models import Country
+from ..rbac import require_ref_view
router = APIRouter(prefix="/countries")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_countries.view"))],
+)
async def list_countries(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
search: str = Query(None, description="Término de búsqueda"),
db: Session = Depends(get_core_db),
):
- """Endpoint público para obtener lista de países - no requiere autenticación"""
+ """Lista de países protegida por permiso ref_countries.view"""
skip = (page - 1) * page_size
query = db.query(Country)
@@ -43,7 +48,11 @@ async def list_countries(
}
-@router.get("/{m3_key}", response_model=CountryDTO)
+@router.get(
+ "/{m3_key}",
+ response_model=CountryDTO,
+ dependencies=[Depends(require_ref_view("ref_countries.view"))],
+)
async def get_country(
m3_key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/currency_types/routes.py b/backend/api/v1/modules/public/reference_data/currency_types/routes.py
index 6d860ba3..bf658a36 100644
--- a/backend/api/v1/modules/public/reference_data/currency_types/routes.py
+++ b/backend/api/v1/modules/public/reference_data/currency_types/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import CurrencyTypeDTO
from .models import CurrencyType
+from ..rbac import require_ref_view
router = APIRouter(prefix="/currency-types")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_currency_types.view"))],
+)
async def list_currency_types(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -42,7 +47,11 @@ async def list_currency_types(
}
-@router.get("/{code}", response_model=CurrencyTypeDTO)
+@router.get(
+ "/{code}",
+ response_model=CurrencyTypeDTO,
+ dependencies=[Depends(require_ref_view("ref_currency_types.view"))],
+)
async def get_currency_type(
code: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/customs_sections/routes.py b/backend/api/v1/modules/public/reference_data/customs_sections/routes.py
index 767462d3..e6710d8f 100644
--- a/backend/api/v1/modules/public/reference_data/customs_sections/routes.py
+++ b/backend/api/v1/modules/public/reference_data/customs_sections/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import CustomsSectionDTO
from .models import CustomsSection
+from ..rbac import require_ref_view
router = APIRouter(prefix="/customs-sections")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_customs_sections.view"))],
+)
def list_customs_sections(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -41,7 +46,11 @@ def list_customs_sections(
}
-@router.get("/{customs_code}", response_model=CustomsSectionDTO)
+@router.get(
+ "/{customs_code}",
+ response_model=CustomsSectionDTO,
+ dependencies=[Depends(require_ref_view("ref_customs_sections.view"))],
+)
def get_customs_section(
customs_code: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/customs_warehouses/routes.py b/backend/api/v1/modules/public/reference_data/customs_warehouses/routes.py
index 03947cd0..3e7211e3 100644
--- a/backend/api/v1/modules/public/reference_data/customs_warehouses/routes.py
+++ b/backend/api/v1/modules/public/reference_data/customs_warehouses/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import CustomsWarehouseDTO
from .models import CustomsWarehouse
+from ..rbac import require_ref_view
router = APIRouter(prefix="/customs-warehouses")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_customs_warehouses.view"))],
+)
def list_customs_warehouses(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -42,7 +47,11 @@ def list_customs_warehouses(
}
-@router.get("/{key}/{customs}", response_model=CustomsWarehouseDTO)
+@router.get(
+ "/{key}/{customs}",
+ response_model=CustomsWarehouseDTO,
+ dependencies=[Depends(require_ref_view("ref_customs_warehouses.view"))],
+)
def get_customs_warehouse(
key: str,
customs: str,
diff --git a/backend/api/v1/modules/public/reference_data/identifiers/routes.py b/backend/api/v1/modules/public/reference_data/identifiers/routes.py
index b2263881..1b6594b7 100644
--- a/backend/api/v1/modules/public/reference_data/identifiers/routes.py
+++ b/backend/api/v1/modules/public/reference_data/identifiers/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy.orm import Session
from .dto import IdentifierDTO
from .models import IdentifierCatalog
+from ..rbac import require_ref_view
router = APIRouter(prefix="/identifiers")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("cat_identifiers.view"))],
+)
async def list_identifiers(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"),
@@ -59,7 +64,11 @@ async def list_identifiers(
}
-@router.get("/{key}", response_model=IdentifierDTO)
+@router.get(
+ "/{key}",
+ response_model=IdentifierDTO,
+ dependencies=[Depends(require_ref_view("cat_identifiers.view"))],
+)
async def get_identifier(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/incoterms/routes.py b/backend/api/v1/modules/public/reference_data/incoterms/routes.py
index 6661e334..634ac861 100644
--- a/backend/api/v1/modules/public/reference_data/incoterms/routes.py
+++ b/backend/api/v1/modules/public/reference_data/incoterms/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import IncotermDTO
from .models import Incoterm
+from ..rbac import require_ref_view
router = APIRouter(prefix="/incoterms")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_incoterms.view"))],
+)
async def list_incoterms(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -53,7 +58,11 @@ async def list_incoterms(
}
-@router.get("/{key}", response_model=IncotermDTO)
+@router.get(
+ "/{key}",
+ response_model=IncotermDTO,
+ dependencies=[Depends(require_ref_view("ref_incoterms.view"))],
+)
async def get_incoterm(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/invoice_types/routes.py b/backend/api/v1/modules/public/reference_data/invoice_types/routes.py
index c2b34297..9fb47e3d 100644
--- a/backend/api/v1/modules/public/reference_data/invoice_types/routes.py
+++ b/backend/api/v1/modules/public/reference_data/invoice_types/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import InvoiceTypeDTO
from .models import InvoiceType
+from ..rbac import require_ref_view
router = APIRouter(prefix="/invoice-types")
-@router.get("/", response_model=dict)
+@router.get(
+ "/",
+ response_model=dict,
+ dependencies=[Depends(require_ref_view("ref_invoice_types.view"))],
+)
def list_invoice_types(
page: int = Query(1, ge=1),
page_size: int = Query(50, ge=1, le=100),
@@ -55,7 +60,11 @@ def list_invoice_types(
}
-@router.get("/{key}", response_model=InvoiceTypeDTO)
+@router.get(
+ "/{key}",
+ response_model=InvoiceTypeDTO,
+ dependencies=[Depends(require_ref_view("ref_invoice_types.view"))],
+)
def get_invoice_type(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/license_exceptions/routes.py b/backend/api/v1/modules/public/reference_data/license_exceptions/routes.py
index c98a2eee..c04dfbc0 100644
--- a/backend/api/v1/modules/public/reference_data/license_exceptions/routes.py
+++ b/backend/api/v1/modules/public/reference_data/license_exceptions/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy.orm import Session
from .dto import LicenseExceptionDTO
from .models import LicenseException
+from ..rbac import require_ref_view
router = APIRouter(prefix="/license-exceptions")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_pedimento_regimens.view"))],
+)
async def list_license_exceptions(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"),
@@ -52,7 +57,11 @@ async def list_license_exceptions(
}
-@router.get("/{key}", response_model=LicenseExceptionDTO)
+@router.get(
+ "/{key}",
+ response_model=LicenseExceptionDTO,
+ dependencies=[Depends(require_ref_view("ref_pedimento_regimens.view"))],
+)
async def get_license_exception(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/material_types/routes.py b/backend/api/v1/modules/public/reference_data/material_types/routes.py
index b3982a93..94948c85 100644
--- a/backend/api/v1/modules/public/reference_data/material_types/routes.py
+++ b/backend/api/v1/modules/public/reference_data/material_types/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import MaterialTypeDTO
from .models import MaterialType
+from ..rbac import require_ref_view
router = APIRouter(prefix="/material-types")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_material_types.view"))],
+)
async def list_material_types(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"),
@@ -48,7 +53,11 @@ async def list_material_types(
}
-@router.get("/{key}", response_model=MaterialTypeDTO)
+@router.get(
+ "/{key}",
+ response_model=MaterialTypeDTO,
+ dependencies=[Depends(require_ref_view("ref_material_types.view"))],
+)
async def get_material_type(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/payment_methods/routes.py b/backend/api/v1/modules/public/reference_data/payment_methods/routes.py
index 3e728f4b..8731f4aa 100644
--- a/backend/api/v1/modules/public/reference_data/payment_methods/routes.py
+++ b/backend/api/v1/modules/public/reference_data/payment_methods/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import PaymentMethodDTO
from .models import PaymentMethod
+from ..rbac import require_ref_view
router = APIRouter(prefix="/payment-methods")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_payment_methods.view"))],
+)
def list_payment_methods(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -41,7 +46,11 @@ def list_payment_methods(
}
-@router.get("/{key}", response_model=PaymentMethodDTO)
+@router.get(
+ "/{key}",
+ response_model=PaymentMethodDTO,
+ dependencies=[Depends(require_ref_view("ref_payment_methods.view"))],
+)
def get_payment_method(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/pedimento_codes/routes.py b/backend/api/v1/modules/public/reference_data/pedimento_codes/routes.py
index 0d7aa39e..9d1bead0 100644
--- a/backend/api/v1/modules/public/reference_data/pedimento_codes/routes.py
+++ b/backend/api/v1/modules/public/reference_data/pedimento_codes/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import PedimentoCodeDTO
from .models import PedimentoCode
+from ..rbac import require_ref_view
router = APIRouter(prefix="/pedimento-codes")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_pedimento_codes.view"))],
+)
def list_pedimento_codes(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"),
@@ -41,7 +46,11 @@ def list_pedimento_codes(
}
-@router.get("/{code}", response_model=PedimentoCodeDTO)
+@router.get(
+ "/{code}",
+ response_model=PedimentoCodeDTO,
+ dependencies=[Depends(require_ref_view("ref_pedimento_codes.view"))],
+)
def get_pedimento_code(
code: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/pedimento_regimens/routes.py b/backend/api/v1/modules/public/reference_data/pedimento_regimens/routes.py
index 678fa194..a9ca228b 100644
--- a/backend/api/v1/modules/public/reference_data/pedimento_regimens/routes.py
+++ b/backend/api/v1/modules/public/reference_data/pedimento_regimens/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import RegimenPedimentoDTO
from .models import RegimenPedimento
+from ..rbac import require_ref_view
router = APIRouter(prefix="/pedimento-regimens")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_pedimento_regimens.view"))],
+)
def list_pedimento_regimens(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -41,7 +46,11 @@ def list_pedimento_regimens(
}
-@router.get("/{key}", response_model=RegimenPedimentoDTO)
+@router.get(
+ "/{key}",
+ response_model=RegimenPedimentoDTO,
+ dependencies=[Depends(require_ref_view("ref_pedimento_regimens.view"))],
+)
def get_pedimento_regimen(
key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/pedimento_transport_catalog/routes.py b/backend/api/v1/modules/public/reference_data/pedimento_transport_catalog/routes.py
index e3a0db27..18392d80 100644
--- a/backend/api/v1/modules/public/reference_data/pedimento_transport_catalog/routes.py
+++ b/backend/api/v1/modules/public/reference_data/pedimento_transport_catalog/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import PedimentoTransportCatalogDTO
from .models import PedimentoTransportCatalog
+from ..rbac import require_ref_view
router = APIRouter(prefix="/pedimento-transport-catalog")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_transport_types.view"))],
+)
async def list_pedimento_transport_catalog(
page: int = Query(1, ge=1, description="Numero de pagina"),
page_size: int = Query(100, ge=1, le=200, description="Tamano de pagina"),
@@ -42,7 +47,11 @@ async def list_pedimento_transport_catalog(
}
-@router.get("/{code}", response_model=PedimentoTransportCatalogDTO)
+@router.get(
+ "/{code}",
+ response_model=PedimentoTransportCatalogDTO,
+ dependencies=[Depends(require_ref_view("ref_transport_types.view"))],
+)
async def get_pedimento_transport_catalog(code: str, db: Session = Depends(get_core_db)):
obj = (
db.query(PedimentoTransportCatalog)
diff --git a/backend/api/v1/modules/public/reference_data/rbac.py b/backend/api/v1/modules/public/reference_data/rbac.py
new file mode 100644
index 00000000..baf920b7
--- /dev/null
+++ b/backend/api/v1/modules/public/reference_data/rbac.py
@@ -0,0 +1,25 @@
+from typing import Callable
+
+from core.database import get_core_db
+from core.security import get_current_user, validate_access_to_resource
+from fastapi import Depends, Query
+from sqlalchemy.orm import Session
+
+
+def require_ref_view(permission_code: str) -> Callable:
+ """
+ Dependency factory to enforce reference-data view permissions per company.
+
+ Endpoints using this dependency must receive `company_id` in query params.
+ """
+
+ async def _dep(
+ company_id: int = Query(..., description="Company ID"),
+ db: Session = Depends(get_core_db),
+ current_user: dict = Depends(get_current_user),
+ ) -> bool:
+ validate_access_to_resource(db, company_id, current_user, [permission_code])
+ return True
+
+ return _dep
+
diff --git a/backend/api/v1/modules/public/reference_data/states/routes.py b/backend/api/v1/modules/public/reference_data/states/routes.py
index 52d09f53..79595d33 100644
--- a/backend/api/v1/modules/public/reference_data/states/routes.py
+++ b/backend/api/v1/modules/public/reference_data/states/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import StateDTO
from .models import State
+from ..rbac import require_ref_view
router = APIRouter(prefix="/states")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_states.view"))],
+)
async def list_states(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -41,7 +46,11 @@ async def list_states(
}
-@router.get("/{m3_key}", response_model=StateDTO)
+@router.get(
+ "/{m3_key}",
+ response_model=StateDTO,
+ dependencies=[Depends(require_ref_view("ref_states.view"))],
+)
async def get_state(
m3_key: str,
db: Session = Depends(get_core_db),
diff --git a/backend/api/v1/modules/public/reference_data/trailer_types/routes.py b/backend/api/v1/modules/public/reference_data/trailer_types/routes.py
index e74ab634..9cdbbea5 100644
--- a/backend/api/v1/modules/public/reference_data/trailer_types/routes.py
+++ b/backend/api/v1/modules/public/reference_data/trailer_types/routes.py
@@ -5,11 +5,16 @@ from fastapi import APIRouter, Depends, HTTPException, Query
from sqlalchemy.orm import Session
from . import dto, services
+from ..rbac import require_ref_view
router = APIRouter()
-@router.get("/trailer-types/", response_model=Dict[str, Any])
+@router.get(
+ "/trailer-types/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("trailers.view"))],
+)
def list_trailer_types(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -27,7 +32,9 @@ def list_trailer_types(
@router.get(
- "/trailer-types/{trailer_type_key}", response_model=dto.TrailerTypeResponseDTO
+ "/trailer-types/{trailer_type_key}",
+ response_model=dto.TrailerTypeResponseDTO,
+ dependencies=[Depends(require_ref_view("trailers.view"))],
)
def get_trailer_type(trailer_type_key: str, db: Session = Depends(get_core_db)):
trailer_type = services.TrailerTypeService.get_trailer_type_by_key(
diff --git a/backend/api/v1/modules/public/reference_data/transport_modes/routes.py b/backend/api/v1/modules/public/reference_data/transport_modes/routes.py
index cfe4405d..0f480f64 100644
--- a/backend/api/v1/modules/public/reference_data/transport_modes/routes.py
+++ b/backend/api/v1/modules/public/reference_data/transport_modes/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import TransportModeDTO
from .models import TransportMode
+from ..rbac import require_ref_view
router = APIRouter(prefix="/transport-modes")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_transport_modes.view"))],
+)
async def list_transport_modes(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -40,7 +45,11 @@ async def list_transport_modes(
}
-@router.get("/{key}", response_model=TransportModeDTO)
+@router.get(
+ "/{key}",
+ response_model=TransportModeDTO,
+ dependencies=[Depends(require_ref_view("ref_transport_modes.view"))],
+)
async def get_transport_mode(key: str, db: Session = Depends(get_core_db)):
obj = db.query(TransportMode).filter(TransportMode.key == key).first()
if not obj:
diff --git a/backend/api/v1/modules/public/reference_data/transport_types/routes.py b/backend/api/v1/modules/public/reference_data/transport_types/routes.py
index 563d5e0b..fdfc47a9 100644
--- a/backend/api/v1/modules/public/reference_data/transport_types/routes.py
+++ b/backend/api/v1/modules/public/reference_data/transport_types/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import TransportTypeDTO
from .models import TransportType
+from ..rbac import require_ref_view
router = APIRouter(prefix="/transport-types")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_transport_types.view"))],
+)
def list_transport_types(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -40,7 +45,11 @@ def list_transport_types(
}
-@router.get("/{transport_code}", response_model=TransportTypeDTO)
+@router.get(
+ "/{transport_code}",
+ response_model=TransportTypeDTO,
+ dependencies=[Depends(require_ref_view("ref_transport_types.view"))],
+)
def get_transport_type(transport_code: str, db: Session = Depends(get_core_db)):
obj = (
db.query(TransportType)
diff --git a/backend/api/v1/modules/public/reference_data/valuation_methods/routes.py b/backend/api/v1/modules/public/reference_data/valuation_methods/routes.py
index e0f7c65d..899f80f3 100644
--- a/backend/api/v1/modules/public/reference_data/valuation_methods/routes.py
+++ b/backend/api/v1/modules/public/reference_data/valuation_methods/routes.py
@@ -8,11 +8,16 @@ from sqlalchemy import or_
from .dto import ValuationMethodDTO
from .models import ValuationMethod
+from ..rbac import require_ref_view
router = APIRouter(prefix="/valuation-methods")
-@router.get("/", response_model=Dict[str, Any])
+@router.get(
+ "/",
+ response_model=Dict[str, Any],
+ dependencies=[Depends(require_ref_view("ref_valuation_methods.view"))],
+)
async def list_valuation_methods(
page: int = Query(1, ge=1, description="Número de página"),
page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"),
@@ -41,7 +46,11 @@ async def list_valuation_methods(
}
-@router.get("/{key}", response_model=ValuationMethodDTO)
+@router.get(
+ "/{key}",
+ response_model=ValuationMethodDTO,
+ dependencies=[Depends(require_ref_view("ref_valuation_methods.view"))],
+)
async def get_valuation_method(
key: str,
db: Session = Depends(get_core_db),
diff --git a/frontend/src/lib/components/sidebar/modules.ts b/frontend/src/lib/components/sidebar/modules.ts
index fb38105c..759b6873 100644
--- a/frontend/src/lib/components/sidebar/modules.ts
+++ b/frontend/src/lib/components/sidebar/modules.ts
@@ -100,14 +100,17 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.reference_data.codes_pedimento_regimen"](),
url: "/dashboard/reference_data/code_pedimento_regimens",
+ permission: "ref_pedimento_regimens.view",
},
{
title: m["sidebar.reference_data.containers"](),
url: "/dashboard/reference_data/containers",
+ permission: "ref_containers.view",
},
{
title: m["sidebar.reference_data.countries"](),
url: "/dashboard/reference_data/countries",
+ permission: "ref_countries.view",
},
{
title: m["sidebar.reference_data.currency_types"](),
@@ -117,58 +120,72 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.reference_data.customs_sections"](),
url: "/dashboard/reference_data/customs_sections",
+ permission: "ref_customs_sections.view",
},
{
title: m["sidebar.reference_data.customs_warehouses"](),
url: "/dashboard/reference_data/customs_warehouses",
+ permission: "ref_customs_warehouses.view",
},
{
title: m["sidebar.reference_data.incoterms"](),
url: "/dashboard/reference_data/incoterms",
+ permission: "ref_incoterms.view",
},
{
title: m["sidebar.reference_data.document_types_digitization"](),
url: "/dashboard/reference_data/document_types_digitization",
+ permission: "ref_invoice_types.view",
},
{
title: m["sidebar.reference_data.invoice_types"](),
url: "/dashboard/reference_data/invoice_types",
+ permission: "ref_invoice_types.view",
},
{
title: m["sidebar.reference_data.material_types"](),
url: "/dashboard/reference_data/material_types",
+ permission: "ref_material_types.view",
},
{
title: m["sidebar.reference_data.payment_methods"](),
url: "/dashboard/reference_data/payment_methods",
+ permission: "ref_payment_methods.view",
},
{
title: m["sidebar.reference_data.pedimento_codes"](),
url: "/dashboard/reference_data/pedimento_codes",
+ permission: "ref_pedimento_codes.view",
},
{
title: m["sidebar.reference_data.pedimento_regimes"](),
url: "/dashboard/reference_data/pedimento_regimens",
+ permission: "ref_pedimento_regimens.view",
},
{
title: m["sidebar.reference_data.sectors"](),
url: "/dashboard/reference_data/sectors",
+ permission: "ref_sectors.view",
},
{
title: m["sidebar.reference_data.states"](),
url: "/dashboard/reference_data/states",
+ permission: "ref_states.view",
},
{
title: m["sidebar.reference_data.transportation_modes"](),
url: "/dashboard/reference_data/transport_modes",
+ permission: "ref_transport_modes.view",
},
{
title: m["sidebar.reference_data.transportation_types"](),
url: "/dashboard/reference_data/transport_types",
+ permission: "ref_transport_types.view",
},
{
title: m["sidebar.reference_data.valuation_methods"](),
url: "/dashboard/reference_data/valuation_methods",
+ permission: "ref_valuation_methods.view",
},
],
isActive: false,
@@ -206,6 +223,7 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.general_catalogs.incoterms"](),
url: "/dashboard/reference_data/incoterms",
+ permission: "ref_incoterms.view",
},
{
title: m["sidebar.general_catalogs.inpc"](),
@@ -222,10 +240,12 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.general_catalogs.valuation_methods"](),
url: "/dashboard/reference_data/valuation_methods",
+ permission: "ref_valuation_methods.view",
},
{
title: m["sidebar.general_catalogs.countries"](),
url: "/dashboard/reference_data/countries",
+ permission: "ref_countries.view",
},
{
title: m["sidebar.general_catalogs.ports"](),
@@ -269,6 +289,7 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.general_catalogs.currency_types"](),
url: "/dashboard/reference_data/currency_types",
+ permission: "ref_currency_types.view",
},
{
title: m["sidebar.general_catalogs.multi_currency"](),
@@ -278,6 +299,7 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.general_catalogs.invoice_types"](),
url: "/dashboard/reference_data/invoice_types",
+ permission: "ref_invoice_types.view",
},
{
title: m["sidebar.general_catalogs.electronic_signatures"](),
@@ -290,6 +312,7 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.general_catalogs.customs_warehouses"](),
url: "/dashboard/reference_data/customs_warehouses",
+ permission: "ref_customs_warehouses.view",
},
{
title: m["sidebar.general_catalogs.prevalidators"](),
@@ -413,7 +436,7 @@ export function getSidebarData(): SidebarData {
{
title: m["sidebar.pedimentos.payment_methods"](),
url: "/dashboard/reference_data/payment_methods",
- permission: 'pedimentos_payments.view',
+ permission: 'ref_payment_methods.view',
},
{
title: m["sidebar.pedimentos.customs_sections"](),
diff --git a/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte b/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte
index 7fd3ecf8..895ade27 100644
--- a/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte
+++ b/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte
@@ -41,8 +41,8 @@
// Permisos
const canView = $derived(userHasPermission($currentUser, 'ref_pedimento_regimens.view'));
- const canEdit = $derived(userHasPermission($currentUser, 'ref_pedimento_regimens.edit'));
- const canDelete = $derived(userHasPermission($currentUser, 'ref_pedimento_regimens.delete'));
+ const canEdit = $derived(false);
+ const canDelete = $derived(false);
const isError = $derived(!canView || status >= 400 || error);
@@ -123,7 +123,7 @@
{#if isError}
{:else}
diff --git a/frontend/src/routes/dashboard/reference_data/containers/+page.svelte b/frontend/src/routes/dashboard/reference_data/containers/+page.svelte
index c548628b..343d6b09 100644
--- a/frontend/src/routes/dashboard/reference_data/containers/+page.svelte
+++ b/frontend/src/routes/dashboard/reference_data/containers/+page.svelte
@@ -29,9 +29,9 @@
// Permisos
const canView = $derived(userHasPermission($currentUser, 'ref_containers.view'));
- const canCreate = $derived(userHasPermission($currentUser, 'ref_containers.create'));
- const canEdit = $derived(userHasPermission($currentUser, 'ref_containers.edit'));
- const canDelete = $derived(userHasPermission($currentUser, 'ref_containers.delete'));
+ const canCreate = $derived(false);
+ const canEdit = $derived(false);
+ const canDelete = $derived(false);
const isError = $derived(!canView || status >= 400 || error);
diff --git a/frontend/src/routes/dashboard/reference_data/countries/+page.svelte b/frontend/src/routes/dashboard/reference_data/countries/+page.svelte
index 5e1e7f5e..96520035 100644
--- a/frontend/src/routes/dashboard/reference_data/countries/+page.svelte
+++ b/frontend/src/routes/dashboard/reference_data/countries/+page.svelte
@@ -64,10 +64,10 @@
let status = $state(data.status || 200);
// Permisos
- const canView = $derived(userHasPermission($currentUser, 'ref_countries.view') || userHasPermission($currentUser, 'cat_countries.view'));
- const canCreate = $derived(userHasPermission($currentUser, 'ref_countries.create'));
- const canEdit = $derived(userHasPermission($currentUser, 'ref_countries.edit'));
- const canDelete = $derived(userHasPermission($currentUser, 'ref_countries.delete'));
+ const canView = $derived(userHasPermission($currentUser, 'ref_countries.view'));
+ const canCreate = $derived(false);
+ const canEdit = $derived(false);
+ const canDelete = $derived(false);
const isError = $derived(!canView || status >= 400 || error);
diff --git a/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte b/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte
index 85aa166a..fcd5917f 100644
--- a/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte
+++ b/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte
@@ -62,10 +62,10 @@
let status = $state(data.status || 200);
// Permisos
- const canView = $derived(userHasPermission($currentUser, 'ref_currency_types.view') || userHasPermission($currentUser, 'cat_currency.view'));
- const canCreate = $derived(userHasPermission($currentUser, 'ref_currency_types.create'));
- const canEdit = $derived(userHasPermission($currentUser, 'ref_currency_types.edit'));
- const canDelete = $derived(userHasPermission($currentUser, 'ref_currency_types.delete'));
+ const canView = $derived(userHasPermission($currentUser, 'ref_currency_types.view'));
+ const canCreate = $derived(false);
+ const canEdit = $derived(false);
+ const canDelete = $derived(false);
const isError = $derived(!canView || status >= 400 || error);
diff --git a/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte b/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte
index 6488bfc9..7035ad4e 100644
--- a/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte
+++ b/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte
@@ -33,9 +33,9 @@
// Permisos
const canView = $derived(userHasPermission($currentUser, 'ref_customs_sections.view'));
- const canCreate = $derived(userHasPermission($currentUser, 'ref_customs_sections.create'));
- const canEdit = $derived(userHasPermission($currentUser, 'ref_customs_sections.edit'));
- const canDelete = $derived(userHasPermission($currentUser, 'ref_customs_sections.delete'));
+ const canCreate = $derived(false);
+ const canEdit = $derived(false);
+ const canDelete = $derived(false);
const isError = $derived(status >= 400 || error);
diff --git a/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte b/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte
index 124dd7b4..c7740f53 100644
--- a/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte
+++ b/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte
@@ -37,10 +37,10 @@
let status = $state(data.status || 200);
// Permisos
- const canView = $derived(userHasPermission($currentUser, 'ref_customs_warehouses.view') || userHasPermission($currentUser, 'cat_warehouses.view'));
- const canCreate = $derived(userHasPermission($currentUser, 'ref_customs_warehouses.create'));
- const canEdit = $derived(userHasPermission($currentUser, 'ref_customs_warehouses.edit'));
- const canDelete = $derived(userHasPermission($currentUser, 'ref_customs_warehouses.delete'));
+ const canView = $derived(userHasPermission($currentUser, 'ref_customs_warehouses.view'));
+ const canCreate = $derived(false);
+ const canEdit = $derived(false);
+ const canDelete = $derived(false);
const isError = $derived(!canView || status >= 400 || error);
diff --git a/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.server.ts b/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.server.ts
index 4cb89742..23052018 100644
--- a/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.server.ts
+++ b/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.server.ts
@@ -11,7 +11,8 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => {
items: [],
total: 0,
page: 1,
- page_size: 50
+ page_size: 50,
+ status: 401
};
}
@@ -28,7 +29,8 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => {
items: [],
total: 0,
page,
- page_size: pageSize
+ page_size: pageSize,
+ status: 400
};
}
@@ -50,19 +52,25 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => {
);
if (!response.ok) {
- const errorText = await response.text();
- console.error('📊 [Document Types Digitization] API Error:', {
- status: response.status,
- statusText: response.statusText,
- error: errorText
- });
-
+ const message =
+ response.status === 403
+ ? 'No tiene permiso para ver tipos de documento para digitalización'
+ : `Error ${response.status}: ${response.statusText}`;
+ if (response.status !== 403) {
+ const errorText = await response.text();
+ console.error('📊 [Document Types Digitization] API Error:', {
+ status: response.status,
+ statusText: response.statusText,
+ error: errorText
+ });
+ }
return {
- error: `Error ${response.status}: ${response.statusText}`,
+ error: message,
items: [],
total: 0,
page,
- page_size: pageSize
+ page_size: pageSize,
+ status: response.status
};
}
@@ -73,7 +81,8 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => {
total: data.total || 0,
page: data.page || page,
page_size: data.page_size || pageSize,
- error: null
+ error: null,
+ status: 200
};
} catch (error) {
console.error('📊 [Document Types Digitization] Load error:', error);
@@ -82,7 +91,8 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => {
items: [],
total: 0,
page: 1,
- page_size: 50
+ page_size: 50,
+ status: 500
};
}
};
\ No newline at end of file
diff --git a/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.svelte b/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.svelte
index 5635d344..53321685 100644
--- a/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.svelte
+++ b/frontend/src/routes/dashboard/reference_data/document_types_digitization/+page.svelte
@@ -16,6 +16,8 @@
import { createColumns } from '$lib/components/dashboard/reference_data/document_types_digitization/columns';
import { useShortcuts } from '$lib/hooks/use-shortcuts';
import type { PageData } from './$types';
+ import { currentUser, userHasPermission } from '$lib/auth';
+ import ErrorState from '$lib/components/dashboard/common/error-state.svelte';
let { data }: { data: PageData } = $props();
@@ -39,9 +41,12 @@
let totalItems = $state(data.total || 0);
let loading = $state(false);
let error = $state(data.error || null);
+ let status = $state(data.status || 200);
let searchQuery = $state($page.url.searchParams.get('search') || '');
let timeout: ReturnType;
let hasMore = $derived(allItems.length < totalItems);
+ const canView = $derived(userHasPermission($currentUser, 'ref_invoice_types.view'));
+ const isError = $derived(!canView || status >= 400 || error);
function getActiveCompanyId(): number | null {
const fromStore = companyStore.activeCompany?.id;
@@ -137,42 +142,54 @@
const columns = createColumns();
-