feat: add invoice editing components and UI elements

- Implemented `invoice-top-fields.svelte` for editing main invoice details including operation type, invoice type, and compliance fields.
- Created `observations-tab-form.svelte` to manage invoice observations and compliance data.
- Developed `others-tab-form.svelte` for additional logistics and compliance information related to the invoice.
- Introduced reusable UI components for checkboxes and radio groups in `checkbox.svelte`, `radio-group.svelte`, and their respective item components.
- Enhanced form handling with TypeScript for better type safety and maintainability.
This commit is contained in:
AlexeerCT
2025-12-24 13:52:25 -06:00
parent 7c8b972899
commit 18325a2925
21 changed files with 2569 additions and 537 deletions

View File

@@ -38,6 +38,7 @@ class InvoiceHeader(Base, TenantScopedMixin, TimestampMixin):
id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True)
# Identifiers
system: Mapped[Optional[str]] = mapped_column(String(10)) # 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(10)) # TIPOMOVIMIENTO / Clasifica imp/exp/sm/ctm
invoice_type: Mapped[Optional[str]] = mapped_column(ForeignKey("public.invoice_types.key")) # TIPOFACTURA / TIPODOC
invoice_number: Mapped[Optional[str]] = mapped_column(String(20)) # FACTURAIMPO/FACTURAEXPO/FACTURAREMISION/FACTURAENVIO/FACTURASALIDA
@@ -151,6 +152,7 @@ class InvoiceComplianceMx(Base, TenantScopedMixin, TimestampMixin):
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[str]] = mapped_column(String(2)) # ESDUENOMCIA / Es dueño de mercancía
@@ -268,11 +270,13 @@ 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 Info
carrier_id: Mapped[Optional[str]] = mapped_column(String(10)) # TRANSPORTISTA / Transportista
carrier_us_id: Mapped[Optional[str]] = mapped_column(String(10)) # TRANSPORTISTAAME / Transportista americano
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_mode: Mapped[Optional[str]] = mapped_column(String(15)) # MODTRANS / Modo 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[str]] = mapped_column(String(2)) # ESFERROCARRIL / Es ferrocarril
rail_id: Mapped[Optional[str]] = mapped_column(String(31)) # IDFERRORCARRIL / ID ferrocarril
@@ -357,5 +361,8 @@ class InvoiceCollections(Base, TenantScopedMixin, TimestampMixin):
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
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

View File

