O quê/como/por quê da integração + regras do Redmine de produção (custom fields obrigatórios, atividade por projeto, identifier lowercase, comentário obrigatório, projeto ativo) e gotchas de manutenção. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
5.7 KiB
| id | title | domain | tags | status | severity | created_at | updated_at | applies_to | related_records | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| KB-PLUGIN-037 | Plugin redmine — Contrato de API GLPI → Redmine (o quê, como e por quê) | plugin-dev |
|
active | high | 2026-07-02 | 2026-07-02 |
|
|
Plugin redmine — Contrato de API GLPI → Redmine
Registro de desenvolvedor do plugin redmine (marketplace Mindplace). Resume o quê
sai do GLPI, como entra no Redmine e por quê cada decisão existe. A referência
completa (payloads, exemplos HTTP) vive no repo do plugin: docs/API.md
(servicedesk.mindtek.com.br/git/rodolpho.lopes/redmine, espelho dev em
onmind.mindtek.com.br/git/rodolpho.lopes/Redmine).
O quê (modelo)
| GLPI | Redmine | Quando |
|---|---|---|
| Projeto ou Entidade (vínculo manual na aba Redmine) | Projeto | vínculo 1:1 salvo em glpi_plugin_redmine_projects / _entities |
| Chamado | 1 issue | criada no 1º lançamento de tempo (lazy) |
| TicketTask Feito com duração > 0 | 1 time entry na issue | ao salvar a task (1× por task) |
| Status do chamado | status da issue | mapeado (status_map do formconfig) |
| Técnico atribuído / autor da task | autor da issue / do time entry | via impersonation |
Por quê: o chamado nasce e morre no GLPI; para o Redmine importam as horas e o
andamento. Tarefas não concluídas ficam só no GLPI (gate no state = Feito).
Como (mecanismos-chave)
- Auth: header
X-Redmine-API-Keycom chave de admin (cifrada no banco via GLPIKey). - Autoria — impersonation: header
X-Redmine-Switch-User: <login>. Só funciona se o usuário for membro do projeto com papel que permita a ação → o plugin faz auto-membership (POST /projects/:id/memberships.json, papel =default_role_id). Falhou (403/412)? Refaz como admin e loga — nunca perde o lançamento. - Mapeamento de usuários: cache
glpi_plugin_redmine_users(sync) + auto-match por e-mail + overrides manuais (glpi_plugin_redmine_usermap). - Idempotência:
glpi_plugin_redmine_tickets(1 issue por chamado) eglpi_plugin_redmine_tasks(tempo 1× por task). Se a issue vinculada foi apagada no Redmine, o plugin detecta (GET 404) e recria removendo o vínculo órfão (1.5.5+). - Tempo: GLPI grava segundos → Redmine quer horas decimais →
actiontime/3600, 2 casas (0h05 → 0.08 h; o Redmine exibe "0:05"). - Sincronizar Metadados: cacheia projects, trackers, statuses, priorities,
activities, roles, users e project_custom_fields em
glpi_plugin_redmine_configs.metadata_cache. Os formulários leem SÓ do cache — após mudanças no Redmine (novos campos, projetos), é preciso re-sincronizar.
Por quê — regras do Redmine de PRODUÇÃO que moldaram o contrato
Aprendidas em produção (2026-06-30 a 07-02); o dev era permissivo, o prod não:
- Custom fields de projeto OBRIGATÓRIOS: "Tipo de Projeto" (cf id 4) e
"Tipo de Faturamento" (cf id 5) não podem ficar vazios na criação de projeto →
POST /projects.jsonlevacustom_field_values: {"4": "...", "5": "..."}. O form das abas Redmine renderiza esses campos dinamicamente a partir demetadata_cache.project_custom_fields(dropdown p/field_format=list) — se o Redmine ganhar novos campos obrigatórios, basta re-sincronizar metadados. - Identificador: só minúsculas/números/traços →
createProjectFull()normaliza ("GNA" → "gna"); senão 422 "Identificador não é válido". - Atividade do time entry é POR PROJETO: a
default_activity_iddo formconfig precisa estar habilitada no projeto de destino, senão 422 "não está incluso na lista". - Comentário do time entry é obrigatório: task sem descrição → fallback "Tempo lançado via GLPI (chamado #N)" (1.5.4+).
- Projeto de destino precisa estar ATIVO (status 1): fechado/arquivado → 422/403. O dropdown de vínculo filtra só ativos (1.5.3+).
- Tracker é por projeto: o
default_tracker_idglobal precisa existir no projeto; vazio = o Redmine usa o default do projeto (mais seguro em multi-cliente).
Endpoints usados (resumo)
Leitura (sync): /projects.json, /trackers.json, /issue_statuses.json,
/enumerations/issue_priorities.json, /enumerations/time_entry_activities.json,
/roles.json, /users.json (paginado), /projects/:id/issue_categories.json,
/custom_fields.json (filtra customized_type=project).
Escrita: POST /projects.json (criar projeto c/ custom fields), POST /issues.json
(issue do chamado), PUT /issues/:id.json (status), POST /time_entries.json (tempo),
POST /projects/:id/memberships.json (auto-membership).
Gotchas de manutenção
- Dev = GLPI 11.0.7, prod = 11.0.4: desenvolver mirando 11.0.0+ (sem
fields.csrfField(), semToolbox::logError()). Ver [KB-PLUGIN-036]. - Ambiente em
production: mudou.twig→cache:clearobrigatório (o cache do GLPI não é invalidado por atualização de plugin). - Twig
mergerenumera chaves inteiras: nunca montar opções{id: nome}no Twig — montar no PHP (bug real: vínculo gravava o índice em vez do ID do projeto, 1.5.3). - Release ZIP: wrapper
redmine/como primeira entrada (entrada de diretório), senão o instalador do Mindplace extrai aninhado.
Classificação
- Tipo: contrato de integração + lições de produção.
- Reutilização: obrigatória para quem for manter/evoluir o plugin redmine; útil para qualquer integração GLPI → API externa.