Merge branch 'fix/invoices-both' into development

This commit is contained in:
2026-01-23 12:46:50 -06:00
14 changed files with 304 additions and 164 deletions

View File

@@ -350,7 +350,6 @@ class ClientProviderService:
)
if client_or_provider:
from .models import ClientOrProviderEnum
query = query.filter(
or_(
ClientProvider.client_or_provider == client_or_provider,

View File

@@ -12,6 +12,76 @@ from api.v1.modules.public.reference_data.currency_types.models import CurrencyT
from api.v1.modules.public.reference_data.customs_sections.models import CustomsSection
from ....models import TransportType, Currency, WeightUnit
from core.exceptions import ErrorCollector
from typing import Dict, Any
def validate_required_fields_by_operation(
invoice_data: Dict[str, Any],
operation_type: str,
errors: ErrorCollector
) -> None:
"""
Valida campos obligatorios según tipo de operación.
Usar ANTES de guardar en BD.
"""
# PROVEEDOR (SIEMPRE OBLIGATORIO - mensaje dinámico)
if not invoice_data.get('provider_id'):
# Mensaje dinámico según el header seleccionado
provider_labels = {
'proveedor': 'Proveedor',
'exportador': 'Exportador'
}
provider_header = invoice_data.get('provider_header') or 'proveedor'
field_label = provider_labels.get(provider_header, 'Proveedor')
errors.add_error(
field="provider_id",
message=f"Debe seleccionar {field_label}",
solution=["Seleccione un proveedor de la lista desplegable"],
code="REQUIRED",
value=None
)
# VENDIDO A / CONSIGNADO A (SIEMPRE OBLIGATORIO - mensaje dinámico)
if not invoice_data.get('sold_to_id'):
# Mensaje dinámico según el header seleccionado
sold_to_labels = {
'consignado_a': 'Consignado a',
'vendido_a': 'Vendido a',
'exportado_a': 'Exportado a',
'importador': 'Importador'
}
sold_to_header = invoice_data.get('sold_to_header') or 'consignado_a'
field_label = sold_to_labels.get(sold_to_header, 'Cliente')
errors.add_error(
field="sold_to_id",
message=f"Debe seleccionar {field_label}",
solution=["Seleccione una opción de la lista desplegable"],
code="REQUIRED",
value=None
)
# ENVIADO A (SIEMPRE OBLIGATORIO - mensaje fijo)
if not invoice_data.get('shipped_to_id'):
errors.add_error(
field="shipped_to_id",
message="Debe seleccionar el Destinatario",
solution=["Seleccione un destinatario de la lista desplegable"],
code="REQUIRED",
value=None
)
# AGENTE ADUANAL (OBLIGATORIO si hay pedimento)
if invoice_data.get('pedimento_id') and not invoice_data.get('customs_broker_id'):
errors.add_error(
field="customs_broker_id",
message="Debe seleccionar un Agente Aduanal",
solution=["Seleccione un agente aduanal de la lista desplegable"],
code="REQUIRED",
value=None
)
def validate_common(
@@ -245,78 +315,87 @@ def validate_common(
value=invoice.document_type,
)
provider_exists = (
db.query(ClientProvider)
.filter(
ClientProvider.id == invoice.compliance_mx.provider_id,
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
)
.first()
)
if not provider_exists:
errors.add_error(
field="compliance_mx.provider_id",
message="El Proveedor no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Proveedor", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.provider_id,
# Validar proveedor solo si se proporciona
if invoice.compliance_mx.provider_id:
provider_exists = (
db.query(ClientProvider)
.filter(
ClientProvider.id == invoice.compliance_mx.provider_id,
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
)
.first()
)
if not provider_exists:
errors.add_error(
field="compliance_mx.provider_id",
message="El Proveedor no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Proveedor", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.provider_id,
)
selled_to_exists = (
db.query(ClientProvider)
.filter(
ClientProvider.id == invoice.compliance_mx.sold_to_id,
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
)
.first()
)
if not selled_to_exists:
errors.add_error(
field="compliance_mx.sold_to_id",
message="El Cliente no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Cliente", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.sold_to_id,
# Validar vendido a solo si se proporciona
if invoice.compliance_mx.sold_to_id:
selled_to_exists = (
db.query(ClientProvider)
.filter(
ClientProvider.id == invoice.compliance_mx.sold_to_id,
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
)
.first()
)
if not selled_to_exists:
errors.add_error(
field="compliance_mx.sold_to_id",
message="El Cliente no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Cliente", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.sold_to_id,
)
shipped_to_exists = (
db.query(ClientProvider)
.filter(
ClientProvider.id == invoice.compliance_mx.shipped_to_id,
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
)
.first()
)
if not shipped_to_exists:
errors.add_error(
field="compliance_mx.shipped_to_id",
message="El Destinatario no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Destinatario", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.shipped_to_id,
# Validar destinatario solo si se proporciona
if invoice.compliance_mx.shipped_to_id:
shipped_to_exists = (
db.query(ClientProvider)
.filter(
ClientProvider.id == invoice.compliance_mx.shipped_to_id,
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
)
.first()
)
if not shipped_to_exists:
errors.add_error(
field="compliance_mx.shipped_to_id",
message="El Destinatario no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Destinatario", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.shipped_to_id,
)
customs_broker_exists = (
db.query(CustomsBroker)
.filter(
CustomsBroker.id == invoice.compliance_mx.customs_broker_id,
CustomsBroker.tenant_id == tenant_id,
CustomsBroker.company_id == company_id,
)
.first()
)
if not customs_broker_exists:
errors.add_error(
field="compliance_mx.customs_broker_id",
message="El Agente Aduanal no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Agente Aduanal", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.customs_broker_id,
# Validar agente aduanal solo si se proporciona
if invoice.compliance_mx.customs_broker_id:
customs_broker_exists = (
db.query(CustomsBroker)
.filter(
CustomsBroker.id == invoice.compliance_mx.customs_broker_id,
CustomsBroker.tenant_id == tenant_id,
CustomsBroker.company_id == company_id,
)
.first()
)
if not customs_broker_exists:
errors.add_error(
field="compliance_mx.customs_broker_id",
message="El Agente Aduanal no existe en el Catálogo de Clientes y Proveedores.",
solution=["Verifica el ID del Agente Aduanal", "Revisa el catálogo"],
code="NOT_FOUND",
value=invoice.compliance_mx.customs_broker_id,
)
# Validar transportista solo si se proporciona
if invoice.logistics.carrier_id:
carrier_exists = (
db.query(ClientProvider)

View File

@@ -3,7 +3,7 @@ from sqlalchemy.orm import Session
from api.v1.modules.a76.general_catalogs.exchange_rate.models import ExchangeRate
from core.exceptions import ErrorCollector
from ....schemas import InvoiceHeaderCreate
from .common import validate_common
from .common import validate_common, validate_required_fields_by_operation
def validate_create(db: Session, invoice: InvoiceHeaderCreate, tenant_id: int, company_id: int, errors: ErrorCollector) -> None:
""" Valida la creación de una nueva factura de importe temporal """
@@ -21,22 +21,31 @@ def validate_create(db: Session, invoice: InvoiceHeaderCreate, tenant_id: int, c
errors.add_required_error("invoice_number")
if not invoice.invoice_date:
errors.add_required_error("invoice_date")
if not invoice.compliance_mx.provider_id:
errors.add_required_error("compliance_mx.provider_id")
errors.add_required_error("invoice_date")
if not invoice.compliance_mx.sold_to_id:
errors.add_required_error("compliance_mx.sold_to_id")
if not invoice.compliance_mx.shipped_to_id:
errors.add_required_error("compliance_mx.shipped_to_id")
if not invoice.compliance_mx.customs_broker_id:
errors.add_required_error("compliance_mx.customs_broker_id")
if errors.has_errors():
"""Se retorna por que hay campos obligatiorios para las validaciones que tienen que ser llenados"""
"""Se retorna porque hay campos obligatorios básicos que deben ser llenados"""
return
# Validar campos obligatorios según tipo de operación
invoice_data = {
'provider_header': invoice.compliance_mx.provider_header if invoice.compliance_mx else None,
'provider_id': invoice.compliance_mx.provider_id if invoice.compliance_mx else None,
'sold_to_id': invoice.compliance_mx.sold_to_id if invoice.compliance_mx else None,
'sold_to_header': invoice.compliance_mx.sold_to_header if invoice.compliance_mx else None,
'shipped_to_id': invoice.compliance_mx.shipped_to_id if invoice.compliance_mx else None,
'customs_broker_id': invoice.compliance_mx.customs_broker_id if invoice.compliance_mx else None,
'pedimento_id': invoice.compliance_mx.pedimento_id if invoice.compliance_mx else None,
}
validate_required_fields_by_operation(
invoice_data=invoice_data,
operation_type=invoice.operation_type,
errors=errors
)
if errors.has_errors():
"""Se retorna porque hay campos obligatorios según el tipo de operación que deben ser llenados"""
return
validate_common(db, invoice, tenant_id, company_id, errors)

View File

@@ -5,6 +5,7 @@ from decimal import Decimal
from core.exceptions import ErrorCollector
from ....schemas import InvoiceHeaderUpdate
from ....models import InvoiceHeader
from .common import validate_required_fields_by_operation
# Helper function para limpiar strings (equivalente a Clip())
@@ -33,6 +34,22 @@ def validate_update(
None (modifica invoice_data in-place y acumula errores en errors)
"""
# Validar campos requeridos según el tipo de operación
invoice_dict = {
'provider_id': invoice_data.compliance_mx.provider_id if invoice_data.compliance_mx else None,
'sold_to_id': invoice_data.compliance_mx.sold_to_id if invoice_data.compliance_mx else None,
'sold_to_header': invoice_data.compliance_mx.sold_to_header if invoice_data.compliance_mx else None,
'shipped_to_id': invoice_data.compliance_mx.shipped_to_id if invoice_data.compliance_mx else None,
'customs_broker_id': invoice_data.compliance_mx.customs_broker_id if invoice_data.compliance_mx else None,
'pedimento_id': invoice_data.compliance_mx.pedimento_id if invoice_data.compliance_mx else None,
}
validate_required_fields_by_operation(
invoice_data=invoice_dict,
operation_type=invoice_data.operation_type or 'IMP',
errors=errors
)
# Primero ejecutar validaciones comunes
# validate_common(invoice_data, errors)

View File

@@ -113,17 +113,17 @@ class InvoiceComplianceMxBase(BaseModel):
manifest_number: Optional[str] = Field(
None, max_length=15, description="Manifest number"
)
provider_header: str = Field(None, max_length=20, description="Provider header")
provider_id: int = Field(None, description="Provider ID")
sold_to_header: str = Field(None, max_length=20, description="Sold to header")
sold_to_id: int = Field(None, description="Sold to ID")
shipped_to_header: str = Field(None, max_length=20, description="Shipped to header")
shipped_to_id: int = Field(None, description="Shipped to ID")
provider_header: Optional[str] = Field(None, max_length=20, description="Provider header")
provider_id: Optional[int] = Field(None, description="Provider ID")
sold_to_header: Optional[str] = Field(None, max_length=20, description="Sold to header")
sold_to_id: Optional[int] = Field(None, description="Sold to ID")
shipped_to_header: Optional[str] = Field(None, max_length=20, description="Shipped to header")
shipped_to_id: Optional[int] = Field(None, description="Shipped to ID")
shipped_by_header: Optional[int] = Field(
None, max_length=20, description="Shipped by header"
)
shipped_by_id: Optional[int] = Field(None, description="Shipped by ID")
customs_broker_id: int = Field(None, description="Customs broker ID")
customs_broker_id: Optional[int] = Field(None, description="Customs broker ID")
customs_broker_us_id: Optional[int] = Field(
None, description="US customs broker ID"
)

View File

@@ -91,6 +91,11 @@
},
"clients_and_providers": "Clients and Providers",
"customs_brokers": "Customs Brokers",
"client_provider_type": {
"client_indicator": "C",
"provider_indicator": "P",
"both_indicator": "B"
},
"nav_user": {
"profile": "Profile",
"settings": "Settings",

View File

@@ -91,6 +91,11 @@
},
"clients_and_providers": "Clientes y Proveedores",
"customs_brokers": "Agentes Aduanales",
"client_provider_type": {
"client_indicator": "C",
"provider_indicator": "P",
"both_indicator": "A"
},
"nav_user": {
"profile": "Perfil",
"settings": "Configuración"

View File

@@ -8,6 +8,12 @@
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
interface CodePedimentoRegimen {
regimen_code: string;
type_code: string;
[key: string]: any;
}
let {
invoice,
formData = $bindable(),
@@ -22,6 +28,7 @@
customsSections = [],
codePedimentoRegimens = [],
operationType = undefined,
defaultOperationType = undefined,
exchangeRate = undefined
}: {
invoice: Invoice | null;
@@ -37,8 +44,8 @@
drivers?: any[];
trailers?: any[];
customsSections?: any[];
codePedimentoRegimens?: any[];
defaultOperationType?: string | null;
codePedimentoRegimens?: CodePedimentoRegimen[];
defaultOperationType?: string | number | null;
defaultInvoiceType?: string | null;
operationType?: number | null;
exchangeRate?: number | null;
@@ -176,25 +183,55 @@
]
);
// Combinar clientes y proveedores para shipped_to
const allClientsProviders = [...clients, ...providers];
// Filtrar regímenes por tipo de operación (1='E' exp, 2='I' imp) y obtener valores únicos
const filteredRegimens = $derived.by(() => {
const typeCode = operationType === 1 ? 'E' : operationType === 2 ? 'I' : null;
const filtered = codePedimentoRegimens.filter(r => r.type_code === typeCode);
// Obtener solo regímenes únicos por regimen_code
// Combinar clientes y proveedores para shipped_to, evitando duplicados de tipo "both"
const allClientsProviders = $derived.by(() => {
const uniqueMap = new Map();
filtered.forEach(r => {
if (r.regimen_code && !uniqueMap.has(r.regimen_code)) {
uniqueMap.set(r.regimen_code, r);
// Agregar todos los clientes
clients.forEach(c => {
uniqueMap.set(c.id, { ...c, type: c.client_or_provider });
});
// Agregar proveedores solo si no existen (evita duplicados de "both")
providers.forEach(p => {
if (!uniqueMap.has(p.id)) {
uniqueMap.set(p.id, { ...p, type: p.client_or_provider });
}
});
return Array.from(uniqueMap.values());
});
// Determinar tipo de código basado en operationType prop, defaultOperationType o invoice.operation_type
const typeCode = $derived(
operationType === 1 ? 'E'
: operationType === 2 ? 'I'
: defaultOperationType === 1 ? 'E'
: defaultOperationType === 2 ? 'I'
: defaultOperationType === 'exp' ? 'E'
: defaultOperationType === 'imp' ? 'I'
: invoice?.operation_type === 'exp' ? 'E'
: invoice?.operation_type === 'imp' ? 'I'
: null
);
// Filtrar regímenes por tipo de operación (1='E' exp, 2='I' imp) y obtener valores únicos
const filteredRegimens = $derived<CodePedimentoRegimen[]>(
!codePedimentoRegimens || codePedimentoRegimens.length === 0 || !typeCode
? []
: Array.from(
codePedimentoRegimens
.filter(r => r.type_code === typeCode)
.reduce((map, r) => {
if (r.regimen_code && !map.has(r.regimen_code)) {
map.set(r.regimen_code, r);
}
return map;
}, new Map<string, CodePedimentoRegimen>())
.values()
)
);
// Efecto: Limpiar régimen si no existe en los regímenes filtrados al cambiar operation_type
$effect(() => {
if (formData.document_type && filteredRegimens.length > 0) {
@@ -263,9 +300,11 @@
>
<Select.Trigger id="provider_id" class="h-7 text-xs min-w-[120px] max-w-[250px]">
<span class="truncate">
{formData.provider_id
? providers.find(p => p.id === formData.provider_id)?.name || 'Selecciona...'
: 'Selecciona...'}
{#if formData.provider_id}
{providers.find(p => p.id === formData.provider_id)?.name || 'Selecciona...'}
{:else}
Selecciona...
{/if}
</span>
</Select.Trigger>
<Select.Content class="max-h-[300px]">
@@ -309,9 +348,11 @@
>
<Select.Trigger id="sold_to_id" class="h-7 text-xs min-w-[120px] max-w-[250px]">
<span class="truncate">
{formData.sold_to_id
? clients.find(c => c.id === formData.sold_to_id)?.name || 'Selecciona...'
: 'Selecciona...'}
{#if formData.sold_to_id}
{clients.find(c => c.id === formData.sold_to_id)?.name || 'Selecciona...'}
{:else}
Selecciona...
{/if}
</span>
</Select.Trigger>
<Select.Content class="max-h-[300px]">
@@ -355,15 +396,17 @@
>
<Select.Trigger id="shipped_to_id" class="h-7 text-xs min-w-[120px] max-w-[250px]">
<span class="truncate">
{formData.shipped_to_id
? allClientsProviders.find(cp => cp.id === formData.shipped_to_id)?.name || 'Selecciona...'
: 'Selecciona...'}
{#if formData.shipped_to_id}
{allClientsProviders.find(cp => cp.id === formData.shipped_to_id)?.name || 'Selecciona...'}
{:else}
Selecciona...
{/if}
</span>
</Select.Trigger>
<Select.Content class="max-h-[300px]">
{#each allClientsProviders as cp}
<Select.Item value={String(cp.id)}>
{cp.name} ({cp.type === 'client' ? 'C' : 'P'})
{cp.name}
</Select.Item>
{/each}
</Select.Content>

View File

@@ -46,6 +46,16 @@
}
});
// Efecto para actualizar operation_type cuando cambia defaultOperationType
$effect(() => {
if (formData && defaultOperationType !== undefined && defaultOperationType !== null) {
// Si operation_type está vacío, null, o undefined, actualizarlo con defaultOperationType
if (!formData.operation_type) {
formData.operation_type = defaultOperationType;
}
}
});
if (!formData) {
let operationType: string | null = null;
if (invoice?.operation_type) {
@@ -69,6 +79,11 @@
clave_pedimento: '',
regimen_pedimento: '',
};
} else {
// Si formData ya existe pero operation_type está vacío, usar defaultOperationType
if (!formData.operation_type && defaultOperationType !== undefined && defaultOperationType !== null) {
formData.operation_type = defaultOperationType;
}
}
</script>

View File

@@ -97,7 +97,7 @@ export async function saveInvoice(options: SaveInvoiceOptions): Promise<SaveInvo
}
return { success: true, newInvoiceId: newInvoiceId ?? undefined };
} catch (e) {
} catch (e) {
const error = e instanceof Error ? e.message : 'Error al guardar los cambios';
const validationErrors = (e as any)?.validationErrors;
return { success: false, error, validationErrors };
@@ -110,9 +110,7 @@ function buildInvoicePayload(formData: FormDataSet): CreateInvoiceData | UpdateI
const payload: any = {
// Datos generales desde InvoiceTopFieldsFormData
system: 'fixed_asset',
operation_type: InvoiceTopFieldsFormData?.operation_type !== null && InvoiceTopFieldsFormData?.operation_type !== undefined
? (InvoiceTopFieldsFormData.operation_type === 1 ? 'exp' : 'imp') as OperationType
: undefined,
operation_type: InvoiceTopFieldsFormData?.operation_type || undefined,
invoice_type: InvoiceTopFieldsFormData?.invoice_type || undefined,
document_type: generalFormData?.document_type || undefined,
invoice_number: InvoiceTopFieldsFormData?.invoice_number || undefined,
@@ -130,10 +128,11 @@ function buildInvoicePayload(formData: FormDataSet): CreateInvoiceData | UpdateI
// Solo agregar sub-recursos si tienen valores reales
// Compliance MX
// Compliance MX - Siempre incluir si hay headers o valores
const hasComplianceValue = InvoiceTopFieldsFormData?.pedimento_id || InvoiceTopFieldsFormData?.remesa || generalFormData?.aduana ||
generalFormData?.provider_id || generalFormData?.sold_to_id ||
generalFormData?.shipped_to_id || generalFormData?.customs_broker_id ||
generalFormData?.provider_header || generalFormData?.sold_to_header || generalFormData?.shipped_to_header ||
observationFormData?.movement_type || observationFormData?.enclosure ||
othersFormData?.is_mixed || othersFormData?.contingency_mode ||
othersFormData?.cove || othersFormData?.operation_num ||
@@ -179,11 +178,11 @@ function buildComplianceMxData(InvoiceTopFieldsFormData: any, generalFormData: a
// Fields from generalFormData
aduana: generalFormData?.aduana || null,
port_of_entry: continuationFormData?.puerto_entrada || null,
provider_header: generalFormData?.provider_header || '',
provider_header: generalFormData?.provider_header || null,
provider_id: generalFormData?.provider_id || null,
sold_to_header: generalFormData?.sold_to_header || '',
sold_to_header: generalFormData?.sold_to_header || null,
sold_to_id: generalFormData?.sold_to_id || null,
shipped_to_header: generalFormData?.shipped_to_header || '',
shipped_to_header: generalFormData?.shipped_to_header || null,
shipped_to_id: generalFormData?.shipped_to_id || null,
customs_broker_id: generalFormData?.customs_broker_id ? Number(generalFormData.customs_broker_id) : null,
customs_broker_us_id: generalFormData?.customs_broker_us_id ? Number(generalFormData.customs_broker_us_id) : null,

View File

@@ -240,36 +240,6 @@ export function createColumns(onSuccess?: () => void): ColumnDef<Pedimento>[] {
return renderSnippet(dateSnippet, { date: formatDate(row.original.pedimento_dates?.payment_date) });
}
},
/*{
accessorKey: "pedimento_config_update_rectification.pediment_rectifed_18",
header: "Pedimento 18",
meta: { className: "hidden lg:table-cell" },
cell: ({ row }) => {
const ped18Snippet = createRawSnippet<[{ value?: string | null }]>((getValue) => {
const { value } = getValue();
return {
render: () =>
`<div class="text-sm">${value || '-'}</div>`
};
});
return renderSnippet(ped18Snippet, { value: row.original.pedimento_config_update_rectification?.pediment_rectifed_18 });
}
},*/
/*{
accessorKey: "pedimento_config_update_rectification.r1",
header: "Pedimento R1",
meta: { className: "hidden lg:table-cell" },
cell: ({ row }) => {
const r1Snippet = createRawSnippet<[{ value?: string | null }]>((getValue) => {
const { value } = getValue();
return {
render: () =>
`<div class="text-sm">${value || '-'}</div>`
};
});
return renderSnippet(r1Snippet, { value: row.original.pedimento_config_update_rectification?.r1 });
}
},*/
{
accessorKey: "pedimento_validation.electronic_signature",
header: "Acuse Electrónico",

View File

@@ -427,11 +427,6 @@
function handleCreateClick() {
const params = new URLSearchParams(window.location.search);
const operationType = params.get('operation_type');
if (operationType) {
const operationTypeNumber = operationType === 'exp' ? 1 : 2;
params.set('operation_type', operationTypeNumber.toString());
}
const queryString = params.toString();
const url = queryString

View File

@@ -20,13 +20,10 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
const operationTypeParam = url.searchParams.get('operation_type');
const invoiceTypeParam = url.searchParams.get('invoice_type');
// Parsear operation_type de forma segura
let parsedOperationType: number | null = null;
if (operationTypeParam) {
const parsed = parseInt(operationTypeParam, 10);
if (!isNaN(parsed)) {
parsedOperationType = parsed;
}
// Validar que operation_type sea 'exp' o 'imp'
let parsedOperationType: string | null = null;
if (operationTypeParam && (operationTypeParam === 'exp' || operationTypeParam === 'imp')) {
parsedOperationType = operationTypeParam;
}
// Cargar datos de referencia necesarios

View File

@@ -1,7 +1,8 @@
<script lang="ts">
import { onMount } from 'svelte';
import { browser } from '$app/environment';
import { goto } from '$app/navigation';
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import * as Tabs from '$lib/components/ui/tabs';
import { Button } from '$lib/components/ui/button';
import { Badge } from '$lib/components/ui/badge';
@@ -237,7 +238,7 @@
if (e && typeof e === 'object' && 'validationErrors' in e && Array.isArray((e as any).validationErrors)) {
const validationErrors = (e as any).validationErrors;
const errorList = validationErrors.map((err: any) =>
`• ${err.field}: ${err.message}${err.solution ? ' - ' + err.solution.join(', ') : ''}`
`• ${err.message}`
).join('\n');
toast.error(errorMessage, {
@@ -327,7 +328,13 @@
codePedimentoRegimens={data.codePedimentoRegimens || []}
defaultOperationType={data.filters?.operation_type ?? undefined}
defaultInvoiceType={data.filters?.invoice_type ?? undefined}
operationType={InvoiceTopFieldsFormData?.operation_type}
operationType={
InvoiceTopFieldsFormData?.operation_type === 'exp' ? 1
: InvoiceTopFieldsFormData?.operation_type === 'imp' ? 2
: data.invoice?.operation_type === 'exp' ? 1
: data.invoice?.operation_type === 'imp' ? 2
: undefined
}
exchangeRate={calculatedExchangeRate}
/>
</Tabs.Content>