Merge branch 'feature/invoices' into development
This commit is contained in:
@@ -1,6 +1,13 @@
|
||||
from typing import Optional
|
||||
from decimal import Decimal
|
||||
from sqlalchemy import ForeignKey, Integer, String, ForeignKeyConstraint, UniqueConstraint, Numeric
|
||||
from sqlalchemy import (
|
||||
ForeignKey,
|
||||
Integer,
|
||||
String,
|
||||
ForeignKeyConstraint,
|
||||
UniqueConstraint,
|
||||
Numeric,
|
||||
)
|
||||
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
||||
from api.v1.common.base_models import TenantScopedMixin, TimestampMixin
|
||||
from core.database import Base
|
||||
@@ -11,15 +18,14 @@ from core.database import Base
|
||||
class UnitOfMeasureACE(Base, TimestampMixin):
|
||||
__tablename__ = "unit_of_measure_ace"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("code", name="uq_uom_ace_code"),
|
||||
{"schema": "a76", "extend_existing": True}
|
||||
UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_ace_code"),
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(4), nullable=False) # CLAVEACE
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(49), nullable=True)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(49), nullable=True)
|
||||
|
||||
|
||||
# 2. GUMOMA
|
||||
|
||||
@@ -27,15 +33,14 @@ class UnitOfMeasureACE(Base, TimestampMixin):
|
||||
class UnitOfMeasureOMA(Base, TimestampMixin):
|
||||
__tablename__ = "unit_of_measure_oma"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("code", name="uq_uom_oma_code"),
|
||||
{"schema": "a76", "extend_existing": True}
|
||||
UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_oma_code"),
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(10), nullable=False) # CLAVEUM
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(200), nullable=True)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(200), nullable=True)
|
||||
|
||||
|
||||
# 3. GUMAme
|
||||
|
||||
@@ -43,15 +48,16 @@ class UnitOfMeasureOMA(Base, TimestampMixin):
|
||||
class UnitOfMeasureAmerican(Base, TimestampMixin):
|
||||
__tablename__ = "unit_of_measure_american"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("code", name="uq_uom_american_code"),
|
||||
{"schema": "a76", "extend_existing": True}
|
||||
UniqueConstraint(
|
||||
"code", "tenant_id", "company_id", name="uq_uom_american_code"
|
||||
),
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(3), nullable=False) # CLAVE
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(40), nullable=True)
|
||||
description: Mapped[Optional[str]] = mapped_column(String(40), nullable=True)
|
||||
|
||||
|
||||
# 4. GUMAduana
|
||||
|
||||
@@ -59,17 +65,17 @@ class UnitOfMeasureAmerican(Base, TimestampMixin):
|
||||
class UnitOfMeasureCustoms(Base, TimestampMixin):
|
||||
__tablename__ = "unit_of_measure_customs"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("code", name="uq_uom_customs_code"),
|
||||
{"schema": "a76", "extend_existing": True}
|
||||
UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_customs_code"),
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(10), nullable=False) # CLAVE
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(50), nullable=True)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(2), nullable=False) # CLAVE
|
||||
description: Mapped[Optional[str]] = mapped_column(String(20), nullable=True)
|
||||
scaii_unit_code: Mapped[Optional[str]] = mapped_column(
|
||||
String(5), nullable=True) # UNIDADSCAII
|
||||
String(5), nullable=True
|
||||
) # UNIDADSCAII
|
||||
|
||||
|
||||
# 5. GUniMedida (Main)
|
||||
|
||||
@@ -77,57 +83,80 @@ class UnitOfMeasureCustoms(Base, TimestampMixin):
|
||||
class UnitOfMeasure(Base, TenantScopedMixin, TimestampMixin):
|
||||
__tablename__ = "units_of_measure"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("code", "tenant_id",
|
||||
"company_id", name="uq_uom_code"),
|
||||
UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_code"),
|
||||
ForeignKeyConstraint(
|
||||
["customs_code"],
|
||||
["a76.unit_of_measure_customs.code"],
|
||||
["customs_code", "tenant_id", "company_id"],
|
||||
[
|
||||
"a76.unit_of_measure_customs.code",
|
||||
"a76.unit_of_measure_customs.tenant_id",
|
||||
"a76.unit_of_measure_customs.company_id",
|
||||
],
|
||||
use_alter=True,
|
||||
name="fk_uom_customs"
|
||||
name="fk_uom_customs",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["american_code"],
|
||||
["a76.unit_of_measure_american.code"],
|
||||
["american_code", "tenant_id", "company_id"],
|
||||
[
|
||||
"a76.unit_of_measure_american.code",
|
||||
"a76.unit_of_measure_american.tenant_id",
|
||||
"a76.unit_of_measure_american.company_id",
|
||||
],
|
||||
use_alter=True,
|
||||
name="fk_uom_american"
|
||||
name="fk_uom_american",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["ace_code"],
|
||||
["a76.unit_of_measure_ace.code"],
|
||||
["ace_code", "tenant_id", "company_id"],
|
||||
[
|
||||
"a76.unit_of_measure_ace.code",
|
||||
"a76.unit_of_measure_ace.tenant_id",
|
||||
"a76.unit_of_measure_ace.company_id",
|
||||
],
|
||||
use_alter=True,
|
||||
name="fk_uom_ace"
|
||||
name="fk_uom_ace",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["oma_code"],
|
||||
["a76.unit_of_measure_oma.code"],
|
||||
["oma_code", "tenant_id", "company_id"],
|
||||
[
|
||||
"a76.unit_of_measure_oma.code",
|
||||
"a76.unit_of_measure_oma.tenant_id",
|
||||
"a76.unit_of_measure_oma.company_id",
|
||||
],
|
||||
use_alter=True,
|
||||
name="fk_uom_oma"
|
||||
name="fk_uom_oma",
|
||||
),
|
||||
{"schema": "a76", "extend_existing": True}
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(10), nullable=False) # CLAVEUNI
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(100), nullable=True)
|
||||
description_en: Mapped[Optional[str]] = mapped_column(
|
||||
String(100), nullable=True)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(5), nullable=False) # CLAVEUNI
|
||||
description: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
description_en: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
|
||||
customs_code: Mapped[Optional[str]] = mapped_column(
|
||||
String(10), nullable=True) # CLAVE_AMEX
|
||||
String(2), nullable=True
|
||||
) # CLAVE_AMEX
|
||||
american_code: Mapped[Optional[str]] = mapped_column(
|
||||
String(3), nullable=True) # CLAVE_AAMER
|
||||
String(3), nullable=True
|
||||
) # CLAVE_AAMER
|
||||
ace_code: Mapped[Optional[str]] = mapped_column(
|
||||
String(4), nullable=True) # CLAVEACE
|
||||
String(4), nullable=True
|
||||
) # CLAVEACE
|
||||
oma_code: Mapped[Optional[str]] = mapped_column(
|
||||
String(10), nullable=True) # CLAVEOMA
|
||||
String(10), nullable=True
|
||||
) # CLAVEOMA
|
||||
|
||||
# Relationships omitted for simplicity or need explicit primaryjoin
|
||||
customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship()
|
||||
american_unit: Mapped[Optional["UnitOfMeasureAmerican"]] = relationship()
|
||||
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship()
|
||||
oma_unit: Mapped[Optional["UnitOfMeasureOMA"]] = relationship()
|
||||
american_unit: Mapped[Optional["UnitOfMeasureAmerican"]] = relationship(
|
||||
overlaps="customs_unit"
|
||||
)
|
||||
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(
|
||||
overlaps="american_unit,customs_unit"
|
||||
)
|
||||
oma_unit: Mapped[Optional["UnitOfMeasureOMA"]] = relationship(
|
||||
overlaps="ace_unit,american_unit,customs_unit"
|
||||
)
|
||||
|
||||
|
||||
# 6. GUniMed (General/Conversion)
|
||||
|
||||
@@ -135,40 +164,48 @@ class UnitOfMeasure(Base, TenantScopedMixin, TimestampMixin):
|
||||
class UnitOfMeasureGeneral(Base, TenantScopedMixin, TimestampMixin):
|
||||
__tablename__ = "units_of_measure_general"
|
||||
__table_args__ = (
|
||||
UniqueConstraint("code", "tenant_id", "company_id",
|
||||
name="uq_uom_general_code"),
|
||||
UniqueConstraint("code", "tenant_id", "company_id", name="uq_uom_general_code"),
|
||||
ForeignKeyConstraint(
|
||||
["customs_code"],
|
||||
["a76.unit_of_measure_customs.code"],
|
||||
["customs_code", "tenant_id", "company_id"],
|
||||
[
|
||||
"a76.unit_of_measure_customs.code",
|
||||
"a76.unit_of_measure_customs.tenant_id",
|
||||
"a76.unit_of_measure_customs.company_id",
|
||||
],
|
||||
use_alter=True,
|
||||
name="fk_uom_general_customs"
|
||||
name="fk_uom_general_customs",
|
||||
),
|
||||
ForeignKeyConstraint(
|
||||
["ace_code"],
|
||||
["a76.unit_of_measure_ace.code"],
|
||||
["ace_code", "tenant_id", "company_id"],
|
||||
[
|
||||
"a76.unit_of_measure_ace.code",
|
||||
"a76.unit_of_measure_ace.tenant_id",
|
||||
"a76.unit_of_measure_ace.company_id",
|
||||
],
|
||||
use_alter=True,
|
||||
name="fk_uom_general_ace"
|
||||
name="fk_uom_general_ace",
|
||||
),
|
||||
{"schema": "a76", "extend_existing": True}
|
||||
{"schema": "a76", "extend_existing": True},
|
||||
)
|
||||
|
||||
id: Mapped[int] = mapped_column(
|
||||
Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(10), nullable=False) # UNIDAD
|
||||
description: Mapped[Optional[str]] = mapped_column(
|
||||
String(100), nullable=True)
|
||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||
code: Mapped[str] = mapped_column(String(5), nullable=False) # UNIDAD
|
||||
description: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
||||
conversion_factor: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(13, 6), nullable=True)
|
||||
mexico_unit: Mapped[Optional[str]] = mapped_column(
|
||||
String(10), nullable=True)
|
||||
Numeric(13, 6), nullable=True
|
||||
)
|
||||
mexico_unit: Mapped[Optional[str]] = mapped_column(String(5), nullable=True)
|
||||
# UNIDAD_AME (Note: GUniMed has UNIDAD_AME varchar(5), but GUMAme has CLAVE varchar(3). Keeping as string for now)
|
||||
american_unit_code: Mapped[Optional[str]
|
||||
] = mapped_column(String(5), nullable=True)
|
||||
american_unit_code: Mapped[Optional[str]] = mapped_column(String(5), nullable=True)
|
||||
|
||||
customs_code: Mapped[Optional[str]] = mapped_column(
|
||||
String(10), nullable=True) # CLAVE_ADUANA
|
||||
String(2), nullable=True
|
||||
) # CLAVE_ADUANA
|
||||
ace_code: Mapped[Optional[str]] = mapped_column(
|
||||
String(4), nullable=True) # CLAVEACE
|
||||
String(4), nullable=True
|
||||
) # CLAVEACE
|
||||
|
||||
customs_unit: Mapped[Optional["UnitOfMeasureCustoms"]] = relationship()
|
||||
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(overlaps="customs_unit")
|
||||
ace_unit: Mapped[Optional["UnitOfMeasureACE"]] = relationship(
|
||||
overlaps="customs_unit"
|
||||
)
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy.orm import Session
|
||||
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 ....models import InvoiceComplianceMx
|
||||
from api.v1.modules.a76.items.models import Item
|
||||
from api.v1.modules.public.reference_data.currency_types.models import CurrencyType
|
||||
from api.v1.modules.public.reference_data.customs_sections.models import CustomsSection
|
||||
@@ -38,7 +41,7 @@ def validate_common(
|
||||
)
|
||||
|
||||
if not invoice.compliance_mx.is_regime_change:
|
||||
if not pedimento.operation_type == 1:
|
||||
if not pedimento.operation_type == "imp":
|
||||
errors.add_error(
|
||||
field="compliance_mx.pedimento_id",
|
||||
message="El Pedimento seleccionado no corresponde a una Importación.",
|
||||
@@ -106,10 +109,24 @@ def validate_common(
|
||||
)
|
||||
|
||||
if pedimento.pedimento_type == "consolidated":
|
||||
if (
|
||||
invoice.invoice_date < pedimento.pedimento_dates.entry_date
|
||||
or invoice.invoice_date > pedimento.pedimento_dates.end_date
|
||||
):
|
||||
# Convertir invoice_date a date si es datetime para poder comparar
|
||||
invoice_date = (
|
||||
invoice.invoice_date.date()
|
||||
if hasattr(invoice.invoice_date, "date")
|
||||
else invoice.invoice_date
|
||||
)
|
||||
entry_date = (
|
||||
pedimento.pedimento_dates.entry_date.date()
|
||||
if hasattr(pedimento.pedimento_dates.entry_date, "date")
|
||||
else pedimento.pedimento_dates.entry_date
|
||||
)
|
||||
end_date = (
|
||||
pedimento.pedimento_dates.end_date.date()
|
||||
if hasattr(pedimento.pedimento_dates.end_date, "date")
|
||||
else pedimento.pedimento_dates.end_date
|
||||
)
|
||||
|
||||
if invoice_date < entry_date or invoice_date > end_date:
|
||||
errors.add_error(
|
||||
field="invoice_date",
|
||||
message=f"La Fecha de la Factura {invoice.invoice_date} no está dentro del rango de fechas del Pedimento {pedimento.customs_office}-{pedimento.license}-{pedimento.pedimento_number}.",
|
||||
@@ -138,23 +155,23 @@ def validate_common(
|
||||
)
|
||||
|
||||
duplicated_remesa = (
|
||||
db.query(Pedimentos)
|
||||
db.query(InvoiceComplianceMx)
|
||||
.filter(
|
||||
Pedimentos.remesa == invoice.compliance_mx.remesa,
|
||||
Pedimentos.id != invoice.compliance_mx.pedimento_id,
|
||||
Pedimentos.tenant_id == tenant_id,
|
||||
Pedimentos.company_id == company_id,
|
||||
InvoiceComplianceMx.remesa == invoice.compliance_mx.remesa,
|
||||
InvoiceComplianceMx.tenant_id == tenant_id,
|
||||
InvoiceComplianceMx.company_id == company_id,
|
||||
)
|
||||
.first()
|
||||
)
|
||||
if duplicated_remesa:
|
||||
errors.add_error(
|
||||
field="compliance_mx.remesa",
|
||||
message="El valor de Remesa ya está asociado a otro Pedimento.",
|
||||
solution=["Proporciona un valor único para Remesa"],
|
||||
code="DUPLICATE_VALUE",
|
||||
value=invoice.compliance_mx.remesa,
|
||||
)
|
||||
if duplicated_remesa and hasattr(invoice, "id"):
|
||||
if invoice.id != duplicated_remesa.invoice_id:
|
||||
errors.add_error(
|
||||
field="compliance_mx.remesa",
|
||||
message="El valor de Remesa ya está asociado a otro Pedimento.",
|
||||
solution=["Proporciona un valor único para Remesa"],
|
||||
code="DUPLICATE_VALUE",
|
||||
value=invoice.compliance_mx.remesa,
|
||||
)
|
||||
else:
|
||||
if not invoice.compliance_mx.is_pedimento_pending:
|
||||
errors.add_error(
|
||||
@@ -190,7 +207,7 @@ def validate_common(
|
||||
exchange_rate_exists = (
|
||||
db.query(ExchangeRate)
|
||||
.filter(
|
||||
ExchangeRate.date == invoice.invoice_date,
|
||||
func.date(ExchangeRate.date) == invoice.invoice_date,
|
||||
ExchangeRate.tenant_id == tenant_id,
|
||||
ExchangeRate.company_id == company_id,
|
||||
)
|
||||
@@ -204,6 +221,8 @@ def validate_common(
|
||||
code="EXCHANGE_RATE_NOT_FOUND",
|
||||
value=invoice.financials.exchange_rate,
|
||||
)
|
||||
else:
|
||||
invoice.financials.exchange_rate = exchange_rate_exists.value
|
||||
|
||||
if invoice.compliance_mx.is_regime_change:
|
||||
if invoice.document_type in ["EXD", "ETE", "ETR"]:
|
||||
@@ -281,11 +300,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()
|
||||
)
|
||||
@@ -387,7 +406,11 @@ def validate_common(
|
||||
value=invoice.financials.currency,
|
||||
)
|
||||
|
||||
if invoice.financials.currency == "manual":
|
||||
if invoice.financials.currency == "foreign":
|
||||
invoice.financials.currency_type = "USD"
|
||||
elif invoice.financials.currency == "local":
|
||||
invoice.financials.currency_type = "MXN"
|
||||
elif invoice.financials.currency == "manual":
|
||||
if not invoice.financials.currency_type:
|
||||
errors.add_error(
|
||||
field="financials.currency_type",
|
||||
|
||||
@@ -1,20 +1,36 @@
|
||||
from enum import Enum
|
||||
from typing import Optional, List
|
||||
from sqlalchemy import BigInteger, Boolean, Date, ForeignKey, Integer, Numeric, String, Text, TIMESTAMP
|
||||
from typing import Optional, List, TYPE_CHECKING
|
||||
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
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from api.v1.modules.a76.pedmientos.models.pedimentos import Pedimentos
|
||||
|
||||
|
||||
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 +40,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 +72,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 +147,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,81 +207,186 @@ 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
|
||||
|
||||
# SM specific
|
||||
sem_id: Mapped[Optional[int]] = mapped_column(Integer) # SEM / ID SEM (de SFacEntradaSM/SFacSalidaSM)
|
||||
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
|
||||
|
||||
# Relationship
|
||||
# SM specific
|
||||
sem_id: Mapped[Optional[int]] = mapped_column(
|
||||
Integer
|
||||
) # SEM / ID SEM (de SFacEntradaSM/SFacSalidaSM)
|
||||
|
||||
# Relationships
|
||||
header: Mapped["InvoiceHeader"] = relationship(back_populates="compliance_mx")
|
||||
pedimento: Mapped[Optional["Pedimentos"]] = relationship(
|
||||
foreign_keys=[pedimento_id], lazy="joined"
|
||||
)
|
||||
|
||||
|
||||
# --- 3. Financials (invoice_financials) ---
|
||||
@@ -219,62 +398,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 +543,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 +687,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 +715,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
|
||||
|
||||
@@ -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,9 +460,25 @@ class InvoiceHeaderUpdate(InvoiceHeaderBase):
|
||||
|
||||
# --- Response Schemas ---
|
||||
|
||||
|
||||
class PedimentoBasicInfo(BaseModel):
|
||||
"""Basic Pedimento information for Invoice response"""
|
||||
|
||||
pedimento_number: Optional[str] = None
|
||||
pedimento_code: Optional[str] = None
|
||||
customs_office: Optional[str] = None
|
||||
license: Optional[str] = None
|
||||
year: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
|
||||
|
||||
class InvoiceComplianceMxResponse(InvoiceComplianceMxBase):
|
||||
"""Schema for Compliance MX response"""
|
||||
|
||||
invoice_id: int
|
||||
pedimento: Optional[PedimentoBasicInfo] = None
|
||||
|
||||
class Config:
|
||||
from_attributes = True
|
||||
@@ -438,6 +486,7 @@ class InvoiceComplianceMxResponse(InvoiceComplianceMxBase):
|
||||
|
||||
class InvoiceFinancialsResponse(InvoiceFinancialsBase):
|
||||
"""Schema for Financials response"""
|
||||
|
||||
id: int
|
||||
invoice_id: int
|
||||
|
||||
@@ -447,6 +496,7 @@ class InvoiceFinancialsResponse(InvoiceFinancialsBase):
|
||||
|
||||
class InvoiceLogisticsResponse(InvoiceLogisticsBase):
|
||||
"""Schema for Logistics response"""
|
||||
|
||||
id: int
|
||||
invoice_id: int
|
||||
|
||||
@@ -456,6 +506,7 @@ class InvoiceLogisticsResponse(InvoiceLogisticsBase):
|
||||
|
||||
class InvoiceSalesDetailsResponse(InvoiceSalesDetailsBase):
|
||||
"""Schema for Sales Details response"""
|
||||
|
||||
id: int
|
||||
invoice_id: int
|
||||
|
||||
@@ -465,6 +516,7 @@ class InvoiceSalesDetailsResponse(InvoiceSalesDetailsBase):
|
||||
|
||||
class InvoiceCollectionsResponse(InvoiceCollectionsBase):
|
||||
"""Schema for Collections response"""
|
||||
|
||||
id: int
|
||||
invoice_id: int
|
||||
|
||||
@@ -474,13 +526,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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -12,7 +12,7 @@ class LineCustom(Base):
|
||||
Customs details for line items
|
||||
Consolidates all line-level data from Q and S tables
|
||||
"""
|
||||
__tablename__ = "line_customs"
|
||||
__tablename__ = "item_line_customs"
|
||||
__table_args__ = {
|
||||
"schema": "a76",
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class LineDescription(Base):
|
||||
Description details for line items
|
||||
Consolidates all line-level data from Q and S tables
|
||||
"""
|
||||
__tablename__ = "line_descriptions"
|
||||
__tablename__ = "item_line_descriptions"
|
||||
__table_args__ = {
|
||||
"schema": "a76",
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ class LineFinancial(Base):
|
||||
Financial details for line items
|
||||
Consolidates all line-level data from Q and S tables
|
||||
"""
|
||||
__tablename__ = "line_financials"
|
||||
__tablename__ = "item_line_financials"
|
||||
__table_args__ = {
|
||||
"schema": "a76",
|
||||
}
|
||||
|
||||
@@ -12,6 +12,11 @@ if TYPE_CHECKING:
|
||||
from ..line_customs.models import LineCustom
|
||||
from ..line_descriptions.models import LineDescription
|
||||
from ..line_references.models import LineReference
|
||||
from api.v1.modules.a76.classes.models import Class
|
||||
from api.v1.modules.a76.general_catalogs.units_of_measure.models import (
|
||||
UnitOfMeasure,
|
||||
)
|
||||
from api.v1.modules.a24.fa.fa_item_lines.models import FaLineItem
|
||||
|
||||
|
||||
class LineItem(Base, TenantScopedMixin, TimestampMixin):
|
||||
@@ -19,6 +24,7 @@ class LineItem(Base, TenantScopedMixin, TimestampMixin):
|
||||
Unified line items for all items
|
||||
Consolidates all line-level data from Q and S tables
|
||||
"""
|
||||
|
||||
__tablename__ = "item_lines"
|
||||
__table_args__ = {
|
||||
"schema": "a76",
|
||||
@@ -26,154 +32,152 @@ class LineItem(Base, TenantScopedMixin, TimestampMixin):
|
||||
|
||||
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
||||
item_id: Mapped[int] = mapped_column(ForeignKey("a76.items.id"))
|
||||
line_number: Mapped[int] = mapped_column(
|
||||
Integer) # LINEAIMPO/LINEAEXPO/LINEA
|
||||
line_number: Mapped[int] = mapped_column(Integer) # LINEAIMPO/LINEAEXPO/LINEA
|
||||
|
||||
# Part identification
|
||||
part_number: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.parts.id")) # NUMPARTE
|
||||
component_part_number: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.parts.id")) # NUMPARTECOM
|
||||
class_code: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.classes.id")) # CLASE
|
||||
part_number_id: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.parts.id")
|
||||
) # NUMPARTE
|
||||
component_part_number_id: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.parts.id")
|
||||
) # NUMPARTECOM
|
||||
class_id: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.classes.id")
|
||||
) # CLASE
|
||||
|
||||
# Unit of measure
|
||||
unit_of_measure: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.units_of_measure_general.id")) # UNIDADMEDIDA/UNIMED
|
||||
ForeignKey("a76.units_of_measure.id")
|
||||
) # UNIDADMEDIDA/UNIMED
|
||||
alternate_unit: Mapped[Optional[str]] = mapped_column(
|
||||
ForeignKey("a76.units_of_measure_general.id")) # UNIMEDALTERNA
|
||||
ForeignKey("a76.units_of_measure.id")
|
||||
) # UNIMEDALTERNA
|
||||
uma_key: Mapped[Optional[str]] = mapped_column(String(2)) # CLAVEUMA
|
||||
auxiliary_unit: Mapped[Optional[str]] = mapped_column(
|
||||
String(5)) # UNIMEDAUXILIAR
|
||||
auxiliary_unit: Mapped[Optional[str]] = mapped_column(String(5)) # UNIMEDAUXILIAR
|
||||
|
||||
# Permits and certificates
|
||||
permit_number: Mapped[Optional[str]] = mapped_column(
|
||||
String(20)) # NUMPERMISO
|
||||
permit_number: Mapped[Optional[str]] = mapped_column(String(20)) # NUMPERMISO
|
||||
page_line: Mapped[Optional[str]] = mapped_column(String(10)) # PAGRENGLON
|
||||
has_certificate: Mapped[Optional[bool]] = mapped_column(
|
||||
Boolean) # TIENECO/CERTORIGEN
|
||||
Boolean
|
||||
) # TIENECO/CERTORIGEN
|
||||
certificate_number: Mapped[Optional[str]] = mapped_column(
|
||||
String(10)) # NOCERTIFICADO
|
||||
octave_permit: Mapped[Optional[str]] = mapped_column(
|
||||
String(20)) # PERMISOROCTAVA
|
||||
permits_ped: Mapped[Optional[str]] = mapped_column(
|
||||
String(500)) # PERMISOSPED
|
||||
String(10)
|
||||
) # NOCERTIFICADO
|
||||
octave_permit: Mapped[Optional[str]] = mapped_column(String(20)) # PERMISOROCTAVA
|
||||
permits_ped: Mapped[Optional[str]] = mapped_column(String(500)) # PERMISOSPED
|
||||
|
||||
# FDA
|
||||
has_fda_code: Mapped[Optional[bool]] = mapped_column(
|
||||
Boolean) # LLEVACODFDA
|
||||
fda_key: Mapped[Optional[str]] = mapped_column(String(10)) # CLAVEFDA
|
||||
|
||||
# Subitem flags
|
||||
is_subitem: Mapped[Optional[bool]] = mapped_column(Boolean) # ESSUBPARTIDA
|
||||
contains_subitems: Mapped[Optional[bool]
|
||||
] = mapped_column(Boolean) # CONTIENESUBP
|
||||
includes_subitems: Mapped[Optional[bool]] = mapped_column(
|
||||
Boolean) # INCUYESUBPARTIDAS
|
||||
subitem_number: Mapped[Optional[bool]
|
||||
] = mapped_column(Boolean) # SUBPARTIDA
|
||||
has_fda_code: Mapped[Optional[bool]] = mapped_column(Boolean) # LLEVACODFDA
|
||||
fda_key: Mapped[Optional[str]] = mapped_column(String(10)) # CLAVEFDA
|
||||
|
||||
# Special flags
|
||||
is_military_mcia: Mapped[Optional[bool]
|
||||
] = mapped_column(Boolean) # ESMCIAMILITAR
|
||||
is_military_mcia: Mapped[Optional[bool]] = mapped_column(Boolean) # ESMCIAMILITAR
|
||||
|
||||
# IV32 (Tax identification)
|
||||
iv32_type_key: Mapped[Optional[str]] = mapped_column(
|
||||
String(5)) # CLAVETIPOIV32
|
||||
iv32_number: Mapped[Optional[str]] = mapped_column(
|
||||
String(35)) # NUMEROIV32
|
||||
iv32_type_key: Mapped[Optional[str]] = mapped_column(String(5)) # CLAVETIPOIV32
|
||||
iv32_number: Mapped[Optional[str]] = mapped_column(String(35)) # NUMEROIV32
|
||||
|
||||
# IN CASE OF EXPO
|
||||
scrap_invoice: Mapped[Optional[str]] = mapped_column(
|
||||
String(15)) # FACTURASCRAP
|
||||
consecutive_destination: Mapped[Optional[int]
|
||||
] = mapped_column(Integer) # CONSECUTIVODES
|
||||
ctm_section: Mapped[Optional[str]] = mapped_column(
|
||||
String(3)) # APARTADOCTM
|
||||
scrap_invoice: Mapped[Optional[str]] = mapped_column(String(15)) # FACTURASCRAP
|
||||
consecutive_destination: Mapped[Optional[int]] = mapped_column(
|
||||
Integer
|
||||
) # CONSECUTIVODES
|
||||
ctm_section: Mapped[Optional[str]] = mapped_column(String(3)) # APARTADOCTM
|
||||
|
||||
# Tax payment
|
||||
tax_payment: Mapped[Optional[bool]] = mapped_column(
|
||||
Boolean) # PAGOIMPUESTO
|
||||
tax_payment: Mapped[Optional[bool]] = mapped_column(Boolean) # PAGOIMPUESTO
|
||||
payment_method: Mapped[Optional[str]] = mapped_column(
|
||||
String(9)) # FORMAPAGO/FORMAPAGOTIGI
|
||||
igi_amount: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(23, 8)) # MONTOIGI
|
||||
String(9)
|
||||
) # FORMAPAGO/FORMAPAGOTIGI
|
||||
igi_amount: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # MONTOIGI
|
||||
igi_payment_method: Mapped[Optional[str]] = mapped_column(
|
||||
String(9)) # FORMAPAGOTIGI
|
||||
String(9)
|
||||
) # FORMAPAGOTIGI
|
||||
|
||||
# FCC
|
||||
fcc_key: Mapped[Optional[str]] = mapped_column(String(30)) # CLAVEFCC
|
||||
|
||||
# Valuation method
|
||||
valuation_method: Mapped[Optional[str]
|
||||
] = mapped_column(String(2)) # METVALOR
|
||||
valuation_method: Mapped[Optional[str]] = mapped_column(String(2)) # METVALOR
|
||||
valuation_determined_value: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(29, 8)) # METVALORVALORDETERMINADO/METVALORACIONVALORDETERMINADO
|
||||
Numeric(29, 8)
|
||||
) # METVALORVALORDETERMINADO/METVALORACIONVALORDETERMINADO
|
||||
valuation_reason: Mapped[Optional[str]] = mapped_column(
|
||||
String(500)) # METVALORMOTIVODEUSO/METVALORACIONMOTIVODEUSO
|
||||
String(500)
|
||||
) # METVALORMOTIVODEUSO/METVALORACIONMOTIVODEUSO
|
||||
|
||||
# Container rules
|
||||
container_rule: Mapped[Optional[str]] = mapped_column(
|
||||
String(50)) # CONTENEDORREGLA
|
||||
container_rule: Mapped[Optional[str]] = mapped_column(String(50)) # CONTENEDORREGLA
|
||||
container_parts_ii: Mapped[Optional[str]] = mapped_column(
|
||||
String(50)) # CONTENEDORPARTESII
|
||||
String(50)
|
||||
) # CONTENEDORPARTESII
|
||||
|
||||
# APHIS
|
||||
consecutive_aphis: Mapped[Optional[int]] = mapped_column(
|
||||
Integer) # CONSECUTIVOAPHIS
|
||||
Integer
|
||||
) # CONSECUTIVOAPHIS
|
||||
|
||||
# BOM/Commercial
|
||||
bom_version: Mapped[Optional[int]] = mapped_column(Integer) # VERSIONBOM
|
||||
bill_version: Mapped[Optional[int]] = mapped_column(Integer) # VERSIONBILL
|
||||
|
||||
# TLCAN value
|
||||
tlcan_value: Mapped[Optional[Decimal]] = mapped_column(
|
||||
Numeric(23, 8)) # VALORTLCAN
|
||||
tlcan_value: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORTLCAN
|
||||
|
||||
# Identifier
|
||||
identifier: Mapped[Optional[str]] = mapped_column(
|
||||
String(2)) # IDENTIFICADOR
|
||||
identifier: Mapped[Optional[str]] = mapped_column(String(2)) # IDENTIFICADOR
|
||||
|
||||
# Validation fields
|
||||
validation_zero: Mapped[Optional[int]] = mapped_column(
|
||||
Integer) # VALIDACIONZERO
|
||||
validation_one: Mapped[Optional[int]] = mapped_column(
|
||||
Integer) # VALIDACIONUNO
|
||||
validation_zero: Mapped[Optional[int]] = mapped_column(Integer) # VALIDACIONZERO
|
||||
validation_one: Mapped[Optional[int]] = mapped_column(Integer) # VALIDACIONUNO
|
||||
|
||||
# Material type
|
||||
material_type: Mapped[Optional[str]] = mapped_column(
|
||||
String(50)) # TIPOMAT/TIPODENUMPARTE
|
||||
String(50)
|
||||
) # TIPOMAT/TIPODENUMPARTE
|
||||
|
||||
# Order concept
|
||||
order_type: Mapped[Optional[str]] = mapped_column(
|
||||
String(50)) # TIPODEORDEN
|
||||
order_type: Mapped[Optional[str]] = mapped_column(String(50)) # TIPODEORDEN
|
||||
line_concept: Mapped[Optional[str]] = mapped_column(
|
||||
String(50)) # CONCEPTODELAPARTIDA
|
||||
String(50)
|
||||
) # CONCEPTODELAPARTIDA
|
||||
|
||||
# Review dispatch
|
||||
review_dispatch: Mapped[Optional[str]] = mapped_column(
|
||||
String(10)) # REVISARDESP
|
||||
review_dispatch: Mapped[Optional[str]] = mapped_column(String(10)) # REVISARDESP
|
||||
|
||||
# Take component from PT
|
||||
take_component_pt: Mapped[Optional[int]
|
||||
] = mapped_column(Integer) # TOMARCOMOPT
|
||||
take_component_pt: Mapped[Optional[int]] = mapped_column(Integer) # TOMARCOMOPT
|
||||
|
||||
# Pallet
|
||||
pallet2: Mapped[Optional[int]] = mapped_column(SmallInteger) # PALLET2
|
||||
|
||||
# Wildcard field
|
||||
wildcard_field: Mapped[Optional[str]] = mapped_column(
|
||||
String(100)) # CAMPOCOMODIN
|
||||
wildcard_field: Mapped[Optional[str]] = mapped_column(String(100)) # CAMPOCOMODIN
|
||||
|
||||
# Relationships
|
||||
item: Mapped["Item"] = relationship(back_populates="lines")
|
||||
financial: Mapped[Optional["LineFinancial"]] = relationship(
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False)
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False
|
||||
)
|
||||
quantity: Mapped[Optional["LineQuantity"]] = relationship(
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False)
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False
|
||||
)
|
||||
customs: Mapped[Optional["LineCustom"]] = relationship(
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False)
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False
|
||||
)
|
||||
description: Mapped[Optional["LineDescription"]] = relationship(
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False)
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False
|
||||
)
|
||||
reference: Mapped[Optional["LineReference"]] = relationship(
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False)
|
||||
back_populates="line", cascade="all, delete-orphan", uselist=False
|
||||
)
|
||||
class_info: Mapped[Optional["Class"]] = relationship(
|
||||
foreign_keys=[class_id], viewonly=True
|
||||
)
|
||||
unit_of_measure_info: Mapped[Optional["UnitOfMeasure"]] = relationship(
|
||||
foreign_keys=[unit_of_measure], viewonly=True
|
||||
)
|
||||
fa_data: Mapped[Optional["FaLineItem"]] = relationship(
|
||||
"FaLineItem", back_populates="master_info", uselist=False
|
||||
)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field, ConfigDict, field_validator
|
||||
from typing import Optional, Any
|
||||
from pydantic import BaseModel, Field, ConfigDict, field_validator, model_validator
|
||||
|
||||
# Import nested schemas
|
||||
from ..line_customs.schemas import (
|
||||
@@ -29,6 +29,13 @@ from ..line_references.schemas import (
|
||||
LineReferenceResponse,
|
||||
)
|
||||
|
||||
from api.v1.modules.a24.fa.fa_item_lines.dto import (
|
||||
FaLineItemCreateDTO,
|
||||
FaLineItemUpdateDTO,
|
||||
FaLineItemResponseDTO,
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# LINE ITEM SCHEMAS
|
||||
# ============================================================================
|
||||
@@ -40,16 +47,13 @@ class LineItemBase(BaseModel):
|
||||
line_number: int = Field(..., description="Line number")
|
||||
|
||||
# Part identification
|
||||
part_number: Optional[str] = Field(None, max_length=50, description="Part number")
|
||||
component_part_number: Optional[str] = Field(
|
||||
None, max_length=50, description="Component part number"
|
||||
part_number_id: Optional[int] = Field(None, description="Part number")
|
||||
component_part_number_id: Optional[int] = Field(
|
||||
None, description="Component part number"
|
||||
)
|
||||
class_code: Optional[str] = Field(None, max_length=20, description="Class code")
|
||||
class_id: Optional[int] = Field(None, description="Class code")
|
||||
|
||||
@field_validator(
|
||||
"class_code",
|
||||
"part_number",
|
||||
"component_part_number",
|
||||
"unit_of_measure",
|
||||
"alternate_unit",
|
||||
mode="before",
|
||||
@@ -91,12 +95,6 @@ class LineItemBase(BaseModel):
|
||||
has_fda_code: Optional[bool] = Field(None, description="Has FDA code")
|
||||
fda_key: Optional[str] = Field(None, max_length=10, description="FDA key")
|
||||
|
||||
# Subitem flags
|
||||
is_subitem: Optional[bool] = Field(None, description="Is subitem")
|
||||
contains_subitems: Optional[bool] = Field(None, description="Contains subitems")
|
||||
includes_subitems: Optional[bool] = Field(None, description="Includes subitems")
|
||||
subitem_number: Optional[bool] = Field(None, description="Subitem number")
|
||||
|
||||
# Special flags
|
||||
is_military_mcia: Optional[bool] = Field(
|
||||
None, description="Is military merchandise"
|
||||
@@ -210,6 +208,9 @@ class LineItemCreate(LineItemBase):
|
||||
reference: Optional[LineReferenceCreate] = Field(
|
||||
None, description="Reference data for this line"
|
||||
)
|
||||
fa_data: Optional[FaLineItemCreateDTO] = Field(
|
||||
None, description="Fixed Asset data for this line"
|
||||
)
|
||||
|
||||
|
||||
class LineItemUpdate(LineItemBase):
|
||||
@@ -231,6 +232,9 @@ class LineItemUpdate(LineItemBase):
|
||||
reference: Optional[LineReferenceUpdate] = Field(
|
||||
None, description="Reference data for this line"
|
||||
)
|
||||
fa_data: Optional[FaLineItemUpdateDTO] = Field(
|
||||
None, description="Fixed Asset data for this line"
|
||||
)
|
||||
|
||||
|
||||
class LineItemResponse(LineItemBase):
|
||||
@@ -243,5 +247,38 @@ class LineItemResponse(LineItemBase):
|
||||
customs: Optional[LineCustomResponse] = None
|
||||
description: Optional[LineDescriptionResponse] = None
|
||||
reference: Optional[LineReferenceResponse] = None
|
||||
fa_data: Optional[FaLineItemResponseDTO] = None
|
||||
|
||||
# Fields populated from relationships
|
||||
class_code: Optional[str] = None
|
||||
class_description: Optional[str] = None
|
||||
unit_of_measure_code: Optional[str] = None
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def extract_relationship_info(cls, data: Any) -> Any:
|
||||
"""Extract class_code, class_description and unit_of_measure_code from relationships"""
|
||||
if isinstance(data, dict):
|
||||
return data
|
||||
|
||||
# It's an ORM object
|
||||
result = {}
|
||||
for key in cls.model_fields.keys():
|
||||
if hasattr(data, key):
|
||||
result[key] = getattr(data, key)
|
||||
|
||||
# Extract class info
|
||||
if hasattr(data, "class_info") and data.class_info is not None:
|
||||
result["class_code"] = data.class_info.class_code
|
||||
result["class_description"] = data.class_info.description_es
|
||||
|
||||
# Extract unit of measure code
|
||||
if (
|
||||
hasattr(data, "unit_of_measure_info")
|
||||
and data.unit_of_measure_info is not None
|
||||
):
|
||||
result["unit_of_measure_code"] = data.unit_of_measure_info.code
|
||||
|
||||
return result
|
||||
|
||||
@@ -12,7 +12,7 @@ class LineQuantity(Base):
|
||||
Quantity details for line items
|
||||
Consolidates all line-level data from Q and S tables
|
||||
"""
|
||||
__tablename__ = "line_quantities"
|
||||
__tablename__ = "item_line_quantities"
|
||||
__table_args__ = {
|
||||
"schema": "a76",
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ class LineReference(Base):
|
||||
Reference details for line items
|
||||
Consolidates all line-level data from Q and S tables
|
||||
"""
|
||||
__tablename__ = "line_references"
|
||||
__tablename__ = "item_line_references"
|
||||
__table_args__ = {
|
||||
"schema": "a76",
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ Item -> LineItem -> LineFinancial
|
||||
-> LineCustoms
|
||||
-> LineDescription
|
||||
-> LineReference
|
||||
-> FaLineItem (Fixed Assets - a24)
|
||||
"""
|
||||
|
||||
import logging
|
||||
@@ -24,6 +25,7 @@ from .line_quantities.models import LineQuantity
|
||||
from .line_customs.models import LineCustom
|
||||
from .line_descriptions.models import LineDescription
|
||||
from .line_references.models import LineReference
|
||||
from api.v1.modules.a24.fa.fa_item_lines.models import FaLineItem
|
||||
from .models import Item
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -47,6 +49,9 @@ class ItemService:
|
||||
joinedload(Item.lines).joinedload(LineItem.customs),
|
||||
joinedload(Item.lines).joinedload(LineItem.description),
|
||||
joinedload(Item.lines).joinedload(LineItem.reference),
|
||||
joinedload(Item.lines).joinedload(LineItem.class_info),
|
||||
joinedload(Item.lines).joinedload(LineItem.unit_of_measure_info),
|
||||
joinedload(Item.lines).joinedload(LineItem.fa_data),
|
||||
)
|
||||
.filter(
|
||||
Item.id == item_id,
|
||||
@@ -74,6 +79,9 @@ class ItemService:
|
||||
joinedload(Item.lines).joinedload(LineItem.customs),
|
||||
joinedload(Item.lines).joinedload(LineItem.description),
|
||||
joinedload(Item.lines).joinedload(LineItem.reference),
|
||||
joinedload(Item.lines).joinedload(LineItem.class_info),
|
||||
joinedload(Item.lines).joinedload(LineItem.unit_of_measure_info),
|
||||
joinedload(Item.lines).joinedload(LineItem.fa_data),
|
||||
)
|
||||
.filter(
|
||||
Item.tenant_id == tenant_id,
|
||||
@@ -122,6 +130,7 @@ class ItemService:
|
||||
joinedload(Item.lines).joinedload(LineItem.customs),
|
||||
joinedload(Item.lines).joinedload(LineItem.description),
|
||||
joinedload(Item.lines).joinedload(LineItem.reference),
|
||||
joinedload(Item.lines).joinedload(LineItem.fa_data),
|
||||
)
|
||||
.filter(
|
||||
Item.invoice_id == invoice_id,
|
||||
@@ -164,6 +173,7 @@ class ItemService:
|
||||
customs_data = line_data.customs
|
||||
description_data = line_data.description
|
||||
reference_data = line_data.reference
|
||||
fa_data = line_data.fa_data
|
||||
|
||||
line_dict = line_data.model_dump(
|
||||
exclude={
|
||||
@@ -172,6 +182,7 @@ class ItemService:
|
||||
"customs",
|
||||
"description",
|
||||
"reference",
|
||||
"fa_data",
|
||||
}
|
||||
)
|
||||
line_dict["item_id"] = db_item.id
|
||||
@@ -210,7 +221,7 @@ class ItemService:
|
||||
description_dict["item_line_id"] = db_line.id
|
||||
db_description = LineDescription(**description_dict)
|
||||
db.add(db_description)
|
||||
|
||||
|
||||
# Create reference data if provided
|
||||
if reference_data:
|
||||
reference_dict = reference_data.model_dump()
|
||||
@@ -218,9 +229,17 @@ class ItemService:
|
||||
db_reference = LineReference(**reference_dict)
|
||||
db.add(db_reference)
|
||||
|
||||
# Create FA data if provided
|
||||
if fa_data:
|
||||
fa_dict = fa_data.model_dump()
|
||||
fa_dict["id"] = db_line.id # FA table uses same ID as line item
|
||||
fa_dict["tenant_id"] = tenant_id
|
||||
fa_dict["company_id"] = company_id
|
||||
db_fa = FaLineItem(**fa_dict)
|
||||
db.add(db_fa)
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
|
||||
return db_item
|
||||
|
||||
except IntegrityError as e:
|
||||
@@ -273,6 +292,7 @@ class ItemService:
|
||||
customs_data = line_data.customs
|
||||
description_data = line_data.description
|
||||
reference_data = line_data.reference
|
||||
fa_data = line_data.fa_data
|
||||
|
||||
line_dict = line_data.model_dump(
|
||||
exclude={
|
||||
@@ -281,6 +301,7 @@ class ItemService:
|
||||
"customs",
|
||||
"description",
|
||||
"reference",
|
||||
"fa_data",
|
||||
},
|
||||
exclude_unset=True,
|
||||
)
|
||||
@@ -320,6 +341,14 @@ class ItemService:
|
||||
reference_dict["item_line_id"] = db_line.id
|
||||
db.add(LineReference(**reference_dict))
|
||||
|
||||
# Create FA data if provided
|
||||
if fa_data is not None:
|
||||
fa_dict = fa_data.model_dump(exclude_unset=True)
|
||||
fa_dict["id"] = db_line.id # FA table uses same ID as line item
|
||||
fa_dict["tenant_id"] = tenant_id
|
||||
fa_dict["company_id"] = company_id
|
||||
db.add(FaLineItem(**fa_dict))
|
||||
|
||||
db.commit()
|
||||
db.refresh(db_item)
|
||||
return db_item
|
||||
|
||||
Reference in New Issue
Block a user