diff --git a/src/db.mjs b/src/db.mjs index 034367b..6ebf47a 100644 --- a/src/db.mjs +++ b/src/db.mjs @@ -238,6 +238,85 @@ const MIGRATIONS = [ // usar placeholders resolvidos por lead: {empresa}, {produto}, {cidade}, etc. ` ALTER TABLE message_templates ADD COLUMN template_waba_variaveis_json TEXT NOT NULL DEFAULT '[]'; + `, + // v8 — campanhas de outreach com catalogo dinamico (a Comercial tem 57 + // segmentos e 4 produtos, catalogo fixo em codigo nao cabe nessa escala: + // o operador cria a campanha pela tela). Roda ao lado do runOutreachBatch + // existente (run.mjs) sem substitui-lo. outreach_opt_out e novo aqui (nao + // existia antes): uma fila que recontata o mesmo lead ao longo de semanas + // precisa checar quem ja pediu pra parar. + ` + CREATE TABLE outreach_campaigns ( + id INTEGER PRIMARY KEY, + nome TEXT NOT NULL, + produto TEXT NOT NULL, + segmentos_json TEXT NOT NULL DEFAULT '[]', + status TEXT NOT NULL DEFAULT 'rascunho', + pause_reason TEXT, + limite_diario_atual INTEGER NOT NULL DEFAULT 10, + limite_diario_teto INTEGER NOT NULL DEFAULT 80, + semana_iniciada_em TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE outreach_campaign_variants ( + id INTEGER PRIMARY KEY, + campaign_id INTEGER NOT NULL REFERENCES outreach_campaigns(id), + code TEXT NOT NULL, + mensagem TEXT NOT NULL, + ativo INTEGER NOT NULL DEFAULT 1 + ); + CREATE INDEX idx_campaign_variants_campaign ON outreach_campaign_variants(campaign_id); + + CREATE TABLE outreach_campaign_queue ( + id INTEGER PRIMARY KEY, + campaign_id INTEGER NOT NULL REFERENCES outreach_campaigns(id), + lead_id INTEGER NOT NULL REFERENCES leads(id), + variant_id INTEGER NOT NULL REFERENCES outreach_campaign_variants(id), + segmento TEXT NOT NULL DEFAULT '', + numero TEXT NOT NULL, + rendered_message TEXT NOT NULL, + status TEXT NOT NULL DEFAULT 'preview', + cancel_reason TEXT, + outreach_send_id INTEGER REFERENCES outreach_sends(id), + created_at TEXT NOT NULL DEFAULT (datetime('now')), + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE INDEX idx_campaign_queue_campaign_status ON outreach_campaign_queue(campaign_id, status); + CREATE INDEX idx_campaign_queue_lead ON outreach_campaign_queue(lead_id); + + CREATE TABLE outreach_lead_states ( + lead_id INTEGER PRIMARY KEY REFERENCES leads(id), + stage TEXT NOT NULL, + updated_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + + CREATE TABLE outreach_stage_events ( + id INTEGER PRIMARY KEY, + lead_id INTEGER NOT NULL REFERENCES leads(id), + campaign_id INTEGER REFERENCES outreach_campaigns(id), + stage TEXT NOT NULL, + note TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE INDEX idx_stage_events_lead ON outreach_stage_events(lead_id); + + CREATE TABLE outreach_appointments ( + id INTEGER PRIMARY KEY, + lead_id INTEGER NOT NULL REFERENCES leads(id), + kind TEXT NOT NULL, + scheduled_at TEXT, + note TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); + CREATE INDEX idx_appointments_lead ON outreach_appointments(lead_id); + + CREATE TABLE outreach_opt_out ( + numero TEXT PRIMARY KEY, + motivo TEXT, + created_at TEXT NOT NULL DEFAULT (datetime('now')) + ); ` ]; diff --git a/tests/db.test.mjs b/tests/db.test.mjs index 17b28c1..c29e1cd 100644 --- a/tests/db.test.mjs +++ b/tests/db.test.mjs @@ -126,3 +126,21 @@ test('seeds de segments, municipios e geo_cache', () => { assert.equal(bbox.provider, 'config-legacy'); db.close(); }); + +test('migracao v8 cria as tabelas de campanha de outreach', () => { + const db = openDb(':memory:'); + const tabelas = db.prepare( + `SELECT name FROM sqlite_master WHERE type='table' AND name LIKE 'outreach_%'` + ).all().map((r) => r.name); + for (const nome of [ + 'outreach_campaigns', 'outreach_campaign_variants', 'outreach_campaign_queue', + 'outreach_lead_states', 'outreach_stage_events', 'outreach_appointments', 'outreach_opt_out' + ]) { + assert.ok(tabelas.includes(nome), `tabela ${nome} deveria existir`); + } + db.prepare(`INSERT INTO outreach_campaigns (nome, produto, segmentos_json) VALUES ('Teste', 'ERP', '["oficinas"]')`).run(); + const campanha = db.prepare('SELECT * FROM outreach_campaigns').get(); + assert.equal(campanha.status, 'rascunho'); + assert.equal(campanha.limite_diario_atual, 10); + db.close(); +});