diff --git a/backend/api/v1/modules/a76/general_catalogs/sectors/service.py b/backend/api/v1/modules/a76/general_catalogs/sectors/service.py index 6d1ec4ee..fea918cd 100644 --- a/backend/api/v1/modules/a76/general_catalogs/sectors/service.py +++ b/backend/api/v1/modules/a76/general_catalogs/sectors/service.py @@ -8,6 +8,7 @@ from typing import Any, Dict, List, Optional, Tuple from fastapi import HTTPException from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session +from sqlalchemy import or_ from . import dto, models @@ -33,6 +34,14 @@ class SectorService: ) if filters: + if filters.get("search"): + search_term = f"%{filters['search']}%" + query = query.filter( + or_( + models.Sector.key.ilike(search_term), + models.Sector.description.ilike(search_term) + ) + ) if filters.get("key"): query = query.filter( models.Sector.key.ilike(f"%{filters['key']}%") 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 40bb035f..89cb4220 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 @@ -18,12 +18,25 @@ async def list_agency_tariff_codes( page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"), agency: str = Query(None, description="Filtrar por código de agencia"), q: str = Query(None, description="Búsqueda general"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(AgencyTariffCode) + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + AgencyTariffCode.tariff_flag_code.ilike(search_filter), + AgencyTariffCode.agency_code.ilike(search_filter), + AgencyTariffCode.requirement_level.ilike(search_filter), + AgencyTariffCode.program_code.ilike(search_filter), + AgencyTariffCode.definition.ilike(search_filter) + ) + ) + if agency: query = query.filter(AgencyTariffCode.agency_code == agency) 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 28429a4f..38a2f213 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 @@ -18,12 +18,22 @@ async def list_carta_porte( page: int = Query(1, ge=1), page_size: int = Query(50, ge=1, le=100), q: str = Query(None, description="Buscar por código o descripción"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(CartaPorte) + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + CartaPorte.code.ilike(search_filter), + CartaPorte.description.ilike(search_filter) + ) + ) + if q: search_filter = f"%{q}%" query = query.filter( 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 6d421350..03f5efd7 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import CodePedimentoRegimenDTO from .models import CodePedimentoRegimen @@ -18,11 +19,20 @@ def list_code_pedimento_regimens( code: str = Query(None, description="Filter by code"), regime: str = Query(None, description="Filter by regime"), type: str = Query(None, description="Filter by type"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(CodePedimentoRegimen) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + CodePedimentoRegimen.pedimento_code.ilike(search_filter) + ) + ) if code is not None: query = query.filter(CodePedimentoRegimen.pedimento_code == code) 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 ea0f18e1..7182f848 100644 --- a/backend/api/v1/modules/public/reference_data/containers/routes.py +++ b/backend/api/v1/modules/public/reference_data/containers/routes.py @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import ContainerDTO from .models import Container @@ -15,11 +16,21 @@ router = APIRouter(prefix="/containers") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(Container) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + Container.key.ilike(search_filter), + Container.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 988bf086..6d860ba3 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import CurrencyTypeDTO from .models import CurrencyType @@ -15,11 +16,22 @@ router = APIRouter(prefix="/currency-types") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(CurrencyType) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + CurrencyType.code.ilike(search_filter), + CurrencyType.currency_name.ilike(search_filter), + CurrencyType.country_description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 62ceac7c..767462d3 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import CustomsSectionDTO from .models import CustomsSection @@ -15,11 +16,21 @@ router = APIRouter(prefix="/customs-sections") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(CustomsSection) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + CustomsSection.code.ilike(search_filter), + CustomsSection.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 802e1c9c..03947cd0 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import CustomsWarehouseDTO from .models import CustomsWarehouse @@ -15,11 +16,22 @@ router = APIRouter(prefix="/customs-warehouses") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(CustomsWarehouse) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + CustomsWarehouse.key.ilike(search_filter), + CustomsWarehouse.customs.ilike(search_filter), + CustomsWarehouse.fiscalized_warehouse.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 9f8c012c..b2263881 100644 --- a/backend/api/v1/modules/public/reference_data/identifiers/routes.py +++ b/backend/api/v1/modules/public/reference_data/identifiers/routes.py @@ -18,12 +18,24 @@ async def list_identifiers( page_size: int = Query(50, ge=1, le=1000, description="Tamaño de página"), level: str = Query(None, description="Filtrar por nivel (G o P)"), q: str = Query(None, description="Búsqueda general"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(IdentifierCatalog) + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + IdentifierCatalog.key.ilike(search_filter), + IdentifierCatalog.description.ilike(search_filter), + IdentifierCatalog.level.ilike(search_filter), + IdentifierCatalog.complement.ilike(search_filter) + ) + ) + if level: query = query.filter(IdentifierCatalog.nivel == level) 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 261cfddc..6661e334 100644 --- a/backend/api/v1/modules/public/reference_data/incoterms/routes.py +++ b/backend/api/v1/modules/public/reference_data/incoterms/routes.py @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import IncotermDTO from .models import Incoterm @@ -17,12 +18,23 @@ async def list_incoterms( page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"), code: str = Query(None, description="Filtrar por clave"), description: str = Query(None, description="Filtrar por descripción"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(Incoterm) + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + Incoterm.code.ilike(search_filter), + Incoterm.description_es.ilike(search_filter), + Incoterm.description_en.ilike(search_filter) + ) + ) + if code: query = query.filter(Incoterm.code.ilike(f"%{code}%")) if description: 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 e0ce0866..c2b34297 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import InvoiceTypeDTO from .models import InvoiceType @@ -17,9 +18,22 @@ def list_invoice_types( page_size: int = Query(50, ge=1, le=100), type: Optional[str] = Query(None, description="Filter by type"), operation: Optional[str] = Query(None, description="Filter by operation type (imp, exp, both)"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), ): query = db.query(InvoiceType) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + InvoiceType.key.ilike(search_filter), + InvoiceType.description.ilike(search_filter), + InvoiceType.note.ilike(search_filter), + InvoiceType.type.ilike(search_filter), + InvoiceType.operation.ilike(search_filter) + ) + ) # Filter by operation if provided if operation: 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 0c41fffa..c98a2eee 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 @@ -17,12 +17,22 @@ 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"), q: str = Query(None, description="Búsqueda general"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(LicenseException) + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + LicenseException.key.ilike(search_filter), + LicenseException.description.ilike(search_filter) + ) + ) + if q: search_term = f"%{q}%" query = query.filter( 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 159778bf..b3982a93 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import MaterialTypeDTO from .models import MaterialType @@ -16,12 +17,23 @@ 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"), type: str = Query(None, description="Filtrar por tipo (ACTIVO FIJO, MATERIALES, PRODUCTOS)"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(MaterialType) + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + MaterialType.key.ilike(search_filter), + MaterialType.type.ilike(search_filter), + MaterialType.description.ilike(search_filter) + ) + ) + # Aplicar filtro por tipo si se proporciona if type: query = query.filter(MaterialType.type == type) 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 465630fc..3e728f4b 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import PaymentMethodDTO from .models import PaymentMethod @@ -15,11 +16,21 @@ router = APIRouter(prefix="/payment-methods") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(PaymentMethod) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + PaymentMethod.key.ilike(search_filter), + PaymentMethod.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 93b188f6..0d7aa39e 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import PedimentoCodeDTO from .models import PedimentoCode @@ -15,11 +16,21 @@ router = APIRouter(prefix="/pedimento-codes") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(PedimentoCode) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + PedimentoCode.code.ilike(search_filter), + PedimentoCode.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 d788510d..678fa194 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import RegimenPedimentoDTO from .models import RegimenPedimento @@ -15,11 +16,21 @@ router = APIRouter(prefix="/pedimento-regimens") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(RegimenPedimento) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + RegimenPedimento.code.ilike(search_filter), + RegimenPedimento.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 a8667238..e3a0db27 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import PedimentoTransportCatalogDTO from .models import PedimentoTransportCatalog @@ -15,10 +16,22 @@ router = APIRouter(prefix="/pedimento-transport-catalog") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), ): skip = (page - 1) * page_size - query = db.query(PedimentoTransportCatalog).order_by(PedimentoTransportCatalog.code.asc()) + query = db.query(PedimentoTransportCatalog) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + PedimentoTransportCatalog.code.ilike(search_filter), + PedimentoTransportCatalog.transport_en.ilike(search_filter), + PedimentoTransportCatalog.transport_es.ilike(search_filter), + PedimentoTransportCatalog.payment_date_code.ilike(search_filter) + ) + ).order_by(PedimentoTransportCatalog.code.asc()) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 151c1172..52d09f53 100644 --- a/backend/api/v1/modules/public/reference_data/states/routes.py +++ b/backend/api/v1/modules/public/reference_data/states/routes.py @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import StateDTO from .models import State @@ -15,11 +16,21 @@ router = APIRouter(prefix="/states") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(State) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + State.m3_key.ilike(search_filter), + State.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 5e0ba35e..e74ab634 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 @@ -13,10 +13,11 @@ router = APIRouter() 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), ): skip = (page - 1) * page_size - items, total = services.TrailerTypeService.list_trailer_types(db, skip, page_size) + items, total = services.TrailerTypeService.list_trailer_types(db, skip, page_size, search) return { "items": [dto.TrailerTypeListItemDTO.model_validate(obj) for obj in items], "total": total, diff --git a/backend/api/v1/modules/public/reference_data/trailer_types/services.py b/backend/api/v1/modules/public/reference_data/trailer_types/services.py index 577637e5..d36f3e3d 100644 --- a/backend/api/v1/modules/public/reference_data/trailer_types/services.py +++ b/backend/api/v1/modules/public/reference_data/trailer_types/services.py @@ -1,6 +1,7 @@ from typing import List, Tuple from sqlalchemy.orm import Session +from sqlalchemy import or_ from . import dto, models @@ -8,9 +9,19 @@ from . import dto, models class TrailerTypeService: @staticmethod def list_trailer_types( - db: Session, skip: int = 0, limit: int = 50 + db: Session, skip: int = 0, limit: int = 50, search: str = None ) -> Tuple[List[models.TrailerType], int]: q = db.query(models.TrailerType).order_by(models.TrailerType.trailer_type_key) + + if search: + search_filter = f"%{search}%" + q = q.filter( + or_( + models.TrailerType.trailer_type_key.ilike(search_filter), + models.TrailerType.description.ilike(search_filter) + ) + ) + total = q.count() items = q.offset(skip).limit(limit).all() return items, total 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 25a22036..cfe4405d 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import TransportModeDTO from .models import TransportMode @@ -15,10 +16,20 @@ router = APIRouter(prefix="/transport-modes") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), ): skip = (page - 1) * page_size query = db.query(TransportMode) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + TransportMode.key.ilike(search_filter), + TransportMode.name.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 69bf624e..563d5e0b 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import TransportTypeDTO from .models import TransportType @@ -15,10 +16,20 @@ router = APIRouter(prefix="/transport-types") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), ): skip = (page - 1) * page_size query = db.query(TransportType) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + TransportType.transport_code.ilike(search_filter), + TransportType.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { 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 4742609d..e0f7c65d 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 @@ -4,6 +4,7 @@ from core.database import get_core_db from core.security import get_current_user, has_role from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session +from sqlalchemy import or_ from .dto import ValuationMethodDTO from .models import ValuationMethod @@ -15,11 +16,21 @@ router = APIRouter(prefix="/valuation-methods") 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"), + search: str = Query(None, description="Término de búsqueda"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(ValuationMethod) + + if search: + search_filter = f"%{search}%" + query = query.filter( + or_( + ValuationMethod.key.ilike(search_filter), + ValuationMethod.description.ilike(search_filter) + ) + ) items = query.offset(skip).limit(page_size).all() total = query.count() return { diff --git a/frontend/src/lib/api/dashboard/reference_data/sectors.ts b/frontend/src/lib/api/dashboard/reference_data/sectors.ts index d36a26eb..43b9c72b 100644 --- a/frontend/src/lib/api/dashboard/reference_data/sectors.ts +++ b/frontend/src/lib/api/dashboard/reference_data/sectors.ts @@ -40,7 +40,7 @@ export const sectorsApi = { page_size: pageSize.toString(), company_id: companyId.toString() }); - if (search) params.append('key', search); + if (search) params.append('search', search); return api.get(`/v1/a76/sectors/?${params.toString()}`); }, diff --git a/frontend/src/lib/components/dashboard/reference_data/sectors/columns.ts b/frontend/src/lib/components/dashboard/reference_data/sectors/columns.ts index 9392d3aa..ba15fc57 100644 --- a/frontend/src/lib/components/dashboard/reference_data/sectors/columns.ts +++ b/frontend/src/lib/components/dashboard/reference_data/sectors/columns.ts @@ -1,7 +1,6 @@ import type { ColumnDef } from "@tanstack/table-core"; -import { renderComponent, renderSnippet } from "$lib/components/ui/data-table/index.js"; +import { renderSnippet } from "$lib/components/ui/data-table/index.js"; import { createRawSnippet } from "svelte"; -import DataTableActions from "./data-table-actions.svelte"; export type Sector = { id: number; @@ -56,12 +55,6 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { }); return renderSnippet(authSnippet, { authorized: row.original.authorized }); } - }, - { - id: "actions", - cell: ({ row }) => { - return renderComponent(DataTableActions, { item: row.original, onSuccess }); - } } ]; } diff --git a/frontend/src/lib/components/dashboard/reference_data/sectors/data-table-actions.svelte b/frontend/src/lib/components/dashboard/reference_data/sectors/data-table-actions.svelte index bf90e548..49013bc0 100644 --- a/frontend/src/lib/components/dashboard/reference_data/sectors/data-table-actions.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/sectors/data-table-actions.svelte @@ -1,5 +1,6 @@ - - - {#snippet child({ props })} - - {/snippet} - - - - Acciones - - Copiar Clave - - - - Ver detalles - Editar - - +
+ + + + + {#snippet child({ props })} + + {/snippet} + + + + Acciones + + Copiar Clave + + + + Ver detalles + Editar + + +
diff --git a/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte b/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte index 8d722ad7..c155863d 100644 --- a/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte @@ -2,14 +2,15 @@ import { onMount } from 'svelte'; import { sectorsApi, type Sector } from '$lib/api/dashboard/reference_data/sectors'; import { companyStore } from '$lib/stores/company.svelte'; - import DataTable from '$lib/components/dashboard/reference_data/sectors/data-table.svelte'; + import InfiniteDataTable from '$lib/components/dashboard/common/infinite-data-table.svelte'; + import CreateEditDialog from '$lib/components/dashboard/reference_data/sectors/create-edit-dialog.svelte'; import { createColumns } from '$lib/components/dashboard/reference_data/sectors/columns.js'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; import { Input } from '$lib/components/ui/input'; import { page } from '$app/stores'; import { browser } from '$app/environment'; - import { RefreshCw } from 'lucide-svelte'; + import { RefreshCw, Plus, Pencil } from 'lucide-svelte'; import { useShortcuts } from '$lib/hooks/use-shortcuts'; import type { PageData } from './$types'; @@ -31,7 +32,7 @@ } }); - // Estado para infinite scroll + // Estado para infinite scroll y seleccion let allItems = $state(data.items || []); let currentPage = $state(data.page || 1); let pageSize = $state(50); @@ -39,6 +40,13 @@ let loading = $state(false); let hasMore = $derived(allItems.length < totalItems); let error = $state(data.error || null); + + let createDialogOpen = $state(false); + let editingItem = $state(null); + let selectedIds = $state<(string | number)[]>([]); + const selectedSector = $derived( + selectedIds.length === 1 ? allItems.find((s) => s.id === selectedIds[0]) : null + ); // Filtros let searchQuery = $state($page.url.searchParams.get('search') || ''); @@ -123,11 +131,48 @@ } function handleSuccess() { + createDialogOpen = false; + editingItem = null; reloadData(); } + function handleRowDoubleClick(row: Sector) { + editingItem = row; + createDialogOpen = true; + } + + function handleRowClick(row: Sector) { + const id = row.id; + if (selectedIds.includes(id)) { + selectedIds = selectedIds.filter((i) => i !== id); + } else { + selectedIds = [id]; // Selección única + } + } + + function handleEditSelected() { + if (selectedSector) { + editingItem = selectedSector; + createDialogOpen = true; + } + } + + $effect(() => { + if (!createDialogOpen) { + editingItem = null; + } + }); + useShortcuts('Sectores', [ - { key: 'Alt+Shift+R', description: 'Actualizar Lista', action: reloadData } + { key: 'Alt+Shift+R', description: 'Actualizar Lista', action: reloadData }, + { + key: 'Ctrl+S', + description: 'Guardar', + action: () => { + if (!createDialogOpen) return; + document.dispatchEvent(new CustomEvent('save-form')); + } + } ]); const columns = createColumns(handleSuccess); @@ -144,6 +189,12 @@ Gestiona los sectores económicos del sistema

+
+ +
{#if error} @@ -152,10 +203,55 @@ {/if} - -
Listado de Sectores
-
+ + +
+ Listado de Sectores +
+ +
+
+
+ +
+ (selectedIds = ids)} + onRowClick={handleRowClick} + onRowDoubleClick={handleRowDoubleClick} + /> +
+
Mostrando {allItems.length} de {totalItems} registros
+ +
+ + + + +