// SGAB v2 — Calibração RBC e Rastreada const { useState, useMemo } = React; function Calibracao({ user, toast, onNav }) { const [db, setDb] = useState(() => SGAB.getDB()); const [view, setView] = useState('lista'); // lista | nova | detalhe | cert const [sel, setSel] = useState(null); const [tipo, setTipo] = useState('RBC'); const [form, setForm] = useState(null); const { fmt } = SGAB; function reload() { setDb(SGAB.getDB()); } // ── NOVA CALIBRAÇÃO: INIT FORM ──────────────────────────────── function novaCalib(t) { setTipo(t); setForm({ tipo:t, data:fmt.today(), local:'In Situ', tecnico:user.nome, clienteId:'', equipamentoId:'', osId:'', cond:{ temp:'22', umid:'55', press:'1013', densAr:'1.193' }, padroes:[{ tag:'PAD-001', cert:'', val:'', rastr:'IPT/INMETRO', classe:'F1', cap:'', res:'', incert:'', k:'2' }], pontos:[ {nom:'',l1:'',l2:'',l3:''}, {nom:'',l1:'',l2:'',l3:''}, {nom:'',l1:'',l2:'',l3:''}, {nom:'',l1:'',l2:'',l3:''}, {nom:'',l1:'',l2:'',l3:''}, ], excent:[ {pos:'A — Centro', carga:'',leit:''}, {pos:'B — Frente', carga:'',leit:''}, {pos:'C — Trás', carga:'',leit:''}, {pos:'D — Esquerda', carga:'',leit:''}, {pos:'E — Direita', carga:'',leit:''}, {pos:'F — Centro', carga:'',leit:''}, ], conclusao:'CONFORME', obs:'' }); setView('nova'); } // ── SALVAR CALIBRAÇÃO ───────────────────────────────────────── function salvarCalib() { if (!form.clienteId) { toast('Selecione o cliente.','error'); return; } if (!form.equipamentoId) { toast('Selecione o equipamento.','error'); return; } const ndb = SGAB.getDB(); const id = SGAB.nxtId(ndb,'calib'); const ano2 = String(new Date().getFullYear()).slice(2); const mes2 = String(new Date().getMonth()+1).padStart(2,'0'); const dia2 = String(new Date().getDate()).padStart(2,'0'); const eq = ndb.equipamentos.find(e=>e.id===+form.equipamentoId); const cli = ndb.clientes.find(c=>c.id===+form.clienteId); const numero = tipo==='RBC' ? `CCR_${String(id+429).padStart(3,'0')}` : `CC_${Date.now()}`; const sufixo = `${dia2}.${mes2}.${ano2}_${(cli?.fantasia||'').replace(/\s/g,'').slice(0,8)}`; // Auto-calculate pontos const pontos = form.pontos.filter(p=>p.nom!=='').map(p=>{ if (!p.l1||!p.l2||!p.l3) return {...p,med:0,err:0,corr:0,tol:50,incU:0,ok:true}; const r = SGAB.calcPonto(p.l1,p.l2,p.l3,p.nom); const pad = form.padroes[0]; const div = eq?.divE?.replace(/[^\d.]/g,'')||'10'; const iu = SGAB.calcIncerteza(div,pad?.incert||'0.01',r.rep); const tol = calcTolerance(+p.nom, eq?.cap?.replace(/[^\d.]/g,'')||1000, eq?.classe||'III'); return {...p, med:r.med, err:r.err, corr:r.corr, tol, incU:+iu.U.toFixed(2), ok:Math.abs(r.err)<=tol}; }); const conclusao = pontos.every(p=>p.ok) ? 'CONFORME' : 'NÃO CONFORME'; const newCalib = { id, tipo, numero, sufixo, clienteId:+form.clienteId, equipamentoId:+form.equipamentoId, osId:form.osId?+form.osId:null, data:form.data, local:form.local, tecnico:form.tecnico, cond:form.cond, padroes:form.padroes, pontos, excent:form.excent, conclusao, obs:form.obs }; ndb.calibracoes.push(newCalib); // Atualiza ultima calibração do equip const eIdx = ndb.equipamentos.findIndex(e=>e.id===+form.equipamentoId); if(eIdx>=0){ ndb.equipamentos[eIdx].ultimaCalib = form.data; const proxDate = new Date(form.data); proxDate.setFullYear(proxDate.getFullYear()+1); ndb.equipamentos[eIdx].proxCalib = proxDate.toISOString().slice(0,10); } SGAB.saveDB(ndb); reload(); setSel(newCalib); setView('detalhe'); toast(`Calibração ${numero} salva com sucesso!`,'success'); } function calcTolerance(valor, cap, classe) { const c = +cap; if (classe==='II') return valor <= c*0.5 ? c*0.001 : valor <= c*0.8 ? c*0.002 : c*0.003; if (classe==='IIII') return valor <= c*0.5 ? c*0.02 : valor <= c*0.8 ? c*0.03 : c*0.05; // Classe III (padrão) return valor <= c*0.5 ? c*0.01 : valor <= c*0.8 ? c*0.02 : c*0.03; } // ── CERTIFICADO PDF ─────────────────────────────────────────── function gerarCertificado(calib) { const cli = db.clientes.find(c=>c.id===calib.clienteId); const eq = db.equipamentos.find(e=>e.id===calib.equipamentoId); const config = db.config.empresa; const isRBC = calib.tipo==='RBC'; const tituloCert = isRBC ? 'CERTIFICADO DE CALIBRAÇÃO RBC' : 'CERTIFICADO DE CONFORMIDADE — CALIBRAÇÃO RASTREADA'; const html = `${calib.numero}
${config.nome}${config.cnpj}
${config.endereco} — ${config.cidade}/${config.uf}
${config.norma}
${isRBC?`
⭐ ACREDITADO CGCRE — ${config.acred}
`:'
CALIBRAÇÃO RASTREADA — DOC 11
'}
${calib.numero}
${tituloCert}
Data: ${fmt.date(calib.data)} | Local: ${calib.local}
01 — IDENTIFICAÇÃO DO INSTRUMENTO
Fabricante${eq?.fab||'—'}Modelo${eq?.modelo||'—'}
N° de Série${eq?.serie||'—'}TAG / Patrimônio${eq?.tag||'—'}
Capacidade${eq?.cap||'—'}Divisão (d=e)${eq?.divD||'—'}
Classe de Exatidão${eq?.classe||'—'}Portaria${eq?.portaria||'—'}
Localização${eq?.local||'—'} — ${cli?.razaoSocial||'—'}, ${cli?.cidade||'—'}/${cli?.uf||'—'}
02 — CONDIÇÕES AMBIENTAIS
Temperatura (°C)${calib.cond?.temp||'—'}Umidade Relativa (%)${calib.cond?.umid||'—'}
Pressão Atm. (hPa)${calib.cond?.press||'—'}Densidade do Ar (kg/m³)${calib.cond?.densAr||'—'}
03 — PADRÕES DE TRABALHO UTILIZADOS
${(calib.padroes||[]).map(p=>``).join('')}
TAGN° CertificadoValidadeRastreabilidadeClasseCapacidadeResoluçãoIncertezak
${p.tag}${p.cert}${fmt.date(p.val)}${p.rastr}${p.classe}${p.cap}${p.res}${p.incert} g${p.k}
04 — RESULTADOS DE CALIBRAÇÃO — ERROS E CORREÇÕES
${(calib.pontos||[]).map(p=>``).join('')}
Carga Nominal (g)Leitura 1Leitura 2Leitura 3MédiaErro (g)Correção (g)Tolerância (g)Incerteza U (g)Resultado
${p.nom}${p.l1}${p.l2}${p.l3}${p.med}${p.err}${p.corr}±${p.tol}${p.incU}${p.ok?'CONFORME':'NÃO CONFORME'}
05 — ENSAIO DE EXCENTRICIDADE
${(calib.excent||[]).map((e,i,arr)=>{const ref=arr[0]?.leit||0;const diff=+e.leit-+ref;return``}).join('')}
PosiçãoCarga de Ensaio (g)Leitura (g)Diferença (g)
${e.pos}${e.carga}${e.leit}${diff>=0?'+':''}${diff}
${calib.conclusao==='CONFORME'?'✓ CONFORME':'✗ NÃO CONFORME'} — ${calib.conclusao==='CONFORME'?'O instrumento atende aos requisitos metrológicos aplicáveis.':'O instrumento não atende aos requisitos metrológicos. Recomenda-se ajuste/reparo.'}
${calib.obs?`
OBSERVAÇÕES

${calib.obs}

`:''}
Técnico Responsável
${calib.tecnico||user.nome}
Levi Balanças
Responsável pelo Cliente
${cli?.contato||'—'}
${cli?.razaoSocial||'—'}
${isRBC?`
Responsável Técnico RBC
Acreditação ${config.acred}
Data: ${fmt.date(calib.data)}
`:''}