Refactor invoice schemas and services for improved clarity and structure

- Updated schemas in `schemas.py` to enhance readability by aligning field definitions and descriptions.
- Consolidated optional fields and improved default values for better data handling.
- Modified the `InvoiceService` class in `services.py` to streamline error handling and data extraction for nested invoice components.
- Ensured that nested data is processed correctly before creating invoice entries, improving overall service reliability.
This commit is contained in:
2026-01-11 17:09:04 -06:00
parent 887cbfa5ce
commit 20aff5ff7b
4 changed files with 793 additions and 419 deletions

View File

@@ -3,6 +3,7 @@ from .... import schemas
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
from api.v1.modules.a76.general_catalogs.exchange_rate.models import ExchangeRate
from api.v1.modules.a76.clients_and_providers.models import ClientProvider
from api.v1.modules.a76.customs_brokers.models import CustomsBroker
from api.v1.modules.public.reference_data.incoterms.models import Incoterm
from api.v1.modules.a76.items.models import Item
from api.v1.modules.public.reference_data.currency_types.models import CurrencyType
@@ -281,11 +282,11 @@ def validate_common(
)
customs_broker_exists = (
db.query(ClientProvider)
db.query(CustomsBroker)
.filter(
ClientProvider.id == invoice.compliance_mx.customs_broker_id,
ClientProvider.tenant_id == tenant_id,
ClientProvider.company_id == company_id,
CustomsBroker.id == invoice.compliance_mx.customs_broker_id,
CustomsBroker.tenant_id == tenant_id,
CustomsBroker.company_id == company_id,
)
.first()
)

View File

