feat(validators): add validation for required fields based on operation type in invoice processing
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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"
|
||||
)
|
||||
|
||||
@@ -7,11 +7,12 @@
|
||||
import type { InvoiceType } from '$lib/api/dashboard/refrence_data/invoice_types';
|
||||
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
|
||||
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
|
||||
import {
|
||||
"sidebar.client_provider_type.client_indicator" as clientIndicator,
|
||||
"sidebar.client_provider_type.provider_indicator" as providerIndicator,
|
||||
"sidebar.client_provider_type.both_indicator" as bothIndicator
|
||||
} from '$lib/paraglide/messages.js';
|
||||
|
||||
interface CodePedimentoRegimen {
|
||||
regimen_code: string;
|
||||
type_code: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
let {
|
||||
invoice,
|
||||
@@ -27,6 +28,7 @@
|
||||
customsSections = [],
|
||||
codePedimentoRegimens = [],
|
||||
operationType = undefined,
|
||||
defaultOperationType = undefined,
|
||||
exchangeRate = undefined
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
@@ -42,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;
|
||||
@@ -200,21 +202,35 @@
|
||||
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.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
|
||||
const uniqueMap = new Map();
|
||||
filtered.forEach(r => {
|
||||
if (r.regimen_code && !uniqueMap.has(r.regimen_code)) {
|
||||
uniqueMap.set(r.regimen_code, r);
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(uniqueMap.values());
|
||||
});
|
||||
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(() => {
|
||||
@@ -284,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]">
|
||||
@@ -330,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]">
|
||||
@@ -376,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' ? clientIndicator() : cp.type === 'provider' ? providerIndicator() : bothIndicator()})
|
||||
{cp.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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 };
|
||||
@@ -130,10 +130,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 +180,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,
|
||||
|
||||
@@ -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';
|
||||
@@ -169,7 +170,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, {
|
||||
@@ -259,7 +260,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>
|
||||
|
||||
Reference in New Issue
Block a user