Add campaign outreach schema (migration v8)

This commit is contained in:
Gustavo
2026-08-01 10:14:54 -03:00
parent 4f736562f3
commit a7abd3d4d0
2 changed files with 97 additions and 0 deletions
+79
View File
@@ -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'))
);
`
];
+18
View File
@@ -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();
});