620 lines
30 KiB
Python
620 lines
30 KiB
Python
"""
|
|
Normalized Database Schema for SCAF (Fixed Assets) and SCAII (Parts Inventory)
|
|
SQLAlchemy v2 - Annex 24 Compliance
|
|
"""
|
|
|
|
from datetime import datetime
|
|
from decimal import Decimal
|
|
from typing import Optional, TYPE_CHECKING
|
|
from enum import Enum
|
|
from sqlalchemy import Boolean, String, Integer, Numeric, Text, SmallInteger, ForeignKey
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from core.database import Base
|
|
|
|
if TYPE_CHECKING:
|
|
from .series.models import Serie
|
|
|
|
# ============================================================================
|
|
# CORE ENTITIES
|
|
# ============================================================================
|
|
|
|
class Item(Base):
|
|
"""
|
|
Unified item header table for all import/export operations
|
|
Consolidates headers from both SCAF and SCAII systems
|
|
"""
|
|
__tablename__ = "items"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id")) # CONSECUTIVO
|
|
item_type: Mapped[str] = mapped_column(String(20)) # Type: IMPORT_TEMP, IMPORT_DEF, EXPORT, REPAIR, etc.
|
|
system_origin: Mapped[str] = mapped_column(String(10)) # SCAF or SCAII
|
|
|
|
# Item references
|
|
invoice_number: Mapped[Optional[str]] = mapped_column(String(15)) # FACTURAIMPO/FACTURAEXPO
|
|
reference_number: Mapped[Optional[str]] = mapped_column(String(20)) # NUMREFERENCIA
|
|
order: Mapped[Optional[str]] = mapped_column(String(50)) # ORDENCOMPRA / ORDENVENTA
|
|
guide_number: Mapped[Optional[str]] = mapped_column(String(50)) # NUMEROGUIA/NUMERODEGUIA
|
|
|
|
# Dates
|
|
invoice_date: Mapped[Optional[int]] = mapped_column(Integer) # FECHAFACTURA
|
|
depreciation_date: Mapped[Optional[int]] = mapped_column(Integer) # FECHADEPRECIACION
|
|
|
|
# Administrative fields
|
|
rectification: Mapped[Optional[int]] = mapped_column(SmallInteger) # RECTIFICACION
|
|
warehouse: Mapped[Optional[str]] = mapped_column(String(30)) # BODEGA
|
|
location: Mapped[Optional[str]] = mapped_column(String(200)) # LOCALIZACION
|
|
|
|
# Relationships
|
|
lines: Mapped[list["LineItem"]] = relationship(back_populates="item", cascade="all, delete-orphan")
|
|
|
|
|
|
class LineItem(Base):
|
|
"""
|
|
Unified line items for all items
|
|
Consolidates all line-level data from Q and S tables
|
|
"""
|
|
__tablename__ = "item_lines"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
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
|
|
|
|
# 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
|
|
|
|
# Unit of measure
|
|
unit_of_measure: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.units_of_measure_general.id")) # UNIDADMEDIDA/UNIMED
|
|
alternate_unit: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.units_of_measure_general.id")) # UNIMEDALTERNA
|
|
uma_key: Mapped[Optional[str]] = mapped_column(String(2)) # CLAVEUMA
|
|
auxiliary_unit: Mapped[Optional[str]] = mapped_column(String(5)) # UNIMEDAUXILIAR
|
|
|
|
# Permits and certificates
|
|
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
|
|
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
|
|
|
|
# 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
|
|
|
|
# Special flags
|
|
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
|
|
|
|
|
|
|
|
# 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
|
|
|
|
# Tax payment
|
|
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
|
|
igi_payment_method: Mapped[Optional[str]] = mapped_column(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_determined_value: Mapped[Optional[Decimal]] = mapped_column(Numeric(29, 8)) # METVALORVALORDETERMINADO/METVALORACIONVALORDETERMINADO
|
|
valuation_reason: Mapped[Optional[str]] = mapped_column(String(500)) # METVALORMOTIVODEUSO/METVALORACIONMOTIVODEUSO
|
|
|
|
# Container rules
|
|
container_rule: Mapped[Optional[str]] = mapped_column(String(50)) # CONTENEDORREGLA
|
|
container_parts_ii: Mapped[Optional[str]] = mapped_column(String(50)) # CONTENEDORPARTESII
|
|
|
|
# APHIS
|
|
consecutive_aphis: Mapped[Optional[int]] = mapped_column(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
|
|
|
|
# Identifier
|
|
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
|
|
|
|
# Material type
|
|
material_type: Mapped[Optional[str]] = mapped_column(String(50)) # TIPOMAT/TIPODENUMPARTE
|
|
|
|
# Order concept
|
|
order_type: Mapped[Optional[str]] = mapped_column(String(50)) # TIPODEORDEN
|
|
line_concept: Mapped[Optional[str]] = mapped_column(String(50)) # CONCEPTODELAPARTIDA
|
|
|
|
# Review dispatch
|
|
review_dispatch: Mapped[Optional[str]] = mapped_column(String(10)) # REVISARDESP
|
|
|
|
# Take component from PT
|
|
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
|
|
|
|
# Relationships
|
|
item: Mapped["Item"] = relationship(back_populates="lines")
|
|
|
|
class LineFinancial(Base):
|
|
"""
|
|
Financial details for line items
|
|
Consolidates all line-level data from Q and S tables
|
|
"""
|
|
__tablename__ = "line_financials"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
item_line_id: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id"))
|
|
|
|
# Costs - Capture
|
|
unit_cost_capture: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUNITARIOCAPTURA
|
|
|
|
# Costs - USD
|
|
unit_cost_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUNITARIODLLS/COSTOUNITARIOME
|
|
unit_cost_commercial_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUCOMDLLS
|
|
unit_cost_current_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOACTUALDLLS
|
|
unit_cost_depreciated_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTODEPRECME
|
|
unit_cost_subitem_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUNITARIOSUBPDLLS
|
|
unit_cost_auxiliary_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUAUXILIARME
|
|
sales_cost_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOVENTAME
|
|
commercial_unit_cost: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUNICOMERCIAL
|
|
|
|
# Costs - MXN
|
|
unit_cost_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUNITARIOPESOS/COSTOUNITARIOMN
|
|
unit_cost_commercial_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUCOMPESOS
|
|
unit_cost_current_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOACTUALMN
|
|
unit_cost_depreciated_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTODEPRECMN
|
|
unit_cost_subitem_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOUNITARIOSUBPPESOS
|
|
sales_cost_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # COSTOVENTAMN
|
|
|
|
# Costs - MC (Custom Currency)
|
|
unit_cost_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(29, 8)) # COSTOUNITARIOMC
|
|
|
|
# Values - MXN
|
|
value_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORMN/VALORIMPOMN/VALOREXPOMN
|
|
value_commercial_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALOREXPOCOMMN
|
|
value_updated_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORACTUALIZADOMN
|
|
value_subitem_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORSUBPMN
|
|
sub_import_value_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # SUBVALORIMPOMN
|
|
value_returned_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORRETORNADOMN
|
|
value_depreciated_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORDEPRECIADOMN
|
|
customs_value_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORADUANASMN
|
|
value_total_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORTOTALMN
|
|
value_temp_material_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORMPTEMPMN
|
|
value_def_material_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORMPDEFMN
|
|
value_added_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORAGREMN
|
|
value_national_packing_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALEMPAQUENACMN
|
|
vat_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # IVAIMPOMN/IVAEXPOMN/VALORIVAMN
|
|
vat_used_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORIVAMNUSADO
|
|
advalorem_line_mxn: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # ADVALORMNLINEAPED
|
|
|
|
# Values - USD
|
|
value_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(29, 8)) # VALORME/VALORIMPOME/VALOREXPOME
|
|
value_commercial_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALOREXPOCOMME
|
|
value_updated_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORACTUALIZADOME
|
|
value_subitem_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORSUBPME
|
|
sub_import_value_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # SUBVALORIMPOME
|
|
value_returned_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORRETORNADOME
|
|
value_depreciated_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORDEPRECIADOME
|
|
customs_value_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORADUANASME
|
|
value_auxiliary_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORIMPOAUXILIARME / VALOREXPORAUXILIARME
|
|
value_total_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORTOTALME
|
|
value_temp_material_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORMPTEMPME
|
|
value_def_material_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORMPDEFME
|
|
value_added_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORAGREME
|
|
value_national_packing_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALEMPAQUENACME
|
|
value_us_packing_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALEMPAQUEUSME
|
|
vat_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # IVAIMPOME/IVAEXPOME/VALORIVAME
|
|
vat_used_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORIVAMEUSADO
|
|
value_non_originating_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORNOORIGINARIOME
|
|
value_originating_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORORIGINARIOME
|
|
igi_amount_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # MONTOIGIME
|
|
exempt_amount_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # MONTOEXCENTOME
|
|
total_commercial_value: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORTOTALCOMERCIAL
|
|
advalorem_line_usd: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # ADVALORMELINEAPED
|
|
|
|
# Values - MC
|
|
value_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(29, 8)) # VALORIMPOMC/VALOREXPOMC
|
|
sub_import_value_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # SUBVALORIMPOMC
|
|
vat_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # IVAIMPOMC/IVAEXPOMC
|
|
value_added_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORAGREMC
|
|
value_national_packing_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALEMPAQUENACMC
|
|
value_total_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORTOTALMC
|
|
value_temp_material_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORMPTEMPMC
|
|
value_def_material_mc: Mapped[Optional[Decimal]] = mapped_column(Numeric(23, 8)) # VALORMPDEFMC
|
|
|
|
# Relationship
|
|
item_line: Mapped["LineItem"] = relationship()
|
|
|
|
class LineQuantity(Base):
|
|
"""
|
|
Quantity details for line items
|
|
Consolidates all line-level data from Q and S tables
|
|
"""
|
|
__tablename__ = "line_quantities"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
item_line_id: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id"))
|
|
|
|
# Quantities
|
|
quantity: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTIMPO / CANTEXPO / CANTIMPODEF
|
|
alternate_quantity: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTALTERNA
|
|
quantity_uma: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTIMPOUMA / CANTEXPOUMA
|
|
auxiliary_quantity: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTIMPOAUXILIAR / CANTEXPOAUXILIAR
|
|
|
|
# Quantities - Special (SCAF specific)
|
|
quantity_temp_export: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTEXPOTEMP
|
|
quantity_existence: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTEXISTENCIA
|
|
quantity_returned: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTRETORNADA
|
|
quantity_returned_temp: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # CANTRETORNADATEMP
|
|
serial_count: Mapped[Optional[int]] = mapped_column(Integer) # CANT_SERIES/CANT_SERIESDEF
|
|
|
|
# Weight
|
|
weight_unit: Mapped[Optional[str]] = mapped_column(String(3)) # 'KG' o 'LB'
|
|
net_weight: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # PESONETO
|
|
gross_weight: Mapped[Optional[Decimal]] = mapped_column(Numeric(19, 8)) # PESOBRUTO
|
|
|
|
# Packaging
|
|
package_key: Mapped[Optional[str]] = mapped_column(String(5)) # CLAVEBULTOS
|
|
package_quantity: Mapped[Optional[int]] = mapped_column(Integer) # CANTBULTOS
|
|
package_description: Mapped[Optional[str]] = mapped_column(String(40)) # DESCBULTOS
|
|
container_quantity: Mapped[Optional[int]] = mapped_column(SmallInteger) # CANTBULCONT
|
|
container_description: Mapped[Optional[str]] = mapped_column(String(40)) # DESCCONTENEDOR
|
|
box_count: Mapped[Optional[str]] = mapped_column(String(30)) # NOCAJAS
|
|
|
|
# Relationship
|
|
item_line: Mapped["LineItem"] = relationship()
|
|
|
|
class LineCustoms(Base):
|
|
"""
|
|
Customs details for line items
|
|
Consolidates all line-level data from Q and S tables
|
|
"""
|
|
__tablename__ = "line_customs"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
item_line_id: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id"))
|
|
|
|
# Tariff/Customs Classifications
|
|
fraction: Mapped[Optional[str]] = mapped_column(String(10)) # FRACCION / FRACCIONIMPO / FRACCIONEXPO
|
|
fraction_type: Mapped[Optional[str]] = mapped_column(String(7)) # TIPOFRACCION / TIPOFRACCIONIMPO / TIPOFRACCIONEXPO
|
|
american_fraction: Mapped[Optional[str]] = mapped_column(String(16)) # FRACCIONAMERICANA
|
|
alternate_fraction: Mapped[Optional[str]] = mapped_column(String(10)) # FRACCIONALTERNA
|
|
reference_fraction: Mapped[Optional[str]] = mapped_column(String(10)) # FRACCIONREFERENCIA
|
|
octave_fraction: Mapped[Optional[str]] = mapped_column(String(10)) # FRACCIONROCTAVA
|
|
tlcan_fraction: Mapped[Optional[str]] = mapped_column(String(13)) # FRACCIONTLCAN
|
|
extra_american_fraction: Mapped[Optional[str]] = mapped_column(String(16)) # FRACAMESELEXTRA
|
|
garment_fraction: Mapped[Optional[str]] = mapped_column(String(19)) # FRACCIONDELAPRENDA/FRACCIONDELAPARTIDA
|
|
|
|
# Ad Valorem
|
|
advalorem: Mapped[Optional[str]] = mapped_column(String(10)) # ADVIMPO / ADVEXPO
|
|
advalorem_numeric: Mapped[Optional[Decimal]] = mapped_column(Numeric(7, 2)) # ADVIMPONUM / ADVEXPONUM
|
|
advalorem_american: Mapped[Optional[Decimal]] = mapped_column(Numeric(5, 2)) # ADVAME
|
|
advalorem_tlcan: Mapped[Optional[Decimal]] = mapped_column(Numeric(5, 2)) # ADVTLCAN
|
|
|
|
# Rates
|
|
rate: Mapped[Optional[str]] = mapped_column(String(10)) # TASAIM / TASAEX
|
|
depreciation_rate: Mapped[Optional[Decimal]] = mapped_column(Numeric(5, 2)) # TASADEPRECIA
|
|
|
|
# Origin/Destination
|
|
origin_country: Mapped[Optional[str]] = mapped_column(String(3)) # PAISORIGEN
|
|
destination_country: Mapped[Optional[str]] = mapped_column(String(3)) # PAISDESTINO
|
|
optional_country: Mapped[Optional[str]] = mapped_column(String(3)) # PAISOPCIONAL
|
|
origin_procedure: Mapped[Optional[str]] = mapped_column(String(3)) # PROCEDENCIA
|
|
scrap_procedure: Mapped[Optional[str]] = mapped_column(String(3)) # PROCSCRAP
|
|
|
|
# Sector
|
|
sector: Mapped[Optional[str]] = mapped_column(String(8)) # SECTOR
|
|
|
|
# Relationship
|
|
item_line: Mapped["LineItem"] = relationship()
|
|
|
|
class LineDescription(Base):
|
|
"""
|
|
Description details for line items
|
|
Consolidates all line-level data from Q and S tables
|
|
"""
|
|
__tablename__ = "line_descriptions"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
item_line_id: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id"))
|
|
|
|
# Descriptions
|
|
description_spanish: Mapped[Optional[str]] = mapped_column(String(4999)) # DESCRIPCIONE
|
|
description_english: Mapped[Optional[str]] = mapped_column(String(4999)) # DESCRIPCIONI
|
|
extra_description: Mapped[Optional[str]] = mapped_column(Text) # DESCRIPCIONEEXTRA
|
|
part_description: Mapped[Optional[str]] = mapped_column(String(500)) # DESCRIPCIONPARTE
|
|
class_description: Mapped[Optional[str]] = mapped_column(String(500)) # DESCRIPCIONCLASE
|
|
|
|
# Product attributes
|
|
brand: Mapped[Optional[str]] = mapped_column(String(50)) # MARCA
|
|
model: Mapped[Optional[str]] = mapped_column(String(50)) # MODELO
|
|
has_serial: Mapped[Optional[bool]] = mapped_column(Boolean) # LLEVASERIE
|
|
|
|
# Additional information
|
|
additional_info_spanish: Mapped[Optional[str]] = mapped_column(String(1000)) # INFOADICIONESP
|
|
additional_info_english: Mapped[Optional[str]] = mapped_column(String(1000)) # INFOADICIONING
|
|
|
|
# Lot and entry tracking
|
|
lot: Mapped[Optional[str]] = mapped_column(String(254)) # LOTE
|
|
entry_number: Mapped[Optional[str]] = mapped_column(String(50)) # NUMENTRADA/NUMERODEENTRADA
|
|
|
|
# Relationship
|
|
item_line: Mapped["LineItem"] = relationship()
|
|
|
|
class LineReference(Base):
|
|
"""
|
|
Reference details for line items
|
|
Consolidates all line-level data from Q and S tables
|
|
"""
|
|
__tablename__ = "line_references"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
item_line_id: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id"))
|
|
|
|
serie_id: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.item_line_series.id")) # SERIEPARTIDA
|
|
|
|
# Customer/Vendor
|
|
customer_invoice: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # CLIENTEFACTURAR
|
|
assigned_client: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # CLIENTEASIGNADO
|
|
supplier: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # PROVEEDOR
|
|
requisitioner: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # REQUISITOR
|
|
sent_to: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.clients_and_providers.id")) # ENVIADOA
|
|
|
|
# PED line reference
|
|
ped_line: Mapped[Optional[int]] = mapped_column(Integer) # LINEAPED
|
|
ro_line: Mapped[Optional[int]] = mapped_column(Integer) # LINEARO
|
|
|
|
# Relationship
|
|
item_line: Mapped["LineItem"] = relationship()
|
|
|
|
# ============================================================================
|
|
# SUPPORTING TABLES
|
|
# ============================================================================
|
|
|
|
class PackingList(Base):
|
|
"""
|
|
Packing list items
|
|
From: SPartidasPackingList
|
|
"""
|
|
__tablename__ = "packing_lists"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
item_line_id: Mapped[int] = mapped_column(Integer) # LINEA
|
|
packing_list_number: Mapped[Optional[str]] = mapped_column(String(100)) # NUMPACKINGLIST
|
|
|
|
|
|
class RepairPart(Base):
|
|
"""
|
|
Repair parts (orphan table without primary key in original)
|
|
From: SPartidasRep
|
|
"""
|
|
__tablename__ = "repair_parts"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
import_line: Mapped[int] = mapped_column(Integer) # LINEAIMPO
|
|
|
|
|
|
class CTMShipment(Base):
|
|
"""
|
|
CTM Shipment lines (temporary manufacturing)
|
|
From: SPartidasEnviaCTM
|
|
"""
|
|
__tablename__ = "ctm_shipments"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
shipment_line: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id")) # LINEAENVIO
|
|
brand: Mapped[Optional[str]] = mapped_column(String(50)) # MARCA
|
|
model: Mapped[Optional[str]] = mapped_column(String(50)) # MODELO
|
|
serial_numbers: Mapped[Optional[str]] = mapped_column(String(50)) # SERIES
|
|
|
|
|
|
class CTMReceipt(Base):
|
|
"""
|
|
CTM Receipt lines (temporary manufacturing)
|
|
From: SPartidasReciboCTM
|
|
"""
|
|
__tablename__ = "ctm_receipts"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
receipt_line: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id")) # LINEARECIBO
|
|
|
|
option: Mapped[Optional[str]] = mapped_column(String(3)) # OPCION
|
|
exit_invoice: Mapped[Optional[str]] = mapped_column(String(19)) # FACTURASALIDA
|
|
|
|
|
|
class SubassemblyEntry(Base):
|
|
"""
|
|
Subassembly/Submanufacturing Entry lines
|
|
From: SPartidasEntradaSM
|
|
"""
|
|
__tablename__ = "subassembly_entries"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
remission_line: Mapped[int] = mapped_column(Integer) # LINEAREMISION
|
|
exit_invoice: Mapped[Optional[str]] = mapped_column(String(15)) # FACTURASALIDA
|
|
exit_line: Mapped[Optional[int]] = mapped_column(Integer) # LINEASALIDA
|
|
|
|
|
|
class SubassemblyExit(Base):
|
|
"""
|
|
Subassembly/Submanufacturing Exit lines
|
|
From: SPartidasSalidaSM
|
|
"""
|
|
__tablename__ = "subassembly_exits"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
exit_line: Mapped[int] = mapped_column(ForeignKey("a76.item_lines.id")) # LINEASALIDA
|
|
|
|
|
|
class ImpositionPart(Base):
|
|
"""
|
|
Imposition parts (orphan table without primary key constraint)
|
|
From: SPartidasImposion
|
|
"""
|
|
__tablename__ = "imposition_parts"
|
|
__table_args__ = {
|
|
"schema": "a76",
|
|
}
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, autoincrement=True)
|
|
import_line: Mapped[int] = mapped_column(Integer) # LINEAIMPO
|
|
|
|
|
|
|
|
# ============================================================================
|
|
# INDEXES AND CONSTRAINTS
|
|
# ============================================================================
|
|
|
|
"""
|
|
Recommended indexes for optimal query performance:
|
|
|
|
CREATE INDEX idx_items_consecutive ON items(consecutive);
|
|
CREATE INDEX idx_items_invoice ON items(invoice_number);
|
|
CREATE INDEX idx_items_type_system ON items(item_type, system_origin);
|
|
CREATE INDEX idx_items_dates ON items(invoice_date, depreciation_date);
|
|
|
|
CREATE INDEX idx_lines_item ON item_lines(item_id);
|
|
CREATE INDEX idx_lines_part ON item_lines(part_number);
|
|
CREATE INDEX idx_lines_class ON item_lines(class_code);
|
|
CREATE INDEX idx_lines_invoice_refs ON item_lines(import_invoice, export_invoice);
|
|
CREATE INDEX idx_lines_fractions ON item_lines(import_fraction, export_fraction);
|
|
|
|
CREATE INDEX idx_packing_consecutive ON packing_lists(consecutive);
|
|
CREATE INDEX idx_packing_part ON packing_lists(part_number);
|
|
|
|
CREATE INDEX idx_repair_invoice ON repair_parts(import_invoice, import_line);
|
|
CREATE INDEX idx_ctm_ship_consec ON ctm_shipments(consecutive);
|
|
CREATE INDEX idx_ctm_rcpt_consec ON ctm_receipts(consecutive);
|
|
CREATE INDEX idx_sub_entry_consec ON subassembly_entries(consecutive);
|
|
CREATE INDEX idx_sub_exit_consec ON subassembly_exits(consecutive);
|
|
CREATE INDEX idx_imposition_consec ON imposition_parts(consecutive);
|
|
"""
|
|
|
|
|
|
# ============================================================================
|
|
# MIGRATION NOTES
|
|
# ============================================================================
|
|
|
|
"""
|
|
MIGRATION STRATEGY FROM ORIGINAL TABLES TO NORMALIZED SCHEMA:
|
|
|
|
1. DOCUMENT MAPPING:
|
|
- QEqeMaq (Equipment Import Temp) → items (type='EQUIPMENT_IMPORT_TEMP', system='SCAF')
|
|
- QEqeMaqRep (Equipment Repair Export) → items (type='EQUIPMENT_REPAIR_EXPORT', system='SCAF')
|
|
- QEqiDef (Equipment Import Definitive) → items (type='EQUIPMENT_IMPORT_DEF', system='SCAF')
|
|
- QEqiMaq (Equipment Machinery) → items (type='EQUIPMENT_MACHINERY', system='SCAF')
|
|
- QEqiMaqRep (Equipment Machinery Repair) → items (type='EQUIPMENT_REPAIR_IMPORT', system='SCAF')
|
|
- SPartidasCM (Common Commerce) → items (type='COMMON_COMMERCE', system='SCAII')
|
|
- SPartidasExpo (Export) → items (type='EXPORT', system='SCAII')
|
|
- SPartidasImpo (Import) → items (type='IMPORT', system='SCAII')
|
|
|
|
2. LINE MAPPING:
|
|
All line items from Q* and SPartidas* tables map to item_lines with appropriate
|
|
field mapping based on the original column names (preserved as comments).
|
|
|
|
3. FIELD CONSOLIDATION RULES:
|
|
- Costs: Unified under unit_cost_* with currency suffix (usd/mxn/mc)
|
|
- Values: Unified under value_* with currency suffix
|
|
- Quantities: Unified under quantity_* with specific purpose suffixes
|
|
- Descriptions: Consolidated into description_spanish/english/extra
|
|
- Fractions: All fraction fields preserved with clear naming
|
|
|
|
4. DATA INTEGRITY:
|
|
- Original CONSECUTIVO + LINE number preserved for traceability
|
|
- Foreign key relationships established via item_id
|
|
- All original fields retained to prevent data loss
|
|
|
|
5. SPECIAL TABLES:
|
|
- PackingList, RepairPart, CTM*, Subassembly*, ImpositionPart remain separate
|
|
as they serve specific purposes and don't fit the main item/line pattern
|
|
|
|
6. BENEFITS:
|
|
- Eliminates redundancy across 13 original tables
|
|
- Unified query interface for all operations
|
|
- Maintains full audit trail with original field names
|
|
- Enables cross-system reporting (SCAF + SCAII)
|
|
- Simplifies maintenance with single schema
|
|
|
|
7. QUERYING EXAMPLES:
|
|
```python
|
|
# Get all imports (both systems)
|
|
session.query(Item).filter(
|
|
Item.item_type.in_(['IMPORT', 'EQUIPMENT_IMPORT_TEMP', 'EQUIPMENT_IMPORT_DEF'])
|
|
)
|
|
|
|
# Get all lines for a specific part across all items
|
|
session.query(LineItem).filter(
|
|
LineItem.part_number == 'ABC123'
|
|
)
|
|
|
|
# Get SCAF equipment with depreciation
|
|
session.query(Item).join(LineItem).filter(
|
|
Item.system_origin == 'SCAF',
|
|
LineItem.value_depreciated_usd.isnot(None)
|
|
)
|
|
```
|
|
""" |