From da43659eb3f5d07cc0da2da3ce58ed246552e09f Mon Sep 17 00:00:00 2001 From: Galindo97 Date: Tue, 17 Feb 2026 11:50:50 -0600 Subject: [PATCH] feat: Add Canadian fraction management dialog, implement debounced search for historical fractions and sectors, and enhance USA fraction search with description support. --- .../fractions/tariff_fractions/service.py | 17 +- .../modules/sitar/fracciones_usa/service.py | 3 + .../historical-tariff-fractions.ts | 12 +- .../sectors/SectorsList.svelte | 24 ++- .../fractions/CanadianFractionDialog.svelte | 180 ++++++++++++++++++ .../fractions/CanadianFractionList.svelte | 138 +++++++++++--- .../fractions/HistoricalFractionList.svelte | 14 +- 7 files changed, 345 insertions(+), 43 deletions(-) create mode 100644 frontend/src/lib/components/dashboard/goods/fractions/CanadianFractionDialog.svelte diff --git a/backend/api/v1/modules/a76/general_catalogs/fractions/tariff_fractions/service.py b/backend/api/v1/modules/a76/general_catalogs/fractions/tariff_fractions/service.py index 09ea40bb..24669f33 100644 --- a/backend/api/v1/modules/a76/general_catalogs/fractions/tariff_fractions/service.py +++ b/backend/api/v1/modules/a76/general_catalogs/fractions/tariff_fractions/service.py @@ -145,12 +145,23 @@ class TariffFractionService: try: usa_service = FraccionesUSAService.get_instance() search_term = None + search_description = None + if filters and filters.get("search"): - search_term = filters["search"] + term = filters["search"] + # Simple heuristic: if it looks like a code, use code search, else description + # FIX: Short numeric codes (e.g. "01") often fail strict 'fraccion' search. + # Treat them as description search for partial matching. + clean_term = term.replace(".", "") + if clean_term.isdigit() and len(clean_term) >= 4: + search_term = term + else: + search_description = term - # USA Service search signature: fraccion, skip, limit + # USA Service search signature: fraccion, descripcion, skip, limit usa_items = await usa_service.search( fraccion=search_term, + descripcion=search_description, skip=skip, limit=limit ) @@ -161,7 +172,9 @@ class TariffFractionService: total += 1 return items, total except Exception as e: + import traceback logger.error(f"Error fetching USA fractions (API): {e}") + logger.error(traceback.format_exc()) # Return empty list on error as per requirement (since API is broken) return [], 0 diff --git a/backend/api/v1/modules/sitar/fracciones_usa/service.py b/backend/api/v1/modules/sitar/fracciones_usa/service.py index 00820835..b1f739df 100644 --- a/backend/api/v1/modules/sitar/fracciones_usa/service.py +++ b/backend/api/v1/modules/sitar/fracciones_usa/service.py @@ -21,6 +21,7 @@ class FraccionesUSAService(SitarAPIBaseService): async def search( self, fraccion: Optional[str] = None, + descripcion: Optional[str] = None, skip: int = 0, limit: int = 100, ) -> List[FraccionesUSAResponse]: @@ -28,6 +29,8 @@ class FraccionesUSAService(SitarAPIBaseService): params = {"skip": skip, "limit": min(limit, 1000)} if fraccion: params["fraccion"] = fraccion + if descripcion: + params["descripcion"] = descripcion data = await self._make_request("GET", "api/v1/fracciones-usa/", params=params) return [FraccionesUSAResponse(**item) for item in data] diff --git a/frontend/src/lib/api/dashboard/a76/general_catalogs/historical-tariff-fractions.ts b/frontend/src/lib/api/dashboard/a76/general_catalogs/historical-tariff-fractions.ts index 427d0a90..79b2ae75 100644 --- a/frontend/src/lib/api/dashboard/a76/general_catalogs/historical-tariff-fractions.ts +++ b/frontend/src/lib/api/dashboard/a76/general_catalogs/historical-tariff-fractions.ts @@ -41,7 +41,15 @@ export async function getHistoricalFractions( if (historicalFraction) params.append('historical_fraction', historicalFraction); - const response = await api.get(`/v1/a76/general_catalogs/fractions/historical-tariff-fractions/?${params.toString()}`); + const response = await api.get<{ message?: string, items?: HistoricalFraction[], total?: number }>(`/v1/a76/fractions/historical-tariff-fractions/?${params.toString()}`); + + // Handle potential wrapper response + if (response.data && 'items' in response.data) { + return response.data as unknown as HistoricalFractionList; + } + if (!response.data) throw new Error('Error fetching historical fractions'); - return response.data; + + // Fallback + return response.data as unknown as HistoricalFractionList; } diff --git a/frontend/src/lib/components/dashboard/general_catalogs/sectors/SectorsList.svelte b/frontend/src/lib/components/dashboard/general_catalogs/sectors/SectorsList.svelte index 9527302c..6f60f61a 100644 --- a/frontend/src/lib/components/dashboard/general_catalogs/sectors/SectorsList.svelte +++ b/frontend/src/lib/components/dashboard/general_catalogs/sectors/SectorsList.svelte @@ -18,6 +18,7 @@ let pageSize = 50; let hasMore = true; let total = 0; + let searchTimeout: ReturnType; async function loadSectors(reset = false) { if (loading || (!hasMore && !reset)) return; @@ -52,6 +53,20 @@ loadSectors(true); } + function handleSearchInput() { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(() => { + loadSectors(true); + }, 500); + } + + function handleKeyDown(event: KeyboardEvent) { + if (event.key === 'Enter') { + clearTimeout(searchTimeout); + handleSearch(); + } + } + function handleLoadMore() { if (!loading && hasMore) { page++; @@ -78,15 +93,10 @@ placeholder="Buscar por clave o descripción..." class="pl-8" bind:value={searchTerm} - onkeydown={(e) => e.key === 'Enter' && handleSearch()} + oninput={handleSearchInput} + onkeydown={handleKeyDown} /> - diff --git a/frontend/src/lib/components/dashboard/goods/fractions/CanadianFractionDialog.svelte b/frontend/src/lib/components/dashboard/goods/fractions/CanadianFractionDialog.svelte new file mode 100644 index 00000000..2ccf045e --- /dev/null +++ b/frontend/src/lib/components/dashboard/goods/fractions/CanadianFractionDialog.svelte @@ -0,0 +1,180 @@ + + + + + + {fraction ? 'Editar' : 'Crear'} Fracción Canadiense + + {fraction + ? 'Modifica los detalles de la fracción seleccionada.' + : 'Ingresa los datos para la nueva fracción.'} + + + +
+
+
+ + +
+
+ + +
+
+ +
+ +