← story.propek
STORY 8.9
Integracao Bot → CRM (Triggers & Crons)
Epic: CRM Propek v2
PENDING
Implementar os 4 triggers de eventos do bot que criam/movem deals automaticamente no CRM, e os 2 cron jobs que mantêm follow-ups atualizados, fazendo o pipeline se auto-popular sem intervencao manual do atendente.
ACCEPTANCE CRITERIA (0/27)
AC1: Funcao `onLeadScored()` implementada e chamada apos INSERT em `lead_scores` quando classification IN ('A', 'B', 'hot') E customer.customer_type contém 'b2b' (case-insensitive)
AC2: `onLeadScored()` verifica se ja existe deal aberto para o customer antes de criar novo (idempotencia — nao cria deal duplicado)
AC3: Se nao existe deal aberto: INSERT em `deals` (pipeline_type='b2b', current_stage='lead_qualificado', source='bot') + INSERT em `deal_stage_log` (from_stage=NULL, to_stage='lead_qualificado', changed_by='bot')
AC4: Cria `follow_up_crm` tipo 'lead_contact' com due_at = NOW() + 2 horas para o novo deal
AC5: Round-robin: atribui `assigned_to` ao atendente com MENOS deals abertos no momento (query COUNT por assigned_to WHERE closed_at IS NULL)
AC6: Funcao `onOrderCreated()` implementada e chamada apos INSERT em `orders`
AC7: Verifica se ja existe deal para o order_id antes de criar (idempotencia via `WHERE order_id = $1`)
AC8: Detecta se cliente e B2B ou B2C via `customers.customer_type`
AC9: Cria deal com pipeline_type e stage corretos: B2B → stage='confirmado', B2C → stage='pedido'; `estimated_value` = `order.total_amount`
AC10: INSERT em `deal_stage_log` (from_stage=NULL, to_stage adequado, changed_by='bot')
AC11: Funcao `onShipmentCreated()` implementada e chamada quando shipment recebe tracking_code
AC12: Busca deal vinculado via `WHERE order_id = $1 AND closed_at IS NULL`
AC13: Nao move se deal ja esta em stage >= 'enviado' (guard clause: nao regride stages)
AC14: UPDATE deals SET current_stage='enviado' + INSERT deal_stage_log (changed_by='webhook')
AC15: Funcao `onShipmentDelivered()` implementada e chamada quando shipment.status = 'delivered'
AC16: UPDATE deals SET current_stage='entregue' + INSERT deal_stage_log (changed_by='webhook')
AC17: Cria follow_up_crm tipo 'nps' com due_at = NOW() + 48 horas, assigned_to = deal.assigned_to
AC18: Cria 3 follow-ups da sequencia Ezra: 'ezra_d3' (NOW()+3d), 'ezra_d7' (NOW()+7d), 'ezra_d14' (NOW()+14d), todos com assigned_to = deal.assigned_to
AC19: Cron registrado no `cron.handler.ts` do bot para executar a cada 15 minutos
AC20: UPDATE follow_ups_crm SET status='overdue', updated_at=NOW() WHERE status='pending' AND due_at < NOW()
AC21: Loga a quantidade de follow-ups atualizados para overdue com `logger.info`
AC22: Cron registrado para executar diariamente as 08:00 (usando sintaxe cron '0 8 \* \* \*')
AC23: Query detecta clientes B2B que: (a) fizeram pelo menos um pedido, (b) nao fizeram pedido nos ultimos 60 dias, (c) nao tem follow_up_crm tipo 'recompra' com status 'pending' ou 'overdue' ja existente (idempotencia)
AC24: Para cada cliente at-risk: INSERT follow_ups_crm (follow_up_type='recompra', due_at = NOW()+3 dias) — sem deal_id (follow-up de cliente, nao de deal)
AC25: Loga quantidade de clientes at-risk detectados com `logger.info`
AC26: Todos os triggers sao fire-and-forget — erros nao devem propagar para o fluxo principal do bot (try/catch com log do erro, nunca throw)
AC27: Logger do bot usado em todos os eventos para rastreabilidade (formato: `logger.info({ dealId, customerId }, 'CRM: ...')`)