ACCEPTANCE CRITERIA (46/46)
☑ AC1.1: Script em `apps/crm/scripts/kommo-sync.js` executavel via `node apps/crm/scripts/kommo-sync.js`
☑ AC1.2: Le token e URL do Kommo de variaveis de ambiente (`KOMMO_ACCESS_TOKEN`, `KOMMO_BASE_URL`)
☑ AC1.3: Le connection string do banco de variaveis de ambiente
☑ AC1.4: Aceita flags CLI: `--dry-run`, `--verbose`, `--contacts-only`, `--leads-only`, `--force-full`
☑ AC1.5: Mostra help com `--help`
☑ AC1.6: Verifica conexao com API Kommo e banco antes de iniciar sync
☑ AC2.1: Le `last_sync_at` de `crm_sync_state` (sync_type = 'kommo_contacts')
☑ AC2.2: Puxa contatos de `GET /api/v4/contacts?filter[updated_at][from]={unix_ts}&limit=250&with=leads,companies` com paginacao
☑ AC2.3: Respeita rate limit de 7 req/s (minimo 200ms entre requests)
☑ AC2.4: Retry automatico com backoff exponencial (3 tentativas) em caso de erro 429/5xx
☑ AC2.5: Para contatos novos (phone_number nao existe): INSERT no banco
☑ AC2.6: Para contatos existentes: UPDATE campos vazios, NAO sobrescrever preenchidos
☑ AC2.7: Respeita flag `manually_edited` no metadata — se true, NAO atualizar campos editados manualmente
☑ AC2.8: Atualiza `last_sync_at` em `crm_sync_state` ao concluir com sucesso
☑ AC3.1: Le `last_sync_at` de `crm_sync_state` (sync_type = 'kommo_leads')
☑ AC3.2: Puxa leads de `GET /api/v4/leads?filter[updated_at][from]={unix_ts}&limit=250&with=contacts` com paginacao
☑ AC3.3: Para leads novos: INSERT em `deals` + entry em `deal_stage_log`
☑ AC3.4: Para leads existentes (match por `metadata->>'kommo_lead_id'`): UPDATE current_stage, estimated_value, closed_at, close_reason, updated_at
☑ AC3.5: Respeita flag `manually_edited` no metadata do deal — se true, NAO atualizar
☑ AC3.6: Linka novos deals ao customer_id correto via cache de contatos (mesmo padrao da Story 9.5)
☑ AC3.7: Leads sem contato associado: registrar no log de erros, NAO importar
☑ AC3.8: Quando stage muda (deal existente com stage diferente): criar nova entry em `deal_stage_log`
☑ AC3.9: Atualiza `last_sync_at` em `crm_sync_state` ao concluir com sucesso
☑ AC4.1: Mesmo mapeamento de contatos da Story 9.4 (nome, telefone, email, empresa, CNPJ, CPF, tipo)
☑ AC4.2: Mesmo mapeamento de leads da Story 9.5 (stages, pipeline, valor, dates)
☑ AC4.3: Reutiliza funcoes de normalizacao (telefone, CNPJ, CPF) dos scripts existentes
☑ AC5.1: Com `--dry-run`, script NAO escreve no banco
☑ AC5.2: Dry-run mostra: contatos novos/atualizados, leads novos/atualizados, erros
☑ AC5.3: Dry-run gera relatorio em `apps/crm/data/kommo-sync-dryrun-{timestamp}.md`
☑ AC6.1: Com `--force-full`, ignora `last_sync_at` e puxa todos os registros (como import inicial)
☑ AC6.2: Full sync segue mesma logica de dedup (nao cria duplicatas)
☑ AC6.3: Exibe aviso ao usuario: "Full sync pode demorar. Use --dry-run primeiro."
☑ AC7.1: Script pode ser executado via cron sem interacao humana
☑ AC7.2: Crontab sugerido: `0 6 * * * cd /root/propek-whatsapp-bot && NODE_PATH=./node_modules node apps/crm/scripts/kommo-sync.js >> /var/log/kommo-sync.log 2>&1`
☑ AC7.3: Script retorna exit code 0 em sucesso, 1 em erro (para monitoramento cron)
☑ AC7.4: Log file compativel com rotacao (logrotate)
☑ AC8.1: Log estruturado em stdout: `[TIMESTAMP] [LEVEL] mensagem`
☑ AC8.2: Erros incluem contexto: kommo_id, campo, motivo
☑ AC8.3: Relatorio final em `apps/crm/data/kommo-sync-report-{timestamp}.md`
☑ AC8.4: Relatorio inclui: periodo do sync, novos contatos, contatos atualizados, novos deals, deals atualizados, erros, tempo total
☑ AC9.1: Usa parameterized queries ($1, $2) — NUNCA interpolacao SQL
☑ AC9.2: Usa `crm_user` para conexao ao banco
☑ AC9.3: Bot WhatsApp continua operando durante sync
☑ AC9.4: Zero dependencias externas alem de Node.js stdlib + pg
☑ AC9.5: Token do Kommo NUNCA logado ou exibido
☑ AC9.6: CHECK constraints da tabela deals respeitados