@@ -1,20 +1,33 @@
from enum import Enum
from typing import Optional, List
from sqlalchemy import BigInteger, Boolean, Date, ForeignKey, Integer, Numeric, String, Text, TIMESTAMP
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 Currency(str, Enum):
FOREIGN = "foreign"
LOCAL = "local"
MANUAL = "manual"
class WeightUnit(str, Enum):
KGS = "kgs"
LBS = "lbs"
class DestinationOriginCove(str, Enum):
EDO_BC_PARC_SON = "edo_bc_parc_son"
ESTADO_BCS = "estado_bcs"
@@ -24,6 +37,7 @@ class DestinationOriginCove(str, Enum):
INTERIOR_PAIS = "interior_pais"
MPIO_CABORCA_SON = "mpio_caborca_son"
class OperationType(str, Enum):
IMP = "imp" # Importación
EXP = "exp" # Exportación
@@ -55,39 +69,73 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
# Identifiers
system: Mapped[str] = mapped_column(String(12)) # SISTEMA / Sistema de origen <-- no tiene campo en la antigua base de datos, sera para fixed_asset(scaf), inventory(scaii)
operation_type: Mapped[OperationType] = mapped_column(String(11)) # TIPOMOVIMIENTO / Clasifica imp/exp/sm/ctm
invoice_type: Mapped[str] = mapped_column(ForeignKey("public.invoice_types.key")) # TIPOFACTURA / TIPODOC
document_type: Mapped[str] = mapped_column(ForeignKey("public.pedimento_regimens.code")) # CLAVEDOCUMENTO / Clave de documento
invoice_number: Mapped[str] = mapped_column(String(100)) # FACTURAIMPO/FACTURAEXPO/FACTURAREMISION/FACTURAENVIO/FACTURASALIDA
system: Mapped[str] = mapped_column(
String(12)
) # SISTEMA / Sistema de origen <-- no tiene campo en la antigua base de datos, sera para fixed_asset(scaf), inventory(scaii)
operation_type: Mapped[OperationType] = mapped_column(
String(11)
) # TIPOMOVIMIENTO / Clasifica imp/exp/sm/ctm
invoice_type: Mapped[str] = mapped_column(
ForeignKey("public.invoice_types.key")
) # TIPOFACTURA / TIPODOC
document_type: Mapped[str] = mapped_column(
ForeignKey("public.pedimento_regimens.code")
) # CLAVEDOCUMENTO / Clave de documento
invoice_number: Mapped[str] = mapped_column(
String(100)
) # FACTURAIMPO/FACTURAEXPO/FACTURAREMISION/FACTURAENVIO/FACTURASALIDA
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
alternate_invoice: Mapped[Optional[str]] = mapped_column(String(99)) # FACTURAALTERNA
related_doc_id: Mapped[Optional[int]] = mapped_column(
Integer
) # IDRELDOC / Para Rectificaciones
alternate_invoice: Mapped[Optional[str]] = mapped_column(
String(99)
) # FACTURAALTERNA
invoice_ref: Mapped[Optional[str]] = mapped_column(String(19)) # FACTURAEXPOREF
proforma_number: Mapped[Optional[str]] = mapped_column(String(20)) # NUMEROPROFORMA
# Dates
invoice_date: Mapped[datetime] = mapped_column(Date) # FECHAFACTURA
capture_date: Mapped[datetime] = mapped_column(TIMESTAMP(timezone=False), default=datetime.now) # FECHACAPTURA + HORAACTUAL
capture_date: Mapped[datetime] = mapped_column(
TIMESTAMP(timezone=False), default=datetime.now
) # FECHACAPTURA + HORAACTUAL
emission_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAEMISION
# Status & Control
is_updated: Mapped[bool] = mapped_column(Boolean) # ESTATUS
is_updated_rec: Mapped[Optional[bool]] = mapped_column(Boolean) # ESTATUSREC / Estatus de recepción
is_updated_rep: Mapped[Optional[bool]] = mapped_column(Boolean) # ESTATUSREP / Estatus de reporte
updated_date: Mapped[Optional[datetime]] = mapped_column(TIMESTAMP(timezone=False)) # FECHAACTUALIZACION / FECHAACTUAL
who_updated: Mapped[Optional[str]] = mapped_column(String(20)) # USUARIOACT / Quien actualizó
capture_user: Mapped[Optional[str]] = mapped_column(String(20)) # USUARIOCAP / Usuario que capturó
traffic_light_status: Mapped[Optional[str]] = mapped_column(String(50)) # SEMAFORO / SEMAFOROEXPO/IMPO
process_log: Mapped[Optional[str]] = mapped_column(String(300)) # COMOFUEPROCESADA
is_updated_rec: Mapped[Optional[bool]] = mapped_column(
Boolean
) # ESTATUSREC / Estatus de recepción
is_updated_rep: Mapped[Optional[bool]] = mapped_column(
Boolean
) # ESTATUSREP / Estatus de reporte
updated_date: Mapped[Optional[datetime]] = mapped_column(
TIMESTAMP(timezone=False)
) # FECHAACTUALIZACION / FECHAACTUAL
who_updated: Mapped[Optional[str]] = mapped_column(
String(20)
) # USUARIOACT / Quien actualizó
capture_user: Mapped[Optional[str]] = mapped_column(
String(20)
) # USUARIOCAP / Usuario que capturó
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 / Observaciones en español
observation_en: Mapped[Optional[str]] = mapped_column(Text) # OBSERVACIONI / Observaciones en inglés
observation_es: Mapped[Optional[str]] = mapped_column(
Text
) # OBSERVACIONE / Observaciones en español
observation_en: Mapped[Optional[str]] = mapped_column(
Text
) # OBSERVACIONI / Observaciones en inglés
comments_status: Mapped[Optional[str]] = mapped_column(Text) # COMENTARIOSESTATUS
vu_observations: Mapped[Optional[str]] = mapped_column(String(500)) # OBSERVACIONESVU / Observaciones VUCEM
vu_observations: Mapped[Optional[str]] = mapped_column(
String(500)
) # OBSERVACIONESVU / Observaciones VUCEM
# Digital Archive Links
cfdi_uuid: Mapped[Optional[str]] = mapped_column(String(100)) # CFDIUUID
@@ -96,36 +144,59 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
# Control & Subcompany
subcompany: Mapped[Optional[str]] = mapped_column(String(5)) # SUBEMPRESA
party_count: Mapped[Optional[int]] = mapped_column(Integer) # CANT_PARTIDAS / Cantidad de partidas
party_count: Mapped[Optional[int]] = mapped_column(
Integer
) # CANT_PARTIDAS / Cantidad de partidas
# Generation flags
generate_id: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # GENERAID
generate_desc_parties: Mapped[Optional[str]] = mapped_column(String(12)) # GENDESCPARTIDAS / Generar descripción de partidas
apply_manual_discount: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # APLICADESCMANUAL
generate_id: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # GENERAID
generate_desc_parties: Mapped[Optional[str]] = mapped_column(
String(12)
) # GENDESCPARTIDAS / Generar descripción de partidas
apply_manual_discount: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # APLICADESCMANUAL
# Bulk & Downloads
is_bulk: Mapped[Optional[bool]] = mapped_column(Boolean) # ESAGRANEL / Es a granel
download_substance: Mapped[Optional[bool]] = mapped_column(Boolean) # DESCARGASUST / Descarga de sustancia
download_class: Mapped[Optional[bool]] = mapped_column(Boolean) # DESCARGACLASE / Descarga de clase
download_def: Mapped[Optional[bool]] = mapped_column(Boolean) # DESCARGADEF / Descarga definitiva
download_substance: Mapped[Optional[bool]] = mapped_column(
Boolean
) # DESCARGASUST / Descarga de sustancia
download_class: Mapped[Optional[bool]] = mapped_column(
Boolean
) # DESCARGACLASE / Descarga de clase
download_def: Mapped[Optional[bool]] = mapped_column(
Boolean
) # DESCARGADEF / Descarga definitiva
# Additional fields
payment_terms: Mapped[Optional[str]] = mapped_column(String(200)) # TERMINOSPAGO / Términos de pago
payment_terms: Mapped[Optional[str]] = mapped_column(
String(200)
) # TERMINOSPAGO / Términos de pago
handling_fees: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # MANIOBRAS
option_iv18: Mapped[Optional[str]] = mapped_column(String(50)) # OPCIONIV18
enajenation_goods: Mapped[Optional[bool]] = mapped_column(Boolean) # ENAJENACIONBIENES / Enajenación de bienes
enajenation_goods: Mapped[Optional[bool]] = mapped_column(
Boolean
) # ENAJENACIONBIENES / Enajenación de bienes
# Relationships
compliance_mx: Mapped[Optional["InvoiceComplianceMx"]] = relationship(
back_populates="header", cascade="all, delete-orphan", uselist=False)
back_populates="header", cascade="all, delete-orphan", uselist=False
)
financials: Mapped[Optional["InvoiceFinancials"]] = relationship(
back_populates="header", cascade="all, delete-orphan", uselist=False)
back_populates="header", cascade="all, delete-orphan", uselist=False
)
details: Mapped[List["InvoiceSalesDetails"]] = relationship(
back_populates="header", cascade="all, delete-orphan")
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")
back_populates="header", cascade="all, delete-orphan"
)
logistics: Mapped[Optional["InvoiceLogistics"]] = relationship(
back_populates="header", cascade="all, delete-orphan", uselist=False
)
# --- 2. Compliance MX (invoice_compliance_mx) ---
@@ -133,78 +204,180 @@ 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)
invoice_id: Mapped[int] = mapped_column(
ForeignKey("a76.invoice_header.id"), primary_key=True
)
# Core Customs Data
pedimento_id: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTO/PEDIMENTOIMPO/EXPO
pedimento_r1: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTOR1
pedimento_k1: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTOK1
pedimento_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.pedimentos.id")
) # PEDIMENTO/PEDIMENTOIMPO/EXPO
pedimento_r1: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.pedimentos.id")
) # PEDIMENTOR1
pedimento_k1: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.pedimentos.id")
) # PEDIMENTOK1
remesa: Mapped[Optional[int]] = mapped_column(Integer) # REMESA
aduana: Mapped[Optional[str]] = mapped_column(ForeignKey("public.customs_sections.customs_code")) # ADUANA_CRUCE
port_of_entry: Mapped[Optional[str]] = mapped_column(String(6)) # PUERTOENTRADA / Puerto de entrada
destination: Mapped[Optional[str]] = mapped_column(String(3)) # DESTINO / Código de destino
manifest_number: Mapped[Optional[str]] = mapped_column(String(15)) # MANIFIESTO / Número de manifiesto
aduana: Mapped[Optional[str]] = mapped_column(
ForeignKey("public.customs_sections.customs_code")
) # ADUANA_CRUCE
port_of_entry: Mapped[Optional[str]] = mapped_column(
String(6)
) # PUERTOENTRADA / Puerto de entrada
destination: Mapped[Optional[str]] = mapped_column(
String(3)
) # DESTINO / Código de destino
manifest_number: Mapped[Optional[str]] = mapped_column(
String(15)
) # MANIFIESTO / Número de manifiesto
# Clients & Providers
provider_header: Mapped[Optional[str]] = mapped_column(String(20)) # PROVEEDOREXPORTADOR
provider_id: Mapped[Optional[int]] = 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[int]] = 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[int]] = 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[int]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # VENDIDOPOR/ENVIADOPOR
customs_broker_id: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.customs_brokers.id")) # AADUANAL / Agente aduanal
customs_broker_us_id: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.customs_brokers.id")) # AADUANALAME / Agente aduanal americano
provider_header: Mapped[Optional[str]] = mapped_column(
String(20)
) # PROVEEDOREXPORTADOR
provider_id: Mapped[Optional[int]] = 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[int]] = 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[int]] = 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[int]] = mapped_column(
ForeignKey("a76.clients_and_providers.id")
) # VENDIDOPOR/ENVIADOPOR
customs_broker_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.customs_brokers.id")
) # AADUANAL / Agente aduanal
customs_broker_us_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.customs_brokers.id")
) # AADUANALAME / Agente aduanal americano
# Broker Invoice
broker_invoice_num: Mapped[Optional[str]] = mapped_column(String(20)) # NUMFACTURABROKER / Número factura broker
broker_invoice_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAFACBROKER / Fecha factura broker
broker_invoice_num: Mapped[Optional[str]] = mapped_column(
String(20)
) # NUMFACTURABROKER / Número factura broker
broker_invoice_date: Mapped[Optional[datetime]] = mapped_column(
Date
) # FECHAFACBROKER / Fecha factura broker
# Flags & Specific Regimes
is_mixed: Mapped[Optional[bool]] = mapped_column(Boolean) # ESMIXTO / Es mixto
waste_type: Mapped[Optional[str]] = mapped_column(String(1)) # TIPODESPERDICIO / Tipo de desperdicio
scrap_type: Mapped[Optional[str]] = mapped_column(String(1)) # TIPOSCRAP / Tipo de scrap
appendix_17: Mapped[Optional[int]] = mapped_column(Integer) # APENDICE17 / Apéndice 17
is_regime_change: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # ESCAMBIOREGIMEN / Es cambio de régimen
which_exchange_rate: Mapped[Optional[str]] = mapped_column(String(5)) # CUALTIPOCAMBIO / Cuál tipo de cambio
value_method: Mapped[Optional[str]] = mapped_column(String(2)) # METVALOR / Método de valoración
act_value: Mapped[Optional[str]] = mapped_column(String(5)) # ACTVALOR / Actualizar valor
is_pedimento_pending: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # PED_PENDIENTE_ASIGNAR (Mapear 1 -> True, 0 -> False)
waste_type: Mapped[Optional[str]] = mapped_column(
String(1)
) # TIPODESPERDICIO / Tipo de desperdicio
scrap_type: Mapped[Optional[str]] = mapped_column(
String(1)
) # TIPOSCRAP / Tipo de scrap
appendix_17: Mapped[Optional[int]] = mapped_column(
Integer
) # APENDICE17 / Apéndice 17
is_regime_change: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # ESCAMBIOREGIMEN / Es cambio de régimen
which_exchange_rate: Mapped[Optional[str]] = mapped_column(
String(5)
) # CUALTIPOCAMBIO / Cuál tipo de cambio
value_method: Mapped[Optional[str]] = mapped_column(
String(2)
) # METVALOR / Método de valoración
act_value: Mapped[Optional[str]] = mapped_column(
String(5)
) # ACTVALOR / Actualizar valor
is_pedimento_pending: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # PED_PENDIENTE_ASIGNAR (Mapear 1 -> True, 0 -> False)
# Ownership & Balances
is_owner_of_goods: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # ESDUENOMCIA / Es dueño de mercancía
generate_balances: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # GENERARSALDOS / Generar saldos
was_reviewed_by_company: Mapped[Optional[bool]] = mapped_column(Boolean) # FUEREVISADAMCIA / Fue revisada por la compañía
is_owner_of_goods: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # ESDUENOMCIA / Es dueño de mercancía
generate_balances: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # GENERARSALDOS / Generar saldos
was_reviewed_by_company: Mapped[Optional[bool]] = mapped_column(
Boolean
) # FUEREVISADAMCIA / Fue revisada por la compañía
# VUCEM / Digital
edocument: Mapped[Optional[str]] = mapped_column(String(50)) # EDOCUMENT / Documento electrónico
electronic_signature: Mapped[Optional[str]] = mapped_column(String(999)) # FIRMAELECTRONICA / Firma electrónica
certificate_number: Mapped[Optional[str]] = mapped_column(String(99)) # NUMEROCERTIFICADO / Número de certificado
niu_number: Mapped[Optional[str]] = mapped_column(String(19)) # NUMERONIU / Número NIU
bill_of_lading_count: Mapped[Optional[str]] = mapped_column(String(12)) # CANTGUIASEMBARQUE / Cantidad guías embarque
addendum_vu: Mapped[Optional[str]] = mapped_column(String(204)) # ADENDAVU / Adenda VUCEM
origin_destination_cove: Mapped[Optional[DestinationOriginCove]] = mapped_column(String(20)) # DESTINOORIGENCOVE / Destino/Origen COVE
vucem_operation_num: Mapped[Optional[str]] = mapped_column(String(19)) # NUMOPERACIONVU / Número operación VUCEM
customs_person_line: Mapped[Optional[int]] = mapped_column(Integer) # LINEAPERSONAAA / Línea persona agente aduanal
edocument: Mapped[Optional[str]] = mapped_column(
String(50)
) # EDOCUMENT / Documento electrónico
electronic_signature: Mapped[Optional[str]] = mapped_column(
String(999)
) # FIRMAELECTRONICA / Firma electrónica
certificate_number: Mapped[Optional[str]] = mapped_column(
String(99)
) # NUMEROCERTIFICADO / Número de certificado
niu_number: Mapped[Optional[str]] = mapped_column(
String(19)
) # NUMERONIU / Número NIU
bill_of_lading_count: Mapped[Optional[str]] = mapped_column(
String(12)
) # CANTGUIASEMBARQUE / Cantidad guías embarque
addendum_vu: Mapped[Optional[str]] = mapped_column(
String(204)
) # ADENDAVU / Adenda VUCEM
origin_destination_cove: Mapped[Optional[DestinationOriginCove]] = mapped_column(
String(20)
) # DESTINOORIGENCOVE / Destino/Origen COVE
vucem_operation_num: Mapped[Optional[str]] = mapped_column(
String(19)
) # NUMOPERACIONVU / Número operación VUCEM
customs_person_line: Mapped[Optional[int]] = mapped_column(
Integer
) # LINEAPERSONAAA / Línea persona agente aduanal
# Additional Control
contingency_mode: Mapped[Optional[bool]] = mapped_column(Boolean) # MODOCONTINGENCIA / Modo contingencia
enclosure: Mapped[Optional[str]] = mapped_column(String(4)) # RECINTO / Recinto fiscal
guide_type_to_identify: Mapped[Optional[str]] = mapped_column(String(1)) # TIPODEGUIAAIDENTIFICAR / Tipo de guía a identificar
location: Mapped[Optional[str]] = mapped_column(String(200)) # LOCALIZACION / Localización
contingency_mode: Mapped[Optional[bool]] = mapped_column(
Boolean
) # MODOCONTINGENCIA / Modo contingencia
enclosure: Mapped[Optional[str]] = mapped_column(
String(4)
) # RECINTO / Recinto fiscal
guide_type_to_identify: Mapped[Optional[str]] = mapped_column(
String(1)
) # TIPODEGUIAAIDENTIFICAR / Tipo de guía a identificar
location: Mapped[Optional[str]] = mapped_column(
String(200)
) # LOCALIZACION / Localización
# DOT & Official
dot_code: Mapped[Optional[str]] = mapped_column(String(20)) # CLAVEDOT / Clave DOT
subdivision: Mapped[Optional[str]] = mapped_column(String(20)) # SUBDIVISION / Subdivisión
acts_as: Mapped[Optional[str]] = mapped_column(String(20)) # FUNGECOMOCO / Funge como
movement_type: Mapped[Optional[str]] = mapped_column(String(31)) # TIPOMOV / Tipo de movimiento
office_document: Mapped[Optional[str]] = mapped_column(String(30)) # OFICIO / Oficio
reason_export: Mapped[Optional[str]] = mapped_column(String(1)) # RAZONEXPORTACION / Razón de exportación
signature_key: Mapped[Optional[str]] = mapped_column(String(10)) # CLAVEFIRMA / Clave de firma
subdivision: Mapped[Optional[str]] = mapped_column(
String(20)
) # SUBDIVISION / Subdivisión
acts_as: Mapped[Optional[str]] = mapped_column(
String(20)
) # FUNGECOMOCO / Funge como
movement_type: Mapped[Optional[str]] = mapped_column(
String(31)
) # TIPOMOV / Tipo de movimiento
office_document: Mapped[Optional[str]] = mapped_column(
String(30)
) # OFICIO / Oficio
reason_export: Mapped[Optional[str]] = mapped_column(
String(1)
) # RAZONEXPORTACION / Razón de exportación
signature_key: Mapped[Optional[str]] = mapped_column(
String(10)
) # CLAVEFIRMA / Clave de firma
# SM specific
sem_id: Mapped[Optional[int]] = mapped_column(Integer) # SEM / ID SEM (de SFacEntradaSM/SFacSalidaSM)
sem_id: Mapped[Optional[int]] = mapped_column(
Integer
) # SEM / ID SEM (de SFacEntradaSM/SFacSalidaSM)
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="compliance_mx")
@@ -219,62 +392,138 @@ class InvoiceFinancials(Base, TenantScopedMixin, TimestampMixin):
invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id"))
# Currency
currency: Mapped[Currency] = mapped_column(String(7)) # CLAVEMONEDA / Clave de moneda
currency_type: Mapped[Optional[str]] = mapped_column(ForeignKey("public.currency_types.code")) # TIPOMONEDA / TIPOCLAVEMONEDA
exchange_rate: Mapped[Optional[float]] = mapped_column(Numeric(13, 6)) # TIPOCAMBIO / Tipo de cambio
exchange_rate_mm: Mapped[Optional[float]] = mapped_column(Numeric(13, 6)) # TIPOCAMBIOMM / Tipo de cambio moneda a moneda
currency: Mapped[Currency] = mapped_column(
String(7)
) # CLAVEMONEDA / Clave de moneda
currency_type: Mapped[Optional[str]] = mapped_column(
ForeignKey("public.currency_types.code")
) # TIPOMONEDA / TIPOCLAVEMONEDA
exchange_rate: Mapped[Optional[float]] = mapped_column(
Numeric(13, 6)
) # TIPOCAMBIO / Tipo de cambio
exchange_rate_mm: Mapped[Optional[float]] = mapped_column(
Numeric(13, 6)
) # TIPOCAMBIOMM / Tipo de cambio moneda a moneda
# Merchandise Values (MN = National Currency, ME = Foreign Currency, MC = Third Currency)
value_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORIMPOMN/VALOREXPOMN/VALORENTMN/VALORSALMN
value_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORIMPOME/VALOREXPOME/VALORENTME/VALORSALME
value_mc: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORIMPOMC/VALOREXPOMC
value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORIMPOMN/VALOREXPOMN/VALORENTMN/VALORSALMN
value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORIMPOME/VALOREXPOME/VALORENTME/VALORSALME
value_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORIMPOMC/VALOREXPOMC
# Customs Value
customs_value_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORADUANASMN / Valor en aduanas MN
customs_value_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORADUANASME / Valor en aduanas ME
customs_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORADUANASMN / Valor en aduanas MN
customs_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORADUANASME / Valor en aduanas ME
# Raw Materials
raw_material_value_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORMPMN / Valor materia prima MN
raw_material_value_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORMPME / Valor materia prima ME
raw_material_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORMPMN / Valor materia prima MN
raw_material_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORMPME / Valor materia prima ME
# Aggregate Value
aggregate_value_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORAGREMN / Valor agregado MN
aggregate_value_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORAGREME / Valor agregado ME
aggregate_value_mc: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORAGREMC / Valor agregado MC
aggregate_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORAGREMN / Valor agregado MN
aggregate_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORAGREME / Valor agregado ME
aggregate_value_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORAGREMC / Valor agregado MC
# Mexican Merchandise Value
mexican_value_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORVMEXMN / Valor mercancía mexicana MN
mexican_value_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORVMEXME / Valor mercancía mexicana ME
mexican_value_mc: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORVMEXMC / Valor mercancía mexicana MC
mexican_value_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORVMEXMN / Valor mercancía mexicana MN
mexican_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORVMEXME / Valor mercancía mexicana ME
mexican_value_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORVMEXMC / Valor mercancía mexicana MC
# National Packaging
national_packaging_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALEMPAQUENACMN / Valor empaque nacional MN
national_packaging_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALEMPAQUENACME / Valor empaque nacional ME
national_packaging_mc: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALEMPAQUENACMC / Valor empaque nacional MC
national_packaging_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALEMPAQUENACMN / Valor empaque nacional MN
national_packaging_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALEMPAQUENACME / Valor empaque nacional ME
national_packaging_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALEMPAQUENACMC / Valor empaque nacional MC
# Costs & Increments
freight: Mapped[Optional[float]] = mapped_column(Numeric(19, 8), default=0) # FLETE / Flete
insurance: Mapped[Optional[float]] = mapped_column(Numeric(19, 8), default=0) # SEGUROS / Seguros
insurance_value: Mapped[Optional[float]] = mapped_column(Numeric(19, 8), default=0) # VALSEGUROS / Valor seguros
packaging: Mapped[Optional[float]] = mapped_column(Numeric(19, 8), default=0) # EMBALAJES / Embalajes
other_increments: Mapped[Optional[float]] = mapped_column(Numeric(19, 8), default=0) # OTROSINCREMENTA / Otros incrementables
total_increments_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # TOTALINCREMMN / Total incrementables MN
total_increments_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # TOTALINCREMME / Total incrementables ME
freight: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
) # FLETE / Flete
insurance: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
) # SEGUROS / Seguros
insurance_value: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
) # VALSEGUROS / Valor seguros
packaging: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
) # EMBALAJES / Embalajes
other_increments: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8), default=0
) # OTROSINCREMENTA / Otros incrementables
total_increments_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # TOTALINCREMMN / Total incrementables MN
total_increments_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # TOTALINCREMME / Total incrementables ME
# Taxes
iva_mn: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # IVAEXPOMN/VALORIVAMN / IVA en MN
iva_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # IVAEXPOME/VALORIVAME / IVA en ME
iva_mc: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # IVAEXPOMC / IVA en MC
iva_factor: Mapped[Optional[str]] = mapped_column(String(10)) # FACTORIVA / Factor IVA (puede ser varchar en imports)
tax_value_me: Mapped[Optional[float]] = mapped_column(Numeric(23, 8), default=0) # VALORIMPUESTOME / Valor impuesto ME
seal_value_2500: Mapped[Optional[bool]] = mapped_column(Boolean) # SELLOVALOR2500 / Sello valor 2500
iva_mn: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # IVAEXPOMN/VALORIVAMN / IVA en MN
iva_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # IVAEXPOME/VALORIVAME / IVA en ME
iva_mc: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # IVAEXPOMC / IVA en MC
iva_factor: Mapped[Optional[str]] = mapped_column(
String(10)
) # FACTORIVA / Factor IVA (puede ser varchar en imports)
tax_value_me: Mapped[Optional[float]] = mapped_column(
Numeric(23, 8), default=0
) # VALORIMPUESTOME / Valor impuesto ME
seal_value_2500: Mapped[Optional[bool]] = mapped_column(
Boolean
) # SELLOVALOR2500 / Sello valor 2500
# Weights & Quantities
total_quantity: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # CANTEXPO/CANTIMPO / Cantidad total
gross_weight: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # PESOBRUTO / Peso bruto
net_weight: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # PESONETO / Peso neto
bundle_count: Mapped[Optional[int]] = mapped_column(Integer) # CANTBULTOS / Cantidad de bultos
weight_factor: Mapped[Optional[float]] = mapped_column(Numeric(19, 8)) # FACTORPESO / Factor de peso
total_quantity: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8)
) # CANTEXPO/CANTIMPO / Cantidad total
gross_weight: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8)
) # PESOBRUTO / Peso bruto
net_weight: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8)
) # PESONETO / Peso neto
bundle_count: Mapped[Optional[int]] = mapped_column(
Integer
) # CANTBULTOS / Cantidad de bultos
weight_factor: Mapped[Optional[float]] = mapped_column(
Numeric(19, 8)
) # FACTORPESO / Factor de peso
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="financials")
@@ -288,62 +537,136 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
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 / Transportista
transport_id: Mapped[Optional[str]] = mapped_column(String(10)) # NUMTRAILER / Transportista
transport_us_id: Mapped[Optional[str]] = mapped_column(String(10)) # TRANSPORTISTAAME / Transportista americano
transport_type: Mapped[TransportType] = mapped_column(String(15), default="none") # TRANSPORTE / Tipo de transporte
transport_num: Mapped[Optional[str]] = mapped_column(String(20)) # NUMTRASPORTE / Número de transporte
transport_mode: Mapped[Optional[str]] = mapped_column(String(15)) # MODTRANS / Modo de transporte
driver_name: Mapped[Optional[str]] = mapped_column(String(80)) # CONDUCTOR / Nombre del conductor
is_rail: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # ESFERROCARRIL / Es ferrocarril
rail_id: Mapped[Optional[str]] = mapped_column(String(31)) # IDFERRORCARRIL / ID ferrocarril
# Carrier Info
carrier_id: Mapped[Optional[str]] = mapped_column(
String(10)
) # TRANSPORTISTA / Transportista
transport_id: Mapped[Optional[str]] = mapped_column(
String(10)
) # NUMTRAILER / Transportista
transport_us_id: Mapped[Optional[str]] = mapped_column(
String(10)
) # TRANSPORTISTAAME / Transportista americano
transport_type: Mapped[TransportType] = mapped_column(
String(15), default="none"
) # TRANSPORTE / Tipo de transporte
transport_num: Mapped[Optional[str]] = mapped_column(
String(20)
) # NUMTRASPORTE / Número de transporte
transport_mode: Mapped[Optional[str]] = mapped_column(
String(15)
) # MODTRANS / Modo de transporte
driver_name: Mapped[Optional[str]] = mapped_column(
String(80)
) # CONDUCTOR / Nombre del conductor
is_rail: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # ESFERROCARRIL / Es ferrocarril
rail_id: Mapped[Optional[str]] = mapped_column(
String(31)
) # IDFERRORCARRIL / ID ferrocarril
# Vehicle & Tracking
vehicle_num: Mapped[Optional[str]] = mapped_column(String(20)) # NUMVEHICULO / Número de vehículo
license_plate: Mapped[Optional[str]] = mapped_column(String(20)) # NUMTRASPORTE / Número de transporte/placa
license_plate_complete: Mapped[Optional[str]] = mapped_column(String(40)) # NUMTRASPORTECOMPLE / Número transporte completo
trailer_num: Mapped[Optional[str]] = mapped_column(String(20)) # NUMTRAILER / Número de trailer
seal_number: Mapped[Optional[str]] = mapped_column(String(15)) # PRECINTO / Precinto
guide_number: Mapped[Optional[str]] = mapped_column(String(20)) # NUMEROGUIA / Número de guía
bill_number: Mapped[Optional[str]] = mapped_column(String(15)) # BILLNUMBER / Número de bill
reference_number: Mapped[Optional[str]] = mapped_column(String(14)) # NUMREFERENCIA / Número de referencia
shipment_number: Mapped[Optional[str]] = mapped_column(String(19)) # NUMEMBARQUE / Número de embarque
vehicle_num: Mapped[Optional[str]] = mapped_column(
String(20)
) # NUMVEHICULO / Número de vehículo
license_plate: Mapped[Optional[str]] = mapped_column(
String(20)
) # NUMTRASPORTE / Número de transporte/placa
license_plate_complete: Mapped[Optional[str]] = mapped_column(
String(40)
) # NUMTRASPORTECOMPLE / Número transporte completo
trailer_num: Mapped[Optional[str]] = mapped_column(
String(20)
) # NUMTRAILER / Número de trailer
seal_number: Mapped[Optional[str]] = mapped_column(
String(15)
) # PRECINTO / Precinto
guide_number: Mapped[Optional[str]] = mapped_column(
String(20)
) # NUMEROGUIA / Número de guía
bill_number: Mapped[Optional[str]] = mapped_column(
String(15)
) # BILLNUMBER / Número de bill
reference_number: Mapped[Optional[str]] = mapped_column(
String(14)
) # NUMREFERENCIA / Número de referencia
shipment_number: Mapped[Optional[str]] = mapped_column(
String(19)
) # NUMEMBARQUE / Número de embarque
# Incoterms
incoterm: Mapped[Optional[str]] = mapped_column(String(5)) # INCOTERM / Término de comercio internacional
incoterm: Mapped[Optional[str]] = mapped_column(
String(5)
) # INCOTERM / Término de comercio internacional
# Identifiers & Complements
identifier_1: Mapped[Optional[str]] = mapped_column(String(2)) # IDENTIFICADOR / Identificador 1
complement_1: Mapped[Optional[str]] = mapped_column(String(30)) # COMPLEMENTO1 / Complemento 1
identifier_2: Mapped[Optional[str]] = mapped_column(String(2)) # IDENTIFICADOR2 / Identificador 2
complement_2: Mapped[Optional[str]] = mapped_column(String(30)) # COMPLEMENTO2 / Complemento 2
identifier_1: Mapped[Optional[str]] = mapped_column(
String(2)
) # IDENTIFICADOR / Identificador 1
complement_1: Mapped[Optional[str]] = mapped_column(
String(30)
) # COMPLEMENTO1 / Complemento 1
identifier_2: Mapped[Optional[str]] = mapped_column(
String(2)
) # IDENTIFICADOR2 / Identificador 2
complement_2: Mapped[Optional[str]] = mapped_column(
String(30)
) # COMPLEMENTO2 / Complemento 2
# Weight & Container Info
weight_type: Mapped[WeightUnit] = mapped_column(String(3)) # TIPOPESO / Tipo de peso
container_types: Mapped[Optional[str]] = mapped_column(String(500)) # CONTENEDORESTIPO / Tipos de contenedores
vehicle_data: Mapped[Optional[str]] = mapped_column(String(500)) # DATOSVEHICULO / Datos del vehículo
weight_type: Mapped[WeightUnit] = mapped_column(
String(3)
) # TIPOPESO / Tipo de peso
container_types: Mapped[Optional[str]] = mapped_column(
String(500)
) # CONTENEDORESTIPO / Tipos de contenedores
vehicle_data: Mapped[Optional[str]] = mapped_column(
String(500)
) # DATOSVEHICULO / Datos del vehículo
# Locations & Routes
origin_location: Mapped[Optional[str]] = mapped_column(String(200)) # ORIGENUBICACION / Ubicación de origen
destination_location: Mapped[Optional[str]] = mapped_column(String(200)) # DESTINOUBICACION / Ubicación de destino
transport_itinerary: Mapped[Optional[str]] = mapped_column(String(1000)) # ITINERARIOTRANPORTE / Itinerario del transporte
destination_goods: Mapped[Optional[str]] = mapped_column(String(50)) # DESTINOMCIA / Destino de mercancía
origin_location: Mapped[Optional[str]] = mapped_column(
String(200)
) # ORIGENUBICACION / Ubicación de origen
destination_location: Mapped[Optional[str]] = mapped_column(
String(200)
) # DESTINOUBICACION / Ubicación de destino
transport_itinerary: Mapped[Optional[str]] = mapped_column(
String(1000)
) # ITINERARIOTRANPORTE / Itinerario del transporte
destination_goods: Mapped[Optional[str]] = mapped_column(
String(50)
) # DESTINOMCIA / Destino de mercancía
# Logistics Dates
entry_exit_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAENTRADA/FECHAENVIO/FECHARECIBO / Fecha entrada/salida
delivery_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAENTREGA / Fecha de entrega
entry_exit_date: Mapped[Optional[datetime]] = mapped_column(
Date
) # FECHAENTRADA/FECHAENVIO/FECHARECIBO / Fecha entrada/salida
delivery_date: Mapped[Optional[datetime]] = mapped_column(
Date
) # FECHAENTREGA / Fecha de entrega
# Delivery Control
delivered_status: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # ENTREGADO / Estado de entrega
received_by: Mapped[Optional[str]] = mapped_column(String(50)) # RECIBIDOPOR / Recibido por
delivered_status: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # ENTREGADO / Estado de entrega
received_by: Mapped[Optional[str]] = mapped_column(
String(50)
) # RECIBIDOPOR / Recibido por
# Payment Info
payment_date: Mapped[Optional[datetime]] = mapped_column(Date) # FECHAPAGO / Fecha de pago
payment_receipt_num: Mapped[Optional[str]] = mapped_column(String(20)) # NUMRECIBOPAGO / Número de recibo de pago
payment_date: Mapped[Optional[datetime]] = mapped_column(
Date
) # FECHAPAGO / Fecha de pago
payment_receipt_num: Mapped[Optional[str]] = mapped_column(
String(20)
) # NUMRECIBOPAGO / Número de recibo de pago
# CTM Process
is_ctm_process: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # SETRATAPROCESOCTM / Se trata de proceso CTM
is_ctm_process: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # SETRATAPROCESOCTM / Se trata de proceso CTM
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="logistics")
@@ -358,12 +681,20 @@ class InvoiceSalesDetails(Base, TenantScopedMixin, TimestampMixin):
invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id"))
line_number: Mapped[int] = mapped_column(Integer) # LINEA / Número de línea
sales_order: Mapped[Optional[str]] = mapped_column(String(20)) # ORDENVENTA / Orden de venta
sales_order: Mapped[Optional[str]] = mapped_column(
String(20)
) # ORDENVENTA / Orden de venta
# Specific Custom Fields
colors_description: Mapped[Optional[str]] = mapped_column(String(49)) # COLORES / Descripción de colores
square_color_code: Mapped[Optional[str]] = mapped_column(String(1)) # COLORCUADRITO / Código de color cuadrito
line_bundles: Mapped[Optional[int]] = mapped_column(Integer) # CANTBULTOS / Cantidad de bultos de la línea
colors_description: Mapped[Optional[str]] = mapped_column(
String(49)
) # COLORES / Descripción de colores
square_color_code: Mapped[Optional[str]] = mapped_column(
String(1)
) # COLORCUADRITO / Código de color cuadrito
line_bundles: Mapped[Optional[int]] = mapped_column(
Integer
) # CANTBULTOS / Cantidad de bultos de la línea
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="details")
@@ -378,9 +709,11 @@ class InvoiceCollections(Base, TenantScopedMixin, TimestampMixin):
invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id"))
line_number: Mapped[int] = mapped_column(Integer) # LINEA / Número de línea
invoice_number: Mapped[Optional[str]] = mapped_column(String(15)) # FACTURA / Número de factura
invoice_number: Mapped[Optional[str]] = mapped_column(
String(15)
) # FACTURA / Número de factura
concept: Mapped[Optional[str]] = mapped_column(String(100)) # CONCEPTO / Concepto
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="collections")
concept: Mapped[Optional[str]] = mapped_column(String(100)) # CONCEPTO / Conce
concept: Mapped[Optional[str]] = mapped_column(String(100)) # CONCEPTO / Conce