@@ -8,8 +8,10 @@ from .models import OperationType
# --- Base Schemas ---
class InvoiceHeaderBase(BaseModel):
"""Base fields for Invoice Header"""
system: Optional[str] = Field(
None, max_length=10, description="System of origin")
operation_type: Optional[OperationType] = Field(
None, max_length=20, description="Operation type: imp/exp")
None, max_length=10, description="Operation type: imp/exp/sm/ctm")
invoice_type: Optional[str] = Field(
None, max_length=5, description="Invoice type key")
invoice_number: Optional[str] = Field(
@@ -20,20 +22,64 @@ class InvoiceHeaderBase(BaseModel):
None, max_length=50, description="Purchase order")
related_doc_id: Optional[int] = Field(
None, description="Related document ID for rectifications")
alternate_invoice: Optional[str] = Field(
None, max_length=99, description="Alternate invoice")
invoice_ref: Optional[str] = Field(
None, max_length=19, description="Invoice reference")
proforma_number: Optional[str] = Field(
None, max_length=20, description="Proforma number")
invoice_date: Optional[date] = Field(None, description="Invoice date")
emission_date: Optional[date] = Field(None, description="Emission date")
is_updated: Optional[bool] = Field(None, 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")
traffic_light_status: Optional[str] = Field(
None, max_length=50, description="Traffic light status (SEMAFORO)")
None, max_length=50, description="Traffic light status")
process_log: Optional[str] = Field(
None, max_length=300, description="Processing log")
status_rec: Optional[int] = Field(None, description="Reception status")
status_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 and observations")
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")
path_pdf: Optional[str] = Field(
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")
party_count: Optional[int] = Field(None, description="Quantity of parties")
generate_id: Optional[str] = Field(
None, max_length=1, description="Generate ID")
generate_desc_parties: Optional[str] = Field(
None, max_length=12, description="Generate description of parties")
apply_manual_discount: Optional[str] = Field(
None, max_length=1, 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")
payment_terms: Optional[str] = Field(
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")
class InvoiceComplianceMxBase(BaseModel):
@@ -41,21 +87,105 @@ class InvoiceComplianceMxBase(BaseModel):
pedimento: Optional[str] = Field(
None, max_length=19, description="Pedimento number")
pedimento_code: Optional[str] = Field(
None, max_length=5, description="Pedimento code (R1/K1)")
None, max_length=5, description="Pedimento code (R1)")
pedimento_k1: Optional[str] = Field(
None, max_length=15, description="Pedimento K1")
remesa: Optional[int] = Field(None, description="Remesa")
aduana: Optional[str] = Field(
None, max_length=5, description="Customs office")
customs_agent: Optional[str] = Field(
None, max_length=10, description="Customs agent")
port_of_entry: Optional[str] = Field(
None, max_length=6, description="Port of entry")
destination: Optional[str] = Field(
None, max_length=3, description="Destination code")
manifest_number: Optional[str] = Field(
None, max_length=15, description="Manifest number")
provider_header: Optional[str] = Field(
None, max_length=20, description="Provider header")
provider_id: Optional[str] = Field(
None, description="Provider ID")
sold_to_header: Optional[str] = Field(
None, max_length=20, description="Sold to header")
sold_to_id: Optional[str] = Field(
None, description="Sold to ID")
shipped_to_header: Optional[str] = Field(
None, max_length=20, description="Shipped to header")
shipped_to_id: Optional[str] = Field(
None, description="Shipped to ID")
shipped_by_header: Optional[str] = Field(
None, max_length=20, description="Shipped by header")
shipped_by_id: Optional[str] = Field(
None, description="Shipped by ID")
customs_broker_id: Optional[str] = Field(
None, description="Customs broker ID")
customs_broker_us_id: Optional[str] = Field(
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(
None, 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[str] = Field(
None, max_length=1, 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")
is_pedimento_pending: Optional[bool] = Field(
None, description="Is pedimento pending")
is_owner_of_goods: Optional[str] = Field(
None, max_length=2, description="Is owner of goods")
generate_balances: Optional[str] = Field(
None, max_length=2, 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")
electronic_signature: Optional[str] = Field(
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")
bill_of_lading_count: Optional[str] = Field(
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[str] = Field(
None, max_length=19, 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")
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")
movement_type: Optional[str] = Field(
None, max_length=31, description="Movement type")
office_document: Optional[str] = Field(
None, max_length=30, description="Office document")
reason_export: Optional[str] = Field(
None, max_length=1, description="Reason for export")
signature_key: Optional[str] = Field(
None, max_length=10, description="Signature key")
sem_id: Optional[int] = Field(None, description="SEM ID")
@@ -63,44 +193,148 @@ class InvoiceFinancialsBase(BaseModel):
"""Base fields for Financials"""
currency: Optional[str] = Field(
None, max_length=3, description="Currency code")
currency_type: Optional[str] = Field(
None, description="Currency type")
exchange_rate: Optional[Decimal] = Field(None, description="Exchange rate")
exchange_rate_mm: Optional[Decimal] = Field(
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")
customs_value_mn: Optional[Decimal] = Field(
None, description="Customs value in MXN")
customs_value_me: Optional[Decimal] = Field(
None, description="Customs value in foreign currency")
raw_material_value_mn: Optional[Decimal] = Field(
None, description="Raw material value in MXN")
raw_material_value_me: Optional[Decimal] = Field(
None, description="Raw material value in foreign currency")
aggregate_value_mn: Optional[Decimal] = Field(
None, description="Aggregate value in MXN")
aggregate_value_me: Optional[Decimal] = Field(
None, description="Aggregate value in foreign currency")
aggregate_value_mc: Optional[Decimal] = Field(
None, description="Aggregate value in third currency")
mexican_value_mn: Optional[Decimal] = Field(
None, description="Mexican merchandise value in MXN")
mexican_value_me: Optional[Decimal] = Field(
None, description="Mexican merchandise value in foreign currency")
mexican_value_mc: Optional[Decimal] = Field(
None, description="Mexican merchandise value in third currency")
national_packaging_mn: Optional[Decimal] = Field(
None, description="National packaging in MXN")
national_packaging_me: Optional[Decimal] = Field(
None, description="National packaging in foreign currency")
national_packaging_mc: Optional[Decimal] = Field(
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")
packaging: Optional[Decimal] = Field(None, description="Packaging")
other_increments: Optional[Decimal] = Field(
None, description="Other increments")
total_increments_mn: Optional[Decimal] = Field(
None, description="Total increments in MXN")
total_increments_me: Optional[Decimal] = Field(
None, description="Total increments in foreign currency")
iva_mn: Optional[Decimal] = Field(None, description="IVA in MXN")
iva_factor: Optional[Decimal] = Field(None, description="IVA factor")
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[str] = Field(
None, max_length=10, 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")
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")
weight_factor: Optional[Decimal] = Field(None, description="Weight factor")
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")
transport_us_id: Optional[str] = Field(
None, max_length=10, description="US transport ID")
transport_type: Optional[str] = Field(
None, max_length=15, description="Transport type")
transport_num: Optional[str] = Field(
None, max_length=20, description="Transport number")
transport_mode: Optional[str] = Field(
None, max_length=15, description="Transport mode")
driver_name: Optional[str] = Field(
None, max_length=80, description="Driver name")
is_rail: Optional[str] = Field(
None, max_length=2, description="Is rail transport")
rail_id: Optional[str] = Field(None, max_length=31, description="Rail ID")
rail_id: Optional[str] = Field(
None, max_length=31, description="Rail ID")
vehicle_num: Optional[str] = Field(
None, max_length=20, description="Vehicle number")
license_plate: Optional[str] = Field(
None, max_length=20, description="License plate")
license_plate_complete: Optional[str] = Field(
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")
reference_number: Optional[str] = Field(
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")
weight_type: Optional[str] = Field(
None, max_length=6, description="Weight type")
container_types: Optional[str] = Field(
None, max_length=500, description="Container types")
vehicle_data: Optional[str] = Field(
None, max_length=500, description="Vehicle data")
origin_location: Optional[str] = Field(
None, max_length=200, description="Origin location")
destination_location: Optional[str] = Field(
None, max_length=200, description="Destination location")
transport_itinerary: Optional[str] = Field(
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")
payment_receipt_num: Optional[str] = Field(
None, max_length=20, description="Payment receipt number")
is_ctm_process: Optional[str] = Field(
None, max_length=2, description="Is CTM process")
class InvoiceSalesDetailsBase(BaseModel):
@@ -117,13 +351,10 @@ class InvoiceSalesDetailsBase(BaseModel):
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")
concept: Optional[str] = Field(None, max_length=100, description="Concept")
is_collected: Optional[int] = Field(None, description="Is collected flag")
collection_date: Optional[date] = Field(
None, description="Collection date")
amount: Optional[Decimal] = Field(None, description="Amount")
collector_user: Optional[str] = Field(
None, max_length=20, description="Collector user")
# --- Create Schemas ---
@@ -186,7 +417,7 @@ class InvoiceSalesDetailsUpdate(InvoiceSalesDetailsBase):
class InvoiceCollectionsUpdate(InvoiceCollectionsBase):
"""Schema for updating Collections"""
pass
line_number: Optional[int] = None
class InvoiceHeaderUpdate(InvoiceHeaderBase):
@@ -210,6 +441,7 @@ class InvoiceComplianceMxResponse(InvoiceComplianceMxBase):
class InvoiceFinancialsResponse(InvoiceFinancialsBase):
"""Schema for Financials response"""
id: int
invoice_id: int
class Config:
@@ -218,7 +450,7 @@ class InvoiceFinancialsResponse(InvoiceFinancialsBase):
class InvoiceLogisticsResponse(InvoiceLogisticsBase):
"""Schema for Logistics response"""
logistics_id: int
id: int
invoice_id: int
class Config:
@@ -227,7 +459,7 @@ class InvoiceLogisticsResponse(InvoiceLogisticsBase):
class InvoiceSalesDetailsResponse(InvoiceSalesDetailsBase):
"""Schema for Sales Details response"""
detail_id: int
id: int
invoice_id: int
class Config:
@@ -236,7 +468,7 @@ class InvoiceSalesDetailsResponse(InvoiceSalesDetailsBase):
class InvoiceCollectionsResponse(InvoiceCollectionsBase):
"""Schema for Collections response"""
collection_id: int
id: int
invoice_id: int
class Config: