From a297592d84653de67afb515f5766dbff59a298a6 Mon Sep 17 00:00:00 2001 From: Kevin_Ramirez Date: Tue, 31 Mar 2026 10:57:51 -0500 Subject: [PATCH] Correcion del CRUD --- backend/api/v1/common/base_models.py | 9 +++++++-- .../api/v1/modules/a76/app_settings/models.py | 4 ++-- .../v1/modules/a76/app_settings/schemas.py | 19 ++++++++++++++++++ .../v1/modules/a76/app_settings/service.py | 20 +++++++++++++++++-- .../dashboard/settings/SsisGenTabsForm.svelte | 2 +- 5 files changed, 47 insertions(+), 7 deletions(-) diff --git a/backend/api/v1/common/base_models.py b/backend/api/v1/common/base_models.py index 78b26204..c58d8ffe 100644 --- a/backend/api/v1/common/base_models.py +++ b/backend/api/v1/common/base_models.py @@ -5,8 +5,8 @@ from sqlalchemy.orm import Mapped, mapped_column from sqlalchemy.sql import func -class TimestampMixin: - """Mixin for common timestamp fields""" +class BaseTimestampMixin: + """Mixin for basic timestamp fields (no soft delete)""" created_at: Mapped[datetime] = mapped_column( DateTime, nullable=False, server_default=func.now() @@ -14,6 +14,11 @@ class TimestampMixin: updated_at: Mapped[datetime] = mapped_column( DateTime, nullable=False, server_default=func.now(), onupdate=func.now() ) + + +class TimestampMixin(BaseTimestampMixin): + """Mixin for common timestamp fields including soft delete""" + deleted_at: Mapped[datetime | None] = mapped_column(DateTime, nullable=True) diff --git a/backend/api/v1/modules/a76/app_settings/models.py b/backend/api/v1/modules/a76/app_settings/models.py index d9c8f8ca..6e0b0e7a 100644 --- a/backend/api/v1/modules/a76/app_settings/models.py +++ b/backend/api/v1/modules/a76/app_settings/models.py @@ -3,9 +3,9 @@ from sqlalchemy import Integer, ForeignKey, UniqueConstraint from sqlalchemy.dialects.postgresql import JSONB from sqlalchemy.orm import Mapped, mapped_column from core.database import Base -from api.v1.common.base_models import TimestampMixin +from api.v1.common.base_models import BaseTimestampMixin -class AppSetting(Base, TimestampMixin): +class AppSetting(Base, BaseTimestampMixin): """ Unified configuration table for Anexo 76. Replaces 14 legacy tables using a hierarchical JSONB override system. diff --git a/backend/api/v1/modules/a76/app_settings/schemas.py b/backend/api/v1/modules/a76/app_settings/schemas.py index 4cf77221..13dbf0da 100644 --- a/backend/api/v1/modules/a76/app_settings/schemas.py +++ b/backend/api/v1/modules/a76/app_settings/schemas.py @@ -117,6 +117,25 @@ class SSisGenSettings(BaseModel): mostrarprogramaimmexprosec: Optional[int] = None costounitarioporempaquefac: Optional[int] = None transmitirfacalterna: Optional[int] = None + muestra_copias_codbarras: Optional[int] = None + activar_revision_fracciones: Optional[int] = None + usarcoveenarchsaaim3: Optional[int] = None + DownloadFTP: Optional[str] = None + MinsDownLFTP: Optional[int] = None + DownloadFTPPath: Optional[str] = None + DescargarFTPoLocal: Optional[str] = None + ServerFTP: Optional[str] = None + UserFTP: Optional[str] = None + PasswordFTP: Optional[str] = None + DirectorioFTP: Optional[str] = None + PathLocalParaDescDe: Optional[str] = None + AgregarRemplazarAutomatico: Optional[str] = None + ActivarProcesoVEquipment: Optional[int] = None + ActivarProcesoDesperdicioJDEdwards: Optional[int] = None + UsarFechaEmisionFactura: Optional[int] = None + Campo18Valsaaim3: Optional[int] = None + actvaloragre: Optional[int] = None + valoragregadogen: Optional[Decimal] = None class SSisGen2Settings(BaseModel): diff --git a/backend/api/v1/modules/a76/app_settings/service.py b/backend/api/v1/modules/a76/app_settings/service.py index f2e6f36f..60d4e19a 100644 --- a/backend/api/v1/modules/a76/app_settings/service.py +++ b/backend/api/v1/modules/a76/app_settings/service.py @@ -10,7 +10,10 @@ from decimal import Decimal def convert_decimals(obj: Any) -> Any: """ Recursively converts Decimal objects to floats for JSON serialization. + Handles nested structures and None values. """ + if obj is None: + return None if isinstance(obj, list): return [convert_decimals(i) for i in obj] elif isinstance(obj, dict): @@ -40,14 +43,19 @@ class AppSettingsService: def get_resolved_settings(db: Session, tenant_id: int, company_id: int) -> Dict[str, Any]: """ Retrieves settings from all levels (Global -> Tenant -> Company) and merges them. + Treats 0 as None for hierarchy resolution. """ + # Normalize 0 to None for context-less lookups + t_id = tenant_id if tenant_id and tenant_id > 0 else None + c_id = company_id if company_id and company_id > 0 else None + stmt = ( select(AppSetting) .where( or_( and_(AppSetting.tenant_id.is_(None), AppSetting.company_id.is_(None)), - and_(AppSetting.tenant_id == tenant_id, AppSetting.company_id.is_(None)), - and_(AppSetting.tenant_id == tenant_id, AppSetting.company_id == company_id), + and_(AppSetting.tenant_id == t_id, AppSetting.company_id.is_(None)) if t_id else False, + and_(AppSetting.tenant_id == t_id, AppSetting.company_id == c_id) if t_id and c_id else False, ) ) .order_by( @@ -82,7 +90,12 @@ class AppSettingsService: def upsert_settings(db: Session, tenant_id: Optional[int], company_id: Optional[int], settings: Dict[str, Any]) -> AppSetting: """ Inserts or updates settings for a specific level. + Treats 0 as None. """ + # Normalize IDs: 0 or None means Global context at that level + tenant_id = tenant_id if tenant_id and tenant_id > 0 else None + company_id = company_id if company_id and company_id > 0 else None + level_label = f"level(tenant={tenant_id}, company={company_id})" logger.info(f"UPSERT Settings START: {level_label}, keys_to_update={list(settings.keys())}") @@ -110,12 +123,15 @@ class AppSettingsService: new_keys = list(data.keys()) if isinstance(data, dict) else [] logger.info(f"UPSERT: Merging category [{cat}]. Old keys: {old_keys}, New keys to merge/overwrite: {new_keys}") + logger.info(f"UPSERT: Merging {len(settings)} top-level categories into existing row.") deep_merge(new_settings, settings) + # Second pass of conversion (merged results might still have Decimals if original row had them) existing.settings = convert_decimals(new_settings) from sqlalchemy.orm.attributes import flag_modified flag_modified(existing, "settings") + logger.info(f"UPSERT: Row updated and flagged as modified. Fields in ssisgen root: {list(new_settings.get('ssisgen', {}).keys())[:10]}...") else: logger.info(f"UPSERT: Creating NEW row for {level_label}") existing = AppSetting( diff --git a/frontend/src/lib/components/dashboard/settings/SsisGenTabsForm.svelte b/frontend/src/lib/components/dashboard/settings/SsisGenTabsForm.svelte index 851fbc94..8c068c09 100644 --- a/frontend/src/lib/components/dashboard/settings/SsisGenTabsForm.svelte +++ b/frontend/src/lib/components/dashboard/settings/SsisGenTabsForm.svelte @@ -23,7 +23,7 @@ DownloadFTP: 'No', DescargarFTPoLocal: 'FTP', MinsDownLFTP: 0, - UsarFechaEmisionFactura: 'false', + UsarFechaEmisionFactura: 0, Campo18Valsaaim3: 0, ...currentData });