From 45edc56820d5c7e0f797c59c927ee606e87f43dd Mon Sep 17 00:00:00 2001 From: hreyes Date: Mon, 9 Feb 2026 14:01:52 -0600 Subject: [PATCH 1/4] feature/limit-characters-customs-brockers --- .../api/v1/modules/a76/customs_brokers/dto.py | 14 +- .../customs_brokers/create-dialog.svelte | 489 +++++++++++------- .../customs_brokers/edit-dialog.svelte | 138 +++-- .../customs_brokers/edit/[[id]]/+page.svelte | 74 ++- 4 files changed, 451 insertions(+), 264 deletions(-) diff --git a/backend/api/v1/modules/a76/customs_brokers/dto.py b/backend/api/v1/modules/a76/customs_brokers/dto.py index fa11b268..7f65b264 100644 --- a/backend/api/v1/modules/a76/customs_brokers/dto.py +++ b/backend/api/v1/modules/a76/customs_brokers/dto.py @@ -1,6 +1,6 @@ from typing import Optional -from pydantic import BaseModel +from pydantic import BaseModel, Field class CustomsBrokerBaseDTO(BaseModel): @@ -19,7 +19,7 @@ class CustomsBrokerBaseDTO(BaseModel): tax_id: Optional[str] = None personal_id: Optional[str] = None position: Optional[str] = None - license: Optional[str] = None + license: Optional[str] = Field(None, max_length=4, pattern=r"^\d*$") company: Optional[str] = None contact: Optional[str] = None @@ -27,7 +27,7 @@ class CustomsBrokerBaseDTO(BaseModel): class CustomsBrokerCreateDTO(CustomsBrokerBaseDTO): """Schema for creating a new CustomsBroker""" - broker_key: str + broker_key: str = Field(..., max_length=5, pattern=r"^[a-zA-Z0-9]+$", description="Clave única del agente aduanal (máx 5 caracteres)") class CustomsBrokerUpdateDTO(CustomsBrokerBaseDTO): @@ -51,7 +51,7 @@ class CustomsBrokerResponseDTO(CustomsBrokerBaseDTO): # Legacy DTO for backwards compatibility (if needed elsewhere) class CustomsBrokerDTO(BaseModel): type: Optional[str] = None - broker_key: str + broker_key: str = Field(..., max_length=5, pattern=r"^[a-zA-Z0-9]+$") name: Optional[str] = None address: Optional[str] = None postal_code: Optional[str] = None @@ -64,7 +64,7 @@ class CustomsBrokerDTO(BaseModel): tax_id: Optional[str] = None personal_id: Optional[str] = None position: Optional[str] = None - license: Optional[str] = None + license: Optional[str] = Field(None, max_length=4, pattern=r"^\d*$") company: Optional[str] = None contact: Optional[str] = None tenant_id: str @@ -100,13 +100,13 @@ class CustomsBrokerVUCreateDTO(BaseModel): class CustomsBrokerPersonnelDTO(BaseModel): - broker_key: str + broker_key: str = Field(..., max_length=5, pattern=r"^[a-zA-Z0-9]+$") line: int name: Optional[str] tax_id: Optional[str] personal_id: Optional[str] position: Optional[str] - license: Optional[str] + license: Optional[str] = Field(None, max_length=4, pattern=r"^\d*$") first_name: Optional[str] last_name: Optional[str] middle_name: Optional[str] diff --git a/frontend/src/lib/components/dashboard/customs_brokers/create-dialog.svelte b/frontend/src/lib/components/dashboard/customs_brokers/create-dialog.svelte index 0b4393ff..44bca734 100644 --- a/frontend/src/lib/components/dashboard/customs_brokers/create-dialog.svelte +++ b/frontend/src/lib/components/dashboard/customs_brokers/create-dialog.svelte @@ -1,221 +1,310 @@ - - - - {mode === "create" ? "Nuevo Agente Aduanal" : "Editar Agente Aduanal"} - - - Ingresa los datos generales del agente. La configuración de VU y Personal se gestiona aparte. - - + + + + {mode === 'create' ? 'Nuevo Agente Aduanal' : 'Editar Agente Aduanal'} + + + Ingresa los datos generales del agente. La configuración de VU y Personal se gestiona + aparte. + + -
- -
-

