Merge pull request 'fix/client-providers' (#135) from fix/client-providers into development
Reviewed-on: ADUANASOFT/anexo76#135
This commit is contained in:
@@ -1,6 +1,91 @@
|
||||
import { api } from '$lib/api';
|
||||
import type { ApiResponse } from '$lib/api';
|
||||
|
||||
export interface CompanyAddress {
|
||||
id?: number;
|
||||
address_type: string;
|
||||
street?: string | null;
|
||||
exterior_number?: string | null;
|
||||
interior_number?: string | null;
|
||||
postal_code?: string | null;
|
||||
neighborhood?: string | null;
|
||||
city?: string | null;
|
||||
municipality?: string | null;
|
||||
state?: string | null;
|
||||
country?: string | null;
|
||||
phone?: string | null;
|
||||
fax?: string | null;
|
||||
email?: string | null;
|
||||
}
|
||||
|
||||
export interface CompanyCertification {
|
||||
id?: number;
|
||||
is_certified_company?: string | null;
|
||||
certified_company_registration?: string | null;
|
||||
certified_company_start_date?: number | null;
|
||||
certified_company_end_date?: number | null;
|
||||
annex31_certification_date?: number | null;
|
||||
annex31_certification_number?: string | null;
|
||||
annex31_modality?: string | null;
|
||||
annex31_company_type?: string | null;
|
||||
annex31_renewal_date?: number | null;
|
||||
annex31_final_certification_date?: number | null;
|
||||
is_oea_company?: number | null;
|
||||
neec_company?: number | null;
|
||||
}
|
||||
|
||||
export interface CompanyDigitalCertificate {
|
||||
id?: number;
|
||||
certificate_type: string;
|
||||
cer_file_path?: string | null;
|
||||
key_file_path?: string | null;
|
||||
password?: string | null;
|
||||
access_key?: string | null;
|
||||
cer_expiration_date?: number | null;
|
||||
key_expiration_date?: number | null;
|
||||
}
|
||||
|
||||
export interface CompanyVU {
|
||||
id?: number;
|
||||
webservice_user?: string | null;
|
||||
webservice_password?: string | null;
|
||||
email?: string | null;
|
||||
figure_type?: string | null;
|
||||
central_path?: string | null;
|
||||
xml_files_path?: string | null;
|
||||
query_rfc?: string | null;
|
||||
validation_rfc?: string | null;
|
||||
configuration_source?: string | null;
|
||||
measurement_units?: string | null;
|
||||
}
|
||||
|
||||
export interface CompanyElectronicAgent {
|
||||
id?: number;
|
||||
input_folder?: string | null;
|
||||
output_folder?: string | null;
|
||||
send_mask?: string | null;
|
||||
response_mask?: string | null;
|
||||
response_extension?: string | null;
|
||||
counter_start?: number | null;
|
||||
counter_end?: number | null;
|
||||
counter_next?: number | null;
|
||||
}
|
||||
|
||||
export interface CompanyPrevalidator {
|
||||
id?: number;
|
||||
customs?: string | null;
|
||||
key?: string | null;
|
||||
patent?: string | null;
|
||||
description?: string | null;
|
||||
}
|
||||
|
||||
export interface CompanyCFDI {
|
||||
id?: number;
|
||||
xml_save_path?: string | null;
|
||||
cfdi_app_path?: string | null;
|
||||
pac_app_path?: string | null;
|
||||
}
|
||||
|
||||
export interface Company {
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
@@ -32,6 +117,116 @@ export interface Company {
|
||||
inter_db_name?: string | null;
|
||||
prevalidator_key?: string | null;
|
||||
seventh_amendment?: boolean;
|
||||
// Sectores
|
||||
sector1?: string | null;
|
||||
sector2?: string | null;
|
||||
sector3?: string | null;
|
||||
// Flags técnicos
|
||||
active_labels?: number | null;
|
||||
active_fractions?: number | null;
|
||||
activate_caat?: number | null;
|
||||
trans_interface?: number | null;
|
||||
american_costs?: number | null;
|
||||
scaf_readonly?: number | null;
|
||||
parts_replacement?: number | null;
|
||||
activate_facmexame?: number | null;
|
||||
part_reference?: number | null;
|
||||
international_firm?: number | null;
|
||||
// Configuraciones
|
||||
ftp_key?: string | null;
|
||||
sifra_path?: string | null;
|
||||
version_type?: string | null;
|
||||
sql_language?: string | null;
|
||||
balance_operation_mode?: string | null;
|
||||
// Campos de certificación (CompanyCertification aplanado)
|
||||
is_certified_company?: string | null;
|
||||
certified_company_registration?: string | null;
|
||||
certified_company_start_date?: number | null;
|
||||
certified_company_end_date?: number | null;
|
||||
annex31_certification_date?: number | null;
|
||||
annex31_certification_number?: string | null;
|
||||
annex31_modality?: string | null;
|
||||
annex31_company_type?: string | null;
|
||||
annex31_renewal_date?: number | null;
|
||||
annex31_final_certification_date?: number | null;
|
||||
is_oea_company?: number | null;
|
||||
neec_company?: number | null;
|
||||
// Campos de dirección principal (CompanyAddress - main)
|
||||
main_street?: string | null;
|
||||
main_exterior_number?: string | null;
|
||||
main_interior_number?: string | null;
|
||||
main_postal_code?: string | null;
|
||||
main_neighborhood?: string | null;
|
||||
main_city?: string | null;
|
||||
main_municipality?: string | null;
|
||||
main_state?: string | null;
|
||||
main_country?: string | null;
|
||||
main_phone?: string | null;
|
||||
main_fax?: string | null;
|
||||
main_email?: string | null;
|
||||
// Campos de dirección industrial 1 (CompanyAddress - industrial1)
|
||||
ind1_street?: string | null;
|
||||
ind1_exterior_number?: string | null;
|
||||
ind1_interior_number?: string | null;
|
||||
ind1_postal_code?: string | null;
|
||||
ind1_neighborhood?: string | null;
|
||||
ind1_city?: string | null;
|
||||
ind1_municipality?: string | null;
|
||||
ind1_state?: string | null;
|
||||
ind1_country?: string | null;
|
||||
ind1_phone?: string | null;
|
||||
ind1_fax?: string | null;
|
||||
ind1_email?: string | null;
|
||||
// Campos de dirección industrial 2 (CompanyAddress - industrial2)
|
||||
ind2_street?: string | null;
|
||||
ind2_exterior_number?: string | null;
|
||||
ind2_interior_number?: string | null;
|
||||
ind2_postal_code?: string | null;
|
||||
ind2_neighborhood?: string | null;
|
||||
ind2_city?: string | null;
|
||||
ind2_municipality?: string | null;
|
||||
ind2_state?: string | null;
|
||||
ind2_country?: string | null;
|
||||
ind2_phone?: string | null;
|
||||
ind2_fax?: string | null;
|
||||
ind2_email?: string | null;
|
||||
// Campos de prevalidador (CompanyPrevalidator aplanado)
|
||||
prev_customs?: string | null;
|
||||
prev_key?: string | null;
|
||||
prev_patent?: string | null;
|
||||
prev_description?: string | null;
|
||||
// Campos de ventanilla única (CompanyVU aplanado)
|
||||
vu_webservice_user?: string | null;
|
||||
vu_webservice_password?: string | null;
|
||||
vu_email?: string | null;
|
||||
vu_figure_type?: string | null;
|
||||
vu_central_path?: string | null;
|
||||
vu_xml_files_path?: string | null;
|
||||
vu_query_rfc?: string | null;
|
||||
vu_validation_rfc?: string | null;
|
||||
vu_configuration_source?: string | null;
|
||||
vu_measurement_units?: string | null;
|
||||
// Campos de agente aduanal electrónico (CompanyElectronicAgent aplanado)
|
||||
ea_input_folder?: string | null;
|
||||
ea_output_folder?: string | null;
|
||||
ea_send_mask?: string | null;
|
||||
ea_response_mask?: string | null;
|
||||
ea_response_extension?: string | null;
|
||||
ea_counter_start?: number | null;
|
||||
ea_counter_end?: number | null;
|
||||
ea_counter_next?: number | null;
|
||||
// Campos de CFDI (CompanyCFDI aplanado)
|
||||
cfdi_xml_save_path?: string | null;
|
||||
cfdi_app_path?: string | null;
|
||||
cfdi_pac_app_path?: string | null;
|
||||
// Submodelos
|
||||
addresses?: CompanyAddress[];
|
||||
certification?: CompanyCertification | null;
|
||||
digital_certificates?: CompanyDigitalCertificate[];
|
||||
ventanilla_unica?: CompanyVU | null;
|
||||
electronic_agent?: CompanyElectronicAgent | null;
|
||||
prevalidator?: CompanyPrevalidator | null;
|
||||
cfdi?: CompanyCFDI | null;
|
||||
created_at: string | null;
|
||||
updated_at: string | null;
|
||||
}
|
||||
|
||||
@@ -6,16 +6,15 @@ export const obtenerAtajosFormularioEmpresa = (acciones: {
|
||||
manejarCerrar: () => void;
|
||||
}): ShortcutDef[] => [
|
||||
{ key: 'Alt+Digit1', description: 'Pestaña General', action: () => acciones.cambiarPestana('general') },
|
||||
{ key: 'Alt+Digit2', description: 'Pestaña Programas', action: () => acciones.cambiarPestana('programas') },
|
||||
{ key: 'Alt+Digit2', description: 'Pestaña Programa', action: () => acciones.cambiarPestana('programa') },
|
||||
{ key: 'Alt+Digit3', description: 'Pestaña Responsable', action: () => acciones.cambiarPestana('responsable') },
|
||||
{ key: 'Alt+Digit4', description: 'Pestaña Config', action: () => acciones.cambiarPestana('config') },
|
||||
{ key: 'Alt+Digit4', description: 'Pestaña Certificaciones', action: () => acciones.cambiarPestana('certificaciones') },
|
||||
{ key: 'Alt+Digit5', description: 'Pestaña Direcciones', action: () => acciones.cambiarPestana('direcciones') },
|
||||
{ key: 'Alt+Digit6', description: 'Pestaña Config', action: () => acciones.cambiarPestana('config') },
|
||||
{
|
||||
key: 'Control+KeyS',
|
||||
description: 'Guardar Empresa',
|
||||
action: (e) => {
|
||||
e.preventDefault();
|
||||
acciones.manejarGuardar();
|
||||
}
|
||||
action: () => acciones.manejarGuardar()
|
||||
},
|
||||
{
|
||||
key: 'Escape',
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
name: '',
|
||||
short_name: '',
|
||||
curp: '',
|
||||
client_or_provider: 'client',
|
||||
client_or_provider: 'both',
|
||||
type_nat_foreign: 'N',
|
||||
responsible: '',
|
||||
position: '',
|
||||
@@ -224,7 +224,7 @@
|
||||
} catch (e: any) {
|
||||
console.error(e);
|
||||
error = e.message || 'Error al guardar';
|
||||
toast.error(error);
|
||||
toast.error(error || 'Error al guardar');
|
||||
} finally {
|
||||
loading = false;
|
||||
}
|
||||
@@ -314,7 +314,11 @@
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="type">Tipo de Relación <span class="text-destructive">*</span></Label>
|
||||
<Select.Root type="single" bind:value={formData.client_or_provider}>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.client_or_provider}
|
||||
onValueChange={(v) => (formData.client_or_provider = v)}
|
||||
>
|
||||
<Select.Trigger id="type">
|
||||
{typeLabels[formData.client_or_provider] || 'Selecciona un tipo'}
|
||||
</Select.Trigger>
|
||||
@@ -357,7 +361,11 @@
|
||||
</div>
|
||||
<div class="grid gap-2">
|
||||
<Label for="type_nat">Tipo Origen</Label>
|
||||
<Select.Root type="single" bind:value={formData.type_nat_foreign}>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.type_nat_foreign}
|
||||
onValueChange={(v) => (formData.type_nat_foreign = v)}
|
||||
>
|
||||
<Select.Trigger id="type_nat">
|
||||
{formData.type_nat_foreign === 'N'
|
||||
? 'Nacional'
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,184 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { partsApi, type Part } from '$lib/api/dashboard/a76/parts';
|
||||
import DataTable from '$lib/components/dashboard/goods/parts/data-table.svelte';
|
||||
import { createColumns } from '$lib/components/dashboard/goods/parts/columns.js';
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { companyStore } from '$lib/stores/company.svelte';
|
||||
import { Plus, Search, RefreshCw, Loader2, Trash2 } from 'lucide-svelte';
|
||||
|
||||
// 1. ESTADOS
|
||||
let partsList = $state<Part[]>([]);
|
||||
let listLoading = $state(false);
|
||||
let listError = $state<string | null>(null);
|
||||
|
||||
// Estado Búsqueda
|
||||
let searchCode = $state('');
|
||||
let searchResults = $state<Part[]>([]); // Array para guardar los resultados
|
||||
let searchLoading = $state(false);
|
||||
let isSearching = $state(false); // Para saber si estamos viendo resultados de búsqueda
|
||||
|
||||
// 2. CARGA DE DATOS (Lista Completa)
|
||||
async function loadParts() {
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
if (!companyId) return;
|
||||
|
||||
listLoading = true;
|
||||
listError = null;
|
||||
isSearching = false; // Reseteamos modo búsqueda
|
||||
searchResults = [];
|
||||
|
||||
try {
|
||||
const response = await partsApi.list({
|
||||
company_id: companyId,
|
||||
page: 1,
|
||||
page_size: 100
|
||||
});
|
||||
|
||||
if (response.data) {
|
||||
partsList = response.data.items || [];
|
||||
} else if (response.error) {
|
||||
listError = response.error;
|
||||
}
|
||||
} catch (e) {
|
||||
listError = 'Error de conexión con el servidor';
|
||||
} finally {
|
||||
listLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 3. REACTIVIDAD DE COMPAÑÍA
|
||||
$effect(() => {
|
||||
if (companyStore.activeCompany) {
|
||||
loadParts();
|
||||
}
|
||||
});
|
||||
|
||||
// 4. BÚSQUEDA MANUAL
|
||||
async function handleSearch() {
|
||||
if (!searchCode.trim()) return;
|
||||
|
||||
searchLoading = true;
|
||||
listError = null; // Limpiamos errores previos
|
||||
const companyId = companyStore.activeCompany?.id;
|
||||
|
||||
if (!companyId) return;
|
||||
|
||||
try {
|
||||
const response = await partsApi.list({
|
||||
company_id: companyId,
|
||||
q: searchCode.trim()
|
||||
});
|
||||
|
||||
const results = response.data?.items || [];
|
||||
|
||||
if (results.length > 0) {
|
||||
searchResults = results; // Guardamos TODOS los resultados
|
||||
isSearching = true; // Activamos modo búsqueda
|
||||
} else {
|
||||
listError = "No se encontraron partes con ese criterio";
|
||||
searchResults = [];
|
||||
isSearching = true; // Aún en modo búsqueda, pero vacía
|
||||
}
|
||||
} catch (e) {
|
||||
listError = "Error en la búsqueda";
|
||||
} finally {
|
||||
searchLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
function clearSearch() {
|
||||
searchCode = '';
|
||||
searchResults = [];
|
||||
isSearching = false;
|
||||
listError = null;
|
||||
loadParts(); // Recargamos la lista completa
|
||||
}
|
||||
|
||||
const columns = createColumns(loadParts);
|
||||
|
||||
// Si estamos buscando, mostramos searchResults, si no, la lista completa
|
||||
const tableData = $derived(isSearching ? searchResults : partsList);
|
||||
</script>
|
||||
|
||||
<div class="space-y-6 p-4">
|
||||
<div class="flex items-center justify-between">
|
||||
<div>
|
||||
<h1 class="text-3xl font-bold tracking-tight">Catálogo de Partes</h1>
|
||||
<p class="text-muted-foreground">Gestiona las partes y componentes del sistema.</p>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<Button variant="outline" onclick={loadParts} disabled={listLoading}>
|
||||
<RefreshCw class="mr-2 h-4 w-4 {listLoading ? 'animate-spin' : ''}" />
|
||||
Actualizar
|
||||
</Button>
|
||||
<Button href="/dashboard/goods/parts/edit">
|
||||
<Plus class="mr-2 h-4 w-4" />
|
||||
Nueva Parte
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Búsqueda Rápida</Card.Title>
|
||||
<Card.Description>Ingresa el número de parte o descripción para filtrar.</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="flex gap-4">
|
||||
<div class="flex-1 max-w-xl">
|
||||
<Input
|
||||
bind:value={searchCode}
|
||||
placeholder="Buscar por número de parte o descripción..."
|
||||
onkeydown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
/>
|
||||
</div>
|
||||
<Button onclick={handleSearch} disabled={searchLoading}>
|
||||
{#if searchLoading}
|
||||
<Loader2 class="mr-2 h-4 w-4 animate-spin" />
|
||||
Buscando...
|
||||
{:else}
|
||||
<Search class="mr-2 h-4 w-4" />
|
||||
Buscar
|
||||
{/if}
|
||||
</Button>
|
||||
|
||||
{#if isSearching || searchCode}
|
||||
<Button variant="ghost" onclick={clearSearch}>
|
||||
<Trash2 class="mr-2 h-4 w-4" />
|
||||
Limpiar
|
||||
</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>
|
||||
{#if isSearching}
|
||||
Resultados de la búsqueda ({searchResults.length})
|
||||
{:else}
|
||||
Listado General ({partsList.length} registros)
|
||||
{/if}
|
||||
</Card.Title>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
{#if listError}
|
||||
<div class="bg-destructive/10 text-destructive p-4 rounded-lg border border-destructive/20 mb-4">
|
||||
{listError}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<DataTable
|
||||
data={tableData}
|
||||
{columns}
|
||||
loading={listLoading || searchLoading}
|
||||
hasMore={false}
|
||||
loadMore={() => {}}
|
||||
/>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
</div>
|
||||
Reference in New Issue
Block a user