Se completo la integracion del celery en la creacion de reportes

This commit is contained in:
2026-01-13 14:22:15 -06:00
parent 9d8e1283a8
commit 0c43c252a4
6 changed files with 39 additions and 26 deletions

View File

@@ -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):

View File

@@ -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

View File

@@ -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

View File

@@ -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.
"""
@@ -20,6 +21,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(
db=db,

View File

@@ -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):

View File

@@ -13,7 +13,10 @@ function formatDate(date?: string | null): string {
});
}
export function createColumns(onSuccess?: () => void): ColumnDef<Invoice>[] {
export function createColumns(
onSuccess?: () => void,
onDownload?: (invoice: Invoice) => void
): ColumnDef<Invoice>[] {
return [
{
accessorKey: "operation_type",
@@ -29,8 +32,8 @@ export function createColumns(onSuccess?: () => void): ColumnDef<Invoice>[] {
return {
render: () =>
`<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${colorClass}">
${label}
</span>`
${label}
</span>`
};
});
return renderSnippet(operationSnippet, { type: operationType });
@@ -149,7 +152,6 @@ export function createColumns(onSuccess?: () => void): ColumnDef<Invoice>[] {
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<Invoice>[] {
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<Invoice>[] {
return {
render: () =>
`<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${colorClass}">
${label}
</span>`
${label}
</span>`
};
});
return renderSnippet(updatedSnippet, { isUpdated });
@@ -244,8 +245,8 @@ export function createColumns(onSuccess?: () => void): ColumnDef<Invoice>[] {
return {
render: () =>
`<span class="inline-flex items-center rounded-full px-2.5 py-0.5 text-xs font-medium ${colorClass}">
${label}
</span>`
${label}
</span>`
};
});
return renderSnippet(mixedSnippet, { isMixed });
@@ -267,14 +268,19 @@ export function createColumns(onSuccess?: () => void): ColumnDef<Invoice>[] {
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();