Identificación

-
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - -
-
-
+
+
+

Identificación

+
+
+ + { + const val = e.currentTarget.value.toUpperCase(); + if (val.length > 5) { + brokerKeyError = true; + formData.broker_key = val.slice(0, 5); + e.currentTarget.value = formData.broker_key; - + clearTimeout(brokerKeyTimeout); + brokerKeyTimeout = setTimeout(() => { + brokerKeyError = false; + }, 3000); + } else { + brokerKeyError = false; + formData.broker_key = val; + } + }} + placeholder="Ej. 550" + maxlength="6" + class={brokerKeyError ? 'border-red-500 focus-visible:ring-red-500' : ''} + disabled={mode === 'edit' || loading} + /> + {#if brokerKeyError} +

+ La clave no debe superar los 5 caracteres +

+ {/if} +
+
+ + { + const val = e.currentTarget.value.replace(/\D/g, ''); + if (val.length > 4) { + licenseError = true; + formData.license = val.slice(0, 4); + e.currentTarget.value = formData.license; -
-

Contacto

-
-
- - -
-
- - -
-
-
+ clearTimeout(licenseTimeout); + licenseTimeout = setTimeout(() => { + licenseError = false; + }, 3000); + } else { + licenseError = false; + formData.license = val; + } + }} + placeholder="Ej. 3421" + maxlength="5" + class={licenseError ? 'border-red-500 focus-visible:ring-red-500' : ''} + disabled={loading} + /> + {#if licenseError} +

+ La patente no debe superar los 4 dígitos +

+ {/if} +
+
+
+
+ + +
+
+ + +
+
+ + +
+
+
- + -
-

Dirección Fiscal

- -
- - -
+
+

Contacto

+
+
+ + +
+
+ + +
+
+
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
-
+ -
+
+

Dirección Fiscal

- - - - - - \ No newline at end of file +
+ + +
+ +
+
+ + +
+
+ + +
+
+ + +
+
+
+
+ + +
+
+
+
+ + + + + +
+
diff --git a/frontend/src/lib/components/dashboard/customs_brokers/edit-dialog.svelte b/frontend/src/lib/components/dashboard/customs_brokers/edit-dialog.svelte index e5bfeebb..147df888 100644 --- a/frontend/src/lib/components/dashboard/customs_brokers/edit-dialog.svelte +++ b/frontend/src/lib/components/dashboard/customs_brokers/edit-dialog.svelte @@ -1,10 +1,14 @@
@@ -264,45 +267,13 @@
- - - - - - - - - - - {#if isLoading} - - {:else if paginatedItems.length === 0} - - {:else} - {#each paginatedItems as item (item.broker_key)} - selectItem(item)} - > - - - - - - {/each} - {/if} - -
PatenteNombreLicenciaCiudad
Cargando...
No se encontraron registros
{item.broker_key}{item.name || '-'}{item.license || '-'}{item.city || '-'}
+
{#if totalItems > pageSize} @@ -434,7 +405,7 @@ - + + From adc5ab5caedd9d8a4c856527d1643933f9a382ed Mon Sep 17 00:00:00 2001 From: hreyes Date: Mon, 9 Feb 2026 16:04:28 -0600 Subject: [PATCH 3/4] fix/visual-bugs-laptop --- .../dashboard/a76/general_catalogs/ports.ts | 4 +- .../classes/forms/FixedAssetClassForm.svelte | 1245 ++++++++--------- .../edit/items/fa/country-dialog.svelte | 26 +- .../edit/items/fa/part-number-dialog.svelte | 48 +- .../items/fa/tariff-fraction-dialog.svelte | 36 +- .../items/fa/unit-of-measure-dialog.svelte | 16 +- .../lib/components/dashboard/ports/columns.ts | 1 + .../dashboard/ports/data-table-actions.svelte | 62 +- .../dashboard/ports/delete-dialog.svelte | 117 ++ .../goods/fixed-asset-classes/+page.svelte | 2 +- 10 files changed, 797 insertions(+), 760 deletions(-) create mode 100644 frontend/src/lib/components/dashboard/ports/delete-dialog.svelte diff --git a/frontend/src/lib/api/dashboard/a76/general_catalogs/ports.ts b/frontend/src/lib/api/dashboard/a76/general_catalogs/ports.ts index d397e245..fe2caaba 100644 --- a/frontend/src/lib/api/dashboard/a76/general_catalogs/ports.ts +++ b/frontend/src/lib/api/dashboard/a76/general_catalogs/ports.ts @@ -57,7 +57,7 @@ class PortsApi { const queryParams = new URLSearchParams({ company_id: companyId.toString() }); - return api.get(`${this.baseUrl}/${id}/?${queryParams.toString()}`); + return api.get(`${this.baseUrl}/${id}?${queryParams.toString()}`); } async create(data: PortCreate, companyId: string | number): Promise> { @@ -78,7 +78,7 @@ class PortsApi { const queryParams = new URLSearchParams({ company_id: companyId.toString() }); - return api.delete(`${this.baseUrl}/${id}/?${queryParams.toString()}`); + return api.delete(`${this.baseUrl}/${id}?${queryParams.toString()}`); } } diff --git a/frontend/src/lib/components/dashboard/goods/classes/forms/FixedAssetClassForm.svelte b/frontend/src/lib/components/dashboard/goods/classes/forms/FixedAssetClassForm.svelte index 5ebda988..3e48edfc 100644 --- a/frontend/src/lib/components/dashboard/goods/classes/forms/FixedAssetClassForm.svelte +++ b/frontend/src/lib/components/dashboard/goods/classes/forms/FixedAssetClassForm.svelte @@ -5,12 +5,27 @@ import * as Dialog from '$lib/components/ui/dialog'; import { Folder } from 'lucide-svelte'; import { toast } from 'svelte-sonner'; - import { materialTypesApi, type MaterialType } from '$lib/api/dashboard/reference_data/material_types'; + import { + materialTypesApi, + type MaterialType + } from '$lib/api/dashboard/reference_data/material_types'; import { unitsOfMeasureApi } from '$lib/api/dashboard/a76/units_of_measure'; - import { getTariffFractions, type TariffFraction } from '$lib/api/dashboard/a76/general_catalogs/tariff-fractions'; - import { getUSTariffFractions, type USTariffFraction } from '$lib/api/dashboard/a76/general_catalogs/us-tariff-fractions'; - import { getDepreciationCatalog, type DepreciationCatalog } from '$lib/api/dashboard/a76/general_catalogs/depreciation-catalog'; - import { getFDACatalog, type FDACatalog } from '$lib/api/dashboard/a76/general_catalogs/fda-catalog'; + import { + getTariffFractions, + type TariffFraction + } from '$lib/api/dashboard/a76/general_catalogs/tariff-fractions'; + import { + getUSTariffFractions, + type USTariffFraction + } from '$lib/api/dashboard/a76/general_catalogs/us-tariff-fractions'; + import { + getDepreciationCatalog, + type DepreciationCatalog + } from '$lib/api/dashboard/a76/general_catalogs/depreciation-catalog'; + import { + getFDACatalog, + type FDACatalog + } from '$lib/api/dashboard/a76/general_catalogs/fda-catalog'; import { companyStore } from '$lib/stores/company.svelte'; interface Props { @@ -72,7 +87,7 @@ if (initialData) { // Tomar snapshot para evitar referencias reactivas const snap = $state.snapshot(initialData); - + // Usar ?? para manejar null/undefined correctamente (evita que inputs pasen de controlado a no controlado) formData.class_code = snap.class_code ?? ''; formData.description_es = snap.description_es ?? ''; @@ -82,7 +97,8 @@ formData.fraction = snap.fraction ?? ''; formData.us_fraction = snap.us_fraction ?? ''; // Mapear depreciation_rate a annual_depreciation_rate si existe (números pueden ser null) - formData.annual_depreciation_rate = snap.depreciation_rate ?? snap.annual_depreciation_rate ?? null; + formData.annual_depreciation_rate = + snap.depreciation_rate ?? snap.annual_depreciation_rate ?? null; // Mapear fda_code a fda_key si existe formData.fda_key = snap.fda_code ?? snap.fda_key ?? ''; formData.eccn_code = snap.eccn_code ?? ''; @@ -92,7 +108,7 @@ formData.import_tariff_code = snap.import_tariff_code ?? ''; formData.import_tariff_type = snap.import_tariff_type ?? ''; formData.export_tariff_code = snap.export_tariff_code ?? ''; - formData.export_tariff_type = snap.export_tariff_type ?? ''; + formData.export_tariff_type = snap.export_tariff_type ?? ''; } else { // Reset form when initialData is null (new class) formData.class_code = ''; @@ -111,7 +127,7 @@ formData.import_tariff_code = ''; formData.import_tariff_type = ''; formData.export_tariff_code = ''; - formData.export_tariff_type = ''; + formData.export_tariff_type = ''; } }); @@ -193,7 +209,7 @@ if (!companyId) return; const response = await unitsOfMeasureApi.list(companyId, 1, 100); if (response.data) { - unitsOfMeasureData = response.data.items.map(item => ({ + unitsOfMeasureData = response.data.items.map((item) => ({ code: item.code, description: item.description || '', descriptionEnglish: item.description_en || '', @@ -264,16 +280,16 @@ try { const filters = search ? { search } : {}; const pageSize = 100; - + const response = await getTariffFractions(page, pageSize, companyId, filters); - + if (response.data) { if (page === 1) { tariffFractions = [...response.data.items]; } else { tariffFractions = [...tariffFractions, ...response.data.items]; } - + totalFractions = response.data.total; currentPage = page; hasMoreFractions = tariffFractions.length < response.data.total; @@ -314,16 +330,16 @@ try { const filters = search ? { search } : {}; const pageSize = 100; - + const response = await getUSTariffFractions(page, pageSize, companyId, filters); - + if (response.data) { if (page === 1) { usTariffFractions = [...response.data.items]; } else { usTariffFractions = [...usTariffFractions, ...response.data.items]; } - + totalUSFractions = response.data.total; currentUSPage = page; hasMoreUSFractions = usTariffFractions.length < response.data.total; @@ -364,16 +380,16 @@ try { const filters = search ? { search } : {}; const pageSize = 100; - + const response = await getDepreciationCatalog(page, pageSize, companyId, filters); - + if (response.data) { if (page === 1) { depreciationCatalog = [...response.data.items]; } else { depreciationCatalog = [...depreciationCatalog, ...response.data.items]; } - + totalDepreciation = response.data.total; currentDepreciationPage = page; hasMoreDepreciation = depreciationCatalog.length < response.data.total; @@ -409,16 +425,16 @@ try { const filters = search ? { search } : {}; const pageSize = 100; - + const response = await getFDACatalog(page, pageSize, companyId, filters); - + if (response.data) { if (page === 1) { fdaCatalog = [...response.data.items]; } else { fdaCatalog = [...fdaCatalog, ...response.data.items]; } - + totalFDA = response.data.total; currentFDAPage = page; hasMoreFDA = fdaCatalog.length < response.data.total; @@ -444,27 +460,27 @@ // Función de validación function validateForm(): boolean { const errors: Record = {}; - + if (!formData.class_code?.trim()) { errors.class_code = 'El código de clase es obligatorio'; } - + if (!formData.description_es?.trim()) { errors.description_es = 'La descripción en español es obligatoria'; } - + if (!formData.material_key?.trim()) { errors.material_key = 'El tipo de activo fijo es obligatorio'; } - + if (!formData.unit_of_measure?.trim()) { errors.unit_of_measure = 'La unidad de medida comercial es obligatoria'; } - + if (!formData.fraction?.trim()) { errors.fraction = 'La fracción arancelaria es obligatoria'; } - + validationErrors = errors; return Object.keys(errors).length === 0; } @@ -472,9 +488,9 @@ // Validar campo individual (para validación en blur) function validateField(fieldName: string) { if (!showErrors) return; // Solo validar si ya se intentó guardar - + const errors = { ...validationErrors }; - + switch (fieldName) { case 'class_code': if (!formData.class_code?.trim()) { @@ -512,29 +528,29 @@ } break; } - + validationErrors = errors; } function handleSave() { // Activar visualización de errores showErrors = true; - + // Validar formulario - if (!validateForm()) { + if (!validateForm()) { toast.error('Por favor, complete todos los campos obligatorios'); return; } - + // Tomamos una copia muerta de los datos actuales - const dataToSave = $state.snapshot(formData); - + const dataToSave = $state.snapshot(formData); + // Ejecutamos el onSave pasándole la copia if (onSave) { - onSave(dataToSave); + onSave(dataToSave); } } - + function handleCancel() { if (onCancel) { onCancel(); @@ -558,10 +574,11 @@ id="class_code" bind:value={formData.class_code} placeholder="Ingrese código de clase" - class="uppercase {validationErrors.class_code ? 'border-red-500 focus-visible:ring-red-500' : ''}" - maxlength={8} + class="uppercase {validationErrors.class_code + ? 'border-red-500 focus-visible:ring-red-500' + : ''}" + maxlength={20} oninput={() => { - // Limpiar error local si existe if (validationErrors.class_code) { const errors = { ...validationErrors }; delete errors.class_code; @@ -574,615 +591,569 @@

{validationErrors.class_code}

{/if} -
- -
- - -
- - validateField('description_es')} - /> - {#if validationErrors.description_es} -

{validationErrors.description_es}

- {/if} -
- - -
- - -
- - -
- -
- validateField('material_key')} - /> - - - {formData.material_description || ''} - -
- {#if validationErrors.material_key} -

{validationErrors.material_key}

- {/if} -
- - -
- -
- validateField('unit_of_measure')} - /> - - - {formData.unit_of_measure_description || ''} - - - Clave U.M.A: {formData.unit_measure_key || ''} - -
- {#if validationErrors.unit_of_measure} -

{validationErrors.unit_of_measure}

- {/if} -
- - -
- -
- validateField('fraction')} - /> - - - U.M.T: {formData.fraction_umt || ''} - - - Clave U.M.A: {formData.fraction_uma_key || ''} - -
- {#if validationErrors.fraction} -

{validationErrors.fraction}

- {/if} -
- - -
- -
- - - - Ad/valorem: {formData.us_fraction_ad_valorem || '0.00'} - - - Tasa Fija: {formData.us_fraction_fixed_rate || '0.00000000'} - -
-
- - -
- -
- - % - -
- +
validateField('material_key')} /> + + + {formData.material_description || ''} +
+ {#if validationErrors.material_key} +

{validationErrors.material_key}

+ {/if}
- -
- -
+ +
+
+ validateField('description_es')} /> - + {#if validationErrors.description_es} +

{validationErrors.description_es}

+ {/if}
-
- -
- -
-
- (formData.iva_exempt_fraction = true)} - class="h-4 w-4" - /> - -
-
- (formData.iva_exempt_fraction = false)} - class="h-4 w-4" - /> - -
-
-
- - -
- -
+
+ -
+ + +
+
+ +
+ validateField('unit_of_measure')} + /> + + + {formData.unit_of_measure_description || ''} + +
+ {#if validationErrors.unit_of_measure} +

{validationErrors.unit_of_measure}

+ {/if} +
+ +
+ +
+ validateField('fraction')} + /> + +
+ {#if validationErrors.fraction} +

{validationErrors.fraction}

+ {/if} +
+
+ + +
+
+ +
+ + +
+
+ +
+ +
+ + % + +
+
+
+ + +
+
+ +
+ + +
+
+ +
+ +
+ + +
+
+
+ +
+ + +
+
+
+ + + + + + + CATALOGO DE ACTIVO FIJO + +
+
+ + +
+
+ + + + + + + + + {#each filteredMaterialTypes as material (material.key)} + selectMaterial(material)} + > + + + + {/each} + +
ClaveDescripción
{material.key}{material.description}
+
+
+ + + +
+
+ + + + + + UNIDADES DE MEDIDA + +
+
+ + +
+
+ + + + + + + + + + + {#each filteredUnits as unit (unit.code)} + selectUnit(unit)} + > + + + + + + {/each} + +
CódigoDescripciónDescription (English)Clave Mexicana
{unit.code}{unit.description}{unit.descriptionEnglish}{unit.claveMexicana}
+
+
+ + + +
+
+ + + + + + CATALOGO DE FRACCIONES SITAR - SCAII + +
+
+ + +
+
+ + + + + + + + + + + {#each tariffFractions as fraction (fraction.code)} + selectFraction(fraction)} + > + + + + + + {:else} + + + + {/each} + +
FracciónNICODescripciónU.M.T
{fraction.fraction}{fraction.nico}{fraction.description}{fraction.umt}
+ {#if isLoadingFractions} + Cargando fracciones... + {:else} + No hay fracciones disponibles + {/if} +
+
+
+ + + +
+
+ + + + + + CATALOGO DE FRACCIONES AMERICANAS + +
+
+ + +
+
+ + + + + + + + + + + + {#each usTariffFractions as fraction (fraction.id)} + selectUSFraction(fraction)} + > + + + + + + + {:else} + + + + {/each} + +
CódigoPrefijoAd valoremCosto FijoDescripción
{fraction.code}{fraction.prefix || ''}{fraction.ad_valorem || '0.00'}{fraction.fixed_cost || '0.00'}{fraction.description || ''}
+ {#if isLoadingUSFractions} + Cargando fracciones... + {:else} + No hay fracciones disponibles + {/if} +
+
+
+ + + +
+
+ + + + + + CATALOGO DE DEPRECIACION + +
+
+ + +
+
+ + + + + + + + + + {#each depreciationCatalog as item (item.id)} + selectDepreciation(item)} + > + + + + + {:else} + + + + {/each} + +
FracciónDescripción% Depreciación
{item.fraction}{item.description}{item.depreciation_rate}%
+ {#if isLoadingDepreciation} + Cargando... + {:else} + No hay registros disponibles + {/if} +
+
+
+ + + +
+
+ + + + + + CATALOGO FDA + +
+
+ + +
+
+ + + + + + + + + {#each fdaCatalog as item (item.id)} + selectFDA(item)} + > + + + + {:else} + + + + {/each} + +
Clave FDADescripción
{item.fda_key}{item.description}
+ {#if isLoadingFDA} + Cargando... + {:else} + No hay registros disponibles + {/if} +
+
+
+ + + +
+
+ + + + + + CATALOGO DE CARTA PORTE + +
+
+ + +
+
+ + + + + + + + + {#each cartaPorteCatalog as item (item.id)} + { + formData.carta_porte_code = item.code; + showCartaPorteDialog = false; + }} + > + + + + {:else} + + + + {/each} + +
CódigoDescripción
{item.code}{item.description}
+ No hay registros disponibles +
+
+
+ + + +
+
- - - - - - - CATALOGO DE ACTIVO FIJO - -
-
- - -
-
- - - - - - - - - {#each filteredMaterialTypes as material (material.key)} - selectMaterial(material)} - > - - - - {/each} - -
ClaveDescripción
{material.key}{material.description}
-
-
- - - -
-
- - - - - - UNIDADES DE MEDIDA - -
-
- - -
-
- - - - - - - - - - - {#each filteredUnits as unit (unit.code)} - selectUnit(unit)} - > - - - - - - {/each} - -
CódigoDescripciónDescription (English)Clave Mexicana
{unit.code}{unit.description}{unit.descriptionEnglish}{unit.claveMexicana}
-
-
- - - -
-
- - - - - - CATALOGO DE FRACCIONES SITAR - SCAII - -
-
- - -
-
- - - - - - - - - - - {#each tariffFractions as fraction (fraction.code)} - selectFraction(fraction)} - > - - - - - - {:else} - - - - {/each} - -
FracciónNICODescripciónU.M.T
{fraction.fraction}{fraction.nico}{fraction.description}{fraction.umt}
- {#if isLoadingFractions} - Cargando fracciones... - {:else} - No hay fracciones disponibles - {/if} -
-
-
- - - -
-
- - - - - - CATALOGO DE FRACCIONES AMERICANAS - -
-
- - -
-
- - - - - - - - - - - - {#each usTariffFractions as fraction (fraction.id)} - selectUSFraction(fraction)} - > - - - - - - - {:else} - - - - {/each} - -
CódigoPrefijoAd valoremCosto FijoDescripción
{fraction.code}{fraction.prefix || ''}{fraction.ad_valorem || '0.00'}{fraction.fixed_cost || '0.00'}{fraction.description || ''}
- {#if isLoadingUSFractions} - Cargando fracciones... - {:else} - No hay fracciones disponibles - {/if} -
-
-
- - - -
-
- - - - - - CATALOGO DE DEPRECIACION - -
-
- - -
-
- - - - - - - - - - {#each depreciationCatalog as item (item.id)} - selectDepreciation(item)} - > - - - - - {:else} - - - - {/each} - -
FracciónDescripción% Depreciación
{item.fraction}{item.description}{item.depreciation_rate}%
- {#if isLoadingDepreciation} - Cargando... - {:else} - No hay registros disponibles - {/if} -
-
-
- - - -
-
- - - - - - CATALOGO FDA - -
-
- - -
-
- - - - - - - - - {#each fdaCatalog as item (item.id)} - selectFDA(item)} - > - - - - {:else} - - - - {/each} - -
Clave FDADescripción
{item.fda_key}{item.description}
- {#if isLoadingFDA} - Cargando... - {:else} - No hay registros disponibles - {/if} -
-
-
- - - -
-
- - - - - - CATALOGO DE CARTA PORTE - -
-
- - -
-
- - - - - - - - - {#each cartaPorteCatalog as item (item.id)} - { - formData.carta_porte_code = item.code; - showCartaPorteDialog = false; - }} - > - - - - {:else} - - - - {/each} - -
CódigoDescripción
{item.code}{item.description}
- No hay registros disponibles -
-
-
- - - -
-
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte index ddb6b92d..80cdbf0b 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/country-dialog.svelte @@ -83,7 +83,7 @@ - + CATALOGO DE PAISES @@ -91,11 +91,7 @@
- +
@@ -113,9 +109,7 @@ - + @@ -134,11 +128,11 @@ class="border-b hover:bg-zinc-100 dark:hover:bg-zinc-800 cursor-pointer transition-colors" onclick={() => handleSelect(country)} > - - - - - + + + + + {/each} {#if filteredCountries.length === 0} @@ -152,7 +146,9 @@
Clave M3Clave M3 Clave Mexicana{country.m3_key || ''}{country.mex_key || ''}{country.description_es || ''}{country.ame_key || ''}{country.description_en || ''}{country.m3_key || ''}{country.mex_key || ''}{country.description_es || ''}{country.ame_key || ''}{country.description_en || ''}
-
+
+ {/each} diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tariff-fraction-dialog.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tariff-fraction-dialog.svelte index 02721e57..cdf26bb0 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tariff-fraction-dialog.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tariff-fraction-dialog.svelte @@ -17,7 +17,7 @@ let loadingMore = $state(false); let searchTerm = $state(''); let error = $state(''); - + // Pagination state let currentPage = $state(1); let totalPages = $state(1); @@ -34,7 +34,7 @@ loadingMore = true; } error = ''; - + try { const params = new URLSearchParams({ page: page.toString(), @@ -45,11 +45,11 @@ const response = await fetch(`/api-sveltekit/tariff-fractions?${params}`, { credentials: 'include' }); - + if (response.ok) { const data = await response.json(); console.log('Tariff fractions data received:', data); - + if (data.items && Array.isArray(data.items)) { if (append) { fractions = [...fractions, ...data.items]; @@ -79,10 +79,10 @@ function handleScroll(e: Event) { if (!scrollContainer || loading || loadingMore || !hasMore) return; - + const target = e.target as HTMLDivElement; const scrollBottom = target.scrollHeight - target.scrollTop - target.clientHeight; - + // Load more when within 200px of bottom if (scrollBottom < 200) { loadFractions(currentPage + 1, true); @@ -120,7 +120,7 @@ - + FRACCIONES ARANCELARIAS @@ -139,11 +139,7 @@

-
+
{#if loading}
@@ -157,18 +153,12 @@ - - + + - + @@ -204,9 +194,7 @@ {/if} {#if !hasMore && fractions.length > 0} -
- Todos los resultados cargados -
+
Todos los resultados cargados
{/if} {/if} diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/unit-of-measure-dialog.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/unit-of-measure-dialog.svelte index 55dab050..fa12288a 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/unit-of-measure-dialog.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/unit-of-measure-dialog.svelte @@ -84,7 +84,7 @@ - + CATALOGOS DE UNIDADES DE MEDIDA @@ -92,11 +92,7 @@
- +
@@ -114,9 +110,7 @@
CódigoFracciónCódigoFracción DescripciónNICONICO UMT
- + @@ -157,7 +151,9 @@
U.M.U.M. Descripción Español
-
+
{:else}

No hay información de cumplimiento disponible.

{/if} @@ -362,7 +376,9 @@
-

Descripción de Colores

+

+ Descripción de Colores +

{detail.colors_description || '-'}

@@ -399,7 +415,9 @@
-

Fecha de Cobranza

+

+ Fecha de Cobranza +

{formatDate(collection.collection_date)}

@@ -427,9 +445,7 @@ {/if} - + diff --git a/frontend/src/lib/utils.ts b/frontend/src/lib/utils.ts index c8ad1e74..e7df9728 100644 --- a/frontend/src/lib/utils.ts +++ b/frontend/src/lib/utils.ts @@ -12,19 +12,19 @@ export function cn(...inputs: ClassValue[]) { */ export function getBackendAssetUrl(path: string | null | undefined): string { if (!path) return ''; - + // Si ya es una URL completa, retornarla tal cual if (path.startsWith('http://') || path.startsWith('https://')) { return path; } - + // Eliminar la / inicial si existe para evitar // const cleanPath = path.startsWith('/') ? path.slice(1) : path; - + // Obtener la base URL del API y limpiar el / final si existe let baseUrl = import.meta.env.VITE_API_URL || 'http://localhost:8000'; baseUrl = baseUrl.replace(/\/+$/, ''); // Eliminar todas las / del final - + return `${baseUrl}/${cleanPath}`; } @@ -34,3 +34,39 @@ export type WithoutChild = T extends { child?: any } ? Omit : T; export type WithoutChildren = T extends { children?: any } ? Omit : T; export type WithoutChildrenOrChild = WithoutChildren>; export type WithElementRef = T & { ref?: U | null }; + +/** + * Obtiene el color del badge según el tipo de factura (SCAII Standards) + */ +export function getInvoiceTypeColor(type?: string | null): string { + if (!type) return 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'; + + const normalizedType = type.toLowerCase().trim(); + + // Impo tem (Rojo) + if (normalizedType.includes('impo tem') || normalizedType === 'tem') { + return 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200'; + } + // Impo Def (Verde) + if (normalizedType.includes('impo def') || normalizedType === 'def') { + return 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'; + } + // Comp Mex (Morado) + if (normalizedType.includes('comp mex') || normalizedType === 'mex') { + return 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200'; + } + // Cam. Reg. (Azul) + if (normalizedType.includes('cam. reg.') || normalizedType.includes('cam reg') || normalizedType === 'cr') { + return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'; + } + // Expo. (Azul) + if (normalizedType.includes('expo') || normalizedType === 'exdef' || normalizedType === 'pterm') { + return 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200'; + } + // Imp. Rep. (Azul Claro) + if (normalizedType.includes('imp. rep.') || normalizedType.includes('imp rep') || normalizedType === 'repar') { + return 'bg-sky-100 text-sky-800 dark:bg-sky-900 dark:text-sky-200'; + } + + return 'bg-gray-100 text-gray-800 dark:bg-gray-800 dark:text-gray-200'; +}