snglpi/docs/fluxo.md
Rafael Lopes bce9a52494 FEAT: Motor de regras único de status/fechamento + mandante configurável
Consolida os dois controladores legados que decidiam status/fechamento de
forma concorrente (processGlpiClosureController + processStatusController)
em um único processTicketLifecycleController, eliminando a dependência do
campo source_last (mantido só como rastro de auditoria) e a flag
ENABLE_STATUS_SYNC (removida). Isso corrige o bug de reabertura: chamados
reabertos no SN voltavam a ser fechados no GLPI porque a regra de
precedência de finalização do fluxo legado forçava o status do GLPI de
volta pro SN.

Correções de bugs encontrados em teste (Frente 1):
- Constraint UNIQUE em ticket_sync.sn_ticket_id + ON CONFLICT DO NOTHING
  na criação do registro, prevenindo duplicação de tickets.
- Fechamento no SN agora é confirmado por leitura ao vivo do status antes
  de marcar o ticket como solved/closed, em vez de confiar só no HTTP 200
  (a API do SN pode retornar sucesso e silenciosamente ignorar um valor de
  estado inválido — descoberto e corrigido também no mandante).
- Nota de encerramento do SN pro GLPI agora é idempotente e criada uma
  única vez por ticket.
- Erros de sincronização passam a ter um limite de tentativas
  (MAX_ERROR_RETRIES) antes de marcar o ticket como error_permanent,
  exigindo intervenção manual em vez de tentar para sempre.
- Chamados criados no GLPI passam a gravar o número do ticket do SN no
  campo externalid.

Nova funcionalidade (Frente 2): quando o SN resolve/encerra um chamado por
conta própria antes do GLPI, além da nota informativa no GLPI (agora com o
nome de quem encerrou, buscado ao vivo via resolved_by/closed_by/
sys_updated_by), o próprio SN recebe um aviso de que a Sothis não vai mais
atender aquele chamado e ele é transferido pra fila de TI da CAOA.

Nova funcionalidade (Mandante, regras 19-22): espelhamento opcional e
configurável de status intermediário (Aguardando Atendimento/Em
Atendimento/Em Espera) entre os dois sistemas, controlado pela env
MANDANTE=GLPI|SNOW. Sem essa env configurada, mantém o comportamento atual
(nenhum espelhamento de status intermediário). Nunca afeta os status finais
(resolvido/encerrado/fora de escopo), que continuam regidos pelas regras
fixas de fechamento.

Corrigido também o workflow de deploy (.gitea/workflows/deploy-production.yml),
que ainda validava os controladores antigos (já removidos) e exigia
ENABLE_STATUS_SYNC=true, o que quebraria o próximo deploy em produção.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 09:45:34 -03:00

4.1 KiB

Fluxo Operacional

1. Fluxo do ciclo (main)

  1. processErrorController
  2. processTicketsController
  3. processTicketLifecycleController (se ENABLE_GLPI_CLOSE_CRON=true) — motor de regras unico pra status/fechamento/reabertura, ver secao 4.
  4. processCommentsController

2. Fluxo SN -> GLPI

  1. Coleta incidentes/requisicoes no SN por watermark.
  2. UPSERT em tickets_sn.
  3. Cria/atualiza ticket_sync com estados iniciais.
  4. Cria ticket no GLPI quando pending_check.
  5. Grava glpi_ticket_id em ticket_sync.
  6. Atualiza campo "Ticket Externo" no SN com ID GLPI.
  7. Se falhar atualizar "Ticket Externo", agenda retry em ticket_updates.

3. Fluxo de comentarios/tasks

3.1 SN -> GLPI

  1. Busca comments e work_notes no SN.
  2. Persiste em ticket_updates (idempotente por source_id).
  3. Envia:
    • comment -> followup GLPI
    • task -> task GLPI

3.2 GLPI -> SN

  1. Busca followups GLPI.
  2. Sanitiza conteudo HTML/imagens.
  3. Envia comentario para SN evitando duplicidade.
  4. Atualiza destiny_id para rastrear espelhamento.

4. Motor de regras unico (processTicketLifecycleController)

Substitui os antigos processGlpiClosureController + processStatusController. Nao ha mais dois controladores decidindo o mesmo ticket, e nenhuma decisao depende de source_last (campo mantido so como rastro de auditoria). Roda pra todo ticket com glpi_ticket_id que nao esteja closed/ignored/error_permanent:

  1. GLPI status 5 (Solucionado):
    • se o ticket ja estava solved localmente, so verifica se o SN foi reaberto (ver secao 5) e para por ai;
    • senao: verifica fora de escopo; se nao for, busca nota de solucao, se SN estiver em espera/aguardando move pra "Em Atendimento", resolve o SN confirmando via leitura ao vivo antes de marcar solved/solved (nunca marca so por causa de um HTTP 200).
  2. GLPI status 6 (Fechado definitivamente):
    • adiciona comentario idempotente no SN: "Chamado encerrado definitivamente. Reabertura deste chamado nao sera atendida. Caso necessario, abra um novo chamado."
    • nao altera status do chamado no SN
    • marca ticket_sync como closed/closed para retirar do monitoramento
  3. GLPI ainda aberto (< 5) e SN resolvido/encerrado por conta propria:
    • adiciona nota idempotente no GLPI avisando o encerramento vindo do SN, sem fechar o chamado (inclui o nome de quem encerrou no SN, buscado ao vivo: resolved_by -> closed_by -> sys_updated_by, o que vier preenchido primeiro)
    • (primeira vez apenas) avisa no proprio SN que a Sothis nao vai mais atender esse chamado e transfere pra fila CAOA
    • marca ticket_sync como solved/solved ou closed/closed
  4. GLPI ainda aberto (< 5) e SN tambem em status intermediario (nenhum dos dois em Resolvido/Encerrado): aplica o mandante, se configurado — ver secao 6.

5. Reabertura

  1. Reabertura automatica permitida apenas para tickets em GLPI 5 — verificado a cada ciclo pelo mesmo processTicketLifecycleController, sem depender de nenhuma flag.
  2. Se GLPI 6, reabertura automatica bloqueada (a regra nem entra nesse caminho).

6. Mandante (regras 19-22)

Espelhamento opcional de status intermediario (Aguardando Atendimento/Em Atendimento/ Em Espera no SN, equivalentes ao GLPI 1/2/4 — GLPI 3 tambem mapeia para Em Atendimento). Nunca toca em status finais (5, 6, fora de escopo) — essas continuam sendo as regras fixas das secoes 4.1-4.3, sempre ativas.

Controlado pelo env MANDANTE:

  1. MANDANTE=GLPI: quando o status intermediario do GLPI muda, o SN e atualizado pra acompanhar. Mudancas de status intermediario feitas diretamente no SN sao ignoradas.
  2. MANDANTE=SNOW: o oposto — o SN manda, o GLPI acompanha.
  3. MANDANTE vazio/ausente/invalido: nenhum espelhamento (comportamento padrao, igual a antes do mandante existir).

Em qualquer configuracao, so escreve quando o valor desejado difere do atual (evita chamada de API desnecessaria) e nunca le o status do lado seguidor pra decidir nada — o mandante so manda, nunca escuta, entao nao ha risco de loop entre os dois sistemas.