// SGAB v2 — Ordens de Serviço
const { useState } = React;
function OrdemServico({ user, toast, onNav }) {
const [db, setDb] = useState(() => SGAB.getDB());
const [q, setQ] = useState('');
const [filtro, setFiltro] = useState('todos');
const [modal, setModal] = useState(false);
const [editItem, setEdit] = useState(null);
const [form, setForm] = useState({});
const [detalhe, setDetalhe] = useState(null);
const [imprimindo, setImprimindo] = useState(false);
const { fmt } = SGAB;
const isAdmin = user.perfil === 'administrador';
function reload() { setDb(SGAB.getDB()); }
function F(k,v) { setForm(f=>({...f,[k]:v})); }
const STATUS_OPTS = ['Agendada','Em Andamento','Concluída','Cancelada'];
const TIPO_OPTS = ['Calibração RBC','Calibração Rastreada','Manutenção Corretiva','Manutenção Preventiva','Instalação','Ajuste'];
let lista = db.ordens.filter(o => {
const cli = db.clientes.find(c=>c.id===o.clienteId);
const matchQ = !q || o.numero.toLowerCase().includes(q.toLowerCase()) ||
(cli?.razaoSocial||'').toLowerCase().includes(q.toLowerCase()) ||
(o.tipo||'').toLowerCase().includes(q.toLowerCase());
const matchF = filtro==='todos' || o.status===filtro;
return matchQ && matchF;
}).sort((a,b)=>b.data.localeCompare(a.data));
function openNew() {
const ndb = SGAB.getDB();
const num = `OS_${new Date().getFullYear()}${String(SGAB.nxtId(ndb,'ordens')).padStart(4,'0')}`;
SGAB.saveDB(ndb);
setEdit(null);
setForm({ numero:num, status:'Agendada', data:fmt.today(), pecas:[], equipIds:[], hIni:'08:00', viagem:'0.5h' });
setModal(true);
}
function openEdit(os) { setEdit(os); setForm({...os, pecas:[...(os.pecas||[])], equipIds:[...(os.equipIds||[])]}); setModal(true); }
function salvar() {
if (!form.clienteId) { toast('Selecione o cliente.','error'); return; }
if (!form.tipo) { toast('Selecione o tipo de serviço.','error'); return; }
const ndb = SGAB.getDB();
const obj = {...form, clienteId:+form.clienteId, valor:form.valor?+form.valor:0, equipIds:(form.equipIds||[]).map(Number)};
if (editItem) {
const i = ndb.ordens.findIndex(o=>o.id===editItem.id);
if(i>=0) ndb.ordens[i] = {...ndb.ordens[i],...obj};
} else {
ndb.ordens.push({id:SGAB.nxtId(ndb,'ordens'),...obj});
}
SGAB.saveDB(ndb); reload(); setModal(false);
toast(editItem?'OS atualizada!':'OS criada com sucesso!');
if(detalhe) setDetalhe(ndb.ordens.find(o=>o.id===detalhe.id)||null);
}
function excluir(os) {
if (!confirm(`Excluir ${os.numero}?`)) return;
const ndb = SGAB.getDB(); ndb.ordens = ndb.ordens.filter(o=>o.id!==os.id);
SGAB.saveDB(ndb); reload(); setDetalhe(null); toast('OS removida.','info');
}
function mudarStatus(os, status) {
const ndb = SGAB.getDB();
const i = ndb.ordens.findIndex(o=>o.id===os.id);
if(i>=0) ndb.ordens[i].status = status;
if(status==='Concluída') ndb.ordens[i].hFim = ndb.ordens[i].hFim||'17:00';
SGAB.saveDB(ndb); reload();
if(detalhe) setDetalhe(ndb.ordens.find(o=>o.id===os.id));
toast(`Status alterado para "${status}".`);
}
function printRS(os) {
const cli = db.clientes.find(c=>c.id===os.clienteId);
const equips = db.equipamentos.filter(e=>(os.equipIds||[]).includes(e.id));
const tecnico = db.usuarios.find(u=>u.id===os.tecnicoId);
const config = db.config.empresa;
const html = `
RS — ${os.numero}
01 — DADOS DO CLIENTE
| Razão Social | ${cli?.razaoSocial||'—'} | CNPJ | ${cli?.cnpj||'—'} |
| Endereço | ${cli?.endereco||'—'}, ${cli?.cidade||'—'}/${cli?.uf||'—'} |
| Contato | ${cli?.contato||'—'} | Telefone | ${cli?.tel||'—'} |
|---|
02 — DESCRIÇÃO DOS EQUIPAMENTOS
${equips.length>0?`| TAG | Fabricante | Modelo | N° Série | Capacidade | Lacre Ret. | Lacre Fix. | Selo INMETRO |
${equips.map(e=>`| ${e.tag} | ${e.fab} | ${e.modelo} | ${e.serie} | ${e.cap} | ______ | ______ | ______ |
`).join('')}
`:
'Nenhum equipamento vinculado.
'}
03 — DESCRIÇÃO DOS SERVIÇOS
| Tipo de Serviço | ${os.tipo} | Data | ${fmt.date(os.data)} |
| Descrição | ${os.desc||'—'} |
| Hora Início | ${os.hIni||'—'} | Hora Término | ${os.hFim||'—'} |
| Tempo de Viagem | ${os.viagem||'—'} |
|---|
${(os.pecas&&os.pecas.length>0)?`04 — PEÇAS APLICADAS
| Código | Descrição | Qtd | Valor Unit. | Total |
${os.pecas.map(p=>{const peca=db.estoque.find(e=>e.id===p.estoqueId);return`| ${peca?.cod||'—'} | ${peca?.desc||'—'} | ${p.qtd} | ${fmt.money(p.val)} | ${fmt.money(p.qtd*p.val)} |
`;}).join('')}
`:''}
05 — CUSTOS
| Mão de Obra | ${fmt.money(os.valor*(0.6))} | Desl. + Viagem | ${fmt.money(os.valor*(0.1))} |
| Peças | ${fmt.money((os.pecas||[]).reduce((s,p)=>s+(p.qtd*p.val),0))} | TOTAL GERAL | ${fmt.money(os.valor)} |
|---|
06 — OBSERVAÇÕES
${os.obs||'—'}
Status: ${os.status}
Técnico: ${tecnico?.nome||'—'} | Gerado em: ${new Date().toLocaleDateString('pt-BR')}
Assinatura do Técnico
${tecnico?.nome||'—'}
Assinatura do Responsável
${cli?.contato||'—'} — ${cli?.razaoSocial||'—'}