Merge branch 'development' of https://git.aduanasoft.com/ADUANASOFT/anexo76 into fix/modulos-ingles
This commit is contained in:
@@ -598,8 +598,9 @@ export const invoicesApi = {
|
||||
);
|
||||
},
|
||||
|
||||
copyInvoice: (invoiceId: number, companyId: number) => {
|
||||
copyInvoice: (invoiceId: number, companyId: number, newInvoiceNumber?: string) => {
|
||||
const params = new URLSearchParams({ company_id: companyId.toString() });
|
||||
if (newInvoiceNumber) params.set('new_invoice_number', newInvoiceNumber);
|
||||
return api.post<Invoice>(`/v1/a76/invoices/${invoiceId}/copy?${params.toString()}`, {});
|
||||
},
|
||||
copyInvoiceHeader: (invoiceId: number, companyId: number) => {
|
||||
|
||||
@@ -0,0 +1,148 @@
|
||||
import type { InvoiceType } from '$lib/api/dashboard/reference_data/invoice_types';
|
||||
|
||||
export type InvoiceListFilters = {
|
||||
operation_type?: string | null;
|
||||
invoice_type?: string | null;
|
||||
};
|
||||
|
||||
/** Claves de importación expuestas en el menú lateral. */
|
||||
export const IMPORT_INVOICE_TYPE_KEYS = ['TEM', 'DEF', 'MEX', 'CR', 'REP'] as const;
|
||||
|
||||
/** Claves de exportación relevantes para configuración (Exportación y Reparación). */
|
||||
export const EXPORT_INVOICE_TYPE_KEYS = ['EXDEF', 'REPAR'] as const;
|
||||
|
||||
/**
|
||||
* Filtra tipos de factura por operación (imp/exp), incluyendo tipos marcados como `both`.
|
||||
*/
|
||||
export function filterInvoiceTypesByOperation(
|
||||
invoiceTypes: InvoiceType[],
|
||||
operationType: string
|
||||
): InvoiceType[] {
|
||||
if (!operationType) return [];
|
||||
|
||||
return invoiceTypes.filter(
|
||||
(type) => !type.operation || type.operation === 'both' || type.operation === operationType
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tipos visibles en parámetros.
|
||||
*
|
||||
* - Para importación: se limitan a las claves expuestas en el menú lateral (TEM, DEF, MEX, CR, REP).
|
||||
* - Para exportación: se permiten todos los tipos cuyo `operation` sea `exp` (catálogo completo).
|
||||
*/
|
||||
export function getInvoiceTypesForSettings(
|
||||
invoiceTypes: InvoiceType[],
|
||||
operationType: string
|
||||
): InvoiceType[] {
|
||||
const filtered = filterInvoiceTypesByOperation(invoiceTypes, operationType);
|
||||
if (!operationType) return filtered;
|
||||
|
||||
// Importación: solo los tipos configurables desde el menú lateral
|
||||
if (operationType === 'imp') {
|
||||
const byKey = new Map(filtered.map((type) => [type.key, type]));
|
||||
return IMPORT_INVOICE_TYPE_KEYS.map((key) => byKey.get(key)).filter(
|
||||
(type): type is InvoiceType => !!type
|
||||
);
|
||||
}
|
||||
|
||||
if (operationType === 'exp') {
|
||||
const byKey = new Map(filtered.map((type) => [type.key, type]));
|
||||
const fromBackend = EXPORT_INVOICE_TYPE_KEYS.map((key) => byKey.get(key)).filter(
|
||||
(type): type is InvoiceType => !!type
|
||||
);
|
||||
|
||||
// Si el backend devolvió ambos tipos, usar sus descripciones reales.
|
||||
if (fromBackend.length > 0) {
|
||||
return fromBackend;
|
||||
}
|
||||
|
||||
// Fallback mínimo para no dejar el combo vacío
|
||||
const fallback: InvoiceType[] = [
|
||||
{ key: 'EXDEF', description: 'EXPORTACIÓN DEFINITIVA', operation: 'exp' },
|
||||
{ key: 'REPAR', description: 'REPARACIÓN', operation: 'exp' }
|
||||
];
|
||||
return fallback;
|
||||
}
|
||||
|
||||
// Otra operación válida: devolvemos lo filtrado
|
||||
return filtered;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tipo de factura por defecto cuando la URL no lo incluye.
|
||||
*/
|
||||
export function getDefaultInvoiceTypeForOperation(
|
||||
operationType: string,
|
||||
invoiceTypes: InvoiceType[]
|
||||
): string {
|
||||
const options = getInvoiceTypesForSettings(invoiceTypes, operationType);
|
||||
if (options.length === 0) return '';
|
||||
|
||||
if (operationType === 'imp') {
|
||||
return options.find((type) => type.key === 'TEM')?.key ?? options[0].key;
|
||||
}
|
||||
|
||||
if (operationType === 'exp') {
|
||||
return (
|
||||
options.find((type) => type.key === 'EXDEF')?.key ??
|
||||
options.find((type) => type.key === 'REPAR')?.key ??
|
||||
options[0].key
|
||||
);
|
||||
}
|
||||
|
||||
return options[0].key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construye la ruta del listado de facturas preservando query params y evitando
|
||||
* navegar sin `operation_type` (el SSR del listado redirige al dashboard).
|
||||
*/
|
||||
export function buildInvoicesListPath(
|
||||
searchParams: URLSearchParams,
|
||||
fallbacks?: InvoiceListFilters
|
||||
): string {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
|
||||
if (!params.has('operation_type') && fallbacks?.operation_type) {
|
||||
params.set('operation_type', fallbacks.operation_type);
|
||||
}
|
||||
|
||||
if (!params.has('invoice_type') && fallbacks?.invoice_type) {
|
||||
params.set('invoice_type', fallbacks.invoice_type);
|
||||
}
|
||||
|
||||
const operationType = params.get('operation_type');
|
||||
if (!operationType) {
|
||||
return '/dashboard';
|
||||
}
|
||||
|
||||
const qs = params.toString();
|
||||
return qs ? `/dashboard/invoices?${qs}` : `/dashboard/invoices?operation_type=${operationType}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Construye la ruta de parámetros de factura conservando el contexto del listado.
|
||||
*/
|
||||
export function buildInvoicesSettingsPath(searchParams: URLSearchParams): string {
|
||||
const params = new URLSearchParams(searchParams);
|
||||
const qs = params.toString();
|
||||
return qs ? `/dashboard/invoices/settings?${qs}` : '/dashboard/invoices/settings';
|
||||
}
|
||||
|
||||
/**
|
||||
* Resuelve operación y tipo inicial para la pantalla de parámetros.
|
||||
*/
|
||||
export function resolveInvoiceSettingsContext(
|
||||
searchParams: URLSearchParams,
|
||||
invoiceTypes: InvoiceType[]
|
||||
): { operationType: string; invoiceType: string } {
|
||||
const operationType = searchParams.get('operation_type') ?? '';
|
||||
let invoiceType = searchParams.get('invoice_type') ?? '';
|
||||
|
||||
if (operationType && !invoiceType) {
|
||||
invoiceType = getDefaultInvoiceTypeForOperation(operationType, invoiceTypes);
|
||||
}
|
||||
|
||||
return { operationType, invoiceType };
|
||||
}
|
||||
@@ -38,14 +38,14 @@
|
||||
{#snippet child({ props })}
|
||||
<button
|
||||
{...props}
|
||||
class="inline-flex size-8 shrink-0 items-center justify-center rounded-md
|
||||
class="inline-flex size-10 shrink-0 select-none items-center justify-center rounded-md
|
||||
text-sidebar-foreground/70 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground
|
||||
transition-colors outline-none focus-visible:ring-2 focus-visible:ring-ring
|
||||
data-[state=open]:bg-sidebar-accent data-[state=open]:text-sidebar-accent-foreground"
|
||||
title="Aplicaciones"
|
||||
aria-label="Abrir selector de aplicaciones"
|
||||
>
|
||||
<LayoutGridIcon class="size-4" />
|
||||
<LayoutGridIcon class="size-5" />
|
||||
</button>
|
||||
{/snippet}
|
||||
</DropdownMenu.Trigger>
|
||||
|
||||
@@ -84,6 +84,21 @@
|
||||
inventory: BarChart3Icon,
|
||||
};
|
||||
|
||||
const SYSTEM_COLORS: Record<SystemType, { border: string; bg: string; iconBg: string; iconText: string }> = {
|
||||
fixed_asset: {
|
||||
border: 'border-amber-500/40',
|
||||
bg: 'bg-amber-500/5 dark:bg-amber-500/10',
|
||||
iconBg: 'bg-amber-500/10',
|
||||
iconText: 'text-amber-600 dark:text-amber-400',
|
||||
},
|
||||
inventory: {
|
||||
border: 'border-emerald-500/40',
|
||||
bg: 'bg-emerald-500/5 dark:bg-emerald-500/10',
|
||||
iconBg: 'bg-emerald-500/10',
|
||||
iconText: 'text-emerald-600 dark:text-emerald-400',
|
||||
},
|
||||
};
|
||||
|
||||
onMount(() => {
|
||||
const expandForKeyboardNav = () => {
|
||||
sidebar.setOpen(true);
|
||||
@@ -104,15 +119,15 @@
|
||||
{@const activeLabel = systemStore.activeLabel}
|
||||
{@const activeSystem = systemStore.activeSystem}
|
||||
{@const Icon = activeSystem ? ACTIVE_ICONS[activeSystem] : null}
|
||||
{@const colors = activeSystem ? SYSTEM_COLORS[activeSystem] : null}
|
||||
|
||||
<div
|
||||
class="flex items-center gap-2 rounded-lg border border-primary/40 bg-primary/5 px-3 py-2 text-xs shadow-sm
|
||||
dark:bg-primary/10"
|
||||
class="flex items-center gap-2 rounded-lg border {colors?.border} {colors?.bg} px-3 py-2 text-xs shadow-sm"
|
||||
aria-label={`Aplicación activa: ${activeLabel.name}`}
|
||||
title={`Aplicación activa: ${activeLabel.name}`}
|
||||
>
|
||||
{#if Icon}
|
||||
<div class="flex size-8 items-center justify-center rounded-md bg-primary/10 text-primary">
|
||||
<div class="flex size-8 items-center justify-center rounded-md {colors?.iconBg} {colors?.iconText}">
|
||||
<Icon class="size-4" />
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
import FixedAssetClassForm from '$lib/components/dashboard/goods/classes/forms/FixedAssetClassForm.svelte';
|
||||
import { Folder, Save, Plus, RefreshCw } from 'lucide-svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { friendlyApiErrorParts, type ApiResponse } from '$lib/api';
|
||||
import { classesApi, type A76Class } from '$lib/api/dashboard/a76/classes';
|
||||
import { faClassesApi, type FAClass } from '$lib/api/dashboard/a24/fa_classes';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
@@ -322,29 +323,33 @@
|
||||
|
||||
const idsToDelete = [...selectedClassIds];
|
||||
let okCount = 0;
|
||||
const errors: number[] = [];
|
||||
const failedResponses: ApiResponse[] = [];
|
||||
|
||||
for (const id of idsToDelete) {
|
||||
try {
|
||||
// El backend elimina automáticamente la extensión FA si existe
|
||||
await classesApi.delete(id, companyId);
|
||||
// El backend elimina automáticamente la extensión FA si existe
|
||||
const res = await classesApi.delete(id, companyId);
|
||||
if (res.status >= 400 || res.error) {
|
||||
console.error(`Error deleting class ${id}:`, res.error ?? res.status);
|
||||
failedResponses.push(res);
|
||||
} else {
|
||||
okCount++;
|
||||
} catch (error) {
|
||||
console.error(`Error deleting class ${id}:`, error);
|
||||
errors.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
if (errors.length === 0) {
|
||||
if (failedResponses.length === 0) {
|
||||
toast.success(
|
||||
okCount === 1
|
||||
? 'Clase eliminada correctamente'
|
||||
: `${okCount} clases eliminadas correctamente`
|
||||
);
|
||||
} else if (okCount === 0) {
|
||||
toast.error(`Error al eliminar ${errors.length} clase(s)`);
|
||||
const { title, description } = friendlyApiErrorParts(failedResponses[0]);
|
||||
toast.error(title, { description });
|
||||
} else {
|
||||
toast.error(`${okCount} eliminada(s), ${errors.length} con error`);
|
||||
const { title, description } = friendlyApiErrorParts(failedResponses[0]);
|
||||
toast.error(`${okCount} eliminada(s), ${failedResponses.length} con error`, {
|
||||
description: description || title
|
||||
});
|
||||
}
|
||||
|
||||
await loadClasses();
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
import { reportsWinsaaiApi } from '$lib/api/dashboard/a76/reports/reports-winsaai';
|
||||
import DataTable from '$lib/components/dashboard/invoices/data-table.svelte';
|
||||
import { createColumns } from '$lib/components/dashboard/invoices/columns';
|
||||
import { buildInvoicesSettingsPath } from '$lib/components/dashboard/invoices/invoice-list-navigation';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import * as AlertDialog from '$lib/components/ui/alert-dialog';
|
||||
import * as Dialog from '$lib/components/ui/dialog';
|
||||
@@ -319,6 +320,30 @@
|
||||
if (fromContextMenu) contextMenuOpen = false;
|
||||
}
|
||||
|
||||
// --- Dialog: nombre para la copia de factura ---
|
||||
let copyDialogOpen = $state(false);
|
||||
let copyDialogInvoice = $state<Invoice | null>(null);
|
||||
let copyDialogNumber = $state('');
|
||||
|
||||
function openCopyDialog(inv: Invoice) {
|
||||
copyDialogInvoice = inv;
|
||||
copyDialogNumber = `${inv.invoice_number ?? ''}-COPIA`;
|
||||
copyDialogOpen = true;
|
||||
}
|
||||
|
||||
function handleCopyConfirm() {
|
||||
if (!copyDialogInvoice || !copyDialogNumber.trim()) return;
|
||||
const inv = copyDialogInvoice;
|
||||
const num = copyDialogNumber.trim();
|
||||
copyDialogOpen = false;
|
||||
invoicesApi.copyInvoice(inv.id, companyStore.activeCompany!.id, num)
|
||||
.then((res) => {
|
||||
if (res.error) { toast.error(res.error); return; }
|
||||
toast.success('Factura copiada');
|
||||
reloadData();
|
||||
});
|
||||
}
|
||||
|
||||
function openExportItemsDialog(inv: Invoice) {
|
||||
exportItemsInvoice = inv;
|
||||
exportItemsFormat = 'csv';
|
||||
@@ -1629,7 +1654,13 @@
|
||||
<p class="text-muted-foreground">{m.invoice_list_header_description()}</p>
|
||||
</div>
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<Button variant="outline" class="h-9" onclick={() => goto('/dashboard/invoices/settings')}>
|
||||
<Button
|
||||
variant="outline"
|
||||
class="h-9"
|
||||
onclick={() => {
|
||||
void goto(buildInvoicesSettingsPath(new URLSearchParams(window.location.search)));
|
||||
}}
|
||||
>
|
||||
<Settings class="mr-2" size={16} />
|
||||
{m.invoice_list_actions_parameters()}
|
||||
</Button>
|
||||
@@ -1727,6 +1758,33 @@
|
||||
|
||||
<div class="h-20"></div>
|
||||
|
||||
<!-- Dialog: nombre para la copia de factura -->
|
||||
<Dialog.Root bind:open={copyDialogOpen}>
|
||||
<Dialog.Content class="sm:!max-w-md">
|
||||
<Dialog.Header>
|
||||
<Dialog.Title>Copiar Factura</Dialog.Title>
|
||||
<Dialog.Description>
|
||||
Indica el número que tendrá la factura copiada.
|
||||
</Dialog.Description>
|
||||
</Dialog.Header>
|
||||
<div class="space-y-2 py-2">
|
||||
<Label for="copy-invoice-number">Número de Factura Nuevo</Label>
|
||||
<Input
|
||||
id="copy-invoice-number"
|
||||
bind:value={copyDialogNumber}
|
||||
placeholder="Ej. 123123-COPIA"
|
||||
onkeydown={(e) => { if (e.key === 'Enter') handleCopyConfirm(); }}
|
||||
/>
|
||||
</div>
|
||||
<Dialog.Footer>
|
||||
<Button variant="outline" onclick={() => (copyDialogOpen = false)}>Cancelar</Button>
|
||||
<Button onclick={handleCopyConfirm} disabled={!copyDialogNumber.trim()}>
|
||||
Proceso Copiar
|
||||
</Button>
|
||||
</Dialog.Footer>
|
||||
</Dialog.Content>
|
||||
</Dialog.Root>
|
||||
|
||||
<Dialog.Root bind:open={isCoveDialogOpen}>
|
||||
<Dialog.Content class="sm:!max-w-2xl">
|
||||
<Dialog.Header>
|
||||
@@ -1940,12 +1998,8 @@
|
||||
</button>
|
||||
{/snippet}
|
||||
{@render cmItem(Copy, 'Copiar Factura', () => {
|
||||
invoicesApi.copyInvoice(contextMenuInvoice!.id, companyStore.activeCompany!.id)
|
||||
.then((res) => {
|
||||
if (res.error) { toast.error(res.error); return; }
|
||||
toast.success('Factura copiada');
|
||||
reloadData();
|
||||
});
|
||||
contextMenuOpen = false;
|
||||
openCopyDialog(contextMenuInvoice!);
|
||||
})}
|
||||
{@render cmItem(Download, 'Exportar Partidas', () => {
|
||||
openExportItemsDialog(contextMenuInvoice!);
|
||||
@@ -2330,14 +2384,8 @@
|
||||
<DropdownMenu.Item
|
||||
onclick={() => {
|
||||
if (!selectedInvoice) return;
|
||||
const invId = selectedInvoice.id;
|
||||
copiasInterfacesMenuOpen = false;
|
||||
invoicesApi.copyInvoice(invId, companyStore.activeCompany!.id)
|
||||
.then((res) => {
|
||||
if (res.error) { toast.error(res.error); return; }
|
||||
toast.success('Factura copiada');
|
||||
reloadData();
|
||||
});
|
||||
openCopyDialog(selectedInvoice);
|
||||
}}
|
||||
>
|
||||
<Copy class="mr-2 h-4 w-4" />
|
||||
|
||||
@@ -15,6 +15,14 @@ export const load: PageServerLoad = async ({ cookies, fetch, url }) => {
|
||||
throw error(400, 'No se encontró una compañía seleccionada');
|
||||
}
|
||||
|
||||
const operationTypeParam = url.searchParams.get('operation_type');
|
||||
const invoiceTypeParam = url.searchParams.get('invoice_type');
|
||||
|
||||
let parsedOperationType: string | null = null;
|
||||
if (operationTypeParam && (operationTypeParam === 'exp' || operationTypeParam === 'imp')) {
|
||||
parsedOperationType = operationTypeParam;
|
||||
}
|
||||
|
||||
// Load necessary reference data for the settings form
|
||||
// We need: InvoiceTypes, CustomsBrokers, Incoterms, etc. to populate the reusable forms
|
||||
|
||||
@@ -22,7 +30,7 @@ export const load: PageServerLoad = async ({ cookies, fetch, url }) => {
|
||||
// Reuse the same calls as in edit/[id] to ensure we have data for the dropdowns
|
||||
|
||||
const invoiceTypesPromise = authenticatedFetch(
|
||||
'v1/public/reference_data/invoice-types/?page=1&page_size=100',
|
||||
'v1/public/reference_data/invoice-types?page=1&page_size=100',
|
||||
{},
|
||||
cookies,
|
||||
fetch
|
||||
@@ -237,7 +245,11 @@ export const load: PageServerLoad = async ({ cookies, fetch, url }) => {
|
||||
seals: seals.items || [],
|
||||
enclosure: enclosures.items || [],
|
||||
pedimentos: pedimentos.items || [],
|
||||
companyId
|
||||
companyId,
|
||||
filters: {
|
||||
operation_type: parsedOperationType,
|
||||
invoice_type: invoiceTypeParam || null
|
||||
}
|
||||
};
|
||||
|
||||
} catch (err) {
|
||||
|
||||
@@ -28,13 +28,29 @@
|
||||
import SsisdefSettingsForm from '$lib/components/dashboard/invoices/settings/ssisdef-settings-form.svelte';
|
||||
import SsicmSettingsForm from '$lib/components/dashboard/invoices/settings/ssicm-settings-form.svelte';
|
||||
import SscrSettingsForm from '$lib/components/dashboard/invoices/settings/sscr-settings-form.svelte';
|
||||
import {
|
||||
buildInvoicesListPath,
|
||||
getDefaultInvoiceTypeForOperation,
|
||||
getInvoiceTypesForSettings,
|
||||
resolveInvoiceSettingsContext
|
||||
} from '$lib/components/dashboard/invoices/invoice-list-navigation';
|
||||
import { m } from '$lib/i18n/messages';
|
||||
|
||||
// Props
|
||||
let { data } = $props();
|
||||
|
||||
const initialParams = new URLSearchParams();
|
||||
if (data.filters?.operation_type) {
|
||||
initialParams.set('operation_type', data.filters.operation_type);
|
||||
}
|
||||
if (data.filters?.invoice_type) {
|
||||
initialParams.set('invoice_type', data.filters.invoice_type);
|
||||
}
|
||||
const initialContext = resolveInvoiceSettingsContext(initialParams, data.invoiceTypes || []);
|
||||
|
||||
// State
|
||||
let selectedInvoiceType = $state<string>('');
|
||||
let selectedOperationType = $state<string>('');
|
||||
let selectedInvoiceType = $state<string>(initialContext.invoiceType);
|
||||
let selectedOperationType = $state<string>(initialContext.operationType);
|
||||
let isLoading = $state(false);
|
||||
let isSaving = $state(false);
|
||||
let activeTab = $state('general');
|
||||
@@ -85,6 +101,13 @@
|
||||
{ value: 'exp', label: 'Exportación' }
|
||||
];
|
||||
|
||||
let invoiceTypesForOperation = $derived(
|
||||
getInvoiceTypesForSettings(
|
||||
data.invoiceTypes || [],
|
||||
selectedOperationType || initialContext.operationType
|
||||
)
|
||||
);
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
const companyStoreModule = await import('$lib/stores/company.svelte');
|
||||
@@ -415,8 +438,15 @@
|
||||
return obj;
|
||||
}
|
||||
|
||||
function invoicesListPath(): string {
|
||||
return buildInvoicesListPath($page.url.searchParams, {
|
||||
operation_type: selectedOperationType || null,
|
||||
invoice_type: selectedInvoiceType || null
|
||||
});
|
||||
}
|
||||
|
||||
function handleBack() {
|
||||
goto('/dashboard/invoices');
|
||||
goto(invoicesListPath());
|
||||
}
|
||||
|
||||
let operationTypeText = $derived.by(() => {
|
||||
@@ -503,7 +533,6 @@
|
||||
const cid = companyStore?.activeCompany?.id;
|
||||
if (browser && cid && selectedOperationType && selectedInvoiceType && !lastLoadedKey && !isLoading) {
|
||||
untrack(() => {
|
||||
console.log('EFFECT: Initial load triggered');
|
||||
loadSettings();
|
||||
});
|
||||
}
|
||||
@@ -512,6 +541,12 @@
|
||||
// Handle explicit changes
|
||||
function handleOperationTypeChange(v: string) {
|
||||
selectedOperationType = v;
|
||||
const options = getInvoiceTypesForSettings(data.invoiceTypes || [], v);
|
||||
const stillValid = options.some((type) => type.key === selectedInvoiceType);
|
||||
if (!stillValid) {
|
||||
selectedInvoiceType = getDefaultInvoiceTypeForOperation(v, data.invoiceTypes || []);
|
||||
lastLoadedKey = '';
|
||||
}
|
||||
loadSettings();
|
||||
}
|
||||
|
||||
@@ -540,7 +575,9 @@
|
||||
{/if}
|
||||
{#if selectedInvoiceType}
|
||||
<Badge variant="secondary">
|
||||
{data.invoiceTypes.find((t: any) => t.key === selectedInvoiceType)?.description || selectedInvoiceType}
|
||||
{invoiceTypesForOperation.find((t: any) => t.key === selectedInvoiceType)?.description ||
|
||||
data.invoiceTypes.find((t: any) => t.key === selectedInvoiceType)?.description ||
|
||||
selectedInvoiceType}
|
||||
</Badge>
|
||||
{/if}
|
||||
</div>
|
||||
@@ -553,45 +590,32 @@
|
||||
<Separator />
|
||||
|
||||
<div class="pb-48">
|
||||
<Card.Root class="mb-8 border-dashed bg-muted/30">
|
||||
<Card.Header class="py-4">
|
||||
<Card.Title class="text-lg">Contexto de Configuración</Card.Title>
|
||||
<Card.Description
|
||||
>Selecciona el contexto para editar sus valores por defecto.</Card.Description
|
||||
>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex gap-6 py-2">
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs uppercase text-muted-foreground font-bold tracking-wider">Tipo de Operación</Label>
|
||||
<Select type="single" value={selectedOperationType} onValueChange={handleOperationTypeChange}>
|
||||
<SelectTrigger class="w-[200px] h-10 bg-background">
|
||||
{operationTypes.find((t: any) => t.value === selectedOperationType)?.label ||
|
||||
'Seleccionar...'}
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{#each operationTypes as type}
|
||||
<SelectItem value={type.value}>{type.label}</SelectItem>
|
||||
{/each}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs uppercase text-muted-foreground font-bold tracking-wider">Tipo de Factura</Label>
|
||||
<Select type="single" value={selectedInvoiceType} onValueChange={handleInvoiceTypeChange}>
|
||||
<SelectTrigger class="w-[300px] h-10 bg-background">
|
||||
{@const type = data.invoiceTypes.find((t: any) => t.key === selectedInvoiceType)}
|
||||
{type ? `${type.key} - ${type.description}` : 'Seleccionar...'}
|
||||
</SelectTrigger>
|
||||
<SelectContent class="max-h-[300px]">
|
||||
{#each data.invoiceTypes as type}
|
||||
<SelectItem value={type.key}>{type.key} - {type.description}</SelectItem>
|
||||
{/each}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
{#if !selectedOperationType || selectedOperationType !== 'imp'}
|
||||
<Card.Root class="mb-8 border-dashed bg-muted/30">
|
||||
<Card.Header class="py-4">
|
||||
<Card.Title class="text-lg">Contexto de Configuración</Card.Title>
|
||||
<Card.Description
|
||||
>Selecciona el contexto para editar sus valores por defecto.</Card.Description
|
||||
>
|
||||
</Card.Header>
|
||||
<Card.Content class="flex gap-6 py-2">
|
||||
<div class="space-y-2">
|
||||
<Label class="text-xs uppercase text-muted-foreground font-bold tracking-wider">Tipo de Factura</Label>
|
||||
<Select type="single" value={selectedInvoiceType} onValueChange={handleInvoiceTypeChange}>
|
||||
<SelectTrigger class="w-[300px] h-10 bg-background">
|
||||
{@const type = invoiceTypesForOperation.find((t: any) => t.key === selectedInvoiceType)}
|
||||
{type ? `${type.key} - ${type.description}` : 'Seleccionar...'}
|
||||
</SelectTrigger>
|
||||
<SelectContent class="max-h-[300px]">
|
||||
{#each invoiceTypesForOperation as type}
|
||||
<SelectItem value={type.key}>{type.key} - {type.description}</SelectItem>
|
||||
{/each}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
{/if}
|
||||
|
||||
{#if selectedInvoiceType && selectedOperationType}
|
||||
{#if isLoading}
|
||||
@@ -615,7 +639,7 @@
|
||||
<InvoiceTopFields
|
||||
{invoice}
|
||||
bind:formData={InvoiceTopFieldsFormData}
|
||||
invoiceTypes={data.invoiceTypes || []}
|
||||
invoiceTypes={invoiceTypesForOperation}
|
||||
pedimentos={data.pedimentos || []}
|
||||
defaultOperationType={selectedOperationType}
|
||||
defaultInvoiceType={selectedInvoiceType}
|
||||
@@ -628,7 +652,7 @@
|
||||
<GeneralTabForm
|
||||
{invoice}
|
||||
bind:formData={generalFormData}
|
||||
invoiceTypes={data.invoiceTypes || []}
|
||||
invoiceTypes={invoiceTypesForOperation}
|
||||
customsBrokers={data.customsBrokers || []}
|
||||
clients={data.clients || []}
|
||||
providers={data.providers || []}
|
||||
@@ -817,6 +841,9 @@
|
||||
{/if}
|
||||
|
||||
<div class="flex justify-end gap-3">
|
||||
<Button variant="outline" onclick={handleBack} disabled={isSaving}>
|
||||
{m.invoice_edit_page_cancel()}
|
||||
</Button>
|
||||
<Button variant="outline" onclick={resetForms} disabled={isSaving || !selectedInvoiceType}>
|
||||
Restablecer
|
||||
</Button>
|
||||
|
||||
Reference in New Issue
Block a user