// Schema (idempotente) das tabelas de horário/feriado usadas pela Secretária. // Chamado uma vez no registerNewwhats. Espelha os tipos de `dentistas_horarios`. // // clinicas_horarios — horário de FUNCIONAMENTO da clínica (config do DONO). // feriados — EXCEÇÕES da clínica (fechamento/ponte/horário especial); // os feriados NACIONAIS são computados (ver feriados.js). export async function ensureAgendaSchema(pool) { try { await pool.query(` CREATE TABLE IF NOT EXISTS clinicas_horarios ( id varchar PRIMARY KEY, clinica_id varchar NOT NULL, dia_semana integer NOT NULL, -- 0=domingo … 6=sábado hora_inicio time without time zone NOT NULL, hora_fim time without time zone NOT NULL, ativo smallint DEFAULT 1 )`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_clinicas_horarios_clinica ON clinicas_horarios (clinica_id)`); await pool.query(` CREATE TABLE IF NOT EXISTS feriados ( id varchar PRIMARY KEY, clinica_id varchar NOT NULL, data date NOT NULL, nome varchar NOT NULL, fecha smallint DEFAULT 1, -- 1=fechado o dia; 0=abre em horário especial hora_inicio time without time zone, -- horário especial (quando fecha=0) hora_fim time without time zone, recorrente smallint DEFAULT 0, -- 1=repete todo ano (mesmo dia/mês) created_at timestamptz DEFAULT now() )`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_feriados_clinica_data ON feriados (clinica_id, data)`); // ── dentistas_folgas — folga/férias/indisponibilidade por DATA do dentista. // O dentista (ou o dono) marca dias em que NÃO atende; o /slots pula. await pool.query(` CREATE TABLE IF NOT EXISTS dentistas_folgas ( id varchar PRIMARY KEY, dentista_id varchar NOT NULL, clinica_id varchar, data_inicio date NOT NULL, data_fim date NOT NULL, -- inclusive; = data_inicio p/ 1 dia motivo varchar, created_at timestamptz DEFAULT now() )`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_dentistas_folgas_dent ON dentistas_folgas (dentista_id, data_inicio, data_fim)`); // ── B (salas): agendamentos.sala_id — permite agendar num quarto alugado. // Aditivo/nullable: NÃO afeta o fluxo existente (clínica) quando nulo. await pool.query(`ALTER TABLE agendamentos ADD COLUMN IF NOT EXISTS sala_id varchar`); // ── Janela ESTENDIDA do dentista (zona cinza): a IA agenda livre em // [hora_inicio, hora_fim]; entre a estendida e a livre exige confirmação // humana. Nulo = sem zona cinza (só a janela livre). await pool.query(`ALTER TABLE dentistas_horarios ADD COLUMN IF NOT EXISTS hora_inicio_flex time without time zone`); await pool.query(`ALTER TABLE dentistas_horarios ADD COLUMN IF NOT EXISTS hora_fim_flex time without time zone`); // ── agenda_pedidos — pedidos da ZONA CINZA aguardando a secretária humana. // A IA cria (status 'pendente') e diz "vou confirmar"; a humana confirma // (vira agendamento) ou recusa. await pool.query(` CREATE TABLE IF NOT EXISTS agenda_pedidos ( id varchar PRIMARY KEY, clinica_id varchar NOT NULL, dentista_id varchar, sala_id varchar, data date NOT NULL, hora_inicio time without time zone NOT NULL, hora_fim time without time zone, paciente_nome varchar, paciente_telefone varchar, procedimento varchar, origem varchar DEFAULT 'secretaria_ia', status varchar DEFAULT 'pendente', -- pendente | confirmado | recusado agendamento_id varchar, -- preenchido ao confirmar created_at timestamptz DEFAULT now(), resolved_at timestamptz, resolved_by varchar )`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_agenda_pedidos_clinica_status ON agenda_pedidos (clinica_id, status, created_at)`); // Pedido também serve para DÚVIDAS (sem horário): tipo + resumo; data/hora nullable. await pool.query(`ALTER TABLE agenda_pedidos ADD COLUMN IF NOT EXISTS tipo varchar DEFAULT 'horario'`); // horario | duvida await pool.query(`ALTER TABLE agenda_pedidos ADD COLUMN IF NOT EXISTS resumo text`); await pool.query(`ALTER TABLE agenda_pedidos ALTER COLUMN data DROP NOT NULL`).catch(() => {}); await pool.query(`ALTER TABLE agenda_pedidos ALTER COLUMN hora_inicio DROP NOT NULL`).catch(() => {}); // ── agenda_situacoes — "área de situações": regras da clínica que a Secretária // IA deve seguir (ex.: "plano X não cobre canal posterior — ofereça avaliação"). await pool.query(` CREATE TABLE IF NOT EXISTS agenda_situacoes ( id varchar PRIMARY KEY, clinica_id varchar NOT NULL, texto text NOT NULL, ativo smallint DEFAULT 1, ordem integer DEFAULT 0, created_at timestamptz DEFAULT now() )`); await pool.query(`CREATE INDEX IF NOT EXISTS idx_agenda_situacoes_clinica ON agenda_situacoes (clinica_id)`); // ── agenda_checklist — respostas Sim/Não do DONO sobre a clínica (ver // checklist-defs.js). Conferidas contra o banco → geram pendências de config // e deixam a Secretária cautelosa. resposta: 1=sim, 0=não, NULL=não respondido. await pool.query(` CREATE TABLE IF NOT EXISTS agenda_checklist ( clinica_id varchar NOT NULL, chave varchar NOT NULL, resposta smallint, updated_at timestamptz DEFAULT now(), PRIMARY KEY (clinica_id, chave) )`); return true; } catch (e) { // Não derruba o boot do satélite se o banco estiver indisponível no momento. console.error('[newwhats] ensureAgendaSchema falhou:', e.message); return false; } }