fix: calibracao advance steel quantidade marca 20, comprimentos e subtotal somatoria de peso
This commit is contained in:
@@ -424,160 +424,69 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calibração inicial das colunas X
|
const KNOWN_MATERIALS = [
|
||||||
const colX = {
|
'A572-GR 50', 'A572-GR50', 'A572-GR.50', 'A572 GR50', 'A572GR50', 'A572GR5', 'A572',
|
||||||
marca: 30,
|
'A36', 'ASTM A36', 'ASTM A572', '10.9', '8.8', 'SAE 1020', 'SAE 1045', 'INOX', '304', '316'
|
||||||
qtde: 100,
|
];
|
||||||
nome: 180,
|
|
||||||
qualidade: 290,
|
|
||||||
comprimento: 370,
|
|
||||||
largura: 430,
|
|
||||||
pesoPeca: 490,
|
|
||||||
totalPeso: 550,
|
|
||||||
superficie: 610,
|
|
||||||
totalSuperficie: 670
|
|
||||||
};
|
|
||||||
|
|
||||||
for (const line of lines) {
|
function normalizeLineText(lineStr) {
|
||||||
const lineText = line.map(i => i.text).join(' ').toLowerCase();
|
let s = lineStr.trim();
|
||||||
if (lineText.includes('marca') && (lineText.includes('qtde') || lineText.includes('quant'))) {
|
s = s.replace(/([A-Za-z0-9]+)\s*-\s*(\d+)\s*-\s*(\d+)/g, '$1-$2-$3');
|
||||||
for (const item of line) {
|
s = s.replace(/([A-Za-z0-9]+)\s*-\s*(\d+)/g, '$1-$2');
|
||||||
const t = item.text.toLowerCase().trim();
|
s = s.replace(/A572\s*-\s*GR\s*50/gi, 'A572-GR 50');
|
||||||
if (t === 'marca') colX.marca = item.x;
|
s = s.replace(/A572\s*-\s*GR/gi, 'A572-GR');
|
||||||
else if (t.startsWith('qtd') || t.startsWith('quant')) colX.qtde = item.x;
|
return s;
|
||||||
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;
|
||||||
|
|
||||||
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 rawLineText = line.map(it => it.text).join(' ').trim();
|
||||||
|
const lineText = normalizeLineText(rawLineText);
|
||||||
|
|
||||||
if (
|
if (
|
||||||
lineText.includes('Lista de Peças') ||
|
lineText.includes('LISTA DE PEÇAS') ||
|
||||||
|
lineText.includes('LISTA ESTRUTURADA') ||
|
||||||
|
lineText.includes('DetailDwgExtract') ||
|
||||||
lineText.includes('Cliente:') ||
|
lineText.includes('Cliente:') ||
|
||||||
lineText.includes('Marca Quant Nome') ||
|
lineText.includes('Desenhado') ||
|
||||||
lineText.includes('Superfície') ||
|
lineText.includes('Superfície') ||
|
||||||
lineText.includes('Trabalho:')
|
lineText.includes('Trabalho:') ||
|
||||||
|
lineText.includes('Kg/piece') ||
|
||||||
|
lineText.includes('( mm)') ||
|
||||||
|
(lineText.toLowerCase().includes('marca') && lineText.toLowerCase().includes('nome'))
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let markStr = '';
|
// 1. Linha Mestre de Peça Principal (ex: "B133-1-20 2 Pl 6x160x154" ou "B133-1-11 1 C 100x50x20x2.8")
|
||||||
let markTokensConsumed = 0;
|
const masterMatch = lineText.match(/^([A-Za-z0-9]+(?:-\d+)+)\s+(\d+)\s+([A-Za-z].*)$/);
|
||||||
|
if (masterMatch) {
|
||||||
const firstTok = line[0].text.trim();
|
const fullMark = masterMatch[1];
|
||||||
if (firstTok.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/) || firstTok.match(/^([A-Za-z0-9]+)-(\d+)$/)) {
|
const quant = parseInt(masterMatch[2], 10);
|
||||||
markStr = firstTok;
|
const descCandidate = masterMatch[3].trim();
|
||||||
markTokensConsumed = 1;
|
const parts = fullMark.split('-');
|
||||||
} else {
|
const pieceNumber = parts[parts.length - 1];
|
||||||
let candidate = '';
|
|
||||||
let found3 = null;
|
|
||||||
let found2 = null;
|
|
||||||
|
|
||||||
for (let k = 0; k < Math.min(line.length, 6); k++) {
|
|
||||||
candidate += line[k].text.trim();
|
|
||||||
if (candidate.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/)) {
|
|
||||||
found3 = { mark: candidate, count: k + 1 };
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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 remainingItems = line.slice(markTokensConsumed);
|
|
||||||
|
|
||||||
if (!isComponent) {
|
if (pieceNumInt < 1000) {
|
||||||
// Se a marca for idêntica à da peça principal atual, é a linha de detalhe da peça simples!
|
const hasMaterial = KNOWN_MATERIALS.some(m => descCandidate.includes(m));
|
||||||
if (currentAssembly && currentAssembly.rawMark === markStr) {
|
const hasManyNumbers = (descCandidate.match(/[0-9.,]+/g) || []).length >= 3;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nova Peça Principal
|
if (!hasMaterial && !hasManyNumbers) {
|
||||||
let mainQuant = 1;
|
const ofCode = parts.length >= 2 ? parts[0] : defaultOf;
|
||||||
const descTokens = [];
|
const phaseCode = parts.length >= 3 ? parts[1] : '0';
|
||||||
|
|
||||||
for (const it of remainingItems) {
|
|
||||||
const col = getNearestCol(it.x);
|
|
||||||
const qVal = parseInt(it.text, 10);
|
|
||||||
if (col === 'qtde' && !isNaN(qVal) && qVal > 0) {
|
|
||||||
mainQuant = qVal;
|
|
||||||
} else if (col === 'nome' || (!it.text.match(/^[0-9.,]+$/) && descTokens.length === 0)) {
|
|
||||||
descTokens.push(it.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainQuant === 1 && remainingItems.length > 0) {
|
|
||||||
const firstNum = remainingItems.find((it) => /^[0-9]+$/.test(it.text.trim()));
|
|
||||||
if (firstNum) {
|
|
||||||
const parsedQ = parseInt(firstNum.text.trim(), 10);
|
|
||||||
if (parsedQ > 0 && parsedQ < 1000) mainQuant = parsedQ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentAssembly = {
|
currentAssembly = {
|
||||||
rawMark: markStr,
|
rawMark: fullMark,
|
||||||
of: ofCode || defaultOf,
|
of: ofCode || defaultOf,
|
||||||
fase: phaseCode,
|
fase: phaseCode,
|
||||||
marca: pieceNumber,
|
marca: pieceNumber,
|
||||||
numeroPeca: pieceNumber,
|
numeroPeca: pieceNumber,
|
||||||
descricao: descTokens.join(' ') || 'ESTRUTURA',
|
descricao: descCandidate || 'ESTRUTURA',
|
||||||
quantidade: mainQuant,
|
quantidade: quant > 0 ? quant : 1,
|
||||||
material: '',
|
material: '',
|
||||||
comprimento: 0,
|
comprimento: 0,
|
||||||
pesoUnitario: 0,
|
pesoUnitario: 0,
|
||||||
@@ -587,108 +496,119 @@
|
|||||||
components: []
|
components: []
|
||||||
};
|
};
|
||||||
assemblies.push(currentAssembly);
|
assemblies.push(currentAssembly);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Linha com Subtotal da Marca (ex: "81 3,07" ou "3,5 0,155" ou "2,6 0,25")
|
||||||
|
const tokens = lineText.split(/\s+/);
|
||||||
|
const allTokensNumeric = tokens.length > 0 && tokens.every(tok => /^[0-9.,]+$/.test(tok));
|
||||||
|
if (allTokensNumeric && tokens.length <= 3) {
|
||||||
|
if (currentAssembly && (!currentAssembly.subtotalPeso || currentAssembly.subtotalPeso === 0)) {
|
||||||
|
const subP = parseNumeric(tokens[0]);
|
||||||
|
if (subP > 0) {
|
||||||
|
currentAssembly.subtotalPeso = subP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. Linha de Detalhe ou Componente
|
||||||
|
let itemMark = '';
|
||||||
|
let itemQuant = 1;
|
||||||
|
let remaining = lineText;
|
||||||
|
|
||||||
|
const mMark = remaining.match(/^([A-Za-z0-9]+(?:-\d+)+|-)\s+(\d+)\s+/);
|
||||||
|
if (mMark) {
|
||||||
|
itemMark = mMark[1];
|
||||||
|
itemQuant = parseInt(mMark[2], 10);
|
||||||
|
remaining = remaining.substring(mMark[0].length).trim();
|
||||||
|
} else if (remaining.startsWith('-')) {
|
||||||
|
itemMark = '-';
|
||||||
|
remaining = remaining.substring(1).trim();
|
||||||
|
const mQ = remaining.match(/^(\d+)\s+/);
|
||||||
|
if (mQ) {
|
||||||
|
itemQuant = parseInt(mQ[1], 10);
|
||||||
|
remaining = remaining.substring(mQ[0].length).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let detectedMaterial = '';
|
||||||
|
let matIdx = -1;
|
||||||
|
let matLen = 0;
|
||||||
|
|
||||||
|
for (const mat of KNOWN_MATERIALS) {
|
||||||
|
const idx = remaining.indexOf(mat);
|
||||||
|
if (idx !== -1 && idx > matIdx) {
|
||||||
|
detectedMaterial = mat;
|
||||||
|
matIdx = idx;
|
||||||
|
matLen = mat.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemDesc = '';
|
||||||
|
let afterMat = '';
|
||||||
|
|
||||||
|
if (matIdx !== -1) {
|
||||||
|
itemDesc = remaining.substring(0, matIdx).trim();
|
||||||
|
afterMat = remaining.substring(matIdx + matLen).trim();
|
||||||
} else {
|
} else {
|
||||||
// Componente com marca >= 1000 (Peça Composta)
|
const parts = remaining.split(/\s+/);
|
||||||
|
const firstNumIdx = parts.findIndex(p => /^[0-9.,]+$/.test(p));
|
||||||
|
if (firstNumIdx > 0) {
|
||||||
|
itemDesc = parts.slice(0, firstNumIdx).join(' ');
|
||||||
|
afterMat = parts.slice(firstNumIdx).join(' ');
|
||||||
|
} else {
|
||||||
|
itemDesc = remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const numTokens = afterMat.split(/\s+/).filter(tok => /^[0-9.,]+$/.test(tok));
|
||||||
|
const nums = numTokens.map(parseNumeric);
|
||||||
|
|
||||||
|
let itemComp = 0;
|
||||||
|
let itemPesoTot = 0;
|
||||||
|
|
||||||
|
if (nums.length >= 1) {
|
||||||
|
itemComp = nums[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nums.length === 3) {
|
||||||
|
itemPesoTot = nums[2];
|
||||||
|
} else if (nums.length === 4) {
|
||||||
|
itemPesoTot = nums[2];
|
||||||
|
} else if (nums.length === 5) {
|
||||||
|
itemPesoTot = nums[2];
|
||||||
|
} else if (nums.length >= 6) {
|
||||||
|
itemPesoTot = nums[3];
|
||||||
|
}
|
||||||
|
|
||||||
if (currentAssembly) {
|
if (currentAssembly) {
|
||||||
|
const isSubComp =
|
||||||
|
itemMark === '-' ||
|
||||||
|
itemMark.includes('-100') ||
|
||||||
|
itemMark.includes('-10') ||
|
||||||
|
(itemMark.match(/-\d+$/) && parseInt(itemMark.match(/-(\d+)$/)[1], 10) >= 1000);
|
||||||
|
|
||||||
|
if (isSubComp) {
|
||||||
currentAssembly.isComposed = 'SIM';
|
currentAssembly.isComposed = 'SIM';
|
||||||
let compQuant = 1;
|
|
||||||
const compDescTokens = [];
|
|
||||||
let compMaterial = 'A36';
|
|
||||||
let compLength = 0;
|
|
||||||
let compWeightTotal = 0;
|
|
||||||
|
|
||||||
for (const it of remainingItems) {
|
|
||||||
const col = getNearestCol(it.x);
|
|
||||||
const valNum = parseNumeric(it.text);
|
|
||||||
const t = it.text.trim();
|
|
||||||
|
|
||||||
if (col === 'qtde') {
|
|
||||||
const qVal = parseInt(t, 10);
|
|
||||||
if (!isNaN(qVal)) compQuant = qVal;
|
|
||||||
} else if (col === 'nome') {
|
|
||||||
compDescTokens.push(t);
|
|
||||||
} else if (col === 'qualidade') {
|
|
||||||
compMaterial = t;
|
|
||||||
} else if (col === 'comprimento' && valNum > 0) {
|
|
||||||
compLength = valNum;
|
|
||||||
} else if (col === 'totalPeso' && valNum > 0) {
|
|
||||||
compWeightTotal = valNum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (compWeightTotal === 0) {
|
|
||||||
const nums = remainingItems.map((it) => parseNumeric(it.text)).filter((n) => n > 0);
|
|
||||||
if (nums.length >= 2) {
|
|
||||||
compWeightTotal = nums[nums.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentAssembly.components.push({
|
currentAssembly.components.push({
|
||||||
quantidade: compQuant,
|
quantidade: itemQuant,
|
||||||
perfil: compDescTokens.join(' ') || currentAssembly.descricao,
|
perfil: itemDesc || currentAssembly.descricao,
|
||||||
material: compMaterial,
|
material: detectedMaterial || 'A36',
|
||||||
comprimento: compLength,
|
comprimento: itemComp,
|
||||||
pesoTotal: compWeightTotal
|
pesoTotal: itemPesoTot
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Linhas sem marca explícita
|
|
||||||
if (currentAssembly) {
|
|
||||||
if (lineText.includes('M13') || lineText.startsWith('-') || lineText.includes('ASTM') || lineText.includes('10.9')) {
|
|
||||||
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 ')) {
|
|
||||||
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 {
|
} else {
|
||||||
for (const it of line) {
|
if (!currentAssembly.material && detectedMaterial) {
|
||||||
const col = getNearestCol(it.x);
|
currentAssembly.material = detectedMaterial;
|
||||||
const valNum = parseNumeric(it.text);
|
|
||||||
if (col === 'totalPeso' && valNum > 0) {
|
|
||||||
currentAssembly.subtotalPeso = valNum;
|
|
||||||
}
|
}
|
||||||
|
if (itemComp > 0 && (!currentAssembly.comprimento || currentAssembly.comprimento === 0)) {
|
||||||
|
currentAssembly.comprimento = itemComp;
|
||||||
}
|
}
|
||||||
|
if (itemPesoTot > 0 && (!currentAssembly.pesoTotal || currentAssembly.pesoTotal === 0)) {
|
||||||
|
currentAssembly.pesoTotal = itemPesoTot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -706,6 +626,8 @@
|
|||||||
if (isComposed) {
|
if (isComposed) {
|
||||||
let sumComponentsWeight = 0;
|
let sumComponentsWeight = 0;
|
||||||
let bestComp = asm.components[0];
|
let bestComp = asm.components[0];
|
||||||
|
maxComp = 0;
|
||||||
|
|
||||||
asm.components.forEach(c => {
|
asm.components.forEach(c => {
|
||||||
if (c.comprimento > maxComp) {
|
if (c.comprimento > maxComp) {
|
||||||
maxComp = c.comprimento;
|
maxComp = c.comprimento;
|
||||||
@@ -717,6 +639,7 @@
|
|||||||
if (bestComp) {
|
if (bestComp) {
|
||||||
perfilPrincipal = bestComp.perfil || asm.descricao;
|
perfilPrincipal = bestComp.perfil || asm.descricao;
|
||||||
materialPrincipal = bestComp.material || 'A36';
|
materialPrincipal = bestComp.material || 'A36';
|
||||||
|
maxComp = bestComp.comprimento;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!totalPeso || totalPeso === 0) {
|
if (!totalPeso || totalPeso === 0) {
|
||||||
|
|||||||
@@ -215,51 +215,18 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calibração inicial das colunas X para o layout padrão do Advance Steel
|
const KNOWN_MATERIALS = [
|
||||||
const colX = {
|
'A572-GR 50', 'A572-GR50', 'A572-GR.50', 'A572 GR50', 'A572GR50', 'A572GR5', 'A572',
|
||||||
marca: 30,
|
'A36', 'ASTM A36', 'ASTM A572', '10.9', '8.8', 'SAE 1020', 'SAE 1045', 'INOX', '304', '316'
|
||||||
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
|
const normalizeLineText = (lineStr: string): string => {
|
||||||
for (const line of lines) {
|
let s = lineStr.trim();
|
||||||
const lineText = line.map((i) => i.text).join(' ').toLowerCase();
|
s = s.replace(/([A-Za-z0-9]+)\s*-\s*(\d+)\s*-\s*(\d+)/g, '$1-$2-$3');
|
||||||
if (lineText.includes('marca') && (lineText.includes('qtde') || lineText.includes('quant'))) {
|
s = s.replace(/([A-Za-z0-9]+)\s*-\s*(\d+)/g, '$1-$2');
|
||||||
for (const item of line) {
|
s = s.replace(/A572\s*-\s*GR\s*50/gi, 'A572-GR 50');
|
||||||
const t = item.text.toLowerCase().trim();
|
s = s.replace(/A572\s*-\s*GR/gi, 'A572-GR');
|
||||||
if (t === 'marca') colX.marca = item.x;
|
return s;
|
||||||
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[] = [];
|
||||||
@@ -267,110 +234,56 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
|
|
||||||
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((it) => it.text).join(' ');
|
let rawLineText = line.map((it) => it.text).join(' ').trim();
|
||||||
|
const lineText = normalizeLineText(rawLineText);
|
||||||
|
|
||||||
|
// Ignorar cabeçalhos e metadados conhecidos
|
||||||
if (
|
if (
|
||||||
lineText.includes('Lista de Peças') ||
|
lineText.includes('LISTA DE PEÇAS') ||
|
||||||
|
lineText.includes('LISTA ESTRUTURADA') ||
|
||||||
|
lineText.includes('DetailDwgExtract') ||
|
||||||
lineText.includes('Cliente:') ||
|
lineText.includes('Cliente:') ||
|
||||||
lineText.includes('Marca Quant Nome') ||
|
lineText.includes('Desenhado') ||
|
||||||
lineText.includes('Superfície') ||
|
lineText.includes('Superfície') ||
|
||||||
lineText.includes('Trabalho:')
|
lineText.includes('Trabalho:') ||
|
||||||
|
lineText.includes('Kg/piece') ||
|
||||||
|
lineText.includes('( mm)') ||
|
||||||
|
(lineText.toLowerCase().includes('marca') && lineText.toLowerCase().includes('nome'))
|
||||||
) {
|
) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 1. Identificar se a linha inicia com marca ou aglutinação de tokens (ex: B133-1-20)
|
// 1. Verificar se é Linha Mestre de Peça Principal (ex: "B133-1-20 2 Pl 6x160x154" ou "B133-1-11 1 C 100x50x20x2.8")
|
||||||
let markStr = '';
|
const masterMatch = lineText.match(/^([A-Za-z0-9]+(?:-\d+)+)\s+(\d+)\s+([A-Za-z].*)$/);
|
||||||
let markTokensConsumed = 0;
|
let isMasterLine = false;
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
for (let k = 0; k < Math.min(line.length, 6); k++) {
|
|
||||||
candidate += line[k].text.trim();
|
|
||||||
if (candidate.match(/^([A-Za-z0-9]+)-(\d+)-(\d+)$/)) {
|
|
||||||
found3 = { mark: candidate, count: k + 1 };
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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];
|
|
||||||
|
|
||||||
|
if (masterMatch) {
|
||||||
|
const fullMark = masterMatch[1];
|
||||||
|
const quant = parseInt(masterMatch[2], 10);
|
||||||
|
const descCandidate = masterMatch[3].trim();
|
||||||
|
const parts = fullMark.split('-');
|
||||||
|
const pieceNumber = parts[parts.length - 1];
|
||||||
const pieceNumInt = parseInt(pieceNumber, 10);
|
const pieceNumInt = parseInt(pieceNumber, 10);
|
||||||
const isComponent = !isNaN(pieceNumInt) && pieceNumInt >= 1000;
|
|
||||||
const remainingItems = line.slice(markTokensConsumed);
|
|
||||||
|
|
||||||
if (!isComponent) {
|
// Se o número da peça for menor que 1000, é uma peça principal mestre
|
||||||
// Se a marca for idêntica à da peça principal atual, é a linha de detalhe da peça simples!
|
if (pieceNumInt < 1000) {
|
||||||
if (currentAssembly && currentAssembly.rawMark === markStr) {
|
// Checar se não é uma linha completa de detalhe com material e medidas
|
||||||
for (const it of remainingItems) {
|
const hasMaterial = KNOWN_MATERIALS.some((m) => descCandidate.includes(m));
|
||||||
const col = getNearestCol(it.x);
|
const hasManyNumbers = (descCandidate.match(/[0-9.,]+/g) || []).length >= 3;
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nova Peça Principal
|
if (!hasMaterial && !hasManyNumbers) {
|
||||||
let mainQuant = 1;
|
isMasterLine = true;
|
||||||
const descTokens: string[] = [];
|
const ofCode = parts.length >= 2 ? parts[0] : defaultOf;
|
||||||
|
const phaseCode = parts.length >= 3 ? parts[1] : '0';
|
||||||
for (const it of remainingItems) {
|
|
||||||
const col = getNearestCol(it.x);
|
|
||||||
const qVal = parseInt(it.text, 10);
|
|
||||||
if (col === 'qtde' && !isNaN(qVal) && qVal > 0) {
|
|
||||||
mainQuant = qVal;
|
|
||||||
} else if (col === 'nome' || (!it.text.match(/^[0-9.,]+$/) && descTokens.length === 0)) {
|
|
||||||
descTokens.push(it.text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mainQuant === 1 && remainingItems.length > 0) {
|
|
||||||
const firstNum = remainingItems.find((it) => /^[0-9]+$/.test(it.text.trim()));
|
|
||||||
if (firstNum) {
|
|
||||||
const parsedQ = parseInt(firstNum.text.trim(), 10);
|
|
||||||
if (parsedQ > 0 && parsedQ < 1000) mainQuant = parsedQ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentAssembly = {
|
currentAssembly = {
|
||||||
rawMark: markStr,
|
rawMark: fullMark,
|
||||||
of: ofCode || defaultOf,
|
of: ofCode || defaultOf,
|
||||||
fase: phaseCode,
|
fase: phaseCode,
|
||||||
marca: pieceNumber,
|
marca: pieceNumber,
|
||||||
numeroPeca: pieceNumber,
|
numeroPeca: pieceNumber,
|
||||||
descricao: descTokens.join(' ') || 'ESTRUTURA',
|
descricao: descCandidate || 'ESTRUTURA',
|
||||||
quantidade: mainQuant,
|
quantidade: quant > 0 ? quant : 1,
|
||||||
material: '',
|
material: '',
|
||||||
comprimento: 0,
|
comprimento: 0,
|
||||||
pesoUnitario: 0,
|
pesoUnitario: 0,
|
||||||
@@ -380,111 +293,125 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
components: []
|
components: []
|
||||||
};
|
};
|
||||||
assemblies.push(currentAssembly);
|
assemblies.push(currentAssembly);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. Linha com Subtotal da Marca (ex: "81 3,07" ou "3,5 0,155" ou "2,6 0,25")
|
||||||
|
const tokens = lineText.split(/\s+/);
|
||||||
|
const allTokensNumeric = tokens.length > 0 && tokens.every((tok) => /^[0-9.,]+$/.test(tok));
|
||||||
|
if (allTokensNumeric && tokens.length <= 3) {
|
||||||
|
if (currentAssembly && (!currentAssembly.subtotalPeso || currentAssembly.subtotalPeso === 0)) {
|
||||||
|
const subP = parseNumeric(tokens[0]);
|
||||||
|
if (subP > 0) {
|
||||||
|
currentAssembly.subtotalPeso = subP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
const mMark = remaining.match(/^([A-Za-z0-9]+(?:-\d+)+|-)\s+(\d+)\s+/);
|
||||||
|
if (mMark) {
|
||||||
|
itemMark = mMark[1];
|
||||||
|
itemQuant = parseInt(mMark[2], 10);
|
||||||
|
remaining = remaining.substring(mMark[0].length).trim();
|
||||||
|
} else if (remaining.startsWith('-')) {
|
||||||
|
itemMark = '-';
|
||||||
|
remaining = remaining.substring(1).trim();
|
||||||
|
const mQ = remaining.match(/^(\d+)\s+/);
|
||||||
|
if (mQ) {
|
||||||
|
itemQuant = parseInt(mQ[1], 10);
|
||||||
|
remaining = remaining.substring(mQ[0].length).trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Localizar Material dentro de remaining
|
||||||
|
let detectedMaterial = '';
|
||||||
|
let matIdx = -1;
|
||||||
|
let matLen = 0;
|
||||||
|
|
||||||
|
for (const mat of KNOWN_MATERIALS) {
|
||||||
|
const idx = remaining.indexOf(mat);
|
||||||
|
if (idx !== -1 && idx > matIdx) {
|
||||||
|
detectedMaterial = mat;
|
||||||
|
matIdx = idx;
|
||||||
|
matLen = mat.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let itemDesc = '';
|
||||||
|
let afterMat = '';
|
||||||
|
|
||||||
|
if (matIdx !== -1) {
|
||||||
|
itemDesc = remaining.substring(0, matIdx).trim();
|
||||||
|
afterMat = remaining.substring(matIdx + matLen).trim();
|
||||||
} else {
|
} else {
|
||||||
// Componente com marca >= 1000 (Peça Composta)
|
const parts = remaining.split(/\s+/);
|
||||||
|
const firstNumIdx = parts.findIndex((p) => /^[0-9.,]+$/.test(p));
|
||||||
|
if (firstNumIdx > 0) {
|
||||||
|
itemDesc = parts.slice(0, firstNumIdx).join(' ');
|
||||||
|
afterMat = parts.slice(firstNumIdx).join(' ');
|
||||||
|
} else {
|
||||||
|
itemDesc = remaining;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const numTokens = afterMat.split(/\s+/).filter((tok) => /^[0-9.,]+$/.test(tok));
|
||||||
|
const nums = numTokens.map(parseNumeric);
|
||||||
|
|
||||||
|
let itemComp = 0;
|
||||||
|
let itemPesoTot = 0;
|
||||||
|
|
||||||
|
if (nums.length >= 1) {
|
||||||
|
itemComp = nums[0]; // O comprimento é sempre o primeiro valor numérico após o material
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nums.length === 3) {
|
||||||
|
itemPesoTot = nums[2];
|
||||||
|
} else if (nums.length === 4) {
|
||||||
|
itemPesoTot = nums[2];
|
||||||
|
} else if (nums.length === 5) {
|
||||||
|
itemPesoTot = nums[2];
|
||||||
|
} else if (nums.length >= 6) {
|
||||||
|
itemPesoTot = nums[3];
|
||||||
|
}
|
||||||
|
|
||||||
if (currentAssembly) {
|
if (currentAssembly) {
|
||||||
|
const isSubComp =
|
||||||
|
itemMark === '-' ||
|
||||||
|
itemMark.includes('-100') ||
|
||||||
|
itemMark.includes('-10') ||
|
||||||
|
(itemMark.match(/-\d+$/) && parseInt(itemMark.match(/-(\d+)$/)![1], 10) >= 1000);
|
||||||
|
|
||||||
|
if (isSubComp) {
|
||||||
currentAssembly.isComposed = 'SIM';
|
currentAssembly.isComposed = 'SIM';
|
||||||
let compQuant = 1;
|
|
||||||
const compDescTokens: string[] = [];
|
|
||||||
let compMaterial = 'A36';
|
|
||||||
let compLength = 0;
|
|
||||||
let compWeightTotal = 0;
|
|
||||||
|
|
||||||
for (const it of remainingItems) {
|
|
||||||
const col = getNearestCol(it.x);
|
|
||||||
const valNum = parseNumeric(it.text);
|
|
||||||
const t = it.text.trim();
|
|
||||||
|
|
||||||
if (col === 'qtde') {
|
|
||||||
const qVal = parseInt(t, 10);
|
|
||||||
if (!isNaN(qVal)) compQuant = qVal;
|
|
||||||
} else if (col === 'nome') {
|
|
||||||
compDescTokens.push(t);
|
|
||||||
} else if (col === 'qualidade') {
|
|
||||||
compMaterial = t;
|
|
||||||
} else if (col === 'comprimento' && valNum > 0) {
|
|
||||||
compLength = valNum;
|
|
||||||
} else if (col === 'totalPeso' && valNum > 0) {
|
|
||||||
compWeightTotal = valNum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback caso coordenadas não tenham capturado peso total do componente
|
|
||||||
if (compWeightTotal === 0) {
|
|
||||||
const nums = remainingItems.map((it) => parseNumeric(it.text)).filter((n) => n > 0);
|
|
||||||
if (nums.length >= 2) {
|
|
||||||
compWeightTotal = nums[nums.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
currentAssembly.components.push({
|
currentAssembly.components.push({
|
||||||
quantidade: compQuant,
|
quantidade: itemQuant,
|
||||||
perfil: compDescTokens.join(' ') || currentAssembly.descricao,
|
perfil: itemDesc || currentAssembly.descricao,
|
||||||
material: compMaterial,
|
material: detectedMaterial || 'A36',
|
||||||
comprimento: compLength,
|
comprimento: itemComp,
|
||||||
pesoTotal: compWeightTotal
|
pesoTotal: itemPesoTot
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Linhas sem marca explícita (subitens, parafusos '-' ou linha de subtotal)
|
|
||||||
if (currentAssembly) {
|
|
||||||
if (lineText.includes('M13') || lineText.startsWith('-') || lineText.includes('ASTM') || lineText.includes('10.9')) {
|
|
||||||
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 {
|
} else {
|
||||||
// Linha com totais do conjunto (ex: 3,5 na coluna Total Peso)
|
// Monopeça (peça simples sem componentes subordinados)
|
||||||
for (const it of line) {
|
if (!currentAssembly.material && detectedMaterial) {
|
||||||
const col = getNearestCol(it.x);
|
currentAssembly.material = detectedMaterial;
|
||||||
const valNum = parseNumeric(it.text);
|
|
||||||
if (col === 'totalPeso' && valNum > 0) {
|
|
||||||
currentAssembly.subtotalPeso = valNum;
|
|
||||||
}
|
}
|
||||||
|
if (itemComp > 0 && (!currentAssembly.comprimento || currentAssembly.comprimento === 0)) {
|
||||||
|
currentAssembly.comprimento = itemComp;
|
||||||
}
|
}
|
||||||
|
if (itemPesoTot > 0 && (!currentAssembly.pesoTotal || currentAssembly.pesoTotal === 0)) {
|
||||||
|
currentAssembly.pesoTotal = itemPesoTot;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -502,6 +429,8 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
if (isComposed) {
|
if (isComposed) {
|
||||||
let sumComponentsWeight = 0;
|
let sumComponentsWeight = 0;
|
||||||
let bestComp = asm.components[0];
|
let bestComp = asm.components[0];
|
||||||
|
maxComp = 0;
|
||||||
|
|
||||||
asm.components.forEach((c) => {
|
asm.components.forEach((c) => {
|
||||||
if (c.comprimento > maxComp) {
|
if (c.comprimento > maxComp) {
|
||||||
maxComp = c.comprimento;
|
maxComp = c.comprimento;
|
||||||
@@ -513,8 +442,10 @@ const AdvanceSteelConverterContent: React.FC = () => {
|
|||||||
if (bestComp) {
|
if (bestComp) {
|
||||||
perfilPrincipal = bestComp.perfil || asm.descricao;
|
perfilPrincipal = bestComp.perfil || asm.descricao;
|
||||||
materialPrincipal = bestComp.material || 'A36';
|
materialPrincipal = bestComp.material || 'A36';
|
||||||
|
maxComp = bestComp.comprimento;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Se não obteve subtotal na linha isolada, usar a somatória dos componentes
|
||||||
if (!totalPeso || totalPeso === 0) {
|
if (!totalPeso || totalPeso === 0) {
|
||||||
totalPeso = sumComponentsWeight;
|
totalPeso = sumComponentsWeight;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user