From ebcfe3c9d2c22b46235833cbcc9b859631f1502b Mon Sep 17 00:00:00 2001 From: admbrainsteel Date: Fri, 16 Oct 2026 15:43:15 -0300 Subject: [PATCH] fix: persistencia e extracao de comprimento de pecas e subcomponentes na importacao --- .../conversores/AdvanceSteelConverter.tsx | 11 +- src/components/conversores/BocadConverter.tsx | 11 +- src/components/pecas/ImportarPecasModal.tsx | 7 +- src/components/pecas/ImportarXLSModal.tsx | 7 +- src/hooks/usePecas.tsx | 9 +- src/utils/importDiffService.ts | 105 ++++++++++++++++-- src/utils/importExecutionService.ts | 8 +- 7 files changed, 141 insertions(+), 17 deletions(-) diff --git a/src/components/conversores/AdvanceSteelConverter.tsx b/src/components/conversores/AdvanceSteelConverter.tsx index 07710d1..99d9de3 100644 --- a/src/components/conversores/AdvanceSteelConverter.tsx +++ b/src/components/conversores/AdvanceSteelConverter.tsx @@ -26,7 +26,7 @@ import * as XLSX from 'xlsx'; import { useAppLabels } from '@/hooks/useAppLabels'; import { usePecas } from '@/hooks/usePecas'; import { supabase } from '@/integrations/supabase/client'; -import { analisarConflitosImportacao, consolidarPecasEComponentes } from '@/utils/importDiffService'; +import { analisarConflitosImportacao, consolidarPecasEComponentes, extrairComprimentoSeguro, extrairComprimentoComponenteSeguro } from '@/utils/importDiffService'; import { ResultadoAnaliseImportacao } from '@/types/importDiff'; import { ImportarPecasDiffModal } from '@/components/pecas/ImportarPecasDiffModal'; @@ -835,8 +835,12 @@ const AdvanceSteelConverterContent: React.FC = () => { try { const importacaoRows: any[] = []; extractedData.forEach((item) => { + const comprPeca = extrairComprimentoSeguro(item.comprimentoMax); + if (item.components && item.components.length > 0) { item.components.forEach((comp) => { + const comprComp = extrairComprimentoComponenteSeguro(comp.comprimento); + importacaoRows.push({ of_number: item.of, etapa_fase: item.fase, @@ -845,6 +849,8 @@ const AdvanceSteelConverterContent: React.FC = () => { quantidade: Number(item.quantidade), peso_unitario: item.pesoUnit === '-' ? 0 : Number(item.pesoUnit), peso_total: item.pesoTotal === '-' ? 0 : Number(item.pesoTotal), + comprimento: comprPeca, + comprimento_ref: comprPeca, tratamento_superficial: item.tratamentoSuperficial || tratamentoGlobal, material: item.material, perfil_principal: item.perfilPrincipal, @@ -853,6 +859,7 @@ const AdvanceSteelConverterContent: React.FC = () => { descricao_componente: comp.perfil || item.descricao, perfil_componente: comp.perfil || item.descricao, peso_unitario_componente: comp.pesoUnitario || 0, + comprimento_componente: comprComp, quantidade_por_peca: Number(comp.quantidade) || 1 }); }); @@ -865,6 +872,8 @@ const AdvanceSteelConverterContent: React.FC = () => { quantidade: Number(item.quantidade), peso_unitario: item.pesoUnit === '-' ? 0 : Number(item.pesoUnit), peso_total: item.pesoTotal === '-' ? 0 : Number(item.pesoTotal), + comprimento: comprPeca, + comprimento_ref: comprPeca, tratamento_superficial: item.tratamentoSuperficial || tratamentoGlobal, material: item.material, perfil_principal: item.perfilPrincipal, diff --git a/src/components/conversores/BocadConverter.tsx b/src/components/conversores/BocadConverter.tsx index 3ffeded..58ced70 100644 --- a/src/components/conversores/BocadConverter.tsx +++ b/src/components/conversores/BocadConverter.tsx @@ -25,7 +25,7 @@ import { import { toast } from 'sonner'; import * as XLSX from 'xlsx'; import { usePecas } from '@/hooks/usePecas'; -import { analisarConflitosImportacao, consolidarPecasEComponentes } from '@/utils/importDiffService'; +import { analisarConflitosImportacao, consolidarPecasEComponentes, extrairComprimentoSeguro, extrairComprimentoComponenteSeguro } from '@/utils/importDiffService'; import { ResultadoAnaliseImportacao } from '@/types/importDiff'; import { ImportarPecasDiffModal } from '@/components/pecas/ImportarPecasDiffModal'; @@ -532,8 +532,12 @@ export const BocadConverter: React.FC = () => { try { const importacaoRows: any[] = []; extractedPieces.forEach((item) => { + const comprPeca = extrairComprimentoSeguro(item.comprimentoRef); + if (item.compostoPorComponentes === 'SIM' && item.components && item.components.length > 0) { item.components.forEach((comp) => { + const comprComp = extrairComprimentoComponenteSeguro(comp.compr); + importacaoRows.push({ of_number: item.of, etapa_fase: item.fase, @@ -542,6 +546,8 @@ export const BocadConverter: React.FC = () => { quantidade: Number(item.quantidade), peso_unitario: item.pesoUnitario || 0, peso_total: item.pesoTotal || 0, + comprimento: comprPeca, + comprimento_ref: comprPeca, tratamento_superficial: item.tratamentoSuperficial, material: item.material, perfil_principal: item.perfilPrincipal, @@ -550,6 +556,7 @@ export const BocadConverter: React.FC = () => { descricao_componente: comp.perfil || item.descricao, perfil_componente: comp.perfil || item.descricao, peso_unitario_componente: comp.pesoUnit || 0, + comprimento_componente: comprComp, quantidade_por_peca: Number(comp.quant) || 1 }); }); @@ -562,6 +569,8 @@ export const BocadConverter: React.FC = () => { quantidade: Number(item.quantidade), peso_unitario: item.pesoUnitario || 0, peso_total: item.pesoTotal || 0, + comprimento: comprPeca, + comprimento_ref: comprPeca, tratamento_superficial: item.tratamentoSuperficial, material: item.material, perfil_principal: item.perfilPrincipal, diff --git a/src/components/pecas/ImportarPecasModal.tsx b/src/components/pecas/ImportarPecasModal.tsx index 0b8d750..712bbdf 100644 --- a/src/components/pecas/ImportarPecasModal.tsx +++ b/src/components/pecas/ImportarPecasModal.tsx @@ -25,10 +25,13 @@ interface PecaCSVData { material: string; perfil_principal: string; tem_componentes: boolean; + comprimento?: number | null; + comprimento_ref?: number | string; marca_componente?: string; descricao_componente?: string; perfil_componente?: string; peso_unitario_componente?: number; + comprimento_componente?: number | null; quantidade_por_peca?: number; } @@ -108,7 +111,7 @@ export function ImportarPecasModal({ open, onOpenChange, onImport, pecasExistent }); // Validar e converter campos numéricos - const numericFields = ['quantidade', 'peso_unitario', 'peso_total', 'peso_unitario_componente', 'quantidade_por_peca']; + const numericFields = ['quantidade', 'peso_unitario', 'peso_total', 'peso_unitario_componente', 'quantidade_por_peca', 'comprimento', 'comprimento_ref', 'comprimento_componente']; const convertedData: any = { ...rawRow }; numericFields.forEach(field => { @@ -177,10 +180,12 @@ export function ImportarPecasModal({ open, onOpenChange, onImport, pecasExistent material: convertedData.material?.toString().trim() || '', perfil_principal: convertedData.perfil_principal?.toString().trim() || '', tem_componentes: convertedData.tem_componentes, + comprimento: convertedData.comprimento ? Number(convertedData.comprimento) : (convertedData.comprimento_ref ? Number(convertedData.comprimento_ref) : null), marca_componente: convertedData.marca_componente?.toString().trim() || '', descricao_componente: convertedData.descricao_componente?.toString().trim() || '', perfil_componente: convertedData.perfil_componente?.toString().trim() || '', peso_unitario_componente: convertedData.peso_unitario_componente || 0, + comprimento_componente: convertedData.comprimento_componente ? Number(convertedData.comprimento_componente) : null, quantidade_por_peca: convertedData.quantidade_por_peca || 0 }; diff --git a/src/components/pecas/ImportarXLSModal.tsx b/src/components/pecas/ImportarXLSModal.tsx index d58b19d..bdc5f5e 100644 --- a/src/components/pecas/ImportarXLSModal.tsx +++ b/src/components/pecas/ImportarXLSModal.tsx @@ -15,7 +15,7 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@ import { FileSpreadsheet, Upload, Download, CheckCircle2, AlertCircle, Trash2, ArrowLeft, RefreshCw } from 'lucide-react'; import { toast } from 'sonner'; import * as XLSX from 'xlsx'; -import { analisarConflitosImportacao, consolidarPecasEComponentes } from '@/utils/importDiffService'; +import { analisarConflitosImportacao, consolidarPecasEComponentes, extrairComprimentoSeguro, extrairComprimentoComponenteSeguro } from '@/utils/importDiffService'; import { ResultadoAnaliseImportacao } from '@/types/importDiff'; import { ImportarPecasDiffModal } from '@/components/pecas/ImportarPecasDiffModal'; @@ -471,8 +471,8 @@ export function ImportarXLSModal({ quantidade: p.quantidade, peso_unitario: p.peso_unitario, peso_total: p.peso_total, - comprimento: p.comprimento !== undefined && p.comprimento !== null ? Number(p.comprimento) : (p.comprimento_ref ? Number(p.comprimento_ref) : null), - comprimento_ref: p.comprimento_ref ? Number(p.comprimento_ref) : (p.comprimento ? Number(p.comprimento) : null), + comprimento: extrairComprimentoSeguro(p), + comprimento_ref: extrairComprimentoSeguro(p), tratamento_superficial: p.tratamento_superficial || 'pintura', material: p.material, perfil_principal: p.perfil_principal, @@ -481,6 +481,7 @@ export function ImportarXLSModal({ descricao_componente: p.descricao_componente, perfil_componente: p.perfil_componente, peso_unitario_componente: p.peso_unitario_componente, + comprimento_componente: extrairComprimentoComponenteSeguro(p), quantidade_por_peca: p.quantidade_por_peca, })); diff --git a/src/hooks/usePecas.tsx b/src/hooks/usePecas.tsx index bfd49b1..3733761 100644 --- a/src/hooks/usePecas.tsx +++ b/src/hooks/usePecas.tsx @@ -2,6 +2,7 @@ import { useState, useEffect, useCallback } from 'react'; import { supabase } from '@/integrations/supabase/client'; import { toast } from 'sonner'; import { useAuth } from '@/hooks/useAuth'; +import { extrairComprimentoSeguro, extrairComprimentoComponenteSeguro } from '@/utils/importDiffService'; export interface Peca { id: string; @@ -425,6 +426,7 @@ export function usePecas() { pecasData.forEach((item, index) => { const pecaKey = `${item.of_number || ''}-${item.etapa_fase || ''}-${item.marca || ''}`; + const comprimentoPeca = extrairComprimentoSeguro(item); const pecaData = { of_number: item.of_number || '', @@ -434,7 +436,7 @@ export function usePecas() { quantidade: Number(item.quantidade) || 0, peso_unitario: Number(item.peso_unitario) || 0, peso_total: Number(item.peso_total) || 0, - comprimento: item.comprimento ? Number(item.comprimento) : (item.comprimento_ref ? Number(item.comprimento_ref) : null), + comprimento: comprimentoPeca, tratamento_superficial: item.tratamento_superficial || '', material: item.material || '', perfil_principal: item.perfil_principal || '', @@ -445,6 +447,8 @@ export function usePecas() { if (!pecasMap.has(pecaKey)) { pecasMap.set(pecaKey, pecaData); + } else if (comprimentoPeca && !pecasMap.get(pecaKey).comprimento) { + pecasMap.get(pecaKey).comprimento = comprimentoPeca; } if (item.marca_componente && String(item.marca_componente).trim()) { @@ -470,12 +474,13 @@ export function usePecas() { } cleanMarca = String(numVal); + const compComprimento = extrairComprimentoComponenteSeguro(item); const componenteData = { marca_componente: cleanMarca, descricao: item.descricao_componente || item.descricao || '', perfil: item.perfil_componente || item.perfil_principal || '', peso_unitario: Number(item.peso_unitario_componente) || 0, - comprimento: item.comprimento_componente ? Number(item.comprimento_componente) : null, + comprimento: compComprimento, quantidade_por_peca: Math.max(1, Math.round(Number(item.quantidade_por_peca) || 1)), user_id: user.id }; diff --git a/src/utils/importDiffService.ts b/src/utils/importDiffService.ts index e1885a5..234611c 100644 --- a/src/utils/importDiffService.ts +++ b/src/utils/importDiffService.ts @@ -27,6 +27,90 @@ const CAMPOS_MONITORADOS: CampoConfig[] = [ { key: 'tem_componentes', label: 'Tem Componentes', isCritico: true }, ]; +/** + * Função utilitária resiliente para extrair o comprimento numérico de uma peça principal. + * Suporta múltiplos aliases de importação (Bocad, Advance Steel, Excel, CSV) e formatações de string. + */ +export function extrairComprimentoSeguro(item: any): number | null { + if (!item) return null; + + const raw = + item.comprimento ?? + item.comprimento_ref ?? + item.comprimentoRef ?? + item.comprimentoMax ?? + item.comprimento_max ?? + item.comprimento_peca ?? + item['Comprimento'] ?? + item['Comprimento (mm)'] ?? + item['Comprimento Ref. (mm)'] ?? + item['Comprimento Max. (mm)'] ?? + item['Comp. (mm)'] ?? + item['Comp (mm)'] ?? + item.compr ?? + item.comp; + + if (raw === undefined || raw === null || raw === '' || raw === '-') return null; + + if (typeof raw === 'number') { + return isNaN(raw) || raw <= 0 ? null : Math.round(raw); + } + + // String cleanup: trata formatos como "3.241", "3,241", "6000 mm", "5850.5" + let str = String(raw).trim().replace(/mm/gi, '').trim(); + if (str.includes('.') && str.includes(',')) { + if (str.indexOf('.') < str.indexOf(',')) { + str = str.replace(/\./g, '').replace(',', '.'); + } else { + str = str.replace(/,/g, ''); + } + } else if (str.includes(',')) { + str = str.replace(',', '.'); + } + + const num = parseFloat(str); + return isNaN(num) || num <= 0 ? null : Math.round(num); +} + +/** + * Função utilitária resiliente para extrair o comprimento numérico de subcomponentes. + */ +export function extrairComprimentoComponenteSeguro(item: any): number | null { + if (!item) return null; + + const raw = + item.comprimento_componente ?? + item.comprimentoComponente ?? + item.comprimento ?? + item.comprimento_ref ?? + item.compr ?? + item.comp ?? + item['Comprimento Componente'] ?? + item['Comprimento (mm)'] ?? + item['Comprimento'] ?? + item['Comp. (mm)']; + + if (raw === undefined || raw === null || raw === '' || raw === '-') return null; + + if (typeof raw === 'number') { + return isNaN(raw) || raw <= 0 ? null : Math.round(raw); + } + + let str = String(raw).trim().replace(/mm/gi, '').trim(); + if (str.includes('.') && str.includes(',')) { + if (str.indexOf('.') < str.indexOf(',')) { + str = str.replace(/\./g, '').replace(',', '.'); + } else { + str = str.replace(/,/g, ''); + } + } else if (str.includes(',')) { + str = str.replace(',', '.'); + } + + const num = parseFloat(str); + return isNaN(num) || num <= 0 ? null : Math.round(num); +} + /** * Normaliza e agrupa linhas de peças e subcomponentes */ @@ -45,6 +129,7 @@ export function consolidarPecasEComponentes(rawItems: any[]): { if (!marca) return; const pecaKey = `${ofNumber}-${etapaFase}-${marca}`; + const comprimentoFinal = extrairComprimentoSeguro(item); const pecaData = { of_number: ofNumber, @@ -54,12 +139,7 @@ export function consolidarPecasEComponentes(rawItems: any[]): { quantidade: Number(item.quantidade) || 0, peso_unitario: Number(item.peso_unitario) || 0, peso_total: Number(item.peso_total) || 0, - comprimento: - item.comprimento !== undefined && item.comprimento !== null - ? Number(item.comprimento) - : item.comprimento_ref - ? Number(item.comprimento_ref) - : null, + comprimento: comprimentoFinal, tratamento_superficial: item.tratamento_superficial || 'pintura', material: item.material || 'Aço A36', perfil_principal: item.perfil_principal || item.descricao || '', @@ -68,6 +148,9 @@ export function consolidarPecasEComponentes(rawItems: any[]): { if (!pecasMap.has(pecaKey)) { pecasMap.set(pecaKey, pecaData); + } else if (comprimentoFinal && !pecasMap.get(pecaKey).comprimento) { + // Se a peça já estava no mapa mas sem comprimento e esta linha tem, atualiza + pecasMap.get(pecaKey).comprimento = comprimentoFinal; } // Se o item contém um subcomponente @@ -92,12 +175,14 @@ export function consolidarPecasEComponentes(rawItems: any[]): { } cleanMarca = String(numVal); + const compComprimento = extrairComprimentoComponenteSeguro(item); + const compData = { marca_componente: cleanMarca, descricao: item.descricao_componente || item.descricao || '', perfil: item.perfil_componente || item.perfil_principal || '', peso_unitario: Number(item.peso_unitario_componente) || 0, - comprimento: item.comprimento_componente ? Number(item.comprimento_componente) : null, + comprimento: compComprimento, quantidade_por_peca: Math.max(1, Math.round(Number(item.quantidade_por_peca) || 1)), }; @@ -110,6 +195,12 @@ export function consolidarPecasEComponentes(rawItems: any[]): { const exists = comps.some((c) => c.marca_componente === cleanMarca); if (!exists) { comps.push(compData); + } else if (compComprimento) { + // Se já existia mas sem comprimento, atualiza + const cExistente = comps.find((c) => c.marca_componente === cleanMarca); + if (cExistente && !cExistente.comprimento) { + cExistente.comprimento = compComprimento; + } } } }); diff --git a/src/utils/importExecutionService.ts b/src/utils/importExecutionService.ts index e9efc84..861e819 100644 --- a/src/utils/importExecutionService.ts +++ b/src/utils/importExecutionService.ts @@ -1,5 +1,6 @@ import { supabase } from '@/integrations/supabase/client'; import { ItemAnaliseImportacao } from '@/types/importDiff'; +import { extrairComprimentoSeguro } from '@/utils/importDiffService'; export interface ExecucaoImportacaoParams { itens: ItemAnaliseImportacao[]; @@ -75,12 +76,14 @@ export async function executarImportacaoConciliada( const pesoUnitFinal = Number(importado.peso_unitario || existente.peso_unitario || 0); const pesoTotalCalculado = Number((qtdFinal * pesoUnitFinal).toFixed(3)) || Number(importado.peso_total || 0); + const comprFinal = extrairComprimentoSeguro(importado) ?? extrairComprimentoSeguro(existente); + const payloadUpdate: any = { descricao: importado.descricao || existente.descricao || '', quantidade: qtdFinal, peso_unitario: pesoUnitFinal, peso_total: pesoTotalCalculado, - comprimento: importado.comprimento !== undefined ? importado.comprimento : existente.comprimento, + comprimento: comprFinal, tratamento_superficial: importado.tratamento_superficial || existente.tratamento_superficial || 'pintura', material: importado.material || existente.material || 'Aço A36', perfil_principal: importado.perfil_principal || existente.perfil_principal || '', @@ -170,6 +173,7 @@ export async function executarImportacaoConciliada( const qtd = Number(imp.quantidade) || 0; const pesoU = Number(imp.peso_unitario) || 0; const pesoT = Number(imp.peso_total) || Number((qtd * pesoU).toFixed(3)); + const comprFinal = extrairComprimentoSeguro(imp); return { of_number: imp.of_number, @@ -179,7 +183,7 @@ export async function executarImportacaoConciliada( quantidade: qtd, peso_unitario: pesoU, peso_total: pesoT, - comprimento: imp.comprimento || null, + comprimento: comprFinal, tratamento_superficial: imp.tratamento_superficial || 'pintura', material: imp.material || 'Aço A36', perfil_principal: imp.perfil_principal || '',