Se termino de incorprar la edicion y la creacion en DODA, ademas de acortar el nombre de unidades

This commit is contained in:
2025-12-31 09:25:10 -06:00
parent 9741d586d7
commit f35f0b005a
4 changed files with 250 additions and 136 deletions

View File

@@ -41,11 +41,11 @@
"valuation_methods": "Metódos de valoración",
"countries": "Países",
"ports": "Puertos",
"unit_measures": "Unidades de medida general",
"um_customs_mex": "Unidades de medida - Aduanas Mexicanas",
"um_customs_ame": "Unidades de medida - Aduanas Americanas",
"um_ace": "Unidades de medida - ACE",
"um_oma": "Unidades de medida - OMA",
"unit_measures": "UM general",
"um_customs_mex": "UM Aduanas MX",
"um_customs_ame": "UM Aduanas USA",
"um_ace": "UM ACE",
"um_oma": "UM OMA",
"conversions": "Conversiones",
"equivalences": "Equivalencias",
"exchange_rates": "Tipos de cambio",

View File

@@ -1,6 +1,7 @@
import { api } from '$lib/api';
import type { ApiResponse } from '$lib/api';
// --- Interfaces para detalles (Hijos) ---
export interface DodaContainerSeal {
id: number;
@@ -28,7 +29,6 @@ export interface DodaContainerCreate {
seals_detail?: DodaContainerSealCreate[];
}
export interface DodaAmericanPedimento {
id: number;
doda_sys_id: number;
@@ -66,14 +66,13 @@ export interface DodaPedimentoCreate {
authorization_patent?: string;
document?: string;
shipment?: string;
effective_amount_usd?: number;
}
// --- Interfaces Principales (Padre) ---
export interface Doda {
id: number;
integration_number?: string;
doda_date?: number;
doda_time?: number;
@@ -87,18 +86,38 @@ export interface Doda {
operation_type?: string;
status?: string;
// Relaciones
containers?: DodaContainer[];
american_pedimentos?: DodaAmericanPedimento[];
pedimentos_detail?: DodaPedimento[];
// Metadata
tenant_id?: string;
created_at?: string;
updated_at?: string;
// Campos extra del formulario que vimos en el frontend
selected?: boolean;
user_selected?: string;
last_user?: string;
responsible?: string;
carrier?: string;
shipments?: string;
pedimento_type?: string;
original_chain?: string;
serial_number?: string;
electronic_signature?: string;
transaction_number?: string;
linq_sat_qr?: string;
sat_certificate?: string;
sat_digital_seal?: string;
xml_doda_sent_path?: string;
xml_doda_response_path?: string;
sat_original_chain?: string;
customs_clearance?: number;
unique_badge_number?: string;
}
export interface DodaCreate {
integration_number?: string;
doda_date?: number;
@@ -143,6 +162,7 @@ export interface DodaListResponse {
pages: number;
}
// --- Funciones de API ---
export async function getDodas(
page: number = 1,
@@ -177,7 +197,7 @@ export async function createDoda(data: DodaCreate, companyId: number): Promise<D
}
export async function updateDoda(id: number, data: DodaUpdate, companyId: number): Promise<Doda> {
const response = await api.patch(`/v1/a76/doda/${id}?company_id=${companyId}`, data);
const response = await api.put(`/v1/a76/doda/${id}?company_id=${companyId}`, data);
return response.data;
}

View File

@@ -9,7 +9,6 @@
const sidebar = useSidebar();
// Ya no es necesario inicializar aquí, se inicializa en +layout.svelte con datos SSR
</script>
<Sidebar.Menu>

View File

@@ -17,7 +17,7 @@
} from '$lib/api/dashboard/a76/general_catalogs/doda';
import { ArrowLeft, LoaderCircle, Save } from 'lucide-svelte';
// 1. Identificación reactiva de la URL
// 1. Identificación reactiva
let id = $derived($page.params.id);
let isEdit = $derived(!!id);
let title = $derived(isEdit ? "Editar DODA" : "Nuevo DODA");
@@ -25,7 +25,6 @@
let loading = $state(false);
let error = $state<string | null>(null);
// 2. Función para obtener un objeto limpio
function getEmptyForm(): DodaCreate {
return {
integration_number: '', doda_date: undefined, doda_time: undefined,
@@ -42,16 +41,12 @@
};
}
// 3. Estado reactivo del formulario
let formData = $state<DodaCreate>(getEmptyForm());
// 4. Efecto de carga: Detecta cambios en el ID y trae datos
$effect(() => {
if (id) {
// Si hay un ID, lo cargamos
loadDoda(Number(id));
} else {
// Si no hay ID, limpiamos para "Nuevo"
formData = getEmptyForm();
error = null;
}
@@ -61,15 +56,45 @@
loading = true;
try {
const companyId = companyStore.activeCompany?.id;
// IMPORTANTE: Esperamos la respuesta de tu API
const data = await getDoda(dodaId, companyId);
// Reasignamos el objeto completo para forzar a Svelte 5 a actualizar la UI
if (data) {
formData = { ...getEmptyForm(), ...data };
formData = {
integration_number: data.integration_number || '',
doda_date: data.doda_date,
doda_time: data.doda_time,
dispatch_customs: data.dispatch_customs || '',
customs_sections: data.customs_sections || '',
patent: data.patent || '',
pedimentos: data.pedimentos || '',
caat: data.caat || '',
transport_identification: data.transport_identification || '',
fast_id: data.fast_id || '',
operation_type: data.operation_type || '',
selected: data.selected || false,
user_selected: data.user_selected || '',
last_user: data.last_user || '',
responsible: data.responsible || '',
carrier: data.carrier || '',
shipments: data.shipments || '',
pedimento_type: data.pedimento_type || '',
original_chain: data.original_chain || '',
serial_number: data.serial_number || '',
electronic_signature: data.electronic_signature || '',
transaction_number: data.transaction_number || '',
status: data.status || '',
linq_sat_qr: data.linq_sat_qr || '',
sat_certificate: data.sat_certificate || '',
sat_digital_seal: data.sat_digital_seal || '',
xml_doda_sent_path: data.xml_doda_sent_path || '',
xml_doda_response_path: data.xml_doda_response_path || '',
sat_original_chain: data.sat_original_chain || '',
customs_clearance: data.customs_clearance,
unique_badge_number: data.unique_badge_number || ''
};
}
} catch (e) {
console.error(e);
error = "No se pudo cargar la información del DODA";
} finally {
loading = false;
@@ -84,11 +109,22 @@
if (!companyId) throw new Error('Selecciona una compañía');
if (!formData.integration_number?.trim()) throw new Error('El número de integración es requerido');
const payload: DodaCreate = {
...formData,
integration_number: formData.integration_number.trim(),
doda_date: formData.doda_date || undefined,
doda_time: formData.doda_time || undefined,
customs_clearance: formData.customs_clearance || undefined
};
if (isEdit) {
await updateDoda(Number(id), formData, companyId);
await updateDoda(Number(id), payload, companyId);
} else {
await createDoda(formData, companyId);
await createDoda(payload, companyId);
}
goto('/dashboard/general_catalogs/doda');
} catch (e: any) {
error = e.message || 'Error al guardar';
@@ -105,7 +141,6 @@
</Button>
<div>
<h1 class="text-2xl font-bold tracking-tight">{title}</h1>
<p class="text-muted-foreground">Captura la información técnica del DODA.</p>
</div>
</div>
@@ -115,129 +150,189 @@
</div>
{/if}
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }} class="space-y-6">
<Card.Root>
<Card.Content class="p-6">
<Tabs.Root value="general" class="w-full">
<div class="min-h-[500px]">
<Tabs.Content value="general" class="space-y-4 pt-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="integration_number">No. Integración <span class="text-destructive">*</span></Label>
<Input id="integration_number" bind:value={formData.integration_number} maxlength={30} />
{#key id}
<form onsubmit={(e) => { e.preventDefault(); handleSubmit(); }} class="space-y-6">
<Card.Root>
<Card.Content class="p-6">
<Tabs.Root value="general" class="w-full">
<div class="min-h-[500px]">
<Tabs.Content value="general" class="space-y-4 pt-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="integration_number">No. Integración <span class="text-destructive">*</span></Label>
<Input id="integration_number" bind:value={formData.integration_number} maxlength={30} />
</div>
<div class="grid gap-2">
<Label for="status">Estatus</Label>
<Input id="status" bind:value={formData.status} />
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="grid gap-2">
<Label for="doda_date">Fecha (YYYYMMDD)</Label>
<Input type="number" id="doda_date" bind:value={formData.doda_date} placeholder="Ej: 20240101" />
</div>
<div class="grid gap-2">
<Label for="doda_time">Hora (HHMMSS)</Label>
<Input type="number" id="doda_time" bind:value={formData.doda_time} placeholder="Ej: 143000" />
</div>
<div class="grid gap-2">
<Label for="operation_type">Tipo Operación</Label>
<Input id="operation_type" bind:value={formData.operation_type} maxlength={1} />
</div>
</div>
<div class="grid gap-2">
<Label for="status">Estatus</Label>
<Input id="status" bind:value={formData.status} />
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="grid gap-2">
<Label for="doda_date">Fecha (YYYYMMDD)</Label>
<Input type="number" id="doda_date" bind:value={formData.doda_date} />
<Label for="pedimentos">Pedimentos</Label>
<Input id="pedimentos" bind:value={formData.pedimentos} />
</div>
<div class="grid gap-2">
<Label for="doda_time">Hora (HHMMSS)</Label>
<Input type="number" id="doda_time" bind:value={formData.doda_time} />
<Label for="pedimento_type">Tipo Pedimento</Label>
<Input id="pedimento_type" bind:value={formData.pedimento_type} />
</div>
<div class="grid gap-2">
<Label for="operation_type">Tipo Operación</Label>
<Input id="operation_type" bind:value={formData.operation_type} maxlength={1} />
</div>
</div>
<div class="grid gap-2">
<Label for="pedimentos">Pedimentos</Label>
<Input id="pedimentos" bind:value={formData.pedimentos} />
</div>
</Tabs.Content>
</Tabs.Content>
<Tabs.Content value="transport" class="space-y-4 pt-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="grid gap-2">
<Label for="patent">Patente</Label>
<Input id="patent" bind:value={formData.patent} maxlength={4} />
<Tabs.Content value="transport" class="space-y-4 pt-4">
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="grid gap-2">
<Label for="patent">Patente</Label>
<Input id="patent" bind:value={formData.patent} maxlength={4} />
</div>
<div class="grid gap-2">
<Label for="dispatch_customs">Aduana Despacho</Label>
<Input id="dispatch_customs" bind:value={formData.dispatch_customs} maxlength={3} />
</div>
<div class="grid gap-2">
<Label for="customs_sections">Sección Aduanera</Label>
<Input id="customs_sections" bind:value={formData.customs_sections} maxlength={3} />
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="caat">CAAT</Label>
<Input id="caat" bind:value={formData.caat} />
</div>
<div class="grid gap-2">
<Label for="carrier">Carrier</Label>
<Input id="carrier" bind:value={formData.carrier} />
</div>
<div class="grid gap-2 md:col-span-2">
<Label for="transport_id">Ident. Transporte</Label>
<Input id="transport_id" bind:value={formData.transport_identification} />
</div>
<div class="grid gap-2">
<Label for="fast_id">FAST ID</Label>
<Input id="fast_id" bind:value={formData.fast_id} />
</div>
</div>
<div class="grid gap-2">
<Label for="dispatch_customs">Aduana Despacho</Label>
<Input id="dispatch_customs" bind:value={formData.dispatch_customs} maxlength={3} />
<Label for="shipments">Embarques (Shipments)</Label>
<Input id="shipments" bind:value={formData.shipments} />
</div>
<div class="grid gap-2">
<Label for="customs_sections">Sección Aduanera</Label>
<Input id="customs_sections" bind:value={formData.customs_sections} maxlength={3} />
<Label for="customs_clearance">Despacho Aduanero (ID)</Label>
<Input type="number" id="customs_clearance" bind:value={formData.customs_clearance} />
</div>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="caat">CAAT</Label>
<Input id="caat" bind:value={formData.caat} />
</div>
<div class="grid gap-2">
<Label for="carrier">Carrier</Label>
<Input id="carrier" bind:value={formData.carrier} />
</div>
<div class="grid gap-2 md:col-span-2">
<Label for="transport_id">Ident. Transporte</Label>
<Input id="transport_id" bind:value={formData.transport_identification} />
</div>
</div>
</Tabs.Content>
</Tabs.Content>
<Tabs.Content value="sat" class="space-y-4 pt-4">
<div class="grid gap-2">
<Label for="serial">Número de Serie</Label>
<Input id="serial" bind:value={formData.serial_number} />
</div>
<div class="grid gap-2">
<Label for="chain">Cadena Original</Label>
<Textarea id="chain" bind:value={formData.original_chain} />
</div>
<div class="grid gap-2">
<Label for="signature">Firma Electrónica</Label>
<Textarea id="signature" bind:value={formData.electronic_signature} />
</div>
</Tabs.Content>
<Tabs.Content value="sat" class="space-y-4 pt-4">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="serial">Número de Serie</Label>
<Input id="serial" bind:value={formData.serial_number} />
</div>
<div class="grid gap-2">
<Label for="transaction">No. Transacción</Label>
<Input id="transaction" bind:value={formData.transaction_number} />
</div>
</div>
<div class="grid gap-2">
<Label for="chain">Cadena Original</Label>
<Textarea id="chain" bind:value={formData.original_chain} class="min-h-[80px]" />
</div>
<div class="grid gap-2">
<Label for="signature">Firma Electrónica</Label>
<Textarea id="signature" bind:value={formData.electronic_signature} class="min-h-[80px]" />
</div>
<div class="grid gap-2">
<Label for="seal">Sello Digital SAT</Label>
<Textarea id="seal" bind:value={formData.sat_digital_seal} class="min-h-[80px]" />
</div>
<div class="grid gap-2">
<Label for="sat_original_chain">Cadena Original SAT</Label>
<Textarea id="sat_original_chain" bind:value={formData.sat_original_chain} class="min-h-[80px]" />
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="xml_sent">Ruta XML Enviado</Label>
<Input id="xml_sent" bind:value={formData.xml_doda_sent_path} />
</div>
<div class="grid gap-2">
<Label for="xml_res">Ruta XML Respuesta</Label>
<Input id="xml_res" bind:value={formData.xml_doda_response_path} />
</div>
</div>
</Tabs.Content>
<Tabs.Content value="other" class="space-y-4 pt-4">
<div class="flex items-center gap-3 p-4 border rounded-lg">
<Switch id="selected" bind:checked={formData.selected} />
<Label for="selected">Seleccionado para operación</Label>
</div>
<div class="grid gap-2">
<Label for="responsible">RFC Responsable</Label>
<Input id="responsible" bind:value={formData.responsible} />
</div>
<div class="grid gap-2">
<Label for="badge">Número de Gafete</Label>
<Input id="badge" bind:value={formData.unique_badge_number} />
</div>
</Tabs.Content>
</div>
<Tabs.Content value="other" class="space-y-4 pt-4">
<div class="flex items-center gap-3 p-4 border rounded-lg">
<Switch id="selected" bind:checked={formData.selected} />
<Label for="selected">DODA Seleccionado para operación</Label>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="grid gap-2">
<Label for="user_sel">Usuario Selección</Label>
<Input id="user_sel" bind:value={formData.user_selected} />
</div>
<div class="grid gap-2">
<Label for="last_user">Último Usuario</Label>
<Input id="last_user" bind:value={formData.last_user} />
</div>
</div>
<div class="grid gap-2">
<Label for="responsible">RFC Responsable</Label>
<Input id="responsible" bind:value={formData.responsible} maxlength={14} />
</div>
<div class="grid gap-2">
<Label for="badge">Número Único de Gafete</Label>
<Input id="badge" bind:value={formData.unique_badge_number} />
</div>
<div class="grid gap-2">
<Label for="qr">LINQ SAT QR</Label>
<Input id="qr" bind:value={formData.linq_sat_qr} />
</div>
<div class="grid gap-2">
<Label for="sat_cert">Certificado SAT</Label>
<Input id="sat_cert" bind:value={formData.sat_certificate} />
</div>
</Tabs.Content>
</div>
<Tabs.List class="grid grid-cols-4 fixed bottom-24 left-1/2 -translate-x-1/2 w-[95%] max-w-2xl z-40 shadow-xl bg-background border p-1 rounded-xl">
<Tabs.Trigger value="general">General</Tabs.Trigger>
<Tabs.Trigger value="transport">Aduana</Tabs.Trigger>
<Tabs.Trigger value="sat">SAT</Tabs.Trigger>
<Tabs.Trigger value="other">Otros</Tabs.Trigger>
</Tabs.List>
</Tabs.Root>
</Card.Content>
</Card.Root>
<Tabs.List class="grid grid-cols-4 fixed bottom-24 left-1/2 -translate-x-1/2 w-[95%] max-w-2xl z-40 shadow-2xl bg-background border p-1 rounded-xl">
<Tabs.Trigger value="general">General</Tabs.Trigger>
<Tabs.Trigger value="transport">Aduana</Tabs.Trigger>
<Tabs.Trigger value="sat">SAT</Tabs.Trigger>
<Tabs.Trigger value="other">Otros</Tabs.Trigger>
</Tabs.List>
</Tabs.Root>
</Card.Content>
</Card.Root>
<div class="fixed bottom-0 right-0 left-0 md:left-64 p-4 border-t bg-background/95 backdrop-blur z-50 flex justify-end gap-4 shadow-inner">
<div class="max-w-6xl mx-auto flex justify-end gap-4 px-4 w-full">
<Button type="button" variant="ghost" href="/dashboard/general_catalogs/doda" disabled={loading}>
Cancelar
</Button>
<Button type="submit" disabled={loading} class="min-w-[140px]">
{#if loading}
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
Guardando...
{:else}
<Save class="mr-2 h-4 w-4" />
{isEdit ? 'Actualizar' : 'Guardar'}
{/if}
</Button>
<div class="fixed bottom-0 right-0 left-0 md:left-64 p-4 border-t bg-background/95 backdrop-blur z-50 flex justify-end gap-4 shadow-inner">
<div class="max-w-6xl mx-auto flex justify-end gap-4 px-4 w-full">
<Button type="button" variant="ghost" href="/dashboard/general_catalogs/doda" disabled={loading}>
Cancelar
</Button>
<Button type="submit" disabled={loading} class="min-w-[140px]">
{#if loading}
<LoaderCircle class="mr-2 h-4 w-4 animate-spin" />
Guardando...
{:else}
<Save class="mr-2 h-4 w-4" />
{isEdit ? 'Actualizar' : 'Guardar'}
{/if}
</Button>
</div>
</div>
</div>
</form>
</form>
{/key}
</div>