fix(conversores): calibrar extracao de quantidades, comprimentos e pesos no conversor advance steel
This commit is contained in:
@@ -398,7 +398,7 @@
|
|||||||
let lastPage = null;
|
let lastPage = null;
|
||||||
|
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
if (lastPage === null || item.page !== lastPage || Math.abs(item.y - lastY) > 3.5) {
|
if (lastPage === null || item.page !== lastPage || Math.abs(item.y - (lastY ?? item.y)) > 3.5) {
|
||||||
if (currentLine.length > 0) {
|
if (currentLine.length > 0) {
|
||||||
lines.push(currentLine);
|
lines.push(currentLine);
|
||||||
}
|
}
|
||||||
@@ -416,148 +416,216 @@
|
|||||||
let defaultOf = "";
|
let defaultOf = "";
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const lineText = line.map(i => i.text).join(' ');
|
const lineText = line.map(i => i.text).join(' ');
|
||||||
const matchTrabalho = lineText.match(/Trabalho:\s*([A-Za-z0-9\-]+)/i);
|
const matchTrabalho = lineText.match(/Trabalho:\s*([A-Za-z0-9\-]+)/i) || lineText.match(/Projeto:\s*([A-Za-z0-9\-]+)/i);
|
||||||
if (matchTrabalho) {
|
if (matchTrabalho) {
|
||||||
defaultOf = matchTrabalho[1].replace('-', '');
|
defaultOf = matchTrabalho[1].replace(/-/g, '');
|
||||||
log(`OF / Trabalho identificado no cabeçalho: ${defaultOf}`);
|
log(`OF / Trabalho identificado no cabeçalho: ${defaultOf}`);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calibração inicial das colunas X
|
||||||
|
const colX = {
|
||||||
|
marca: 30,
|
||||||
|
qtde: 100,
|
||||||
|
nome: 180,
|
||||||
|
qualidade: 290,
|
||||||
|
comprimento: 370,
|
||||||
|
largura: 430,
|
||||||
|
pesoPeca: 490,
|
||||||
|
totalPeso: 550,
|
||||||
|
superficie: 610,
|
||||||
|
totalSuperficie: 670
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
const lineText = line.map(i => i.text).join(' ').toLowerCase();
|
||||||
|
if (lineText.includes('marca') && (lineText.includes('qtde') || lineText.includes('quant'))) {
|
||||||
|
for (const item of line) {
|
||||||
|
const t = item.text.toLowerCase().trim();
|
||||||
|
if (t === 'marca') colX.marca = item.x;
|
||||||
|
else if (t.startsWith('qtd') || t.startsWith('quant')) colX.qtde = item.x;
|
||||||
|
else if (t === 'nome') colX.nome = item.x;
|
||||||
|
else if (t.startsWith('qualid')) colX.qualidade = item.x;
|
||||||
|
else if (t.startsWith('comprim')) colX.comprimento = item.x;
|
||||||
|
else if (t.startsWith('largura')) colX.largura = item.x;
|
||||||
|
else if (t.includes('peso da') || t.includes('da peça') || t.includes('piece')) colX.pesoPeca = item.x;
|
||||||
|
else if (t.includes('total peso') || (t === 'total' && item.x < 600)) colX.totalPeso = item.x;
|
||||||
|
else if (t.includes('total superf') || (t === 'total' && item.x >= 600)) colX.totalSuperficie = item.x;
|
||||||
|
else if (t.startsWith('superf')) colX.superficie = item.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNearestCol = (x) => {
|
||||||
|
let bestCol = '';
|
||||||
|
let minDiff = Infinity;
|
||||||
|
for (const [colName, targetX] of Object.entries(colX)) {
|
||||||
|
const diff = Math.abs(x - targetX);
|
||||||
|
if (diff < minDiff) {
|
||||||
|
minDiff = diff;
|
||||||
|
bestCol = colName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestCol;
|
||||||
|
};
|
||||||
|
|
||||||
const assemblies = [];
|
const assemblies = [];
|
||||||
let currentAssembly = null;
|
let currentAssembly = null;
|
||||||
|
|
||||||
// Filtro estrito para evitar linhas duplicadas/ruídos de componentes repetidos na listagem
|
|
||||||
const processedMarks = new Set();
|
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
const lineText = line.map(i => i.text).join(' ');
|
const lineText = line.map(i => i.text).join(' ');
|
||||||
|
|
||||||
if (lineText.includes('Lista de Peças') || lineText.includes('Cliente:') || lineText.includes('Marca Quant Nome') || lineText.includes('Superfície')) {
|
if (
|
||||||
|
lineText.includes('Lista de Peças') ||
|
||||||
|
lineText.includes('Cliente:') ||
|
||||||
|
lineText.includes('Marca Quant Nome') ||
|
||||||
|
lineText.includes('Superfície') ||
|
||||||
|
lineText.includes('Trabalho:')
|
||||||
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const firstToken = line[0].text;
|
let markStr = '';
|
||||||
const markMatch = firstToken.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || firstToken.match(/^([A-Za-z0-9]+)-(\d+)$/);
|
let markTokensConsumed = 0;
|
||||||
|
|
||||||
if (markMatch) {
|
const firstTok = line[0].text.trim();
|
||||||
const parts = firstToken.split('-');
|
if (firstTok.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || firstTok.match(/^([A-Za-z0-9]+)-(\d+)$/)) {
|
||||||
let ofCode = "";
|
markStr = firstTok;
|
||||||
let phaseCode = "0";
|
markTokensConsumed = 1;
|
||||||
let pieceNumber = "";
|
} else {
|
||||||
|
let candidate = '';
|
||||||
|
let found3 = null;
|
||||||
|
let found2 = null;
|
||||||
|
|
||||||
if (parts.length === 3) {
|
for (let k = 0; k < Math.min(line.length, 6); k++) {
|
||||||
ofCode = parts[0];
|
candidate += line[k].text.trim();
|
||||||
phaseCode = parts[1];
|
if (candidate.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/)) {
|
||||||
pieceNumber = parts[2];
|
found3 = { mark: candidate, count: k + 1 };
|
||||||
} else if (parts.length === 2) {
|
break;
|
||||||
ofCode = parts[0];
|
}
|
||||||
phaseCode = "0";
|
if (candidate.match(/^([A-Za-z0-9]+)-(\d+)$/) && !found2) {
|
||||||
pieceNumber = parts[1];
|
found2 = { mark: candidate, count: k + 1 };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (found3) {
|
||||||
|
markStr = found3.mark;
|
||||||
|
markTokensConsumed = found3.count;
|
||||||
|
} else if (found2) {
|
||||||
|
markStr = found2.mark;
|
||||||
|
markTokensConsumed = found2.count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (markStr) {
|
||||||
|
const parts = markStr.split('-');
|
||||||
|
const ofCode = parts.length === 3 ? parts[0] : (parts.length === 2 ? parts[0] : defaultOf);
|
||||||
|
const phaseCode = parts.length === 3 ? parts[1] : '0';
|
||||||
|
const pieceNumber = parts.length === 3 ? parts[2] : parts[1];
|
||||||
|
|
||||||
const pieceNumInt = parseInt(pieceNumber, 10);
|
const pieceNumInt = parseInt(pieceNumber, 10);
|
||||||
const isComponent = !isNaN(pieceNumInt) && pieceNumInt >= 1000;
|
const isComponent = !isNaN(pieceNumInt) && pieceNumInt >= 1000;
|
||||||
|
const remainingItems = line.slice(markTokensConsumed);
|
||||||
|
|
||||||
if (!isComponent) {
|
if (!isComponent) {
|
||||||
// Evitar duplicidade caso a mesma marca principal apareça mais de uma vez agrupada
|
// Se a marca for idêntica à da peça principal atual, é a linha de detalhe da peça simples!
|
||||||
if (processedMarks.has(firstToken)) {
|
if (currentAssembly && currentAssembly.rawMark === markStr) {
|
||||||
|
for (const it of remainingItems) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'qualidade' && !currentAssembly.material) {
|
||||||
|
currentAssembly.material = it.text;
|
||||||
|
} else if (col === 'comprimento' && valNum > 0) {
|
||||||
|
currentAssembly.comprimento = valNum;
|
||||||
|
} else if (col === 'pesoPeca' && valNum > 0) {
|
||||||
|
currentAssembly.pesoUnitario = valNum;
|
||||||
|
} else if (col === 'totalPeso' && valNum > 0) {
|
||||||
|
currentAssembly.pesoTotal = valNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
processedMarks.add(firstToken);
|
|
||||||
|
|
||||||
|
// Nova Peça Principal
|
||||||
let mainQuant = 1;
|
let mainQuant = 1;
|
||||||
let mainDesc = "";
|
const descTokens = [];
|
||||||
|
|
||||||
if (line.length >= 2) {
|
for (const it of remainingItems) {
|
||||||
const qVal = parseInt(line[1].text, 10);
|
const col = getNearestCol(it.x);
|
||||||
if (!isNaN(qVal)) {
|
const qVal = parseInt(it.text, 10);
|
||||||
|
if (col === 'qtde' && !isNaN(qVal) && qVal > 0) {
|
||||||
mainQuant = qVal;
|
mainQuant = qVal;
|
||||||
// CORREÇÃO 2: Garantir que a descrição seja única, pegando apenas o primeiro token descritivo limpo (ex: W 310x38.7)
|
} else if (col === 'nome' || (!it.text.match(/^[0-9.,]+$/) && descTokens.length === 0)) {
|
||||||
let rawDescTokens = line.slice(2).map(item => item.text);
|
descTokens.push(it.text);
|
||||||
if (rawDescTokens.length > 0) {
|
}
|
||||||
mainDesc = rawDescTokens[0]; // Nome único/perfil inicial da peça principal
|
}
|
||||||
// Se o perfil tiver complemento separado por espaço (ex: W 310x38.7), agrupar se necessário ou manter limpo
|
|
||||||
if (rawDescTokens.length > 1 && (rawDescTokens[1].includes('x') || rawDescTokens[1].includes('Pl') || !isNaN(parseFloat(rawDescTokens[1])))) {
|
if (mainQuant === 1 && remainingItems.length > 0) {
|
||||||
mainDesc = rawDescTokens.slice(0, 2).join(' ');
|
const firstNum = remainingItems.find((it) => /^[0-9]+$/.test(it.text.trim()));
|
||||||
}
|
if (firstNum) {
|
||||||
}
|
const parsedQ = parseInt(firstNum.text.trim(), 10);
|
||||||
} else {
|
if (parsedQ > 0 && parsedQ < 1000) mainQuant = parsedQ;
|
||||||
let rawDescTokens = line.slice(1).map(item => item.text);
|
|
||||||
mainDesc = rawDescTokens.length > 0 ? rawDescTokens[0] : 'ESTRUTURA';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAssembly = {
|
currentAssembly = {
|
||||||
rawMark: firstToken,
|
rawMark: markStr,
|
||||||
of: ofCode || defaultOf,
|
of: ofCode || defaultOf,
|
||||||
fase: phaseCode,
|
fase: phaseCode,
|
||||||
marca: pieceNumber, // CORREÇÃO 1: Apenas o número da peça (última numeração)
|
marca: pieceNumber,
|
||||||
numeroPeca: pieceNumber,
|
numeroPeca: pieceNumber,
|
||||||
descricao: mainDesc || 'ESTRUTURA',
|
descricao: descTokens.join(' ') || 'ESTRUTURA',
|
||||||
quantidade: mainQuant,
|
quantidade: mainQuant,
|
||||||
|
material: '',
|
||||||
|
comprimento: 0,
|
||||||
pesoUnitario: 0,
|
pesoUnitario: 0,
|
||||||
pesoTotal: 0,
|
pesoTotal: 0,
|
||||||
|
subtotalPeso: 0,
|
||||||
|
isComposed: 'NÃO',
|
||||||
components: []
|
components: []
|
||||||
};
|
};
|
||||||
assemblies.push(currentAssembly);
|
assemblies.push(currentAssembly);
|
||||||
} else {
|
} else {
|
||||||
// Componente subordinado
|
// Componente com marca >= 1000 (Peça Composta)
|
||||||
if (currentAssembly) {
|
if (currentAssembly) {
|
||||||
|
currentAssembly.isComposed = 'SIM';
|
||||||
let compQuant = 1;
|
let compQuant = 1;
|
||||||
let compProfile = "";
|
const compDescTokens = [];
|
||||||
let compMaterial = "A36";
|
let compMaterial = 'A36';
|
||||||
let compLength = 0;
|
let compLength = 0;
|
||||||
let compWeightTotal = 0;
|
let compWeightTotal = 0;
|
||||||
|
|
||||||
let compTokens = line.map(it => it.text);
|
for (const it of remainingItems) {
|
||||||
if (compTokens.length >= 2) {
|
const col = getNearestCol(it.x);
|
||||||
const qTest = parseInt(compTokens[1], 10);
|
const valNum = parseNumeric(it.text);
|
||||||
if (!isNaN(qTest)) compQuant = qTest;
|
const t = it.text.trim();
|
||||||
}
|
|
||||||
|
|
||||||
let matIndex = -1;
|
if (col === 'qtde') {
|
||||||
for (let k = 1; k < compTokens.length; k++) {
|
const qVal = parseInt(t, 10);
|
||||||
const tok = compTokens[k].toUpperCase();
|
if (!isNaN(qVal)) compQuant = qVal;
|
||||||
if (tok.includes('A572') || tok.includes('A36') || tok.includes('A500') || tok.includes('A106') || tok.includes('GR') || tok.includes('INOX') || tok.includes('SAE')) {
|
} else if (col === 'nome') {
|
||||||
matIndex = k;
|
compDescTokens.push(t);
|
||||||
compMaterial = compTokens[k];
|
} else if (col === 'qualidade') {
|
||||||
if (k + 1 < compTokens.length && (compTokens[k+1].toUpperCase().includes('GR') || compTokens[k+1] === '50')) {
|
compMaterial = t;
|
||||||
compMaterial += ' ' + compTokens[k+1];
|
} else if (col === 'comprimento' && valNum > 0) {
|
||||||
}
|
compLength = valNum;
|
||||||
break;
|
} else if (col === 'totalPeso' && valNum > 0) {
|
||||||
|
compWeightTotal = valNum;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matIndex > 2) {
|
if (compWeightTotal === 0) {
|
||||||
compProfile = compTokens.slice(2, matIndex).join(' ');
|
const nums = remainingItems.map((it) => parseNumeric(it.text)).filter((n) => n > 0);
|
||||||
} else if (compTokens.length >= 3) {
|
if (nums.length >= 2) {
|
||||||
compProfile = compTokens[2];
|
compWeightTotal = nums[nums.length - 1];
|
||||||
}
|
|
||||||
|
|
||||||
const afterMatTokens = matIndex !== -1 ? compTokens.slice(matIndex + 1) : compTokens.slice(3);
|
|
||||||
const numericValues = [];
|
|
||||||
afterMatTokens.forEach(t => {
|
|
||||||
const cleanT = t.replace(/[()]/g, '');
|
|
||||||
if (/[0-9]/.test(cleanT) && !cleanT.toUpperCase().includes('GR')) {
|
|
||||||
numericValues.push(cleanT);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (numericValues.length >= 1) {
|
|
||||||
compLength = parseNumeric(numericValues[0]);
|
|
||||||
}
|
|
||||||
// O peso total do componente geralmente é o último ou penúltimo valor numérico da linha de detalhe
|
|
||||||
if (numericValues.length >= 3) {
|
|
||||||
compWeightTotal = parseNumeric(numericValues[numericValues.length - 1]);
|
|
||||||
} else if (numericValues.length === 2) {
|
|
||||||
compWeightTotal = parseNumeric(numericValues[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAssembly.components.push({
|
currentAssembly.components.push({
|
||||||
quantidade: compQuant,
|
quantidade: compQuant,
|
||||||
perfil: compProfile,
|
perfil: compDescTokens.join(' ') || currentAssembly.descricao,
|
||||||
material: compMaterial,
|
material: compMaterial,
|
||||||
comprimento: compLength,
|
comprimento: compLength,
|
||||||
pesoTotal: compWeightTotal
|
pesoTotal: compWeightTotal
|
||||||
@@ -565,11 +633,62 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Linha de subtotal ou peso geral da peça principal
|
// Linhas sem marca explícita
|
||||||
if (currentAssembly && line.length <= 3) {
|
if (currentAssembly) {
|
||||||
const possibleTotal = parseNumeric(line[0].text);
|
if (lineText.includes('M13') || lineText.startsWith('-') || lineText.includes('ASTM') || lineText.includes('10.9')) {
|
||||||
if (possibleTotal > 10 && currentAssembly.pesoTotal === 0) {
|
currentAssembly.isComposed = 'SIM';
|
||||||
currentAssembly.pesoTotal = possibleTotal;
|
let compLength = 0;
|
||||||
|
let compWeightTotal = 0;
|
||||||
|
let compQuant = 1;
|
||||||
|
|
||||||
|
for (const it of line) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'qtde') {
|
||||||
|
const q = parseInt(it.text, 10);
|
||||||
|
if (!isNaN(q)) compQuant = q;
|
||||||
|
} else if (col === 'comprimento' && valNum > 0) {
|
||||||
|
compLength = valNum;
|
||||||
|
} else if (col === 'totalPeso' && valNum > 0) {
|
||||||
|
compWeightTotal = valNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAssembly.components.push({
|
||||||
|
quantidade: compQuant,
|
||||||
|
perfil: lineText.split(' ')[0] || 'ACESSÓRIO',
|
||||||
|
material: '10.9',
|
||||||
|
comprimento: compLength,
|
||||||
|
pesoTotal: compWeightTotal
|
||||||
|
});
|
||||||
|
} else if (lineText.includes('W ') || lineText.includes('C ') || lineText.includes('Pl ')) {
|
||||||
|
let compLength = 0;
|
||||||
|
let compWeightTotal = 0;
|
||||||
|
let compMaterial = 'A36';
|
||||||
|
|
||||||
|
for (const it of line) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'qualidade') compMaterial = it.text;
|
||||||
|
else if (col === 'comprimento' && valNum > 0) compLength = valNum;
|
||||||
|
else if (col === 'totalPeso' && valNum > 0) compWeightTotal = valNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAssembly.components.push({
|
||||||
|
quantidade: 1,
|
||||||
|
perfil: lineText.split(' ')[0] + ' ' + (lineText.split(' ')[1] || ''),
|
||||||
|
material: compMaterial,
|
||||||
|
comprimento: compLength,
|
||||||
|
pesoTotal: compWeightTotal
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
for (const it of line) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'totalPeso' && valNum > 0) {
|
||||||
|
currentAssembly.subtotalPeso = valNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -578,14 +697,14 @@
|
|||||||
log(`Total de Peças Principais processadas: ${assemblies.length}`);
|
log(`Total de Peças Principais processadas: ${assemblies.length}`);
|
||||||
|
|
||||||
extractedData = assemblies.map(asm => {
|
extractedData = assemblies.map(asm => {
|
||||||
const hasComponents = asm.components.length > 0;
|
const isComposed = asm.isComposed === 'SIM' && asm.components.length > 0;
|
||||||
|
|
||||||
let perfilPrincipal = asm.descricao;
|
let perfilPrincipal = asm.descricao;
|
||||||
let materialPrincipal = "A36";
|
let materialPrincipal = asm.material || 'A36';
|
||||||
let maxComp = 0;
|
let maxComp = asm.comprimento || 0;
|
||||||
let sumComponentsWeight = 0;
|
let totalPeso = asm.subtotalPeso > 0 ? asm.subtotalPeso : asm.pesoTotal;
|
||||||
|
|
||||||
if (hasComponents) {
|
if (isComposed) {
|
||||||
|
let sumComponentsWeight = 0;
|
||||||
let bestComp = asm.components[0];
|
let bestComp = asm.components[0];
|
||||||
asm.components.forEach(c => {
|
asm.components.forEach(c => {
|
||||||
if (c.comprimento > maxComp) {
|
if (c.comprimento > maxComp) {
|
||||||
@@ -595,28 +714,31 @@
|
|||||||
sumComponentsWeight += c.pesoTotal;
|
sumComponentsWeight += c.pesoTotal;
|
||||||
});
|
});
|
||||||
|
|
||||||
perfilPrincipal = bestComp.perfil || asm.descricao;
|
if (bestComp) {
|
||||||
materialPrincipal = bestComp.material || "A36";
|
perfilPrincipal = bestComp.perfil || asm.descricao;
|
||||||
|
materialPrincipal = bestComp.material || 'A36';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!totalPeso || totalPeso === 0) {
|
||||||
|
totalPeso = sumComponentsWeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Definir peso total final (prioriza o subtotal extraído do relatório)
|
// Regra de Ouro: Peso Unitário = Peso Total dividido pela quantidade da peça principal
|
||||||
let finalPesoTotal = asm.pesoTotal > 0 ? asm.pesoTotal : sumComponentsWeight;
|
const pesoUnit = asm.quantidade > 0 ? (totalPeso / asm.quantidade) : totalPeso;
|
||||||
|
|
||||||
// CORREÇÃO 3: Peso Unitário = Peso Total dividido pela quantidade da peça principal
|
|
||||||
let finalPesoUnit = asm.quantidade > 0 ? (finalPesoTotal / asm.quantidade) : finalPesoTotal;
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
of: asm.of,
|
of: asm.of,
|
||||||
fase: asm.fase,
|
fase: asm.fase,
|
||||||
marca: asm.marca,
|
marca: asm.marca,
|
||||||
descricao: asm.descricao,
|
descricao: asm.descricao,
|
||||||
isComposed: hasComponents ? "SIM" : "NÃO",
|
isComposed: isComposed ? "SIM" : "NÃO",
|
||||||
quantidade: asm.quantidade,
|
quantidade: asm.quantidade,
|
||||||
material: materialPrincipal,
|
material: materialPrincipal,
|
||||||
perfilPrincipal: perfilPrincipal,
|
perfilPrincipal: perfilPrincipal,
|
||||||
comprimentoMax: maxComp > 0 ? maxComp : "-",
|
comprimentoMax: maxComp > 0 ? maxComp : "-",
|
||||||
pesoUnit: finalPesoUnit > 0 ? finalPesoUnit.toFixed(2) : "-",
|
pesoUnit: pesoUnit > 0 ? pesoUnit.toFixed(2) : "-",
|
||||||
pesoTotal: finalPesoTotal > 0 ? finalPesoTotal.toFixed(2) : "-",
|
pesoTotal: totalPeso > 0 ? totalPeso.toFixed(2) : "-",
|
||||||
tratamentoSuperficial: "pintura"
|
tratamentoSuperficial: "pintura"
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,8 +39,12 @@ interface AssemblyItem {
|
|||||||
numeroPeca: string;
|
numeroPeca: string;
|
||||||
descricao: string;
|
descricao: string;
|
||||||
quantidade: number;
|
quantidade: number;
|
||||||
|
material: string;
|
||||||
|
comprimento: number;
|
||||||
pesoUnitario: number;
|
pesoUnitario: number;
|
||||||
pesoTotal: number;
|
pesoTotal: number;
|
||||||
|
subtotalPeso: number;
|
||||||
|
isComposed: 'SIM' | 'NÃO';
|
||||||
components: ComponentItem[];
|
components: ComponentItem[];
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -203,7 +207,7 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
let defaultOf = '';
|
let defaultOf = '';
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const lineText = line.map((i) => i.text).join(' ');
|
const lineText = line.map((i) => i.text).join(' ');
|
||||||
const matchTrabalho = lineText.match(/Trabalho:\s*([A-Za-z0-9-]+)/i);
|
const matchTrabalho = lineText.match(/Trabalho:\s*([A-Za-z0-9-]+)/i) || lineText.match(/Projeto:\s*([A-Za-z0-9-]+)/i);
|
||||||
if (matchTrabalho) {
|
if (matchTrabalho) {
|
||||||
defaultOf = matchTrabalho[1].replace(/-/g, '');
|
defaultOf = matchTrabalho[1].replace(/-/g, '');
|
||||||
addLog(`OF / Trabalho identificado no cabeçalho: ${defaultOf}`);
|
addLog(`OF / Trabalho identificado no cabeçalho: ${defaultOf}`);
|
||||||
@@ -211,9 +215,55 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Calibração inicial das colunas X para o layout padrão do Advance Steel
|
||||||
|
const colX = {
|
||||||
|
marca: 30,
|
||||||
|
qtde: 100,
|
||||||
|
nome: 180,
|
||||||
|
qualidade: 290,
|
||||||
|
comprimento: 370,
|
||||||
|
largura: 430,
|
||||||
|
pesoPeca: 490,
|
||||||
|
totalPeso: 550,
|
||||||
|
superficie: 610,
|
||||||
|
totalSuperficie: 670
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tentar recalibrar X dinamicamente a partir dos títulos das colunas
|
||||||
|
for (const line of lines) {
|
||||||
|
const lineText = line.map((i) => i.text).join(' ').toLowerCase();
|
||||||
|
if (lineText.includes('marca') && (lineText.includes('qtde') || lineText.includes('quant'))) {
|
||||||
|
for (const item of line) {
|
||||||
|
const t = item.text.toLowerCase().trim();
|
||||||
|
if (t === 'marca') colX.marca = item.x;
|
||||||
|
else if (t.startsWith('qtd') || t.startsWith('quant')) colX.qtde = item.x;
|
||||||
|
else if (t === 'nome') colX.nome = item.x;
|
||||||
|
else if (t.startsWith('qualid')) colX.qualidade = item.x;
|
||||||
|
else if (t.startsWith('comprim')) colX.comprimento = item.x;
|
||||||
|
else if (t.startsWith('largura')) colX.largura = item.x;
|
||||||
|
else if (t.includes('peso da') || t.includes('da peça') || t.includes('piece')) colX.pesoPeca = item.x;
|
||||||
|
else if (t.includes('total peso') || (t === 'total' && item.x < 600)) colX.totalPeso = item.x;
|
||||||
|
else if (t.includes('total superf') || (t === 'total' && item.x >= 600)) colX.totalSuperficie = item.x;
|
||||||
|
else if (t.startsWith('superf')) colX.superficie = item.x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const getNearestCol = (x: number): string => {
|
||||||
|
let bestCol = '';
|
||||||
|
let minDiff = Infinity;
|
||||||
|
for (const [colName, targetX] of Object.entries(colX)) {
|
||||||
|
const diff = Math.abs(x - targetX);
|
||||||
|
if (diff < minDiff) {
|
||||||
|
minDiff = diff;
|
||||||
|
bestCol = colName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestCol;
|
||||||
|
};
|
||||||
|
|
||||||
const assemblies: AssemblyItem[] = [];
|
const assemblies: AssemblyItem[] = [];
|
||||||
let currentAssembly: AssemblyItem | null = null;
|
let currentAssembly: AssemblyItem | null = null;
|
||||||
const processedMarks = new Set<string>();
|
|
||||||
|
|
||||||
for (let i = 0; i < lines.length; i++) {
|
for (let i = 0; i < lines.length; i++) {
|
||||||
const line = lines[i];
|
const line = lines[i];
|
||||||
@@ -223,150 +273,153 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
lineText.includes('Lista de Peças') ||
|
lineText.includes('Lista de Peças') ||
|
||||||
lineText.includes('Cliente:') ||
|
lineText.includes('Cliente:') ||
|
||||||
lineText.includes('Marca Quant Nome') ||
|
lineText.includes('Marca Quant Nome') ||
|
||||||
lineText.includes('Superfície')
|
lineText.includes('Superfície') ||
|
||||||
|
lineText.includes('Trabalho:')
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let firstToken = line[0].text.trim();
|
// 1. Identificar se a linha inicia com marca ou aglutinação de tokens (ex: B133-1-20)
|
||||||
|
let markStr = '';
|
||||||
|
let markTokensConsumed = 0;
|
||||||
|
|
||||||
|
const firstTok = line[0].text.trim();
|
||||||
|
if (firstTok.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || firstTok.match(/^([A-Za-z0-9]+)-(\d+)$/)) {
|
||||||
|
markStr = firstTok;
|
||||||
|
markTokensConsumed = 1;
|
||||||
|
} else {
|
||||||
|
let candidate = '';
|
||||||
|
let found3: { mark: string; count: number } | null = null;
|
||||||
|
let found2: { mark: string; count: number } | null = null;
|
||||||
|
|
||||||
// MONTAGEM INTELIGENTE DE TOKENS SEPARADOS (Ex: ["B132", "-", "1", "-", "1"] -> "B132-1-1")
|
|
||||||
if (!firstToken.includes('-') && line.length >= 3) {
|
|
||||||
let assembled = '';
|
|
||||||
for (let k = 0; k < Math.min(line.length, 6); k++) {
|
for (let k = 0; k < Math.min(line.length, 6); k++) {
|
||||||
assembled += line[k].text.trim();
|
candidate += line[k].text.trim();
|
||||||
if (assembled.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || assembled.match(/^([A-Za-z0-9]+)-(\d+)$/)) {
|
if (candidate.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/)) {
|
||||||
firstToken = assembled;
|
found3 = { mark: candidate, count: k + 1 };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (candidate.match(/^([A-Za-z0-9]+)-(\d+)$/) && !found2) {
|
||||||
|
found2 = { mark: candidate, count: k + 1 };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found3) {
|
||||||
|
markStr = found3.mark;
|
||||||
|
markTokensConsumed = found3.count;
|
||||||
|
} else if (found2) {
|
||||||
|
markStr = found2.mark;
|
||||||
|
markTokensConsumed = found2.count;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const markMatch = firstToken.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || firstToken.match(/^([A-Za-z0-9]+)-(\d+)$/);
|
if (markStr) {
|
||||||
|
const parts = markStr.split('-');
|
||||||
if (markMatch) {
|
const ofCode = parts.length === 3 ? parts[0] : (parts.length === 2 ? parts[0] : defaultOf);
|
||||||
const parts = firstToken.split('-');
|
const phaseCode = parts.length === 3 ? parts[1] : '0';
|
||||||
let ofCode = '';
|
const pieceNumber = parts.length === 3 ? parts[2] : parts[1];
|
||||||
let phaseCode = '0';
|
|
||||||
let pieceNumber = '';
|
|
||||||
|
|
||||||
if (parts.length === 3) {
|
|
||||||
ofCode = parts[0];
|
|
||||||
phaseCode = parts[1];
|
|
||||||
pieceNumber = parts[2];
|
|
||||||
} else if (parts.length === 2) {
|
|
||||||
ofCode = parts[0];
|
|
||||||
phaseCode = '0';
|
|
||||||
pieceNumber = parts[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
const pieceNumInt = parseInt(pieceNumber, 10);
|
const pieceNumInt = parseInt(pieceNumber, 10);
|
||||||
const isComponent = !isNaN(pieceNumInt) && pieceNumInt >= 1000;
|
const isComponent = !isNaN(pieceNumInt) && pieceNumInt >= 1000;
|
||||||
|
const remainingItems = line.slice(markTokensConsumed);
|
||||||
|
|
||||||
if (!isComponent) {
|
if (!isComponent) {
|
||||||
if (processedMarks.has(firstToken)) {
|
// Se a marca for idêntica à da peça principal atual, é a linha de detalhe da peça simples!
|
||||||
|
if (currentAssembly && currentAssembly.rawMark === markStr) {
|
||||||
|
for (const it of remainingItems) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'qualidade' && !currentAssembly.material) {
|
||||||
|
currentAssembly.material = it.text;
|
||||||
|
} else if (col === 'comprimento' && valNum > 0) {
|
||||||
|
currentAssembly.comprimento = valNum;
|
||||||
|
} else if (col === 'pesoPeca' && valNum > 0) {
|
||||||
|
currentAssembly.pesoUnitario = valNum;
|
||||||
|
} else if (col === 'totalPeso' && valNum > 0) {
|
||||||
|
currentAssembly.pesoTotal = valNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
processedMarks.add(firstToken);
|
|
||||||
|
|
||||||
|
// Nova Peça Principal
|
||||||
let mainQuant = 1;
|
let mainQuant = 1;
|
||||||
let mainDesc = '';
|
const descTokens: string[] = [];
|
||||||
|
|
||||||
if (line.length >= 2) {
|
for (const it of remainingItems) {
|
||||||
const qVal = parseInt(line[1].text, 10);
|
const col = getNearestCol(it.x);
|
||||||
if (!isNaN(qVal)) {
|
const qVal = parseInt(it.text, 10);
|
||||||
|
if (col === 'qtde' && !isNaN(qVal) && qVal > 0) {
|
||||||
mainQuant = qVal;
|
mainQuant = qVal;
|
||||||
const rawDescTokens = line.slice(2).map((item) => item.text);
|
} else if (col === 'nome' || (!it.text.match(/^[0-9.,]+$/) && descTokens.length === 0)) {
|
||||||
if (rawDescTokens.length > 0) {
|
descTokens.push(it.text);
|
||||||
mainDesc = rawDescTokens[0];
|
}
|
||||||
if (
|
}
|
||||||
rawDescTokens.length > 1 &&
|
|
||||||
(rawDescTokens[1].includes('x') || rawDescTokens[1].includes('Pl') || !isNaN(parseFloat(rawDescTokens[1])))
|
if (mainQuant === 1 && remainingItems.length > 0) {
|
||||||
) {
|
const firstNum = remainingItems.find((it) => /^[0-9]+$/.test(it.text.trim()));
|
||||||
mainDesc = rawDescTokens.slice(0, 2).join(' ');
|
if (firstNum) {
|
||||||
}
|
const parsedQ = parseInt(firstNum.text.trim(), 10);
|
||||||
}
|
if (parsedQ > 0 && parsedQ < 1000) mainQuant = parsedQ;
|
||||||
} else {
|
|
||||||
const rawDescTokens = line.slice(1).map((item) => item.text);
|
|
||||||
mainDesc = rawDescTokens.length > 0 ? rawDescTokens[0] : 'ESTRUTURA';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAssembly = {
|
currentAssembly = {
|
||||||
rawMark: firstToken,
|
rawMark: markStr,
|
||||||
of: ofCode || defaultOf,
|
of: ofCode || defaultOf,
|
||||||
fase: phaseCode,
|
fase: phaseCode,
|
||||||
marca: pieceNumber,
|
marca: pieceNumber,
|
||||||
numeroPeca: pieceNumber,
|
numeroPeca: pieceNumber,
|
||||||
descricao: mainDesc || 'ESTRUTURA',
|
descricao: descTokens.join(' ') || 'ESTRUTURA',
|
||||||
quantidade: mainQuant,
|
quantidade: mainQuant,
|
||||||
|
material: '',
|
||||||
|
comprimento: 0,
|
||||||
pesoUnitario: 0,
|
pesoUnitario: 0,
|
||||||
pesoTotal: 0,
|
pesoTotal: 0,
|
||||||
|
subtotalPeso: 0,
|
||||||
|
isComposed: 'NÃO',
|
||||||
components: []
|
components: []
|
||||||
};
|
};
|
||||||
assemblies.push(currentAssembly);
|
assemblies.push(currentAssembly);
|
||||||
} else {
|
} else {
|
||||||
|
// Componente com marca >= 1000 (Peça Composta)
|
||||||
if (currentAssembly) {
|
if (currentAssembly) {
|
||||||
|
currentAssembly.isComposed = 'SIM';
|
||||||
let compQuant = 1;
|
let compQuant = 1;
|
||||||
let compProfile = '';
|
const compDescTokens: string[] = [];
|
||||||
let compMaterial = 'A36';
|
let compMaterial = 'A36';
|
||||||
let compLength = 0;
|
let compLength = 0;
|
||||||
let compWeightTotal = 0;
|
let compWeightTotal = 0;
|
||||||
|
|
||||||
const compTokens = line.map((it) => it.text);
|
for (const it of remainingItems) {
|
||||||
if (compTokens.length >= 2) {
|
const col = getNearestCol(it.x);
|
||||||
const qTest = parseInt(compTokens[1], 10);
|
const valNum = parseNumeric(it.text);
|
||||||
if (!isNaN(qTest)) compQuant = qTest;
|
const t = it.text.trim();
|
||||||
}
|
|
||||||
|
|
||||||
let matIndex = -1;
|
if (col === 'qtde') {
|
||||||
for (let k = 1; k < compTokens.length; k++) {
|
const qVal = parseInt(t, 10);
|
||||||
const tok = compTokens[k].toUpperCase();
|
if (!isNaN(qVal)) compQuant = qVal;
|
||||||
if (
|
} else if (col === 'nome') {
|
||||||
tok.includes('A572') ||
|
compDescTokens.push(t);
|
||||||
tok.includes('A36') ||
|
} else if (col === 'qualidade') {
|
||||||
tok.includes('A500') ||
|
compMaterial = t;
|
||||||
tok.includes('A106') ||
|
} else if (col === 'comprimento' && valNum > 0) {
|
||||||
tok.includes('GR') ||
|
compLength = valNum;
|
||||||
tok.includes('INOX') ||
|
} else if (col === 'totalPeso' && valNum > 0) {
|
||||||
tok.includes('SAE')
|
compWeightTotal = valNum;
|
||||||
) {
|
|
||||||
matIndex = k;
|
|
||||||
compMaterial = compTokens[k];
|
|
||||||
if (k + 1 < compTokens.length && (compTokens[k + 1].toUpperCase().includes('GR') || compTokens[k + 1] === '50')) {
|
|
||||||
compMaterial += ' ' + compTokens[k + 1];
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matIndex > 2) {
|
// Fallback caso coordenadas não tenham capturado peso total do componente
|
||||||
compProfile = compTokens.slice(2, matIndex).join(' ');
|
if (compWeightTotal === 0) {
|
||||||
} else if (compTokens.length >= 3) {
|
const nums = remainingItems.map((it) => parseNumeric(it.text)).filter((n) => n > 0);
|
||||||
compProfile = compTokens[2];
|
if (nums.length >= 2) {
|
||||||
}
|
compWeightTotal = nums[nums.length - 1];
|
||||||
|
|
||||||
const afterMatTokens = matIndex !== -1 ? compTokens.slice(matIndex + 1) : compTokens.slice(3);
|
|
||||||
const numericValues: string[] = [];
|
|
||||||
afterMatTokens.forEach((t) => {
|
|
||||||
const cleanT = t.replace(/[()]/g, '');
|
|
||||||
if (/[0-9]/.test(cleanT) && !cleanT.toUpperCase().includes('GR')) {
|
|
||||||
numericValues.push(cleanT);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
if (numericValues.length >= 1) {
|
|
||||||
compLength = parseNumeric(numericValues[0]);
|
|
||||||
}
|
|
||||||
if (numericValues.length >= 3) {
|
|
||||||
compWeightTotal = parseNumeric(numericValues[numericValues.length - 1]);
|
|
||||||
} else if (numericValues.length === 2) {
|
|
||||||
compWeightTotal = parseNumeric(numericValues[1]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
currentAssembly.components.push({
|
currentAssembly.components.push({
|
||||||
quantidade: compQuant,
|
quantidade: compQuant,
|
||||||
perfil: compProfile,
|
perfil: compDescTokens.join(' ') || currentAssembly.descricao,
|
||||||
material: compMaterial,
|
material: compMaterial,
|
||||||
comprimento: compLength,
|
comprimento: compLength,
|
||||||
pesoTotal: compWeightTotal
|
pesoTotal: compWeightTotal
|
||||||
@@ -374,10 +427,64 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (currentAssembly && line.length <= 3) {
|
// Linhas sem marca explícita (subitens, parafusos '-' ou linha de subtotal)
|
||||||
const possibleTotal = parseNumeric(line[0].text);
|
if (currentAssembly) {
|
||||||
if (possibleTotal > 10 && currentAssembly.pesoTotal === 0) {
|
if (lineText.includes('M13') || lineText.startsWith('-') || lineText.includes('ASTM') || lineText.includes('10.9')) {
|
||||||
currentAssembly.pesoTotal = possibleTotal;
|
currentAssembly.isComposed = 'SIM';
|
||||||
|
let compLength = 0;
|
||||||
|
let compWeightTotal = 0;
|
||||||
|
let compQuant = 1;
|
||||||
|
|
||||||
|
for (const it of line) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'qtde') {
|
||||||
|
const q = parseInt(it.text, 10);
|
||||||
|
if (!isNaN(q)) compQuant = q;
|
||||||
|
} else if (col === 'comprimento' && valNum > 0) {
|
||||||
|
compLength = valNum;
|
||||||
|
} else if (col === 'totalPeso' && valNum > 0) {
|
||||||
|
compWeightTotal = valNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAssembly.components.push({
|
||||||
|
quantidade: compQuant,
|
||||||
|
perfil: lineText.split(' ')[0] || 'ACESSÓRIO',
|
||||||
|
material: '10.9',
|
||||||
|
comprimento: compLength,
|
||||||
|
pesoTotal: compWeightTotal
|
||||||
|
});
|
||||||
|
} else if (lineText.includes('W ') || lineText.includes('C ') || lineText.includes('Pl ')) {
|
||||||
|
// Perfil principal de conjunto composto
|
||||||
|
let compLength = 0;
|
||||||
|
let compWeightTotal = 0;
|
||||||
|
let compMaterial = 'A36';
|
||||||
|
|
||||||
|
for (const it of line) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'qualidade') compMaterial = it.text;
|
||||||
|
else if (col === 'comprimento' && valNum > 0) compLength = valNum;
|
||||||
|
else if (col === 'totalPeso' && valNum > 0) compWeightTotal = valNum;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAssembly.components.push({
|
||||||
|
quantidade: 1,
|
||||||
|
perfil: lineText.split(' ')[0] + ' ' + (lineText.split(' ')[1] || ''),
|
||||||
|
material: compMaterial,
|
||||||
|
comprimento: compLength,
|
||||||
|
pesoTotal: compWeightTotal
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Linha com totais do conjunto (ex: 3,5 na coluna Total Peso)
|
||||||
|
for (const it of line) {
|
||||||
|
const col = getNearestCol(it.x);
|
||||||
|
const valNum = parseNumeric(it.text);
|
||||||
|
if (col === 'totalPeso' && valNum > 0) {
|
||||||
|
currentAssembly.subtotalPeso = valNum;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -386,14 +493,14 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
addLog(`Total de Peças Principais extraídas: ${assemblies.length}`);
|
addLog(`Total de Peças Principais extraídas: ${assemblies.length}`);
|
||||||
|
|
||||||
const finalPieces: ExtractedPiece[] = assemblies.map((asm) => {
|
const finalPieces: ExtractedPiece[] = assemblies.map((asm) => {
|
||||||
const hasComponents = asm.components.length > 0;
|
const isComposed = asm.isComposed === 'SIM' && asm.components.length > 0;
|
||||||
|
|
||||||
let perfilPrincipal = asm.descricao;
|
let perfilPrincipal = asm.descricao;
|
||||||
let materialPrincipal = 'A36';
|
let materialPrincipal = asm.material || 'A36';
|
||||||
let maxComp = 0;
|
let maxComp = asm.comprimento || 0;
|
||||||
let sumComponentsWeight = 0;
|
let totalPeso = asm.subtotalPeso > 0 ? asm.subtotalPeso : asm.pesoTotal;
|
||||||
|
|
||||||
if (hasComponents) {
|
if (isComposed) {
|
||||||
|
let sumComponentsWeight = 0;
|
||||||
let bestComp = asm.components[0];
|
let bestComp = asm.components[0];
|
||||||
asm.components.forEach((c) => {
|
asm.components.forEach((c) => {
|
||||||
if (c.comprimento > maxComp) {
|
if (c.comprimento > maxComp) {
|
||||||
@@ -403,25 +510,31 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
sumComponentsWeight += c.pesoTotal;
|
sumComponentsWeight += c.pesoTotal;
|
||||||
});
|
});
|
||||||
|
|
||||||
perfilPrincipal = bestComp.perfil || asm.descricao;
|
if (bestComp) {
|
||||||
materialPrincipal = bestComp.material || 'A36';
|
perfilPrincipal = bestComp.perfil || asm.descricao;
|
||||||
|
materialPrincipal = bestComp.material || 'A36';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!totalPeso || totalPeso === 0) {
|
||||||
|
totalPeso = sumComponentsWeight;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const finalPesoTotal = asm.pesoTotal > 0 ? asm.pesoTotal : sumComponentsWeight;
|
// Regra de Ouro: Peso Unitário = Peso Total dividido pela Quantidade da peça principal
|
||||||
const finalPesoUnit = asm.quantidade > 0 ? finalPesoTotal / asm.quantidade : finalPesoTotal;
|
const pesoUnit = asm.quantidade > 0 ? totalPeso / asm.quantidade : totalPeso;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
of: asm.of,
|
of: asm.of,
|
||||||
fase: asm.fase,
|
fase: asm.fase,
|
||||||
marca: asm.marca,
|
marca: asm.marca,
|
||||||
descricao: asm.descricao,
|
descricao: asm.descricao,
|
||||||
isComposed: hasComponents ? 'SIM' : 'NÃO',
|
isComposed: isComposed ? 'SIM' : 'NÃO',
|
||||||
quantidade: asm.quantidade,
|
quantidade: asm.quantidade,
|
||||||
material: materialPrincipal,
|
material: materialPrincipal,
|
||||||
perfilPrincipal: perfilPrincipal,
|
perfilPrincipal: perfilPrincipal,
|
||||||
comprimentoMax: maxComp > 0 ? maxComp : '-',
|
comprimentoMax: maxComp > 0 ? maxComp : '-',
|
||||||
pesoUnit: finalPesoUnit > 0 ? finalPesoUnit.toFixed(2) : '-',
|
pesoUnit: pesoUnit > 0 ? pesoUnit.toFixed(2) : '-',
|
||||||
pesoTotal: finalPesoTotal > 0 ? finalPesoTotal.toFixed(2) : '-',
|
pesoTotal: totalPeso > 0 ? totalPeso.toFixed(2) : '-',
|
||||||
tratamentoSuperficial: 'pintura'
|
tratamentoSuperficial: 'pintura'
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user