From 4c781b460a1139cc782a7a1853622a79b4c0eb11 Mon Sep 17 00:00:00 2001 From: hreyes Date: Tue, 3 Mar 2026 16:45:12 -0700 Subject: [PATCH] feature/layouts-csv-refact --- .gitignore | 1 + README.md | 19 +++++++++++++- backend/api/v1/modules/a76/boms/routes.py | 2 +- backend/api/v1/modules/a76/classes/routes.py | 2 +- .../a76/clients_and_providers/routes.py | 2 +- .../v1/modules/a76/csv_templates/registry.py | 24 +++++++++--------- .../a76/customs_brokers/imports/__init__.py | 2 -- .../v1/modules/a76/customs_brokers/routes.py | 2 +- .../general_catalogs/exchange_rate/routes.py | 2 +- .../fractions/us_tariff_fractions/routes.py | 2 +- .../v1/modules/a76/layouts_csv/__init__.py | 1 + .../imports => layouts_csv/boms}/__init__.py | 0 .../imports => layouts_csv/boms}/routes.py | 3 ++- .../imports => layouts_csv/boms}/schemas.py | 0 .../imports => layouts_csv/boms}/tasks.py | 8 +++--- .../boms}/template_config.py | 0 .../classes}/__init__.py | 0 .../imports => layouts_csv/classes}/routes.py | 3 ++- .../classes}/schemas.py | 0 .../imports => layouts_csv/classes}/tasks.py | 8 +++--- .../classes}/template_config.py | 0 .../clients_and_providers}/__init__.py | 0 .../clients_and_providers}/routes.py | 3 ++- .../clients_and_providers}/schemas.py | 0 .../clients_and_providers}/tasks.py | 8 +++--- .../clients_and_providers}/template_config.py | 0 .../layouts_csv/customs_brokers/__init__.py | 1 + .../customs_brokers}/routes.py | 3 ++- .../customs_brokers}/schemas.py | 0 .../customs_brokers}/tasks.py | 8 +++--- .../customs_brokers}/template_config.py | 0 .../drivers}/__init__.py | 0 .../imports => layouts_csv/drivers}/routes.py | 3 ++- .../drivers}/schemas.py | 0 .../imports => layouts_csv/drivers}/tasks.py | 8 +++--- .../drivers}/template_config.py | 0 .../exchange_rate}/__init__.py | 0 .../exchange_rate}/routes.py | 3 ++- .../exchange_rate}/schemas.py | 0 .../exchange_rate}/tasks.py | 8 +++--- .../exchange_rate}/template_config.py | 0 .../facturas}/__init__.py | 0 .../facturas}/routes.py | 3 ++- .../facturas}/schemas.py | 0 .../facturas}/tasks.py | 11 +++++--- .../facturas}/template_config.py | 0 .../imports => layouts_csv/parts}/__init__.py | 0 .../imports => layouts_csv/parts}/routes.py | 3 ++- .../imports => layouts_csv/parts}/schemas.py | 0 .../imports => layouts_csv/parts}/tasks.py | 8 +++--- .../parts}/template_config.py | 0 .../pedmientos}/__init__.py | 0 .../pedmientos}/routes.py | 3 ++- .../pedmientos}/schemas.py | 0 .../pedmientos}/tasks.py | 8 +++--- .../pedmientos}/template_config.py | 0 .../trailers}/__init__.py | 0 .../trailers}/routes.py | 3 ++- .../trailers}/schemas.py | 0 .../imports => layouts_csv/trailers}/tasks.py | 8 +++--- .../trailers}/template_config.py | 0 .../us_tariff_fractions}/__init__.py | 0 .../us_tariff_fractions}/routes.py | 3 ++- .../us_tariff_fractions}/schemas.py | 0 .../us_tariff_fractions}/tasks.py | 8 +++--- .../us_tariff_fractions}/template_config.py | 0 .../vehicles}/__init__.py | 0 .../vehicles}/routes.py | 3 ++- .../vehicles}/schemas.py | 0 .../imports => layouts_csv/vehicles}/tasks.py | 8 +++--- .../vehicles}/template_config.py | 0 backend/api/v1/modules/a76/parts/routes.py | 2 +- .../api/v1/modules/a76/pedmientos/router.py | 2 +- backend/api/v1/modules/a76/router.py | 2 +- .../a76/transportation/drivers/routes.py | 2 +- .../a76/transportation/trailers/routes.py | 2 +- .../a76/transportation/vehicles/routes.py | 2 +- backend/celerybeat-schedule | Bin 16384 -> 0 bytes backend/core/celery_app.py | 22 ++++++++-------- backend/core/paths.py | 15 +++++++++++ backend/main.py | 4 +++ 81 files changed, 151 insertions(+), 97 deletions(-) delete mode 100644 backend/api/v1/modules/a76/customs_brokers/imports/__init__.py create mode 100644 backend/api/v1/modules/a76/layouts_csv/__init__.py rename backend/api/v1/modules/a76/{boms/imports => layouts_csv/boms}/__init__.py (100%) rename backend/api/v1/modules/a76/{boms/imports => layouts_csv/boms}/routes.py (98%) rename backend/api/v1/modules/a76/{boms/imports => layouts_csv/boms}/schemas.py (100%) rename backend/api/v1/modules/a76/{boms/imports => layouts_csv/boms}/tasks.py (98%) rename backend/api/v1/modules/a76/{boms/imports => layouts_csv/boms}/template_config.py (100%) rename backend/api/v1/modules/a76/{classes/imports => layouts_csv/classes}/__init__.py (100%) rename backend/api/v1/modules/a76/{classes/imports => layouts_csv/classes}/routes.py (98%) rename backend/api/v1/modules/a76/{classes/imports => layouts_csv/classes}/schemas.py (100%) rename backend/api/v1/modules/a76/{classes/imports => layouts_csv/classes}/tasks.py (98%) rename backend/api/v1/modules/a76/{classes/imports => layouts_csv/classes}/template_config.py (100%) rename backend/api/v1/modules/a76/{clients_and_providers/imports => layouts_csv/clients_and_providers}/__init__.py (100%) rename backend/api/v1/modules/a76/{clients_and_providers/imports => layouts_csv/clients_and_providers}/routes.py (98%) rename backend/api/v1/modules/a76/{clients_and_providers/imports => layouts_csv/clients_and_providers}/schemas.py (100%) rename backend/api/v1/modules/a76/{clients_and_providers/imports => layouts_csv/clients_and_providers}/tasks.py (98%) rename backend/api/v1/modules/a76/{clients_and_providers/imports => layouts_csv/clients_and_providers}/template_config.py (100%) create mode 100644 backend/api/v1/modules/a76/layouts_csv/customs_brokers/__init__.py rename backend/api/v1/modules/a76/{customs_brokers/imports => layouts_csv/customs_brokers}/routes.py (98%) rename backend/api/v1/modules/a76/{customs_brokers/imports => layouts_csv/customs_brokers}/schemas.py (100%) rename backend/api/v1/modules/a76/{customs_brokers/imports => layouts_csv/customs_brokers}/tasks.py (98%) rename backend/api/v1/modules/a76/{customs_brokers/imports => layouts_csv/customs_brokers}/template_config.py (100%) rename backend/api/v1/modules/a76/{transportation/drivers/imports => layouts_csv/drivers}/__init__.py (100%) rename backend/api/v1/modules/a76/{transportation/drivers/imports => layouts_csv/drivers}/routes.py (98%) rename backend/api/v1/modules/a76/{transportation/drivers/imports => layouts_csv/drivers}/schemas.py (100%) rename backend/api/v1/modules/a76/{transportation/drivers/imports => layouts_csv/drivers}/tasks.py (98%) rename backend/api/v1/modules/a76/{transportation/drivers/imports => layouts_csv/drivers}/template_config.py (100%) rename backend/api/v1/modules/a76/{general_catalogs/exchange_rate/imports => layouts_csv/exchange_rate}/__init__.py (100%) rename backend/api/v1/modules/a76/{general_catalogs/exchange_rate/imports => layouts_csv/exchange_rate}/routes.py (98%) rename backend/api/v1/modules/a76/{general_catalogs/exchange_rate/imports => layouts_csv/exchange_rate}/schemas.py (100%) rename backend/api/v1/modules/a76/{general_catalogs/exchange_rate/imports => layouts_csv/exchange_rate}/tasks.py (98%) rename backend/api/v1/modules/a76/{general_catalogs/exchange_rate/imports => layouts_csv/exchange_rate}/template_config.py (100%) rename backend/api/v1/modules/a76/{imports => layouts_csv/facturas}/__init__.py (100%) rename backend/api/v1/modules/a76/{imports => layouts_csv/facturas}/routes.py (96%) rename backend/api/v1/modules/a76/{imports => layouts_csv/facturas}/schemas.py (100%) rename backend/api/v1/modules/a76/{imports => layouts_csv/facturas}/tasks.py (97%) rename backend/api/v1/modules/a76/{imports => layouts_csv/facturas}/template_config.py (100%) rename backend/api/v1/modules/a76/{parts/imports => layouts_csv/parts}/__init__.py (100%) rename backend/api/v1/modules/a76/{parts/imports => layouts_csv/parts}/routes.py (98%) rename backend/api/v1/modules/a76/{parts/imports => layouts_csv/parts}/schemas.py (100%) rename backend/api/v1/modules/a76/{parts/imports => layouts_csv/parts}/tasks.py (99%) rename backend/api/v1/modules/a76/{parts/imports => layouts_csv/parts}/template_config.py (100%) rename backend/api/v1/modules/a76/{pedmientos/imports => layouts_csv/pedmientos}/__init__.py (100%) rename backend/api/v1/modules/a76/{pedmientos/imports => layouts_csv/pedmientos}/routes.py (98%) rename backend/api/v1/modules/a76/{pedmientos/imports => layouts_csv/pedmientos}/schemas.py (100%) rename backend/api/v1/modules/a76/{pedmientos/imports => layouts_csv/pedmientos}/tasks.py (98%) rename backend/api/v1/modules/a76/{pedmientos/imports => layouts_csv/pedmientos}/template_config.py (100%) rename backend/api/v1/modules/a76/{transportation/trailers/imports => layouts_csv/trailers}/__init__.py (100%) rename backend/api/v1/modules/a76/{transportation/trailers/imports => layouts_csv/trailers}/routes.py (98%) rename backend/api/v1/modules/a76/{transportation/trailers/imports => layouts_csv/trailers}/schemas.py (100%) rename backend/api/v1/modules/a76/{transportation/trailers/imports => layouts_csv/trailers}/tasks.py (98%) rename backend/api/v1/modules/a76/{transportation/trailers/imports => layouts_csv/trailers}/template_config.py (100%) rename backend/api/v1/modules/a76/{general_catalogs/fractions/us_tariff_fractions/imports => layouts_csv/us_tariff_fractions}/__init__.py (100%) rename backend/api/v1/modules/a76/{general_catalogs/fractions/us_tariff_fractions/imports => layouts_csv/us_tariff_fractions}/routes.py (98%) rename backend/api/v1/modules/a76/{general_catalogs/fractions/us_tariff_fractions/imports => layouts_csv/us_tariff_fractions}/schemas.py (100%) rename backend/api/v1/modules/a76/{general_catalogs/fractions/us_tariff_fractions/imports => layouts_csv/us_tariff_fractions}/tasks.py (98%) rename backend/api/v1/modules/a76/{general_catalogs/fractions/us_tariff_fractions/imports => layouts_csv/us_tariff_fractions}/template_config.py (100%) rename backend/api/v1/modules/a76/{transportation/vehicles/imports => layouts_csv/vehicles}/__init__.py (100%) rename backend/api/v1/modules/a76/{transportation/vehicles/imports => layouts_csv/vehicles}/routes.py (98%) rename backend/api/v1/modules/a76/{transportation/vehicles/imports => layouts_csv/vehicles}/schemas.py (100%) rename backend/api/v1/modules/a76/{transportation/vehicles/imports => layouts_csv/vehicles}/tasks.py (98%) rename backend/api/v1/modules/a76/{transportation/vehicles/imports => layouts_csv/vehicles}/template_config.py (100%) delete mode 100644 backend/celerybeat-schedule create mode 100644 backend/core/paths.py diff --git a/.gitignore b/.gitignore index 08138e68..d5f197cc 100644 --- a/.gitignore +++ b/.gitignore @@ -67,6 +67,7 @@ htmlcov/ *.dockerignore postgres-data/ backend/uploads/ +backend/layouts/imports/ docker-compose.yml .mypy_cache/ diff --git a/README.md b/README.md index d6d16209..b5b129cf 100644 --- a/README.md +++ b/README.md @@ -42,14 +42,24 @@ anexo76/ │ ├── .env.example # Variables de entorno de ejemplo │ ├── core/ # Módulos core │ │ ├── config.py # Configuración centralizada +│ │ ├── paths.py # Rutas base y layout_path() para importación CSV │ │ ├── database.py # Configuración de BD multi-tenant │ │ ├── security.py # Autenticación y autorización │ │ └── middleware.py # Middlewares personalizados +│ ├── layouts/ # Directorio de datos: temp y errors de importación CSV (creado al arranque) │ └── api/ │ └── v1/ │ ├── router.py # Router principal API v1 │ └── modules/ # Módulos de negocio -│ ├── auth/ # Autenticación +│ └── a76/ +│ ├── layouts_csv/ # Lógica centralizada de cargas por CSV (routes, tasks, validaciones por proceso) +│ │ ├── facturas/ +│ │ ├── customs_brokers/ +│ │ ├── clients_and_providers/ +│ │ └── ... # Una carpeta por proceso de importación +│ ├── csv_templates/ +│ └── ... +│ ├── auth/ │ ├── tenants/ # Gestión de tenants │ ├── licenses/ # Control de licencias │ └── ... @@ -136,6 +146,11 @@ python -c "from core.database import init_db; init_db()" ``` docker build -t dev.aduanasoft.com/anexo76/backend:latest -f ./backend/Dockerfile ./backend +``` + +**Importación CSV y workers Celery**: La lógica de cargas por CSV está en `api/v1/modules/a76/layouts_csv/` (una carpeta por proceso). Los workers Celery deben cargar los módulos `api.v1.modules.a76.layouts_csv..tasks`. El directorio de trabajo del worker debe ser la raíz del backend para que `layout_path("imports", "temp")` y `layout_path("imports", "errors")` apunten a los mismos directorios que la API. Si API y worker comparten volumen, los archivos temporales y de errores se escriben en `backend/layouts/imports/temp` y `backend/layouts/imports/errors`. + +``` docker build \ --build-arg VITE_API_URL=https://anexo76-dev.aduanasoft.com/api/ \ --build-arg VITE_KEYCLOAK_URL=https://anexo76-dev.aduanasoft.com/kcauth/ \ @@ -165,6 +180,8 @@ pip install -r requirements.txt uvicorn main:app --reload ``` +Los directorios `backend/layouts/imports/temp` y `backend/layouts/imports/errors` se crean automáticamente al arrancar el backend para la importación CSV. + ### Frontend ```bash diff --git a/backend/api/v1/modules/a76/boms/routes.py b/backend/api/v1/modules/a76/boms/routes.py index ddd64d15..98f5a529 100644 --- a/backend/api/v1/modules/a76/boms/routes.py +++ b/backend/api/v1/modules/a76/boms/routes.py @@ -5,7 +5,7 @@ Mismo patrón que parts y classes: upload → scan → status → commit. from fastapi import APIRouter -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.boms.routes import router as imports_router router = APIRouter() diff --git a/backend/api/v1/modules/a76/classes/routes.py b/backend/api/v1/modules/a76/classes/routes.py index 2166610f..389bbbd5 100644 --- a/backend/api/v1/modules/a76/classes/routes.py +++ b/backend/api/v1/modules/a76/classes/routes.py @@ -12,7 +12,7 @@ from api.v1.common.tenant_crud_routes import TenantCRUDRoutes, validate_access_t from .dto import ClassCreateDTO, ClassCreateDTOFA, ClassResponseDTO, ClassResponseDTOFA, ClassUpdateDTO, ClassWithFADataResponse from .service import ClassService -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.classes.routes import router as imports_router # Create a new router for custom endpoints router = APIRouter() diff --git a/backend/api/v1/modules/a76/clients_and_providers/routes.py b/backend/api/v1/modules/a76/clients_and_providers/routes.py index c9c67efe..bd3ad62c 100644 --- a/backend/api/v1/modules/a76/clients_and_providers/routes.py +++ b/backend/api/v1/modules/a76/clients_and_providers/routes.py @@ -20,7 +20,7 @@ from .dto import ( ) from .service import ClientProviderService from .models import ClientProvider -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.clients_and_providers.routes import router as imports_router # Create main router to add custom endpoints router = APIRouter(prefix="/clients-providers") diff --git a/backend/api/v1/modules/a76/csv_templates/registry.py b/backend/api/v1/modules/a76/csv_templates/registry.py index e6486244..2e933200 100644 --- a/backend/api/v1/modules/a76/csv_templates/registry.py +++ b/backend/api/v1/modules/a76/csv_templates/registry.py @@ -7,35 +7,35 @@ import io from typing import Dict, List, Optional # Importar configs de cada módulo -from api.v1.modules.a76.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.facturas.template_config import ( TEMPLATE_COLUMNS as IMPORTS_TEMPLATE_COLUMNS, _resolve_template_columns as resolve_imports_template, ) -from api.v1.modules.a76.parts.imports.template_config import TEMPLATE_COLUMNS as PARTS_TEMPLATE_COLUMNS -from api.v1.modules.a76.boms.imports.template_config import TEMPLATE_COLUMNS as BOMS_TEMPLATE_COLUMNS -from api.v1.modules.a76.classes.imports.template_config import TEMPLATE_COLUMNS as CLASSES_TEMPLATE_COLUMNS -from api.v1.modules.a76.customs_brokers.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.parts.template_config import TEMPLATE_COLUMNS as PARTS_TEMPLATE_COLUMNS +from api.v1.modules.a76.layouts_csv.boms.template_config import TEMPLATE_COLUMNS as BOMS_TEMPLATE_COLUMNS +from api.v1.modules.a76.layouts_csv.classes.template_config import TEMPLATE_COLUMNS as CLASSES_TEMPLATE_COLUMNS +from api.v1.modules.a76.layouts_csv.customs_brokers.template_config import ( TEMPLATE_COLUMNS as CUSTOMS_BROKERS_TEMPLATE_COLUMNS, ) -from api.v1.modules.a76.clients_and_providers.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.clients_and_providers.template_config import ( TEMPLATE_COLUMNS as CLIENTS_PROVIDERS_TEMPLATE_COLUMNS, ) -from api.v1.modules.a76.general_catalogs.exchange_rate.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.exchange_rate.template_config import ( TEMPLATE_COLUMNS as EXCHANGE_RATE_TEMPLATE_COLUMNS, ) -from api.v1.modules.a76.general_catalogs.fractions.us_tariff_fractions.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.us_tariff_fractions.template_config import ( TEMPLATE_COLUMNS as US_TARIFF_FRACTIONS_TEMPLATE_COLUMNS, ) -from api.v1.modules.a76.pedmientos.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.pedmientos.template_config import ( TEMPLATE_COLUMNS as PEDIMENTOS_TEMPLATE_COLUMNS, ) -from api.v1.modules.a76.transportation.vehicles.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.vehicles.template_config import ( TEMPLATE_COLUMNS as VEHICLES_TEMPLATE_COLUMNS, ) -from api.v1.modules.a76.transportation.drivers.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.drivers.template_config import ( TEMPLATE_COLUMNS as DRIVERS_TEMPLATE_COLUMNS, ) -from api.v1.modules.a76.transportation.trailers.imports.template_config import ( +from api.v1.modules.a76.layouts_csv.trailers.template_config import ( TEMPLATE_COLUMNS as TRAILERS_TEMPLATE_COLUMNS, ) diff --git a/backend/api/v1/modules/a76/customs_brokers/imports/__init__.py b/backend/api/v1/modules/a76/customs_brokers/imports/__init__.py deleted file mode 100644 index c1c90d12..00000000 --- a/backend/api/v1/modules/a76/customs_brokers/imports/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# CSV import flow for Agentes Aduanales (Customs Brokers). -# Replicates the same two-phase flow as a76.imports: upload → scan → waiting_confirmation → commit. diff --git a/backend/api/v1/modules/a76/customs_brokers/routes.py b/backend/api/v1/modules/a76/customs_brokers/routes.py index a94eccd1..960fb1f9 100644 --- a/backend/api/v1/modules/a76/customs_brokers/routes.py +++ b/backend/api/v1/modules/a76/customs_brokers/routes.py @@ -7,7 +7,7 @@ from core.security import get_current_user, validate_access_to_resource from api.v1.common.tenant_crud_routes import TenantCRUDRoutes from . import dto, services -from .imports.routes import router as imports_router +from ..layouts_csv.customs_brokers.routes import router as imports_router router = APIRouter() diff --git a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/routes.py b/backend/api/v1/modules/a76/general_catalogs/exchange_rate/routes.py index 09086071..4fdef285 100644 --- a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/routes.py +++ b/backend/api/v1/modules/a76/general_catalogs/exchange_rate/routes.py @@ -35,7 +35,7 @@ from fastapi import APIRouter custom_router = APIRouter(prefix="/exchange-rate", tags=[]) # CSV import: add to custom_router BEFORE including it in master, so /exchange-rate/imports/* is registered -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.exchange_rate.routes import router as imports_router custom_router.include_router(imports_router, prefix="/imports", tags=["exchange_rate / csv_import"]) @custom_router.get("/test-ping") diff --git a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/routes.py b/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/routes.py index 61907ffa..d228d085 100644 --- a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/routes.py +++ b/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/routes.py @@ -31,7 +31,7 @@ crud_router = TenantCRUDRoutes( ) # Master router with prefix so all routes live under /us-tariff-fractions -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.us_tariff_fractions.routes import router as imports_router main_router = APIRouter(prefix="/us-tariff-fractions", tags=["a76 / general catalogs / us tariff fractions"]) main_router.include_router(imports_router, prefix="/imports", tags=["us_tariff_fractions / csv_import"]) main_router.include_router(crud_router.router) diff --git a/backend/api/v1/modules/a76/layouts_csv/__init__.py b/backend/api/v1/modules/a76/layouts_csv/__init__.py new file mode 100644 index 00000000..29379c6d --- /dev/null +++ b/backend/api/v1/modules/a76/layouts_csv/__init__.py @@ -0,0 +1 @@ +# Lógica centralizada de cargas por CSV (routes, tasks, validaciones, template_config por proceso) diff --git a/backend/api/v1/modules/a76/boms/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/boms/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/boms/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/boms/__init__.py diff --git a/backend/api/v1/modules/a76/boms/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/boms/routes.py similarity index 98% rename from backend/api/v1/modules/a76/boms/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/boms/routes.py index 4a87ed0e..730878bb 100644 --- a/backend/api/v1/modules/a76/boms/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/boms/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -78,7 +79,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"bom_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/boms/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/boms/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/boms/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/boms/schemas.py diff --git a/backend/api/v1/modules/a76/boms/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/boms/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/boms/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/boms/tasks.py index ac72ad0d..ea544d8d 100644 --- a/backend/api/v1/modules/a76/boms/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/boms/tasks.py @@ -15,6 +15,7 @@ from typing import Dict, Any, Optional, List, Set from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -33,7 +34,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -162,7 +163,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"bom_{job_id}.jsonl") @@ -306,8 +307,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"bom_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/boms/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/boms/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/boms/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/boms/template_config.py diff --git a/backend/api/v1/modules/a76/classes/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/classes/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/classes/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/classes/__init__.py diff --git a/backend/api/v1/modules/a76/classes/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/classes/routes.py similarity index 98% rename from backend/api/v1/modules/a76/classes/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/classes/routes.py index 5c9c869f..0b61efb2 100644 --- a/backend/api/v1/modules/a76/classes/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/classes/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -78,7 +79,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"cls_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/classes/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/classes/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/classes/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/classes/schemas.py diff --git a/backend/api/v1/modules/a76/classes/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/classes/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/classes/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/classes/tasks.py index ca754d06..1a230911 100644 --- a/backend/api/v1/modules/a76/classes/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/classes/tasks.py @@ -13,6 +13,7 @@ from typing import Dict, Any, Optional, List, Set from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -31,7 +32,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -156,7 +157,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"cls_{job_id}.jsonl") @@ -310,8 +311,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"cls_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/classes/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/classes/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/classes/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/classes/template_config.py diff --git a/backend/api/v1/modules/a76/clients_and_providers/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/clients_and_providers/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/clients_and_providers/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/clients_and_providers/__init__.py diff --git a/backend/api/v1/modules/a76/clients_and_providers/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/clients_and_providers/routes.py similarity index 98% rename from backend/api/v1/modules/a76/clients_and_providers/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/clients_and_providers/routes.py index 68029417..6795787f 100644 --- a/backend/api/v1/modules/a76/clients_and_providers/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/clients_and_providers/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"cp_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/clients_and_providers/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/clients_and_providers/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/clients_and_providers/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/clients_and_providers/schemas.py diff --git a/backend/api/v1/modules/a76/clients_and_providers/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/clients_and_providers/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/clients_and_providers/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/clients_and_providers/tasks.py index 34fea87c..a133918c 100644 --- a/backend/api/v1/modules/a76/clients_and_providers/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/clients_and_providers/tasks.py @@ -13,6 +13,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template from api.v1.modules.a76.clients_and_providers.models import ( @@ -37,7 +38,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -165,7 +166,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"cp_{job_id}.jsonl") @@ -290,8 +291,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"cp_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/clients_and_providers/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/clients_and_providers/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/clients_and_providers/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/clients_and_providers/template_config.py diff --git a/backend/api/v1/modules/a76/layouts_csv/customs_brokers/__init__.py b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/__init__.py new file mode 100644 index 00000000..2632bd0c --- /dev/null +++ b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/__init__.py @@ -0,0 +1 @@ +# layouts_csv.customs_brokers diff --git a/backend/api/v1/modules/a76/customs_brokers/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/routes.py similarity index 98% rename from backend/api/v1/modules/a76/customs_brokers/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/customs_brokers/routes.py index 99f7c609..369208f0 100644 --- a/backend/api/v1/modules/a76/customs_brokers/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"cb_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/customs_brokers/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/customs_brokers/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/customs_brokers/schemas.py diff --git a/backend/api/v1/modules/a76/customs_brokers/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/customs_brokers/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/customs_brokers/tasks.py index ecaed98f..80a8df78 100644 --- a/backend/api/v1/modules/a76/customs_brokers/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/tasks.py @@ -13,6 +13,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -32,7 +33,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -120,7 +121,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"cb_{job_id}.jsonl") @@ -245,8 +246,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"cb_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/customs_brokers/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/customs_brokers/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/customs_brokers/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/customs_brokers/template_config.py diff --git a/backend/api/v1/modules/a76/transportation/drivers/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/drivers/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/drivers/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/drivers/__init__.py diff --git a/backend/api/v1/modules/a76/transportation/drivers/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/drivers/routes.py similarity index 98% rename from backend/api/v1/modules/a76/transportation/drivers/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/drivers/routes.py index ea5d2eb4..17e9c7c9 100644 --- a/backend/api/v1/modules/a76/transportation/drivers/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/drivers/routes.py @@ -15,6 +15,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"drv_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/transportation/drivers/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/drivers/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/drivers/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/drivers/schemas.py diff --git a/backend/api/v1/modules/a76/transportation/drivers/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/drivers/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/transportation/drivers/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/drivers/tasks.py index 9234776e..e0dfb784 100644 --- a/backend/api/v1/modules/a76/transportation/drivers/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/drivers/tasks.py @@ -13,6 +13,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -32,7 +33,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -281,7 +282,7 @@ def _do_scan(job_id: str, progress_callback: Optional[Any] = None) -> Dict[str, return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"drv_{job_id}.jsonl") @@ -429,8 +430,7 @@ def _do_commit(job_id: str) -> Dict[str, Any]: else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"drv_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/transportation/drivers/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/drivers/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/drivers/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/drivers/template_config.py diff --git a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/exchange_rate/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/exchange_rate/__init__.py diff --git a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/exchange_rate/routes.py similarity index 98% rename from backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/exchange_rate/routes.py index 75a9aef8..006eee35 100644 --- a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/exchange_rate/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"er_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/exchange_rate/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/exchange_rate/schemas.py diff --git a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/exchange_rate/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/exchange_rate/tasks.py index c214b80b..5ab2657e 100644 --- a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/exchange_rate/tasks.py @@ -15,6 +15,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -36,7 +37,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -150,7 +151,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"er_{job_id}.jsonl") @@ -274,8 +275,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"er_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/exchange_rate/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/general_catalogs/exchange_rate/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/exchange_rate/template_config.py diff --git a/backend/api/v1/modules/a76/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/facturas/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/facturas/__init__.py diff --git a/backend/api/v1/modules/a76/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/facturas/routes.py similarity index 96% rename from backend/api/v1/modules/a76/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/facturas/routes.py index f3c3d9ee..c7e9c570 100644 --- a/backend/api/v1/modules/a76/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/facturas/routes.py @@ -11,6 +11,7 @@ from typing import Optional, Literal, Dict, Any from core.celery_app import celery_app from core.config import settings from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .tasks import ( @@ -88,7 +89,7 @@ async def upload_import_file( # Optional: also write to local disk (e.g. for same-machine worker or debugging) try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) file_path = os.path.join(upload_dir, f"{job_id}.csv") meta_path = os.path.join(upload_dir, f"{job_id}.meta.json") diff --git a/backend/api/v1/modules/a76/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/facturas/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/facturas/schemas.py diff --git a/backend/api/v1/modules/a76/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/facturas/tasks.py similarity index 97% rename from backend/api/v1/modules/a76/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/facturas/tasks.py index b6e8e8d9..e136e618 100644 --- a/backend/api/v1/modules/a76/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/facturas/tasks.py @@ -11,6 +11,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template # Models are imported inside tasks to avoid circular dependencies and mapper initialization issues in the API process @@ -37,7 +38,7 @@ def _get_redis(): def _worker_upload_dir() -> str: """Directory on the worker for temp CSV and meta (same structure as API, but local to worker).""" - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -189,8 +190,9 @@ def scan_file(self, job_id: str, model_target: str, config: str = None): _ensure_worker_has_meta_from_redis(job_id, file_path) # 2. Setup Error Log - error_path = file_path.replace("temp", "errors").replace(".csv", ".jsonl") - os.makedirs(os.path.dirname(error_path), exist_ok=True) + error_dir = layout_path("imports", "errors") + os.makedirs(error_dir, exist_ok=True) + error_path = os.path.join(error_dir, f"{job_id}.jsonl") total_rows = 0 error_count = 0 @@ -805,7 +807,8 @@ def insert_valid_rows(self, job_id: str, model_target: str): from api.v1.modules.a76.items.line_descriptions.models import LineDescription from api.v1.modules.a76.parts.models import Part - error_path = file_path.replace("temp", "errors").replace(".csv", ".jsonl") + error_dir = layout_path("imports", "errors") + error_path = os.path.join(error_dir, f"{job_id}.jsonl") # 1. Load Error Line Numbers (from Redis if scan ran on another worker, else from file) error_lines = set() diff --git a/backend/api/v1/modules/a76/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/facturas/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/facturas/template_config.py diff --git a/backend/api/v1/modules/a76/parts/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/parts/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/parts/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/parts/__init__.py diff --git a/backend/api/v1/modules/a76/parts/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/parts/routes.py similarity index 98% rename from backend/api/v1/modules/a76/parts/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/parts/routes.py index bffb87ac..9f1befa4 100644 --- a/backend/api/v1/modules/a76/parts/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/parts/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -78,7 +79,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"part_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/parts/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/parts/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/parts/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/parts/schemas.py diff --git a/backend/api/v1/modules/a76/parts/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/parts/tasks.py similarity index 99% rename from backend/api/v1/modules/a76/parts/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/parts/tasks.py index c87bca7c..8311b001 100644 --- a/backend/api/v1/modules/a76/parts/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/parts/tasks.py @@ -14,6 +14,7 @@ from typing import Dict, Any, Optional, List, Set from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -32,7 +33,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -183,7 +184,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"part_{job_id}.jsonl") @@ -368,8 +369,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"part_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/parts/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/parts/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/parts/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/parts/template_config.py diff --git a/backend/api/v1/modules/a76/pedmientos/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/pedmientos/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/pedmientos/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/pedmientos/__init__.py diff --git a/backend/api/v1/modules/a76/pedmientos/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/pedmientos/routes.py similarity index 98% rename from backend/api/v1/modules/a76/pedmientos/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/pedmientos/routes.py index f624e5dc..eea706fb 100644 --- a/backend/api/v1/modules/a76/pedmientos/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/pedmientos/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"ped_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/pedmientos/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/pedmientos/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/pedmientos/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/pedmientos/schemas.py diff --git a/backend/api/v1/modules/a76/pedmientos/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/pedmientos/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/pedmientos/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/pedmientos/tasks.py index 82b630d2..8bfea495 100644 --- a/backend/api/v1/modules/a76/pedmientos/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/pedmientos/tasks.py @@ -15,6 +15,7 @@ from typing import Dict, Any, Optional, List, Set from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -34,7 +35,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -243,7 +244,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"ped_{job_id}.jsonl") @@ -381,8 +382,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"ped_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/pedmientos/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/pedmientos/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/pedmientos/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/pedmientos/template_config.py diff --git a/backend/api/v1/modules/a76/transportation/trailers/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/trailers/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/trailers/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/trailers/__init__.py diff --git a/backend/api/v1/modules/a76/transportation/trailers/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/trailers/routes.py similarity index 98% rename from backend/api/v1/modules/a76/transportation/trailers/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/trailers/routes.py index 7e51aca1..64dc86f1 100644 --- a/backend/api/v1/modules/a76/transportation/trailers/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/trailers/routes.py @@ -15,6 +15,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"trl_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/transportation/trailers/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/trailers/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/trailers/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/trailers/schemas.py diff --git a/backend/api/v1/modules/a76/transportation/trailers/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/trailers/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/transportation/trailers/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/trailers/tasks.py index c43c93cf..73c43aad 100644 --- a/backend/api/v1/modules/a76/transportation/trailers/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/trailers/tasks.py @@ -14,6 +14,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -33,7 +34,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -180,7 +181,7 @@ def _do_scan(job_id: str, progress_callback: Optional[Any] = None) -> Dict[str, return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"trl_{job_id}.jsonl") @@ -328,8 +329,7 @@ def _do_commit(job_id: str) -> Dict[str, Any]: else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"trl_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/transportation/trailers/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/trailers/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/trailers/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/trailers/template_config.py diff --git a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/__init__.py diff --git a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/routes.py similarity index 98% rename from backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/routes.py index bf5083a3..0d13c4c7 100644 --- a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/routes.py @@ -14,6 +14,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"fa_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/schemas.py diff --git a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/tasks.py index edac5df2..5d58a79c 100644 --- a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/tasks.py @@ -14,6 +14,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -34,7 +35,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -157,7 +158,7 @@ def scan_file(self, job_id: str, config: str = None): return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"fa_{job_id}.jsonl") @@ -290,8 +291,7 @@ def insert_valid_rows(self, job_id: str): else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"fa_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/general_catalogs/fractions/us_tariff_fractions/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/us_tariff_fractions/template_config.py diff --git a/backend/api/v1/modules/a76/transportation/vehicles/imports/__init__.py b/backend/api/v1/modules/a76/layouts_csv/vehicles/__init__.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/vehicles/imports/__init__.py rename to backend/api/v1/modules/a76/layouts_csv/vehicles/__init__.py diff --git a/backend/api/v1/modules/a76/transportation/vehicles/imports/routes.py b/backend/api/v1/modules/a76/layouts_csv/vehicles/routes.py similarity index 98% rename from backend/api/v1/modules/a76/transportation/vehicles/imports/routes.py rename to backend/api/v1/modules/a76/layouts_csv/vehicles/routes.py index 20960753..7e159a00 100644 --- a/backend/api/v1/modules/a76/transportation/vehicles/imports/routes.py +++ b/backend/api/v1/modules/a76/layouts_csv/vehicles/routes.py @@ -15,6 +15,7 @@ from typing import Dict, Any from core.celery_app import celery_app from core.database import get_core_db +from core.paths import layout_path from core.security import get_current_user, validate_access_to_resource from .schemas import ImportJobResponse @@ -81,7 +82,7 @@ async def upload_import_file( raise HTTPException(status_code=500, detail="No se pudo encolar el archivo.") try: - upload_dir = os.path.join(os.getcwd(), "uploads", "temp") + upload_dir = layout_path("imports", "temp") os.makedirs(upload_dir, exist_ok=True) with open(os.path.join(upload_dir, f"veh_{job_id}.csv"), "wb") as f: f.write(contents) diff --git a/backend/api/v1/modules/a76/transportation/vehicles/imports/schemas.py b/backend/api/v1/modules/a76/layouts_csv/vehicles/schemas.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/vehicles/imports/schemas.py rename to backend/api/v1/modules/a76/layouts_csv/vehicles/schemas.py diff --git a/backend/api/v1/modules/a76/transportation/vehicles/imports/tasks.py b/backend/api/v1/modules/a76/layouts_csv/vehicles/tasks.py similarity index 98% rename from backend/api/v1/modules/a76/transportation/vehicles/imports/tasks.py rename to backend/api/v1/modules/a76/layouts_csv/vehicles/tasks.py index 7e9df06c..533193e5 100644 --- a/backend/api/v1/modules/a76/transportation/vehicles/imports/tasks.py +++ b/backend/api/v1/modules/a76/layouts_csv/vehicles/tasks.py @@ -15,6 +15,7 @@ from typing import Dict, Any, Optional, List from core.celery_app import celery_app from core.database import CoreSessionLocal +from core.paths import layout_path from .template_config import row_from_template @@ -34,7 +35,7 @@ def _get_redis(): def _worker_upload_dir() -> str: - return os.path.join(os.getcwd(), "uploads", "temp") + return layout_path("imports", "temp") def _ensure_worker_has_file_from_redis(job_id: str) -> Optional[str]: @@ -213,7 +214,7 @@ def _do_scan( return {"status": "failed", "error": "Archivo no encontrado (expirado o no subido). Sube de nuevo."} _ensure_worker_has_meta_from_redis(job_id, file_path) - error_dir = os.path.join(os.path.dirname(file_path).replace("temp", "errors"), "") + error_dir = layout_path("imports", "errors") os.makedirs(error_dir, exist_ok=True) error_path = os.path.join(error_dir, f"veh_{job_id}.jsonl") @@ -404,8 +405,7 @@ def _do_commit(job_id: str) -> Dict[str, Any]: else: _ensure_worker_has_meta_from_redis(job_id, file_path) - base_dir = os.path.dirname(file_path) - error_dir = base_dir.replace("temp", "errors") + error_dir = layout_path("imports", "errors") error_path = os.path.join(error_dir, f"veh_{job_id}.jsonl") error_lines = set() diff --git a/backend/api/v1/modules/a76/transportation/vehicles/imports/template_config.py b/backend/api/v1/modules/a76/layouts_csv/vehicles/template_config.py similarity index 100% rename from backend/api/v1/modules/a76/transportation/vehicles/imports/template_config.py rename to backend/api/v1/modules/a76/layouts_csv/vehicles/template_config.py diff --git a/backend/api/v1/modules/a76/parts/routes.py b/backend/api/v1/modules/a76/parts/routes.py index 17bf937a..c68c19b3 100644 --- a/backend/api/v1/modules/a76/parts/routes.py +++ b/backend/api/v1/modules/a76/parts/routes.py @@ -8,7 +8,7 @@ from api.v1.common.tenant_crud_routes import TenantCRUDRoutes from .dto import PartCreateDTO, PartResponseDTO, PartUpdateDTO from .service import PartService -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.parts.routes import router as imports_router router = APIRouter() diff --git a/backend/api/v1/modules/a76/pedmientos/router.py b/backend/api/v1/modules/a76/pedmientos/router.py index 2b987b46..b9444c04 100644 --- a/backend/api/v1/modules/a76/pedmientos/router.py +++ b/backend/api/v1/modules/a76/pedmientos/router.py @@ -31,7 +31,7 @@ from .routes.pedimento_rectification_origin import ( from .routes.pedimento_transport_means import router as pedimento_transport_means_router from .routes.pedimento_validation import router as pedimento_validation_router from .routes.pedimentos import router as pedimentos_router -from .imports.routes import router as pedimentos_imports_router +from api.v1.modules.a76.layouts_csv.pedmientos.routes import router as pedimentos_imports_router router = APIRouter() diff --git a/backend/api/v1/modules/a76/router.py b/backend/api/v1/modules/a76/router.py index e30a47df..2098bb1a 100644 --- a/backend/api/v1/modules/a76/router.py +++ b/backend/api/v1/modules/a76/router.py @@ -14,7 +14,7 @@ from .items.routes import router as items_router from .classes import router as classes_router from .clients_and_providers import router as client_and_provider_router -from .imports.routes import router as imports_router +from .layouts_csv.facturas.routes import router as imports_router from .csv_templates.routes import router as csv_templates_router from .invoice_settings.routes import router as invoice_settings_router from .item_presets.routes import router as item_presets_router diff --git a/backend/api/v1/modules/a76/transportation/drivers/routes.py b/backend/api/v1/modules/a76/transportation/drivers/routes.py index a07b5c55..eb0d61a7 100644 --- a/backend/api/v1/modules/a76/transportation/drivers/routes.py +++ b/backend/api/v1/modules/a76/transportation/drivers/routes.py @@ -8,7 +8,7 @@ from sqlalchemy.orm import Session from .dto import DriverCreateDTO, DriverResponseDTO from .models import Driver from .services import DriverService -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.drivers.routes import router as imports_router router = APIRouter(prefix="/drivers") diff --git a/backend/api/v1/modules/a76/transportation/trailers/routes.py b/backend/api/v1/modules/a76/transportation/trailers/routes.py index 4dcea2e3..81c7fbb7 100644 --- a/backend/api/v1/modules/a76/transportation/trailers/routes.py +++ b/backend/api/v1/modules/a76/transportation/trailers/routes.py @@ -4,7 +4,7 @@ from api.v1.common.tenant_crud_routes import TenantCRUDRoutes from .dto import TrailerCreateDTO, TrailerResponseDTO, TrailerUpdateDTO from .services import TrailerService -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.trailers.routes import router as imports_router # Main router: trailers CRUD + CSV imports router = APIRouter() diff --git a/backend/api/v1/modules/a76/transportation/vehicles/routes.py b/backend/api/v1/modules/a76/transportation/vehicles/routes.py index ce823259..1df5b21c 100644 --- a/backend/api/v1/modules/a76/transportation/vehicles/routes.py +++ b/backend/api/v1/modules/a76/transportation/vehicles/routes.py @@ -4,7 +4,7 @@ from api.v1.common.tenant_crud_routes import TenantCRUDRoutes from .dto import VehicleCreateDTO, VehicleResponseDTO, VehicleUpdateDTO from .services import VehicleService -from .imports.routes import router as imports_router +from api.v1.modules.a76.layouts_csv.vehicles.routes import router as imports_router # Main router: vehicles CRUD + CSV imports router = APIRouter() diff --git a/backend/celerybeat-schedule b/backend/celerybeat-schedule deleted file mode 100644 index 8cda996d90f19534ef5b500c264fc536e50391cb..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 zcmeI(O=}ZD7zgl)i6*fP6eB1KB3^8etZ&$Zcu;}|S*&0M!9$nLW@?6Rc4c=~Odybx zT9|{-DTp7yv-kx(d-4N#^5CUVKY^gnbhb8!;!Dt?_8*vip54vNPJWqvNCJ8H;%=SI zFt*t!m{`r@OB1h}4P&2ez!m}!fB*y_009U<00Izz00bZafqzlpWIky`3jqf>7y+yU ztOKkA+y|%wr~{}2r~{}2r~{}2s00659VptnnoZ}CowLd1f{ly$e!XV*H!f^F@z-OC zx_qI>PCaqls=xsbO7Lk^YgZUnZi7M2ZZNjo^M{=IVuN?oyqIf zJ{sl}wZF~Nyyt?aqtZ}2=JRioia7GTQE6@SUjQ^?wf~ZH(V5eZ9MI7tmV>GKwH{AI z;Gez8ABiCLT0-`hDQEUDm47@g~$dgqty7R))*T&8C7*{Aum=|YvQoJQ;(P*pR>4iBYI`z~{@xa str: + """Construye una ruta absoluta bajo backend/layouts/.""" + p = BASE_DIR / "layouts" + for part in parts: + p = p / part + return str(p) diff --git a/backend/main.py b/backend/main.py index b50f92a0..5a7887cf 100644 --- a/backend/main.py +++ b/backend/main.py @@ -89,6 +89,7 @@ import core.celery_app # Initialize Celery App from api.v1.router import router as api_v1_router from core.config import settings from core.database import init_db +from core.paths import layout_path from core.error_handlers import register_exception_handlers from core.middleware import ( LicenseValidationMiddleware, @@ -359,6 +360,9 @@ uploads_dir = Path("uploads").resolve() uploads_dir.mkdir(parents=True, exist_ok=True) app.mount("/api/uploads", StaticFiles(directory=str(uploads_dir)), name="uploads") +# Directorios para importación CSV (layouts: temp y errors) +Path(layout_path("imports", "temp")).mkdir(parents=True, exist_ok=True) +Path(layout_path("imports", "errors")).mkdir(parents=True, exist_ok=True) # Registrar routers app.include_router(api_v1_router, prefix="/api/v1")