feat: Enhance Fixed Asset Class form with blur-based lookups and US fraction masking, and refine pedimento creation flow and button labels.
This commit is contained in:
@@ -109,6 +109,10 @@
|
||||
formData.import_tariff_type = snap.import_tariff_type ?? '';
|
||||
formData.export_tariff_code = snap.export_tariff_code ?? '';
|
||||
formData.export_tariff_type = snap.export_tariff_type ?? '';
|
||||
|
||||
// Reiniciar banderas de error al cargar datos
|
||||
showErrors = false;
|
||||
validationErrors = {};
|
||||
} else {
|
||||
// Reset form when initialData is null (new class)
|
||||
formData.class_code = '';
|
||||
@@ -128,12 +132,17 @@
|
||||
formData.import_tariff_type = '';
|
||||
formData.export_tariff_code = '';
|
||||
formData.export_tariff_type = '';
|
||||
|
||||
// Reiniciar banderas de error al limpiar formulario
|
||||
showErrors = false;
|
||||
validationErrors = {};
|
||||
}
|
||||
});
|
||||
|
||||
// Estado de validación
|
||||
let validationErrors = $state<Record<string, string>>({});
|
||||
let showErrors = $state(false);
|
||||
let isSubmitting = $state(false);
|
||||
|
||||
// Estado de diálogos
|
||||
let showMaterialDialog = $state(false);
|
||||
@@ -262,6 +271,37 @@
|
||||
searchMaterial = '';
|
||||
}
|
||||
|
||||
// Fix 1: Buscar descripción de Tipo de Activo Fijo al perder el foco
|
||||
async function handleMaterialBlur() {
|
||||
validateField('material_key');
|
||||
const code = formData.material_key?.trim().toUpperCase();
|
||||
if (!code) {
|
||||
formData.material_description = '';
|
||||
return;
|
||||
}
|
||||
// Primero buscar en la caché local (si ya se cargó el catálogo)
|
||||
if (materialTypes.length > 0) {
|
||||
const found = materialTypes.find((m) => m.key.toUpperCase() === code);
|
||||
if (found) {
|
||||
formData.material_key = found.key;
|
||||
formData.material_description = found.description;
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Si no está en caché, consultar el API
|
||||
try {
|
||||
const response = await materialTypesApi.get(code);
|
||||
if (response.data) {
|
||||
formData.material_key = response.data.key;
|
||||
formData.material_description = response.data.description;
|
||||
} else {
|
||||
formData.material_description = '(Código no encontrado)';
|
||||
}
|
||||
} catch {
|
||||
formData.material_description = '(Código no encontrado)';
|
||||
}
|
||||
}
|
||||
|
||||
async function openUnitOfMeasureSearch() {
|
||||
showUnitDialog = true;
|
||||
searchUnit = '';
|
||||
@@ -276,6 +316,39 @@
|
||||
searchUnit = '';
|
||||
}
|
||||
|
||||
// Fix 1: Buscar descripción de U.M. Comercial al perder el foco
|
||||
async function handleUnitBlur() {
|
||||
validateField('unit_of_measure');
|
||||
const code = formData.unit_of_measure?.trim().toUpperCase();
|
||||
if (!code) {
|
||||
formData.unit_of_measure_description = '';
|
||||
return;
|
||||
}
|
||||
// Primero buscar en la caché local (si ya se cargó el catálogo)
|
||||
await loadUnitsOfMeasure();
|
||||
const found = unitsOfMeasureData.find((u) => u.code.toUpperCase() === code);
|
||||
if (found) {
|
||||
formData.unit_of_measure = found.code;
|
||||
formData.unit_of_measure_description = found.description;
|
||||
formData.unit_measure_key = found.claveMexicana;
|
||||
} else {
|
||||
formData.unit_of_measure_description = '(Código no encontrado)';
|
||||
}
|
||||
}
|
||||
|
||||
// Fix 2: Máscara para Fracción Americana (formato 0000.00.00.00 = 13 chars)
|
||||
function formatUSFraction(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
// Extraer solo dígitos
|
||||
const digits = input.value.replace(/\D/g, '').slice(0, 10);
|
||||
// Construir la máscara insertando puntos en las posiciones correctas
|
||||
let masked = digits;
|
||||
if (digits.length > 4) masked = digits.slice(0, 4) + '.' + digits.slice(4);
|
||||
if (digits.length > 6) masked = digits.slice(0, 4) + '.' + digits.slice(4, 6) + '.' + digits.slice(6);
|
||||
if (digits.length > 8) masked = digits.slice(0, 4) + '.' + digits.slice(4, 6) + '.' + digits.slice(6, 8) + '.' + digits.slice(8);
|
||||
formData.us_fraction = masked;
|
||||
}
|
||||
|
||||
async function openUSFractionSearch() {
|
||||
showUSFractionDialog = true;
|
||||
searchUSFraction = '';
|
||||
@@ -451,7 +524,8 @@
|
||||
|
||||
// Validar campo individual (para validación en blur)
|
||||
function validateField(fieldName: string) {
|
||||
if (!showErrors) return; // Solo validar si ya se intentó guardar
|
||||
// Bloquear validación repetida si: a) no se ha intentado guardar o b) está guardando
|
||||
if (!showErrors || isSubmitting) return;
|
||||
|
||||
const errors = { ...validationErrors };
|
||||
|
||||
@@ -496,22 +570,28 @@
|
||||
validationErrors = errors;
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
// Activar visualización de errores
|
||||
showErrors = true;
|
||||
|
||||
async function handleSave() {
|
||||
// Validar formulario
|
||||
if (!validateForm()) {
|
||||
showErrors = true;
|
||||
toast.error('Por favor, complete todos los campos obligatorios');
|
||||
return;
|
||||
}
|
||||
|
||||
// Activar bandera the submitting para que no se validen inputs al azar en el blur
|
||||
isSubmitting = true;
|
||||
|
||||
// Tomamos una copia muerta de los datos actuales
|
||||
const dataToSave = $state.snapshot(formData);
|
||||
|
||||
// Ejecutamos el onSave pasándole la copia
|
||||
if (onSave) {
|
||||
onSave(dataToSave);
|
||||
try {
|
||||
// Ejecutamos el onSave pasándole la copia. Await en caso de que devuelva promesa.
|
||||
if (onSave) {
|
||||
await onSave(dataToSave);
|
||||
}
|
||||
} finally {
|
||||
// Liberar el estado de subida solo después de que acabe todo el flujo
|
||||
isSubmitting = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,9 +602,17 @@
|
||||
}
|
||||
|
||||
// Escuchar el evento de guardado del padre
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
if (typeof document !== 'undefined') {
|
||||
document.addEventListener('save-form', handleSave);
|
||||
}
|
||||
|
||||
onDestroy(() => {
|
||||
if (typeof document !== 'undefined') {
|
||||
document.removeEventListener('save-form', handleSave);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="space-y-3">
|
||||
@@ -569,7 +657,7 @@
|
||||
? 'border-red-500 focus-visible:ring-red-500'
|
||||
: ''}"
|
||||
maxlength={10}
|
||||
onblur={() => validateField('material_key')}
|
||||
onblur={handleMaterialBlur}
|
||||
/>
|
||||
<Button type="button" variant="outline" size="icon" onclick={openMaterialSearch}>
|
||||
<Folder class="h-4 w-4" />
|
||||
@@ -629,7 +717,7 @@
|
||||
? 'border-red-500 focus-visible:ring-red-500'
|
||||
: ''}"
|
||||
maxlength={5}
|
||||
onblur={() => validateField('unit_of_measure')}
|
||||
onblur={handleUnitBlur}
|
||||
/>
|
||||
<Button type="button" variant="outline" size="icon" onclick={openUnitOfMeasureSearch}>
|
||||
<Folder class="h-4 w-4" />
|
||||
@@ -676,9 +764,10 @@
|
||||
<Input
|
||||
id="us_fraction"
|
||||
bind:value={formData.us_fraction}
|
||||
placeholder="Fracción americana"
|
||||
class="flex-1"
|
||||
maxlength={16}
|
||||
placeholder="0000.00.00.00"
|
||||
class="flex-1 font-mono tracking-wider"
|
||||
maxlength={13}
|
||||
oninput={formatUSFraction}
|
||||
/>
|
||||
<Button type="button" variant="outline" size="icon" onclick={openUSFractionSearch}>
|
||||
<Folder class="h-4 w-4" />
|
||||
|
||||
@@ -1016,7 +1016,7 @@
|
||||
</Button>
|
||||
<Button size="sm" onclick={handleAdd} type="button">
|
||||
<Plus class="mr-1 h-4 w-4" />
|
||||
Agregar Item
|
||||
Agregar Partidas
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user