Módulo SLA — visão geral
O módulo de SLA (Service Level Agreement) define o prazo de laudo que cada exame precisa cumprir.
Ele guarda a política de prazo por prioridade e modalidade e o cap de protocolo clínico
(AVC e Trauma), que aperta esse prazo e pode disparar um achado crítico. Ele vive em
package/diagnosis/sla-policy (core/http/persistence), dentro do domínio Diagnosis do backend
(mm-pacs-portal-main-api).
Este módulo não calcula sozinho o prazo de um exame específico: ele só guarda e expõe a
política. O cálculo do prazo (sla_expiration_date de um exame) é acionado pelo domínio de Exames
(criação, edição e reprocessamento de exame) e pelo domínio de Laudo/achado crítico, que chamam os
serviços internos deste módulo — ver Motor de cálculo (uso interno)
abaixo. A confirmar — responsável: time de Diagnosis/Exames; data: 24/09/2026. A documentação do
lado de Exames que aciona esse cálculo ainda não existe neste repositório de docs.
Reescrita do legado
Este módulo é a reescrita das features de legado F-001 (configurar SLA por prioridade e
modalidade), F-002 (calcular o prazo de SLA do exame) e F-003 (cap de SLA por protocolo
clínico AVC/Trauma), antes implementadas em mm-pacs-portal-api/routes/empresa.js sobre as
tabelas tb_sla e tb_empresa_sla_protocolo. Ao comparar com a especificação de legado (funil
Bitrix, cards F-001/F-002/F-003), confirme estas mudanças de contrato no código atual:
- Hierarquia tenant → unidade. O legado guardava um único SLA "por empresa" (
tb_slavinculada aempresa_id). O código atual tem dois escopos: tenant (DiagnosisSlaScopeType.TENANT) e unidade (DiagnosisSlaScopeType.UNIT), com a unidade herdando do tenant e o tenant herdando de um catálogo de padrões do sistema. Não existe mais um único nível "empresa". - Substituição integral virou versionamento otimista. O legado sempre fazia
destroy + bulkCreate(BR-SLA-017) e descartava os ids das linhas (BR-SLA-018). O código atual versiona cada política (version, incrementada a cadaPUT) e exigeexpectedVersion(e, para unidade, tambémexpectedTenantVersion) no corpo/query de toda escrita — conflito vira409, não uma sobrescrita silenciosa. - Protocolo: os nomes mudaram de
avc/trauma(legado) para o enumDiagnosisSlaProtocol(STROKE,TRAUMA), mas o par continua fixo — sempre os dois, nunca mais nem menos (DiagnosisSlaProtocolPolicyInvalidError, 422). - Limite de minutos do protocolo mudou. A especificação de legado (BR-SLA-057) limitava o prazo
de protocolo habilitado a no máximo 59 minutos. O código atual
(
DiagnosisSlaProtocolRule.assertDuration) usa o mesmo limite das regras normais de SLA — 1 a 59.999 minutos — sem o teto de 59.A confirmar — responsável: time de Diagnosis; data: 24/09/2026.: não foi possível confirmar se essa mudança foi intencional (o cap de protocolo existe para ser mais curto que o SLA base, mas o código não impõe isso estruturalmente; quem cadastra o valor precisa saber disso). - "Nunca afrouxa" (BR-SLA-037) continua valendo, mas hoje é aplicado pelo serviço interno
ApplyDiagnosisSlaProtocolService(ver abaixo), não por uma rota HTTP deste módulo. - Os prazos-padrão por prioridade (BR-SLA-013) são os mesmos no código atual — ver tabela na página Política de SLA por prioridade e modalidade.
A pesquisa no card de contexto do Bitrix não trouxe o campo de correlação "status vs código real"
preenchido (ufCrm110_1782750927 vazio nos três cards F-001/F-002/F-003) — trate as regras de
legado (BR-SLA-*) só como pista de intenção de produto, não como contrato atual; o contrato atual é
o descrito nas páginas deste módulo, confirmado no código.
Arquitetura
Escopos e herança
Toda política deste módulo é resolvida em cascata, do mais específico para o mais genérico:
- Unidade, prioridade + modalidade (override exato da unidade).
- Unidade, só prioridade (
modality: nullna unidade). - Tenant, prioridade + modalidade (override exato do tenant).
- Tenant, só prioridade (
modality: nullno tenant). - Padrão do sistema para a prioridade (constante no código, ver tabela na página de política de prioridade/modalidade).
O mesmo padrão vale para o cap de protocolo, mas sem o eixo de modalidade: unidade (se tiver
override) → tenant (se version > 0) → padrão do sistema (as duas regras desabilitadas).
O campo source de cada resposta (SYSTEM | TENANT | UNIT) diz de onde veio o valor efetivo.
Versionamento otimista
Nenhuma escrita substitui a política sem confirmar a versão que o cliente leu por último:
- Escrita em tenant: exige
expectedVersionigual à versão atual do tenant. Diferente →409 SLA_POLICY_VERSION_CONFLICT. - Escrita em unidade: exige
expectedVersionda própria unidade eexpectedTenantVersionigual à versão atual do tenant (mesmo que a unidade não tenha override próprio, a versão do tenant herdado precisa bater). Tenant divergente →409 SLA_TENANT_VERSION_CONFLICT; unidade divergente →409 SLA_POLICY_VERSION_CONFLICT. - Toda escrita bem-sucedida incrementa a versão em 1.
Permissões
Todas as rotas exigem um Bearer token válido (JwtAuthenticationGuard) e a permissão do domínio
SLA no escopo do path (AuthorizationGuard + @Authorize):
| Permissão | Uso |
|---|---|
sla:read | Ler a política efetiva ou o protocolo (tenant ou unidade) |
sla:write | Substituir a política ou o protocolo (tenant ou unidade) |
sla:delete | Restaurar (remover) um override — de uma regra ou do protocolo — de volta ao valor herdado |
O escopo da permissão é o tenantId ou unitId do próprio path da rota — sem token válido → 401;
sem a permissão nesse escopo → 403.
Transação e isolamento por tenant (RLS)
Toda rota deste controller passa pelo DiagnosisAuthorizationTransactionInterceptor, que abre a
escrita/leitura dentro de uma transação com Row-Level Security aplicada para o domínio diagnosis
antes de executar o handler. Se o contexto de RLS não puder ser montado, a chamada falha com
403 RLS_TRANSACTION_UNAVAILABLE antes mesmo de chegar à lógica de SLA.
Auditoria
Toda escrita (replace ou restore, em qualquer escopo) grava um evento de auditoria com ator, IP, snapshot antes/depois e a versão resultante — nunca dados clínicos do paciente:
| Evento | Quando |
|---|---|
diagnosis.sla-policy.tenant-replaced | PUT /tenants/:tenantId/sla-policy |
diagnosis.sla-policy.unit-replaced | PUT /units/:unitId/sla-policy |
diagnosis.sla-policy.unit-rule-restored | DELETE /units/:unitId/sla-policy/rules/:priority |
diagnosis.sla-policy.tenant-protocol-replaced | PUT /tenants/:tenantId/sla-protocol-policy |
diagnosis.sla-policy.unit-protocol-replaced | PUT /units/:unitId/sla-protocol-policy |
diagnosis.sla-policy.unit-protocol-restored | DELETE /units/:unitId/sla-protocol-policy |
Motor de cálculo (uso interno)
ResolveDiagnosisSlaDeadlineService, CalculateDiagnosisSlaDeadlineService e
ApplyDiagnosisSlaProtocolService não têm rota HTTP própria neste módulo — são serviços
injetados diretamente pelos serviços de Exame, Prontuário clínico e Laudo (confirmado em
change-diagnosis-exam-attributes.service.ts, create-diagnosis-exam.service.ts,
duplicate-diagnosis-exam.service.ts, change-diagnosis-exam-unit.service.ts,
process-diagnosis-exam-ingestion.service.ts, delete-diagnosis-exam.service.ts,
maintain-diagnosis-exam-clinical-markings.service.ts e maintain-diagnosis-report.service.ts).
Resumo do comportamento confirmado no código:
- O cálculo do prazo base soma
slaMinutesda regra efetiva só dentro das janelas ativas (ALL_DAYconta o dia inteiro;WINDOWsó entrestartTimeeendTime;INACTIVEnão conta), avançando semana a semana no fuso configurado emSLA_TIMEZONE(defaultAmerica/Sao_Paulo) a partir de um instante de referência (referenceAt). - O cap de protocolo pega o menor prazo entre os protocolos habilitados e com marcação presente no
exame, e só reduz o prazo atual — nunca o aumenta (
minimumDeadlinesempre escolhe o menor dos dois quando ambos existem). - Se uma regra de protocolo com
startsCriticalFindinghabilitado se aplica e o exame ainda não está marcado como achado crítico, o exame passa a ser marcado — e a operação exige umactorUserIdnão vazio, senão lança erro interno (UnexpectedError).
Este comportamento é o contrato observável do motor de cálculo, mas não é uma rota — não tem
página de referência HTTP própria. A confirmar — responsável: time de Diagnosis/Exames; data: 24/09/2026.: como e quando exatamente o domínio de Exames aciona esse cálculo (criação, edição,
reprocessamento) ainda não está documentado neste repositório.
Páginas deste módulo
| Página | Cobre |
|---|---|
| Política de SLA por prioridade e modalidade | GET/PUT /tenants/:tenantId/sla-policy, GET/PUT /units/:unitId/sla-policy, DELETE /units/:unitId/sla-policy/rules/:priority |
| Cap de SLA por protocolo clínico (AVC/Trauma) | GET/PUT /tenants/:tenantId/sla-protocol-policy, GET/PUT /units/:unitId/sla-protocol-policy, DELETE /units/:unitId/sla-protocol-policy |
:::tip OpenAPI
A documentação interativa (schemas + "Try it out") está disponível em /docs no ambiente onde a
API está rodando (local: http://localhost:3000/docs, tag Diagnosis — SLA policy).
:::