From ff14bd36457fd9048d688e10bff9a8f7a6b51ebc Mon Sep 17 00:00:00 2001 From: hreyes Date: Fri, 29 May 2026 11:52:32 -0600 Subject: [PATCH] feature/mensajes-claros-en-eliminar-clases --- backend/api/v1/modules/a76/classes/service.py | 37 ++++++++++++++++++- .../goods/fixed-asset-classes/+page.svelte | 25 ++++++++----- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/backend/api/v1/modules/a76/classes/service.py b/backend/api/v1/modules/a76/classes/service.py index 5dd6c532..3f106c6b 100644 --- a/backend/api/v1/modules/a76/classes/service.py +++ b/backend/api/v1/modules/a76/classes/service.py @@ -370,11 +370,46 @@ class ClassService: def delete(db: Session, class_id: int, tenant_id: int, company_id: int) -> bool: """Delete a class (and its FA extension if exists)""" from api.v1.modules.a24.fa.fa_classes.models import QClasses - + from api.v1.modules.a76.items.models import LineItem + from api.v1.modules.a76.parts.models import Part + class_obj = ClassService.get_by_id(db, class_id, tenant_id, company_id) if not class_obj: return False + used_in_parts = ( + db.query(Part.id) + .filter( + Part.part_class == class_obj.class_code, + Part.tenant_id == tenant_id, + Part.company_id == company_id, + ) + .first() + is not None + ) + used_in_invoices = ( + db.query(LineItem.id) + .filter( + LineItem.class_id == class_id, + LineItem.tenant_id == tenant_id, + LineItem.company_id == company_id, + ) + .first() + is not None + ) + + if used_in_parts or used_in_invoices: + reasons: list[str] = [] + if used_in_parts: + reasons.append("partes") + if used_in_invoices: + reasons.append("facturas") + usage = " y ".join(reasons) + raise HTTPException( + status_code=409, + detail=f"No se puede eliminar la clase porque ya fue utilizada en {usage}.", + ) + # Delete FA extension first (if exists) to avoid FK constraint violation fa_extension = db.query(QClasses).filter( QClasses.class_id == class_id, diff --git a/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte b/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte index abf8562c..7d866221 100644 --- a/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte +++ b/frontend/src/routes/dashboard/goods/fixed-asset-classes/+page.svelte @@ -6,6 +6,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'; @@ -321,29 +322,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();