View File

@@ -2,253 +2,267 @@ from typing import Literal, Optional, List
from datetime import datetime, date
from decimal import Decimal
from pydantic import BaseModel, Field
from .models import DestinationOriginCove, OperationType, Currency, TransportType, WeightUnit
from .models import (
DestinationOriginCove,
OperationType,
Currency,
TransportType,
WeightUnit,
)
# --- Base Schemas ---
class InvoiceHeaderBase(BaseModel):
"""Base fields for Invoice Header"""
system: Optional[str] = Field(
None, max_length=12, description="System of origin")
system: Optional[str] = Field(None, max_length=12, description="System of origin")
operation_type: Optional[OperationType] = Field(
..., description="Operation type: imp/exp/sm/ctm")
..., description="Operation type: imp/exp/sm/ctm"
)
invoice_type: Optional[str] = Field(
None, max_length=5, description="Invoice type key")
None, max_length=5, description="Invoice type key"
)
document_type: str = Field(
..., max_length=3, description="Document type (Regimen Aduanero)")
..., max_length=3, description="Document type (Regimen Aduanero)"
)
invoice_number: Optional[str] = Field(
None, max_length=100, description="Invoice number")
None, max_length=100, description="Invoice number"
)
project_number: Optional[str] = Field(
None, max_length=14, description="Project number")
None, max_length=14, description="Project number"
)
purchase_order: Optional[str] = Field(
None, max_length=50, description="Purchase order")
None, max_length=50, description="Purchase order"
)
related_doc_id: Optional[int] = Field(
None, description="Related document ID for rectifications")
None, description="Related document ID for rectifications"
)
alternate_invoice: Optional[str] = Field(
None, max_length=99, description="Alternate invoice")
None, max_length=99, description="Alternate invoice"
)
invoice_ref: Optional[str] = Field(
None, max_length=19, description="Invoice reference")
None, max_length=19, description="Invoice reference"
)
proforma_number: Optional[str] = Field(
None, max_length=20, description="Proforma number")
None, max_length=20, description="Proforma number"
)
invoice_date: date = Field(..., description="Invoice date")
emission_date: Optional[date] = Field(None, description="Emission date")
is_updated: bool = Field(False, description="Status")
updated_date: Optional[datetime] = Field(None, description="Update date")
who_updated: Optional[str] = Field(
None, max_length=20, description="Who updated")
capture_user: Optional[str] = Field(
None, max_length=20, description="Capture user")
who_updated: Optional[str] = Field(None, max_length=20, description="Who updated")
capture_user: Optional[str] = Field(None, max_length=20, description="Capture user")
traffic_light_status: Optional[str] = Field(
None, max_length=50, description="Traffic light status")
None, max_length=50, description="Traffic light status"
)
process_log: Optional[str] = Field(
None, max_length=300, description="Processing log")
None, max_length=300, description="Processing log"
)
is_updated_rec: Optional[int] = Field(None, description="Reception status")
is_updated_rep: Optional[str] = Field(
None, max_length=2, description="Report status")
observation_es: Optional[str] = Field(
None, description="Observations in Spanish")
observation_en: Optional[str] = Field(
None, description="Observations in English")
comments_status: Optional[str] = Field(
None, description="Comments status")
None, max_length=2, description="Report status"
)
observation_es: Optional[str] = Field(None, description="Observations in Spanish")
observation_en: Optional[str] = Field(None, description="Observations in English")
comments_status: Optional[str] = Field(None, description="Comments status")
vu_observations: Optional[str] = Field(
None, max_length=500, description="VUCEM observations")
cfdi_uuid: Optional[str] = Field(
None, max_length=100, description="CFDI UUID")
None, max_length=500, description="VUCEM observations"
)
cfdi_uuid: Optional[str] = Field(None, max_length=100, description="CFDI UUID")
path_pdf: Optional[str] = Field(
None, max_length=500, description="Path to PDF file")
None, max_length=500, description="Path to PDF file"
)
path_xml: Optional[str] = Field(
None, max_length=500, description="Path to XML file")
subcompany: Optional[str] = Field(
None, max_length=5, description="Subcompany")
None, max_length=500, description="Path to XML file"
)
subcompany: Optional[str] = Field(None, max_length=5, description="Subcompany")
party_count: Optional[int] = Field(None, description="Quantity of parties")
generate_id: Optional[bool] = Field(False, description="Generate ID")
generate_desc_parties: Optional[str] = Field(
None, max_length=12, description="Generate description of parties")
apply_manual_discount: Optional[bool] = Field(False, description="Apply manual discount")
None, max_length=12, description="Generate description of parties"
)
apply_manual_discount: Optional[bool] = Field(
False, description="Apply manual discount"
)
is_bulk: Optional[bool] = Field(None, description="Is bulk")
download_substance: Optional[bool] = Field(
None, description="Download substance")
download_class: Optional[bool] = Field(
None, description="Download class")
download_def: Optional[bool] = Field(
None, description="Definitive download")
download_substance: Optional[bool] = Field(None, description="Download substance")
download_class: Optional[bool] = Field(None, description="Download class")
download_def: Optional[bool] = Field(None, description="Definitive download")
payment_terms: Optional[str] = Field(
None, max_length=200, description="Payment terms")
None, max_length=200, description="Payment terms"
)
handling_fees: Optional[Decimal] = Field(None, description="Handling fees")
option_iv18: Optional[str] = Field(
None, max_length=50, description="Option IV18")
enajenation_goods: Optional[bool] = Field(
None, description="Enajenation of goods")
option_iv18: Optional[str] = Field(None, max_length=50, description="Option IV18")
enajenation_goods: Optional[bool] = Field(None, description="Enajenation of goods")
class InvoiceComplianceMxBase(BaseModel):
"""Base fields for Compliance MX"""
pedimento_id: Optional[int] = Field(
None, description="Pedimento id")
pedimento_r1: Optional[int] = Field(
None, description="Pedimento id (R1)")
pedimento_k1: Optional[int] = Field(
None, description="Pedimento id (K1)")
pedimento_id: Optional[int] = Field(None, description="Pedimento id")
pedimento_r1: Optional[int] = Field(None, description="Pedimento id (R1)")
pedimento_k1: Optional[int] = Field(None, description="Pedimento id (K1)")
remesa: Optional[int] = Field(None, description="Remesa")
aduana: Optional[str] = Field(None, max_length=5, description="Customs office")
port_of_entry: Optional[str] = Field(
None, max_length=6, description="Port of entry")
None, max_length=6, description="Port of entry"
)
destination: Optional[str] = Field(
None, max_length=3, description="Destination code")
None, max_length=3, description="Destination code"
)
manifest_number: Optional[str] = Field(
None, max_length=15, description="Manifest number")
provider_header: str = Field(
None, max_length=20, description="Provider header")
provider_id: int = Field(
None, description="Provider ID")
sold_to_header: str = Field(
None, max_length=20, description="Sold to header")
sold_to_id: int = Field(
None, description="Sold to ID")
shipped_to_header: str = Field(
None, max_length=20, description="Shipped to header")
shipped_to_id:int = Field(
None, description="Shipped to ID")
None, max_length=15, description="Manifest number"
)
provider_header: str = Field(None, max_length=20, description="Provider header")
provider_id: int = Field(None, description="Provider ID")
sold_to_header: str = Field(None, max_length=20, description="Sold to header")
sold_to_id: int = Field(None, description="Sold to ID")
shipped_to_header: str = Field(None, max_length=20, description="Shipped to header")
shipped_to_id: int = Field(None, description="Shipped to ID")
shipped_by_header: Optional[int] = Field(
None, max_length=20, description="Shipped by header")
shipped_by_id: Optional[int] = Field(
None, description="Shipped by ID")
customs_broker_id: int = Field(
None, description="Customs broker ID")
None, max_length=20, description="Shipped by header"
)
shipped_by_id: Optional[int] = Field(None, description="Shipped by ID")
customs_broker_id: int = Field(None, description="Customs broker ID")
customs_broker_us_id: Optional[int] = Field(
None, description="US customs broker ID")
None, description="US customs broker ID"
)
broker_invoice_num: Optional[str] = Field(
None, max_length=20, description="Broker invoice number")
broker_invoice_date: Optional[date] = Field(
None, description="Broker invoice date")
is_mixed: Optional[bool] = Field(
False, description="Is mixed operation")
waste_type: Optional[str] = Field(
None, max_length=1, description="Waste type")
scrap_type: Optional[str] = Field(
None, max_length=1, description="Scrap type")
None, max_length=20, description="Broker invoice number"
)
broker_invoice_date: Optional[date] = Field(None, description="Broker invoice date")
is_mixed: Optional[bool] = Field(False, description="Is mixed operation")
waste_type: Optional[str] = Field(None, max_length=1, description="Waste type")
scrap_type: Optional[str] = Field(None, max_length=1, description="Scrap type")
appendix_17: Optional[int] = Field(None, description="Appendix 17")
is_regime_change: Optional[bool] = Field(
False, description="Is regime change")
is_regime_change: Optional[bool] = Field(False, description="Is regime change")
which_exchange_rate: Optional[str] = Field(
None, max_length=5, description="Which exchange rate")
value_method: Optional[str] = Field(
None, max_length=2, description="Value method")
act_value: Optional[str] = Field(
None, max_length=5, description="Act value")
None, max_length=5, description="Which exchange rate"
)
value_method: Optional[str] = Field(None, max_length=2, description="Value method")
act_value: Optional[str] = Field(None, max_length=5, description="Act value")
is_pedimento_pending: bool = Field(..., description="Is pedimento pending")
is_owner_of_goods: Optional[bool] = Field(
False, description="Is owner of goods")
generate_balances: Optional[bool] = Field(
False, description="Generate balances")
is_owner_of_goods: Optional[bool] = Field(False, description="Is owner of goods")
generate_balances: Optional[bool] = Field(False, description="Generate balances")
was_reviewed_by_company: Optional[bool] = Field(
None, description="Was reviewed by company")
edocument: Optional[str] = Field(
None, max_length=50, description="E-document")
None, description="Was reviewed by company"
)
edocument: Optional[str] = Field(None, max_length=50, description="E-document")
electronic_signature: Optional[str] = Field(
None, max_length=999, description="Electronic signature")
None, max_length=999, description="Electronic signature"
)
certificate_number: Optional[str] = Field(
None, max_length=99, description="Certificate number")
niu_number: Optional[str] = Field(
None, max_length=19, description="NIU number")
None, max_length=99, description="Certificate number"
)
niu_number: Optional[str] = Field(None, max_length=19, description="NIU number")
bill_of_lading_count: Optional[str] = Field(
None, max_length=12, description="Bill of lading count")
None, max_length=12, description="Bill of lading count"
)
addendum_vu: Optional[str] = Field(
None, max_length=204, description="VUCEM addendum")
origin_destination_cove: Optional[DestinationOriginCove] = Field('franja_front_norte', max_length=20, description="Origin/Destination COVE")
None, max_length=204, description="VUCEM addendum"
)
origin_destination_cove: Optional[DestinationOriginCove] = Field(
"franja_front_norte", max_length=20, description="Origin/Destination COVE"
)
vucem_operation_num: Optional[str] = Field(
None, max_length=19, description="VUCEM operation number")
customs_person_line: Optional[int] = Field(
None, description="Customs person line")
contingency_mode: Optional[bool] = Field(
None, description="Contingency mode")
enclosure: Optional[str] = Field(
None, max_length=4, description="Enclosure")
None, max_length=19, description="VUCEM operation number"
)
customs_person_line: Optional[int] = Field(None, description="Customs person line")
contingency_mode: Optional[bool] = Field(None, description="Contingency mode")
enclosure: Optional[str] = Field(None, max_length=4, description="Enclosure")
guide_type_to_identify: Optional[str] = Field(
None, max_length=1, description="Guide type to identify")
location: Optional[str] = Field(
None, max_length=200, description="Location")
dot_code: Optional[str] = Field(
None, max_length=20, description="DOT code")
subdivision: Optional[str] = Field(
None, max_length=20, description="Subdivision")
acts_as: Optional[str] = Field(
None, max_length=20, description="Acts as")
None, max_length=1, description="Guide type to identify"
)
location: Optional[str] = Field(None, max_length=200, description="Location")
dot_code: Optional[str] = Field(None, max_length=20, description="DOT code")
subdivision: Optional[str] = Field(None, max_length=20, description="Subdivision")
acts_as: Optional[str] = Field(None, max_length=20, description="Acts as")
movement_type: Optional[str] = Field(
None, max_length=31, description="Movement type")
None, max_length=31, description="Movement type"
)
office_document: Optional[str] = Field(
None, max_length=30, description="Office document")
None, max_length=30, description="Office document"
)
reason_export: Optional[str] = Field(
None, max_length=1, description="Reason for export")
None, max_length=1, description="Reason for export"
)
signature_key: Optional[str] = Field(
None, max_length=10, description="Signature key")
None, max_length=10, description="Signature key"
)
sem_id: Optional[int] = Field(None, description="SEM ID")
class InvoiceFinancialsBase(BaseModel):
"""Base fields for Financials"""
currency: Currency = Field(
None, max_length=7, description="Currency code")
currency_type: Optional[str] = Field(
"USD", description="Currency type")
currency: Currency = Field(None, max_length=7, description="Currency code")
currency_type: Optional[str] = Field("USD", description="Currency type")
exchange_rate: Decimal = Field(0.00, description="Exchange rate")
exchange_rate_mm: Optional[Decimal] = Field(
None, description="Exchange rate currency to currency")
None, description="Exchange rate currency to currency"
)
value_mn: Optional[Decimal] = Field(None, description="Value in MXN")
value_me: Optional[Decimal] = Field(
None, description="Value in foreign currency")
value_mc: Optional[Decimal] = Field(
None, description="Value in third currency")
value_me: Optional[Decimal] = Field(None, description="Value in foreign currency")
value_mc: Optional[Decimal] = Field(None, description="Value in third currency")
customs_value_mn: Optional[Decimal] = Field(
None, description="Customs value in MXN")
None, description="Customs value in MXN"
)
customs_value_me: Optional[Decimal] = Field(
None, description="Customs value in foreign currency")
None, description="Customs value in foreign currency"
)
raw_material_value_mn: Optional[Decimal] = Field(
None, description="Raw material value in MXN")
None, description="Raw material value in MXN"
)
raw_material_value_me: Optional[Decimal] = Field(
None, description="Raw material value in foreign currency")
None, description="Raw material value in foreign currency"
)
aggregate_value_mn: Optional[Decimal] = Field(
None, description="Aggregate value in MXN")
None, description="Aggregate value in MXN"
)
aggregate_value_me: Optional[Decimal] = Field(
None, description="Aggregate value in foreign currency")
None, description="Aggregate value in foreign currency"
)
aggregate_value_mc: Optional[Decimal] = Field(
None, description="Aggregate value in third currency")
None, description="Aggregate value in third currency"
)
mexican_value_mn: Optional[Decimal] = Field(
None, description="Mexican merchandise value in MXN")
None, description="Mexican merchandise value in MXN"
)
mexican_value_me: Optional[Decimal] = Field(
None, description="Mexican merchandise value in foreign currency")
None, description="Mexican merchandise value in foreign currency"
)
mexican_value_mc: Optional[Decimal] = Field(
None, description="Mexican merchandise value in third currency")
None, description="Mexican merchandise value in third currency"
)
national_packaging_mn: Optional[Decimal] = Field(
None, description="National packaging in MXN")
None, description="National packaging in MXN"
)
national_packaging_me: Optional[Decimal] = Field(
None, description="National packaging in foreign currency")
None, description="National packaging in foreign currency"
)
national_packaging_mc: Optional[Decimal] = Field(
None, description="National packaging in third currency")
None, description="National packaging in third currency"
)
freight: Optional[Decimal] = Field(None, description="Freight cost")
insurance: Optional[Decimal] = Field(None, description="Insurance cost")
insurance_value: Optional[Decimal] = Field(
None, description="Insurance value")
insurance_value: Optional[Decimal] = Field(None, description="Insurance value")
packaging: Optional[Decimal] = Field(None, description="Packaging")
other_increments: Optional[Decimal] = Field(
None, description="Other increments")
other_increments: Optional[Decimal] = Field(None, description="Other increments")
total_increments_mn: Optional[Decimal] = Field(
None, description="Total increments in MXN")
None, description="Total increments in MXN"
)
total_increments_me: Optional[Decimal] = Field(
None, description="Total increments in foreign currency")
None, description="Total increments in foreign currency"
)
iva_mn: Optional[Decimal] = Field(None, description="IVA in MXN")
iva_me: Optional[Decimal] = Field(
None, description="IVA in foreign currency")
iva_mc: Optional[Decimal] = Field(
None, description="IVA in third currency")
iva_me: Optional[Decimal] = Field(None, description="IVA in foreign currency")
iva_mc: Optional[Decimal] = Field(None, description="IVA in third currency")
iva_factor: Optional[Decimal] = Field(None, description="IVA factor")
tax_value_me: Optional[Decimal] = Field(
None, description="Tax value in foreign currency")
seal_value_2500: Optional[bool] = Field(
None, description="Seal value 2500")
total_quantity: Optional[Decimal] = Field(
None, description="Total quantity")
None, description="Tax value in foreign currency"
)
seal_value_2500: Optional[bool] = Field(None, description="Seal value 2500")
total_quantity: Optional[Decimal] = Field(None, description="Total quantity")
gross_weight: Optional[Decimal] = Field(None, description="Gross weight")
net_weight: Optional[Decimal] = Field(None, description="Net weight")
bundle_count: Optional[int] = Field(None, description="Bundle count")
@@ -257,131 +271,142 @@ class InvoiceFinancialsBase(BaseModel):
class InvoiceLogisticsBase(BaseModel):
"""Base fields for Logistics"""
carrier_id: Optional[str] = Field(
None, max_length=10, description="Carrier ID")
transport_id: Optional[str] = Field(
None, max_length=10, description="Transport ID")
carrier_id: Optional[str] = Field(None, max_length=10, description="Carrier ID")
transport_id: Optional[str] = Field(None, max_length=10, description="Transport ID")
transport_us_id: Optional[str] = Field(
None, max_length=10, description="US transport ID")
None, max_length=10, description="US transport ID"
)
transport_type: TransportType = Field(
'none', max_length=15, description="Transport type")
"none", max_length=15, description="Transport type"
)
transport_num: Optional[str] = Field(
None, max_length=20, description="Transport number")
None, max_length=20, description="Transport number"
)
transport_mode: Optional[str] = Field(
30, max_length=15, description="Transport mode")
driver_name: Optional[str] = Field(
None, max_length=80, description="Driver name")
is_rail: Optional[bool] = Field(
False, description="Is rail transport")
rail_id: Optional[str] = Field(
None, max_length=31, description="Rail ID")
30, max_length=15, description="Transport mode"
)
driver_name: Optional[str] = Field(None, max_length=80, description="Driver name")
is_rail: Optional[bool] = Field(False, description="Is rail transport")
rail_id: Optional[str] = Field(None, max_length=31, description="Rail ID")
vehicle_num: Optional[str] = Field(
None, max_length=20, description="Vehicle number")
None, max_length=20, description="Vehicle number"
)
license_plate: Optional[str] = Field(
None, max_length=20, description="License plate")
None, max_length=20, description="License plate"
)
license_plate_complete: Optional[str] = Field(
None, max_length=40, description="Complete license plate")
None, max_length=40, description="Complete license plate"
)
trailer_num: Optional[str] = Field(
None, max_length=20, description="Trailer number")
seal_number: Optional[str] = Field(
None, max_length=15, description="Seal number")
guide_number: Optional[str] = Field(
None, max_length=20, description="Guide number")
bill_number: Optional[str] = Field(
None, max_length=15, description="Bill number")
None, max_length=20, description="Trailer number"
)
seal_number: Optional[str] = Field(None, max_length=15, description="Seal number")
guide_number: Optional[str] = Field(None, max_length=20, description="Guide number")
bill_number: Optional[str] = Field(None, max_length=15, description="Bill number")
reference_number: Optional[str] = Field(
None, max_length=14, description="Reference number")
None, max_length=14, description="Reference number"
)
shipment_number: Optional[str] = Field(
None, max_length=19, description="Shipment number")
incoterm: Optional[str] = Field(
None, max_length=5, description="Incoterm")
identifier_1: Optional[str] = Field(
None, max_length=2, description="Identifier 1")
complement_1: Optional[str] = Field(
None, max_length=30, description="Complement 1")
identifier_2: Optional[str] = Field(
None, max_length=2, description="Identifier 2")
complement_2: Optional[str] = Field(
None, max_length=30, description="Complement 2")
None, max_length=19, description="Shipment number"
)
incoterm: Optional[str] = Field(None, max_length=5, description="Incoterm")
identifier_1: Optional[str] = Field(None, max_length=2, description="Identifier 1")
complement_1: Optional[str] = Field(None, max_length=30, description="Complement 1")
identifier_2: Optional[str] = Field(None, max_length=2, description="Identifier 2")
complement_2: Optional[str] = Field(None, max_length=30, description="Complement 2")
weight_type: WeightUnit = Field(
default="kgs", max_length=3, description="Weight type")
default="kgs", max_length=3, description="Weight type"
)
container_types: Optional[str] = Field(
None, max_length=500, description="Container types")
None, max_length=500, description="Container types"
)
vehicle_data: Optional[str] = Field(
None, max_length=500, description="Vehicle data")
None, max_length=500, description="Vehicle data"
)
origin_location: Optional[str] = Field(
None, max_length=200, description="Origin location")
None, max_length=200, description="Origin location"
)
destination_location: Optional[str] = Field(
None, max_length=200, description="Destination location")
None, max_length=200, description="Destination location"
)
transport_itinerary: Optional[str] = Field(
None, max_length=1000, description="Transport itinerary")
None, max_length=1000, description="Transport itinerary"
)
destination_goods: Optional[str] = Field(
None, max_length=50, description="Destination of goods")
entry_exit_date: Optional[date] = Field(
None, description="Entry/Exit date")
delivery_date: Optional[date] = Field(
None, description="Delivery date")
delivered_status: Optional[str] = Field(
None, max_length=2, description="Delivered status")
received_by: Optional[str] = Field(
None, max_length=50, description="Received by")
payment_date: Optional[date] = Field(
None, description="Payment date")
None, max_length=50, description="Destination of goods"
)
entry_exit_date: Optional[date] = Field(None, description="Entry/Exit date")
delivery_date: Optional[date] = Field(None, description="Delivery date")
delivered_status: Optional[bool] = Field(False, description="Delivered status")
received_by: Optional[str] = Field(None, max_length=50, description="Received by")
payment_date: Optional[date] = Field(None, description="Payment date")
payment_receipt_num: Optional[str] = Field(
None, max_length=20, description="Payment receipt number")
is_ctm_process: Optional[bool] = Field(
False, description="Is CTM process")
None, max_length=20, description="Payment receipt number"
)
is_ctm_process: Optional[bool] = Field(False, description="Is CTM process")
class InvoiceSalesDetailsBase(BaseModel):
"""Base fields for Sales Details"""
line_number: int = Field(..., description="Line number")
sales_order: Optional[str] = Field(
None, max_length=20, description="Sales order")
sales_order: Optional[str] = Field(None, max_length=20, description="Sales order")
colors_description: Optional[str] = Field(
None, max_length=49, description="Colors description")
None, max_length=49, description="Colors description"
)
square_color_code: Optional[str] = Field(
None, max_length=1, description="Square color code")
None, max_length=1, description="Square color code"
)
line_bundles: Optional[int] = Field(None, description="Line bundles count")
class InvoiceCollectionsBase(BaseModel):
"""Base fields for Collections"""
line_number: int = Field(..., description="Line number")
invoice_number: Optional[str] = Field(
None, max_length=15, description="Invoice number")
None, max_length=15, description="Invoice number"
)
concept: Optional[str] = Field(None, max_length=100, description="Concept")
# --- Create Schemas ---
class InvoiceComplianceMxCreate(InvoiceComplianceMxBase):
"""Schema for creating Compliance MX"""
pass
class InvoiceFinancialsCreate(InvoiceFinancialsBase):
"""Schema for creating Financials"""
pass
class InvoiceLogisticsCreate(InvoiceLogisticsBase):
"""Schema for creating Logistics"""
pass
class InvoiceSalesDetailsCreate(InvoiceSalesDetailsBase):
"""Schema for creating Sales Details"""
pass
class InvoiceCollectionsCreate(InvoiceCollectionsBase):
"""Schema for creating Collections"""
pass
class InvoiceHeaderCreate(InvoiceHeaderBase):
"""Schema for creating Invoice Header with nested relations"""
compliance_mx: Optional[InvoiceComplianceMxCreate] = None
financials: Optional[InvoiceFinancialsCreate] = None
logistics: Optional[InvoiceLogisticsCreate] = None
@@ -391,33 +416,40 @@ class InvoiceHeaderCreate(InvoiceHeaderBase):
# --- Update Schemas ---
class InvoiceComplianceMxUpdate(InvoiceComplianceMxBase):
"""Schema for updating Compliance MX"""
pass
class InvoiceFinancialsUpdate(InvoiceFinancialsBase):
"""Schema for updating Financials"""
pass
class InvoiceLogisticsUpdate(InvoiceLogisticsBase):
"""Schema for updating Logistics"""
pass
class InvoiceSalesDetailsUpdate(InvoiceSalesDetailsBase):
"""Schema for updating Sales Details"""
line_number: Optional[int] = None
class InvoiceCollectionsUpdate(InvoiceCollectionsBase):
"""Schema for updating Collections"""
line_number: Optional[int] = None
class InvoiceHeaderUpdate(InvoiceHeaderBase):
"""Schema for updating Invoice Header with nested relations"""
id: int
compliance_mx: Optional[InvoiceComplianceMxUpdate] = None
financials: Optional[InvoiceFinancialsUpdate] = None
@@ -428,8 +460,10 @@ class InvoiceHeaderUpdate(InvoiceHeaderBase):
# --- Response Schemas ---
class InvoiceComplianceMxResponse(InvoiceComplianceMxBase):
"""Schema for Compliance MX response"""
invoice_id: int
class Config:
@@ -438,6 +472,7 @@ class InvoiceComplianceMxResponse(InvoiceComplianceMxBase):
class InvoiceFinancialsResponse(InvoiceFinancialsBase):
"""Schema for Financials response"""
id: int
invoice_id: int
@@ -447,6 +482,7 @@ class InvoiceFinancialsResponse(InvoiceFinancialsBase):
class InvoiceLogisticsResponse(InvoiceLogisticsBase):
"""Schema for Logistics response"""
id: int
invoice_id: int
@@ -456,6 +492,7 @@ class InvoiceLogisticsResponse(InvoiceLogisticsBase):
class InvoiceSalesDetailsResponse(InvoiceSalesDetailsBase):
"""Schema for Sales Details response"""
id: int
invoice_id: int
@@ -465,6 +502,7 @@ class InvoiceSalesDetailsResponse(InvoiceSalesDetailsBase):
class InvoiceCollectionsResponse(InvoiceCollectionsBase):
"""Schema for Collections response"""
id: int
invoice_id: int
@@ -474,13 +512,14 @@ class InvoiceCollectionsResponse(InvoiceCollectionsBase):
class InvoiceHeaderResponse(InvoiceHeaderBase):
"""Schema for Invoice Header response with nested relations"""
id: int
capture_date: datetime
compliance_mx: Optional[InvoiceComplianceMxResponse] = None
financials: Optional[InvoiceFinancialsResponse] = None
logistics: Optional[InvoiceLogisticsResponse] = []
details: Optional[InvoiceSalesDetailsResponse] = []
collections: Optional[InvoiceCollectionsResponse] = []
logistics: Optional[InvoiceLogisticsResponse] = None
details: Optional[List[InvoiceSalesDetailsResponse]] = []
collections: Optional[List[InvoiceCollectionsResponse]] = []
class Config:
from_attributes = True

