feat: Add new fields for invoice logistics and line items, enhance UI for class selection

This commit is contained in:
Galindo97
2026-01-15 08:12:00 -06:00
parent 98c7fe3bb5
commit 7e9a1acd30
15 changed files with 465 additions and 43 deletions

View File

@@ -674,6 +674,32 @@ class InvoiceLogistics(Base, TenantScopedMixin, TimestampMixin):
Boolean, default=False
) # SETRATAPROCESOCTM / Se trata de proceso CTM
# Continuation Tab Fields
equipment_reviewed: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Fue revisado el equipo
is_subdivision: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Sub división
acts_as_cd: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Funge como CD
pedimento_arrived: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Llegó el pedimento
green_light_mx: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Semáforo verde México
green_light_us: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Semáforo verde USA
red_light_mx: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Semáforo rojo México
red_light_us: Mapped[Optional[bool]] = mapped_column(
Boolean, default=False
) # Semáforo rojo USA
# Relationship
header: Mapped["InvoiceHeader"] = relationship(back_populates="logistics")

View File

@@ -345,6 +345,15 @@ class InvoiceLogisticsBase(BaseModel):
None, max_length=20, description="Payment receipt number"
)
is_ctm_process: Optional[bool] = Field(False, description="Is CTM process")
# Continuation Tab Fields
equipment_reviewed: Optional[bool] = Field(False, description="Equipment reviewed")
is_subdivision: Optional[bool] = Field(False, description="Is subdivision")
acts_as_cd: Optional[bool] = Field(False, description="Acts as CD")
pedimento_arrived: Optional[bool] = Field(False, description="Pedimento arrived")
green_light_mx: Optional[bool] = Field(False, description="Green light Mexico")
green_light_us: Optional[bool] = Field(False, description="Green light USA")
red_light_mx: Optional[bool] = Field(False, description="Red light Mexico")
red_light_us: Optional[bool] = Field(False, description="Red light USA")
class InvoiceSalesDetailsBase(BaseModel):

View File

@@ -1,5 +1,5 @@
from typing import Optional, TYPE_CHECKING
from sqlalchemy import Boolean, String, Text, ForeignKey
from sqlalchemy import Boolean, String, Text, Integer, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from core.database import Base
@@ -39,5 +39,13 @@ class LineDescription(Base):
lot: Mapped[Optional[str]] = mapped_column(String(254)) # LOTE
entry_number: Mapped[Optional[str]] = mapped_column(String(50)) # NUMENTRADA/NUMERODEENTRADA
# Eighth rule and A31 fields
eighth_rule_fraction: Mapped[Optional[str]] = mapped_column(String(20)) # Eighth Rule Fraction
eighth_rule_line: Mapped[Optional[int]] = mapped_column(Integer) # Eighth Rule Line
consider_a31: Mapped[Optional[bool]] = mapped_column(Boolean, default=False) # Consider in A31
# Machinery location
machinery_location: Mapped[Optional[str]] = mapped_column(String(200)) # Machinery and equipment location
# Relationship (one-to-one)
line: Mapped["LineItem"] = relationship(back_populates="description")

View File

@@ -26,6 +26,14 @@ class LineDescriptionBase(BaseModel):
# Lot and entry tracking
lot: Optional[str] = Field(None, max_length=254, description="Lot (LOTE)")
entry_number: Optional[str] = Field(None, max_length=50, description="Entry number (NUMENTRADA/NUMERODEENTRADA)")
# Eighth rule and A31 fields
eighth_rule_fraction: Optional[str] = Field(None, max_length=20, description="Eighth Rule Fraction")
eighth_rule_line: Optional[int] = Field(None, description="Eighth Rule Line")
consider_a31: Optional[bool] = Field(False, description="Consider in A31")
# Machinery location
machinery_location: Optional[str] = Field(None, max_length=200, description="Machinery and equipment location")
class LineDescriptionCreate(LineDescriptionBase):

View File

@@ -41,15 +41,15 @@ class LineItem(Base, TenantScopedMixin, TimestampMixin):
component_part_number_id: Mapped[Optional[str]] = mapped_column(
ForeignKey("a76.parts.id")
) # NUMPARTECOM
class_id: Mapped[Optional[str]] = mapped_column(
class_id: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.classes.id")
) # CLASE
# Unit of measure
unit_of_measure: Mapped[Optional[str]] = mapped_column(
unit_of_measure: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.units_of_measure.id")
) # UNIDADMEDIDA/UNIMED
alternate_unit: Mapped[Optional[str]] = mapped_column(
alternate_unit: Mapped[Optional[int]] = mapped_column(
ForeignKey("a76.units_of_measure.id")
) # UNIMEDALTERNA
uma_key: Mapped[Optional[str]] = mapped_column(String(2)) # CLAVEUMA
@@ -173,11 +173,15 @@ class LineItem(Base, TenantScopedMixin, TimestampMixin):
back_populates="line", cascade="all, delete-orphan", uselist=False
)
class_info: Mapped[Optional["Class"]] = relationship(
foreign_keys=[class_id], viewonly=True
"api.v1.modules.a76.classes.models.Class",
foreign_keys=[class_id],
viewonly=True
)
unit_of_measure_info: Mapped[Optional["UnitOfMeasure"]] = relationship(
foreign_keys=[unit_of_measure], viewonly=True
"api.v1.modules.a76.general_catalogs.units_of_measure.models.UnitOfMeasure",
foreign_keys=[unit_of_measure],
viewonly=True
)
fa_data: Mapped[Optional["FaLineItem"]] = relationship(
"FaLineItem", back_populates="master_info", uselist=False
"FaLineItem", back_populates="master_info", uselist=False, cascade="all, delete"
)

View File

@@ -53,24 +53,12 @@ class LineItemBase(BaseModel):
)
class_id: Optional[int] = Field(None, description="Class code")
@field_validator(
"unit_of_measure",
"alternate_unit",
mode="before",
)
@classmethod
def convert_to_string(cls, v):
"""Convert integers to strings for FK fields"""
if v is not None and not isinstance(v, str):
return str(v)
return v
# Unit of measure
unit_of_measure: Optional[str] = Field(
None, max_length=10, description="Unit of measure"
unit_of_measure: Optional[int] = Field(
None, description="Unit of measure"
)
alternate_unit: Optional[str] = Field(
None, max_length=10, description="Alternate unit"
alternate_unit: Optional[int] = Field(
None, description="Alternate unit"
)
uma_key: Optional[str] = Field(None, max_length=2, description="UMA key")
auxiliary_unit: Optional[str] = Field(