From 5ef130f031b008e070bd71c7057826491fcc21c1 Mon Sep 17 00:00:00 2001 From: Marcos Reifonas Date: Fri, 11 Sep 2026 11:39:42 +0000 Subject: [PATCH] fix: reconhecimento robusto de pecas advance steel sem quantidade inline e recuperacao de pecas compostas --- public/conversor_relatorio_estruturas.html | 34 +++++++++++----- .../conversores/AdvanceSteelConverter.tsx | 40 +++++++++++++------ 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/public/conversor_relatorio_estruturas.html b/public/conversor_relatorio_estruturas.html index 3bd2b44..8f26fa7 100644 --- a/public/conversor_relatorio_estruturas.html +++ b/public/conversor_relatorio_estruturas.html @@ -398,7 +398,7 @@ let lastPage = null; items.forEach(item => { - if (lastPage === null || item.page !== lastPage || Math.abs(item.y - (lastY ?? item.y)) > 3.5) { + if (lastPage === null || item.page !== lastPage || Math.abs(item.y - (lastY ?? item.y)) > 4.5) { if (currentLine.length > 0) { lines.push(currentLine); } @@ -461,21 +461,21 @@ continue; } - // 1. Linha Mestre de Peça Principal (ex: "B133-1-20 2 Pl 6x160x154" ou "B133-1-11 1 C 100x50x20x2.8") - const masterMatch = lineText.match(/^([A-Za-z0-9]+(?:-\d+)+)\s+(\d+)\s+([A-Za-z].*)$/); + // 1. Linha Mestre de Peça Principal (ex: "B133-1-20 2 Pl 6x160x154" ou "B133-1-20 Pl 6x160x154") + const masterMatch = lineText.match(/^([A-Za-z0-9]+(?:-\d+)+)(?:\s+(\d+))?\s+([A-Za-z].*)$/); if (masterMatch) { const fullMark = masterMatch[1]; - const quant = parseInt(masterMatch[2], 10); + const inlineQuant = masterMatch[2] ? parseInt(masterMatch[2], 10) : 0; const descCandidate = masterMatch[3].trim(); const parts = fullMark.split('-'); const pieceNumber = parts[parts.length - 1]; const pieceNumInt = parseInt(pieceNumber, 10); + // Peça principal tem identificador < 1000 e não possui material na linha de cabeçalho da marca if (pieceNumInt < 1000) { const hasMaterial = KNOWN_MATERIALS.some(m => descCandidate.includes(m)); - const hasManyNumbers = (descCandidate.match(/[0-9.,]+/g) || []).length >= 3; - if (!hasMaterial && !hasManyNumbers) { + if (!hasMaterial) { const ofCode = parts.length >= 2 ? parts[0] : defaultOf; const phaseCode = parts.length >= 3 ? parts[1] : '0'; @@ -486,7 +486,7 @@ marca: pieceNumber, numeroPeca: pieceNumber, descricao: descCandidate || 'ESTRUTURA', - quantidade: quant > 0 ? quant : 1, + quantidade: inlineQuant > 0 ? inlineQuant : 1, material: '', comprimento: 0, pesoUnitario: 0, @@ -623,6 +623,8 @@ let maxComp = asm.comprimento || 0; let totalPeso = asm.subtotalPeso > 0 ? asm.subtotalPeso : asm.pesoTotal; + let finalQuant = asm.quantidade; + if (isComposed) { let sumComponentsWeight = 0; let bestComp = asm.components[0]; @@ -645,10 +647,24 @@ if (!totalPeso || totalPeso === 0) { totalPeso = sumComponentsWeight; } + + // Se a quantidade veio 1 (ou não veio no cabeçalho), calcular relação peso total / soma peso unitário dos componentes + if (finalQuant <= 1 && asm.components.length > 0) { + const sumUnitWeights = asm.components.reduce((acc, c) => { + const unitW = c.quantidade > 0 ? (c.pesoTotal / c.quantidade) : c.pesoTotal; + return acc + unitW; + }, 0); + if (sumUnitWeights > 0 && totalPeso > sumUnitWeights * 1.3) { + const ratio = Math.round(totalPeso / sumUnitWeights); + if (ratio > 1) { + finalQuant = ratio; + } + } + } } // Regra de Ouro: Peso Unitário = Peso Total dividido pela quantidade da peça principal - const pesoUnit = asm.quantidade > 0 ? (totalPeso / asm.quantidade) : totalPeso; + const pesoUnit = finalQuant > 0 ? (totalPeso / finalQuant) : totalPeso; return { of: asm.of, @@ -656,7 +672,7 @@ marca: asm.marca, descricao: asm.descricao, isComposed: isComposed ? "SIM" : "NÃO", - quantidade: asm.quantidade, + quantidade: finalQuant, material: materialPrincipal, perfilPrincipal: perfilPrincipal, comprimentoMax: maxComp > 0 ? maxComp : "-", diff --git a/src/components/conversores/AdvanceSteelConverter.tsx b/src/components/conversores/AdvanceSteelConverter.tsx index e916ed6..f6d8260 100644 --- a/src/components/conversores/AdvanceSteelConverter.tsx +++ b/src/components/conversores/AdvanceSteelConverter.tsx @@ -28,6 +28,7 @@ interface ComponentItem { perfil: string; material: string; comprimento: number; + pesoUnitario?: number; pesoTotal: number; } @@ -189,7 +190,7 @@ const AdvanceSteelConverterContent: React.FC = () => { let lastPage: number | null = null; items.forEach((item) => { - if (lastPage === null || item.page !== lastPage || Math.abs(item.y - (lastY ?? item.y)) > 3.5) { + if (lastPage === null || item.page !== lastPage || Math.abs(item.y - (lastY ?? item.y)) > 4.5) { if (currentLine.length > 0) { lines.push(currentLine); } @@ -253,13 +254,12 @@ const AdvanceSteelConverterContent: React.FC = () => { continue; } - // 1. Verificar se é Linha Mestre de Peça Principal (ex: "B133-1-20 2 Pl 6x160x154" ou "B133-1-11 1 C 100x50x20x2.8") - const masterMatch = lineText.match(/^([A-Za-z0-9]+(?:-\d+)+)\s+(\d+)\s+([A-Za-z].*)$/); - let isMasterLine = false; + // 1. Linha Mestre de Peça Principal (ex: "B133-1-20 Pl 6x160x154" ou "B133-1-20 2 Pl 6x160x154") + const masterMatch = lineText.match(/^([A-Za-z0-9]+(?:-\d+)+)(?:\s+(\d+))?\s+([A-Za-z].*)$/); if (masterMatch) { const fullMark = masterMatch[1]; - const quant = parseInt(masterMatch[2], 10); + const quant = masterMatch[2] ? parseInt(masterMatch[2], 10) : 1; const descCandidate = masterMatch[3].trim(); const parts = fullMark.split('-'); const pieceNumber = parts[parts.length - 1]; @@ -267,12 +267,9 @@ const AdvanceSteelConverterContent: React.FC = () => { // Se o número da peça for menor que 1000, é uma peça principal mestre if (pieceNumInt < 1000) { - // Checar se não é uma linha completa de detalhe com material e medidas const hasMaterial = KNOWN_MATERIALS.some((m) => descCandidate.includes(m)); - const hasManyNumbers = (descCandidate.match(/[0-9.,]+/g) || []).length >= 3; - if (!hasMaterial && !hasManyNumbers) { - isMasterLine = true; + if (!hasMaterial) { const ofCode = parts.length >= 2 ? parts[0] : defaultOf; const phaseCode = parts.length >= 3 ? parts[1] : '0'; @@ -312,10 +309,6 @@ const AdvanceSteelConverterContent: React.FC = () => { } // 3. Linha de Detalhe de Monopeça ou Componente Subordinado - // Ex: "B133-1-11 1 C 100x50x20x2.8 A36 540 2,6 2,6 0,25 0,25" - // Ex: "B133-1-1000 1 Pl 6x120x91 A36 120 91 0,5 1,1 0,024 0,049" - // Ex: "B133-1-1002 1 W 150x18.0 A572-GR 50 4.000 72 72 2,76 2,76" - // Ex: "- 12 M13 NaW 32 ASTM A325 10.9 10.9 32 0,1 1,7" let itemMark = ''; let itemQuant = 1; let remaining = lineText; @@ -370,6 +363,7 @@ const AdvanceSteelConverterContent: React.FC = () => { const nums = numTokens.map(parseNumeric); let itemComp = 0; + let itemPUn = 0; let itemPesoTot = 0; if (nums.length >= 1) { @@ -377,12 +371,16 @@ const AdvanceSteelConverterContent: React.FC = () => { } if (nums.length === 3) { + itemPUn = nums[1]; itemPesoTot = nums[2]; } else if (nums.length === 4) { + itemPUn = nums[1]; itemPesoTot = nums[2]; } else if (nums.length === 5) { + itemPUn = nums[1]; itemPesoTot = nums[2]; } else if (nums.length >= 6) { + itemPUn = nums[2]; itemPesoTot = nums[3]; } @@ -400,6 +398,7 @@ const AdvanceSteelConverterContent: React.FC = () => { perfil: itemDesc || currentAssembly.descricao, material: detectedMaterial || 'A36', comprimento: itemComp, + pesoUnitario: itemPUn, pesoTotal: itemPesoTot }); } else { @@ -410,6 +409,9 @@ const AdvanceSteelConverterContent: React.FC = () => { if (itemComp > 0 && (!currentAssembly.comprimento || currentAssembly.comprimento === 0)) { currentAssembly.comprimento = itemComp; } + if (itemPUn > 0 && (!currentAssembly.pesoUnitario || currentAssembly.pesoUnitario === 0)) { + currentAssembly.pesoUnitario = itemPUn; + } if (itemPesoTot > 0 && (!currentAssembly.pesoTotal || currentAssembly.pesoTotal === 0)) { currentAssembly.pesoTotal = itemPesoTot; } @@ -428,6 +430,7 @@ const AdvanceSteelConverterContent: React.FC = () => { if (isComposed) { let sumComponentsWeight = 0; + let sumUnitComponentsWeight = 0; let bestComp = asm.components[0]; maxComp = 0; @@ -437,6 +440,9 @@ const AdvanceSteelConverterContent: React.FC = () => { bestComp = c; } sumComponentsWeight += c.pesoTotal; + if (c.pesoUnitario > 0) { + sumUnitComponentsWeight += c.pesoUnitario * c.quantidade; + } }); if (bestComp) { @@ -449,6 +455,14 @@ const AdvanceSteelConverterContent: React.FC = () => { if (!totalPeso || totalPeso === 0) { totalPeso = sumComponentsWeight; } + + // Detecção de quantidade de conjunto caso tenha vindo 1: + if (asm.quantidade === 1 && sumUnitComponentsWeight > 0 && totalPeso > 0) { + const ratio = Math.round(totalPeso / sumUnitComponentsWeight); + if (ratio > 1) { + asm.quantidade = ratio; + } + } } // Regra de Ouro: Peso Unitário = Peso Total dividido pela Quantidade da peça principal