From 0c43c252a4f95b1cb3493ba02155ef4bad6c8c7e Mon Sep 17 00:00:00 2001 From: Kevin_Ramirez Date: Tue, 13 Jan 2026 14:22:15 -0600 Subject: [PATCH] Se completo la integracion del celery en la creacion de reportes --- backend/api/v1/modules/a76/classes/models.py | 5 ++-- .../units_of_measure/models.py | 4 +++ .../v1/modules/a76/items/line_items/models.py | 17 ++++++----- .../a76/reports/importacion/facturas/task.py | 8 ++++-- backend/api/v1/modules/core/tenants/models.py | 3 +- .../components/dashboard/invoices/columns.ts | 28 +++++++++++-------- 6 files changed, 39 insertions(+), 26 deletions(-) diff --git a/backend/api/v1/modules/a76/classes/models.py b/backend/api/v1/modules/a76/classes/models.py index 58459b11..e97a2f80 100644 --- a/backend/api/v1/modules/a76/classes/models.py +++ b/backend/api/v1/modules/a76/classes/models.py @@ -17,10 +17,11 @@ from sqlalchemy import ( ) from sqlalchemy.orm import Mapped, mapped_column, relationship +from api.v1.modules.public.reference_data.material_types.models import MaterialType +from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure + if TYPE_CHECKING: from api.v1.modules.a76.parts.models import Part - from api.v1.modules.public.reference_data.material_types.models import MaterialType - from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure class Class(Base, TenantScopedMixin, TimestampMixin): diff --git a/backend/api/v1/modules/a76/general_catalogs/units_of_measure/models.py b/backend/api/v1/modules/a76/general_catalogs/units_of_measure/models.py index d33b82b6..8bf32a30 100644 --- a/backend/api/v1/modules/a76/general_catalogs/units_of_measure/models.py +++ b/backend/api/v1/modules/a76/general_catalogs/units_of_measure/models.py @@ -12,6 +12,10 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from api.v1.common.base_models import TenantScopedMixin, TimestampMixin from core.database import Base +# Import models referenced by TenantScopedMixin (Tenant, Company) to ensure they are loaded +from api.v1.modules.core.tenants.models import Tenant +from api.v1.modules.a76.general_catalogs.company.models import Company + # 1. GUniMedACE diff --git a/backend/api/v1/modules/a76/items/line_items/models.py b/backend/api/v1/modules/a76/items/line_items/models.py index ad85b357..8d9fefad 100644 --- a/backend/api/v1/modules/a76/items/line_items/models.py +++ b/backend/api/v1/modules/a76/items/line_items/models.py @@ -5,6 +5,9 @@ from sqlalchemy.orm import Mapped, mapped_column, relationship from api.v1.common.base_models import TenantScopedMixin, TimestampMixin from core.database import Base +from api.v1.modules.a76.classes.models import Class +from api.v1.modules.a76.general_catalogs.units_of_measure.models import UnitOfMeasure + if TYPE_CHECKING: from ..models import Item from ..line_financials.models import LineFinancial @@ -12,10 +15,6 @@ if TYPE_CHECKING: from ..line_customs.models import LineCustom from ..line_descriptions.models import LineDescription from ..line_references.models import LineReference - from api.v1.modules.a76.classes.models import Class - from api.v1.modules.a76.general_catalogs.units_of_measure.models import ( - UnitOfMeasure, - ) from api.v1.modules.a24.fa.fa_item_lines.models import FaLineItem @@ -35,14 +34,14 @@ class LineItem(Base, TenantScopedMixin, TimestampMixin): line_number: Mapped[int] = mapped_column(Integer) # LINEAIMPO/LINEAEXPO/LINEA # Part identification - part_number_id: Mapped[Optional[str]] = mapped_column( + part_number: Mapped[Optional[str]] = mapped_column( ForeignKey("a76.parts.id") ) # NUMPARTE - component_part_number_id: Mapped[Optional[str]] = mapped_column( + component_part_number: Mapped[Optional[str]] = mapped_column( ForeignKey("a76.parts.id") ) # NUMPARTECOM - class_id: Mapped[Optional[str]] = mapped_column( - ForeignKey("a76.classes.id") + class_code: Mapped[Optional[str]] = mapped_column( + ForeignKey("a76.classes.class_code") ) # CLASE # Unit of measure @@ -173,7 +172,7 @@ class LineItem(Base, TenantScopedMixin, TimestampMixin): back_populates="line", cascade="all, delete-orphan", uselist=False ) class_info: Mapped[Optional["Class"]] = relationship( - foreign_keys=[class_id], viewonly=True + foreign_keys=[class_code], viewonly=True ) unit_of_measure_info: Mapped[Optional["UnitOfMeasure"]] = relationship( foreign_keys=[unit_of_measure], viewonly=True diff --git a/backend/api/v1/modules/a76/reports/importacion/facturas/task.py b/backend/api/v1/modules/a76/reports/importacion/facturas/task.py index 1b859965..a4059f34 100644 --- a/backend/api/v1/modules/a76/reports/importacion/facturas/task.py +++ b/backend/api/v1/modules/a76/reports/importacion/facturas/task.py @@ -1,14 +1,15 @@ import base64 import logging from core.celery_app import celery_app +from celery import current_task, states from core.database import CoreSessionLocal from .mex.service import FacturaImportacionMexService logger = logging.getLogger(__name__) -@celery_app.task(name="generar_pdf_factura_async") -def generar_pdf_factura_async(invoice_id: int, company_id: int): +@celery_app.task(name="generar_pdf_factura_async", bind=True) +def generar_pdf_factura_async(self, invoice_id: int, company_id: int): """ Esta tarea la ejecuta el Worker. No usa FastAPI, usa directamente SQLAlchemy. """ @@ -19,6 +20,9 @@ def generar_pdf_factura_async(invoice_id: int, company_id: int): # 2. Instanciamos el servicio de reportes service = FacturaImportacionMexService() + + # Update state to PROCESSING + self.update_state(state='PROCESSING', meta={'current': 1, 'total': 1, 'status': 'Generating PDF...'}) # 3. Generamos los bytes del PDF pdf_bytes, nombre, media_type = service.generar_factura_completa( diff --git a/backend/api/v1/modules/core/tenants/models.py b/backend/api/v1/modules/core/tenants/models.py index e76fd34e..579db1e7 100644 --- a/backend/api/v1/modules/core/tenants/models.py +++ b/backend/api/v1/modules/core/tenants/models.py @@ -12,8 +12,7 @@ from sqlalchemy import Enum as SQLEnum from sqlalchemy import Integer, String, Text from sqlalchemy.orm import Mapped, relationship -if TYPE_CHECKING: - from api.v1.modules.core.user_tenant.models import UserTenant +from api.v1.modules.core.user_tenant.models import UserTenant class TenantType(enum.Enum): diff --git a/frontend/src/lib/components/dashboard/invoices/columns.ts b/frontend/src/lib/components/dashboard/invoices/columns.ts index aef1f484..7a22bac0 100644 --- a/frontend/src/lib/components/dashboard/invoices/columns.ts +++ b/frontend/src/lib/components/dashboard/invoices/columns.ts @@ -13,7 +13,10 @@ function formatDate(date?: string | null): string { }); } -export function createColumns(onSuccess?: () => void): ColumnDef[] { +export function createColumns( + onSuccess?: () => void, + onDownload?: (invoice: Invoice) => void +): ColumnDef[] { return [ { accessorKey: "operation_type", @@ -29,8 +32,8 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { return { render: () => ` - ${label} - ` + ${label} + ` }; }); return renderSnippet(operationSnippet, { type: operationType }); @@ -149,7 +152,6 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { accessorKey: "total_items", header: "Total Partidas", cell: ({ row }) => { - // El total de items viene del conteo de details const totalItems = row.original.details?.length || 0; const itemsSnippet = createRawSnippet<[{ total: number }]>((getTotal) => { @@ -198,7 +200,6 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { accessorKey: "logistics.weight_type", header: "Tipo Peso", cell: ({ row }) => { - // weight_type está en logistics que es un array, tomamos el primer elemento const weightType = row.original.logistics?.weight_type; const weightSnippet = createRawSnippet<[{ type?: string | null }]>((getType) => { @@ -224,8 +225,8 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { return { render: () => ` - ${label} - ` + ${label} + ` }; }); return renderSnippet(updatedSnippet, { isUpdated }); @@ -244,8 +245,8 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { return { render: () => ` - ${label} - ` + ${label} + ` }; }); return renderSnippet(mixedSnippet, { isMixed }); @@ -267,14 +268,19 @@ export function createColumns(onSuccess?: () => void): ColumnDef[] { return renderSnippet(relDocSnippet, { relDoc }); } }, + // 2. MODIFICAMOS AQUÍ: Pasamos onDownload al componente { id: "actions", cell: ({ row }) => { - return renderComponent(DataTableActions, { invoice: row.original, onSuccess }); + return renderComponent(DataTableActions, { + invoice: row.original, + onSuccess, + onDownload + }); } } ]; } -// Mantener compatibilidad hacia atrás + export const columns = createColumns(); \ No newline at end of file