Exclude leads sharing a WhatsApp number from the automatic campaign queue
candidateQuery now blocks a lead whose WhatsApp number is shared by 2+ leads, unless a shared-number override exists. The inner correlated subquery compares the bare other.whatsapp column (not normalizedSql()) against the outer row's two computed number forms so SQLite can use idx_leads_whatsapp instead of a full table scan, keeping the 1.3M-row perf test within budget. Also updates the pre-existing 'persiste erro temporario e nao repete equivalente' test: its two leads use 8/9-digit equivalent numbers, which the new filter now blocks by default, so an override is added to keep exercising the intended equivalent-number dedup behavior.
This commit is contained in:
@@ -63,6 +63,15 @@ function candidateQuery(confirmed) {
|
||||
SELECT 1 FROM outreach_opt_outs opt_out
|
||||
WHERE opt_out.numero IN (${leadNumber}, ${leadEquivalent})
|
||||
)
|
||||
AND (
|
||||
(SELECT COUNT(DISTINCT other.id) FROM leads other
|
||||
WHERE other.whatsapp IN (${leadNumber}, ${leadEquivalent})
|
||||
) < 2
|
||||
OR EXISTS (
|
||||
SELECT 1 FROM outreach_shared_number_overrides ov
|
||||
WHERE ov.numero IN (${leadNumber}, ${leadEquivalent})
|
||||
)
|
||||
)
|
||||
AND NOT EXISTS (
|
||||
SELECT 1 FROM outreach_inbound_replies replies
|
||||
WHERE replies.lead_id = leads.id
|
||||
|
||||
@@ -5,6 +5,7 @@ import os from 'node:os';
|
||||
import path from 'node:path';
|
||||
import {
|
||||
addOptOut,
|
||||
addSharedNumberOverride,
|
||||
openDb,
|
||||
saveWhatsAppVerification
|
||||
} from '../src/db.mjs';
|
||||
@@ -304,6 +305,7 @@ test('persiste erro temporario e nao repete equivalente na mesma preparacao', as
|
||||
const { db, campaign } = setupCampaign();
|
||||
insertLead(db, { key: 'temporary-a', whatsapp: '5575999091780', score: 100 });
|
||||
insertLead(db, { key: 'temporary-b', whatsapp: '557599091780', score: 90 });
|
||||
addSharedNumberOverride(db, '5575999091780', 'numeros equivalentes usados apenas para testar o dedup de verificacao');
|
||||
const calls = [];
|
||||
const whatsapp = noSendWhatsapp(async (numero) => {
|
||||
calls.push(numero);
|
||||
@@ -544,3 +546,50 @@ test('usa somente a lista de segmentos configurada na campanha', async () => {
|
||||
assert.notEqual(configured, catalogButRemoved);
|
||||
db.close();
|
||||
});
|
||||
|
||||
test('numero compartilhado por mais de um lead fica fora da fila ate liberacao manual', async () => {
|
||||
const { db, campaign } = setupCampaign();
|
||||
const sharedA = insertLead(db, { key: 'shared-a', empresa: 'Contabilidade Cliente A', whatsapp: '5575999091780', score: 100 });
|
||||
const sharedB = insertLead(db, { key: 'shared-b', empresa: 'Contabilidade Cliente B', whatsapp: '5575999091780', score: 90 });
|
||||
const solo = insertLead(db, { key: 'solo', whatsapp: '5575992220000', score: 80 });
|
||||
for (const numero of ['5575999091780', '5575992220000']) confirmed(db, numero);
|
||||
|
||||
await prepareDailyQueue({
|
||||
db,
|
||||
whatsapp: noSendWhatsapp(async () => { throw new Error('todos os numeros possuem cache'); }),
|
||||
campaignId: campaign.id,
|
||||
now: new Date('2026-07-29T14:00:00-03:00')
|
||||
});
|
||||
let queue = listCampaignQueue(db, campaign.id);
|
||||
assert.deepEqual(queue.map(({ lead_id }) => lead_id), [solo]);
|
||||
assert.equal(queue.some(({ lead_id }) => [sharedA, sharedB].includes(lead_id)), false);
|
||||
|
||||
addSharedNumberOverride(db, '5575999091780', 'confirmado contato direto');
|
||||
await prepareDailyQueue({
|
||||
db,
|
||||
whatsapp: noSendWhatsapp(async () => { throw new Error('todos os numeros possuem cache'); }),
|
||||
campaignId: campaign.id,
|
||||
now: new Date('2026-07-29T14:00:00-03:00')
|
||||
});
|
||||
queue = listCampaignQueue(db, campaign.id);
|
||||
assert.deepEqual(queue.map(({ lead_id }) => lead_id).sort((a, b) => a - b), [sharedA, sharedB, solo].sort((a, b) => a - b));
|
||||
db.close();
|
||||
});
|
||||
|
||||
test('variantes 8 e 9 digitos do mesmo numero contam como compartilhado na fila', async () => {
|
||||
const { db, campaign } = setupCampaign();
|
||||
const nineDigits = insertLead(db, { key: 'nine-digits', whatsapp: '5575999091780', score: 100 });
|
||||
const eightDigits = insertLead(db, { key: 'eight-digits', whatsapp: '557599091780', score: 90 });
|
||||
confirmed(db, '5575999091780');
|
||||
confirmed(db, '557599091780');
|
||||
|
||||
await prepareDailyQueue({
|
||||
db,
|
||||
whatsapp: noSendWhatsapp(async () => { throw new Error('todos os numeros possuem cache'); }),
|
||||
campaignId: campaign.id,
|
||||
now: new Date('2026-07-29T14:00:00-03:00')
|
||||
});
|
||||
const queue = listCampaignQueue(db, campaign.id);
|
||||
assert.equal(queue.some(({ lead_id }) => [nineDigits, eightDigits].includes(lead_id)), false);
|
||||
db.close();
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user