diff --git a/backend/alembic/versions/add_timestamps_to_classes.py b/backend/alembic/versions/add_timestamps_to_classes.py deleted file mode 100644 index a2b4f554..00000000 --- a/backend/alembic/versions/add_timestamps_to_classes.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Add timestamp fields to classes table - -Revision ID: add_timestamps_classes -Revises: 7937209f9718 -Create Date: 2025-11-16 00:20:00.000000 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'add_timestamps_classes' -down_revision: Union[str, None] = '7937209f9718' -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - # Add created_at column with default value - op.execute(""" - ALTER TABLE a76.classes - ADD COLUMN created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL - """) - - # Add updated_at column with default value - op.execute(""" - ALTER TABLE a76.classes - ADD COLUMN updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL - """) - - # Add deleted_at column (nullable for soft deletes) - op.execute(""" - ALTER TABLE a76.classes - ADD COLUMN deleted_at TIMESTAMP WITH TIME ZONE - """) - - # Create trigger to auto-update updated_at - op.execute(""" - CREATE OR REPLACE FUNCTION a76.update_classes_updated_at() - RETURNS TRIGGER AS $$ - BEGIN - NEW.updated_at = CURRENT_TIMESTAMP; - RETURN NEW; - END; - $$ language 'plpgsql'; - """) - - op.execute(""" - CREATE TRIGGER update_classes_updated_at - BEFORE UPDATE ON a76.classes - FOR EACH ROW - EXECUTE FUNCTION a76.update_classes_updated_at(); - """) - - -def downgrade() -> None: - # Drop trigger and function - op.execute("DROP TRIGGER IF EXISTS update_classes_updated_at ON a76.classes") - op.execute("DROP FUNCTION IF EXISTS a76.update_classes_updated_at()") - - # Drop columns - op.execute("ALTER TABLE a76.classes DROP COLUMN IF EXISTS deleted_at") - op.execute("ALTER TABLE a76.classes DROP COLUMN IF EXISTS updated_at") - op.execute("ALTER TABLE a76.classes DROP COLUMN IF EXISTS created_at") diff --git a/backend/api/v1/modules/a76/clients_and_providers/dto.py b/backend/api/v1/modules/a76/clients_and_providers/dto.py index 10fa8858..f77df843 100644 --- a/backend/api/v1/modules/a76/clients_and_providers/dto.py +++ b/backend/api/v1/modules/a76/clients_and_providers/dto.py @@ -4,7 +4,7 @@ Reemplaza schemas.py siguiendo enfoque DDD y estilo NestJS """ from decimal import Decimal -from typing import Literal, Optional +from typing import List, Literal, Optional from pydantic import BaseModel, Field @@ -119,7 +119,7 @@ class ClientProviderCreateDTO(BaseModel): is_national_provider: Optional[str] = Field( None, max_length=2, description="Is national provider" ) - enabled_disabled: Optional[int] = Field(None, description="Enabled/Disabled status") + is_active: Optional[bool] = Field(None, description="Enabled/Disabled status") # Nested DTOs address: Optional[ClientProviderAddressDTO] = Field( @@ -162,7 +162,7 @@ class ClientProviderUpdateDTO(BaseModel): is_national_provider: Optional[str] = Field( None, max_length=2, description="Is national provider" ) - enabled_disabled: Optional[int] = Field(None, description="Enabled/Disabled status") + is_active: Optional[bool] = Field(None, description="Enabled/Disabled status") # Nested DTOs address: Optional[ClientProviderAddressDTO] = Field( @@ -194,7 +194,7 @@ class ClientProviderResponseDTO(BaseModel): position: Optional[str] = None incoterm: Optional[str] = None is_national_provider: Optional[str] = None - enabled_disabled: Optional[int] = None + is_active: Optional[bool] = None tenant_id: int company_id: int @@ -215,7 +215,7 @@ class ClientProviderBasicDTO(BaseModel): short_name: Optional[str] = None rfc: Optional[str] = None client_or_provider: Optional[str] = None - enabled_disabled: Optional[int] = None + is_active: Optional[bool] = None class Config: from_attributes = True @@ -231,3 +231,15 @@ class ClientProviderListDTO(BaseModel): class Config: from_attributes = True + + +class ClientProviderPaginatedResponseDTO(BaseModel): + """DTO para respuesta paginada de clientes/proveedores""" + + items: List[ClientProviderResponseDTO] + total: int + page: int + page_size: int + + class Config: + from_attributes = True diff --git a/backend/api/v1/modules/a76/clients_and_providers/models.py b/backend/api/v1/modules/a76/clients_and_providers/models.py index da2a02eb..ea9a7f7d 100644 --- a/backend/api/v1/modules/a76/clients_and_providers/models.py +++ b/backend/api/v1/modules/a76/clients_and_providers/models.py @@ -15,7 +15,8 @@ from sqlalchemy import ( PrimaryKeyConstraint, SmallInteger, String, - Enum as PgEnum + Enum as PgEnum, + Boolean ) from sqlalchemy.orm import Mapped, mapped_column, relationship from enum import Enum @@ -60,8 +61,8 @@ class ClientProvider(Base, TenantScopedMixin): responsible: Mapped[Optional[str]] = mapped_column(String(80)) position: Mapped[Optional[str]] = mapped_column(String(30)) incoterm: Mapped[Optional[str]] = mapped_column(String(19)) - is_national_provider: Mapped[Optional[str]] = mapped_column(String(2)) - enabled_disabled: Mapped[Optional[int]] = mapped_column(SmallInteger) + is_national_provider: Mapped[Optional[bool]] = mapped_column(Boolean) + is_active: Mapped[Optional[bool]] = mapped_column(Boolean) # Relationships address: Mapped[Optional["ClientProviderAddress"]] = relationship( 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 68e74d13..970b71ad 100644 --- a/backend/api/v1/modules/a76/clients_and_providers/routes.py +++ b/backend/api/v1/modules/a76/clients_and_providers/routes.py @@ -9,136 +9,57 @@ from core.security import get_current_user, validate_access_to_resource from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy.orm import Session from api.v1.common.tenant_crud_routes import TenantCRUDRoutes +from .models import ClientOrProviderEnum from .dto import ( ClientProviderBasicDTO, ClientProviderCreateDTO, ClientProviderResponseDTO, ClientProviderUpdateDTO, + ClientProviderPaginatedResponseDTO, ) from .service import ClientProviderService from .models import ClientProvider -# Create base CRUD router using TenantCRUDRoutes factory -base_router = TenantCRUDRoutes( - service=ClientProviderService, - create_schema=ClientProviderCreateDTO, - update_schema=ClientProviderUpdateDTO, - response_schema=ClientProviderResponseDTO, - prefix="", - tags=[], - resource_name="Client/Provider", - id_name="id", # Using numeric ID - enable_list=True, # Enable GET /clients-providers with pagination - enable_filters=True, # Enable filtering - default_page_size=50, - max_page_size=100, -).router - # Create main router to add custom endpoints router = APIRouter(prefix="/clients-providers") -# Include base CRUD routes -router.include_router(base_router, prefix="") - -# Custom endpoints -@router.get("/clients", response_model=List[ClientProviderBasicDTO]) -async def get_clients_only( +@router.get("/", response_model=ClientProviderPaginatedResponseDTO) +async def get_clients_and_providers( company_id: int = Query(..., description="Company ID"), + type: Optional[ClientOrProviderEnum] = Query( + None, description="Type of entity (client or provider)" + ), + active: Optional[bool] = Query(None, description="Active status"), skip: int = Query(0, ge=0), limit: int = Query(100, ge=1, le=1000), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): - """Get only clients (client_or_provider = 'client')""" + """Get clients and providers""" tenant_id = validate_access_to_resource(db, company_id, current_user) - - clients = ( - db.query(ClientProvider) - .filter( - ClientProvider.tenant_id == tenant_id, - ClientProvider.company_id == company_id, - ClientProvider.client_or_provider == "client" - ) - .offset(skip) - .limit(limit) - .all() + + query = db.query(ClientProvider).filter( + ClientProvider.tenant_id == tenant_id, + ClientProvider.company_id == company_id, ) - return [ClientProviderBasicDTO.model_validate(c) for c in clients] + if type is not None: + query = query.filter(ClientProvider.client_or_provider == type) -@router.get("/providers", response_model=List[ClientProviderBasicDTO]) -async def get_providers_only( - company_id: int = Query(..., description="Company ID"), - skip: int = Query(0, ge=0), - limit: int = Query(100, ge=1, le=1000), - db: Session = Depends(get_core_db), - current_user: dict = Depends(get_current_user), -): - """Get only providers (client_or_provider = 'provider')""" - tenant_id = validate_access_to_resource(db, company_id, current_user) - - providers = ( - db.query(ClientProvider) - .filter( - ClientProvider.tenant_id == tenant_id, - ClientProvider.company_id == company_id, - ClientProvider.client_or_provider == "provider" - ) - .offset(skip) - .limit(limit) - .all() - ) - return [ClientProviderBasicDTO.model_validate(p) for p in providers] + if active is not None: + query = query.filter(ClientProvider.is_active == active) + total = query.count() + clients = query.offset(skip).limit(limit).all() -@router.get("/search/rfc/{rfc}", response_model=List[ClientProviderBasicDTO]) -async def search_by_rfc( - rfc: str, - company_id: int = Query(..., description="Company ID"), - db: Session = Depends(get_core_db), - current_user: dict = Depends(get_current_user), -): - """Search clients/providers by RFC""" - tenant_id = validate_access_to_resource(db, company_id, current_user) - - clients = ( - db.query(ClientProvider) - .filter( - ClientProvider.tenant_id == tenant_id, - ClientProvider.company_id == company_id, - ClientProvider.rfc.ilike(f"%{rfc}%") - ) - .all() - ) - return [ClientProviderBasicDTO.model_validate(c) for c in clients] - - -@router.patch("/{client_id}/toggle-status", response_model=ClientProviderResponseDTO) -async def toggle_clients_and_providers_status( - client_id: int, - company_id: int = Query(..., description="Company ID"), - db: Session = Depends(get_core_db), - current_user: dict = Depends(get_current_user), -): - """Toggle client/provider enabled/disabled status""" - tenant_id = validate_access_to_resource(db, company_id, current_user) - - client = ClientProviderService.get_by_id(db, client_id, tenant_id, company_id) - if not client: - raise HTTPException(status_code=404, detail="Client/Provider not found") - - # Toggle status (1 = enabled, 0 = disabled) - client.enabled_disabled = 1 if client.enabled_disabled == 0 else 0 - - try: - db.commit() - db.refresh(client) - return client - except Exception as e: - db.rollback() - raise HTTPException(status_code=500, detail="Error updating status") + return { + "items": [ClientProviderResponseDTO.model_validate(c) for c in clients], + "total": total, + "page": (skip // limit) + 1, + "page_size": limit, + } @router.get("/{client_id}/basic", response_model=ClientProviderBasicDTO) @@ -150,9 +71,59 @@ async def get_clients_and_providers_basic_info( ): """Get basic information for a client/provider (without address and programs)""" tenant_id = validate_access_to_resource(db, company_id, current_user) - + client = ClientProviderService.get_by_id(db, client_id, tenant_id, company_id) if not client: raise HTTPException(status_code=404, detail="Client/Provider not found") - + return ClientProviderBasicDTO.model_validate(client) + + +@router.post("/", response_model=ClientProviderResponseDTO) +async def create_client_provider( + client_data: ClientProviderCreateDTO, + company_id: int = Query(..., description="Company ID"), + db: Session = Depends(get_core_db), + current_user: dict = Depends(get_current_user), +): + """Create a new client/provider""" + tenant_id = validate_access_to_resource(db, company_id, current_user) + + return ClientProviderService.create(db, client_data, tenant_id, company_id) + + +@router.patch("/{client_id}", response_model=ClientProviderResponseDTO) +async def update_client_provider( + client_id: int, + client_data: ClientProviderUpdateDTO, + company_id: int = Query(..., description="Company ID"), + db: Session = Depends(get_core_db), + current_user: dict = Depends(get_current_user), +): + """Update a client/provider""" + tenant_id = validate_access_to_resource(db, company_id, current_user) + + client = ClientProviderService.update( + db, client_id, tenant_id, company_id, client_data + ) + if not client: + raise HTTPException(status_code=404, detail="Client/Provider not found") + + return client + + +@router.delete("/{client_id}", response_model=bool) +async def delete_client_provider( + client_id: int, + company_id: int = Query(..., description="Company ID"), + db: Session = Depends(get_core_db), + current_user: dict = Depends(get_current_user), +): + """Delete a client/provider""" + tenant_id = validate_access_to_resource(db, company_id, current_user) + + success = ClientProviderService.delete(db, client_id, tenant_id, company_id) + if not success: + raise HTTPException(status_code=404, detail="Client/Provider not found") + + return success diff --git a/backend/api/v1/modules/a76/clients_and_providers/service.py b/backend/api/v1/modules/a76/clients_and_providers/service.py index c9bc22e3..5a329673 100644 --- a/backend/api/v1/modules/a76/clients_and_providers/service.py +++ b/backend/api/v1/modules/a76/clients_and_providers/service.py @@ -61,13 +61,17 @@ class ClientProviderService: ) if filters.get("status"): enabled = 1 if filters["status"] == "enabled" else 0 - query = query.filter(ClientProvider.enabled_disabled == enabled) + query = query.filter(ClientProvider.is_active == enabled) total = query.count() - clients = query.options( - joinedload(ClientProvider.address), - joinedload(ClientProvider.programs) - ).offset(skip).limit(limit).all() + clients = ( + query.options( + joinedload(ClientProvider.address), joinedload(ClientProvider.programs) + ) + .offset(skip) + .limit(limit) + .all() + ) return clients, total @@ -79,8 +83,7 @@ class ClientProviderService: return ( db.query(ClientProvider) .options( - joinedload(ClientProvider.address), - joinedload(ClientProvider.programs) + joinedload(ClientProvider.address), joinedload(ClientProvider.programs) ) .filter( ClientProvider.id == client_id, @@ -102,9 +105,7 @@ class ClientProviderService: # Create main client/provider data_dict = client_data.model_dump(exclude={"address", "programs"}) db_client = ClientProvider( - **data_dict, - tenant_id=tenant_id, - company_id=company_id + **data_dict, tenant_id=tenant_id, company_id=company_id ) db.add(db_client) @@ -112,8 +113,8 @@ class ClientProviderService: # Create address if provided if client_data.address: - db_address = ClientProviderAddress( - tenant_id=tenant_id, + db_address = ClientProviderAddress( + tenant_id=tenant_id, company_id=company_id, client_id=db_client.id, **client_data.address.model_dump(exclude_unset=True), @@ -122,7 +123,7 @@ class ClientProviderService: # Create programs if provided if client_data.programs: - db_programs = ClientProviderPrograms( + db_programs = ClientProviderPrograms( tenant_id=tenant_id, company_id=company_id, client_id=db_client.id, @@ -210,9 +211,7 @@ class ClientProviderService: ) @staticmethod - def delete( - db: Session, client_id: int, tenant_id: int, company_id: int - ) -> bool: + def delete(db: Session, client_id: int, tenant_id: int, company_id: int) -> bool: """Delete a client/provider""" client = ClientProviderService.get_by_id(db, client_id, tenant_id, company_id) if not client: @@ -351,7 +350,7 @@ class ClientProviderService: ) if enabled_only: - query = query.filter(ClientProvider.enabled_disabled == 1) + query = query.filter(ClientProvider.is_active == 1) # Contar total total = query.count() @@ -478,59 +477,4 @@ class ClientProviderService: logger.error(f"Error deleting client/provider {client_id}: {str(e)}") raise HTTPException( status_code=500, detail="Error deleting client/provider" - ) - - def get_clients_only( - self, skip: int = 0, limit: int = 100 - ) -> List[ClientProviderBasicDTO]: - """Obtiene solo clientes (client)""" - query = self.db.query(ClientProvider).filter( - ClientProvider.client_or_provider == "client" - ) - clients = query.offset(skip).limit(limit).all() - return [ClientProviderBasicDTO.model_validate(client) for client in clients] - - def get_providers_only( - self, skip: int = 0, limit: int = 100 - ) -> List[ClientProviderBasicDTO]: - """Obtiene solo proveedores (provider)""" - query = self.db.query(ClientProvider).filter( - ClientProvider.client_or_provider == "provider" - ) - providers = query.offset(skip).limit(limit).all() - return [ - ClientProviderBasicDTO.model_validate(provider) for provider in providers - ] - - def search_by_rfc(self, rfc: str) -> List[ClientProviderBasicDTO]: - """Busca clientes/proveedores por RFC""" - clients = ( - self.db.query(ClientProvider) - .filter(ClientProvider.rfc.ilike(f"%{rfc}%")) - .all() - ) - return [ClientProviderBasicDTO.model_validate(client) for client in clients] - - def toggle_status(self, client_id: str) -> Optional[ClientProviderResponseDTO]: - """Cambia el estado habilitado/deshabilitado""" - client = ( - self.db.query(ClientProvider) - .filter(ClientProvider.client_id == client_id) - .first() - ) - if not client: - return None - - # Toggle status (1 = habilitado, 0 = deshabilitado) - client.enabled_disabled = 1 if client.enabled_disabled == 0 else 0 - - try: - self.db.commit() - logger.info( - f"Client/Provider status toggled: {client_id} -> {client.enabled_disabled}" - ) - return self._get_client_with_relations(client_id) - except Exception as e: - self.db.rollback() - logger.error(f"Error toggling status for {client_id}: {str(e)}") - raise HTTPException(status_code=500, detail="Error updating status") + ) \ No newline at end of file diff --git a/backend/api/v1/modules/a76/parts/dto.py b/backend/api/v1/modules/a76/parts/dto.py index 1414fd82..e21e7772 100644 --- a/backend/api/v1/modules/a76/parts/dto.py +++ b/backend/api/v1/modules/a76/parts/dto.py @@ -67,7 +67,7 @@ class PartCreateDTO(BaseModel): added_value: Optional[Decimal] = Field(None, description="Added value") # Status and media - enabled_disabled: Optional[int] = Field(None, description="Enabled/Disabled status") + is_active: Optional[bool] = Field(None, description="Enabled/Disabled status") creation_date: Optional[int] = Field(None, description="Creation date") part_photo: Optional[str] = Field( None, max_length=255, description="Part photo URL" @@ -132,7 +132,7 @@ class PartUpdateDTO(BaseModel): added_value: Optional[Decimal] = Field(None, description="Added value") # Status and media - enabled_disabled: Optional[int] = Field(None, description="Enabled/Disabled status") + is_active: Optional[bool] = Field(None, description="Enabled/Disabled status") part_photo: Optional[str] = Field( None, max_length=255, description="Part photo URL" ) @@ -178,7 +178,7 @@ class PartResponseDTO(BaseModel): added_value: Optional[Decimal] = None # Status and dates - enabled_disabled: Optional[int] = None + is_active: Optional[bool] = None creation_date: Optional[int] = None modification_date: Optional[int] = None modification_date_iso: Optional[datetime] = None @@ -200,7 +200,7 @@ class PartBasicDTO(BaseModel): part_class: Optional[str] = None unit_cost: Optional[Decimal] = None currency_key: Optional[str] = None - enabled_disabled: Optional[int] = None + is_active: Optional[bool] = None class Config: from_attributes = True diff --git a/backend/api/v1/modules/a76/parts/models.py b/backend/api/v1/modules/a76/parts/models.py index ef8b325b..b961c272 100644 --- a/backend/api/v1/modules/a76/parts/models.py +++ b/backend/api/v1/modules/a76/parts/models.py @@ -16,6 +16,7 @@ from sqlalchemy import ( SmallInteger, String, UniqueConstraint, + Boolean, ) from sqlalchemy.orm import Mapped, mapped_column, relationship @@ -90,7 +91,7 @@ class Part(Base, TenantScopedMixin): added_value: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # Status and dates - enabled_disabled: Mapped[Optional[int]] = mapped_column(SmallInteger) + is_active: Mapped[Optional[bool]] = mapped_column(Boolean) creation_date: Mapped[Optional[int]] = mapped_column() # FECHACREACIONPARTE modification_date: Mapped[Optional[int]] = mapped_column() # FECHAMODIFICA modification_date_iso: Mapped[Optional[datetime]] = ( diff --git a/backend/api/v1/modules/a76/parts/routes.py b/backend/api/v1/modules/a76/parts/routes.py index e271cb12..0f5996b6 100644 --- a/backend/api/v1/modules/a76/parts/routes.py +++ b/backend/api/v1/modules/a76/parts/routes.py @@ -348,7 +348,7 @@ async def get_part_basic_info( part_class=part.part_class, unit_cost=part.unit_cost, currency_key=part.currency_key, - enabled_disabled=part.enabled_disabled, + is_active=part.is_active, ) diff --git a/backend/api/v1/modules/a76/parts/service.py b/backend/api/v1/modules/a76/parts/service.py index d8ba3d06..48c2eab9 100644 --- a/backend/api/v1/modules/a76/parts/service.py +++ b/backend/api/v1/modules/a76/parts/service.py @@ -221,7 +221,7 @@ class PartService: return None # Toggle status (assuming 1 = enabled, 0 = disabled) - db_part.enabled_disabled = 1 if db_part.enabled_disabled == 0 else 0 + db_part.is_active = 1 if db_part.is_active == 0 else 0 db.commit() db.refresh(db_part) @@ -257,8 +257,8 @@ class PartService: ) # Partes habilitadas vs deshabilitadas - enabled_parts = db.query(Part).filter(Part.enabled_disabled == 1).count() - disabled_parts = db.query(Part).filter(Part.enabled_disabled == 0).count() + enabled_parts = db.query(Part).filter(Part.is_active == 1).count() + disabled_parts = db.query(Part).filter(Part.is_active == 0).count() return { "total_parts": total_parts, 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 93d2ed7c..76533e83 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 @@ -16,15 +16,21 @@ def list_code_pedimento_regimens( page: int = Query(1, ge=1, description="Número de página"), page_size: int = Query(50, ge=1, le=100, description="Tamaño de página"), code: str = Query(None, description="Filter by code"), + regime: str = Query(None, description="Filter by regime"), + type: str = Query(None, description="Filter by type"), db: Session = Depends(get_core_db), current_user: dict = Depends(get_current_user), ): skip = (page - 1) * page_size query = db.query(CodePedimentoRegimen) - + if code is not None: query = query.filter(CodePedimentoRegimen.pedimento_code == code) - + if regime is not None: + query = query.filter(CodePedimentoRegimen.regime == regime) + if type is not None: + query = query.filter(CodePedimentoRegimen.type == type) + items = query.offset(skip).limit(page_size).all() total = query.count() return { diff --git a/frontend/src/lib/api/dashboard/a76/clients-providers.ts b/frontend/src/lib/api/dashboard/a76/clients-providers.ts index 40faa07d..773de3b0 100644 --- a/frontend/src/lib/api/dashboard/a76/clients-providers.ts +++ b/frontend/src/lib/api/dashboard/a76/clients-providers.ts @@ -31,8 +31,8 @@ export interface ClientProvider { domicile_fiscal?: string | null; foreign_tax_id?: string | null; client_or_provider?: string | null; - enabled_disabled?: number; - tenant_id: number; + is_active?: boolean; + tenant_id: number; address?: ClientProviderAddress | null; programs?: ClientProviderPrograms | null; } @@ -46,7 +46,7 @@ export interface ClientProviderBasic { domicile_fiscal?: string | null; foreign_tax_id?: string | null; client_or_provider?: string | null; - enabled_disabled?: number; + is_active?: boolean; tenant_id: number; } @@ -65,7 +65,7 @@ export interface CreateClientProviderData { domicile_fiscal?: string | null; foreign_tax_id?: string | null; client_or_provider?: string | null; - enabled_disabled?: number; + is_active?: boolean; address?: Omit | null; programs?: Omit | null; } @@ -78,7 +78,7 @@ export interface UpdateClientProviderData { domicile_fiscal?: string | null; foreign_tax_id?: string | null; client_or_provider?: string | null; - enabled_disabled?: number; + is_active?: boolean; address?: Partial | null; programs?: Partial | null; } @@ -100,7 +100,7 @@ export const clientsProvidersApi = { page: page.toString(), page_size: pageSize.toString() }); - + if (filters) { Object.entries(filters).forEach(([key, value]) => { if (value !== undefined && value !== null && value !== '') { @@ -108,7 +108,7 @@ export const clientsProvidersApi = { } }); } - + return api.get( `/v1/a76/clients-providers?${params.toString()}` ); @@ -166,7 +166,7 @@ export const clientsProvidersApi = { /** * Crea un nuevo cliente/proveedor - * @param companyId - ID de la compañía + * @param companyId - ID de la compañía * @param data - Datos del cliente/proveedor a crear */ create: (companyId: number, data: CreateClientProviderData) => diff --git a/frontend/src/lib/api/dashboard/a76/customs-brokers.ts b/frontend/src/lib/api/dashboard/a76/customs-brokers.ts index d6a15c7c..17a82200 100644 --- a/frontend/src/lib/api/dashboard/a76/customs-brokers.ts +++ b/frontend/src/lib/api/dashboard/a76/customs-brokers.ts @@ -19,7 +19,7 @@ export interface CustomsBroker { tax_id?: string | null; personal_id?: string | null; position?: string | null; - license?: string | null; + license: string; company?: string | null; contact?: string | null; tenant_id: string; @@ -117,6 +117,14 @@ export const customsBrokersApi = { return api.delete(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`); }, + /** + * Actualiza la información de un agente aduanal + */ + update: (brokerKey: string, data: CreateCustomsBrokerData) => { + const companyId = data.company_id; + return api.put(`/v1/a76/customs-brokers/${brokerKey}?company_id=${companyId}`, data); + }, + /** * Actualiza la información de VU de un agente aduanal */ diff --git a/frontend/src/lib/api/dashboard/a76/pedimento-dates.ts b/frontend/src/lib/api/dashboard/a76/pedimento-dates.ts index 7e99ef9e..1cbe8ef4 100644 --- a/frontend/src/lib/api/dashboard/a76/pedimento-dates.ts +++ b/frontend/src/lib/api/dashboard/a76/pedimento-dates.ts @@ -17,8 +17,6 @@ export interface PedimentoDates { original_date?: string | null; start_date?: string | null; end_date?: string | null; - capture_date?: string | null; - capture_time?: string | null; created_at: string; } @@ -33,8 +31,6 @@ export interface CreatePedimentoDatesData { original_date?: string | null; start_date?: string | null; end_date?: string | null; - capture_date?: string | null; - capture_time?: string | null; } export interface UpdatePedimentoDatesData { @@ -48,8 +44,6 @@ export interface UpdatePedimentoDatesData { original_date?: string | null; start_date?: string | null; end_date?: string | null; - capture_date?: string | null; - capture_time?: string | null; } export const pedimentoDatesApi = { diff --git a/frontend/src/lib/api/dashboard/a76/pedimentos.ts b/frontend/src/lib/api/dashboard/a76/pedimentos.ts index 339ada55..3e93e440 100644 --- a/frontend/src/lib/api/dashboard/a76/pedimentos.ts +++ b/frontend/src/lib/api/dashboard/a76/pedimentos.ts @@ -15,9 +15,7 @@ export interface PedimentoDates { eucan_date?: string | null; original_date?: string | null; start_date?: string | null; - end_date?: string | null; - capture_date?: string | null; - capture_time?: string | null; + end_date?: string | null; } export interface PedimentoPayments { diff --git a/frontend/src/lib/components/dashboard/classes/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/classes/create-edit-dialog.svelte index cac2f932..a61219e4 100644 --- a/frontend/src/lib/components/dashboard/classes/create-edit-dialog.svelte +++ b/frontend/src/lib/components/dashboard/classes/create-edit-dialog.svelte @@ -9,6 +9,7 @@ import { materialTypesApi, type MaterialType } from "$lib/api/dashboard/refrence_data/material_types"; import { companyStore } from "$lib/stores/company.svelte"; import { onMount } from 'svelte'; + import { LoaderCircle, Home } from 'lucide-svelte'; let { open = $bindable(false), @@ -248,21 +249,7 @@ {#if companyStore.activeCompany}
- - - - +

