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:
@@ -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
|
||||
@@ -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:
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
"@eslint/js": "^9.36.0",
|
||||
"@inlang/paraglide-js": "^2.3.2",
|
||||
"@internationalized/date": "^3.10.0",
|
||||
"@lucide/svelte": "^0.544.0",
|
||||
"@lucide/svelte": "^0.561.0",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@sveltejs/adapter-node": "^5.3.2",
|
||||
"@sveltejs/kit": "^2.43.2",
|
||||
@@ -32,7 +32,7 @@
|
||||
"@tanstack/table-core": "^8.21.3",
|
||||
"@types/node": "^20",
|
||||
"@vitest/browser": "^3.2.4",
|
||||
"bits-ui": "^2.14.2",
|
||||
"bits-ui": "^2.14.4",
|
||||
"clsx": "^2.1.1",
|
||||
"eslint": "^9.36.0",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
|
||||
20
frontend/pnpm-lock.yaml
generated
20
frontend/pnpm-lock.yaml
generated
@@ -28,8 +28,8 @@ importers:
|
||||
specifier: ^3.10.0
|
||||
version: 3.10.0
|
||||
'@lucide/svelte':
|
||||
specifier: ^0.544.0
|
||||
version: 0.544.0(svelte@5.40.2)
|
||||
specifier: ^0.561.0
|
||||
version: 0.561.0(svelte@5.40.2)
|
||||
'@playwright/test':
|
||||
specifier: ^1.55.1
|
||||
version: 1.56.1
|
||||
@@ -61,8 +61,8 @@ importers:
|
||||
specifier: ^3.2.4
|
||||
version: 3.2.4(playwright@1.56.1)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1))(vitest@3.2.4)
|
||||
bits-ui:
|
||||
specifier: ^2.14.2
|
||||
version: 2.14.2(@internationalized/date@3.10.0)(@sveltejs/kit@2.47.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)
|
||||
specifier: ^2.14.4
|
||||
version: 2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.47.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)
|
||||
clsx:
|
||||
specifier: ^2.1.1
|
||||
version: 2.1.1
|
||||
@@ -407,8 +407,8 @@ packages:
|
||||
'@lix-js/server-protocol-schema@0.1.1':
|
||||
resolution: {integrity: sha512-jBeALB6prAbtr5q4vTuxnRZZv1M2rKe8iNqRQhFJ4Tv7150unEa0vKyz0hs8Gl3fUGsWaNJBh3J8++fpbrpRBQ==}
|
||||
|
||||
'@lucide/svelte@0.544.0':
|
||||
resolution: {integrity: sha512-9f9O6uxng2pLB01sxNySHduJN3HTl5p0HDu4H26VR51vhZfiMzyOMe9Mhof3XAk4l813eTtl+/DYRvGyoRR+yw==}
|
||||
'@lucide/svelte@0.561.0':
|
||||
resolution: {integrity: sha512-vofKV2UFVrKE6I4ewKJ3dfCXSV6iP6nWVmiM83MLjsU91EeJcEg7LoWUABLp/aOTxj1HQNbJD1f3g3L0JQgH9A==}
|
||||
peerDependencies:
|
||||
svelte: ^5
|
||||
|
||||
@@ -919,8 +919,8 @@ packages:
|
||||
balanced-match@1.0.2:
|
||||
resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
|
||||
|
||||
bits-ui@2.14.2:
|
||||
resolution: {integrity: sha512-YqpAJj/nRTZjf7IlgUC3QlepVZ7YFiAQWpZaYUOAZFW5Py+g5DYkhEDTdNFI5SReo7l1rct/nRpMK4pfL9Xffw==}
|
||||
bits-ui@2.14.4:
|
||||
resolution: {integrity: sha512-W6kenhnbd/YVvur+DKkaVJ6GldE53eLewur5AhUCqslYQ0vjZr8eWlOfwZnMiPB+PF5HMVqf61vXBvmyrAmPWg==}
|
||||
engines: {node: '>=20'}
|
||||
peerDependencies:
|
||||
'@internationalized/date': ^3.8.1
|
||||
@@ -2240,7 +2240,7 @@ snapshots:
|
||||
|
||||
'@lix-js/server-protocol-schema@0.1.1': {}
|
||||
|
||||
'@lucide/svelte@0.544.0(svelte@5.40.2)':
|
||||
'@lucide/svelte@0.561.0(svelte@5.40.2)':
|
||||
dependencies:
|
||||
svelte: 5.40.2
|
||||
|
||||
@@ -2735,7 +2735,7 @@ snapshots:
|
||||
|
||||
balanced-match@1.0.2: {}
|
||||
|
||||
bits-ui@2.14.2(@internationalized/date@3.10.0)(@sveltejs/kit@2.47.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2):
|
||||
bits-ui@2.14.4(@internationalized/date@3.10.0)(@sveltejs/kit@2.47.1(@sveltejs/vite-plugin-svelte@6.2.1(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2)(vite@7.1.10(@types/node@20.19.22)(jiti@2.6.1)(lightningcss@1.30.1)))(svelte@5.40.2):
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.7.3
|
||||
'@floating-ui/dom': 1.7.4
|
||||
|
||||
@@ -13,8 +13,12 @@ export interface InvoiceComplianceMx {
|
||||
invoice_id?: number;
|
||||
pedimento?: string | null;
|
||||
pedimento_code?: string | null;
|
||||
pedimento_k1?: string | null;
|
||||
remesa?: number | null;
|
||||
aduana?: string | null;
|
||||
aduana?: string | null;
|
||||
port_of_entry?: string | null;
|
||||
destination?: string | null;
|
||||
manifest_number?: string | null;
|
||||
provider_header?: string | null;
|
||||
provider_id?: string | null;
|
||||
sold_to_header?: string | null;
|
||||
@@ -24,11 +28,42 @@ export interface InvoiceComplianceMx {
|
||||
shipped_by_header?: string | null;
|
||||
shipped_by_id?: string | null;
|
||||
customs_broker_id?: string | null;
|
||||
customs_broker_us_id?: string | null;
|
||||
broker_invoice_num?: string | null;
|
||||
broker_invoice_date?: string | null;
|
||||
is_mixed?: boolean | null;
|
||||
waste_type?: string | null;
|
||||
scrap_type?: string | null;
|
||||
appendix_17?: number | null;
|
||||
is_regime_change?: string | null;
|
||||
which_exchange_rate?: string | null;
|
||||
value_method?: string | null;
|
||||
act_value?: string | null;
|
||||
is_pedimento_pending?: boolean | null;
|
||||
is_owner_of_goods?: string | null;
|
||||
generate_balances?: string | null;
|
||||
was_reviewed_by_company?: boolean | null;
|
||||
edocument?: string | null;
|
||||
code_signature?: string | null;
|
||||
electronic_signature?: string | null;
|
||||
certificate_number?: string | null;
|
||||
niu_number?: string | null;
|
||||
bill_of_lading_count?: string | null;
|
||||
addendum_vu?: string | null;
|
||||
origin_destination_cove?: string | null;
|
||||
vucem_operation_num?: string | null;
|
||||
customs_person_line?: number | null;
|
||||
contingency_mode?: boolean | null;
|
||||
enclosure?: string | null;
|
||||
guide_type_to_identify?: string | null;
|
||||
location?: string | null;
|
||||
dot_code?: string | null;
|
||||
subdivision?: string | null;
|
||||
acts_as?: string | null;
|
||||
movement_type?: string | null;
|
||||
office_document?: string | null;
|
||||
reason_export?: string | null;
|
||||
signature_key?: string | null;
|
||||
sem_id?: number | null;
|
||||
}
|
||||
|
||||
@@ -38,33 +73,83 @@ export interface InvoiceFinancials {
|
||||
currency?: string | null;
|
||||
currency_type?: string | null;
|
||||
exchange_rate?: number | null;
|
||||
exchange_rate_mm?: number | null;
|
||||
value_mn?: number | null;
|
||||
value_me?: number | null;
|
||||
value_mc?: number | null;
|
||||
customs_value_mn?: number | null;
|
||||
customs_value_me?: number | null;
|
||||
raw_material_value_mn?: number | null;
|
||||
raw_material_value_me?: number | null;
|
||||
aggregate_value_mn?: number | null;
|
||||
aggregate_value_me?: number | null;
|
||||
aggregate_value_mc?: number | null;
|
||||
mexican_value_mn?: number | null;
|
||||
mexican_value_me?: number | null;
|
||||
mexican_value_mc?: number | null;
|
||||
national_packaging_mn?: number | null;
|
||||
national_packaging_me?: number | null;
|
||||
national_packaging_mc?: number | null;
|
||||
freight?: number | null;
|
||||
insurance?: number | null;
|
||||
insurance_value?: number | null;
|
||||
packaging?: number | null;
|
||||
other_increments?: number | null;
|
||||
total_increments_mn?: number | null;
|
||||
total_increments_me?: number | null;
|
||||
iva_mn?: number | null;
|
||||
iva_factor?: number | null;
|
||||
iva_me?: number | null;
|
||||
iva_mc?: number | null;
|
||||
iva_factor?: string | null;
|
||||
tax_value_me?: number | null;
|
||||
seal_value_2500?: boolean | null;
|
||||
total_quantity?: number | null;
|
||||
gross_weight?: number | null;
|
||||
net_weight?: number | null;
|
||||
bundle_count?: number | null;
|
||||
weight_factor?: number | null;
|
||||
}
|
||||
|
||||
export interface InvoiceLogistics {
|
||||
id?: number;
|
||||
invoice_id?: number;
|
||||
carrier_id?: string | null;
|
||||
transport_id?: string | null;
|
||||
transport_us_id?: string | null;
|
||||
transport_type?: TransportType | null;
|
||||
transport_num?: string | null;
|
||||
transport_mode?: string | null;
|
||||
driver_name?: string | null;
|
||||
is_rail?: string | null;
|
||||
rail_id?: string | null;
|
||||
vehicle_num?: string | null;
|
||||
license_plate?: string | null;
|
||||
license_plate_complete?: string | null;
|
||||
trailer_num?: string | null;
|
||||
seal_number?: string | null;
|
||||
guide_number?: string | null;
|
||||
bill_number?: string | null;
|
||||
reference_number?: string | null;
|
||||
shipment_number?: string | null;
|
||||
incoterm?: string | null;
|
||||
identifier_1?: string | null;
|
||||
complement_1?: string | null;
|
||||
identifier_2?: string | null;
|
||||
complement_2?: string | null;
|
||||
weight_type?: string | null;
|
||||
container_types?: string | null;
|
||||
vehicle_data?: string | null;
|
||||
origin_location?: string | null;
|
||||
destination_location?: string | null;
|
||||
transport_itinerary?: string | null;
|
||||
destination_goods?: string | null;
|
||||
entry_exit_date?: string | null;
|
||||
delivery_date?: string | null;
|
||||
delivered_status?: string | null;
|
||||
received_by?: string | null;
|
||||
payment_date?: string | null;
|
||||
payment_receipt_num?: string | null;
|
||||
is_ctm_process?: string | null;
|
||||
}
|
||||
|
||||
export interface InvoiceSalesDetails {
|
||||
@@ -91,25 +176,47 @@ export interface Invoice {
|
||||
id: number;
|
||||
tenant_id: number;
|
||||
company_id: number;
|
||||
system?: string | null;
|
||||
operation_type?: OperationType | null;
|
||||
invoice_type?: string | null;
|
||||
invoice_number?: string | null;
|
||||
project_number?: string | null;
|
||||
purchase_order?: string | null;
|
||||
related_doc_id?: number | null;
|
||||
alternate_invoice?: string | null;
|
||||
invoice_ref?: string | null;
|
||||
proforma_number?: string | null;
|
||||
invoice_date?: string | null;
|
||||
capture_date: string;
|
||||
emission_date?: string | null;
|
||||
is_updated?: boolean | null;
|
||||
updated_date?: string | null;
|
||||
who_updated?: string | null;
|
||||
capture_user?: string | null;
|
||||
traffic_light_status?: string | null;
|
||||
process_log?: string | null;
|
||||
status_rec?: number | null;
|
||||
status_rep?: string | null;
|
||||
observation_es?: string | null;
|
||||
observation_en?: string | null;
|
||||
comments_status?: string | null;
|
||||
vu_observations?: string | null;
|
||||
cfdi_uuid?: string | null;
|
||||
path_pdf?: string | null;
|
||||
path_xml?: string | null;
|
||||
subcompany?: string | null;
|
||||
party_count?: number | null;
|
||||
generate_id?: string | null;
|
||||
generate_desc_parties?: string | null;
|
||||
apply_manual_discount?: string | null;
|
||||
is_bulk?: boolean | null;
|
||||
download_substance?: boolean | null;
|
||||
download_class?: boolean | null;
|
||||
download_def?: boolean | null;
|
||||
payment_terms?: string | null;
|
||||
handling_fees?: number | null;
|
||||
option_iv18?: string | null;
|
||||
enajenation_goods?: boolean | null;
|
||||
compliance_mx?: InvoiceComplianceMx | null;
|
||||
financials?: InvoiceFinancials | null;
|
||||
logistics?: InvoiceLogistics[];
|
||||
@@ -125,21 +232,46 @@ export interface InvoiceListResponse {
|
||||
}
|
||||
|
||||
export interface CreateInvoiceData {
|
||||
system?: string | null;
|
||||
operation_type?: OperationType | null;
|
||||
invoice_type?: string | null;
|
||||
invoice_number?: string | null;
|
||||
project_number?: string | null;
|
||||
purchase_order?: string | null;
|
||||
related_doc_id?: number | null;
|
||||
alternate_invoice?: string | null;
|
||||
invoice_ref?: string | null;
|
||||
proforma_number?: string | null;
|
||||
invoice_date?: string | null;
|
||||
emission_date?: string | null;
|
||||
is_updated?: boolean | null;
|
||||
updated_date?: string | null;
|
||||
who_updated?: string | null;
|
||||
capture_user?: string | null;
|
||||
traffic_light_status?: string | null;
|
||||
process_log?: string | null;
|
||||
status_rec?: number | null;
|
||||
status_rep?: string | null;
|
||||
observation_es?: string | null;
|
||||
observation_en?: string | null;
|
||||
comments_status?: string | null;
|
||||
vu_observations?: string | null;
|
||||
cfdi_uuid?: string | null;
|
||||
path_pdf?: string | null;
|
||||
path_xml?: string | null;
|
||||
subcompany?: string | null;
|
||||
party_count?: number | null;
|
||||
generate_id?: string | null;
|
||||
generate_desc_parties?: string | null;
|
||||
apply_manual_discount?: string | null;
|
||||
is_bulk?: boolean | null;
|
||||
download_substance?: boolean | null;
|
||||
download_class?: boolean | null;
|
||||
download_def?: boolean | null;
|
||||
payment_terms?: string | null;
|
||||
handling_fees?: number | null;
|
||||
option_iv18?: string | null;
|
||||
enajenation_goods?: boolean | null;
|
||||
compliance_mx?: Omit<InvoiceComplianceMx, 'invoice_id'> | null;
|
||||
financials?: Omit<InvoiceFinancials, 'id' | 'invoice_id'> | null;
|
||||
logistics?: Omit<InvoiceLogistics, 'id' | 'invoice_id'>[] | null;
|
||||
|
||||
@@ -1,13 +1 @@
|
||||
<svg width="64" height="64" viewBox="0 0 64 64" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<defs>
|
||||
<linearGradient id="techGradient" x1="16" y1="16" x2="48" y2="48" gradientUnits="userSpaceOnUse">
|
||||
<stop offset="0%" stop-color="#00F2FE" /> <stop offset="100%" stop-color="#4FACFE" /> </linearGradient>
|
||||
</defs>
|
||||
|
||||
<rect width="64" height="64" rx="18" fill="#0F172A"/>
|
||||
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32 14L46 26V40L32 52L18 40V26L32 14ZM32 20.5L23 28.2V35.8L32 43.5L41 35.8V28.2L32 20.5Z" fill="url(#techGradient)"/>
|
||||
|
||||
<path d="M32 20.5V30M32 34V43.5" stroke="#0F172A" stroke-width="2" stroke-linecap="round"/>
|
||||
<path d="M23 35.8L32 30M41 35.8L32 30" stroke="#0F172A" stroke-width="2" stroke-linecap="round"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="64" height="64" fill="none" viewBox="0 0 64 64"><defs><linearGradient id="techGradient" x1="16" x2="48" y1="16" y2="48" gradientUnits="userSpaceOnUse"><stop offset="0%" stop-color="#00f2fe"/><stop offset="100%" stop-color="#4facfe"/></linearGradient></defs><rect width="64" height="64" fill="#0f172a" rx="18"/><path fill="url(#techGradient)" fill-rule="evenodd" d="m32 14 14 12v14L32 52 18 40V26zm0 6.5-9 7.7v7.6l9 7.7 9-7.7v-7.6z" clip-rule="evenodd"/><path stroke="#0f172a" stroke-linecap="round" stroke-width="2" d="M32 20.5V30m0 4v9.5m-9-7.7 9-5.8m9 5.8L32 30"/></svg>
|
||||
|
Before Width: | Height: | Size: 781 B After Width: | Height: | Size: 619 B |
@@ -1,60 +0,0 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable(),
|
||||
customsBrokers = [],
|
||||
clients = [],
|
||||
providers = []
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
customsBrokers?: any[];
|
||||
clients?: any[];
|
||||
providers?: any[];
|
||||
} = $props();
|
||||
|
||||
if (!formData && invoice?.compliance_mx) {
|
||||
formData = { ...invoice.compliance_mx };
|
||||
exists = true;
|
||||
} else if (!formData) {
|
||||
formData = {
|
||||
pedimento: '',
|
||||
pedimento_code: '',
|
||||
remesa: null,
|
||||
aduana: '',
|
||||
provider_id: '',
|
||||
sold_to_id: '',
|
||||
shipped_to_id: '',
|
||||
shipped_by_id: '',
|
||||
customs_broker_id: ''
|
||||
};
|
||||
exists = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Cumplimiento Aduanal</Card.Title>
|
||||
<Card.Description>Información de cumplimiento y aduanas</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="pedimento">Pedimento</Label>
|
||||
<Input id="pedimento" bind:value={formData.pedimento} placeholder="Número de pedimento" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="aduana">Aduana</Label>
|
||||
<Input id="aduana" bind:value={formData.aduana} placeholder="Código de aduana" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground">Más campos por implementar...</p>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
@@ -0,0 +1,298 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import { RadioGroup, RadioGroupItem } from '$lib/components/ui/radio-group';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable()
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
} = $props();
|
||||
|
||||
if (!formData && invoice?.financials) {
|
||||
formData = {
|
||||
// Currency & Exchange
|
||||
currency: invoice.financials.currency || '',
|
||||
currency_type: invoice.financials.currency_type || '',
|
||||
exchange_rate: invoice.financials.exchange_rate || null,
|
||||
exchange_rate_mm: invoice.financials.exchange_rate_mm || null,
|
||||
// Values
|
||||
value_mn: invoice.financials.value_mn || null,
|
||||
value_me: invoice.financials.value_me || null,
|
||||
value_mc: invoice.financials.value_mc || null,
|
||||
customs_value_mn: invoice.financials.customs_value_mn || null,
|
||||
customs_value_me: invoice.financials.customs_value_me || null,
|
||||
// Raw materials
|
||||
raw_material_value_mn: invoice.financials.raw_material_value_mn || null,
|
||||
raw_material_value_me: invoice.financials.raw_material_value_me || null,
|
||||
// Aggregate values
|
||||
aggregate_value_mn: invoice.financials.aggregate_value_mn || null,
|
||||
aggregate_value_me: invoice.financials.aggregate_value_me || null,
|
||||
aggregate_value_mc: invoice.financials.aggregate_value_mc || null,
|
||||
// Mexican values
|
||||
mexican_value_mn: invoice.financials.mexican_value_mn || null,
|
||||
mexican_value_me: invoice.financials.mexican_value_me || null,
|
||||
mexican_value_mc: invoice.financials.mexican_value_mc || null,
|
||||
// National packaging
|
||||
national_packaging_mn: invoice.financials.national_packaging_mn || null,
|
||||
national_packaging_me: invoice.financials.national_packaging_me || null,
|
||||
national_packaging_mc: invoice.financials.national_packaging_mc || null,
|
||||
// Costs & increments
|
||||
freight: invoice.financials.freight || null,
|
||||
insurance: invoice.financials.insurance || null,
|
||||
insurance_value: invoice.financials.insurance_value || null,
|
||||
packaging: invoice.financials.packaging || null,
|
||||
other_increments: invoice.financials.other_increments || null,
|
||||
total_increments_mn: invoice.financials.total_increments_mn || null,
|
||||
total_increments_me: invoice.financials.total_increments_me || null,
|
||||
// Taxes
|
||||
iva_mn: invoice.financials.iva_mn || null,
|
||||
iva_me: invoice.financials.iva_me || null,
|
||||
iva_mc: invoice.financials.iva_mc || null,
|
||||
iva_factor: invoice.financials.iva_factor || '',
|
||||
tax_value_me: invoice.financials.tax_value_me || null,
|
||||
seal_value_2500: invoice.financials.seal_value_2500 || false,
|
||||
// Weights & quantities
|
||||
total_quantity: invoice.financials.total_quantity || null,
|
||||
gross_weight: invoice.financials.gross_weight || null,
|
||||
net_weight: invoice.financials.net_weight || null,
|
||||
bundle_count: invoice.financials.bundle_count || null,
|
||||
weight_factor: invoice.financials.weight_factor || null,
|
||||
// Additional fields not in backend
|
||||
numero_tipo_transporte: '',
|
||||
es_ferrocarril: 'no',
|
||||
numero_bl: '',
|
||||
cantidad_guias_embarque: null,
|
||||
destino_origen: '',
|
||||
puerto_entrada: '',
|
||||
// Checkboxes
|
||||
fue_revisado_equipo: false,
|
||||
sub_division: false,
|
||||
funge_como_cd: false,
|
||||
llego_pedimento: false,
|
||||
// Errores
|
||||
errores_facturacion: [],
|
||||
// Semáforos
|
||||
semaforo_verde_aduana_mexicana: false,
|
||||
semaforo_verde_aduana_americana: false,
|
||||
semaforo_rojo_aduana_mexicana: false,
|
||||
semaforo_rojo_aduana_americana: false
|
||||
};
|
||||
exists = true;
|
||||
} else if (!formData) {
|
||||
formData = {
|
||||
// Currency & Exchange
|
||||
currency: '',
|
||||
currency_type: '',
|
||||
exchange_rate: null,
|
||||
exchange_rate_mm: null,
|
||||
// Values
|
||||
value_mn: null,
|
||||
value_me: null,
|
||||
value_mc: null,
|
||||
customs_value_mn: null,
|
||||
customs_value_me: null,
|
||||
// Raw materials
|
||||
raw_material_value_mn: null,
|
||||
raw_material_value_me: null,
|
||||
// Aggregate values
|
||||
aggregate_value_mn: null,
|
||||
aggregate_value_me: null,
|
||||
aggregate_value_mc: null,
|
||||
// Mexican values
|
||||
mexican_value_mn: null,
|
||||
mexican_value_me: null,
|
||||
mexican_value_mc: null,
|
||||
// National packaging
|
||||
national_packaging_mn: null,
|
||||
national_packaging_me: null,
|
||||
national_packaging_mc: null,
|
||||
// Costs & increments
|
||||
freight: null,
|
||||
insurance: null,
|
||||
insurance_value: null,
|
||||
packaging: null,
|
||||
other_increments: null,
|
||||
total_increments_mn: null,
|
||||
total_increments_me: null,
|
||||
// Taxes
|
||||
iva_mn: null,
|
||||
iva_me: null,
|
||||
iva_mc: null,
|
||||
iva_factor: '',
|
||||
tax_value_me: null,
|
||||
seal_value_2500: false,
|
||||
// Weights & quantities
|
||||
total_quantity: null,
|
||||
gross_weight: null,
|
||||
net_weight: null,
|
||||
bundle_count: null,
|
||||
weight_factor: null,
|
||||
// Additional fields not in backend
|
||||
numero_tipo_transporte: '',
|
||||
es_ferrocarril: 'no',
|
||||
numero_bl: '',
|
||||
cantidad_guias_embarque: null,
|
||||
destino_origen: '',
|
||||
puerto_entrada: '',
|
||||
// Checkboxes
|
||||
fue_revisado_equipo: false,
|
||||
sub_division: false,
|
||||
funge_como_cd: false,
|
||||
llego_pedimento: false,
|
||||
// Errores
|
||||
errores_facturacion: [],
|
||||
// Semáforos
|
||||
semaforo_verde_aduana_mexicana: false,
|
||||
semaforo_verde_aduana_americana: false,
|
||||
semaforo_rojo_aduana_mexicana: false,
|
||||
semaforo_rojo_aduana_americana: false
|
||||
};
|
||||
exists = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Layout de 2 columnas compacto -->
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<!-- Columna Izquierda -->
|
||||
<div class="border rounded-md p-3 space-y-3">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Información General</h4>
|
||||
|
||||
<!-- NÚMERO/TIPO DE TRANSPORTE -->
|
||||
<div class="space-y-1.5">
|
||||
<Label for="numero_tipo_transporte" class="text-xs">Número/Tipo de Transporte:</Label>
|
||||
<Input id="numero_tipo_transporte" bind:value={formData.numero_tipo_transporte} class="h-7 text-xs" />
|
||||
</div>
|
||||
|
||||
<!-- DATOS VEHÍCULO -->
|
||||
<div class="border rounded p-2 space-y-2 bg-muted/30">
|
||||
<Label class="text-xs font-semibold">Datos Vehículo:</Label>
|
||||
<div class="space-y-1.5">
|
||||
<Label class="text-xs">Es Ferrocarril?</Label>
|
||||
<RadioGroup bind:value={formData.es_ferrocarril} class="flex gap-4">
|
||||
<div class="flex items-center gap-2">
|
||||
<RadioGroupItem value="si" id="ferrocarril_si" />
|
||||
<Label for="ferrocarril_si" class="text-xs">SI</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<RadioGroupItem value="no" id="ferrocarril_no" />
|
||||
<Label for="ferrocarril_no" class="text-xs">NO</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="numero_bl" class="text-xs">Número BL:</Label>
|
||||
<Input id="numero_bl" bind:value={formData.numero_bl} class="h-7 text-xs" />
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="cantidad_guias_embarque" class="text-xs">Cantidad de Guías de Embarque (BL):</Label>
|
||||
<Input id="cantidad_guias_embarque" type="number" bind:value={formData.cantidad_guias_embarque} class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- DESTINO/ORIGEN Y PUERTO ENTRADA -->
|
||||
<div class="space-y-1.5">
|
||||
<Label for="destino_origen" class="text-xs">Destino/Origen:</Label>
|
||||
<Input id="destino_origen" bind:value={formData.destino_origen} placeholder="FRANJA FRONT." class="h-7 text-xs" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="puerto_entrada" class="text-xs">Puerto Entrada:</Label>
|
||||
<Input id="puerto_entrada" bind:value={formData.puerto_entrada} class="h-7 text-xs" />
|
||||
</div>
|
||||
|
||||
<!-- CHECKBOXES -->
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="fue_revisado" bind:checked={formData.fue_revisado_equipo} />
|
||||
<Label for="fue_revisado" class="text-xs">Fue Revisado el Equipo</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="sub_division" bind:checked={formData.sub_division} />
|
||||
<Label for="sub_division" class="text-xs">Sub División</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="funge_como_cd" bind:checked={formData.funge_como_cd} />
|
||||
<Label for="funge_como_cd" class="text-xs">Funge Como CD</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<Checkbox id="llego_pedimento" bind:checked={formData.llego_pedimento} />
|
||||
<Label for="llego_pedimento" class="text-xs">Llegó el Pedimento</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Columna Derecha - Errores de Facturación -->
|
||||
<div class="border rounded-md p-3 space-y-3">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Errores de Facturación</h4>
|
||||
|
||||
<div class="border rounded">
|
||||
<table class="w-full text-xs">
|
||||
<thead class="bg-muted">
|
||||
<tr>
|
||||
<th class="border px-2 py-1 text-left">Línea</th>
|
||||
<th class="border px-2 py-1 text-left">Clave</th>
|
||||
<th class="border px-2 py-1 text-left">Descripción</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#if formData.errores_facturacion?.length}
|
||||
{#each formData.errores_facturacion as error}
|
||||
<tr>
|
||||
<td class="border px-2 py-1">{error.linea}</td>
|
||||
<td class="border px-2 py-1">{error.clave}</td>
|
||||
<td class="border px-2 py-1">{error.descripcion}</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{:else}
|
||||
<tr>
|
||||
<td colspan="3" class="border px-2 py-12 text-center text-muted-foreground">
|
||||
Sin errores registrados
|
||||
</td>
|
||||
</tr>
|
||||
{/if}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end gap-2">
|
||||
<Button variant="outline" size="sm" class="h-7 text-xs">Insertar</Button>
|
||||
<Button variant="outline" size="sm" class="h-7 text-xs">Editar</Button>
|
||||
<Button variant="outline" size="sm" class="h-7 text-xs">Borrar</Button>
|
||||
</div>
|
||||
|
||||
<!-- SEMÁFORO -->
|
||||
<div class="border rounded p-2 space-y-2 bg-muted/30">
|
||||
<Label class="text-xs font-semibold">Semáforo</Label>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-3 h-3 rounded-full bg-green-500"></div>
|
||||
<Checkbox id="verde_mex" bind:checked={formData.semaforo_verde_aduana_mexicana} />
|
||||
<Label for="verde_mex" class="text-xs">Verde MX</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-3 h-3 rounded-full bg-green-600"></div>
|
||||
<Checkbox id="verde_usa" bind:checked={formData.semaforo_verde_aduana_americana} />
|
||||
<Label for="verde_usa" class="text-xs">Verde USA</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-3 h-3 rounded-full bg-red-500"></div>
|
||||
<Checkbox id="rojo_mex" bind:checked={formData.semaforo_rojo_aduana_mexicana} />
|
||||
<Label for="rojo_mex" class="text-xs">Rojo MX</Label>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<div class="w-3 h-3 rounded-full bg-red-600"></div>
|
||||
<Checkbox id="rojo_usa" bind:checked={formData.semaforo_rojo_aduana_americana} />
|
||||
<Label for="rojo_usa" class="text-xs">Rojo USA</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,53 +0,0 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable()
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
} = $props();
|
||||
|
||||
if (!formData && invoice?.financials) {
|
||||
formData = { ...invoice.financials };
|
||||
exists = true;
|
||||
} else if (!formData) {
|
||||
formData = {
|
||||
currency: '',
|
||||
exchange_rate: null,
|
||||
value_mn: null,
|
||||
value_me: null,
|
||||
freight: null,
|
||||
insurance: null,
|
||||
gross_weight: null,
|
||||
net_weight: null
|
||||
};
|
||||
exists = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Información Financiera</Card.Title>
|
||||
<Card.Description>Valores, monedas y datos financieros</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-4">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="value_mn">Valor MN</Label>
|
||||
<Input id="value_mn" type="number" step="0.01" bind:value={formData.value_mn} placeholder="0.00" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label for="value_me">Valor ME</Label>
|
||||
<Input id="value_me" type="number" step="0.01" bind:value={formData.value_me} placeholder="0.00" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-sm text-muted-foreground">Más campos por implementar...</p>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
@@ -1,22 +1,32 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
import type { InvoiceType } from '$lib/api/dashboard/refrence_data/invoice_types';
|
||||
import type { CustomsBroker } from '$lib/api/dashboard/a76/customs-brokers';
|
||||
import type { ClientProvider } from '$lib/api/dashboard/a76/clients-providers';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
invoiceTypes = [],
|
||||
customsBrokers = [],
|
||||
clients = [],
|
||||
providers = [],
|
||||
currencyTypes = [],
|
||||
transportTypes = [],
|
||||
defaultOperationType = undefined,
|
||||
defaultInvoiceType = undefined
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
invoiceTypes?: InvoiceType[];
|
||||
customsBrokers?: CustomsBroker[];
|
||||
clients?: ClientProvider[];
|
||||
providers?: any[];
|
||||
currencyTypes?: any[];
|
||||
transportTypes?: any[];
|
||||
defaultOperationType?: number | null;
|
||||
defaultInvoiceType?: string | null;
|
||||
} = $props();
|
||||
@@ -24,207 +34,477 @@
|
||||
if (!formData) {
|
||||
if (invoice) {
|
||||
// Editando una factura existente
|
||||
// Convertir operation_type de string ('imp'/'exp') a número (1/2)
|
||||
let operationType: number | null = null;
|
||||
if (invoice.operation_type) {
|
||||
operationType = invoice.operation_type === 'exp' ? 1 : 2;
|
||||
}
|
||||
|
||||
formData = {
|
||||
operation_type: operationType,
|
||||
invoice_type: invoice.invoice_type || '',
|
||||
// TOP fields
|
||||
is_pedimento_pending: false,
|
||||
pedimento: invoice.compliance_mx?.pedimento || '',
|
||||
remesa: invoice.compliance_mx?.remesa || '',
|
||||
invoice_number: invoice.invoice_number || '',
|
||||
invoice_date: invoice.invoice_date || '',
|
||||
project_number: invoice.project_number || '',
|
||||
traffic_light_status: invoice.traffic_light_status || 'green',
|
||||
observation_es: invoice.observation_es || ''
|
||||
emission_date: '',
|
||||
|
||||
// Extra fields
|
||||
operation_type: operationType,
|
||||
|
||||
// LEFT fields
|
||||
provider_header: invoice.compliance_mx?.provider_header || '',
|
||||
provider_id: invoice.compliance_mx?.provider_id || null,
|
||||
sold_to_header: invoice.compliance_mx?.sold_to_header || '',
|
||||
sold_to_id: invoice.compliance_mx?.sold_to_id || null,
|
||||
shipped_to_header: invoice.compliance_mx?.shipped_to_header || '',
|
||||
shipped_to_id: invoice.compliance_mx?.shipped_to_id || null,
|
||||
customs_broker_id: invoice.compliance_mx?.customs_broker_id || null,
|
||||
customs_broker_us_id: null,
|
||||
|
||||
// RIGHT fields
|
||||
currency_type: invoice.financials?.currency_type || '',
|
||||
weight_type: '',
|
||||
iva_factor: invoice.financials?.iva_factor || null,
|
||||
carrier_id: invoice.logistics?.[0]?.carrier_id || null,
|
||||
transport_id: '',
|
||||
driver_name: invoice.logistics?.[0]?.driver_name || '',
|
||||
transport_type: invoice.logistics?.[0]?.transport_type || '',
|
||||
transport_num: invoice.logistics?.[0]?.vehicle_num || '',
|
||||
aduana: invoice.compliance_mx?.aduana || '',
|
||||
invoice_type: invoice.invoice_type || '',
|
||||
};
|
||||
} else {
|
||||
// Creando una nueva factura - usar valores por defecto de los filtros si están disponibles
|
||||
// Creando una nueva factura
|
||||
formData = {
|
||||
operation_type: defaultOperationType ?? null,
|
||||
invoice_type: defaultInvoiceType ?? '',
|
||||
// TOP fields
|
||||
is_pedimento_pending: false,
|
||||
pedimento: '',
|
||||
remesa: '',
|
||||
invoice_number: '',
|
||||
invoice_date: '',
|
||||
project_number: '',
|
||||
traffic_light_status: 'green',
|
||||
observation_es: ''
|
||||
emission_date: '',
|
||||
|
||||
// Extra fields
|
||||
operation_type: defaultOperationType ?? null,
|
||||
|
||||
// LEFT fields
|
||||
provider_header: '',
|
||||
provider_id: null,
|
||||
sold_to_header: '',
|
||||
sold_to_id: null,
|
||||
shipped_to_header: '',
|
||||
shipped_to_id: null,
|
||||
customs_broker_id: null,
|
||||
customs_broker_us_id: null,
|
||||
|
||||
// RIGHT fields
|
||||
currency_type: '',
|
||||
weight_type: '',
|
||||
iva_factor: null,
|
||||
carrier_id: null,
|
||||
transport_id: '',
|
||||
driver_name: '',
|
||||
transport_type: '',
|
||||
transport_num: '',
|
||||
aduana: '',
|
||||
invoice_type: defaultInvoiceType ?? '',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Todas las opciones de tipo de factura disponibles con su campo de operación
|
||||
const allInvoiceTypeOptions = invoiceTypes.map(t => ({
|
||||
key: t.key,
|
||||
description: t.description,
|
||||
operation: t.operation // 'imp', 'exp', 'both'
|
||||
}));
|
||||
|
||||
// Filtrar tipos de factura basados en operation_type
|
||||
const filteredInvoiceTypes = $derived(() => {
|
||||
if (formData.operation_type === null) {
|
||||
// Mostrar todos
|
||||
return allInvoiceTypeOptions;
|
||||
}
|
||||
|
||||
// 1 = Exportación, 2 = Importación
|
||||
const targetOp = formData.operation_type === 1 ? 'exp' : 'imp';
|
||||
|
||||
return allInvoiceTypeOptions.filter(t =>
|
||||
t.operation === 'both' || t.operation === targetOp
|
||||
);
|
||||
});
|
||||
|
||||
// Limpiar invoice_type si ya no es válido para operation_type
|
||||
$effect(() => {
|
||||
if (!formData.invoice_type) return;
|
||||
|
||||
const isValid = filteredInvoiceTypes().some(t => t.key === formData.invoice_type);
|
||||
if (!isValid) {
|
||||
formData.invoice_type = '';
|
||||
}
|
||||
});
|
||||
|
||||
const operationOptions = [
|
||||
{ value: 1, label: 'Exportación' },
|
||||
{ value: 2, label: 'Importación' },
|
||||
// Opciones de tipo de peso
|
||||
const weightTypeOptions = [
|
||||
{ value: 'kg', label: 'Kilogramos (kg)' },
|
||||
{ value: 'lb', label: 'Libras (lb)' }
|
||||
];
|
||||
|
||||
const trafficLightOptions = [
|
||||
{ value: 'green', label: 'Verde' },
|
||||
{ value: 'yellow', label: 'Amarillo' },
|
||||
{ value: 'red', label: 'Rojo' }
|
||||
// Opciones de encabezados
|
||||
const providerHeaderOptions = [
|
||||
{ value: 'proveedor', label: 'Proveedor' },
|
||||
{ value: 'exportador', label: 'Exportador' }
|
||||
];
|
||||
|
||||
const soldToHeaderOptions = $derived([
|
||||
{ value: 'consignado_a', label: 'Consignado a' },
|
||||
{ value: 'vendido_a', label: 'Vendido a' },
|
||||
{ value: formData.operation_type === 1 ? 'exportado_a' : 'importador', label: formData.operation_type === 1 ? 'Exportado a' : 'Importador' }
|
||||
]);
|
||||
|
||||
const shippedToHeaderOptions = $derived(
|
||||
formData.operation_type === 1
|
||||
? [
|
||||
{ value: 'enviado_por', label: 'Enviado Por' },
|
||||
{ value: 'destinatario', label: 'Destinatario' },
|
||||
{ value: 'vendido_por', label: 'Vendido Por' },
|
||||
{ value: 'consignado_a', label: 'Consignado a' },
|
||||
{ value: 'vendido_a', label: 'Vendido a' },
|
||||
{ value: 'exportado_a', label: 'Exportado a' },
|
||||
{ value: 'enviado_a', label: 'Enviado a' },
|
||||
{ value: 'transferido_a', label: 'Transferido a' },
|
||||
{ value: 'donado_a', label: 'Donado a' },
|
||||
{ value: 'notificar_a', label: 'Notificar a' }
|
||||
]
|
||||
: [
|
||||
{ value: 'enviado_a', label: 'Enviado a' },
|
||||
{ value: 'transferido_a', label: 'Transferido a' }
|
||||
]
|
||||
);
|
||||
|
||||
// Combinar clientes y proveedores para shipped_to
|
||||
const allClientsProviders = [...clients, ...providers];
|
||||
</script>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<Card.Title>Información General</Card.Title>
|
||||
<Card.Description>
|
||||
Edita los datos principales de la factura
|
||||
</Card.Description>
|
||||
</Card.Header>
|
||||
<Card.Content>
|
||||
<div class="space-y-6">
|
||||
<!-- Fila 1: Tipo de Operación, Tipo de Factura -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="operation_type">Tipo de Operación *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.operation_type !== null ? String(formData.operation_type) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.operation_type = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="operation_type">
|
||||
<span class="truncate">
|
||||
{formData.operation_type !== null
|
||||
? operationOptions.find(o => o.value === formData.operation_type)?.label
|
||||
: 'Selecciona tipo...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each operationOptions as option}
|
||||
<Select.Item value={String(option.value)}>{option.label}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<!-- Top fields moved to shared component -->
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="invoice_type">Tipo de Factura *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.invoice_type || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.invoice_type = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="invoice_type">
|
||||
<span class="truncate">
|
||||
{formData.invoice_type
|
||||
? `${formData.invoice_type} - ${filteredInvoiceTypes().find(t => t.key === formData.invoice_type)?.description || ''}`
|
||||
: 'Selecciona tipo...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each filteredInvoiceTypes() as type}
|
||||
<Select.Item value={type.key}>
|
||||
{type.key} - {type.description}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Layout de 2 columnas compacto -->
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Columna Izquierda: Clientes - Proveedores - Agente Aduanal -->
|
||||
<div class="border rounded-md p-3 space-y-3">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Clientes - Proveedores - Agente Aduanal</h4>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="provider_id" class="text-xs">Proveedor:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.provider_header || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.provider_header = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="provider_header" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.provider_header
|
||||
? providerHeaderOptions.find(o => o.value === formData.provider_header)?.label || formData.provider_header
|
||||
: 'Selecciona encabezado...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each providerHeaderOptions as option}
|
||||
<Select.Item value={option.value}>
|
||||
{option.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.provider_id ? String(formData.provider_id) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.provider_id = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="provider_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.provider_id
|
||||
? providers.find(p => p.id === formData.provider_id)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each providers as provider}
|
||||
<Select.Item value={String(provider.id)}>
|
||||
{provider.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Fila 2: Número de Factura, Fecha de Factura -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="invoice_number">Número de Factura *</Label>
|
||||
<Input
|
||||
id="invoice_number"
|
||||
bind:value={formData.invoice_number}
|
||||
placeholder="Ej: INV-2024-001"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="sold_to_id" class="text-xs">Consignado a:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.sold_to_header || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.sold_to_header = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="sold_to_header" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.sold_to_header
|
||||
? soldToHeaderOptions.find(o => o.value === formData.sold_to_header)?.label || formData.sold_to_header
|
||||
: 'Selecciona encabezado...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each soldToHeaderOptions as option}
|
||||
<Select.Item value={option.value}>
|
||||
{option.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.sold_to_id ? String(formData.sold_to_id) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.sold_to_id = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="sold_to_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.sold_to_id
|
||||
? clients.find(c => c.id === formData.sold_to_id)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each clients as client}
|
||||
<Select.Item value={String(client.id)}>
|
||||
{client.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="invoice_date">Fecha de Factura</Label>
|
||||
<Input
|
||||
id="invoice_date"
|
||||
type="date"
|
||||
bind:value={formData.invoice_date}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="shipped_to_id" class="text-xs">Enviado a:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.shipped_to_header || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.shipped_to_header = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="shipped_to_header" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.shipped_to_header
|
||||
? shippedToHeaderOptions.find(o => o.value === formData.shipped_to_header)?.label || formData.shipped_to_header
|
||||
: 'Selecciona encabezado...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each shippedToHeaderOptions as option}
|
||||
<Select.Item value={option.value}>
|
||||
{option.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.shipped_to_id ? String(formData.shipped_to_id) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.shipped_to_id = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="shipped_to_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.shipped_to_id
|
||||
? allClientsProviders.find(cp => cp.id === formData.shipped_to_id)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each allClientsProviders as cp}
|
||||
<Select.Item value={String(cp.id)}>
|
||||
{cp.name} ({cp.type === 'client' ? 'C' : 'P'})
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Fila 3: Número de Proyecto, Semáforo -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label for="project_number">Número de Proyecto</Label>
|
||||
<Input
|
||||
id="project_number"
|
||||
bind:value={formData.project_number}
|
||||
placeholder="Número de proyecto"
|
||||
/>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="customs_broker_id" class="text-xs">Agente Aduanal Mex:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.customs_broker_id || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.customs_broker_id = v || null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="customs_broker_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.customs_broker_id
|
||||
? customsBrokers.find(cb => cb.broker_key === formData.customs_broker_id)?.name || '...'
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each customsBrokers as broker}
|
||||
<Select.Item value={broker.broker_key}>
|
||||
{broker.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<Label for="traffic_light_status">Semáforo</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.traffic_light_status || 'green'}
|
||||
onValueChange={(v) => {
|
||||
formData.traffic_light_status = v ?? 'green';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="traffic_light_status">
|
||||
<span class="truncate">
|
||||
{trafficLightOptions.find(t => t.value === formData.traffic_light_status)?.label || 'Verde'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each trafficLightOptions as option}
|
||||
<Select.Item value={option.value}>{option.label}</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-1.5">
|
||||
<Label for="customs_broker_us_id" class="text-xs">Agente Aduanal Ame:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.customs_broker_us_id || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.customs_broker_us_id = v || null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="customs_broker_us_id" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.customs_broker_us_id
|
||||
? customsBrokers.find(cb => cb.broker_key === formData.customs_broker_us_id)?.name || '...'
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each customsBrokers as broker}
|
||||
<Select.Item value={broker.broker_key}>
|
||||
{broker.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Observaciones -->
|
||||
<div class="space-y-2">
|
||||
<Label for="observation_es">Observaciones (Español)</Label>
|
||||
<Textarea
|
||||
id="observation_es"
|
||||
bind:value={formData.observation_es}
|
||||
placeholder="Notas u observaciones adicionales..."
|
||||
rows={4}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
<!-- Columna Derecha: Tipo de Moneda y Transportista -->
|
||||
<div class="space-y-3">
|
||||
<!-- Tipo de Moneda - Pesos Netos y Brutos -->
|
||||
<div class="border rounded-md p-3 space-y-2">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Tipo de Moneda - Pesos Netos y Brutos</h4>
|
||||
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="currency_type" class="text-xs">Moneda:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.currency_type || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.currency_type = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="currency_type" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.currency_type || '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each currencyTypes as currencyType}
|
||||
<Select.Item value={currencyType.code}>
|
||||
{currencyType.code}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="weight_type" class="text-xs">Tipo Peso:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.weight_type || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.weight_type = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="weight_type" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.weight_type || '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each weightTypeOptions as weightType}
|
||||
<Select.Item value={weightType.value}>
|
||||
{weightType.value}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="iva_factor" class="text-xs">IVA:</Label>
|
||||
<Input id="iva_factor" type="number" step="0.0001" bind:value={formData.iva_factor} placeholder="0.16" class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="invoice_type" class="text-xs">Tipo de Cambio:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.invoice_type || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.invoice_type = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="invoice_type" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.invoice_type
|
||||
? `${formData.invoice_type}`
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each invoiceTypes as type}
|
||||
<Select.Item value={type.key}>
|
||||
{type.key} - {type.description}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Transportista -->
|
||||
<div class="border rounded-md p-3 space-y-2">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Transportista</h4>
|
||||
|
||||
<div class="grid grid-cols-3 gap-2">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="carrier_id" class="text-xs">Clave:</Label>
|
||||
<Input id="carrier_id" type="number" bind:value={formData.carrier_id} class="h-7 text-xs" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 space-y-1.5">
|
||||
<Label for="transport_type" class="text-xs">Clave Transporte:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.transport_type || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.transport_type = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="transport_type" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.transport_type || '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each transportTypes as transportType}
|
||||
<Select.Item value={transportType.transport_code}>
|
||||
{transportType.transport_code}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="driver_name" class="text-xs">Conductor:</Label>
|
||||
<Input id="driver_name" bind:value={formData.driver_name} class="h-7 text-xs" />
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="space-y-1.5">
|
||||
<Label for="transport_type" class="text-xs">Tipo Transporte:</Label>
|
||||
<Input id="transport_id" bind:value={formData.transport_id} class="h-7 text-xs" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="transport_num" class="text-xs">Placas:</Label>
|
||||
<Input id="transport_num" bind:value={formData.transport_num} class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1.5">
|
||||
<Label for="aduana" class="text-xs">Aduana y Sección de Despacho:</Label>
|
||||
<Input id="aduana" bind:value={formData.aduana} class="h-7 text-xs" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
<script lang="ts">
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { Switch } from '$lib/components/ui/switch';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
import type { InvoiceType } from '$lib/api/dashboard/refrence_data/invoice_types';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
invoiceTypes = [],
|
||||
defaultOperationType = undefined,
|
||||
defaultInvoiceType = undefined,
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
invoiceTypes?: InvoiceType[];
|
||||
defaultOperationType?: number | null;
|
||||
defaultInvoiceType?: string | null;
|
||||
} = $props();
|
||||
|
||||
if (!formData) {
|
||||
let operationType: number | null = null;
|
||||
if (invoice?.operation_type) {
|
||||
operationType = invoice.operation_type === 'exp' ? 1 : 2;
|
||||
} else if (defaultOperationType !== undefined) {
|
||||
operationType = defaultOperationType ?? null;
|
||||
}
|
||||
|
||||
formData = {
|
||||
is_pedimento_pending: false,
|
||||
pedimento: invoice?.compliance_mx?.pedimento || '',
|
||||
remesa: invoice?.compliance_mx?.remesa || '',
|
||||
invoice_number: invoice?.invoice_number || '',
|
||||
invoice_date: invoice?.invoice_date || '',
|
||||
emission_date: '',
|
||||
operation_type: operationType,
|
||||
invoice_type: invoice?.invoice_type || (defaultInvoiceType ?? ''),
|
||||
};
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- Datos Principales en una fila compacta (reusable across tabs) -->
|
||||
<div class="grid grid-cols-12 gap-3 items-end pb-3">
|
||||
<div class="col-span-1 space-y-1">
|
||||
<Label for="operation_type" class="text-xs">Tipo de Operación *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.operation_type !== null ? String(formData.operation_type) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.operation_type = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="operation_type" class="h-8 text-sm">
|
||||
<span class="truncate">
|
||||
{formData.operation_type !== null
|
||||
? (formData.operation_type === 1 ? 'Exp' : 'Imp')
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
<Select.Item value="1">Exportación</Select.Item>
|
||||
<Select.Item value="2">Importación</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="col-span-1 space-y-1">
|
||||
<Label for="operation_type" class="text-xs">Tipo de Operación *</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.invoice_type || ''}
|
||||
onValueChange={(v) => {
|
||||
formData.invoice_type = v ?? '';
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="invoice_type" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.invoice_type
|
||||
? `${formData.invoice_type}`
|
||||
: '...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each invoiceTypes as type}
|
||||
<Select.Item value={type.key}>
|
||||
{type.key} - {type.description}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<div class="col-span-1 space-y-1 pb-1 items-center flex flex-col">
|
||||
<Label for="is_pedimento_pending" class="text-xs">Pedimento Pendiente?</Label>
|
||||
<Switch
|
||||
id="is_pedimento_pending"
|
||||
checked={formData.is_pedimento_pending}
|
||||
onCheckedChange={(checked) => {
|
||||
formData.is_pedimento_pending = checked;
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="pedimento" class="text-xs">Pedimento</Label>
|
||||
<Input id="pedimento" bind:value={formData.pedimento} class="h-8 text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-1 space-y-1">
|
||||
<Label for="remesa" class="text-xs">Remesa</Label>
|
||||
<Input id="remesa" bind:value={formData.remesa} class="h-8 text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="invoice_number" class="text-xs">Núm. Factura *</Label>
|
||||
<Input id="invoice_number" bind:value={formData.invoice_number} class="h-8 text-sm font-medium" required />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="invoice_date" class="text-xs">Fecha Factura</Label>
|
||||
<Input id="invoice_date" type="date" bind:value={formData.invoice_date} class="h-8 text-sm" />
|
||||
</div>
|
||||
|
||||
<div class="col-span-2 space-y-1">
|
||||
<Label for="emission_date" class="text-xs">Fecha Emisión</Label>
|
||||
<Input id="emission_date" type="date" bind:value={formData.emission_date} class="h-8 text-sm" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,73 +0,0 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Plus } from 'lucide-svelte';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable()
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
} = $props();
|
||||
|
||||
if (!formData && invoice?.logistics && invoice.logistics.length > 0) {
|
||||
formData = invoice.logistics.map(l => ({ ...l }));
|
||||
exists = true;
|
||||
} else if (!formData) {
|
||||
formData = [];
|
||||
exists = false;
|
||||
}
|
||||
|
||||
function addLogistic() {
|
||||
formData = [...formData, {
|
||||
carrier_id: '',
|
||||
transport_type: null,
|
||||
driver_name: '',
|
||||
vehicle_num: '',
|
||||
license_plate: ''
|
||||
}];
|
||||
}
|
||||
</script>
|
||||
|
||||
<Card.Root>
|
||||
<Card.Header>
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<Card.Title>Logística y Transporte</Card.Title>
|
||||
<Card.Description>Información de transportistas y vehículos</Card.Description>
|
||||
</div>
|
||||
<Button onclick={addLogistic} size="sm">
|
||||
<Plus size={16} class="mr-2" />
|
||||
Agregar
|
||||
</Button>
|
||||
</div>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-4">
|
||||
{#if formData.length === 0}
|
||||
<p class="text-sm text-muted-foreground text-center py-8">No hay registros de logística. Haz clic en "Agregar" para crear uno.</p>
|
||||
{:else}
|
||||
{#each formData as logistic, index}
|
||||
<div class="border rounded-lg p-4 space-y-4">
|
||||
<h4 class="font-semibold">Logística #{index + 1}</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div class="space-y-2">
|
||||
<Label>Transportista</Label>
|
||||
<Input bind:value={logistic.carrier_id} placeholder="ID del transportista" />
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<Label>Conductor</Label>
|
||||
<Input bind:value={logistic.driver_name} placeholder="Nombre del conductor" />
|
||||
</div>
|
||||
</div>
|
||||
<p class="text-xs text-muted-foreground">Más campos por implementar...</p>
|
||||
</div>
|
||||
{/each}
|
||||
{/if}
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
@@ -0,0 +1,404 @@
|
||||
<script lang="ts">
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Textarea } from "$lib/components/ui/textarea/index.js";
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable(),
|
||||
seals = [],
|
||||
incoterms = [],
|
||||
enclosure = [],
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
seals?: any[];
|
||||
incoterms?: any[];
|
||||
enclosure?: any[];
|
||||
} = $props();
|
||||
|
||||
if (!formData && invoice) {
|
||||
formData = {
|
||||
// Invoice header fields
|
||||
observation_es: invoice.observation_es || '',
|
||||
observation_en: invoice.observation_en || '',
|
||||
alternate_invoice: invoice.alternate_invoice || '',
|
||||
// Compliance MX fields
|
||||
pedimento: invoice.compliance_mx?.pedimento || '',
|
||||
pedimento_code: invoice.compliance_mx?.pedimento_code || '',
|
||||
pedimento_k1: invoice.compliance_mx?.pedimento_k1 || '',
|
||||
remesa: invoice.compliance_mx?.remesa || null,
|
||||
aduana: invoice.compliance_mx?.aduana || '',
|
||||
port_of_entry: invoice.compliance_mx?.port_of_entry || '',
|
||||
destination: invoice.compliance_mx?.destination || '',
|
||||
manifest_number: invoice.compliance_mx?.manifest_number || '',
|
||||
provider_header: invoice.compliance_mx?.provider_header || '',
|
||||
provider_id: invoice.compliance_mx?.provider_id || null,
|
||||
sold_to_header: invoice.compliance_mx?.sold_to_header || '',
|
||||
sold_to_id: invoice.compliance_mx?.sold_to_id || null,
|
||||
shipped_to_header: invoice.compliance_mx?.shipped_to_header || '',
|
||||
shipped_to_id: invoice.compliance_mx?.shipped_to_id || null,
|
||||
shipped_by_header: invoice.compliance_mx?.shipped_by_header || '',
|
||||
shipped_by_id: invoice.compliance_mx?.shipped_by_id || null,
|
||||
customs_broker_id: invoice.compliance_mx?.customs_broker_id || null,
|
||||
broker_invoice_num: invoice.compliance_mx?.broker_invoice_num || '',
|
||||
broker_invoice_date: invoice.compliance_mx?.broker_invoice_date || '',
|
||||
is_mixed: invoice.compliance_mx?.is_mixed || null,
|
||||
waste_type: invoice.compliance_mx?.waste_type || '',
|
||||
scrap_type: invoice.compliance_mx?.scrap_type || '',
|
||||
appendix_17: invoice.compliance_mx?.appendix_17 || null,
|
||||
is_regime_change: invoice.compliance_mx?.is_regime_change || '',
|
||||
which_exchange_rate: invoice.compliance_mx?.which_exchange_rate || '',
|
||||
value_method: invoice.compliance_mx?.value_method || '',
|
||||
act_value: invoice.compliance_mx?.act_value || '',
|
||||
is_pedimento_pending: invoice.compliance_mx?.is_pedimento_pending || false,
|
||||
is_owner_of_goods: invoice.compliance_mx?.is_owner_of_goods || '',
|
||||
generate_balances: invoice.compliance_mx?.generate_balances || '',
|
||||
was_reviewed_by_company: invoice.compliance_mx?.was_reviewed_by_company || false,
|
||||
edocument: invoice.compliance_mx?.edocument || '',
|
||||
electronic_signature: invoice.compliance_mx?.electronic_signature || '',
|
||||
certificate_number: invoice.compliance_mx?.certificate_number || '',
|
||||
niu_number: invoice.compliance_mx?.niu_number || '',
|
||||
bill_of_lading_count: invoice.compliance_mx?.bill_of_lading_count || '',
|
||||
addendum_vu: invoice.compliance_mx?.addendum_vu || '',
|
||||
origin_destination_cove: invoice.compliance_mx?.origin_destination_cove || '',
|
||||
vucem_operation_num: invoice.compliance_mx?.vucem_operation_num || '',
|
||||
customs_person_line: invoice.compliance_mx?.customs_person_line || null,
|
||||
contingency_mode: invoice.compliance_mx?.contingency_mode || false,
|
||||
enclosure: invoice.compliance_mx?.enclosure || '',
|
||||
guide_type_to_identify: invoice.compliance_mx?.guide_type_to_identify || '',
|
||||
location: invoice.compliance_mx?.location || '',
|
||||
dot_code: invoice.compliance_mx?.dot_code || '',
|
||||
subdivision: invoice.compliance_mx?.subdivision || '',
|
||||
acts_as: invoice.compliance_mx?.acts_as || '',
|
||||
movement_type: invoice.compliance_mx?.movement_type || '',
|
||||
office_document: invoice.compliance_mx?.office_document || '',
|
||||
reason_export: invoice.compliance_mx?.reason_export || '',
|
||||
signature_key: invoice.compliance_mx?.signature_key || '',
|
||||
sem_id: invoice.compliance_mx?.sem_id || null,
|
||||
// Financials fields (incrementables)
|
||||
freight: invoice.financials?.freight || null,
|
||||
insurance_value: invoice.financials?.insurance_value || null,
|
||||
insurance: invoice.financials?.insurance || null,
|
||||
packaging: invoice.financials?.packaging || null,
|
||||
other_increments: invoice.financials?.other_increments || null,
|
||||
total_increments_mn: invoice.financials?.total_increments_mn || null,
|
||||
total_increments_me: invoice.financials?.total_increments_me || null,
|
||||
// Logistics fields
|
||||
incoterm: invoice.logistics?.[0]?.incoterm || ''
|
||||
};
|
||||
exists = true;
|
||||
} else if (!formData) {
|
||||
formData = {
|
||||
// Invoice header fields
|
||||
observation_es: '',
|
||||
observation_en: '',
|
||||
alternate_invoice: '',
|
||||
// Compliance MX fields
|
||||
pedimento: '',
|
||||
pedimento_code: '',
|
||||
pedimento_k1: '',
|
||||
remesa: null,
|
||||
aduana: '',
|
||||
port_of_entry: '',
|
||||
destination: '',
|
||||
manifest_number: '',
|
||||
provider_header: '',
|
||||
provider_id: null,
|
||||
sold_to_header: '',
|
||||
sold_to_id: null,
|
||||
shipped_to_header: '',
|
||||
shipped_to_id: null,
|
||||
shipped_by_header: '',
|
||||
shipped_by_id: null,
|
||||
customs_broker_id: null,
|
||||
broker_invoice_num: '',
|
||||
broker_invoice_date: '',
|
||||
is_mixed: null,
|
||||
waste_type: '',
|
||||
scrap_type: '',
|
||||
appendix_17: null,
|
||||
is_regime_change: '',
|
||||
which_exchange_rate: '',
|
||||
value_method: '',
|
||||
act_value: '',
|
||||
is_pedimento_pending: false,
|
||||
is_owner_of_goods: '',
|
||||
generate_balances: '',
|
||||
was_reviewed_by_company: false,
|
||||
edocument: '',
|
||||
electronic_signature: '',
|
||||
certificate_number: '',
|
||||
niu_number: '',
|
||||
bill_of_lading_count: '',
|
||||
addendum_vu: '',
|
||||
origin_destination_cove: '',
|
||||
vucem_operation_num: '',
|
||||
customs_person_line: null,
|
||||
contingency_mode: false,
|
||||
enclosure: '',
|
||||
guide_type_to_identify: '',
|
||||
location: '',
|
||||
dot_code: '',
|
||||
subdivision: '',
|
||||
acts_as: '',
|
||||
movement_type: '',
|
||||
office_document: '',
|
||||
reason_export: '',
|
||||
signature_key: '',
|
||||
sem_id: null,
|
||||
// Financials fields
|
||||
freight: null,
|
||||
insurance_value: null,
|
||||
insurance: null,
|
||||
packaging: null,
|
||||
other_increments: null,
|
||||
total_increments_mn: null,
|
||||
total_increments_me: null,
|
||||
// Logistics fields
|
||||
incoterm: ''
|
||||
};
|
||||
exists = false;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
|
||||
<div class="grid grid-cols-2 grid-rows-5 gap-4">
|
||||
<div class="border rounded-md p-3 space-y-3 row-span-2">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Observacion de la factura mexicana y bilingue:</h4>
|
||||
<div class="space-y-1">
|
||||
<Textarea
|
||||
id="observation_es"
|
||||
bind:value={formData.observation_es}
|
||||
class="min-h-[150px] max-h-[150px] text-sm font-medium"
|
||||
placeholder="Escribe tus observaciones aqui."
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 row-span-2 col-start-1 row-start-3">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Observacion de la factura americana:</h4>
|
||||
<Textarea
|
||||
id="observation_en"
|
||||
bind:value={formData.observation_en}
|
||||
class="min-h-[150px] max-h-[150px] text-sm font-medium"
|
||||
placeholder="Escribe tus observaciones aqui."
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 col-span-2 col-start-1 row-start-5">
|
||||
<div class="grid grid-cols-4 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="num_seals" class="text-xs">Num Precintos:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.num_seals ? String(formData.num_seals) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.num_seals = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="num_seals" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.num_seals
|
||||
? seals.find(p => p.id === formData.num_seals)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each seals as seal}
|
||||
<Select.Item value={String(seal.id)}>
|
||||
{seal.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="movement_type" class="text-xs">Tipo Movimiento:</Label>
|
||||
<Input
|
||||
id="movement_type"
|
||||
bind:value={formData.movement_type}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="alternate_invoice" class="text-xs">Factura Alterna:</Label>
|
||||
<Input
|
||||
id="alternate_invoice"
|
||||
bind:value={formData.alternate_invoice}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="valuation_method" class="text-xs">Met. Valoracion:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.valuation_method ? String(formData.valuation_method) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.valuation_method = v;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="valuation_method" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.valuation_method || 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
<Select.Item value="general">General</Select.Item>
|
||||
<Select.Item value="devalued">Devaluado</Select.Item>
|
||||
<Select.Item value="special">Especial</Select.Item>
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 row-span-3 col-start-2 row-start-1">
|
||||
<h4 class="text-xs font-semibold text-muted-foreground uppercase">Incrementables:</h4>
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
<div class="space-y-1">
|
||||
<Label for="freight" class="text-xs">Flete:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="freight"
|
||||
type="number"
|
||||
bind:value={formData.freight}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="insurance_value" class="text-xs">Val. Seguros:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="insurance_value"
|
||||
type="number"
|
||||
bind:value={formData.insurance_value}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="insurance" class="text-xs">Seguros:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="insurance"
|
||||
type="number"
|
||||
bind:value={formData.insurance}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="packaging" class="text-xs">Embalajes:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="packaging"
|
||||
type="number"
|
||||
bind:value={formData.packaging}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="other_increments" class="text-xs">Otros incrementables:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="other_increments"
|
||||
type="number"
|
||||
bind:value={formData.other_increments}
|
||||
class="h-8 text-sm font-medium"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="space-y-1">
|
||||
<Label for="total_increments_mn" class="text-xs">Total Incrementables:</Label>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="total_increments_mn"
|
||||
type="number"
|
||||
bind:value={formData.total_increments_mn}
|
||||
class="h-8 text-sm font-medium"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Input
|
||||
id="total_increments_me"
|
||||
type="number"
|
||||
bind:value={formData.total_increments_me}
|
||||
class="h-8 text-sm font-medium"
|
||||
disabled
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 col-start-2 row-start-4">
|
||||
<div class="grid grid-cols-2 gap-3">
|
||||
<div class="space-y-1">
|
||||
<Label for="incoterm" class="text-xs">Incoterm:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.incoterm ? String(formData.incoterm) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.incoterm = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="incoterm" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.incoterm
|
||||
? incoterms.find(p => p.id === formData.incoterm)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each incoterms as inco}
|
||||
<Select.Item value={String(inco.id)}>
|
||||
{inco.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<Label for="enclosure" class="text-xs">Recinto:</Label>
|
||||
<Select.Root
|
||||
type="single"
|
||||
value={formData.enclosure ? String(formData.enclosure) : ''}
|
||||
onValueChange={(v) => {
|
||||
formData.enclosure = v ? parseInt(v) : null;
|
||||
}}
|
||||
>
|
||||
<Select.Trigger id="enclosure" class="h-7 text-xs">
|
||||
<span class="truncate">
|
||||
{formData.enclosure
|
||||
? enclosure.find(p => p.id === formData.enclosure)?.name || 'Selecciona...'
|
||||
: 'Selecciona...'}
|
||||
</span>
|
||||
</Select.Trigger>
|
||||
<Select.Content class="max-h-[300px]">
|
||||
{#each enclosure as rec}
|
||||
<Select.Item value={String(rec.id)}>
|
||||
{rec.name}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,285 @@
|
||||
<script lang="ts">
|
||||
import * as Card from '$lib/components/ui/card';
|
||||
import { Input } from '$lib/components/ui/input';
|
||||
import { Label } from '$lib/components/ui/label';
|
||||
import { Button } from '$lib/components/ui/button';
|
||||
import { Checkbox } from '$lib/components/ui/checkbox';
|
||||
import { RadioGroup, RadioGroupItem } from '$lib/components/ui/radio-group';
|
||||
import * as Select from '$lib/components/ui/select';
|
||||
import { Textarea } from '$lib/components/ui/textarea';
|
||||
import { Plus, Upload } from 'lucide-svelte';
|
||||
import type { Invoice } from '$lib/api/dashboard/a76/invoices';
|
||||
|
||||
let {
|
||||
invoice,
|
||||
formData = $bindable(),
|
||||
exists = $bindable()
|
||||
}: {
|
||||
invoice: Invoice | null;
|
||||
formData?: any;
|
||||
exists?: boolean;
|
||||
} = $props();
|
||||
|
||||
if (!formData && invoice?.logistics && invoice.logistics.length > 0) {
|
||||
formData = invoice.logistics.map(l => ({
|
||||
// Carrier info
|
||||
carrier_id: l.carrier_id || '',
|
||||
transport_id: l.transport_id || '',
|
||||
transport_us_id: l.transport_us_id || '',
|
||||
transport_type: l.transport_type || null,
|
||||
transport_num: l.transport_num || '',
|
||||
transport_mode: l.transport_mode || '',
|
||||
driver_name: l.driver_name || '',
|
||||
is_rail: l.is_rail || '',
|
||||
rail_id: l.rail_id || '',
|
||||
// Vehicle & tracking
|
||||
vehicle_num: l.vehicle_num || '',
|
||||
license_plate: l.license_plate || '',
|
||||
license_plate_complete: l.license_plate_complete || '',
|
||||
trailer_num: l.trailer_num || '',
|
||||
seal_number: l.seal_number || '',
|
||||
guide_number: l.guide_number || '',
|
||||
bill_number: l.bill_number || '',
|
||||
reference_number: l.reference_number || '',
|
||||
shipment_number: l.shipment_number || '',
|
||||
// Incoterms
|
||||
incoterm: l.incoterm || '',
|
||||
// Identifiers
|
||||
identifier_1: l.identifier_1 || '',
|
||||
complement_1: l.complement_1 || '',
|
||||
identifier_2: l.identifier_2 || '',
|
||||
complement_2: l.complement_2 || '',
|
||||
// Weight & container
|
||||
weight_type: l.weight_type || '',
|
||||
container_types: l.container_types || '',
|
||||
vehicle_data: l.vehicle_data || '',
|
||||
// Locations
|
||||
origin_location: l.origin_location || '',
|
||||
destination_location: l.destination_location || '',
|
||||
transport_itinerary: l.transport_itinerary || '',
|
||||
destination_goods: l.destination_goods || '',
|
||||
// Dates
|
||||
entry_exit_date: l.entry_exit_date || '',
|
||||
delivery_date: l.delivery_date || '',
|
||||
// Delivery control
|
||||
delivered_status: l.delivered_status || '',
|
||||
received_by: l.received_by || '',
|
||||
// Payment
|
||||
payment_date: l.payment_date || '',
|
||||
payment_receipt_num: l.payment_receipt_num || '',
|
||||
// CTM
|
||||
is_ctm_process: l.is_ctm_process || ''
|
||||
}));
|
||||
exists = true;
|
||||
} else if (!formData) {
|
||||
formData = [];
|
||||
exists = false;
|
||||
}
|
||||
|
||||
// Campos adicionales que van en otros recursos
|
||||
let transportMode = $state('TRUCK');
|
||||
// is_mixed va en compliance_mx
|
||||
let isMixed = $state(invoice?.compliance_mx?.is_mixed ? 'yes' : 'no');
|
||||
// related_doc_id va en invoice header
|
||||
let relationDocsId = $state(invoice?.related_doc_id?.toString() || '0');
|
||||
// electronic_signature va en compliance_mx
|
||||
let code_signature = $state(invoice?.compliance_mx?.code_signature || '');
|
||||
let electronicSignature = $state(invoice?.compliance_mx?.electronic_signature || '');
|
||||
// Estos campos no existen en el schema del backend
|
||||
let mandatoryPerson = $state('0');
|
||||
let rfc = $state('');
|
||||
let contingencyMode = $state(invoice?.compliance_mx?.contingency_mode || false);
|
||||
let curp = $state('');
|
||||
let rule3121PartiesII = $state<boolean>(false);
|
||||
// origin_destination_cove va en compliance_mx
|
||||
let cove = $state(invoice?.compliance_mx?.origin_destination_cove || '');
|
||||
// vucem_operation_num va en compliance_mx
|
||||
let operationNum = $state(invoice?.compliance_mx?.vucem_operation_num || '');
|
||||
// addendum_vu va en compliance_mx
|
||||
let adendas = $state(invoice?.compliance_mx?.addendum_vu || '');
|
||||
// vu_observations va en invoice header
|
||||
let observationsVU = $state(invoice?.vu_observations || '');
|
||||
// certificate_number va en compliance_mx
|
||||
let certifiedNumber = $state(invoice?.compliance_mx?.certificate_number || '');
|
||||
// seal_value_2500 va en financials
|
||||
let printStamp = $state(invoice?.financials?.seal_value_2500 || false);
|
||||
// comments_status va en invoice header
|
||||
let commentsStatus = $state(invoice?.comments_status || '');
|
||||
|
||||
const transportModes = [
|
||||
{ value: 'TRUCK', label: 'Camión' },
|
||||
{ value: 'TRAIN', label: 'Tren' },
|
||||
{ value: 'SHIP', label: 'Marítimo' },
|
||||
{ value: 'AIR', label: 'Aéreo' },
|
||||
{ value: 'OTHER', label: 'Otro' }
|
||||
];
|
||||
|
||||
function addLogistic() {
|
||||
formData = [...formData, {
|
||||
carrier_id: '',
|
||||
transport_type: null,
|
||||
driver_name: '',
|
||||
vehicle_num: '',
|
||||
license_plate: ''
|
||||
}];
|
||||
}
|
||||
|
||||
function loadInfo() {
|
||||
// Función para cargar información
|
||||
console.log('Cargar información');
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="grid grid-cols-3 grid-rows-1 gap-3">
|
||||
<div class="border rounded-md p-3 space-y-3">
|
||||
|
||||
<!-- Modo de Transporte -->
|
||||
<div class="space-y-2">
|
||||
<Label for="transport-mode">Modo de Transporte:</Label>
|
||||
<Select.Root type="single" value={transportMode} onValueChange={(value: string | undefined) => transportMode = value || 'TRUCK'}>
|
||||
<Select.Trigger id="transport-mode">
|
||||
{transportModes.find(m => m.value === transportMode)?.label || 'Seleccionar modo'}
|
||||
</Select.Trigger>
|
||||
<Select.Content>
|
||||
{#each transportModes as mode}
|
||||
<Select.Item value={mode.value}>
|
||||
{mode.label}
|
||||
</Select.Item>
|
||||
{/each}
|
||||
</Select.Content>
|
||||
</Select.Root>
|
||||
</div>
|
||||
|
||||
<!-- Imprimir Sello -->
|
||||
<div class="space-y-2">
|
||||
<Label class="opacity-0">Spacer</Label>
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox id="print-stamp" bind:checked={printStamp} />
|
||||
<Label for="print-stamp" class="font-normal">
|
||||
Imprimir el Sello por Valor menor a 2500 dlls
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Es Mixto -->
|
||||
<div class="space-y-2">
|
||||
<Label class="opacity-0">Spacer</Label>
|
||||
<Label>Es Mixto?</Label>
|
||||
<RadioGroup bind:value={isMixed} class="flex gap-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroupItem value="yes" id="mixed-yes" />
|
||||
<Label for="mixed-yes" class="font-normal">Sí</Label>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<RadioGroupItem value="no" id="mixed-no" />
|
||||
<Label for="mixed-no" class="font-normal">No</Label>
|
||||
</div>
|
||||
</RadioGroup>
|
||||
<Label class="opacity-0">Spacer</Label>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox id="rule-3121" bind:checked={rule3121PartiesII} />
|
||||
<Label for="rule-3121" class="font-normal">Regla 3.1.21 Partes II</Label>
|
||||
</div>
|
||||
|
||||
<!-- Comentario estatus -->
|
||||
<div class="space-y-2">
|
||||
<Label class="opacity-0">Spacer</Label>
|
||||
<Label>Comentario Estatus:</Label>
|
||||
<Textarea
|
||||
id="description_es"
|
||||
bind:value={formData.description_es}
|
||||
placeholder="Comentario estatus"
|
||||
rows={3}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="border rounded-md p-3 space-y-3 col-span-2">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
<!-- ID Relación Docs -->
|
||||
<div class="space-y-2">
|
||||
<Label for="relation-docs-id">ID Relación Docs:</Label>
|
||||
<Input id="relation-docs-id" bind:value={relationDocsId} />
|
||||
</div>
|
||||
|
||||
<!-- Firma Electrónica -->
|
||||
<div class="space-y-2 md:col-span-2">
|
||||
<Label for="electronic-sig-1">Firma Electrónica:</Label>
|
||||
<div class="flex gap-2">
|
||||
<Input id="electronic-sig-1" bind:value={code_signature} class="flex-1" />
|
||||
<Button variant="outline" size="icon">
|
||||
<Upload class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mandatario/Persona Autorizada -->
|
||||
<div class="space-y-2">
|
||||
<Label for="mandatory-person">Mandatario/Persona Autorizada:</Label>
|
||||
<Input id="mandatory-person" bind:value={mandatoryPerson} />
|
||||
</div>
|
||||
|
||||
<!-- RFC -->
|
||||
<div class="space-y-2">
|
||||
<Label id="rfc" for="rfc">RFC: {rfc}</Label>
|
||||
</div>
|
||||
|
||||
<!-- CURP -->
|
||||
<div class="space-y-2">
|
||||
<Label id="curp" for="curp">CURP: {curp}</Label>
|
||||
</div>
|
||||
|
||||
<!-- Modo Contingencia -->
|
||||
<div class="space-y-2 col-span-4">
|
||||
<div class="flex items-center space-x-2">
|
||||
<Checkbox id="contingency-mode" bind:checked={contingencyMode} />
|
||||
<Label for="contingency-mode" class="font-normal">Modo Contingencia</Label>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- COVE -->
|
||||
<div class="space-y-2">
|
||||
<Label for="cove">COVE:</Label>
|
||||
<Input id="cove" bind:value={cove} placeholder="COVE" />
|
||||
</div>
|
||||
|
||||
<!-- Número de Operación -->
|
||||
<div class="space-y-2">
|
||||
<Label for="operation-num">Núm Operación:</Label>
|
||||
<Input id="operation-num" bind:value={operationNum} />
|
||||
</div>
|
||||
|
||||
<!-- Adenda(s) -->
|
||||
<div class="space-y-2 md:col-span-2">
|
||||
<Label for="adendas">Adenda(s):</Label>
|
||||
<Input id="adendas" bind:value={adendas} />
|
||||
</div>
|
||||
|
||||
<!-- Observaciones VU -->
|
||||
<div class="space-y-2 md:col-span-2">
|
||||
<Label for="observations-vu">Observaciones VU:</Label>
|
||||
<div class="flex gap-2">
|
||||
<Textarea id="observations-vu" bind:value={observationsVU} class="flex-1 min-h-[60px]" />
|
||||
<Button variant="outline" onclick={loadInfo}>
|
||||
Cargar Info.
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Número Certificado -->
|
||||
<div class="space-y-2">
|
||||
<Label for="certified-num">Número Certificado:</Label>
|
||||
<Input id="certified-num" bind:value={certifiedNumber} />
|
||||
</div>
|
||||
|
||||
<!-- Firma Electrónica 2 -->
|
||||
<div class="space-y-2 md:col-span-2">
|
||||
<Label for="electronic-sig-2">Firma Electrónica:</Label>
|
||||
<Input id="electronic-sig-2" bind:value={electronicSignature} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
36
frontend/src/lib/components/ui/checkbox/checkbox.svelte
Normal file
36
frontend/src/lib/components/ui/checkbox/checkbox.svelte
Normal file
@@ -0,0 +1,36 @@
|
||||
<script lang="ts">
|
||||
import { Checkbox as CheckboxPrimitive } from "bits-ui";
|
||||
import CheckIcon from "@lucide/svelte/icons/check";
|
||||
import MinusIcon from "@lucide/svelte/icons/minus";
|
||||
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
checked = $bindable(false),
|
||||
indeterminate = $bindable(false),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<CheckboxPrimitive.RootProps> = $props();
|
||||
</script>
|
||||
|
||||
<CheckboxPrimitive.Root
|
||||
bind:ref
|
||||
data-slot="checkbox"
|
||||
class={cn(
|
||||
"border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive peer flex size-4 shrink-0 items-center justify-center rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
bind:checked
|
||||
bind:indeterminate
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({ checked, indeterminate })}
|
||||
<div data-slot="checkbox-indicator" class="text-current transition-none">
|
||||
{#if checked}
|
||||
<CheckIcon class="size-3.5" />
|
||||
{:else if indeterminate}
|
||||
<MinusIcon class="size-3.5" />
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</CheckboxPrimitive.Root>
|
||||
6
frontend/src/lib/components/ui/checkbox/index.ts
Normal file
6
frontend/src/lib/components/ui/checkbox/index.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import Root from "./checkbox.svelte";
|
||||
export {
|
||||
Root,
|
||||
//
|
||||
Root as Checkbox,
|
||||
};
|
||||
10
frontend/src/lib/components/ui/radio-group/index.ts
Normal file
10
frontend/src/lib/components/ui/radio-group/index.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import Root from "./radio-group.svelte";
|
||||
import Item from "./radio-group-item.svelte";
|
||||
|
||||
export {
|
||||
Root,
|
||||
Item,
|
||||
//
|
||||
Root as RadioGroup,
|
||||
Item as RadioGroupItem,
|
||||
};
|
||||
@@ -0,0 +1,31 @@
|
||||
<script lang="ts">
|
||||
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
|
||||
import CircleIcon from "@lucide/svelte/icons/circle";
|
||||
import { cn, type WithoutChildrenOrChild } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
...restProps
|
||||
}: WithoutChildrenOrChild<RadioGroupPrimitive.ItemProps> = $props();
|
||||
</script>
|
||||
|
||||
<RadioGroupPrimitive.Item
|
||||
bind:ref
|
||||
data-slot="radio-group-item"
|
||||
class={cn(
|
||||
"border-input text-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 aspect-square size-4 shrink-0 rounded-full border shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
|
||||
className
|
||||
)}
|
||||
{...restProps}
|
||||
>
|
||||
{#snippet children({ checked })}
|
||||
<div data-slot="radio-group-indicator" class="relative flex items-center justify-center">
|
||||
{#if checked}
|
||||
<CircleIcon
|
||||
class="fill-primary absolute start-1/2 top-1/2 size-2 -translate-x-1/2 -translate-y-1/2"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/snippet}
|
||||
</RadioGroupPrimitive.Item>
|
||||
@@ -0,0 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { RadioGroup as RadioGroupPrimitive } from "bits-ui";
|
||||
import { cn } from "$lib/utils.js";
|
||||
|
||||
let {
|
||||
ref = $bindable(null),
|
||||
class: className,
|
||||
value = $bindable(""),
|
||||
...restProps
|
||||
}: RadioGroupPrimitive.RootProps = $props();
|
||||
</script>
|
||||
|
||||
<RadioGroupPrimitive.Root
|
||||
bind:ref
|
||||
bind:value
|
||||
data-slot="radio-group"
|
||||
class={cn("grid gap-3", className)}
|
||||
{...restProps}
|
||||
/>
|
||||
@@ -59,19 +59,75 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
fetch
|
||||
);
|
||||
|
||||
// Cargar tipos de moneda, transporte, etc.
|
||||
const currencyTypesPromise = authenticatedFetch(
|
||||
'v1/public/refrence_data/currency-types?page=1&page_size=100',
|
||||
{},
|
||||
cookies,
|
||||
fetch
|
||||
);
|
||||
|
||||
const transportTypesPromise = authenticatedFetch(
|
||||
'v1/public/refrence_data/transport-types?page=1&page_size=100',
|
||||
{},
|
||||
cookies,
|
||||
fetch
|
||||
);
|
||||
|
||||
const sealsPromise = authenticatedFetch(
|
||||
'v1/public/refrence_data/seals?page=1&page_size=100',
|
||||
{},
|
||||
cookies,
|
||||
fetch
|
||||
);
|
||||
|
||||
const incotermsPromise = authenticatedFetch(
|
||||
'v1/public/refrence_data/incoterms?page=1&page_size=100',
|
||||
{},
|
||||
cookies,
|
||||
fetch
|
||||
);
|
||||
|
||||
const enclosurePromise = authenticatedFetch(
|
||||
'v1/public/refrence_data/enclosure?page=1&page_size=100',
|
||||
{},
|
||||
cookies,
|
||||
fetch
|
||||
);
|
||||
|
||||
// Si el ID es "new", es una creación
|
||||
if (params.id === 'new') {
|
||||
const [invoiceTypesResponse, customsBrokersResponse, clientsResponse, providersResponse] = await Promise.all([
|
||||
const [
|
||||
invoiceTypesResponse,
|
||||
customsBrokersResponse,
|
||||
clientsResponse,
|
||||
providersResponse,
|
||||
currencyTypesResponse,
|
||||
transportTypesResponse,
|
||||
sealsResponse,
|
||||
incotermsResponse,
|
||||
enclosureResponse
|
||||
] = await Promise.all([
|
||||
invoiceTypesPromise,
|
||||
customsBrokersPromise,
|
||||
clientsPromise,
|
||||
providersPromise
|
||||
providersPromise,
|
||||
currencyTypesPromise,
|
||||
transportTypesPromise,
|
||||
sealsPromise,
|
||||
incotermsPromise,
|
||||
enclosurePromise
|
||||
]);
|
||||
|
||||
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
|
||||
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
|
||||
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
|
||||
const providers = providersResponse.ok ? await providersResponse.json() : { items: [] };
|
||||
const currencyTypes = currencyTypesResponse.ok ? await currencyTypesResponse.json() : { items: [] };
|
||||
const transportTypes = transportTypesResponse.ok ? await transportTypesResponse.json() : { items: [] };
|
||||
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
|
||||
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
|
||||
const enclosure = enclosureResponse.ok ? await enclosureResponse.json() : { items: [] };
|
||||
|
||||
return {
|
||||
invoice: null,
|
||||
@@ -81,6 +137,11 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
customsBrokers: customsBrokers.items || [],
|
||||
clients: clients.items || [],
|
||||
providers: providers.items || [],
|
||||
currencyTypes: currencyTypes.items || [],
|
||||
transportTypes: transportTypes.items || [],
|
||||
seals: seals.items || [],
|
||||
incoterms: incoterms.items || [],
|
||||
enclosure: enclosure.items || [],
|
||||
// Filtros desde query parameters para preselección
|
||||
filters: {
|
||||
operation_type: parsedOperationType,
|
||||
@@ -110,17 +171,37 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
const invoice = await response.json();
|
||||
|
||||
// Cargar también los datos de referencia para edición
|
||||
const [invoiceTypesResponse, customsBrokersResponse, clientsResponse, providersResponse] = await Promise.all([
|
||||
const [
|
||||
invoiceTypesResponse,
|
||||
customsBrokersResponse,
|
||||
clientsResponse,
|
||||
providersResponse,
|
||||
currencyTypesResponse,
|
||||
transportTypesResponse,
|
||||
sealsResponse,
|
||||
incotermsResponse,
|
||||
enclosureResponse
|
||||
] = await Promise.all([
|
||||
invoiceTypesPromise,
|
||||
customsBrokersPromise,
|
||||
clientsPromise,
|
||||
providersPromise
|
||||
providersPromise,
|
||||
currencyTypesPromise,
|
||||
transportTypesPromise,
|
||||
sealsPromise,
|
||||
incotermsPromise,
|
||||
enclosurePromise
|
||||
]);
|
||||
|
||||
const invoiceTypes = invoiceTypesResponse.ok ? await invoiceTypesResponse.json() : { items: [] };
|
||||
const customsBrokers = customsBrokersResponse.ok ? await customsBrokersResponse.json() : { items: [] };
|
||||
const clients = clientsResponse.ok ? await clientsResponse.json() : { items: [] };
|
||||
const providers = providersResponse.ok ? await providersResponse.json() : { items: [] };
|
||||
const currencyTypes = currencyTypesResponse.ok ? await currencyTypesResponse.json() : { items: [] };
|
||||
const transportTypes = transportTypesResponse.ok ? await transportTypesResponse.json() : { items: [] };
|
||||
const seals = sealsResponse.ok ? await sealsResponse.json() : { items: [] };
|
||||
const incoterms = incotermsResponse.ok ? await incotermsResponse.json() : { items: [] };
|
||||
const enclosure = enclosureResponse.ok ? await enclosureResponse.json() : { items: [] };
|
||||
|
||||
return {
|
||||
invoice,
|
||||
@@ -130,6 +211,11 @@ export const load: PageServerLoad = async ({ params, cookies, fetch, url }) => {
|
||||
customsBrokers: customsBrokers.items || [],
|
||||
clients: clients.items || [],
|
||||
providers: providers.items || [],
|
||||
currencyTypes: currencyTypes.items || [],
|
||||
transportTypes: transportTypes.items || [],
|
||||
seals: seals.items || [],
|
||||
incoterms: incoterms.items || [],
|
||||
enclosure: enclosure.items || [],
|
||||
// Filtros desde query parameters para preselección
|
||||
filters: {
|
||||
operation_type: parsedOperationType,
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
DollarSign,
|
||||
Truck,
|
||||
Package,
|
||||
Eye,
|
||||
LoaderCircle,
|
||||
Save
|
||||
} from 'lucide-svelte';
|
||||
@@ -21,9 +22,11 @@
|
||||
|
||||
// Importar los componentes de cada pestaña
|
||||
import GeneralTabForm from '$lib/components/dashboard/invoices/edit/general-tab-form.svelte';
|
||||
import ComplianceTabForm from '$lib/components/dashboard/invoices/edit/compliance-tab-form.svelte';
|
||||
import FinancialsTabForm from '$lib/components/dashboard/invoices/edit/financials-tab-form.svelte';
|
||||
import LogisticsTabForm from '$lib/components/dashboard/invoices/edit/logistics-tab-form.svelte';
|
||||
import ObservationsTabForm from '$lib/components/dashboard/invoices/edit/observations-tab-form.svelte';
|
||||
import ItemsTabForm from '$lib/components/dashboard/invoices/edit/continuation-tab-form.svelte';
|
||||
import OthersTabForm from '$lib/components/dashboard/invoices/edit/others-tab-form.svelte';
|
||||
import InvoiceTopFields from '$lib/components/dashboard/invoices/edit/invoice-top-fields.svelte';
|
||||
import ContinuationTabForm from '$lib/components/dashboard/invoices/edit/continuation-tab-form.svelte';
|
||||
|
||||
// Importar la API de facturas
|
||||
import { invoicesApi, type CreateInvoiceData, type UpdateInvoiceData, type OperationType } from '$lib/api/dashboard/a76/invoices';
|
||||
@@ -42,6 +45,11 @@
|
||||
customsBrokers?: CustomsBroker[];
|
||||
clients?: ClientProvider[];
|
||||
providers?: ClientProvider[];
|
||||
seals?: any[];
|
||||
incoterms?: any[];
|
||||
enclosure?: any[];
|
||||
currencyTypes?: any[];
|
||||
transportTypes?: any[];
|
||||
user?: any;
|
||||
companies?: any[];
|
||||
authenticated?: boolean;
|
||||
@@ -63,14 +71,14 @@
|
||||
|
||||
// Referencias a los componentes de formulario para obtener sus datos
|
||||
let generalFormData = $state<any>(null);
|
||||
let complianceFormData = $state<any>(null);
|
||||
let financialsFormData = $state<any>(null);
|
||||
let logisticsFormData = $state<any>(null);
|
||||
let observationFormData = $state<any>(null);
|
||||
let itemsFormData = $state<any>(null);
|
||||
let othersFormData = $state<any>(null);
|
||||
|
||||
// Estados para saber si existen datos previos
|
||||
let complianceExists = $state(false);
|
||||
let financialsExists = $state(false);
|
||||
let logisticsExists = $state(false);
|
||||
let observationExists = $state(false);
|
||||
let itemsExists = $state(false);
|
||||
let othersExists = $state(false);
|
||||
|
||||
function handleBack() {
|
||||
goto('/dashboard/invoices');
|
||||
@@ -110,103 +118,345 @@
|
||||
|
||||
// Construir el payload unificado
|
||||
const payload: CreateInvoiceData | UpdateInvoiceData = {
|
||||
// Datos generales - convertir operation_type de número a string
|
||||
operation_type: generalFormData?.operation_type
|
||||
// Datos generales desde el formulario general
|
||||
operation_type: generalFormData?.operation_type !== null && generalFormData?.operation_type !== undefined
|
||||
? (generalFormData.operation_type === 1 ? 'exp' : 'imp') as OperationType
|
||||
: undefined,
|
||||
invoice_type: generalFormData?.invoice_type || undefined,
|
||||
invoice_number: generalFormData?.invoice_number || undefined,
|
||||
project_number: generalFormData?.project_number || undefined,
|
||||
purchase_order: generalFormData?.purchase_order || undefined,
|
||||
related_doc_id: generalFormData?.related_doc_id || undefined,
|
||||
invoice_date: generalFormData?.invoice_date || undefined,
|
||||
traffic_light_status: generalFormData?.traffic_light_status || undefined,
|
||||
process_log: generalFormData?.process_log || undefined,
|
||||
observation_es: generalFormData?.observation_es || undefined,
|
||||
observation_en: generalFormData?.observation_en || undefined,
|
||||
comments_status: generalFormData?.comments_status || undefined,
|
||||
cfdi_uuid: generalFormData?.cfdi_uuid || undefined,
|
||||
path_pdf: generalFormData?.path_pdf || undefined,
|
||||
path_xml: generalFormData?.path_xml || undefined
|
||||
emission_date: generalFormData?.emission_date || undefined,
|
||||
// Observation fields from observationFormData
|
||||
observation_es: observationFormData?.observation_es || undefined,
|
||||
observation_en: observationFormData?.observation_en || undefined,
|
||||
alternate_invoice: observationFormData?.alternate_invoice || undefined,
|
||||
};
|
||||
|
||||
// Solo agregar sub-recursos si tienen valores reales
|
||||
|
||||
// Compliance MX - solo enviar si hay al menos un campo con valor
|
||||
if (complianceFormData) {
|
||||
const hasComplianceValue = complianceFormData.pedimento || complianceFormData.pedimento_code ||
|
||||
complianceFormData.remesa || complianceFormData.aduana ||
|
||||
complianceFormData.provider_id || complianceFormData.sold_to_id ||
|
||||
complianceFormData.shipped_to_id || complianceFormData.shipped_by_id ||
|
||||
complianceFormData.customs_broker_id;
|
||||
|
||||
if (hasComplianceValue) {
|
||||
payload.compliance_mx = {
|
||||
pedimento: complianceFormData.pedimento || null,
|
||||
pedimento_code: complianceFormData.pedimento_code || null,
|
||||
remesa: complianceFormData.remesa || null,
|
||||
aduana: complianceFormData.aduana || null,
|
||||
provider_header: complianceFormData.provider_header || null,
|
||||
provider_id: complianceFormData.provider_id || null,
|
||||
sold_to_header: complianceFormData.sold_to_header || null,
|
||||
sold_to_id: complianceFormData.sold_to_id || null,
|
||||
shipped_to_header: complianceFormData.shipped_to_header || null,
|
||||
shipped_to_id: complianceFormData.shipped_to_id || null,
|
||||
shipped_by_header: complianceFormData.shipped_by_header || null,
|
||||
shipped_by_id: complianceFormData.shipped_by_id || null,
|
||||
customs_broker_id: complianceFormData.customs_broker_id || null,
|
||||
is_mixed: complianceFormData.is_mixed || null,
|
||||
waste_type: complianceFormData.waste_type || null,
|
||||
appendix_17: complianceFormData.appendix_17 || null,
|
||||
edocument: complianceFormData.edocument || null,
|
||||
electronic_signature: complianceFormData.electronic_signature || null,
|
||||
sem_id: complianceFormData.sem_id || null,
|
||||
};
|
||||
}
|
||||
// Compliance MX - combinar datos del formulario general y observations
|
||||
const hasComplianceValue = generalFormData?.pedimento || generalFormData?.remesa || generalFormData?.aduana ||
|
||||
generalFormData?.provider_id || generalFormData?.sold_to_id ||
|
||||
generalFormData?.shipped_to_id || generalFormData?.customs_broker_id ||
|
||||
observationFormData?.pedimento || observationFormData?.pedimento_code ||
|
||||
observationFormData?.remesa || observationFormData?.aduana ||
|
||||
observationFormData?.provider_id || observationFormData?.sold_to_id ||
|
||||
observationFormData?.shipped_to_id || observationFormData?.shipped_by_id ||
|
||||
observationFormData?.customs_broker_id || observationFormData?.is_mixed ||
|
||||
observationFormData?.waste_type || observationFormData?.appendix_17 ||
|
||||
observationFormData?.edocument || observationFormData?.electronic_signature ||
|
||||
observationFormData?.sem_id || observationFormData?.enclosure ||
|
||||
observationFormData?.incoterm;
|
||||
|
||||
if (hasComplianceValue) {
|
||||
payload.compliance_mx = {
|
||||
// Pedimento fields
|
||||
pedimento: generalFormData?.pedimento || observationFormData?.pedimento || null,
|
||||
pedimento_code: observationFormData?.pedimento_code || null,
|
||||
pedimento_k1: observationFormData?.pedimento_k1 || null,
|
||||
remesa: generalFormData?.remesa || observationFormData?.remesa || null,
|
||||
aduana: generalFormData?.aduana || observationFormData?.aduana || null,
|
||||
port_of_entry: observationFormData?.port_of_entry || null,
|
||||
destination: observationFormData?.destination || null,
|
||||
manifest_number: observationFormData?.manifest_number || null,
|
||||
// Client/Provider fields
|
||||
provider_header: generalFormData?.provider_header || observationFormData?.provider_header || null,
|
||||
provider_id: generalFormData?.provider_id || observationFormData?.provider_id || null,
|
||||
sold_to_header: generalFormData?.sold_to_header || observationFormData?.sold_to_header || null,
|
||||
sold_to_id: generalFormData?.sold_to_id || observationFormData?.sold_to_id || null,
|
||||
shipped_to_header: generalFormData?.shipped_to_header || observationFormData?.shipped_to_header || null,
|
||||
shipped_to_id: generalFormData?.shipped_to_id || observationFormData?.shipped_to_id || null,
|
||||
shipped_by_header: observationFormData?.shipped_by_header || null,
|
||||
shipped_by_id: observationFormData?.shipped_by_id || null,
|
||||
// Customs broker fields
|
||||
customs_broker_id: generalFormData?.customs_broker_id || observationFormData?.customs_broker_id || null,
|
||||
customs_broker_us_id: observationFormData?.customs_broker_us_id || null,
|
||||
broker_invoice_num: observationFormData?.broker_invoice_num || null,
|
||||
broker_invoice_date: observationFormData?.broker_invoice_date || null,
|
||||
// Flags & regimes
|
||||
is_mixed: observationFormData?.is_mixed || null,
|
||||
waste_type: observationFormData?.waste_type || null,
|
||||
scrap_type: observationFormData?.scrap_type || null,
|
||||
appendix_17: observationFormData?.appendix_17 || null,
|
||||
is_regime_change: observationFormData?.is_regime_change || null,
|
||||
which_exchange_rate: observationFormData?.which_exchange_rate || null,
|
||||
value_method: observationFormData?.value_method || null,
|
||||
act_value: observationFormData?.act_value || null,
|
||||
is_pedimento_pending: observationFormData?.is_pedimento_pending || null,
|
||||
// Ownership & balances
|
||||
is_owner_of_goods: observationFormData?.is_owner_of_goods || null,
|
||||
generate_balances: observationFormData?.generate_balances || null,
|
||||
was_reviewed_by_company: observationFormData?.was_reviewed_by_company || null,
|
||||
// VUCEM / Digital
|
||||
edocument: observationFormData?.edocument || null,
|
||||
electronic_signature: observationFormData?.electronic_signature || null,
|
||||
certificate_number: observationFormData?.certificate_number || null,
|
||||
niu_number: observationFormData?.niu_number || null,
|
||||
bill_of_lading_count: observationFormData?.bill_of_lading_count || null,
|
||||
addendum_vu: observationFormData?.addendum_vu || null,
|
||||
origin_destination_cove: observationFormData?.origin_destination_cove || null,
|
||||
vucem_operation_num: observationFormData?.vucem_operation_num || null,
|
||||
customs_person_line: observationFormData?.customs_person_line || null,
|
||||
// Additional control
|
||||
contingency_mode: observationFormData?.contingency_mode || null,
|
||||
enclosure: observationFormData?.enclosure || null,
|
||||
guide_type_to_identify: observationFormData?.guide_type_to_identify || null,
|
||||
location: observationFormData?.location || null,
|
||||
// DOT & official
|
||||
dot_code: observationFormData?.dot_code || null,
|
||||
subdivision: observationFormData?.subdivision || null,
|
||||
acts_as: observationFormData?.acts_as || null,
|
||||
movement_type: observationFormData?.movement_type || null,
|
||||
office_document: observationFormData?.office_document || null,
|
||||
reason_export: observationFormData?.reason_export || null,
|
||||
signature_key: observationFormData?.signature_key || null,
|
||||
// SM specific
|
||||
sem_id: observationFormData?.sem_id || null,
|
||||
};
|
||||
}
|
||||
|
||||
// Financials - solo enviar si hay al menos un campo con valor
|
||||
if (financialsFormData) {
|
||||
const hasFinancialValue = financialsFormData.currency || financialsFormData.exchange_rate ||
|
||||
financialsFormData.value_mn || financialsFormData.value_me ||
|
||||
financialsFormData.customs_value_mn || financialsFormData.freight ||
|
||||
financialsFormData.insurance;
|
||||
|
||||
if (hasFinancialValue) {
|
||||
payload.financials = {
|
||||
currency: financialsFormData.currency || null,
|
||||
currency_type: financialsFormData.currency_type || null,
|
||||
exchange_rate: financialsFormData.exchange_rate || null,
|
||||
value_mn: financialsFormData.value_mn || null,
|
||||
value_me: financialsFormData.value_me || null,
|
||||
customs_value_mn: financialsFormData.customs_value_mn || null,
|
||||
freight: financialsFormData.freight || null,
|
||||
insurance: financialsFormData.insurance || null,
|
||||
iva_mn: financialsFormData.iva_mn || null,
|
||||
iva_factor: financialsFormData.iva_factor || null,
|
||||
total_quantity: financialsFormData.total_quantity || null,
|
||||
gross_weight: financialsFormData.gross_weight || null,
|
||||
net_weight: financialsFormData.net_weight || null,
|
||||
bundle_count: financialsFormData.bundle_count || null,
|
||||
};
|
||||
}
|
||||
// Financials - combinar datos del formulario general y items
|
||||
const hasFinancialsValue = generalFormData?.currency_type ||
|
||||
generalFormData?.iva_factor ||
|
||||
itemsFormData?.currency || itemsFormData?.exchange_rate ||
|
||||
itemsFormData?.value_mn || itemsFormData?.value_me ||
|
||||
itemsFormData?.customs_value_mn || itemsFormData?.freight ||
|
||||
itemsFormData?.insurance;
|
||||
|
||||
if (hasFinancialsValue) {
|
||||
payload.financials = {
|
||||
// Currency
|
||||
currency: itemsFormData?.currency || null,
|
||||
currency_type: generalFormData?.currency_type || itemsFormData?.currency_type || null,
|
||||
exchange_rate: itemsFormData?.exchange_rate || null,
|
||||
exchange_rate_mm: itemsFormData?.exchange_rate_mm || null,
|
||||
// Merchandise values
|
||||
value_mn: itemsFormData?.value_mn || null,
|
||||
value_me: itemsFormData?.value_me || null,
|
||||
value_mc: itemsFormData?.value_mc || null,
|
||||
// Customs value
|
||||
customs_value_mn: itemsFormData?.customs_value_mn || null,
|
||||
customs_value_me: itemsFormData?.customs_value_me || null,
|
||||
// Raw materials
|
||||
raw_material_value_mn: itemsFormData?.raw_material_value_mn || null,
|
||||
raw_material_value_me: itemsFormData?.raw_material_value_me || null,
|
||||
// Aggregate value
|
||||
aggregate_value_mn: itemsFormData?.aggregate_value_mn || null,
|
||||
aggregate_value_me: itemsFormData?.aggregate_value_me || null,
|
||||
aggregate_value_mc: itemsFormData?.aggregate_value_mc || null,
|
||||
// Mexican merchandise value
|
||||
mexican_value_mn: itemsFormData?.mexican_value_mn || null,
|
||||
mexican_value_me: itemsFormData?.mexican_value_me || null,
|
||||
mexican_value_mc: itemsFormData?.mexican_value_mc || null,
|
||||
// National packaging
|
||||
national_packaging_mn: itemsFormData?.national_packaging_mn || null,
|
||||
national_packaging_me: itemsFormData?.national_packaging_me || null,
|
||||
national_packaging_mc: itemsFormData?.national_packaging_mc || null,
|
||||
// Costs & increments
|
||||
freight: itemsFormData?.freight || observationFormData?.freight || null,
|
||||
insurance: itemsFormData?.insurance || observationFormData?.insurance || null,
|
||||
insurance_value: itemsFormData?.insurance_value || observationFormData?.insurance_value || null,
|
||||
packaging: itemsFormData?.packaging || observationFormData?.packaging || null,
|
||||
other_increments: itemsFormData?.other_increments || observationFormData?.other_increments || null,
|
||||
total_increments_mn: itemsFormData?.total_increments_mn || observationFormData?.total_increments_mn || null,
|
||||
total_increments_me: itemsFormData?.total_increments_me || observationFormData?.total_increments_me || null,
|
||||
// Taxes
|
||||
iva_mn: itemsFormData?.iva_mn || null,
|
||||
iva_me: itemsFormData?.iva_me || null,
|
||||
iva_mc: itemsFormData?.iva_mc || null,
|
||||
iva_factor: generalFormData?.iva_factor || itemsFormData?.iva_factor || null,
|
||||
tax_value_me: itemsFormData?.tax_value_me || null,
|
||||
seal_value_2500: itemsFormData?.seal_value_2500 || null,
|
||||
// Weights & quantities
|
||||
total_quantity: itemsFormData?.total_quantity || null,
|
||||
gross_weight: itemsFormData?.gross_weight || null,
|
||||
net_weight: itemsFormData?.net_weight || null,
|
||||
bundle_count: itemsFormData?.bundle_count || null,
|
||||
weight_factor: itemsFormData?.weight_factor || null,
|
||||
};
|
||||
}
|
||||
|
||||
// Logistics - array, se envía si hay elementos
|
||||
if (logisticsFormData && Array.isArray(logisticsFormData) && logisticsFormData.length > 0) {
|
||||
payload.logistics = logisticsFormData.map((item: any) => ({
|
||||
carrier_id: item.carrier_id || null,
|
||||
transport_type: item.transport_type || null,
|
||||
transport_mode: item.transport_mode || null,
|
||||
driver_name: item.driver_name || null,
|
||||
is_rail: item.is_rail || null,
|
||||
rail_id: item.rail_id || null,
|
||||
vehicle_num: item.vehicle_num || null,
|
||||
license_plate: item.license_plate || null,
|
||||
seal_number: item.seal_number || null,
|
||||
guide_number: item.guide_number || null,
|
||||
entry_exit_date: item.entry_exit_date || null,
|
||||
}));
|
||||
// Logistics - combinar datos del formulario general con othersFormData
|
||||
const hasLogisticsFromGeneral = generalFormData?.carrier_id ||
|
||||
generalFormData?.driver_name || generalFormData?.transport_type || generalFormData?.transport_num;
|
||||
|
||||
if (hasLogisticsFromGeneral || (othersFormData && Array.isArray(othersFormData) && othersFormData.length > 0)) {
|
||||
// Si hay datos en el formulario general, crear/actualizar el primer elemento
|
||||
if (hasLogisticsFromGeneral) {
|
||||
const logisticsEntry = {
|
||||
carrier_id: generalFormData?.carrier_id || null,
|
||||
transport_type: generalFormData?.transport_type || null,
|
||||
transport_mode: null,
|
||||
driver_name: generalFormData?.driver_name || null,
|
||||
is_rail: null,
|
||||
rail_id: null,
|
||||
vehicle_num: generalFormData?.transport_num || null,
|
||||
license_plate: null,
|
||||
seal_number: null,
|
||||
guide_number: null,
|
||||
entry_exit_date: null,
|
||||
};
|
||||
|
||||
// Si también hay datos del formulario de others, combinarlos
|
||||
if (othersFormData && Array.isArray(othersFormData) && othersFormData.length > 0) {
|
||||
// Actualizar el primer elemento con datos del general
|
||||
payload.logistics = [
|
||||
{
|
||||
// Carrier info
|
||||
carrier_id: generalFormData?.carrier_id || othersFormData[0].carrier_id || null,
|
||||
transport_id: othersFormData[0].transport_id || null,
|
||||
transport_us_id: othersFormData[0].transport_us_id || null,
|
||||
transport_type: generalFormData?.transport_type || othersFormData[0].transport_type || null,
|
||||
transport_num: othersFormData[0].transport_num || null,
|
||||
transport_mode: othersFormData[0].transport_mode || null,
|
||||
driver_name: generalFormData?.driver_name || othersFormData[0].driver_name || null,
|
||||
is_rail: othersFormData[0].is_rail || null,
|
||||
rail_id: othersFormData[0].rail_id || null,
|
||||
// Vehicle & tracking
|
||||
vehicle_num: generalFormData?.transport_num || othersFormData[0].vehicle_num || null,
|
||||
license_plate: othersFormData[0].license_plate || null,
|
||||
license_plate_complete: othersFormData[0].license_plate_complete || null,
|
||||
trailer_num: othersFormData[0].trailer_num || null,
|
||||
seal_number: othersFormData[0].seal_number || null,
|
||||
guide_number: othersFormData[0].guide_number || null,
|
||||
bill_number: othersFormData[0].bill_number || null,
|
||||
reference_number: othersFormData[0].reference_number || null,
|
||||
shipment_number: othersFormData[0].shipment_number || null,
|
||||
// Incoterms
|
||||
incoterm: othersFormData[0].incoterm || observationFormData?.incoterm || null,
|
||||
// Identifiers & complements
|
||||
identifier_1: othersFormData[0].identifier_1 || null,
|
||||
complement_1: othersFormData[0].complement_1 || null,
|
||||
identifier_2: othersFormData[0].identifier_2 || null,
|
||||
complement_2: othersFormData[0].complement_2 || null,
|
||||
// Weight & container info
|
||||
weight_type: othersFormData[0].weight_type || null,
|
||||
container_types: othersFormData[0].container_types || null,
|
||||
vehicle_data: othersFormData[0].vehicle_data || null,
|
||||
// Locations & routes
|
||||
origin_location: othersFormData[0].origin_location || null,
|
||||
destination_location: othersFormData[0].destination_location || null,
|
||||
transport_itinerary: othersFormData[0].transport_itinerary || null,
|
||||
destination_goods: othersFormData[0].destination_goods || null,
|
||||
// Logistics dates
|
||||
entry_exit_date: othersFormData[0].entry_exit_date || null,
|
||||
delivery_date: othersFormData[0].delivery_date || null,
|
||||
// Delivery control
|
||||
delivered_status: othersFormData[0].delivered_status || null,
|
||||
received_by: othersFormData[0].received_by || null,
|
||||
// Payment info
|
||||
payment_date: othersFormData[0].payment_date || null,
|
||||
payment_receipt_num: othersFormData[0].payment_receipt_num || null,
|
||||
// CTM process
|
||||
is_ctm_process: othersFormData[0].is_ctm_process || null,
|
||||
},
|
||||
// Agregar los demás elementos si existen
|
||||
...othersFormData.slice(1).map((item: any) => ({
|
||||
// Carrier info
|
||||
carrier_id: item.carrier_id || null,
|
||||
transport_id: item.transport_id || null,
|
||||
transport_us_id: item.transport_us_id || null,
|
||||
transport_type: item.transport_type || null,
|
||||
transport_num: item.transport_num || null,
|
||||
transport_mode: item.transport_mode || null,
|
||||
driver_name: item.driver_name || null,
|
||||
is_rail: item.is_rail || null,
|
||||
rail_id: item.rail_id || null,
|
||||
// Vehicle & tracking
|
||||
vehicle_num: item.vehicle_num || null,
|
||||
license_plate: item.license_plate || null,
|
||||
license_plate_complete: item.license_plate_complete || null,
|
||||
trailer_num: item.trailer_num || null,
|
||||
seal_number: item.seal_number || null,
|
||||
guide_number: item.guide_number || null,
|
||||
bill_number: item.bill_number || null,
|
||||
reference_number: item.reference_number || null,
|
||||
shipment_number: item.shipment_number || null,
|
||||
// Incoterms
|
||||
incoterm: item.incoterm || null,
|
||||
// Identifiers & complements
|
||||
identifier_1: item.identifier_1 || null,
|
||||
complement_1: item.complement_1 || null,
|
||||
identifier_2: item.identifier_2 || null,
|
||||
complement_2: item.complement_2 || null,
|
||||
// Weight & container info
|
||||
weight_type: item.weight_type || null,
|
||||
container_types: item.container_types || null,
|
||||
vehicle_data: item.vehicle_data || null,
|
||||
// Locations & routes
|
||||
origin_location: item.origin_location || null,
|
||||
destination_location: item.destination_location || null,
|
||||
transport_itinerary: item.transport_itinerary || null,
|
||||
destination_goods: item.destination_goods || null,
|
||||
// Logistics dates
|
||||
entry_exit_date: item.entry_exit_date || null,
|
||||
delivery_date: item.delivery_date || null,
|
||||
// Delivery control
|
||||
delivered_status: item.delivered_status || null,
|
||||
received_by: item.received_by || null,
|
||||
// Payment info
|
||||
payment_date: item.payment_date || null,
|
||||
payment_receipt_num: item.payment_receipt_num || null,
|
||||
// CTM process
|
||||
is_ctm_process: item.is_ctm_process || null,
|
||||
}))
|
||||
];
|
||||
} else {
|
||||
// Solo datos del general
|
||||
payload.logistics = [logisticsEntry];
|
||||
}
|
||||
} else {
|
||||
// Solo datos del formulario others
|
||||
payload.logistics = othersFormData.map((item: any) => ({
|
||||
// Carrier info
|
||||
carrier_id: item.carrier_id || null,
|
||||
transport_id: item.transport_id || null,
|
||||
transport_us_id: item.transport_us_id || null,
|
||||
transport_type: item.transport_type || null,
|
||||
transport_num: item.transport_num || null,
|
||||
transport_mode: item.transport_mode || null,
|
||||
driver_name: item.driver_name || null,
|
||||
is_rail: item.is_rail || null,
|
||||
rail_id: item.rail_id || null,
|
||||
// Vehicle & tracking
|
||||
vehicle_num: item.vehicle_num || null,
|
||||
license_plate: item.license_plate || null,
|
||||
license_plate_complete: item.license_plate_complete || null,
|
||||
trailer_num: item.trailer_num || null,
|
||||
seal_number: item.seal_number || null,
|
||||
guide_number: item.guide_number || null,
|
||||
bill_number: item.bill_number || null,
|
||||
reference_number: item.reference_number || null,
|
||||
shipment_number: item.shipment_number || null,
|
||||
// Incoterms
|
||||
incoterm: item.incoterm || observationFormData?.incoterm || null,
|
||||
// Identifiers & complements
|
||||
identifier_1: item.identifier_1 || null,
|
||||
complement_1: item.complement_1 || null,
|
||||
identifier_2: item.identifier_2 || null,
|
||||
complement_2: item.complement_2 || null,
|
||||
// Weight & container info
|
||||
weight_type: item.weight_type || null,
|
||||
container_types: item.container_types || null,
|
||||
vehicle_data: item.vehicle_data || null,
|
||||
// Locations & routes
|
||||
origin_location: item.origin_location || null,
|
||||
destination_location: item.destination_location || null,
|
||||
transport_itinerary: item.transport_itinerary || null,
|
||||
destination_goods: item.destination_goods || null,
|
||||
// Logistics dates
|
||||
entry_exit_date: item.entry_exit_date || null,
|
||||
delivery_date: item.delivery_date || null,
|
||||
// Delivery control
|
||||
delivered_status: item.delivered_status || null,
|
||||
received_by: item.received_by || null,
|
||||
// Payment info
|
||||
payment_date: item.payment_date || null,
|
||||
payment_receipt_num: item.payment_receipt_num || null,
|
||||
// CTM process
|
||||
is_ctm_process: item.is_ctm_process || null,
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
// Eliminar campos undefined para no enviarlos
|
||||
@@ -257,7 +507,7 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="space-y-6">
|
||||
<div class="space-y-3">
|
||||
<!-- Header -->
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="space-y-1">
|
||||
@@ -318,41 +568,63 @@
|
||||
|
||||
<!-- Contenido de las tabs con padding inferior para el footer flotante -->
|
||||
<div class="pb-48">
|
||||
<Tabs.Root bind:value={activeTab} class="space-y-4">
|
||||
<Tabs.Content value="general">
|
||||
<Tabs.Root bind:value={activeTab}>
|
||||
<InvoiceTopFields
|
||||
invoice={data.invoice}
|
||||
bind:formData={generalFormData}
|
||||
invoiceTypes={data.invoiceTypes || []}
|
||||
defaultOperationType={data.filters?.operation_type ?? undefined}
|
||||
defaultInvoiceType={data.filters?.invoice_type ?? undefined}
|
||||
/>
|
||||
|
||||
<Tabs.Content value="general">
|
||||
<GeneralTabForm
|
||||
invoice={data.invoice}
|
||||
bind:formData={generalFormData}
|
||||
invoiceTypes={data.invoiceTypes || []}
|
||||
customsBrokers={data.customsBrokers || []}
|
||||
clients={data.clients || []}
|
||||
providers={data.providers || []}
|
||||
currencyTypes={data.currencyTypes || []}
|
||||
transportTypes={data.transportTypes || []}
|
||||
defaultOperationType={data.filters?.operation_type ?? undefined}
|
||||
defaultInvoiceType={data.filters?.invoice_type ?? undefined}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="compliance">
|
||||
<ComplianceTabForm
|
||||
<Tabs.Content value="observations">
|
||||
<ObservationsTabForm
|
||||
invoice={data.invoice}
|
||||
bind:formData={complianceFormData}
|
||||
bind:exists={complianceExists}
|
||||
customsBrokers={data.customsBrokers || []}
|
||||
clients={data.clients || []}
|
||||
providers={data.providers || []}
|
||||
bind:formData={observationFormData}
|
||||
bind:exists={observationExists}
|
||||
seals={data.seals || []}
|
||||
incoterms={data.incoterms || []}
|
||||
enclosure={data.enclosure || []}
|
||||
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="financials">
|
||||
<FinancialsTabForm
|
||||
<Tabs.Content value="items">
|
||||
<ItemsTabForm
|
||||
invoice={data.invoice}
|
||||
bind:formData={financialsFormData}
|
||||
bind:exists={financialsExists}
|
||||
bind:formData={itemsFormData}
|
||||
bind:exists={itemsExists}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="logistics">
|
||||
<LogisticsTabForm
|
||||
<Tabs.Content value="others">
|
||||
<OthersTabForm
|
||||
invoice={data.invoice}
|
||||
bind:formData={logisticsFormData}
|
||||
bind:exists={logisticsExists}
|
||||
bind:formData={othersFormData}
|
||||
bind:exists={othersExists}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
|
||||
<Tabs.Content value="continuation">
|
||||
<ContinuationTabForm
|
||||
invoice={data.invoice}
|
||||
bind:formData={itemsFormData}
|
||||
bind:exists={itemsExists}
|
||||
/>
|
||||
</Tabs.Content>
|
||||
</Tabs.Root>
|
||||
@@ -368,22 +640,26 @@
|
||||
<!-- Tabs Navigation -->
|
||||
<Tabs.Root bind:value={activeTab}>
|
||||
<div class="w-full overflow-x-auto pb-2">
|
||||
<Tabs.List class="inline-flex md:grid md:w-full md:grid-cols-4">
|
||||
<Tabs.List class="inline-flex md:grid md:w-full md:grid-cols-5">
|
||||
<Tabs.Trigger value="general" disabled={false} class="whitespace-nowrap">
|
||||
<FileText size={16} class="mr-2" />
|
||||
General
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="compliance" disabled={false} class="whitespace-nowrap">
|
||||
<Package size={16} class="mr-2" />
|
||||
Cumplimiento
|
||||
<Tabs.Trigger value="observations" disabled={false} class="whitespace-nowrap">
|
||||
<Eye size={16} class="mr-2" />
|
||||
Observaciones
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="financials" disabled={false} class="whitespace-nowrap">
|
||||
<Tabs.Trigger value="items" disabled={false} class="whitespace-nowrap">
|
||||
<DollarSign size={16} class="mr-2" />
|
||||
Financieros
|
||||
Partidas
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="logistics" disabled={false} class="whitespace-nowrap">
|
||||
<Tabs.Trigger value="others" disabled={false} class="whitespace-nowrap">
|
||||
<Truck size={16} class="mr-2" />
|
||||
Logística
|
||||
Otros
|
||||
</Tabs.Trigger>
|
||||
<Tabs.Trigger value="continuation" disabled={false} class="whitespace-nowrap">
|
||||
<Package size={16} class="mr-2" />
|
||||
Cont.
|
||||
</Tabs.Trigger>
|
||||
</Tabs.List>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user