fix/catalogos-fijos
This commit is contained in:
@@ -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']}%")
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<SectorListResponse>(`/v1/a76/sectors/?${params.toString()}`);
|
||||
},
|
||||
|
||||
|
||||
@@ -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<Sector>[] {
|
||||
});
|
||||
return renderSnippet(authSnippet, { authorized: row.original.authorized });
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "actions",
|
||||
cell: ({ row }) => {
|
||||
return renderComponent(DataTableActions, { item: row.original, onSuccess });
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script lang="ts">
|
||||
import EllipsisIcon from "@lucide/svelte/icons/ellipsis";
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
||||
import type { Sector } from "./columns.js";
|
||||
@@ -30,27 +31,34 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="ghost" size="icon" class="relative size-8 p-0">
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<EllipsisIcon class="size-4" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
<DropdownMenu.Item onclick={handleCopyId}>
|
||||
Copiar Clave
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onclick={handleViewDetails}>Ver detalles</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleEdit}>Editar</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
<div class="flex items-center gap-2 justify-end">
|
||||
<Button variant="ghost" size="icon" class="relative size-8 p-0" onclick={handleEdit} title="Editar">
|
||||
<span class="sr-only">Editar</span>
|
||||
<PencilIcon class="size-4" />
|
||||
</Button>
|
||||
|
||||
<DropdownMenu.Root>
|
||||
<DropdownMenu.Trigger>
|
||||
{#snippet child({ props })}
|
||||
<Button {...props} variant="ghost" size="icon" class="relative size-8 p-0">
|
||||
<span class="sr-only">Abrir menú</span>
|
||||
<EllipsisIcon class="size-4" />
|
||||
</Button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content align="end">
|
||||
<DropdownMenu.Group>
|
||||
<DropdownMenu.Label>Acciones</DropdownMenu.Label>
|
||||
<DropdownMenu.Item onclick={handleCopyId}>
|
||||
Copiar Clave
|
||||
</DropdownMenu.Item>
|
||||
</DropdownMenu.Group>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onclick={handleViewDetails}>Ver detalles</DropdownMenu.Item>
|
||||
<DropdownMenu.Item onclick={handleEdit}>Editar</DropdownMenu.Item>
|
||||
</DropdownMenu.Content>
|
||||
</DropdownMenu.Root>
|
||||
</div>
|
||||
|
||||
<!-- Dialogs -->
|
||||
<DetailsDialog bind:open={showDetailsDialog} {item} />
|
||||
|
||||
@@ -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<Sector[]>(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<string | null>(data.error || null);
|
||||
|
||||
let createDialogOpen = $state(false);
|
||||
let editingItem = $state<Sector | null>(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
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<Button class="h-9" onclick={() => { editingItem = null; createDialogOpen = true; }}>
|
||||
<Plus class="mr-2 h-4 w-4" />
|
||||
Nuevo Sector
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{#if error}
|
||||
@@ -152,10 +203,55 @@
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<Card.Root class="border bg-background flex min-h-0 flex-1 flex-col">
|
||||
<Card.Header><div class="flex flex-wrap items-center justify-between gap-3"><Card.Title>Listado de Sectores</Card.Title><div class="flex flex-wrap items-center gap-2"><Input placeholder="Buscar" bind:value={searchQuery} oninput={handleSearch} class="h-9 w-44 bg-card lg:w-64" /></div></div></Card.Header>
|
||||
<Card.Content class="min-h-0 p-0"><div class="rounded-md border bg-background overflow-hidden flex min-h-0 flex-1 flex-col"><DataTable data={allItems} {columns} {loading} {hasMore} {loadMore} /></div></Card.Content>
|
||||
<Card.Root class="border bg-background flex flex-col min-h-0 flex-1">
|
||||
<Card.Header>
|
||||
<div class="flex flex-wrap items-center justify-between gap-3">
|
||||
<Card.Title>Listado de Sectores</Card.Title>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<Input placeholder="Buscar" bind:value={searchQuery} oninput={handleSearch} class="h-9 w-44 bg-card lg:w-64" />
|
||||
</div>
|
||||
</div>
|
||||
</Card.Header>
|
||||
<Card.Content class="min-h-0 p-0 flex-1 flex flex-col">
|
||||
<div class="rounded-md border bg-background overflow-hidden flex min-h-0 flex-1 flex-col">
|
||||
<InfiniteDataTable
|
||||
data={allItems}
|
||||
{columns}
|
||||
{loading}
|
||||
{hasMore}
|
||||
{loadMore}
|
||||
{selectedIds}
|
||||
onSelectedIdsChange={(ids) => (selectedIds = ids)}
|
||||
onRowClick={handleRowClick}
|
||||
onRowDoubleClick={handleRowDoubleClick}
|
||||
/>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<div class="flex-none text-sm text-muted-foreground">Mostrando {allItems.length} de {totalItems} registros</div>
|
||||
|
||||
<div class="h-20 flex-none"></div>
|
||||
|
||||
<!-- Footer fijo con botones de acción -->
|
||||
<div
|
||||
id="sectors-list-footer"
|
||||
class="fixed right-0 bottom-0 left-0 z-50 ml-[calc(var(--sidebar-width))] border-t bg-background/95 shadow-lg backdrop-blur group-has-data-[state=collapsed]/sidebar-wrapper:ml-[calc(var(--sidebar-width-icon))] supports-[backdrop-filter]:bg-background/80"
|
||||
>
|
||||
<div class="mx-auto max-w-[1400px] px-4 py-4">
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onclick={handleEditSelected}
|
||||
disabled={selectedIds.length !== 1}
|
||||
>
|
||||
<Pencil size={16} class="mr-2" />
|
||||
Editar
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<CreateEditDialog bind:open={createDialogOpen} item={editingItem} onSuccess={handleSuccess} />
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user