From c040679fc5117737678088f39bdf77764fd749f5 Mon Sep 17 00:00:00 2001 From: AlexeerCT Date: Thu, 8 Jan 2026 16:24:34 -0600 Subject: [PATCH] feat: Enhance invoice item management with detailed line item structure - Introduced nested interfaces for line items including customs, financials, quantities, descriptions, and references. - Updated Item interface to include lines as an array of LineItem. - Modified invoice top fields to handle pedimento ID and auto-assign values from the invoice. - Enhanced item configuration component to manage line item properties and descriptions. - Updated main data, packages section, summary section, and other components to bind new line item properties. - Implemented normalization of numeric values when editing items to ensure consistent data types. - Adjusted save invoice logic to accommodate new line item structure and compliance data. --- backend/api/v1/modules/a76/invoices/models.py | 6 +- .../modules/a76/items/line_items/schemas.py | 10 +- backend/api/v1/modules/a76/items/service.py | 31 ++++- .../src/lib/api/dashboard/a76/invoices.ts | 8 +- frontend/src/lib/api/dashboard/a76/items.ts | 113 ++++++++++++++++ .../invoices/edit/invoice-top-fields.svelte | 13 +- .../edit/items/fa/item-configuration.svelte | 34 ++++- .../edit/items/fa/item-sheet-fa.svelte | 49 +++++-- .../invoices/edit/items/fa/main-data.svelte | 33 +++-- .../edit/items/fa/packages-section.svelte | 38 ++++-- .../edit/items/fa/summary-section.svelte | 29 ++-- .../edit/items/fa/tab-continuation.svelte | 32 ++++- .../edit/items/fa/tab-identifiers.svelte | 5 +- .../edit/items/fa/tab-labeling.svelte | 4 + .../invoices/edit/items/fa/tab-series.svelte | 4 + .../invoices/edit/items/items-tab-form.svelte | 128 +++++++++++++++++- .../dashboard/invoices/edit/save-invoice.ts | 4 +- 17 files changed, 466 insertions(+), 75 deletions(-) diff --git a/backend/api/v1/modules/a76/invoices/models.py b/backend/api/v1/modules/a76/invoices/models.py index ca6d6845..3bb23271 100644 --- a/backend/api/v1/modules/a76/invoices/models.py +++ b/backend/api/v1/modules/a76/invoices/models.py @@ -136,9 +136,9 @@ class InvoiceComplianceMx(Base, TenantScopedMixin, TimestampMixin): invoice_id: Mapped[int] = mapped_column(ForeignKey("a76.invoice_header.id"), primary_key=True) # Core Customs Data - pedimento_id: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTO/PEDIMENTOIMPO/EXPO - pedimento_r1: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTOR1 - pedimento_k1: Mapped[Optional[str]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTOK1 + pedimento_id: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTO/PEDIMENTOIMPO/EXPO + pedimento_r1: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTOR1 + pedimento_k1: Mapped[Optional[int]] = mapped_column(ForeignKey("a76.pedimentos.id")) # PEDIMENTOK1 remesa: Mapped[Optional[int]] = mapped_column(Integer) # REMESA aduana: Mapped[Optional[str]] = mapped_column(ForeignKey("public.customs_sections.customs_code")) # ADUANA_CRUCE port_of_entry: Mapped[Optional[str]] = mapped_column(String(6)) # PUERTOENTRADA / Puerto de entrada diff --git a/backend/api/v1/modules/a76/items/line_items/schemas.py b/backend/api/v1/modules/a76/items/line_items/schemas.py index c490352f..771196fb 100644 --- a/backend/api/v1/modules/a76/items/line_items/schemas.py +++ b/backend/api/v1/modules/a76/items/line_items/schemas.py @@ -1,6 +1,6 @@ from decimal import Decimal from typing import Optional -from pydantic import BaseModel, Field, ConfigDict +from pydantic import BaseModel, Field, ConfigDict, field_validator # Import nested schemas from ..line_customs.schemas import ( @@ -42,6 +42,14 @@ class LineItemBase(BaseModel): component_part_number: Optional[str] = Field(None, max_length=50, description="Component part number") class_code: Optional[str] = Field(None, max_length=20, description="Class code") + @field_validator('class_code', 'part_number', 'component_part_number', 'unit_of_measure', 'alternate_unit', mode='before') + @classmethod + def convert_to_string(cls, v): + """Convert integers to strings for FK fields""" + if v is not None and not isinstance(v, str): + return str(v) + return v + # Unit of measure unit_of_measure: Optional[str] = Field(None, max_length=10, description="Unit of measure") alternate_unit: Optional[str] = Field(None, max_length=10, description="Alternate unit") diff --git a/backend/api/v1/modules/a76/items/service.py b/backend/api/v1/modules/a76/items/service.py index d8a30b99..4cc4c946 100644 --- a/backend/api/v1/modules/a76/items/service.py +++ b/backend/api/v1/modules/a76/items/service.py @@ -151,6 +151,12 @@ class ItemService: lines_data = item_data.lines or [] item_dict = item_data.model_dump(exclude={"lines"}) + # DEBUG: Log incoming data + print(f"\nπŸ” DEBUG CREATE ITEM:") + print(f" Item data: {item_dict}") + print(f" Lines count: {len(lines_data)}") + print(f" Tenant ID: {tenant_id}, Company ID: {company_id}") + # Add tenant and company item_dict["tenant_id"] = tenant_id item_dict["company_id"] = company_id @@ -160,8 +166,11 @@ class ItemService: db.add(db_item) db.flush() # Get the item ID + print(f" βœ… Item created with ID: {db_item.id}") + # Create line items if provided - for line_data in lines_data: + for idx, line_data in enumerate(lines_data): + print(f"\n πŸ“ Processing line {idx + 1}/{len(lines_data)}") # Extract nested data from line financial_data = line_data.financial quantity_data = line_data.quantity @@ -169,16 +178,26 @@ class ItemService: description_data = line_data.description reference_data = line_data.reference + print(f" Line data: {line_data.model_dump()}") + print(f" Has financial: {financial_data is not None}") + print(f" Has quantity: {quantity_data is not None}") + print(f" Has customs: {customs_data is not None}") + print(f" Has description: {description_data is not None}") + print(f" Has reference: {reference_data is not None}") + line_dict = line_data.model_dump( exclude={"financial", "quantity", "customs", "description", "reference"} ) line_dict["item_id"] = db_item.id + line_dict["tenant_id"] = tenant_id + line_dict["company_id"] = company_id # Create line item db_line = LineItem(**line_dict) db.add(db_line) db.flush() # Get the line ID + print(f" βœ… Line created with ID: {db_line.id}") # Create financial data if provided if financial_data: @@ -186,6 +205,7 @@ class ItemService: financial_dict["item_line_id"] = db_line.id db_financial = LineFinancial(**financial_dict) db.add(db_financial) + print(f" βœ… Financial data added") # Create quantity data if provided if quantity_data: @@ -193,6 +213,7 @@ class ItemService: quantity_dict["item_line_id"] = db_line.id db_quantity = LineQuantity(**quantity_dict) db.add(db_quantity) + print(f" βœ… Quantity data added") # Create customs data if provided if customs_data: @@ -200,6 +221,7 @@ class ItemService: customs_dict["item_line_id"] = db_line.id db_customs = LineCustom(**customs_dict) db.add(db_customs) + print(f" βœ… Customs data added") # Create description data if provided if description_data: @@ -207,6 +229,7 @@ class ItemService: description_dict["item_line_id"] = db_line.id db_description = LineDescription(**description_dict) db.add(db_description) + print(f" βœ… Description data added") # Create reference data if provided if reference_data: @@ -214,9 +237,12 @@ class ItemService: reference_dict["item_line_id"] = db_line.id db_reference = LineReference(**reference_dict) db.add(db_reference) + print(f" βœ… Reference data added") + print(f"\n πŸ’Ύ Committing transaction...") db.commit() db.refresh(db_item) + print(f" βœ… Transaction committed successfully!") return db_item except IntegrityError as e: @@ -277,6 +303,9 @@ class ItemService: exclude_unset=True ) line_dict["item_id"] = db_item.id + line_dict["tenant_id"] = tenant_id + line_dict["company_id"] = company_id + db_line = LineItem(**line_dict) db.add(db_line) db.flush() diff --git a/frontend/src/lib/api/dashboard/a76/invoices.ts b/frontend/src/lib/api/dashboard/a76/invoices.ts index cedb7e74..4af6238b 100644 --- a/frontend/src/lib/api/dashboard/a76/invoices.ts +++ b/frontend/src/lib/api/dashboard/a76/invoices.ts @@ -11,9 +11,9 @@ export type TransportType = 'none' | 'transport' | 'box' | 'licence plates' | 't export interface InvoiceComplianceMx { invoice_id?: number; - pedimento?: string | null; - pedimento_code?: string | null; - pedimento_k1?: string | null; + pedimento_id?: number | null; + pedimento_r1?: number | null; + pedimento_k1?: number | null; remesa?: number | null; aduana?: string | null; port_of_entry?: string | null; @@ -24,7 +24,7 @@ export interface InvoiceComplianceMx { sold_to_header?: string | null; sold_to_id?: number | null; shipped_to_header?: string | null; - shipped_to_id?: string | null; + shipped_to_id?: number | null; shipped_by_header?: string | null; shipped_by_id?: number | null; customs_broker_id?: number | null; diff --git a/frontend/src/lib/api/dashboard/a76/items.ts b/frontend/src/lib/api/dashboard/a76/items.ts index e474411c..3fe23462 100644 --- a/frontend/src/lib/api/dashboard/a76/items.ts +++ b/frontend/src/lib/api/dashboard/a76/items.ts @@ -6,6 +6,115 @@ import { api } from '$lib/api'; // --- Interfaces --- +// --- Nested Interfaces for Line Items --- + +export interface LineCustoms { + id?: number; + line_item_id?: number; + fraction?: string; + fraction_type?: string; + american_fraction?: string; + origin_country?: string; + destination_country?: string; + advalorem?: string; + advalorem_american?: number; // Backend: Decimal + sector?: string; +} + +export interface LineFinancials { + id?: number; + line_item_id?: number; + // Costs + unit_cost_usd?: number; + unit_cost_mxn?: number; + unit_cost_capture?: number; + unit_cost_commercial_usd?: number; + + // Values + value_usd?: number; + value_mxn?: number; + value_returned_usd?: number; + value_returned_mxn?: number; + customs_value_usd?: number; +} + +export interface LineQuantities { + id?: number; + line_item_id?: number; + quantity?: number; + unit_of_measure?: string; + + // Special quantities + quantity_temp_export?: number; + quantity_returned?: number; + + // Weight + net_weight?: number; + gross_weight?: number; + + // Packaging + package_key?: string; + package_quantity?: number; + package_description?: string; +} + +export interface LineDescriptions { + id?: number; + line_item_id?: number; + description_spanish?: string; + description_english?: string; + extra_description?: string; + additional_info_spanish?: string; + brand?: string; + model?: string; + has_serial?: boolean; +} + +export interface LineReferences { + id?: number; + line_item_id?: number; + serie_id?: number; +} + +export interface LineItem { + id?: number; + item_id?: number; + line_number: number; + + // Identification + part_number?: string; + component_part_number?: string; + class_code?: string; + identifier?: string; + + // Unit of Measure + unit_of_measure?: string; + alternate_unit?: string; + + // Permits + permit_number?: string; + page_line?: string; + has_certificate?: boolean; + certificate_number?: string; + + // Flags + is_subitem?: boolean; + includes_subitems?: boolean; + tax_payment?: boolean; + + // Payment + payment_method?: string; + igi_amount?: number; + + + // Nested relations (Singular names to match backend Pydantic models) + customs?: LineCustoms; + financial?: LineFinancials; + quantity?: LineQuantities; + description?: LineDescriptions; + reference?: LineReferences; +} + export interface Item { id?: number; invoice_id: number; @@ -18,6 +127,8 @@ export interface Item { location?: string; created_at?: string; updated_at?: string; + + lines?: LineItem[]; } export interface ItemListResponse { @@ -36,6 +147,7 @@ export interface CreateItemData { rectification?: number; warehouse?: string; location?: string; + lines?: LineItem[]; } export interface UpdateItemData { @@ -46,6 +158,7 @@ export interface UpdateItemData { rectification?: boolean; warehouse?: string; location?: string; + lines?: LineItem[]; } /** diff --git a/frontend/src/lib/components/dashboard/invoices/edit/invoice-top-fields.svelte b/frontend/src/lib/components/dashboard/invoices/edit/invoice-top-fields.svelte index d80b572a..47743eef 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/invoice-top-fields.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/invoice-top-fields.svelte @@ -36,10 +36,16 @@ formData.regimen_pedimento = selectedPedimento.regime || ''; // Construir el nΓΊmero de pedimento completo - const pedimentoNumber = `${selectedPedimento.year || ''}-${selectedPedimento.customs_office || ''}-${selectedPedimento.license || ''}-${selectedPedimento.pedimento_number || ''}`.replace(/^-+|-+$/g, ''); + const pedimentoNumber = `${selectedPedimento.customs_office?.slice(0,2) || ''}-${selectedPedimento.license || ''}-${selectedPedimento.pedimento_number || ''}`.replace(/^-+|-+$/g, ''); formData.pedimento = pedimentoNumber; } + $effect(() => { + if (formData?.pedimento_id && pedimentos.length > 0 && !formData.pedimento) { + handlePedimentoChange(formData.pedimento_id); + } + }); + if (!formData) { let operationType: string | null = null; if (invoice?.operation_type) { @@ -50,8 +56,7 @@ formData = { is_pedimento_pending: false, - pedimento_id: null, - pedimento: invoice?.compliance_mx?.pedimento || '', + pedimento_id: invoice?.compliance_mx?.pedimento_id || '', remesa: invoice?.compliance_mx?.remesa || '', invoice_number: invoice?.invoice_number || '', invoice_date: invoice?.invoice_date || new Date().toISOString().split('T')[0], @@ -147,7 +152,7 @@ {#each pedimentos as pedimento} - {pedimento.year}-{pedimento.customs_office}-{pedimento.license}-{pedimento.pedimento_number} + {pedimento.customs_office?.slice(0, 2)}-{pedimento.license}-{pedimento.pedimento_number} {/each} diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-configuration.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-configuration.svelte index 374d4067..178a48de 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-configuration.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-configuration.svelte @@ -2,14 +2,26 @@ import * as RadioGroup from '$lib/components/ui/radio-group'; import { Input } from '$lib/components/ui/input'; import { Label } from '$lib/components/ui/label'; + import type { LineItem, LineDescriptions } from '$lib/api/dashboard/a76/items'; let { - isSubPartida = $bindable(), - continueSubPartidas = $bindable() + lineItem = $bindable(), + descriptions = $bindable() }: { - isSubPartida: string; - continueSubPartidas: string; + lineItem: LineItem; + descriptions: LineDescriptions; } = $props(); + + // Helper to map boolean to string for RadioGroup + let isSubPartidaValue = $derived(lineItem.is_subitem ? 'subpartida' : 'partida'); + function setIsSubPartida(val: string) { + lineItem.is_subitem = val === 'subpartida'; + } + + let continueSubPartidasValue = $derived(lineItem.includes_subitems ? 'si' : 'no'); + function setContinueSubPartidas(val: string) { + lineItem.includes_subitems = val === 'si'; + }
@@ -17,7 +29,10 @@
Is - +
@@ -31,7 +46,10 @@
Continue Sub-Items - +
@@ -49,7 +67,7 @@
- +
@@ -57,6 +75,7 @@
@@ -65,6 +84,7 @@
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte index ae9cdac2..194af261 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/item-sheet-fa.svelte @@ -49,7 +49,7 @@ Temporary Import Item - Order Number: {invoice?.invoice_number || 'N/A'} | Line: 1 + Order Number: {invoice?.invoice_number || 'N/A'} | Line: currentline @@ -58,11 +58,23 @@
- + {#if editingItem.lines && editingItem.lines.length > 0} + + {/if}
- + {#if editingItem.lines && editingItem.lines.length > 0} + + {/if}
@@ -78,29 +90,48 @@
- - + {#if editingItem.lines && editingItem.lines.length > 0} + + + {/if}
- + {#if editingItem.lines && editingItem.lines.length > 0} + + {/if} - + {#if editingItem.lines && editingItem.lines.length > 0} + + {/if} - + {#if editingItem.lines && editingItem.lines.length > 0} + + {/if} - + {#if editingItem.lines && editingItem.lines.length > 0} + + {/if}
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte index 6230db8e..891d798c 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/main-data.svelte @@ -1,6 +1,19 @@
@@ -11,7 +24,7 @@
- +
@@ -20,13 +33,13 @@
- +
- - +
@@ -37,14 +50,14 @@
- + USD
- +
@@ -54,20 +67,20 @@
- +
- + +
- 0.00 +
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte index dfc5fd75..40183ca1 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/packages-section.svelte @@ -1,6 +1,21 @@
@@ -9,11 +24,11 @@
- +
- +
@@ -25,6 +40,7 @@
+
@@ -34,11 +50,11 @@
- +
- +
@@ -50,37 +66,37 @@
- +
- +
- +
- Advalorem: 0.00 + Advalorem: {customs.advalorem_american || '0.00'}
- +
- +
- +
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte index 186aedfe..3c37fa81 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/summary-section.svelte @@ -1,4 +1,7 @@
@@ -7,19 +10,19 @@
RETURN QUANTITY SUB-ITEMS
-
Temporary: 0.00000000
+
Temporary: {quantities.quantity_temp_export?.toFixed(8) || '0.00000000'}
Replacement or Change: 0.00000000
-
Definitive: 0.00000000
-
Returned Values: 0.00000000
-
Returned Values: 0.00000000
+
Definitive: {quantities.quantity_returned?.toFixed(8) || '0.00000000'}
+
Returned Values: {financials.value_returned_usd?.toFixed(8) || '0.00000000'}
+
Returned Values: {financials.value_returned_mxn?.toFixed(8) || '0.00000000'}
WEIGHTS (KILOS)
WEIGHTS (Pounds)
-
Net: 0.00000000
+
Net: {quantities.net_weight?.toFixed(8) || '0.00000000'}
0.00000000
-
Whole: 0.00000000
+
Whole: {quantities.gross_weight?.toFixed(8) || '0.00000000'}
0.00000000
@@ -31,16 +34,16 @@
(Dollars)
(Pesos)
-
Cost: 0.00000000
-
0.00000000
-
Value: 0.00000000
-
0.00000000
+
Cost: {financials.unit_cost_usd?.toFixed(8) || '0.00000000'}
+
{financials.unit_cost_mxn?.toFixed(8) || '0.00000000'}
+
Value: {financials.value_usd?.toFixed(8) || '0.00000000'}
+
{financials.value_mxn?.toFixed(8) || '0.00000000'}
-
Capture Cost: 0.00000000 USD
-
Capture Value: 0.00000000 USD
-
Customs Value: 0.00000000 USD
+
Capture Cost: {financials.unit_cost_capture?.toFixed(8) || '0.00000000'} USD
+
Capture Value: {financials.value_usd?.toFixed(8) || '0.00000000'} USD
+
Customs Value: {financials.customs_value_usd?.toFixed(8) || '0.00000000'} USD
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-continuation.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-continuation.svelte index cd4ff233..b44702d5 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-continuation.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-continuation.svelte @@ -3,6 +3,23 @@ import { Input } from '$lib/components/ui/input'; import { Label } from '$lib/components/ui/label'; import { Checkbox } from '$lib/components/ui/checkbox'; + import type { LineItem } from '$lib/api/dashboard/a76/items'; + + let { + lineItem = $bindable() + }: { + lineItem: LineItem; + } = $props(); + + let taxPaidValue = $derived(lineItem.tax_payment ? 'si' : 'no'); + function setTaxPaid(val: string) { + lineItem.tax_payment = val === 'si'; + } + + let hasCertificateValue = $derived(lineItem.has_certificate ? 'si' : 'no'); + function setHasCertificate(val: string) { + lineItem.has_certificate = val === 'si'; + }
@@ -12,7 +29,10 @@
TAX PAID - +
@@ -27,7 +47,7 @@
- +
@@ -37,7 +57,8 @@
- + +
@@ -45,7 +66,10 @@
Has Certificate of Origin? - +
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-identifiers.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-identifiers.svelte index 63974265..3a3034c3 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-identifiers.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-identifiers.svelte @@ -1,6 +1,9 @@
@@ -9,7 +12,7 @@
- +
diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-labeling.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-labeling.svelte index 3dd58e63..cacb0306 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-labeling.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-labeling.svelte @@ -1,6 +1,9 @@
@@ -21,6 +24,7 @@ diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-series.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-series.svelte index d5f242f6..240a6c50 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-series.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/fa/tab-series.svelte @@ -1,5 +1,8 @@
@@ -9,6 +12,7 @@ diff --git a/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte b/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte index e30f3a3d..3b10300c 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte +++ b/frontend/src/lib/components/dashboard/invoices/edit/items/items-tab-form.svelte @@ -112,23 +112,139 @@ isEditMode = false; showItemSheet = true; - // Auto-asignar valores desde la factura + // Auto-asignar valores desde la factura con estructura completa editingItem = { invoice_id: invoice.id, reference_number: '', order: invoice.purchase_order || '', warehouse: '', - location: '' + location: '', + lines: [{ + line_number: 1, + // LineItem fields + part_number: undefined, + component_part_number: undefined, + class_code: undefined, + identifier: undefined, + unit_of_measure: undefined, + alternate_unit: undefined, + permit_number: undefined, + page_line: undefined, + has_certificate: false, + certificate_number: undefined, + is_subitem: false, + includes_subitems: false, + tax_payment: false, + payment_method: undefined, + igi_amount: undefined, + // Nested relations + financial: { + unit_cost_usd: undefined, + unit_cost_mxn: undefined, + unit_cost_capture: undefined, + unit_cost_commercial_usd: undefined, + value_usd: undefined, + value_mxn: undefined, + value_returned_usd: undefined, + value_returned_mxn: undefined, + customs_value_usd: undefined, + }, + quantity: { + quantity: undefined, + unit_of_measure: undefined, + quantity_temp_export: undefined, + quantity_returned: undefined, + net_weight: undefined, + gross_weight: undefined, + package_key: undefined, + package_quantity: undefined, + package_description: undefined, + }, + customs: { + fraction: undefined, + fraction_type: 'GENERAL', + american_fraction: undefined, + origin_country: undefined, + destination_country: undefined, + advalorem: undefined, + advalorem_american: undefined, + sector: undefined, + }, + description: { + description_spanish: undefined, + description_english: undefined, + extra_description: undefined, + additional_info_spanish: undefined, + brand: undefined, + model: undefined, + has_serial: false, + }, + reference: { + serie_id: undefined, + }, + }] }; } function handleEdit(item: Item) { isEditMode = true; selectedItem = item; - editingItem = { ...item }; + // Deep clone and normalize numeric values + editingItem = normalizeItemData({ ...item }); showItemSheet = true; } + // Normalize numeric values from strings to numbers + function normalizeItemData(item: Partial): Partial { + if (item.lines && item.lines.length > 0) { + item.lines = item.lines.map(line => { + const normalizedLine = { ...line }; + + // Normalize financials + if (normalizedLine.financial) { + normalizedLine.financial = { + ...normalizedLine.financial, + unit_cost_usd: normalizedLine.financial.unit_cost_usd != null + ? Number(normalizedLine.financial.unit_cost_usd) + : undefined, + unit_cost_mxn: normalizedLine.financial.unit_cost_mxn != null + ? Number(normalizedLine.financial.unit_cost_mxn) + : undefined, + value_usd: normalizedLine.financial.value_usd != null + ? Number(normalizedLine.financial.value_usd) + : undefined, + value_mxn: normalizedLine.financial.value_mxn != null + ? Number(normalizedLine.financial.value_mxn) + : undefined, + }; + } + + // Normalize quantities + if (normalizedLine.quantity) { + normalizedLine.quantity = { + ...normalizedLine.quantity, + quantity: normalizedLine.quantity.quantity != null + ? Number(normalizedLine.quantity.quantity) + : undefined, + net_weight: normalizedLine.quantity.net_weight != null + ? Number(normalizedLine.quantity.net_weight) + : undefined, + gross_weight: normalizedLine.quantity.gross_weight != null + ? Number(normalizedLine.quantity.gross_weight) + : undefined, + package_quantity: normalizedLine.quantity.package_quantity != null + ? Number(normalizedLine.quantity.package_quantity) + : undefined, + }; + } + + return normalizedLine; + }); + } + + return item; + } + function handleDelete(item: Item) { selectedItem = item; showDeleteDialog = true; @@ -144,7 +260,8 @@ reference_number: editingItem.reference_number, order: editingItem.order, warehouse: editingItem.warehouse, - location: editingItem.location + location: editingItem.location, + lines: editingItem.lines || [] }); // Recargar items @@ -174,7 +291,8 @@ reference_number: editingItem.reference_number, order: editingItem.order, warehouse: editingItem.warehouse, - location: editingItem.location + location: editingItem.location, + lines: editingItem.lines || [] }); // Recargar items diff --git a/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts b/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts index 2c4f6bdb..88f965d2 100644 --- a/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts +++ b/frontend/src/lib/components/dashboard/invoices/edit/save-invoice.ts @@ -110,7 +110,7 @@ function buildInvoicePayload(formData: FormDataSet): CreateInvoiceData | UpdateI // Solo agregar sub-recursos si tienen valores reales // Compliance MX - const hasComplianceValue = InvoiceTopFieldsFormData?.pedimento || InvoiceTopFieldsFormData?.remesa || generalFormData?.aduana || + const hasComplianceValue = InvoiceTopFieldsFormData?.pedimento_id || InvoiceTopFieldsFormData?.remesa || generalFormData?.aduana || generalFormData?.provider_id || generalFormData?.sold_to_id || generalFormData?.shipped_to_id || generalFormData?.customs_broker_id || observationFormData?.movement_type || observationFormData?.enclosure || @@ -158,7 +158,7 @@ function buildInvoicePayload(formData: FormDataSet): CreateInvoiceData | UpdateI function buildComplianceMxData(InvoiceTopFieldsFormData: any, generalFormData: any, othersFormData: any, observationFormData: any) { return { // Pedimento fields - desde InvoiceTopFieldsFormData - pedimento: InvoiceTopFieldsFormData?.pedimento || null, + pedimento_id: InvoiceTopFieldsFormData?.pedimento_id ? Number(InvoiceTopFieldsFormData.pedimento_id) : null, remesa: Number(InvoiceTopFieldsFormData?.remesa || null), is_pedimento_pending: Boolean(InvoiceTopFieldsFormData?.is_pedimento_pending || false), // Fields from generalFormData