Files
plantillas-proyectos/backend/api/v1/modules/a76/invoices/models.py
acazares 7e9c8397ef feat: Implement invoice management module with CRUD operations
- Added InvoiceHeader, InvoiceComplianceMx, InvoiceFinancials, InvoiceLogistics, InvoiceSalesDetails, and InvoiceCollections models.
- Created schemas for invoice operations including create, update, and response schemas.
- Developed services for handling business logic related to invoices, including retrieval, creation, updating, and deletion of invoices and their related data.
- Introduced routes for invoice management, enabling CRUD operations through a RESTful API.
- Integrated invoice routes into the main application router.
- Removed unused routers from the core module to streamline the API structure.
2025-12-09 16:00:46 -06:00

169 lines
8.9 KiB
Python

from typing import Optional, List
from sqlalchemy import BigInteger, Date, ForeignKey, Integer, Numeric, String, Text, TIMESTAMP
from sqlalchemy.orm import Mapped, mapped_column, relationship
from core.database import Base
from datetime import datetime
from ....common.base_models import TenantScopedMixin, TimestampMixin
# --- 1. Invoice Header (invoice_header) ---
class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "invoice_header"
__tableargs__ = {'schema': 'a76'}
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
# Identifiers
legacy_id: Mapped[Optional[int]] = mapped_column(Integer) # CONSECUTIVO
operation_type: Mapped[str] = mapped_column(String(20)) # TIPOMOVIMIENTO / Clasifica IMP/EXP/SM
invoice_number: Mapped[Optional[str]] = mapped_column(String(20)) # FACTURAIMPO/FACTURAEXPO/FACTURAREMISION
project_number: Mapped[Optional[str]] = mapped_column(String(14)) # NUMPROYECTO
purchase_order: Mapped[Optional[str]] = mapped_column(String(50)) # ORDENCOMPRA
related_doc_id: Mapped[Optional[int]] = mapped_column(Integer) # IDRELDOC (Para Rectificaciones)
# Dates
invoice_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAFACTURA
capture_date: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=False), default=datetime.now) # FECHACAPTURA + HORAACTUAL
# Status & Control
status: Mapped[Optional[str]] = mapped_column(String(5)) # ESTATUS
traffic_light_status: Mapped[Optional[str]] = mapped_column(String(50)) # SEMAFORO / SEMAFOROEXPO/IMPO
process_log: Mapped[Optional[str]] = mapped_column(String(300)) # COMOFUEPROCESADA **<-- AÑADIDO**
comments: Mapped[Optional[str]] = mapped_column(Text) # OBSERVACIONE + OBSERVACIONI + COMENTARIOSESTATUS
# Digital Archive Links
cfdi_uuid: Mapped[Optional[str]] = mapped_column(String(100)) # CFDIUUID
path_pdf: Mapped[Optional[str]] = mapped_column(String(500)) # CFDIPATHPDF **<-- AÑADIDO**
path_xml: Mapped[Optional[str]] = mapped_column(String(500)) # CFDIPATHXML **<-- AÑADIDO**
# Relationships (Para navegacion ORM)
compliance_mx: Mapped["InvoiceComplianceMx"] = relationship(back_populates="header", cascade="all, delete-orphan")
financials: Mapped["InvoiceFinancials"] = relationship(back_populates="header", cascade="all, delete-orphan")
details: Mapped[List["InvoiceSalesDetails"]] = relationship(back_populates="header", cascade="all, delete-orphan")
collections: Mapped[List["InvoiceCollections"]] = relationship(back_populates="header", cascade="all, delete-orphan")
logistics: Mapped[List["InvoiceLogistics"]] = relationship(back_populates="header", cascade="all, delete-orphan")
# --- 2. Compliance MX (invoice_compliance_mx) ---
class InvoiceComplianceMx(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "invoice_compliance_mx"
__tableargs__ = {'schema': 'a76'}
invoice_id: Mapped[int] = mapped_column(ForeignKey("invoice_header.id"), primary_key=True)
# Core Customs Data
pedimento: Mapped[Optional[str]] = mapped_column(String(19)) # PEDIMENTO/PEDIMENTOIMPO/EXPO
pedimento_code: Mapped[Optional[str]] = mapped_column(String(5)) # PEDIMENTOR1, K1
remesa: Mapped[Optional[int]] = mapped_column(Integer)
aduana: Mapped[Optional[str]] = mapped_column(String(5)) # ADUANA_CRUCE
customs_agent: Mapped[Optional[str]] = mapped_column(String(10)) # AADUANAL
# Flags & Specific Regimes
is_mixed: Mapped[Optional[str]] = mapped_column(String(2)) # ESMIXTO **<-- AÑADIDO**
waste_type: Mapped[Optional[str]] = mapped_column(String(1)) # TIPODESPERDICIO **<-- AÑADIDO**
appendix_17: Mapped[Optional[int]] = mapped_column(Integer) # APENDICE17
# VUCEM / Digital
edocument: Mapped[Optional[str]] = mapped_column(String(50)) # EDOCUMENT
electronic_signature: Mapped[Optional[str]] = mapped_column(String(999)) # FIRMAELECTRONICA
sem_id: Mapped[Optional[int]] = mapped_column(Integer) # SEM (de SFacEntradaSM/SFacSalidaSM)
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="compliance_mx")
# --- 3. Financials (invoice_financials) ---
class InvoiceFinancials(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "invoice_financials"
__tableargs__ = {'schema': 'a76'}
invoice_id: Mapped[int] = mapped_column(ForeignKey("invoice_header.id"), primary_key=True)
currency: Mapped[Optional[str]] = mapped_column(String(3)) # CLAVEMONEDA
exchange_rate: Mapped[Optional[float]] = mapped_column(Numeric(13, 6)) # TIPOCAMBIO
# Merchandise Values
value_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORIMPOMN/EXPOMN
value_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0)
customs_value_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORADUANASMN **<-- AÑADIDO**
# Costs & Taxes
freight: Mapped[Optional[float]] = mapped_column(Numeric(19, 8), default=0) # FLETE
insurance: Mapped[Optional[float]] = mapped_column(Numeric(19, 8), default=0) # VALSEGUROS/SEGUROS
iva_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # IVAEXPOMN / VALORIVAMN
iva_factor: Mapped[Optional[float]] = mapped_column(Numeric(17, 4)) # FACTORIVA
# Weights & Quantities
total_quantity: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # CANTEXPO / CANTIMPO
gross_weight: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # PESOBRUTO **<-- AÑADIDO**
net_weight: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # PESONETO **<-- AÑADIDO**
bundle_count: Mapped[Optional[int]] = mapped_column(Integer) # CANTBULTOS
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="financials")
# --- 4. Logistics (invoice_logistics) ---
class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "invoice_logistics"
__tableargs__ = {'schema': 'a76'}
logistics_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
invoice_id: Mapped[int] = mapped_column(ForeignKey("invoice_header.id"))
# Carrier Info
carrier_id: Mapped[Optional[str]] = mapped_column(String(10)) # TRANSPORTISTA
transport_mode: Mapped[Optional[str]] = mapped_column(String(15)) # TRANSPORTE
driver_name: Mapped[Optional[str]] = mapped_column(String(80)) # CONDUCTOR
is_rail: Mapped[Optional[str]] = mapped_column(String(2)) # ESFERROCARRIL
rail_id: Mapped[Optional[str]] = mapped_column(String(31)) # IDFERRORCARRIL
# Vehicle & Tracking
vehicle_num: Mapped[Optional[str]] = mapped_column(String(20)) # NUMVEHICULO / NUMTRAILER
license_plate: Mapped[Optional[str]] = mapped_column(String(20)) # NUMTRASPORTE
seal_number: Mapped[Optional[str]] = mapped_column(String(15)) # PRECINTO
guide_number: Mapped[Optional[str]] = mapped_column(String(20)) # NUMEROGUIA
# Logistics Dates
entry_exit_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAENTRADA / FECHAENVIO
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="logistics")
# --- 5. Sales Order Details (invoice_sales_details) ---
class InvoiceSalesDetails(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "invoice_sales_details"
__tableargs__ = {'schema': 'a76'}
detail_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
invoice_id: Mapped[int] = mapped_column(ForeignKey("invoice_header.id"))
line_number: Mapped[int] = mapped_column(Integer) # LINEA
sales_order: Mapped[Optional[str]] = mapped_column(String(20)) # ORDENVENTA
# Specific Custom Fields
colors_description: Mapped[Optional[str]] = mapped_column(String(49)) # COLORES
square_color_code: Mapped[Optional[str]] = mapped_column(String(1)) # COLORCUADRITO
line_bundles: Mapped[Optional[int]] = mapped_column(Integer) # CANTBULTOS (de la linea)
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="details")
# --- 6. Collections (invoice_collections) ---
class InvoiceCollections(Base, TenantScopedMixin, TimestampMixin):
__tablename__ = "invoice_collections"
__tableargs__ = {'schema': 'a76'}
collection_id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
invoice_id: Mapped[int] = mapped_column(ForeignKey("invoice_header.id"))
concept: Mapped[Optional[str]] = mapped_column(String(100)) # CONCEPTO
is_collected: Mapped[Optional[int]] = mapped_column(Integer) # COBRADO
collection_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHACOBRANZA
amount: Mapped[Optional[float]] = mapped_column(Numeric(23, 8)) # VALOR
collector_user: Mapped[Optional[str]] = mapped_column(String(20)) # FACTCOBRADOR
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="collections")