from enum import Enum from typing import Optional, List from sqlalchemy import BigInteger, Boolean, 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 class OperationType(str, Enum): IMP = "imp" # Importación EXP = "exp" # Exportación class TransportType(str, Enum): NONE = "none" # Ninguno TRANSPORT = "transport" # Transporte BOX = "box" # Caja PLATES = "licence plates" # Placas TRUCK = "truck" # Camión VESSEL = "vessel" # Buque BARGE = "rail barge" # Ferrobarcaza CONTAINER = "container" # Contenedor AIRPLANE = "airplane" # Avión GONDOLA = "gondola" # Góndola FLATBED = "flatbed" # Plataforma # --- 1. Invoice Header (invoice_header) --- class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin): __tablename__ = "invoice_header" __table_args__ = ( {"schema": "a76"}, ) id: Mapped[int] = mapped_column( BigInteger, primary_key=True, autoincrement=True) # Identifiers operation_type: Mapped[OperationType] = mapped_column( String(3)) # TIPOMOVIMIENTO / Clasifica imp/exp invoice_type: Mapped[Optional[str]] = mapped_column(ForeignKey( "public.invoice_types.key")) # TIPOFACTURA (invoice_types) 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 is_updated: Mapped[Optional[str]] = mapped_column(Boolean) # ESTATUS updated_date: Mapped[Optional[datetime]] = mapped_column(TIMESTAMP(timezone=False)) # FECHAACTUALIZACION who_updated: Mapped[Optional[str]] = mapped_column(String(20)) # QUIENA ACTUALIZO traffic_light_status: Mapped[Optional[str]] = mapped_column(String(50)) # SEMAFORO / SEMAFOROEXPO/IMPO process_log: Mapped[Optional[str]] = mapped_column(String(300)) # COMOFUEPROCESADA # Comments observation_es: Mapped[Optional[str]] = mapped_column(Text) #OBSERVACIONE observation_en: Mapped[Optional[str]] = mapped_column(Text) #OBSERVACIONI comments_status: Mapped[Optional[str]] = mapped_column(Text) #COMENTARIOSESTATUS # Digital Archive Links cfdi_uuid: Mapped[Optional[str]] = mapped_column(String(100)) # CFDIUUID path_pdf: Mapped[Optional[str]] = mapped_column(String(500)) # CFDIPATHPDF path_xml: Mapped[Optional[str]] = mapped_column(String(500)) # CFDIPATHXML # 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" __table_args__ = ( {"schema": "a76"}, ) invoice_id: Mapped[int] = mapped_column( ForeignKey("a76.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(ForeignKey("public.customs_sections.customs_code")) # ADUANA_CRUCE # Clients & Providers provider_header: Mapped[Optional[str]] = mapped_column(String(20)) # PROVEEDOREXPORTADOR provider_id: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # PROVEEDOR sold_to_header: Mapped[Optional[str]] = mapped_column(String(20)) # VENDIDOCONSIGNADO sold_to_id: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # VENDIDOA shipped_to_header: Mapped[Optional[str]] = mapped_column(String(20)) # ENVIADOTRANSFERIDO shipped_to_id: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # ENVIADOA shipped_by_header: Mapped[Optional[str]] = mapped_column(String(20)) # ENVIADOPORVENDIDOPOR shipped_by_id: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # VENDIDOPOR customs_broker_id: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.customs_brokers.id")) # AADUANAL # Flags & Specific Regimes is_mixed: Mapped[Optional[bool]] = mapped_column(Boolean) # ESMIXTO waste_type: Mapped[Optional[str]] = mapped_column(String(1)) # TIPODESPERDICIO 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" __table_args__ = ( {"schema": "a76"}, ) id: Mapped[int] = mapped_column( BigInteger, primary_key=True, autoincrement=True) invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id")) currency: Mapped[Optional[str]] = mapped_column(String(3)) # CLAVEMONEDA currency_type: Mapped[Optional[str]] = mapped_column(ForeignKey("public.currency_types.code")) # TIPOCLAVEMONEDA 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 # 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 net_weight: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # PESONETO 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" __table_args__ = ( {"schema": "a76"}, ) id: Mapped[int] = mapped_column( BigInteger, primary_key=True, autoincrement=True) invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id")) # Carrier Info carrier_id: Mapped[Optional[str]] = mapped_column( String(10)) # TRANSPORTISTA transport_type: Mapped[TransportType] = mapped_column( String(15), default="none") # TRANSPORTE transport_mode: Mapped[Optional[str]] = mapped_column( String(15)) # MODTRANS 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" __table_args__ = ( {"schema": "a76"}, ) id: Mapped[int] = mapped_column( BigInteger, primary_key=True, autoincrement=True) invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.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" __table_args__ = ( {"schema": "a76"}, ) id: Mapped[int] = mapped_column( BigInteger, primary_key=True, autoincrement=True) invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.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")