{companyStore.activeCompany.name} @@ -426,26 +413,7 @@ {/snippet} @@ -102,63 +89,15 @@ Acciones - - - - + Editar {#if loading} - - - - + {:else} - - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts b/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts index cfa37a35..ef8ea700 100644 --- a/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts +++ b/frontend/src/lib/components/dashboard/clients_and_providers/columns.ts @@ -56,10 +56,10 @@ export function createColumns(onSuccess?: () => void): ColumnDef const typeSnippet = createRawSnippet<[{ type: string | null | undefined }]>((getType) => { const { type } = getType(); const displayType = type === 'client' ? 'Cliente' : type === 'provider' ? 'Proveedor' : type === 'both' ? 'Ambos' : 'N/A'; - const colorClass = type === 'client' ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' + const colorClass = type === 'client' ? 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-300' : type === 'provider' ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' - : type === 'both' ? 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300' - : 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300'; + : type === 'both' ? 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-300' + : 'bg-gray-100 text-gray-800 dark:bg-gray-900 dark:text-gray-300'; return { render: () => `${displayType}` }; @@ -81,21 +81,21 @@ export function createColumns(onSuccess?: () => void): ColumnDef } }, { - accessorKey: "enabled_disabled", + accessorKey: "is_active", header: "Estado", cell: ({ row }) => { const statusSnippet = createRawSnippet<[{ status: number | undefined }]>((getStatus) => { const { status } = getStatus(); const isEnabled = status === 1; const statusText = isEnabled ? 'Activo' : 'Inactivo'; - const colorClass = isEnabled - ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' + const colorClass = isEnabled + ? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-300' : 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-300'; return { render: () => `${statusText}` }; }); - return renderSnippet(statusSnippet, { status: row.original.enabled_disabled }); + return renderSnippet(statusSnippet, { status: row.original.is_active }); } }, { diff --git a/frontend/src/lib/components/dashboard/clients_and_providers/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/clients_and_providers/create-edit-dialog.svelte index 2a85404a..811cba86 100644 --- a/frontend/src/lib/components/dashboard/clients_and_providers/create-edit-dialog.svelte +++ b/frontend/src/lib/components/dashboard/clients_and_providers/create-edit-dialog.svelte @@ -5,6 +5,7 @@ import { Label } from "$lib/components/ui/label"; import { clientsProvidersApi, type ClientProvider, type CreateClientProviderData, type UpdateClientProviderData } from "$lib/api/dashboard/a76/clients-providers"; import { companyStore } from "$lib/stores/company.svelte"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -24,7 +25,7 @@ domicile_fiscal: "", foreign_tax_id: "", client_or_provider: "client", - enabled_disabled: 1, + is_active: true, // Address fields street: "", neighborhood: "", @@ -51,7 +52,7 @@ domicile_fiscal: item.domicile_fiscal || "", foreign_tax_id: item.foreign_tax_id || "", client_or_provider: item.client_or_provider || "client", - enabled_disabled: item.enabled_disabled ?? 1, + is_active: item.is_active ?? true, street: item.address?.street || "", neighborhood: item.address?.neighborhood || "", city: item.address?.city || "", @@ -70,7 +71,7 @@ domicile_fiscal: "", foreign_tax_id: "", client_or_provider: "client", - enabled_disabled: 1, + is_active: true, street: "", neighborhood: "", city: "", @@ -107,7 +108,7 @@ domicile_fiscal: formData.domicile_fiscal || null, foreign_tax_id: formData.foreign_tax_id || null, client_or_provider: formData.client_or_provider as "client" | "provider" | "both" | null, - enabled_disabled: formData.enabled_disabled, + is_active: formData.is_active, address: { street: formData.street || null, neighborhood: formData.neighborhood || null, @@ -132,7 +133,7 @@ domicile_fiscal: formData.domicile_fiscal || null, foreign_tax_id: formData.foreign_tax_id || null, client_or_provider: formData.client_or_provider as "client" | "provider" | "both" | null, - enabled_disabled: formData.enabled_disabled, + is_active: formData.is_active, address: { street: formData.street || null, neighborhood: formData.neighborhood || null, @@ -185,7 +186,7 @@ domicile_fiscal: "", foreign_tax_id: "", client_or_provider: "client", - enabled_disabled: 1, + is_active: true, street: "", neighborhood: "", city: "", @@ -432,26 +433,7 @@ diff --git a/frontend/src/lib/components/dashboard/clients_and_providers/data-table-actions.svelte b/frontend/src/lib/components/dashboard/clients_and_providers/data-table-actions.svelte index 982cc4dc..888b9488 100644 --- a/frontend/src/lib/components/dashboard/clients_and_providers/data-table-actions.svelte +++ b/frontend/src/lib/components/dashboard/clients_and_providers/data-table-actions.svelte @@ -90,7 +90,7 @@ Ver detalles Editar - {isToggling ? 'Cambiando...' : item.enabled_disabled === 1 ? 'Desactivar' : 'Activar'} + {isToggling ? 'Cambiando...' : item.is_active === true ? 'Desactivar' : 'Activar'} Eliminar diff --git a/frontend/src/lib/components/dashboard/clients_and_providers/delete-dialog.svelte b/frontend/src/lib/components/dashboard/clients_and_providers/delete-dialog.svelte index 2d0963be..f0b8d48c 100644 --- a/frontend/src/lib/components/dashboard/clients_and_providers/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/clients_and_providers/delete-dialog.svelte @@ -3,6 +3,7 @@ import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { clientsProvidersApi, type ClientProvider } from "$lib/api/dashboard/a76/clients-providers"; import { companyStore } from "$lib/stores/company.svelte"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -97,26 +98,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/clients_and_providers/details-dialog.svelte b/frontend/src/lib/components/dashboard/clients_and_providers/details-dialog.svelte index 592a85e7..fe478312 100644 --- a/frontend/src/lib/components/dashboard/clients_and_providers/details-dialog.svelte +++ b/frontend/src/lib/components/dashboard/clients_and_providers/details-dialog.svelte @@ -85,7 +85,7 @@

Estado - {#if item.enabled_disabled === 1} + {#if item.is_active === true} Activo diff --git a/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte b/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte index 5a6f7281..3696cbd7 100644 --- a/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte +++ b/frontend/src/lib/components/dashboard/customs_brokers/data-table-actions.svelte @@ -5,6 +5,7 @@ import type { CustomsBroker } from "./columns.js"; import DetailsDialog from "./details-dialog.svelte"; import DeleteDialog from "./delete-dialog.svelte"; + import EditDialog from "./edit-dialog.svelte"; let { broker, @@ -16,6 +17,7 @@ let showDetailsDialog = $state(false); let showDeleteDialog = $state(false); + let showEditDialog = $state(false); function handleCopyKey() { navigator.clipboard.writeText(broker.broker_key); @@ -31,6 +33,10 @@ showDetailsDialog = true; } + function handleEdit() { + showEditDialog = true; + } + function handleDelete() { showDeleteDialog = true; } @@ -64,6 +70,9 @@ Ver detalles + + Editar + Eliminar @@ -72,4 +81,5 @@ + diff --git a/frontend/src/lib/components/dashboard/customs_brokers/delete-dialog.svelte b/frontend/src/lib/components/dashboard/customs_brokers/delete-dialog.svelte index bf2ffa39..0658bd95 100644 --- a/frontend/src/lib/components/dashboard/customs_brokers/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/customs_brokers/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { customsBrokersApi, type CustomsBroker } from "$lib/api/dashboard/a76/customs-brokers"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -92,26 +93,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + Eliminando... {:else} Eliminar diff --git a/frontend/src/lib/components/dashboard/customs_brokers/edit-dialog.svelte b/frontend/src/lib/components/dashboard/customs_brokers/edit-dialog.svelte new file mode 100644 index 00000000..e5bfeebb --- /dev/null +++ b/frontend/src/lib/components/dashboard/customs_brokers/edit-dialog.svelte @@ -0,0 +1,384 @@ + + + + + + Editar Agente Aduanal + + Modifica los datos del agente aduanal {broker?.broker_key}. + + + +
+ {#if error} +
+ {error} +
+ {/if} + + +
+

Información Básica

+ +
+
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+
+ + +
+ +
+ + +
+
+
+ + +
+

Información de Contacto

+ +
+
+ + +
+ +
+ + +
+
+ +
+ + +
+ +
+ + +
+
+ + +
+

Dirección

+ +
+ + +
+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+ +
+ + +
+
+ + +
+

Información Fiscal

+ +
+
+ + +
+ +
+ + +
+
+ +
+ + +
+
+ + + + + +
+
+
diff --git a/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte b/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte index 01f9bba9..1aa00270 100644 --- a/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte +++ b/frontend/src/lib/components/dashboard/pedimentos/data-table-actions.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as DropdownMenu from "$lib/components/ui/dropdown-menu"; import { pedimentosApi, type Pedimento } from "$lib/api/dashboard/a76/pedimentos"; + import { EllipsisVertical, Pencil, LoaderCircle, Trash2 } from 'lucide-svelte'; let { item, @@ -62,21 +63,7 @@ {#snippet child({ props })} {/snippet} @@ -84,63 +71,15 @@ Acciones - - - - + Editar {#if loading} - - - - + {:else} - - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte index 897a610c..5377d232 100644 --- a/frontend/src/lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/dates-tab-form.svelte @@ -30,8 +30,6 @@ original_date: datesData.original_date ? datesData.original_date.substring(0, 10) : '', start_date: datesData.start_date ? datesData.start_date.substring(0, 10) : '', end_date: datesData.end_date ? datesData.end_date.substring(0, 10) : '', - capture_date: datesData.capture_date ? datesData.capture_date.substring(0, 10) : '', - capture_time: datesData.capture_time || '' }; } } else { @@ -48,7 +46,6 @@ original_date: '', start_date: '', end_date: '', - capture_date: '', capture_time: '' }; } @@ -163,27 +160,7 @@ type="date" bind:value={formData.end_date} /> -
- - -
- - -
- - -
- - -
+
diff --git a/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte b/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte index bc488ea7..6c7e5ffc 100644 --- a/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte +++ b/frontend/src/lib/components/dashboard/pedimentos/edit/general-tab-form.svelte @@ -5,26 +5,175 @@ import * as Select from '$lib/components/ui/select'; import type { Pedimento } from '$lib/api/dashboard/a76/pedimentos'; import type { PedimentoCode } from '$lib/api/dashboard/refrence_data/pedimento_codes'; + import type { CustomsSection } from '$lib/api/dashboard/refrence_data/customs_sections'; + import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers'; + import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers'; + import type { CodePedimentoRegimen } from '$lib/api/dashboard/refrence_data/code_pedimento_regimens'; let { pedimento, formData = $bindable(), - pedimentoCodes = [] + pedimentoCodes = [], + customsSections = [], + customsBrokers = [], + clients = [], + codePedimentoRegimens = [] }: { pedimento: Pedimento | null; formData?: any; pedimentoCodes?: PedimentoCode[]; + customsSections?: CustomsSection[]; + customsBrokers?: CustomsBroker[]; + clients?: ClientProvider[]; + codePedimentoRegimens?: CodePedimentoRegimen[]; } = $props(); - // Debug: verificar que los datos llegan - $effect(() => { - console.log('pedimentoCodes:', pedimentoCodes.length, 'items'); + // Extraer regímenes únicos de codePedimentoRegimens + const uniqueRegimens = $derived( + Array.from(new Set(codePedimentoRegimens.map(r => r.regimen_code).filter((code): code is string => code !== null))) + .sort() + .map(code => ({ code, label: code })) + ); + + // Funciones de mapeo entre type_code (E/I) y operation_type (1/2) + function typeCodeToOperationType(typeCode: string | null | undefined): number | null { + if (!typeCode) return null; + // E = Exportación = 1, I = Importación = 2 + if (typeCode.toUpperCase() === 'E') return 1; + if (typeCode.toUpperCase() === 'I') return 2; + return null; + } + + function operationTypeToTypeCode(operationType: number | null | undefined): string | null { + if (operationType === null || operationType === undefined) return null; + // 1 = Exportación = E, 2 = Importación = I + if (operationType === 1) return 'E'; + if (operationType === 2) return 'I'; + return null; + } + + // Opciones filtradas para Régimen y Tipo de Operación basadas en las selecciones actuales + // NOTA: La Clave NO se filtra, siempre muestra todas las opciones + + const filteredRegimens = $derived(() => { + // Si hay clave o tipo de operación seleccionado, filtrar + if (formData.pedimento_code || formData.operation_type !== null) { + const matches = codePedimentoRegimens.filter(r => { + const matchesCode = !formData.pedimento_code || r.pedimento_code === formData.pedimento_code; + const matchesType = formData.operation_type === null || r.type_code === operationTypeToTypeCode(formData.operation_type); + return matchesCode && matchesType; + }); + const validRegimens = new Set(matches.map(m => m.regimen_code).filter((code): code is string => code !== null)); + return Array.from(validRegimens).sort().map(code => ({ code, label: code })); + } + return uniqueRegimens; }); + const filteredOperationTypes = $derived(() => { + // Si hay clave o régimen seleccionado, filtrar + if (formData.pedimento_code || formData.regime) { + const matches = codePedimentoRegimens.filter(r => { + const matchesCode = !formData.pedimento_code || r.pedimento_code === formData.pedimento_code; + const matchesRegime = !formData.regime || r.regimen_code === formData.regime; + return matchesCode && matchesRegime; + }); + const validTypes = new Set(matches.map(m => typeCodeToOperationType(m.type_code)).filter(t => t !== null)); + return operationOptions.filter(opt => validTypes.has(opt.value)); + } + return operationOptions; + }); + + // Reactive synchronization between Clave, Régimen, and Tipo de Operación + // REGLA: La Clave es el campo principal y NUNCA se modifica automáticamente + // Solo se auto-llenan Régimen y Tipo de Operación basándose en la Clave + + // Cuando cambia la Clave del Pedimento + $effect(() => { + const currentCode = formData.pedimento_code; + if (!currentCode) return; + + const matches = codePedimentoRegimens.filter(r => r.pedimento_code === currentCode); + + if (matches.length === 0) return; + + // Verificar si los valores actuales de régimen y tipo son válidos para esta clave + const currentIsValid = matches.some(m => { + const matchesRegime = !formData.regime || m.regimen_code === formData.regime; + const matchesType = formData.operation_type === null || m.type_code === operationTypeToTypeCode(formData.operation_type); + return matchesRegime && matchesType; + }); + + // Si los valores actuales son válidos, NO auto-llenar + if (currentIsValid && (formData.regime || formData.operation_type !== null)) { + return; + } + + // Si solo hay un match y no hay valores válidos, auto-llenar + if (matches.length === 1) { + const match = matches[0]; + if (match.regimen_code && formData.regime !== match.regimen_code) { + formData.regime = match.regimen_code; + } + const expectedOpType = typeCodeToOperationType(match.type_code); + if (expectedOpType !== null && formData.operation_type !== expectedOpType) { + formData.operation_type = expectedOpType; + } + } + }); + + // Cuando cambia el Régimen + $effect(() => { + const currentRegime = formData.regime; + if (!currentRegime) return; + + const matches = codePedimentoRegimens.filter(r => r.regimen_code === currentRegime); + + if (matches.length === 0) return; + + // Si hay clave seleccionada, solo validar (NO auto-llenar tipo de operación) + if (formData.pedimento_code) { + const exactMatch = matches.find(m => m.pedimento_code === formData.pedimento_code); + } + // Si hay tipo de operación pero no clave, no hacer nada + // (el usuario debe seleccionar la clave primero) + }); + + // Cuando cambia el Tipo de Operación + $effect(() => { + const currentType = formData.operation_type; + if (currentType === null || currentType === undefined) return; + + const expectedTypeCode = operationTypeToTypeCode(currentType); + const matches = codePedimentoRegimens.filter(r => r.type_code === expectedTypeCode); + + if (matches.length === 0) return; + + // Si hay clave seleccionada, actualizar régimen (forzar si no hay match exacto) + if (formData.pedimento_code) { + const exactMatch = matches.find(m => m.pedimento_code === formData.pedimento_code); + if (exactMatch?.regimen_code && formData.regime !== exactMatch.regimen_code) { + formData.regime = exactMatch.regimen_code; + } else if (!exactMatch) { + // No hay match exacto - buscar cualquier match con la clave actual + const allMatchesForClave = codePedimentoRegimens.filter(r => r.pedimento_code === formData.pedimento_code); + if (allMatchesForClave.length > 0) { + // Forzar el régimen al primer match disponible para esta clave + const firstMatch = allMatchesForClave[0]; + if (firstMatch.regimen_code) formData.regime = firstMatch.regimen_code; + } + } + } + // Si hay régimen pero no clave, no hacer nada + // (el usuario debe seleccionar la clave primero) + }); + + // Obtener el año actual (últimos 2 dígitos) + const currentYear = String(new Date().getFullYear()).slice(-2); + // Inicializar formData con los valores del pedimento (o vacío si es null) if (!formData) { formData = { - year: pedimento?.year || '', + year: pedimento?.year || currentYear, customs_office: pedimento?.customs_office || '', license: pedimento?.license || '', pedimento_number: pedimento?.pedimento_number || '', @@ -41,6 +190,13 @@ }; } + // Asegurar que el año siempre esté actualizado con el año actual + $effect(() => { + if (formData && !pedimento?.year) { + formData.year = currentYear; + } + }); + const operationOptions = [ { value: 1, label: 'Exportación' }, { value: 2, label: 'Importación' }, @@ -85,6 +241,8 @@ placeholder="23" maxlength={2} class="text-center" + disabled + readonly /> @@ -92,15 +250,28 @@
-
-
+
- + formData.customs_office = v ?? ''} + > + + + {formData.customs_office || 'Sel...'} + + + + {#each customsSections as section} + + + {section.customs_code} - {section.section_name} + + + {/each} + +
@@ -109,13 +280,26 @@
- + formData.license = v ?? ''} + > + + + {formData.license || 'Sel...'} + + + + {#each customsBrokers as broker} + + + {broker.broker_key} - {broker.name || ''} + + + {/each} + +
@@ -137,29 +321,24 @@
- - -
- - -
- + formData.operation_type = v ? Number(v) : null} + value={String(formData.client_id ?? '')} + onValueChange={(v: string) => formData.client_id = v ? Number(v) : null} > - {operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'} + + {clients.find(c => c.id === formData.client_id)?.name || 'Seleccionar cliente...'} + - - {#each operationOptions as option} - + + {#each clients as client} + + + {client.name} + + {/each} @@ -176,40 +355,81 @@ />
- -
- - formData.pedimento_code = v ?? ''} - > - - - {pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Seleccionar...'} - - - - {#each pedimentoCodes as code} - - - {code.code} - {code.description} - - - {/each} - - -
+
- -
- - -
+ +
+ +
+ + formData.pedimento_code = v ?? ''} + > + + + {pedimentoCodes.find(o => o.code === formData.pedimento_code)?.code || 'Sel...'} + + + + {#each pedimentoCodes as code} + + + {code.code} - {code.description} + + + {/each} + + +
+ + +
+ + formData.regime = v ?? ''} + > + + + {formData.regime || 'Sel...'} + + + + {#each filteredRegimens() as regimen} + + + {regimen.code} + + + {/each} + + +
+ + +
+ + formData.operation_type = v ? Number(v) : null} + > + + {operationOptions.find(o => o.value === formData.operation_type)?.label || 'Seleccionar...'} + + + {#each filteredOperationTypes() as option} + + {/each} + + +
+
+ +
diff --git a/frontend/src/lib/components/dashboard/reference_data/code_pedimento_regimens/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/code_pedimento_regimens/delete-dialog.svelte index 73d891e6..3f6031ce 100644 --- a/frontend/src/lib/components/dashboard/reference_data/code_pedimento_regimens/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/code_pedimento_regimens/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { codePedimentoRegimensApi, type CodePedimentoRegimen } from "$lib/api/dashboard/refrence_data/code_pedimento_regimens"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), diff --git a/frontend/src/lib/components/dashboard/reference_data/containers/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/containers/delete-dialog.svelte index 88ddcb9b..45c75b8d 100644 --- a/frontend/src/lib/components/dashboard/reference_data/containers/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/containers/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { containersApi, type Container } from "$lib/api/dashboard/refrence_data/containers"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -84,26 +85,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/reference_data/customs_sections/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/customs_sections/delete-dialog.svelte index f71a9d60..f2e3a3ff 100644 --- a/frontend/src/lib/components/dashboard/reference_data/customs_sections/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/customs_sections/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { customsSectionsApi, type CustomsSection } from "$lib/api/dashboard/refrence_data/customs_sections"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), diff --git a/frontend/src/lib/components/dashboard/reference_data/customs_warehouses/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/customs_warehouses/delete-dialog.svelte index 17878734..220f9e5c 100644 --- a/frontend/src/lib/components/dashboard/reference_data/customs_warehouses/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/customs_warehouses/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { customsWarehousesApi, type CustomsWarehouse } from "$lib/api/dashboard/refrence_data/customs_warehouses"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), diff --git a/frontend/src/lib/components/dashboard/reference_data/material_types/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/material_types/delete-dialog.svelte index 7bfb8a17..642bb106 100644 --- a/frontend/src/lib/components/dashboard/reference_data/material_types/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/material_types/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { materialTypesApi, type MaterialType } from "$lib/api/dashboard/refrence_data/material_types"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -88,26 +89,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/reference_data/payment_methods/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/payment_methods/delete-dialog.svelte index f5ff98e7..2b23c0cf 100644 --- a/frontend/src/lib/components/dashboard/reference_data/payment_methods/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/payment_methods/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { paymentMethodsApi, type PaymentMethod } from "$lib/api/dashboard/refrence_data/payment_methods"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -84,26 +85,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/reference_data/pedimento_regimens/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/pedimento_regimens/delete-dialog.svelte index 4c7ba519..27eceaa1 100644 --- a/frontend/src/lib/components/dashboard/reference_data/pedimento_regimens/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/pedimento_regimens/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { pedimentoRegimensApi, type PedimentoRegimen } from "$lib/api/dashboard/refrence_data/pedimento_regimens"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -84,26 +85,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/reference_data/states/create-edit-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/states/create-edit-dialog.svelte index 927f1ccf..cb24372d 100644 --- a/frontend/src/lib/components/dashboard/reference_data/states/create-edit-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/states/create-edit-dialog.svelte @@ -4,6 +4,7 @@ import { Input } from "$lib/components/ui/input"; import { Label } from "$lib/components/ui/label"; import { statesApi, type State, type CreateStateData, type UpdateStateData } from "$lib/api/dashboard/refrence_data/states"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -195,26 +196,7 @@ diff --git a/frontend/src/lib/components/dashboard/reference_data/states/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/states/delete-dialog.svelte index d52d5553..24e0c6e1 100644 --- a/frontend/src/lib/components/dashboard/reference_data/states/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/states/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { statesApi, type State } from "$lib/api/dashboard/refrence_data/states"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -96,26 +97,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/dashboard/reference_data/transport_modes/delete-dialog.svelte b/frontend/src/lib/components/dashboard/reference_data/transport_modes/delete-dialog.svelte index 13462cc5..cc05607a 100644 --- a/frontend/src/lib/components/dashboard/reference_data/transport_modes/delete-dialog.svelte +++ b/frontend/src/lib/components/dashboard/reference_data/transport_modes/delete-dialog.svelte @@ -2,6 +2,7 @@ import { Button } from "$lib/components/ui/button"; import * as AlertDialog from "$lib/components/ui/alert-dialog"; import { transportModesApi, type TransportMode } from "$lib/api/dashboard/refrence_data/transport_modes"; + import { LoaderCircle } from 'lucide-svelte'; let { open = $bindable(false), @@ -84,26 +85,7 @@ class="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {#if loading} - - - - + {/if} Eliminar diff --git a/frontend/src/lib/components/login-form.svelte b/frontend/src/lib/components/login-form.svelte index 11961b10..ebb3726a 100644 --- a/frontend/src/lib/components/login-form.svelte +++ b/frontend/src/lib/components/login-form.svelte @@ -10,6 +10,7 @@ import { Input } from "$lib/components/ui/input/index.js"; import { Button } from "$lib/components/ui/button/index.js"; import { cn } from "$lib/utils.js"; + import { FileText, ShieldCheck } from 'lucide-svelte'; import type { HTMLAttributes } from "svelte/elements"; import { page } from '$app/state'; import { enhance } from '$app/forms'; @@ -203,14 +204,10 @@
- - - +
- - - +
diff --git a/frontend/src/routes/dashboard/+page.svelte b/frontend/src/routes/dashboard/+page.svelte index 105fd63a..71473c62 100644 --- a/frontend/src/routes/dashboard/+page.svelte +++ b/frontend/src/routes/dashboard/+page.svelte @@ -1,5 +1,6 @@
@@ -52,78 +53,27 @@
- - - - - - - - - Código Pedimento - Regímenes - Gestionar relaciones - - -
- - - - - - Catálogo de Tipos - Próximamente -
- -
- - - - - - Reportes - Próximamente -
+ + + Código Pedimento - Regímenes + Gestionar relaciones +
+ + Catálogo de Tipos + Próximamente +
+ + Reportes + Próximamente
- - -
+
+ + +
\ No newline at end of file diff --git a/frontend/src/routes/dashboard/clients_and_providers/+page.server.ts b/frontend/src/routes/dashboard/clients_and_providers/+page.server.ts index f27c7557..dc458a5f 100644 --- a/frontend/src/routes/dashboard/clients_and_providers/+page.server.ts +++ b/frontend/src/routes/dashboard/clients_and_providers/+page.server.ts @@ -4,9 +4,9 @@ import { getAuthTokens, authenticatedFetch } from '$lib/server/api'; export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => { // Esperar a que el layout padre valide/refresque el token const parentData = await parent(); - + const { accessToken } = getAuthTokens(cookies); - + if (!accessToken) { return { error: 'No authenticated', @@ -22,20 +22,20 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => { // Obtener parámetros de paginación de la URL const page = parseInt(url.searchParams.get('page') || '1'); const pageSize = parseInt(url.searchParams.get('page_size') || '50'); - + // Obtener company_id de múltiples fuentes (en orden de prioridad): // 1. URL query param (permite cambiar vía navegación) // 2. Cookie active_company_id (setted por el team-switcher) // 3. Primera compañía del usuario (fallback) const companyIdParam = url.searchParams.get('company_id'); const cookieCompanyId = cookies.get('active_company_id'); - - const companyId = companyIdParam + + const companyId = companyIdParam ? parseInt(companyIdParam) : cookieCompanyId ? parseInt(cookieCompanyId) : parentData.companies?.[0]?.id; - + if (!companyId) { return { error: 'No company selected', @@ -47,9 +47,17 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => { }; } + // Construir URL con parámetros + let apiUrl = `v1/a76/clients-providers?company_id=${companyId}&page=${page}&page_size=${pageSize}`; + + const type = url.searchParams.get('type'); + if (type && type !== 'both') { + apiUrl += `&type=${type}`; + } + // Usar authenticatedFetch para manejar automáticamente el refresh de tokens const response = await authenticatedFetch( - `v1/a76/clients-providers?company_id=${companyId}&page=${page}&page_size=${pageSize}`, + apiUrl, {}, cookies, fetch @@ -62,7 +70,7 @@ export const load: PageServerLoad = async ({ cookies, fetch, url, parent }) => { statusText: response.statusText, error: errorText }); - + return { error: `Error ${response.status}: ${response.statusText}`, items: [], diff --git a/frontend/src/routes/dashboard/clients_and_providers/+page.svelte b/frontend/src/routes/dashboard/clients_and_providers/+page.svelte index 0995b3d6..2d25421a 100644 --- a/frontend/src/routes/dashboard/clients_and_providers/+page.svelte +++ b/frontend/src/routes/dashboard/clients_and_providers/+page.svelte @@ -6,6 +6,10 @@ import CreateEditDialog from '$lib/components/dashboard/clients_and_providers/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import * as Select from '$lib/components/ui/select'; + import { Plus, RefreshCw } from 'lucide-svelte'; + import { page } from '$app/stores'; + import { goto } from '$app/navigation'; import type { PageData } from './$types'; import { browser } from '$app/environment'; import { companyStore } from '$lib/stores/company.svelte'; @@ -16,6 +20,22 @@ // Estado para el diálogo de crear let showCreateDialog = $state(false); + // Estado para el filtro de tipo + let selectedType = $state($page.url.searchParams.get('type') || 'both'); + + // Actualizar URL cuando cambia el filtro + function handleTypeChange(value: string) { + selectedType = value; + const url = new URL(window.location.href); + if (value === 'both') { + url.searchParams.delete('type'); + } else { + url.searchParams.set('type', value); + } + goto(url.toString(), { keepFocus: true, noScroll: true, replaceState: true }); + reloadData(); + } + // Sincronizar token de cookies a localStorage al montar el componente onMount(() => { if (browser) { @@ -77,7 +97,8 @@ const response = await clientsProvidersApi.list( companyStore.activeCompany.id, currentPage + 1, - pageSize + pageSize, + selectedType !== 'both' ? { type: selectedType } : undefined ); if (response.error) { @@ -121,7 +142,8 @@ const response = await clientsProvidersApi.list( companyStore.activeCompany.id, 1, - pageSize + pageSize, + selectedType !== 'both' ? { type: selectedType } : undefined ); if (response.error) { @@ -174,21 +196,7 @@

@@ -216,25 +224,23 @@ {/if}
- + diff --git a/frontend/src/routes/dashboard/customs_brokers/+page.svelte b/frontend/src/routes/dashboard/customs_brokers/+page.svelte index 7ccfb44c..fe9682fd 100644 --- a/frontend/src/routes/dashboard/customs_brokers/+page.svelte +++ b/frontend/src/routes/dashboard/customs_brokers/+page.svelte @@ -11,6 +11,7 @@ import type { PageData } from './$types'; import { browser } from '$app/environment'; import { companyStore } from '$lib/stores/company.svelte'; + import { Plus, Search, Trash2 } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -175,21 +176,7 @@

@@ -226,44 +213,15 @@ Buscando... {:else} - - - - + Buscar {/if} - {#if searchedBroker} - + {#if searchedBroker} + {/if} diff --git a/frontend/src/routes/dashboard/general_catalogs/classes/+page.svelte b/frontend/src/routes/dashboard/general_catalogs/classes/+page.svelte index 458d83ec..247de471 100644 --- a/frontend/src/routes/dashboard/general_catalogs/classes/+page.svelte +++ b/frontend/src/routes/dashboard/general_catalogs/classes/+page.svelte @@ -9,6 +9,7 @@ import { Button } from '$lib/components/ui/button'; import { Input } from '$lib/components/ui/input'; import { Label } from '$lib/components/ui/label'; + import { Plus, Filter, Trash2, RefreshCw } from 'lucide-svelte'; import { browser } from '$app/environment'; // Estado para la lista de classes @@ -255,21 +256,7 @@

@@ -305,38 +292,11 @@
@@ -364,21 +324,7 @@ diff --git a/frontend/src/routes/dashboard/pedimentos/+page.svelte b/frontend/src/routes/dashboard/pedimentos/+page.svelte index 8a90390c..711d5173 100644 --- a/frontend/src/routes/dashboard/pedimentos/+page.svelte +++ b/frontend/src/routes/dashboard/pedimentos/+page.svelte @@ -10,6 +10,7 @@ import type { PageData } from './$types'; import { browser } from '$app/environment'; import { companyStore } from '$lib/stores/company.svelte'; + import { Plus, Filter, Trash2, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -260,21 +261,7 @@

@@ -322,38 +309,11 @@
@@ -381,21 +341,7 @@ diff --git a/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.server.ts b/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.server.ts index 7c17e4c9..d34b2f0c 100644 --- a/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.server.ts +++ b/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.server.ts @@ -9,6 +9,13 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => { throw redirect(302, '/login'); } + // Obtener el company_id de la cookie para ambas ramas (new y edit) + const companyId = await getActiveCompanyId(cookies, fetch); + + if (!companyId) { + throw error(400, 'No se encontró una compañía seleccionada'); + } + // Cargar pedimento_codes (datos de referencia) const pedimentoCodesPromise = authenticatedFetch( 'v1/public/refrence_data/pedimento-codes?page=1&page_size=100', @@ -17,16 +24,63 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => { fetch ); + // Cargar customs_sections (datos de referencia) + const customsSectionsPromise = authenticatedFetch( + 'v1/public/refrence_data/customs-sections?page=1&page_size=100', + {}, + cookies, + fetch + ); + + // Cargar customs_brokers (datos de referencia) + const customsBrokersPromise = authenticatedFetch( + `v1/a76/customs-brokers?company_id=${companyId}`, + {}, + cookies, + fetch + ); + + // Cargar clientes (para el select de client_id) + const clientsPromise = authenticatedFetch( + `v1/a76/clients-providers?company_id=${companyId}&type=client&page=1&page_size=1000`, + {}, + cookies, + fetch + ); + + // Cargar code-pedimento-regimens (para interdependencia de campos) + const codePedimentoRegimensPromise = authenticatedFetch( + 'v1/public/refrence_data/code-pedimento-regimens?page=1&page_size=100', + {}, + cookies, + fetch + ); + // Si el ID es "new", es una creación if (params.id === 'new') { - const pedimentoCodesResponse = await pedimentoCodesPromise; + const [pedimentoCodesResponse, customsSectionsResponse, customsBrokersResponse, clientsResponse, codePedimentoRegimensResponse] = await Promise.all([ + pedimentoCodesPromise, + customsSectionsPromise, + customsBrokersPromise, + clientsPromise, + codePedimentoRegimensPromise + ]); + const pedimentoCodes = pedimentoCodesResponse.ok ? await pedimentoCodesResponse.json() : { items: [] }; - + const customsSections = customsSectionsResponse.ok ? await customsSectionsResponse.json() : { items: [] }; + const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] }; + const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] }; + const codePedimentoRegimens = codePedimentoRegimensResponse.ok ? await codePedimentoRegimensResponse.json() : { items: [] }; + return { pedimento: null, pedimentoId: null, isCreate: true, - pedimentoCodes: pedimentoCodes.items || [] + pedimentoCodes: pedimentoCodes.items || [], + customsSections: customsSections.items || [], + customsBrokers: customsBrokers.items || [], + clients: clients.items || [], + codePedimentoRegimens: codePedimentoRegimens.items || [] }; } @@ -36,12 +90,7 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => { } try { - // Obtener el company_id de la cookie - const companyId = await getActiveCompanyId(cookies, fetch); - - if (!companyId) { - throw error(400, 'No se encontró una compañía seleccionada'); - } + // Cargar el pedimento desde el backend usando authenticatedFetch const response = await authenticatedFetch( @@ -63,15 +112,30 @@ export const load: PageServerLoad = async ({ params, cookies, fetch }) => { const pedimento = await response.json(); - // Cargar pedimento_codes - const pedimentoCodesResponse = await pedimentoCodesPromise; + // Cargar pedimento_codes y customs_sections + const [pedimentoCodesResponse, customsSectionsResponse, customsBrokersResponse, clientsResponse, codePedimentoRegimensResponse] = await Promise.all([ + pedimentoCodesPromise, + customsSectionsPromise, + customsBrokersPromise, + clientsPromise, + codePedimentoRegimensPromise + ]); + const pedimentoCodes = pedimentoCodesResponse.ok ? await pedimentoCodesResponse.json() : { items: [] }; + const customsSections = customsSectionsResponse.ok ? await customsSectionsResponse.json() : { items: [] }; + const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] }; + const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] }; + const codePedimentoRegimens = codePedimentoRegimensResponse.ok ? await codePedimentoRegimensResponse.json() : { items: [] }; return { pedimento, pedimentoId, isCreate: false, - pedimentoCodes: pedimentoCodes.items || [] + pedimentoCodes: pedimentoCodes.items || [], + customsSections: customsSections.items || [], + customsBrokers: customsBrokers.items || [], + clients: clients.items || [], + codePedimentoRegimens: codePedimentoRegimens.items || [] }; } catch (e) { console.error('Error loading pedimento:', e); diff --git a/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.svelte b/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.svelte index 3e1d4d1e..27e9f9b8 100644 --- a/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.svelte +++ b/frontend/src/routes/dashboard/pedimentos/edit/[id]/+page.svelte @@ -7,6 +7,18 @@ import { Button } from '$lib/components/ui/button'; import { Badge } from '$lib/components/ui/badge'; import { Separator } from '$lib/components/ui/separator'; + import { + ArrowLeft, + CircleAlert, + CircleCheck, + FileText, + Calendar, + CreditCard, + Truck, + ShieldCheck, + LoaderCircle, + Save + } from 'lucide-svelte'; import type { PageData } from './$types'; // Importar los componentes de cada pestaña (ahora sin botones de guardar propios) @@ -19,12 +31,20 @@ // Importar solo la API de pedimentos import { pedimentosApi, type CreatePedimentoData, type UpdatePedimentoData } from '$lib/api/dashboard/a76/pedimentos'; import type { PedimentoCode } from '$lib/api/dashboard/refrence_data/pedimento_codes'; + import type { CustomsSection } from '$lib/api/dashboard/refrence_data/customs_sections'; + import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers'; + import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers'; + import type { CodePedimentoRegimen } from '$lib/api/dashboard/refrence_data/code_pedimento_regimens'; interface ExtendedPageData { pedimentoId?: number | null; pedimento?: any; isCreate?: boolean; pedimentoCodes?: PedimentoCode[]; + customsSections?: CustomsSection[]; + customsBrokers?: CustomsBroker[]; + clients?: ClientProvider[]; + codePedimentoRegimens?: CodePedimentoRegimen[]; user?: any; companies?: any[]; authenticated?: boolean; @@ -133,8 +153,7 @@ datesFormData.payment_date || datesFormData.rectification_payment_date || datesFormData.extraction_date || datesFormData.submission_date || datesFormData.eucan_date || datesFormData.original_date || - datesFormData.start_date || datesFormData.end_date || - datesFormData.capture_date || datesFormData.capture_time; + datesFormData.start_date || datesFormData.end_date; if (hasDateValue) { payload.pedimento_dates = { @@ -148,8 +167,6 @@ original_date: datesFormData.original_date || null, start_date: datesFormData.start_date || null, end_date: datesFormData.end_date || null, - capture_date: datesFormData.capture_date || null, - capture_time: datesFormData.capture_time || null }; } } @@ -267,20 +284,7 @@

{#if data.isCreate} @@ -312,21 +316,7 @@ {#if error} - - - - - + Error {error} @@ -334,217 +324,105 @@ {#if success} - - - - + Éxito Todos los cambios se guardaron correctamente {/if} - - - - - - - - - General - - - - - - - - - Fechas - - - - - - - Pagos - - - - - - - - - - Transporte - - - - - - - Validación - - + +
+ + + + - - - + + + - - - + + + - - - + + + - - - - - - - - - - - - -
- - -
-
-
+ + + + +
+

+ + +
+
+ + + + + + General + + + + Fechas + + + + Pagos + + + + Transporte + + + + Validación + + + + + +
+ + +
+
diff --git a/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte b/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte index a72beb5f..6da7f104 100644 --- a/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/code_pedimento_regimens/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/containers/+page.svelte b/frontend/src/routes/dashboard/reference_data/containers/+page.svelte index 98af455c..060fce7d 100644 --- a/frontend/src/routes/dashboard/reference_data/containers/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/containers/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/countries/+page.svelte b/frontend/src/routes/dashboard/reference_data/countries/+page.svelte index fd5ec3ef..4a54e322 100644 --- a/frontend/src/routes/dashboard/reference_data/countries/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/countries/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/countries/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte b/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte index f47264c8..38062c10 100644 --- a/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/currency_types/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/currency_types/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,22 +121,8 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte b/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte index a45491b7..b8b95f42 100644 --- a/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/customs_sections/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte b/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte index 82b7c178..f09cab30 100644 --- a/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/customs_warehouses/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/incoterms/+page.svelte b/frontend/src/routes/dashboard/reference_data/incoterms/+page.svelte index 217d1c6c..201ce115 100644 --- a/frontend/src/routes/dashboard/reference_data/incoterms/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/incoterms/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/incoterms/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/invoice_types/+page.svelte b/frontend/src/routes/dashboard/reference_data/invoice_types/+page.svelte index 1ba35154..72f1f62c 100644 --- a/frontend/src/routes/dashboard/reference_data/invoice_types/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/invoice_types/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/invoice_types/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/material_types/+page.svelte b/frontend/src/routes/dashboard/reference_data/material_types/+page.svelte index 66589b3f..94ee7d33 100644 --- a/frontend/src/routes/dashboard/reference_data/material_types/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/material_types/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/payment_methods/+page.svelte b/frontend/src/routes/dashboard/reference_data/payment_methods/+page.svelte index 860a48f3..a4800ec3 100644 --- a/frontend/src/routes/dashboard/reference_data/payment_methods/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/payment_methods/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/pedimento_codes/+page.svelte b/frontend/src/routes/dashboard/reference_data/pedimento_codes/+page.svelte index dec3a448..3761f69a 100644 --- a/frontend/src/routes/dashboard/reference_data/pedimento_codes/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/pedimento_codes/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/pedimento_codes/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,22 +121,8 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/pedimento_regimens/+page.svelte b/frontend/src/routes/dashboard/reference_data/pedimento_regimens/+page.svelte index 39d1a31f..e636522f 100644 --- a/frontend/src/routes/dashboard/reference_data/pedimento_regimens/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/pedimento_regimens/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte b/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte index 3621eb21..fd4fe48a 100644 --- a/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/sectors/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/sectors/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/states/+page.svelte b/frontend/src/routes/dashboard/reference_data/states/+page.svelte index b164088c..82b50e2d 100644 --- a/frontend/src/routes/dashboard/reference_data/states/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/states/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/transport_modes/+page.svelte b/frontend/src/routes/dashboard/reference_data/transport_modes/+page.svelte index 774372d8..fd4c7553 100644 --- a/frontend/src/routes/dashboard/reference_data/transport_modes/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/transport_modes/+page.svelte @@ -8,6 +8,7 @@ import { Button } from '$lib/components/ui/button'; import type { PageData } from './$types'; import { browser } from '$app/environment'; + import { Plus, RefreshCw } from 'lucide-svelte'; // Los datos iniciales vienen del servidor let { data }: { data: PageData } = $props(); @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/transport_types/+page.svelte b/frontend/src/routes/dashboard/reference_data/transport_types/+page.svelte index 28c96469..a8a57d47 100644 --- a/frontend/src/routes/dashboard/reference_data/transport_types/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/transport_types/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/transport_types/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@ diff --git a/frontend/src/routes/dashboard/reference_data/valuation_methods/+page.svelte b/frontend/src/routes/dashboard/reference_data/valuation_methods/+page.svelte index 81e18284..380c9771 100644 --- a/frontend/src/routes/dashboard/reference_data/valuation_methods/+page.svelte +++ b/frontend/src/routes/dashboard/reference_data/valuation_methods/+page.svelte @@ -6,6 +6,7 @@ import CreateEditDialog from '$lib/components/dashboard/reference_data/valuation_methods/create-edit-dialog.svelte'; import * as Card from '$lib/components/ui/card'; import { Button } from '$lib/components/ui/button'; + import { Plus, RefreshCw } from 'lucide-svelte'; import type { PageData } from './$types'; import { browser } from '$app/environment'; @@ -120,21 +121,7 @@

@@ -160,21 +147,7 @@