diff --git a/backend/api/v1/modules/a76/items/imports/temporary/validators/create.py b/backend/api/v1/modules/a76/items/imports/temporary/validators/create.py index d6080ac3..d14a4113 100644 --- a/backend/api/v1/modules/a76/items/imports/temporary/validators/create.py +++ b/backend/api/v1/modules/a76/items/imports/temporary/validators/create.py @@ -1,6 +1,7 @@ """ Validaciones para creación de items vía API. """ + from sqlalchemy.orm import Session from core.exceptions import ErrorCollector from api.v1.modules.a76.items.line_items.schemas import LineItemCreate @@ -12,11 +13,11 @@ def validate_create( line: LineItemCreate, tenant_id: int, company_id: int, - errors: ErrorCollector + errors: ErrorCollector, ) -> None: """ Validaciones para crear LineItems vía API (actualmente en uso). - + Args: db: Sesión de base de datos line: Datos del line item @@ -30,43 +31,34 @@ def validate_create( field="line_number", message="El número de línea es obligatorio", solution="Proporciona un número de línea válido", - code="REQUIRED" + code="REQUIRED", ) - - # 2. Validar part_number_id - if not line.part_number_id: - errors.add_error( - field="part_number_id", - message="Part Number ID es obligatorio", - solution="Selecciona un número de parte válido del catálogo", - code="REQUIRED" - ) - - # 3. Validar class_id + + # 2. Validar class_id if not line.class_id: errors.add_error( field="class_id", message="Clase (ID) es obligatorio", solution="Selecciona una clasificación válida del catálogo", - code="REQUIRED" + code="REQUIRED", ) - + # 4. Validar unit_of_measure if not line.unit_of_measure: errors.add_error( field="unit_of_measure", message="U.M. es obligatorio", solution="Proporciona una unidad de medida válida", - code="REQUIRED" + code="REQUIRED", ) - + # 5. Validar quantity.quantity if not line.quantity: errors.add_error( field="quantity", message="Quantity es obligatorio", solution="Proporciona una cantidad válida", - code="REQUIRED" + code="REQUIRED", ) else: # Validar con nombre amigable @@ -75,44 +67,51 @@ def validate_create( field="quantity.quantity", message="Quantity debe ser mayor a cero", solution="Proporciona una cantidad válida", - code="INVALID_VALUE" if line.quantity.quantity is not None else "REQUIRED" + code=( + "INVALID_VALUE" + if line.quantity.quantity is not None + else "REQUIRED" + ), ) - + # 6. Validar financial.unit_cost if not line.financial: errors.add_error( field="financial", message="Unit Cost es obligatorio", solution="Proporciona el costo unitario del item", - code="REQUIRED" + code="REQUIRED", ) else: has_cost = ( - line.financial.unit_cost_usd or - line.financial.unit_cost_mxn or - line.financial.unit_cost_capture + line.financial.unit_cost_usd + or line.financial.unit_cost_mxn + or line.financial.unit_cost_capture ) if not has_cost: errors.add_error( field="financial.unit_cost", message="Unit Cost es obligatorio", solution="Proporciona al menos un costo unitario (USD, MXN o captura)", - code="REQUIRED" + code="REQUIRED", ) - + # 7. Validar description.description_spanish if line.description: - if not line.description.description_spanish or not line.description.description_spanish.strip(): + if ( + not line.description.description_spanish + or not line.description.description_spanish.strip() + ): errors.add_error( field="description.description_spanish", message="Description in Spanish es obligatorio", solution="Proporciona una descripción del item en español", - code="REQUIRED" + code="REQUIRED", ) else: errors.add_error( field="description.description_spanish", message="Description in Spanish es obligatorio", solution="Proporciona una descripción del item en español", - code="REQUIRED" - ) \ No newline at end of file + code="REQUIRED", + ) diff --git a/backend/api/v1/modules/a76/items/imports/temporary/validators/update.py b/backend/api/v1/modules/a76/items/imports/temporary/validators/update.py index 8feaa515..f5d183b9 100644 --- a/backend/api/v1/modules/a76/items/imports/temporary/validators/update.py +++ b/backend/api/v1/modules/a76/items/imports/temporary/validators/update.py @@ -1,6 +1,7 @@ """ Validaciones para actualización de items vía API. """ + from sqlalchemy.orm import Session from core.exceptions import ErrorCollector from api.v1.modules.a76.items.line_items.schemas import LineItemUpdate @@ -13,12 +14,12 @@ def validate_update( tenant_id: int, company_id: int, errors: ErrorCollector, - invoice_id: int = None + invoice_id: int = None, ) -> None: """ Validaciones para actualizar LineItems vía API. Incluye todas las validaciones de negocio de Clarion. - + Args: db: Sesión de base de datos line: Datos del line item a actualizar @@ -33,36 +34,27 @@ def validate_update( field="line_number", message="El número de línea no puede estar vacío", solution="Proporciona un número de línea válido", - code="REQUIRED" + code="REQUIRED", ) - - # 2. Validar part_number_id si se proporciona - if line.part_number_id is not None and not line.part_number_id: - errors.add_error( - field="part_number_id", - message="Part Number ID no puede estar vacío", - solution="Selecciona un número de parte válido del catálogo", - code="REQUIRED" - ) - - # 3. Validar class_id si se proporciona + + # 2. Validar class_id si se proporciona if line.class_id is not None and not line.class_id: errors.add_error( field="class_id", message="Clase (ID) no puede estar vacío", solution="Selecciona una clasificación válida del catálogo", - code="REQUIRED" + code="REQUIRED", ) - + # 4. Validar unit_of_measure si se proporciona if line.unit_of_measure is not None and not line.unit_of_measure: errors.add_error( field="unit_of_measure", message="U.M. no puede estar vacío", solution="Proporciona una unidad de medida válida", - code="REQUIRED" + code="REQUIRED", ) - + # 5. Validar cantidad si se proporciona if line.quantity: # Si se proporciona el objeto quantity, validar que quantity.quantity sea válido @@ -72,7 +64,7 @@ def validate_update( field="quantity.quantity", message="Quantity debe ser mayor a cero", solution="Proporciona una cantidad válida", - code="INVALID_VALUE" + code="INVALID_VALUE", ) else: # Si se proporciona quantity pero quantity.quantity es None, es requerido @@ -80,9 +72,9 @@ def validate_update( field="quantity.quantity", message="Quantity es obligatorio", solution="Proporciona una cantidad mayor a 0", - code="REQUIRED" + code="REQUIRED", ) - + # 6. Validar peso neto si se proporciona if line.quantity and line.quantity.net_weight is not None: if line.quantity.net_weight <= 0: @@ -90,25 +82,25 @@ def validate_update( field="quantity.net_weight", message="Net Weight debe ser mayor a cero", solution="Proporciona un peso neto válido", - code="INVALID_VALUE" + code="INVALID_VALUE", ) - + # 7. Validar costo unitario si se proporciona financial (excepto subpartidas) if line.financial: is_subitem = line.fa_data and line.fa_data.is_subitem if line.fa_data else False - + if not is_subitem: has_cost = ( - line.financial.unit_cost_usd or - line.financial.unit_cost_mxn or - line.financial.unit_cost_capture + line.financial.unit_cost_usd + or line.financial.unit_cost_mxn + or line.financial.unit_cost_capture ) if not has_cost: errors.add_error( field="financial.unit_cost", message="Unit Cost es obligatorio", solution="Proporciona al menos un costo unitario (USD, MXN o captura)", - code="REQUIRED" + code="REQUIRED", ) # Validar que sean positivos if line.financial.unit_cost_usd is not None: @@ -117,7 +109,7 @@ def validate_update( field="financial.unit_cost_usd", message="Unit Cost (USD) debe ser mayor a cero", solution="Proporciona un costo unitario válido", - code="INVALID_VALUE" + code="INVALID_VALUE", ) if line.financial.unit_cost_mxn is not None: if line.financial.unit_cost_mxn <= 0: @@ -125,7 +117,7 @@ def validate_update( field="financial.unit_cost_mxn", message="Unit Cost (MXN) debe ser mayor a cero", solution="Proporciona un costo unitario válido", - code="INVALID_VALUE" + code="INVALID_VALUE", ) if line.financial.unit_cost_capture is not None: if line.financial.unit_cost_capture <= 0: @@ -133,9 +125,9 @@ def validate_update( field="financial.unit_cost_capture", message="Unit Cost (Captura) debe ser mayor a cero", solution="Proporciona un costo unitario válido", - code="INVALID_VALUE" + code="INVALID_VALUE", ) - + # 8. Validar datos aduanales si se proporcionan if line.customs: # Validar país de origen @@ -144,56 +136,65 @@ def validate_update( field="customs.origin_country", message="Origin Country no puede estar vacío", solution="Selecciona el país de origen del item", - code="REQUIRED" + code="REQUIRED", ) - + # Validar preferencia arancelaria if line.customs.preference is not None and not line.customs.preference: errors.add_error( field="customs.preference", message="La preferencia arancelaria no puede estar vacía", solution="Selecciona la preferencia arancelaria", - code="REQUIRED" + code="REQUIRED", ) - + # Validar formato de pago de impuestos if line.customs.tax_paid: val_tax = line.customs.tax_paid.upper() - if val_tax not in ['SI', 'NO', 'S', 'N']: + if val_tax not in ["SI", "NO", "S", "N"]: errors.add_error( field="customs.tax_paid", message="El valor de pago de impuesto debe ser SI/NO o S/N", solution="Proporciona un valor válido: SI, NO, S o N", - code="INVALID_VALUE" + code="INVALID_VALUE", ) - + # Validar forma de pago si existe if line.customs.payment_form: - from api.v1.modules.a76.general_catalogs.forms_of_payment.models import PaymentForm - - payment = db.query(PaymentForm).filter( - PaymentForm.code == line.customs.payment_form, - PaymentForm.tenant_id == tenant_id - ).first() - + from api.v1.modules.a76.general_catalogs.forms_of_payment.models import ( + PaymentForm, + ) + + payment = ( + db.query(PaymentForm) + .filter( + PaymentForm.code == line.customs.payment_form, + PaymentForm.tenant_id == tenant_id, + ) + .first() + ) + if not payment: errors.add_error( field="customs.payment_form", message=f"La forma de pago '{line.customs.payment_form}' no es válida", solution="Selecciona una forma de pago válida del catálogo", - code="INVALID_VALUE" + code="INVALID_VALUE", ) - + # 9. Validar descripción en español si se proporciona - if line.description and hasattr(line.description, 'description_spanish'): - if line.description.description_spanish is not None and not line.description.description_spanish: + if line.description and hasattr(line.description, "description_spanish"): + if ( + line.description.description_spanish is not None + and not line.description.description_spanish + ): errors.add_error( field="description.description_spanish", message="La descripción en español no puede estar vacía", solution="Proporciona una descripción del item en español", - code="REQUIRED" + code="REQUIRED", ) - + # 10. Validar subpartidas si se actualizan if line.fa_data and line.fa_data.is_subitem: # Es subpartida, debe tener partida principal @@ -202,30 +203,35 @@ def validate_update( field="fa_data.main_line_id", message="La subpartida debe tener asignada una partida principal", solution="Selecciona la partida principal de esta subpartida", - code="REQUIRED" + code="REQUIRED", ) elif invoice_id: # Validar que la partida principal exista en la misma factura from api.v1.modules.a76.items.line_items.models import LineItem from api.v1.modules.a76.items.models import Item - - parent = db.query(LineItem).join(LineItem.item).filter( - LineItem.line_number == line.fa_data.main_line_id, - Item.invoice_id == invoice_id, - LineItem.company_id == company_id - ).first() - + + parent = ( + db.query(LineItem) + .join(LineItem.item) + .filter( + LineItem.line_number == line.fa_data.main_line_id, + Item.invoice_id == invoice_id, + LineItem.company_id == company_id, + ) + .first() + ) + if not parent: errors.add_error( field="fa_data.main_line_id", message=f"La partida principal {line.fa_data.main_line_id} no existe en esta factura", solution="Verifica el número de la partida principal", - code="NOT_FOUND" + code="NOT_FOUND", ) elif parent.fa_data and parent.fa_data.is_subitem: errors.add_error( field="fa_data.main_line_id", message="La partida principal no puede ser otra subpartida", solution="Selecciona una partida normal como principal", - code="INVALID_VALUE" + code="INVALID_VALUE", ) 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 0bea3647..bd3520b0 100644 --- a/backend/api/v1/modules/a76/items/line_items/schemas.py +++ b/backend/api/v1/modules/a76/items/line_items/schemas.py @@ -47,19 +47,15 @@ class LineItemBase(BaseModel): line_number: int = Field(..., description="Line number") # Part identification - part_number_id: Optional[int] = Field(None, description="Part number") - component_part_number_id: Optional[int] = Field( + part_number: Optional[int] = Field(None, description="Part number") + component_part_number: Optional[int] = Field( None, description="Component part number" ) class_id: Optional[int] = Field(None, description="Class code") # Unit of measure - unit_of_measure: Optional[int] = Field( - None, description="Unit of measure" - ) - alternate_unit: Optional[int] = Field( - None, description="Alternate unit" - ) + unit_of_measure: Optional[int] = Field(None, description="Unit of measure") + alternate_unit: Optional[int] = Field(None, description="Alternate unit") uma_key: Optional[str] = Field(None, max_length=2, description="UMA key") auxiliary_unit: Optional[str] = Field( None, max_length=5, description="Auxiliary unit" diff --git a/backend/api/v1/modules/a76/items/service.py b/backend/api/v1/modules/a76/items/service.py index 2866fb3d..7d8959f7 100644 --- a/backend/api/v1/modules/a76/items/service.py +++ b/backend/api/v1/modules/a76/items/service.py @@ -33,7 +33,7 @@ from api.v1.modules.a24.fa.fa_item_lines.models import FaLineItem from .models import Item from api.v1.modules.a76.invoices.models import InvoiceHeader from api.v1.modules.a76.general_catalogs.company.models import Company - + logger = logging.getLogger(__name__) @@ -158,79 +158,86 @@ class ItemService: company_id: int, ) -> Item: """Create a new item with all related nested data (multiple lines)""" - + # Validaciones con ErrorCollector errors = ErrorCollector() - + # Validar que la factura exista y no esté actualizada (si viene invoice_id) invoice = None if item_data.invoice_id: - invoice = db.query(InvoiceHeader).filter( - InvoiceHeader.id == item_data.invoice_id, - InvoiceHeader.tenant_id == tenant_id, - InvoiceHeader.company_id == company_id - ).first() - + invoice = ( + db.query(InvoiceHeader) + .filter( + InvoiceHeader.id == item_data.invoice_id, + InvoiceHeader.tenant_id == tenant_id, + InvoiceHeader.company_id == company_id, + ) + .first() + ) + if not invoice: errors.add_error( field="invoice_id", message="La factura especificada no existe", code="NOT_FOUND", - value=str(item_data.invoice_id) + value=str(item_data.invoice_id), ) - + # Validar cada line item que se va a crear if item_data.lines: for idx, line_data in enumerate(item_data.lines): # Convertir a LineItemCreate para validar line_create = LineItemCreate(**line_data.model_dump()) - + validate_create(db, line_create, tenant_id, company_id, errors) - + # Validaciones adicionales específicas del negocio - + # Validar apóstrofes en número de parte - if line_data.part_number_id and "'" in str(line_data.part_number_id): + if line_data.part_number and "'" in str(line_data.part_number): errors.add_error( - field=f"lines[{idx}].part_number_id", + field=f"lines[{idx}].part_number", message=f"Advertencia: El Número de Parte contiene apóstrofes y serán omitidos", - code="WARNING_APOSTROPHE" + code="WARNING_APOSTROPHE", ) - + # Validar tipo de partida - if hasattr(line_data, 'item_type'): + if hasattr(line_data, "item_type"): tipo_partida = line_data.item_type - if tipo_partida and tipo_partida not in ['N', 'S']: + if tipo_partida and tipo_partida not in ["N", "S"]: errors.add_error( field=f"lines[{idx}].item_type", message=f"Tipo de partida debe ser 'N' (Normal) o 'S' (Subpartida), recibido: '{tipo_partida}'", code="INVALID_ITEM_TYPE", - value=str(tipo_partida) + value=str(tipo_partida), ) - + # Si es subpartida (S), debe tener partida principal - if tipo_partida == 'S': - if not hasattr(line_data, 'main_line_id') or not line_data.main_line_id: + if tipo_partida == "S": + if ( + not hasattr(line_data, "main_line_id") + or not line_data.main_line_id + ): errors.add_error( field=f"lines[{idx}].main_line_id", message="Las subpartidas (tipo 'S') deben tener una partida principal", - code="MISSING_MAIN_LINE" + code="MISSING_MAIN_LINE", ) - + # Validar que el line_number sea consecutivo (si se especifica) - if hasattr(line_data, 'line_number') and line_data.line_number: + if hasattr(line_data, "line_number") and line_data.line_number: expected_line = idx + 1 if line_data.line_number != expected_line: errors.add_error( field=f"lines[{idx}].line_number", message=f"Número de línea esperado: {expected_line}, recibido: {line_data.line_number}", code="INVALID_LINE_SEQUENCE", - value=str(line_data.line_number) + value=str(line_data.line_number), ) - + # Si hay errores, lanzar excepción ANTES de intentar crear errors.raise_if_errors("Error al crear el item") - + try: # Extract lines data lines_data = item_data.lines or [] @@ -311,7 +318,9 @@ class ItemService: # Create FA data if provided if fa_data: - fa_dict = fa_data.model_dump(exclude={"line_item_id"}) # Exclude line_item_id from DTO + fa_dict = fa_data.model_dump( + exclude={"line_item_id"} + ) # Exclude line_item_id from DTO fa_dict["id"] = db_line.id # FA table uses same ID as line item fa_dict["tenant_id"] = tenant_id fa_dict["company_id"] = company_id @@ -343,47 +352,54 @@ class ItemService: company_id: int, ) -> Item: """Update an item and optionally its nested data (multiple lines)""" - + # Get existing item db_item = ItemService.get_by_id(db, item_id, tenant_id, company_id) if not db_item: raise HTTPException(status_code=404, detail="Item not found") - + # Validaciones con ErrorCollector errors = ErrorCollector() - + # Si se está actualizando el invoice_id, validar la factura invoice = None if item_data.invoice_id: - invoice = db.query(InvoiceHeader).filter( - InvoiceHeader.id == item_data.invoice_id, - InvoiceHeader.tenant_id == tenant_id, - InvoiceHeader.company_id == company_id - ).first() - + invoice = ( + db.query(InvoiceHeader) + .filter( + InvoiceHeader.id == item_data.invoice_id, + InvoiceHeader.tenant_id == tenant_id, + InvoiceHeader.company_id == company_id, + ) + .first() + ) + if not invoice: errors.add_error( field="invoice_id", message="La factura especificada no existe", code="NOT_FOUND", - value=str(item_data.invoice_id) + value=str(item_data.invoice_id), ) else: # Si no se está actualizando invoice_id, obtener la factura actual por invoice_id from api.v1.modules.a76.invoices.models import InvoiceHeader - invoice = db.query(InvoiceHeader).filter( - InvoiceHeader.id == db_item.invoice_id - ).first() - + + invoice = ( + db.query(InvoiceHeader) + .filter(InvoiceHeader.id == db_item.invoice_id) + .first() + ) + # Validar cada line item que se va a actualizar if item_data.lines: for idx, line_data in enumerate(item_data.lines): # Si el line tiene ID, es actualización; si no, es creación - if hasattr(line_data, 'id') and line_data.id: + if hasattr(line_data, "id") and line_data.id: # Buscar el line item existente existing_line = next( (line for line in db_item.lines if line.id == line_data.id), - None + None, ) if existing_line: # Convertir a LineItemUpdate para validar @@ -393,43 +409,46 @@ class ItemService: # Es un nuevo line item, validar como creación line_create = LineItemCreate(**line_data.model_dump()) validate_create(db, line_create, tenant_id, company_id, errors) - + # Validaciones adicionales específicas del negocio # (Aplican tanto para crear como actualizar) - + # Validar apóstrofes en número de parte - if line_data.part_number_id and "'" in str(line_data.part_number_id): + if line_data.part_number and "'" in str(line_data.part_number): errors.add_error( - field=f"lines[{idx}].part_number_id", + field=f"lines[{idx}].part_number", message=f"Advertencia: El Número de Parte contiene apóstrofes y serán omitidos", solution=None, - code="WARNING_APOSTROPHE" + code="WARNING_APOSTROPHE", ) - + # Validar tipo de partida - if hasattr(line_data, 'item_type'): + if hasattr(line_data, "item_type"): tipo_partida = line_data.item_type - if tipo_partida and tipo_partida not in ['N', 'S']: + if tipo_partida and tipo_partida not in ["N", "S"]: errors.add_error( field=f"lines[{idx}].item_type", message=f"Tipo de partida debe ser 'N' (Normal) o 'S' (Subpartida), recibido: '{tipo_partida}'", solution=None, code="INVALID_ITEM_TYPE", - value=str(tipo_partida) + value=str(tipo_partida), ) - + # Si es subpartida (S), debe tener partida principal - if tipo_partida == 'S': - if not hasattr(line_data, 'main_line_id') or not line_data.main_line_id: + if tipo_partida == "S": + if ( + not hasattr(line_data, "main_line_id") + or not line_data.main_line_id + ): errors.add_error( field=f"lines[{idx}].main_line_id", message="Las subpartidas (tipo 'S') deben tener una partida principal", solution=None, - code="MISSING_MAIN_LINE" + code="MISSING_MAIN_LINE", ) - + # Validar que el line_number sea consecutivo (si se especifica) - if hasattr(line_data, 'line_number') and line_data.line_number: + if hasattr(line_data, "line_number") and line_data.line_number: expected_line = idx + 1 if line_data.line_number != expected_line: errors.add_error( @@ -437,12 +456,12 @@ class ItemService: message=f"Número de línea esperado: {expected_line}, recibido: {line_data.line_number}", solution=None, code="INVALID_LINE_SEQUENCE", - value=str(line_data.line_number) + value=str(line_data.line_number), ) - + # Si hay errores, lanzar excepción ANTES de actualizar errors.raise_if_errors("Error al actualizar el item") - + try: # Extract lines data @@ -519,7 +538,9 @@ class ItemService: # Create FA data if provided if fa_data is not None: - fa_dict = fa_data.model_dump(exclude_unset=True, exclude={"line_item_id"}) + fa_dict = fa_data.model_dump( + exclude_unset=True, exclude={"line_item_id"} + ) fa_dict["id"] = db_line.id # FA table uses same ID as line item fa_dict["tenant_id"] = tenant_id fa_dict["company_id"] = company_id diff --git a/frontend/src/lib/api/dashboard/a76/items.ts b/frontend/src/lib/api/dashboard/a76/items.ts index 60cec7d3..5c04f975 100644 --- a/frontend/src/lib/api/dashboard/a76/items.ts +++ b/frontend/src/lib/api/dashboard/a76/items.ts @@ -125,8 +125,8 @@ export interface LineItem { line_number: number; // Identification - part_number_id?: string; - component_part_number_id?: string; + part_number?: string; + component_part_number?: string; class_id?: number; identifier?: string; @@ -244,7 +244,7 @@ export const itemsApi = { const params = new URLSearchParams({ company_id: companyId.toString() }); - return api.get(`/v1/a76/items/invoice/${invoiceId}/items/?${params.toString()}`); + return api.get(`/v1/a76/items/invoice/${invoiceId}/items?${params.toString()}`); }, /** @@ -264,7 +264,7 @@ export const itemsApi = { const params = new URLSearchParams({ company_id: companyId.toString() }); - return api.post(`/v1/a76/items/?${params.toString()}`, data); + return api.post(`/v1/a76/items?${params.toString()}`, data); }, /** @@ -274,7 +274,7 @@ export const itemsApi = { const params = new URLSearchParams({ company_id: companyId.toString() }); - return api.put(`/v1/a76/items/${itemId}/?${params.toString()}`, data); + return api.put(`/v1/a76/items/${itemId}?${params.toString()}`, data); }, /** @@ -284,6 +284,6 @@ export const itemsApi = { const params = new URLSearchParams({ company_id: companyId.toString() }); - return api.delete(`/v1/a76/items/${itemId}/?${params.toString()}`); + return api.delete(`/v1/a76/items/${itemId}?${params.toString()}`); } }; diff --git a/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte b/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte index 5991f15e..6ae6bd7e 100644 --- a/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte +++ b/frontend/src/lib/components/dashboard/goods/parts/partForm.svelte @@ -232,7 +232,6 @@ const activeCompanyId = companyStore.activeCompany?.id; if (!activeCompanyId) { error = 'No hay una compañía activa seleccionada'; return; } if (!formData.client_id) { error = 'Debe seleccionar un Cliente'; return; } - if (!formData.part_number.trim()) { error = 'Número de Parte requerido'; return; } loading = true; try { @@ -272,7 +271,7 @@ } catch (e: any) { console.error("Submit Error:", e); error = e.message || 'Error al guardar'; - toast.error(error); + toast.error(error!); } finally { loading = false; } } @@ -299,7 +298,7 @@ {#if error}
- ⚠️ {error} + ⚠️ {error!}
{/if} @@ -540,7 +539,7 @@
- +
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 34333daa..26da88e3 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 @@ -74,7 +74,7 @@
- +

ID de número de parte existente en catálogo

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 3c4210d3..84c62aa3 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 @@ -139,8 +139,8 @@ lines: [{ line_number: 1, // LineItem fields - part_number_id: undefined, - component_part_number_id: undefined, + part_number: undefined, + component_part_number: undefined, class_id: undefined, identifier: undefined, unit_of_measure: undefined, @@ -294,8 +294,8 @@ }; // Convert integer fields - cleaned.part_number_id = toNumberOrUndefined(cleaned.part_number_id); - cleaned.component_part_number_id = toNumberOrUndefined(cleaned.component_part_number_id); + cleaned.part_number = toNumberOrUndefined(cleaned.part_number); + cleaned.component_part_number = toNumberOrUndefined(cleaned.component_part_number); cleaned.class_id = toNumberOrUndefined(cleaned.class_id); cleaned.unit_of_measure = toNumberOrUndefined(cleaned.unit_of_measure); cleaned.alternate_unit = toNumberOrUndefined(cleaned.alternate_unit);