View File

@@ -90,19 +90,20 @@ class InvoiceService:
errors = ErrorCollector()
# Validar si la factura ya existe
invoice_exists(db, invoice_data.invoice_number, tenant_id, company_id, errors)
validate_create(db, invoice_data, tenant_id, company_id, errors)
invoice_exists(db, invoice_data.invoice_number, tenant_id, company_id, errors)
validate_create(db, invoice_data, tenant_id, company_id, errors)
# Si hay errores, lanzar excepción
# Si hay errores, lanzar excepción ANTES de intentar crear
errors.raise_if_errors("Error al crear la factura")
# Extract nested data
compliance_data = invoice_data.compliance_mx
financials_data = invoice_data.financials
logistics_data = invoice_data.logistics
details_data = invoice_data.details or []
collections_data = invoice_data.collections or []
try:
# Extract nested data
compliance_data = invoice_data.compliance_mx
financials_data = invoice_data.financials
logistics_data = invoice_data.logistics or []
details_data = invoice_data.details or []
collections_data = invoice_data.collections or []
# Create main invoice header
raw_invoice_dict = invoice_data.model_dump(
@@ -148,8 +149,8 @@ class InvoiceService:
db.add(new_financials)
# Create logistics entries
for logistics_item in logistics_data:
raw_log_dict = logistics_item.model_dump()
if logistics_data:
raw_log_dict = logistics_data.model_dump()
logistics_dict = clean_dict(raw_log_dict)
logistics_dict["invoice_id"] = new_invoice.id