# Central do Médico (Cockpit) — Mapeamento de Banco de Dados (v6.0)

**Documentos desta funcionalidade:** [01 - Definição](./01%20-%20Defini%C3%A7%C3%A3o%20v6.0.md) (v6.0) · [02 - Especificação](./02%20-%20Especifica%C3%A7%C3%A3o%20v6.0.md) (v6.0) · [03 - Protótipo](./03%20-%20Prot%C3%B3tipo%20v6.0.md) (v6.0, código executável em `03 - Protótipo v6.0.html`) · [04 - Mapeamento de Banco de Dados](./04%20-%20Mapeamento%20de%20Banco%20de%20Dados%20v6.0.md) (v6.0) · [05 - Registro de Decisões](./05%20-%20Registro%20de%20Decis%C3%B5es%20v1.20.md) (v1.20)

---

## SEÇÃO 4 — MAPEAMENTO DE BANCO DE DADOS

**Exclusão lógica:** um registro excluído tem `RemovidoEm` preenchido; as consultas deste documento ignoram esses registros nas tabelas que têm o campo (em GescomClienteAlfa, segundo a Biblioteca de Schema (DDL): `Agenda`, `Paciente`, `PEP`, `PEPDiagnostico`, `ProgramaApoio` e `ProgramaApoioPaciente`). O campo `Status` com `A`/`I` do legado está sendo descontinuado e não é usado como critério de "ativo" (Log de Mudanças Estruturais do Banco de Dados, 07/10/2026). Tabelas sem `RemovidoEm` (ex.: `Prescricao`, `PacienteFoto`, `PEPProgramaApoioPaciente`) não recebem esse filtro.

**Ligação entre a clínica do login e a base do cliente (D-CM-065).** O login é autorizado para um `Cliente`; as clínicas dele são linhas de `ClienteEmpresa` (IpSeguranca) e os dados delas ficam no banco do cliente. A cadeia, de ponta a ponta: `ClienteEmpresa.Id` (guid; IpSeguranca) = `SegEmpresa.SegurancaClienteEmpresaId` (banco do cliente) → `SegEmpresa.IdEmpresa` → `Clinica.IdEmpresa` (chave estrangeira real) → `Clinica.IdChClinica`. É esse `IdChClinica` que aparece em `Agenda.IdChClinica` e em `IpParametroChave.IdChave`. Por isso `{IdClinica}` nas consultas deste documento é o `Clinica.IdChClinica` da clínica em que o usuário está logado, obtido por essa cadeia a partir do `ClienteEmpresa.Id` da sessão; o fuso (`FusoHorarioOficial`) e o idioma saem da mesma linha de `ClienteEmpresa`. O nome do campo é o da Biblioteca de Schema (DDL), `SegurancaClienteEmpresaId` (D-CM-071). A coluna `Clinica.FusoHorario`, que a DDL ainda traz, está descontinuada e nenhuma consulta deste documento a lê (D-CM-072; Log de Mudanças Estruturais, 26/10/2026). Padrões dos parâmetros por clínica: `NumeroDiasRevisaoPrescricoes` = 1 e `PrescricaoTempoAssinatura` = 7 (D-CM-006, D-CM-037).

**Clínicas em que o médico atende (D-CM-066, D-CM-067).** O médico vê só as clínicas do cliente em que atende, e atender, para o sistema, é ter acesso à clínica (D-CM-067): são as clínicas do cliente da sessão a que o usuário tem acesso, o mesmo critério que o Mapeamento da Agenda do Médico já usa em `ClinicVisible()` (RN-CIU-009), com a ligação de D-CM-065 para chegar ao `IdChClinica`. A aplicação monta a tabela `{ClinicasDoMedico}` (colunas `IdEmpresa` e `IdChClinica`) uma vez por sessão (pode ficar em cache) e a usa nos filtros de 4.1 a 4.8:

```sql
-- 1) IpSeguranca: clínicas (ClienteEmpresa) do cliente da sessão a que o usuário tem acesso
SELECT DISTINCT cpau.ClienteEmpresaId
FROM IpSeguranca.dbo.ClientePerfilAcessoUsuario cpau
JOIN IpSeguranca.dbo.UsuarioCliente uc  ON uc.Id = cpau.UsuarioClienteId
JOIN IpSeguranca.dbo.ClienteEmpresa ce  ON ce.Id = cpau.ClienteEmpresaId
WHERE uc.UsuarioId   = @UsuarioId        -- usuário autenticado
  AND ce.ClienteBDId = @ClienteBDId      -- banco do cliente da sessão
  AND uc.RemovidoEm   IS NULL
  AND cpau.RemovidoEm IS NULL
  AND ce.RemovidoEm   IS NULL;

-- 2) Banco do cliente: de cada ClienteEmpresa para a clínica do banco => {ClinicasDoMedico}
SELECT se.IdEmpresa, c.IdChClinica
FROM SegEmpresa se
JOIN Clinica c ON c.IdEmpresa = se.IdEmpresa
WHERE se.SegurancaClienteEmpresaId IN (/* ClienteEmpresaId do passo 1 */);
```

Onde `{ClinicasDoMedico}` entra: consultas e procedimentos, `Agenda.IdChClinica IN (SELECT IdChClinica FROM {ClinicasDoMedico})`; prescrições, pela tabela `{LimitesPorClinica}`, que passa a ter uma linha por clínica de `{ClinicasDoMedico}` (4.6); conversas, `Conversa.IdEmpresa IN (SELECT IdEmpresa FROM {ClinicasDoMedico})` (4.8; `Conversa.IdEmpresa` = `SegEmpresa.IdEmpresa`, confirmado em D-CM-069; o uso de `IdChClinica` segue em avaliação, P-CM-052); a regra de exibir o nome da clínica é `COUNT({ClinicasDoMedico}) > 1`. A clínica em que o usuário está logado sempre está na lista. **Fora desta regra (D-CM-067):** `Recurso.IdChClinica` e `RecursoHorario` ligam-se à marcação de agendas, tarefa da recepção (só quem é recurso na clínica pode ter horário), e não influenciam quais clínicas o médico vê. **Perda de acesso (D-CM-068, D-CM-070):** quem perde o acesso a uma clínica deixa de ver as agendas e as prescrições dela; para consultas, prescrições, procedimentos e mensagens isso é o filtro acima. O médico **não perde o acesso ao paciente** (D-CM-070): "Em Tratamento", adesão ao protocolo e busca de pacientes não têm filtro por clínica, e `Paciente.IdChClinica` (`smallint` NULL, GescomClienteAlfa) não entra em nenhuma consulta da Central; ele será filtro para o administrativo e outras funções, ainda sem especificação. A expectativa é que as agendas e prescrições da clínica sem acesso sejam redirecionadas ou canceladas; isso não está especificado.

### 4.1 Endpoint: GET /api/v1/cockpit/doctor/appointments

Retorna a lista de consultas do médico logado para o dia atual.

#### Tabelas envolvidas

| Tabela         | Banco             | Função no endpoint                    |
| -------------- | ----------------- | ------------------------------------- |
| Agenda         | GescomClienteAlfa | Tabela principal — dados da agenda    |
| Paciente       | GescomClienteAlfa | Dados do paciente (nome, nome social) |
| PacienteFoto   | GescomClienteAlfa | Foto do paciente                      |
| PEP            | GescomClienteAlfa | Prontuário do paciente — liga o diagnóstico (`PEPDiagnostico`) e as tags atribuídas no prontuário (`PEPProgramaApoioPaciente`) ao paciente; traz a vigência dessas tags |
| PEPDiagnostico | GescomClienteAlfa | Diagnóstico oncológico do paciente    |
| PEPProgramaApoioPaciente | GescomClienteAlfa | Tags atribuídas no prontuário (chave `IdPEP`, `IdProgramaApoio`) |
| ProgramaApoio  | GescomClienteAlfa | Cadastro de tags da clínica (sigla, descrição, cor, telas, vigência) |
| ProgramaApoioPaciente | GescomClienteAlfa | Tags atribuídas no cadastro do paciente, com vigência |
| TermoTraducao  | IpTerminologia    | Tradução do modo de atendimento e do nome do status (`status.name`) |
| DomEstadoAgendaConsulta | IpTerminologia | Cores de cada status nos temas claro e escuro (`status.colors`) |
| Chave          | GescomClienteAlfa | Nome curto da clínica (campo `clinic`; `Chave.Descricao` da `Agenda.IdChClinica`) |
| ClienteEmpresa | IpSeguranca       | Idioma e fuso horário (IANA) da clínica |

#### Mapa de campos

| Campo                   | Tipo            | Banco                                                                                                                                                                                                                       | Observação                                                                                                                                                                                                                                                                                                                                                                   |                                                       |
| :---------------------- | :-------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- |
| `id`                    | int             | Agenda.IdAgenda                                                                                                                                                                                                             |                                                                                                                                                                                                                                                                                                                                                                              |                                                       |
| `clinic`                | string \| null  | `Agenda.IdChClinica` → `Chave.IdChave` → `Chave.Descricao`      | Nome curto da clínica da consulta, mesma origem do campo `clinic` da Agenda do Médico (Mapeamento da Agenda, 4.1). `null` quando o médico atende em uma só clínica, isto é, quando `{ClinicasDoMedico}` tem uma só linha (D-CM-066). É a mesma regra de `ClinicVisible()` da Agenda ("o médico tem acesso a mais de uma clínica"): a Agenda conta os `ClienteEmpresa` acessíveis e a Central conta as linhas de `{ClinicasDoMedico}`, o mesmo número enquanto toda clínica acessível tiver `SegEmpresa` e `Clinica` (D-CM-067; substitui a regra "o cliente tem mais de uma clínica" de E-CM-033) (RN-CM-032, E-CM-028) |
| `startTime`             | horário         | Agenda.DhAgendaIniUTC                                                                                                                                                                                                       | só horário, convertido para o fuso e idioma da clínica                                                                                                                                                                                                                                                                                                                       |                                                       |
| `endTime`               | horário         | Agenda.DhAgendaFimUTC                                                                                                                                                                                                       | só horário, convertido para o fuso e idioma da clínica                                                                                                                                                                                                                                                                                                                       |                                                       |
| `status.domain`         | string          | calculado | vide AppointmentStatus() | |
| `status.name`           | string          | `TermoTraducao.TermoValor` | `Termo` = `CodEstado` do status (tabela "Status × `CodEstado`", em "Domínios"), `Origem` = `DomEstadoAgendaConsulta`, `Idioma` = `ClienteEmpresa.IdiomaOficial`. Sem tradução: vai a própria chave (`CodEstado`) e anomalia no log técnico (DT-011) | |
| `status.colors.light.background` / `.text` | char(7) | `DomEstadoAgendaConsulta.CorClaroFundo` / `CorClaroTexto` | Pela mesma chave `CodEstado`. Base global: iguais para todas as clínicas (DT-016) | |
| `status.colors.dark.background` / `.text` | char(7) | `DomEstadoAgendaConsulta.CorEscuroFundo` / `CorEscuroTexto` | Idem. Se alguma das quatro colunas estiver NULL (são `NULL` na DDL), `colors` não vai na resposta (o front mostra chip e avatar neutros) | |
| `attendanceMode`        | string          | Agenda.Atendimento -> TermoTraducao.TermoValor                                                                                                                                                                              | Termo = Agenda.Atendimento, Origem="Indicadores                                                                                                                                                                                                                                                                                                                              | ModoAtendimento", Idioma=ClienteEmpresa.IdiomaOficial |
| `firstTime`             | boolean         | `Agenda.PrimeiraConsulta = 'S'`                                                                                                                                                                                              | 'S' = sim, 'N' = não                                                                                                                                                                                                                                                                                                                                                         |                                                       |
| `delayMinutes`          | int             | Now(no fuso da clínica)-Agenda.DhAgendaIniUTC(no fuso da clínica)                                                                                                                                                           | Se o valor for negativo (horário previsto ainda não chegou), retorna 0 — só é positivo quando o horário previsto já passou e a consulta não foi iniciada                                                                                                                                                                                                                     |                                                       |
| `elapsedMinutes`        | int             | `DATEDIFF(MINUTE, Agenda.DhAtendimentoIniUTC, SYSDATETIMEOFFSET())` quando `DhAtendimentoIniUTC` não é nulo e `DhAtendimentoFimUTC` é nulo (consulta em atendimento) | `NULL` nos demais estados (D-CM-017) |
| `overtime`              | boolean         | `elapsedMinutes > Agenda.Tempo` (duração prevista da agenda, em minutos) | `false` quando `elapsedMinutes` é nulo ou `Agenda.Tempo` é nulo; sem tolerância (D-CM-017) |
| `patient.id`            | int             | Agenda.IdPaciente                                                                                                                                                                                                           |                                                                                                                                                                                                                                                                                                                                                                              |                                                       |
| `patient.preferredName` | string          | Paciente.NomeSocial                                                                                                                                                                                                         | se existir, usar este como nome principal                                                                                                                                                                                                                                                                                                                                    |                                                       |
| `patient.legalName`     | string          | Paciente.Nome                                                                                                                                                                                                               | se NomeSocial não existir, usar este como nome                                                                                                                                                                                                                                                                                                                               |                                                       |
| `patient.photo`         | url             | PacienteFoto.NomeArquivo, busca IdPaciente e Padrao = 'S', se multiplos, retorna o mais recente                                                                                                                             | url gerada pelo MinIO                                                                                                                                                                                                                                                                                                                                                        |                                                       |
| `patient.diagnosis`     | array de string | PEPDiagnostico.Diagnostico para Tipo = 'O' e PEP.IdPaciente                                                                                                                                                                 | retorna TODOS os diagnósticos oncológicos ativos do paciente (não só um) — o card exibe a lista completa; "ativo" = sem `RemovidoEm` em `PEPDiagnostico` e em `PEP`, e com `PEP.DataInicioUTC` até o fim de hoje no fuso da clínica (nulo = vigente desde sempre) — o diagnóstico de um PEP que só começa depois de hoje não é retornado; só o início é avaliado, `PEP.DataFimUTC` não filtra o diagnóstico                                                                                                                                                                                                                                                                     |                                                       |
| `tags`                  | array de objeto (`code`, `description`, `color`) | União de duas origens, ambas com join em `ProgramaApoio`: (1) `ProgramaApoioPaciente` por `IdPaciente` (atribuição no cadastro do paciente, tags de `Finalidade` `D`, inclusive VIP); (2) `PEPProgramaApoioPaciente` → `PEP` por `PEP.IdPaciente` (atribuição no prontuário, tags de `Finalidade` `A`) | `code` = `ProgramaApoio.Sigla`; `description` = `ProgramaApoio.Descricao`; `color` = `ProgramaApoio.Cor` (`varchar(7)`, hexadecimal `#RRGGBB`, fundo do chip — Design System, seção 5; ausente ou inválida, o chip usa `--color-neutral-lighter`). **Filtros comuns às tags de `ProgramaApoio`:** `(ProgramaApoio.MostrarEm & 32) = 32` (bit 6, Agendas — não o bit da Central do Médico; ver Domínios, abaixo); vigência da própria tag (`ProgramaApoio.DataInicio`/`DataFim`, ver Domínios); `ProgramaApoio.RemovidoEm IS NULL`. A tag VIP é exceção ao `MostrarEm` e à vigência da tag (RN-CM-025). **Vigência da atribuição ao paciente, avaliada hoje (fuso da clínica):** origem (1): `ProgramaApoioPaciente.DataInicio`/`DataFim` e `ProgramaApoioPaciente.RemovidoEm IS NULL`; origem (2): `PEP.DataInicioUTC`/`DataFimUTC` (`datetimeoffset NULL`, os campos de data válidos — `PEP.DataInicio`/`DataFim` não são lidos; nulo em `DataInicioUTC` = vigente desde sempre, nulo em `DataFimUTC` = sem término) e `PEP.RemovidoEm IS NULL` (`PEPProgramaApoioPaciente` não tem datas nem `RemovidoEm`; a PK é `IdPEP` — um registro de PEP leva uma tag). Um período vale para hoje quando a data de início é hoje ou anterior e a data de fim, se houver, é hoje ou posterior: comparam-se só as datas (dia de calendário no fuso da clínica), sem a hora, e início e fim são inclusivos (D-CM-021). Vale para todas as vigências acima; os campos `datetimeoffset` de `PEP` são convertidos para o fuso da clínica antes de tomar a data. A massa tem os dois casos de borda: fim no próprio dia (VIP do paciente 3, `DataFim` = 23/09/2026 00:00 — aparece) e início no próprio dia com hora (Yuki Tanaka, PEP 1107, 23/09/2026 11:56 — aparece). As duas origens separam as tags pela finalidade (`A` só no prontuário, `D` só no cadastro); se a mesma tag vier das duas, aparece uma vez. **Ordem de retorno (D-CM-022):** tags de `Finalidade` `A` primeiro, depois as de `D`; dentro de cada grupo, ordem alfabética por `ProgramaApoio.Sigla`. A tag VIP é uma tag administrativa, atribuída no cadastro (`ProgramaApoioPaciente`) como as demais; `Paciente.PacienteVip` não é lido (registrado na Agenda do Médico: fora de escopo até se decidir se o campo será descontinuado). Na massa de referência, VIP está em 2 linhas de `ProgramaApoioPaciente` (pacientes 3 e 12), e a sigla, a descrição e a cor vêm de `IdProgramaApoio=1` (`Cor='#80FF38'`). |  |

#### Funções de cálculo

AppointmentStatus() — calcula o status da consulta a partir dos campos da Agenda. As condições são avaliadas nesta ordem, e vale a primeira que se aplicar:

| Ordem | Status retornado                | Condição                                                                                  |
| ----- | ------------------------------- | ----------------------------------------------------------------------------------------- |
| 1     | `canceled` (Cancelado)          | Agenda.Situacao IN ('C','D','F','I','M','O','T')                                          |
| 2     | `finished` (Finalizado)         | Agenda.Situacao = 'V' AND DhAtendimentoFimUTC IS NOT NULL                                 |
| 3     | `consultation` (Em atendimento) | Agenda.Situacao = 'V' AND DhAtendimentoIniUTC IS NOT NULL AND DhAtendimentoFimUTC IS NULL |
| 4     | `checked` (Recepcionado)        | Agenda.Situacao = 'V' AND DhAtendimentoIniUTC IS NULL                                     |
| 5     | `waiting` (Aguardando recepção) | Agenda.Situacao = 'A' AND DhChegadaUTC IS NOT NULL                                        |
| 6     | `scheduled` (Agendado)          | Agenda.Situacao = 'A' AND DhChegadaUTC IS NULL                                            |

A ordem resolve uma sobreposição: `Situacao = 'V'` com `DhAtendimentoFimUTC` preenchido e `DhAtendimentoIniUTC` NULL satisfaz `checked` e `finished` — vale `finished`. É a mesma precedência da Agenda do Médico (Mapeamento, 4.1), que usa esta função. `Agenda.Situacao` é `NOT NULL`.

#### Domínios

Status × `CodEstado` (`IpTerminologia.DomEstadoAgendaConsulta`, chave das cores e do nome traduzido): `scheduled` → `A`; `waiting` → `D`; `checked` → `G`; `consultation` → `J`; `finished` → `M`; `canceled` → `X`. É a mesma tabela da Agenda do Médico (Mapeamento, 4.1); `canceled` não aparece neste painel (filtros, abaixo).

TipoAgenda (domínio de Agenda.TipoAgenda) na tabela Indicadores:

| Valor | Significado         |
| ----- | ------------------- |
| C     | Consulta |
| Q     | Quimioterapia |
| R     | Radioterapia |
| S     | Reserva de sala para assuntos não assistenciais |

ModoAtendimento (domínio de Agenda.Atendimento) na tabela Indicadores:

| Valor | Significado         |
| ----- | ------------------- |
| P     | Padrão (Presencial) |
| T     | Teleatendimento     |
| C     | Customizado         |
| R     | Reservado           |

SituacaoAgenda (domínio de Agenda.Situacao) na tabela Indicadores:

| Valor | Significado                     |
| ----- | ------------------------------- |
| A     | Aberta                          |
| V     | Confirmada (check-in realizado) |
| C     | Cancelada pelo usuário          |
| D     | Cancelada pelo paciente         |
| M     | Cancelada pelo médico           |
| F     | Falta                           |
| I     | Cancelado por internação        |
| O     | Cancelada por óbito             |
| T     | Transferido (cancelado)         |

TipoDiagnostico (domínio de Diagnostico.Tipo):

| Valor | Significado                     |
| ----- | ------------------------------- |
| C     | Comorbidade                     |
| O     | Doença oncológica               |

MostrarEm (domínio de `ProgramaApoio.MostrarEm`) — mapa de bits que controla em quais telas do sistema uma tag (`ProgramaApoio`) pode ser exibida; uma tag só aparece numa tela se o bit correspondente estiver ligado. Não é exclusivo deste documento — é consultado por outras funcionalidades do sistema (ex.: Agenda do Médico, RN-AGM-006); esta é a primeira vez que o domínio completo é registrado em forma de tabela num Mapeamento de Banco de Dados — qualquer alteração futura a este domínio deve ser feita apenas aqui, com as demais funcionalidades referenciando por ID em vez de repetir a tabela (Lição #10 da Skill Designer):

| Bit | Valor | Tela habilitada |
| --- | --- | --- |
| 1 | 1 | Cabeçalho do prontuário |
| 2 | 2 | Cadastro do paciente |
| 3 | 4 | Recepção (telas de Quimioterapia e de Consulta) |
| 4 | 8 | Central do Médico |
| 5 | 16 | Confirmação de quimio / Central de atendimento — dois termos em uso para o mesmo bit; qual é o nome oficial da tela não foi decidido aqui |
| 6 | 32 | Agendas (de quimio e de consultas) — usado pelos dois endpoints deste documento (ver nota abaixo) |
| 7 | 64 | sem função definida até esta versão |
| 8 | 128 | sem função definida até esta versão |

`MostrarEm = 0` (todos os bits desligados) → a tag não é exibida em nenhuma tela. Vários bits podem estar ligados ao mesmo tempo — ex.: `MostrarEm = 38` = bits 2+3+6 ligados = tag exibida no cadastro do paciente, na recepção e nas agendas. Filtro usado nesta funcionalidade (painéis de Consultas e Pacientes em procedimento): `(MostrarEm & 32) = 32` (bit 6 — Agendas), não o bit 4 — apesar de os dois painéis estarem dentro da tela "Central do Médico", cada um é, na prática, uma agenda do dia (de consultas e de quimioterapia/infusão, respectivamente), e é esse o critério que rege a exibição, não o nome da tela que os hospeda. O bit 4 (Central do Médico) não é usado por nenhum endpoint deste documento; se ele se aplica a alguma outra tela do Cockpit não documentada aqui, não está confirmado.

Vigência da tag (`ProgramaApoio.DataInicio`/`DataFim`, ambos `datetime NULL`, opcionais) — distinta da vigência da atribuição da tag a um paciente específico (`ProgramaApoioPaciente.DataInicio`/`DataFim` no cadastro do paciente; `PEP.DataInicioUTC`/`DataFimUTC` no prontuário — Seção 4.1, campo `tags`). Quando preenchidos, restringem em quais dias a tag (como conceito, independente de paciente) pode aparecer — por exemplo, uma tag de campanha temporária que a clínica quer parar de usar a partir de uma data:

| `DataInicio` | `DataFim` | Exibida quando |
| --- | --- | --- |
| NULL | NULL | sempre (sem restrição de vigência) |
| preenchida | NULL | `hoje >= DataInicio` |
| NULL | preenchida | `hoje <= DataFim` |
| preenchida | preenchida | `DataInicio <= hoje` E `hoje <= DataFim` |

`DataFim` é inclusiva (tag continua aparecendo no próprio dia de `DataFim`) — mesma convenção de `ProgramaApoioPaciente.DataFim` (Seção 4.1). Filtro usado nesta funcionalidade: `(DataInicio IS NULL OR hoje >= DataInicio) AND (DataFim IS NULL OR hoje <= DataFim)`, com "hoje" avaliado como data (dia de calendário no fuso da clínica; só a data conta, não a hora — D-CM-021), como descrito no campo `tags` (Seção 4.1); não se aplica à tag VIP (mesmo motivo do `MostrarEm`).

#### Infraestrutura de apoio

MinIO (Storage de fotos):

- Bucket global: `gemed`
- Bucket por ambiente do cliente: nome = banco de dados do cliente
- 1 bucket por banco de dados (cliente pode ter múltiplos bancos)
- Pasta de fotos: `FotosPacientes/` dentro do bucket do cliente
- Seleção da foto: `PacienteFoto.Padrao = 'S'`; se múltiplas, selecionar a mais recente (futuro: vamos usar o campo `CriadoEm` da infraestrutura)

Internacionalização (i18n):

- Idioma da clínica: `ClienteEmpresa.IdiomaOficial` (IpSeguranca), o termo segue padrão BCP 47
- Fuso horário da clínica: `ClienteEmpresa.FusoHorarioOficial` (IpSeguranca) — identificador IANA (ex.: `America/Sao_Paulo`); campo novo, ver "Campos novos", ao final
- Traduções: tabela `TermoTraducao` (IpTerminologia) usa chave composta por (Termo, Origem, Idioma)
	- Origem: nome da tabela de origem ou `Indicadores|<NomeIndicador>` quando o domínio está na tabela Indicadores
	- Idioma: idioma da clínica atual (`ClienteEmpresa.IdiomaOficial`; DT-012)
	- Termo: o termo usado como chave na tabela de origem
	- Sem tradução no idioma da clínica: vai a própria chave (`Termo`) e o back-end registra anomalia no log técnico (`Termo`, `Origem`, `Idioma`); não há queda para pt-BR nem para outro idioma (DT-011)

#### Filtros do endpoint

- `Agenda.IdChRecurso = {IdChProfissional do médico logado}` — apenas consultas do médico
- Escopo de clínicas (D-CM-066, RN-CM-032): `Agenda.IdChClinica IN (SELECT IdChClinica FROM {ClinicasDoMedico})` — a lista reúne as consultas das clínicas em que o médico atende (Seção 4, bloco "Clínicas em que o médico atende"; D-CM-067); "hoje" e os horários seguem o fuso da clínica em que o usuário está logado (`ClienteEmpresa.FusoHorarioOficial`, RN-AGM-018)
- `Agenda.TipoAgenda = 'C'` - Agendas de consulta
- `Paciente.Paciente <> 'B'` (via `Agenda.IdPaciente`) — exclui os registros de bloqueio de horário feitos pela Recepção, gravados como agenda de consulta com o paciente reservado "B" (RN-CM-030). A Agenda do Médico lê esses mesmos registros e os exibe em `blocks` (RN-AGM-019); esta funcionalidade só os exclui
- `Agenda.DhAgendaIniUTC(no fuso da clínica) = Hoje(no fuso da clinica)` — apenas consultas do dia atual
- `Agenda.RemovidoEm IS NULL` — exclusão lógica (regra de todo o sistema, ver o início desta Seção)
- Toggle "Pendentes": `Agenda.Situacao IN ('A','V')`AND`DhAtendimentoFimUTC IS NULL` — exclui canceladas e finalizadas
- Toggle "Todas": inclui finalizadas (canceladas não aparecem no painel — apenas finalizadas esmaecidas)
- `(ProgramaApoio.MostrarEm & 32) = 32` — restringe as tags exibidas (campo `tags`, acima) às habilitadas para agendas (RN-CM-025); não se aplica à tag `vip` (exceção de RN-CM-025; ver nota de correção em Mapa de Campos, acima, sobre a origem de cor/descrição).
- `(ProgramaApoio.DataInicio IS NULL OR hoje >= ProgramaApoio.DataInicio) AND (ProgramaApoio.DataFim IS NULL OR hoje <= ProgramaApoio.DataFim)` — restringe pela vigência da própria tag (RN-CM-025); não se aplica à tag `vip`; distinto do filtro de vigência da atribuição ao paciente (`ProgramaApoioPaciente`, já aplicado na origem do campo `tags`).

### 4.2 Endpoint: GET /api/v1/cockpit/doctor/kpis/appointments-today

Retornar o indicador de Consultas Hoje

#### Tabelas envolvidas

| Tabela         | Banco             | Função no endpoint                    |
| -------------- | ----------------- | ------------------------------------- |
| Agenda         | GescomClienteAlfa | Tabela principal — dados da agenda    |
| Paciente       | GescomClienteAlfa | Só para o filtro que exclui registros de bloqueio (`Paciente.Paciente <> 'B'`, RN-CM-030) |

#### Mapa de campos

| Campo      | Tipo | Banco | 
| :--------- | :--- | :---- | 
| `total`    | int  | count(Agenda.Situacao IN ('A', 'V')) |
| `finished` | int  | count(Agenda.Situacao='V', Agenda.DhAtendimentoFimUTC IS NOT NULL) |
| `pending`  | int  | count(Agenda.Situacao IN ('A', 'V'), Agenda.DhAtendimentoFimUTC IS NULL) |
| `delayed`  | int  | count(Agenda.Situacao IN ('A', 'V'), Agenda.DhAtendimentoFimUTC IS NULL, Agenda.DhAtendimentoIniUTC IS NULL, Now(no fuso da clínica)-Agenda.DhAgendaIniUTC(no fuso da clinica)>0) — só consultas ainda não iniciadas; a consulta em atendimento (`DhAtendimentoIniUTC` preenchido) é pendente, mas não é atrasada |

#### Domínios

** os mesmos do endpoint acima

#### Filtros do endpoint

- `Agenda.IdChRecurso = {IdChProfissional do médico logado}` — apenas consultas do médico
- Escopo de clínicas (D-CM-066, RN-CM-032): `Agenda.IdChClinica IN (SELECT IdChClinica FROM {ClinicasDoMedico})` — a lista reúne as consultas das clínicas em que o médico atende (Seção 4, bloco "Clínicas em que o médico atende"; D-CM-067); "hoje" e os horários seguem o fuso da clínica em que o usuário está logado (`ClienteEmpresa.FusoHorarioOficial`, RN-AGM-018)
- `Agenda.TipoAgenda = 'C'` - agendas de consulta
- `Paciente.Paciente <> 'B'` (via `Agenda.IdPaciente`) — exclui os registros de bloqueio de horário feitos pela Recepção de todos os números do KPI (RN-CM-030), mesmo filtro da Seção 4.1
- `Agenda.DhAgendaIniUTC(no fuso da clínica) = Hoje(no fuso da clinica)` — apenas consultas do dia atual
- `Agenda.RemovidoEm IS NULL` — exclusão lógica (regra de todo o sistema, ver o início desta Seção)

### 4.3 Endpoint: [GET] /api/v1/cockpit/doctor/procedures
    `{ "procedures":
        [ { "id": 1234,
            "patient":
                { "id": 567,
                  "preferredName": "",
                  "legalName": "Roberto Oliveira"
                },
            "clinic": "Oncocentro Sul",
            "location": "Sala de Infusão 2",
            "status": "administering",
            "startTime": "09:30",
            "firstTime": false,
            "progress": 65,
            "mine": true,
            "tags": [ { "code": "LC", "description": "Linha de cuidados", "color": "#FFD6D1" } ]
          }
        ]
    }`

Retorna a lista de pacientes em procedimentos no dia atual.

#### Tabelas envolvidas

| Tabela         | Banco             | Função no endpoint                    |
| -------------- | ----------------- | ------------------------------------- |
| Agenda         | GescomClienteAlfa | Tabela principal — dados da agenda    |
| Paciente       | GescomClienteAlfa | Dados do paciente (nome, nome social) |
| PacienteFoto   | GescomClienteAlfa | Foto do paciente                      |
| Prescricao     | GescomClienteAlfa | Prescrição do procedimento (`IdAgenda`): código, médico e `TempoAplicacao` (base do `progress`) |
| PEP / PEPProgramaApoioPaciente / ProgramaApoio / ProgramaApoioPaciente | GescomClienteAlfa | Tags do paciente (campo `tags`), como em 4.1 |
| TermoTraducao  | IpTerminologia    | Tradução do modo de atendimento e do nome do status (`status.name`) |
| DomEstadoAgendaProcedimento | IpTerminologia | Cores de cada status do procedimento nos temas claro e escuro (`status.colors`) |
| Chave          | GescomClienteAlfa | Nome curto da clínica (campo `clinic`; `Chave.Descricao` da `Agenda.IdChClinica`) |
| ClienteEmpresa | IpSeguranca       | Idioma e fuso horário (IANA) da clínica |

#### Mapa de campos

| Campo                   | Tipo    | Banco                                                                                                           | Observação                                             |     |
| :---------------------- | :------ | :-------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------- | --- |
| `id`                    | int     | Prescricao.IdPrescricao                                                                                         |                                                        |     |
| `clinic`                | string \| null | `Prescricao.IdAgenda` → `Agenda.IdChClinica` → `Chave.Descricao` | Nome curto da clínica do procedimento, mesma origem e mesma regra de `appointments.clinic` (4.1); `null` quando o médico atende em uma só clínica (RN-CM-032, D-CM-061, D-CM-066) |
| `prescriptionCode`      | string  | Prescricao.Identificacao                                                                                        |                                                        |     |
| `location`              | string  | Prescricao.IdAgenda->Agenda.IdUAtendimento->UnidadeAtendimento.Descricao + Agenda.IdChRecurso->Chave.Descricao  | IdUAtendimento pode ser NULL                           |     |
| `startTime`             | horário | se finalizado (Situacao='V' AND DhAtendimentoFimUTC IS NOT NULL — mesma condição de `finished` em ProcedureStatus()), Agenda.DhAtendimentoFimUTC(no fuso da clínica); senão se Situacao='V', Agenda.DhAtendimentoIniUTC(no fuso da clínica); senão Agenda.DhAgendaIniUTC(no fuso da clínica) | horário "HH:mm" no fuso da clínica (D-CM-042), sem data nem fuso na cadeia. Campo continua chamado `startTime` por herança do endpoint já existente, mas passa a carregar o horário de término quando o procedimento está Finalizado (RN-CM-026, 01-Definição v4.34) — nome mantido para não alterar o contrato já em uso; uma reformulação futura do contrato pode considerar um nome neutro (ex.: `displayTime`), fora do escopo desta revisão. `DhAtendimentoIniUTC` marca o início real do atendimento (preparo do paciente, pré-medicações etc.), anterior ao início da infusão propriamente dita; `DhInfusaoIniUTC`/`DhInfusaoFimUTC` (usados só em `ProcedureStatus()`, não neste campo) marcam início/fim do primeiro medicamento do protocolo, que ocorre depois desse preparo — `startTime` usa corretamente o horário de atendimento, não o de infusão |     |
| `firstTime`             | boolean | `Agenda.PrimeiraConsulta = 'S'`                                                                                  | mesmo campo e mesma origem de `appointments.firstTime` (4.1); o front mostra o ícone de 1ª Consulta (RN-CM-028, D-CM-029) |     |
| `status.domain`         | string          | calculado | vide ProcedureStatus(), avaliado em ordem; valores `scheduled`, `waiting`, `checked`, `preparation`, `administering`, `administered`, `finished`, `canceled` (D-CM-030, D-CM-074) | |
| `status.name`           | string          | `TermoTraducao.TermoValor` | `Termo` = `CodEstado` do status (tabela "Status × `CodEstado`", em "Domínios"), `Origem` = `DomEstadoAgendaProcedimento`, `Idioma` = `ClienteEmpresa.IdiomaOficial`. Sem tradução: vai a própria chave (`CodEstado`) e anomalia no log técnico (DT-011) | |
| `status.colors.light.background` / `.text` | char(7) | `DomEstadoAgendaProcedimento.CorClaroFundo` / `CorClaroTexto` | Pela mesma chave `CodEstado`. Base global: iguais para todas as clínicas (DT-016) | |
| `status.colors.dark.background` / `.text` | char(7) | `DomEstadoAgendaProcedimento.CorEscuroFundo` / `CorEscuroTexto` | Idem. Se alguma das quatro colunas estiver NULL (são `NULL` na DDL), `colors` não vai na resposta (o front mostra chip e avatar neutros) | |
| `progress`              | int     | `CAST(ROUND(DATEDIFF(MINUTE, Agenda.DhAtendimentoIniUTC, SYSDATETIMEOFFSET()) * 100.0 / NULLIF(Prescricao.TempoAplicacao, 0), 0) AS int)` (`TempoAplicacao` é `numeric(6,0)`, em minutos; arredondado ao inteiro mais próximo; `NULLIF` evita a divisão por zero e devolve nulo) | `NULL` quando `Prescricao.TempoAplicacao` é nulo ou zero — o front não desenha a barra. Substitui `Agenda.Tempo` como base (D-CM-015) |     |
| `mine`                  | boolean | Prescricao.IdChProfissionalMed={IdChProfissional do médico logado}                                              |                                                        |     |
| `patient.id`            | int     | Prescricao.IdPaciente                                                                                           |                                                        |     |
| `patient.preferredName` | string  | Paciente.NomeSocial                                                                                             | se existir, usar este como nome principal              |     |
| `patient.legalName`     | string  | Paciente.Nome                                                                                                    | se NomeSocial não existir, usar este como nome         |     |
| `patient.photo`         | url     | PacienteFoto.NomeArquivo, busca IdPaciente e Padrao = 'S', se multiplos, retorna o mais recente                 | url gerada pelo MinIO                                  |     |
| `tags`                  | array de objeto (`code`, `description`, `color`) | mesma origem, mesmos campos e mesmos filtros de `appointments.tags` (Seção 4.1): duas origens (cadastro do paciente e prontuário), `MostrarEm` pelo bit de agendas (não o da Central do Médico, mesmo este painel estando dentro dela), vigência da tag, vigência da atribuição e `RemovidoEm` |                                                        |     |

#### Funções de cálculo

ProcedureStatus() — calcula o status do procedimento a partir dos campos da Agenda. As condições são avaliadas nesta ordem, e vale a primeira que se aplicar (D-CM-030):

| Ordem | Status retornado                   | Condição                                                                              |
| ----- | ---------------------------------- | ------------------------------------------------------------------------------------- |
| 1     | `canceled` (Cancelado)             | Agenda.Situacao IN ('C','D','F','I','M','O','T')                                      |
| 2     | `finished` (Finalizado)            | Agenda.Situacao = 'V' AND DhAtendimentoFimUTC IS NOT NULL                             |
| 3     | `administered` (Completo)          | Agenda.Situacao = 'V' AND DhInfusaoFimUTC IS NOT NULL AND DhAtendimentoFimUTC IS NULL |
| 4     | `administering` (Em aplicação) | Agenda.Situacao = 'V' AND DhInfusaoIniUTC IS NOT NULL AND DhInfusaoFimUTC IS NULL     |
| 5     | `preparation` (Em preparo)            | Agenda.Situacao = 'V' AND DhAtendimentoIniUTC IS NOT NULL AND DhInfusaoIniUTC IS NULL |
| 6     | `checked` (Recepcionado)           | Agenda.Situacao = 'V' AND DhAtendimentoIniUTC IS NULL                                 |
| 7     | `waiting` (Aguardando recepção)    | Agenda.Situacao = 'A' AND DhChegadaUTC IS NOT NULL                                    |
| 8     | `scheduled` (Agendado)             | Agenda.Situacao = 'A' AND DhChegadaUTC IS NULL                                        |

A ordem resolve as sobreposições: `finished` vale mesmo com o fim da infusão vazio (23 linhas da massa de referência têm alta e `DhInfusaoFimUTC` NULL); com `DhInfusaoFimUTC` preenchido o procedimento nunca é `administering`; com início de infusão preenchido e `DhAtendimentoIniUTC` NULL vale `administering`, não `checked`. O valor de contrato do status 3 é `administered` (D-CM-074; `CodEstado` `ASD` em `DomEstadoAgendaProcedimento`). `Agenda.Situacao` é `NOT NULL`.

#### Domínios

Status × `CodEstado` (`IpTerminologia.DomEstadoAgendaProcedimento`, chave das cores e do nome traduzido): `scheduled` → `SCH`; `waiting` → `WTI`; `checked` → `CKD`; `preparation` → `PRP`; `administering` → `ADM`; `administered` → `ASD`; `finished` → `FNS`; `canceled` → `CNL`. As cores de `SCH`, `WTI`, `CKD`, `FNS` e `CNL` são as mesmas dos status equivalentes da consulta; `PRP`, `ADM` e `ASD` têm cores próprias (Design System, seção 4).

TipoAgenda (domínio de Agenda.TipoAgenda) na tabela Indicadores:

| Valor | Significado         |
| ----- | ------------------- |
| C     | Consulta |
| Q     | Quimioterapia |
| R     | Radioterapia |
| S     | Reserva de sala para assuntos não assistenciais |

SituacaoAgenda (domínio de Agenda.Situacao) na tabela Indicadores:

| Valor | Significado                     |
| ----- | ------------------------------- |
| A     | Aberta                          |
| V     | Confirmada (check-in realizado) |
| C     | Cancelada pelo usuário          |
| D     | Cancelada pelo paciente         |
| M     | Cancelada pelo médico           |
| F     | Falta                           |
| I     | Cancelado por internação        |
| O     | Cancelada por óbito             |
| T     | Transferido (cancelado)         |

#### Infraestrutura de apoio

MinIO (Storage de fotos):

- Bucket global: `gemed`
- Bucket por ambiente do cliente: nome = banco de dados do cliente
- 1 bucket por banco de dados (cliente pode ter múltiplos bancos)
- Pasta de fotos: `FotosPacientes/` dentro do bucket do cliente
- Seleção da foto: `PacienteFoto.Padrao = 'S'`; se múltiplas, selecionar a mais recente (futuro: vamos usar o campo `CriadoEm` da infraestrutura)

Internacionalização (i18n):

- Idioma da clínica: `ClienteEmpresa.IdiomaOficial` (IpSeguranca), o termo segue padrão BCP 47
- Fuso horário da clínica: `ClienteEmpresa.FusoHorarioOficial` (IpSeguranca) — identificador IANA (ex.: `America/Sao_Paulo`); campo novo, ver "Campos novos", ao final
- Traduções: tabela `TermoTraducao` (IpTerminologia) usa chave composta por (Termo, Origem, Idioma)
    - Origem: nome da tabela de origem ou `Indicadores|<NomeIndicador>` quando o domínio está na tabela Indicadores
    - Idioma: idioma da clínica atual (`ClienteEmpresa.IdiomaOficial`; DT-012)
    - Termo: o termo usado como chave na tabela de origem
    - Sem tradução no idioma da clínica: vai a própria chave (`Termo`) e o back-end registra anomalia no log técnico (`Termo`, `Origem`, `Idioma`); não há queda para pt-BR nem para outro idioma (DT-011)

#### Filtros do endpoint

- `Prescricao.IdAgenda IS NOT NULL`
- Escopo de clínicas (D-CM-066, RN-CM-032): `Agenda.IdChClinica IN (SELECT IdChClinica FROM {ClinicasDoMedico})` — a lista reúne os procedimentos das clínicas em que o médico atende (Seção 4, bloco "Clínicas em que o médico atende"; D-CM-067); "hoje" e os horários seguem o fuso da clínica em que o usuário está logado (RN-AGM-018)
- `Agenda.TipoAgenda IN ('Q', 'R')` - Agendas de quimio, radio
- `Agenda.DhAgendaIniUTC(no fuso da clínica) = Hoje(no fuso da clinica)` — apenas procedimentos do dia atual
- `Agenda.RemovidoEm IS NULL` — exclusão lógica (regra de todo o sistema, ver o início desta Seção)
- Toggle "Meus": Prescricao.IdChProfissionalMed={IdChProfissional do médico logado}
- Toggle "Todos": sem restrições
- `(ProgramaApoio.MostrarEm & 32) = 32` e o filtro de vigência de `ProgramaApoio.DataInicio`/`DataFim` — mesmos filtros de tags do painel de Consultas, ver Seção 4.1.

### 4.4 Endpoint GET /api/v1/cockpit/doctor/kpis/procedures-today

GET] /api/v1/cockpit/doctor/kpis/procedures-today
`{   "total": 8, 
    "inProgress": 2, 
    "waiting": 4, 
    "scheduled": 2 }`
Função: Retornar o indicador de Procedimentos Hoje

#### Tabelas envolvidas

| Tabela         | Banco             | Função no endpoint                    |
| -------------- | ----------------- | ------------------------------------- |
| Agenda         | GescomClienteAlfa | Tabela principal — dados da agenda    |
| Paciente       | GescomClienteAlfa | Dados do paciente (nome, nome social) |
| PacienteFoto   | GescomClienteAlfa | Foto do paciente                      |
| Prescricao     | GescomClienteAlfa | Diagnóstico oncológico do paciente    |
| TermoTraducao  | IpTerminologia    | Tradução do modo de atendimento       |
| ClienteEmpresa | IpSeguranca       | Idioma e fuso horário da clínica      |

#### Mapa de campos

| Campo        | Tipo | Banco                                                      |
| :----------- | :--- | :--------------------------------------------------------- |
| `total`      | int  | count(Agenda.Situacao IN ('A', 'V')) — inclui os procedimentos já completos e finalizados (D-CM-038); os três subtotais não precisam somar o total |
| `inProgress` | int  | count(ProcedureStatus()=`administering`) |
| `waiting`    | int  | count(ProcedureStatus()=`waiting` OR `checked` OR `preparation`) |
| `scheduled`  | int  | count(ProcedureStatus()=`scheduled`)                       |

#### Funções de cálculo

ProcedureStatus() — calcula o status do procedimento a partir dos campos da Agenda. As condições são avaliadas nesta ordem, e vale a primeira que se aplicar (D-CM-030):

| Ordem | Status retornado                   | Condição                                                                              |
| ----- | ---------------------------------- | ------------------------------------------------------------------------------------- |
| 1     | `canceled` (Cancelado)             | Agenda.Situacao IN ('C','D','F','I','M','O','T')                                      |
| 2     | `finished` (Finalizado)            | Agenda.Situacao = 'V' AND DhAtendimentoFimUTC IS NOT NULL                             |
| 3     | `administered` (Completo)          | Agenda.Situacao = 'V' AND DhInfusaoFimUTC IS NOT NULL AND DhAtendimentoFimUTC IS NULL |
| 4     | `administering` (Em aplicação) | Agenda.Situacao = 'V' AND DhInfusaoIniUTC IS NOT NULL AND DhInfusaoFimUTC IS NULL     |
| 5     | `preparation` (Em preparo)            | Agenda.Situacao = 'V' AND DhAtendimentoIniUTC IS NOT NULL AND DhInfusaoIniUTC IS NULL |
| 6     | `checked` (Recepcionado)           | Agenda.Situacao = 'V' AND DhAtendimentoIniUTC IS NULL                                 |
| 7     | `waiting` (Aguardando recepção)    | Agenda.Situacao = 'A' AND DhChegadaUTC IS NOT NULL                                    |
| 8     | `scheduled` (Agendado)             | Agenda.Situacao = 'A' AND DhChegadaUTC IS NULL                                        |

A ordem resolve as sobreposições: `finished` vale mesmo com o fim da infusão vazio (23 linhas da massa de referência têm alta e `DhInfusaoFimUTC` NULL); com `DhInfusaoFimUTC` preenchido o procedimento nunca é `administering`; com início de infusão preenchido e `DhAtendimentoIniUTC` NULL vale `administering`, não `checked`. O valor de contrato do status 3 é `administered` (D-CM-074; `CodEstado` `ASD` em `DomEstadoAgendaProcedimento`). `Agenda.Situacao` é `NOT NULL`.

#### Domínios

TipoAgenda (domínio de Agenda.TipoAgenda) na tabela Indicadores:

| Valor | Significado         |
| ----- | ------------------- |
| C     | Consulta |
| Q     | Quimioterapia |
| R     | Radioterapia |
| S     | Reserva de sala para assuntos não assistenciais |

SituacaoAgenda (domínio de Agenda.Situacao) na tabela Indicadores:

| Valor | Significado                     |
| ----- | ------------------------------- |
| A     | Aberta                          |
| V     | Confirmada (check-in realizado) |
| C     | Cancelada pelo usuário          |
| D     | Cancelada pelo paciente         |
| M     | Cancelada pelo médico           |
| F     | Falta                           |
| I     | Cancelado por internação        |
| O     | Cancelada por óbito             |
| T     | Transferido (cancelado)         |


#### Infraestrutura de apoio

Internacionalização (i18n):

- Idioma da clínica: `ClienteEmpresa.IdiomaOficial` (IpSeguranca), o termo segue padrão BCP 47
- Fuso horário da clínica: `ClienteEmpresa.FusoHorarioOficial` (IpSeguranca) — identificador IANA (ex.: `America/Sao_Paulo`); campo novo, ver "Campos novos", ao final
- Traduções: tabela `TermoTraducao` (IpTerminologia) usa chave composta por (Termo, Origem, Idioma)
    - Origem: nome da tabela de origem ou `Indicadores|<NomeIndicador>` quando o domínio está na tabela Indicadores
    - Idioma: idioma da clínica atual (`ClienteEmpresa.IdiomaOficial`; DT-012)
    - Termo: o termo usado como chave na tabela de origem
    - Sem tradução no idioma da clínica: vai a própria chave (`Termo`) e o back-end registra anomalia no log técnico (`Termo`, `Origem`, `Idioma`); não há queda para pt-BR nem para outro idioma (DT-011)

#### Filtros do endpoint

- Escopo de clínicas (D-CM-066, RN-CM-032): `Agenda.IdChClinica IN (SELECT IdChClinica FROM {ClinicasDoMedico})` — o indicador conta os procedimentos das clínicas em que o médico atende, o mesmo conjunto do painel (4.3); "hoje" no fuso da clínica em que o usuário está logado
- `Agenda.TipoAgenda IN ('Q', 'R')` - Agendas de quimio, radio
- `Agenda.DhAgendaIniUTC(no fuso da clínica) = Hoje(no fuso da clinica)` — apenas procedimentos do dia atual
- `Agenda.RemovidoEm IS NULL` — exclusão lógica (regra de todo o sistema, ver o início desta Seção)
- `Prescricao.IdChProfissionalMed={IdChProfissional do médico logado} - só os meus pacientes

### 4.5 Endpoint [GET] /api/v1/cockpit/doctor/kpis/in-treatment
    /api/v1/cockpit/doctor/kpis/in-treatment
    `{  "inTreatment": 43, 
	    "nonAdherent": 2 }`
Função: Retornar o indicador de Em Tratamento
#### Tabelas envolvidas

| Tabela                                      | Banco             | Função no endpoint                                                                                            |
| --------------------------------------------- | ----------------- | ---------------------------------------------------------------------------------------------------------------- |
| Agenda                                        | GescomClienteAlfa | Agendamento real do C1D1 (`Agenda.DhAgendaIniUTC`, via `Prescricao.IdAgenda`) — condição 1                     |
| Paciente                                      | GescomClienteAlfa | Dados do paciente (nome, nome social)                                                                          |
| Prescricao                                     | GescomClienteAlfa | Diagnóstico oncológico do paciente; data real de cada prescrição vinculada a protocolo                          |
| PrescricaoProtocolo                            | GescomClienteAlfa | Vínculo entre cada prescrição real e o protocolo/ciclo/sessão aplicados (`Ciclo`, `Sessao`) — base das condições 2 e 3 de `TreatmentPlanAdherenceCount()` |
| Protocolo                                      | GescomClienteAlfa | `ToleranciaEntreSessoes`/`ToleranciaEntreCiclos` (margens de tolerância, ver função abaixo)                    |
| ProtocoloGrupo                                 | GescomClienteAlfa | `SessoesSugerido` — string de dias sugeridos por ciclo (ex. "1,8,15"); o médico informa "é a sessão N do ciclo" e o sistema copia o N-ésimo valor para `PrescricaoProtocolo.Sessao` (fora deste endpoint) |
| PEP                                            | GescomClienteAlfa | Vínculo prontuário↔paciente (`IdPaciente`) — usado só para localizar o plano terapêutico do paciente (condição 1) |
| PEPProjetoTerapeutico                          | GescomClienteAlfa | Plano terapêutico do paciente, quando existe — condição 1 só se aplica se houver um para o mesmo protocolo     |
| PEPProjetoTerapeuticoProtocolo                 | GescomClienteAlfa | Vínculo entre o plano terapêutico e o protocolo (`IdProtocolo`) — usado para checar se existe plano para o protocolo da prescrição real |
| PEPProjetoTerapeuticoProtocoloProgramacao      | GescomClienteAlfa | `DataEstimada` do C1D1 planejado (`Ciclo=1`/`Sessao=1`) — comparada ao C1D1 real na condição 1                 |
| TermoTraducao                                  | IpTerminologia    | Tradução do modo de atendimento                                                                                |
| IpParametro                                    | GescomClienteAlfa | Definição dos parâmetros `TempoPacienteAtivoAgenda`/`TempoPacienteAtivoPrescricao` (janela de atividade, em dias) — `PatientInTreatmentCount()` |
| IpParametroChave                               | GescomClienteAlfa | Valor de cada parâmetro por clínica (`IdChave` = `Clinica.IdChClinica`, `Valor` em dias) — `PatientInTreatmentCount()` |

#### Mapa de campos

| Campo         | Tipo | Banco                         |
| :------------ | :--- | :---------------------------- |
| `inTreatment` | int  | PatientInTreatmentCount()     |
| `nonAdherent` | int  | TreatmentPlanAdherenceCount() |

#### Funções de cálculo

PatientInTreatmentCount() — calcula a quantidade de pacientes em tratamento do médico
- refere-se aos pacientes com agenda realizada e/ou prescrição registrada dentro da janela de atividade da clínica (RN-CM-024, padrão 180 dias, usado também quando o parâmetro não existe para a clínica)
- essa janela vem de dois parâmetros da clínica, em dias, que dizem o tempo máximo de inatividade para considerar o paciente inativo:
	- quanto às agendas: `IpParametro.TempoPacienteAtivoAgenda` -> `IpParametroChave.Valor` (dias)
	- quanto às prescrições: `IpParametro.TempoPacienteAtivoPrescricao` -> `IpParametroChave.Valor` (dias)
- Nas agendas obter as agendas com:
	- Situação 'V' e `Agenda.RemovidoEm IS NULL`; paciente com `Paciente.RemovidoEm IS NULL` (nas duas partes da consulta)
	- DhAgendaIniUTC > Hoje()-IpParametroChave.Valor (onde IpParametro=TempoPacienteAtivoAgenda)
- Nas prescrições obter as prescrições com:
	- Situação <> 'C' (canceladas — ver "Domínio: Situação da Prescrição" abaixo)
	- Data > Hoje()-IpParametroChave.Valor (onde IpParametro=TempoPacienteAtivoPrescricao)
* A query fica mais ou menos assim:
	`SELECT DISTINCT p.IdChProfissional, p.IdPaciente
	FROM paciente p INNER JOIN agenda a ON a.IdPaciente = p.IdPaciente AND a.IdChRecurso = p.IdChProfissional
	WHERE a.Situacao = 'V' AND a.RemovidoEm IS NULL AND p.RemovidoEm IS NULL AND a.DhAgendaIniUTC > dateadd(day,
		-COALESCE(
		  (SELECT ipc.Valor FROM IpParametroChave ipc
		   WHERE ipc.IdParametro = 'TempoPacienteAtivoAgenda' AND ipc.IdChave = {IdClinica}),
		  180),   -- default 180 dias se IpParametro/IpParametroChave não existir
		getdate()) -- janela de atividade das agendas, em dias
	UNION
	SELECT DISTINCT p.IdChProfissional , p.IdPaciente
	FROM Paciente p INNER JOIN Prescricao p2 on p2.IdPaciente = p.IdPaciente AND p.IdChProfissional = p2.IdChProfissionalMed
	WHERE p2.Situacao <> 'C' AND p.RemovidoEm IS NULL AND p2.[Data] > dateadd(day,
		-COALESCE(
		  (SELECT ipc.Valor FROM IpParametroChave ipc
		   WHERE ipc.IdParametro = 'TempoPacienteAtivoPrescricao' AND ipc.IdChave = {IdClinica}),
		  180),   -- default 180 dias se IpParametro/IpParametroChave não existir
		getdate()) -- janela de atividade das prescrições, em dias`

	**Clínica dos parâmetros (D-CM-063):** `{IdClinica}` é o `Clinica.IdChClinica` da clínica em que o médico está logado (cadeia descrita no início desta Seção, D-CM-065). O indicador conta pacientes do médico, que não pertencem a uma só clínica; valem os parâmetros da clínica logada.

**Comportamento se o parâmetro não existir para a clínica:** o sistema assume 180 dias (RN-CM-024) — ver `COALESCE` na query acima. Os dois parâmetros já existem em produção; seed de referência, com 180 dias por clínica, em `Infra-estrutura/(BD-M) Dados incluídos obrigatórios.md`.
Então conta a quantidade de pacientes do profissional logado.

TreatmentPlanAdherenceCount() — calcula os pacientes cujo tratamento se desvia do planejado no protocolo. A base é a prescrição real: entra na contagem todo paciente do médico com ao menos uma prescrição vinculada a protocolo (`PrescricaoProtocolo`) — não depende de um plano terapêutico formal (`PEPProjetoTerapeutico`) ter sido criado, já que nem todo médico chega a criar um. A única exceção é a condição 1, abaixo, que só se aplica quando um plano existe. Um paciente conta como não aderente quando QUALQUER UMA das três condições abaixo é verdadeira (lógica OU — basta uma delas estar fora do parametrizado):

**Exclusão lógica:** a contagem ignora pacientes com `Paciente.RemovidoEm` preenchido, planos terapêuticos cujo `PEP` tem `RemovidoEm` preenchido e, no C1D1 real, agendas (via `Prescricao.IdAgenda`) com `Agenda.RemovidoEm` preenchido.

**Âncora de cada ciclo:** para cada Ciclo N de um paciente/protocolo, a "âncora do ciclo" é a linha de `PrescricaoProtocolo` daquele `Ciclo` com o menor valor de `Sessao` (a 1ª sessão do ciclo, conforme `ProtocoloGrupo.SessoesSugerido`), unida a `Prescricao` (via `IdPrescricao`) para obter a data real. A âncora do Ciclo 1 é o "C1D1" — usado sozinho na condição 1. A condição 3 abaixo usa a âncora de CADA ciclo, não só a do Ciclo 1 (a condição 2 não depende de âncora nenhuma).

- **Condição 1 — C1D1 real diverge do C1D1 planejado (só quando há plano terapêutico):** aplica-se apenas se existir um plano terapêutico do paciente para o mesmo protocolo da prescrição real — isto é, uma linha de `PEPProjetoTerapeuticoProtocolo` cujo `IdProtocolo` bate com o `IdProtocolo` de `PrescricaoProtocolo` e cujo `IdPEP` pertence ao `PEP` do paciente (`PEP.IdPaciente`). Quando não existe esse plano para o protocolo, a condição simplesmente não se aplica ao paciente (não pesa nem a favor nem contra a adesão). Quando existe: compara-se a data agendada do C1D1 real (`Agenda.DhAgendaIniUTC`, via `Prescricao.IdAgenda` do C1D1 de `PrescricaoProtocolo`) com a data estimada do C1D1 do plano (`PEPProjetoTerapeuticoProtocoloProgramacao.DataEstimada` na linha `Ciclo=1`/`Sessao=1` daquele `PEPProjetoTerapeuticoProtocolo`); diferença maior que `Protocolo.ToleranciaEntreSessoes` conta como não aderente.

- **Condição 2 — prescrição esperada não realizada:** para qualquer linha de `PrescricaoProtocolo` do paciente cuja `Prescricao.Data` já passou e cuja `Prescricao.Situacao` está em `('P','G','A')` — ou seja, a sessão estava agendada para uma data que já passou, mas a prescrição segue em um status de "ainda por realizar" (Confirma Prescrição, Gerada ou Admitida — ver domínio abaixo), sem nunca ter sido efetivamente concluída (`Q`/`F`) — conta como não aderente. **Não conta uma sessão cancelada (`C`)** — cancelamento é uma decisão deliberada, não uma omissão, e por isso foi excluído desta condição nesta versão (até então, `C` entrava indevidamente na lista). Mecânica quanto ao restante inalterada desde a v4.30 — não dependia de plano terapêutico nem antes.

- **Condição 3 — sessão realizada diverge da data esperada:** tem duas partes, conforme a linha de `PrescricaoProtocolo` seja ou não a âncora do seu próprio ciclo:
  - **3a — sessão comum (não é a âncora do ciclo):** `diasDecorridos = Prescricao.Data (da linha) − Prescricao.Data (âncora do MESMO Ciclo)`; compara-se diretamente ao valor de `Sessao` da própria linha (o dia bruto de `SessoesSugerido` daquela sessão): se `ABS(diasDecorridos − Sessao)` for maior que `Protocolo.ToleranciaEntreSessoes`, conta como não aderente.
  - **3b — início de um novo ciclo (a linha É a âncora do seu ciclo, e o ciclo não é o 1º):** `diasDecorridos = Prescricao.Data (âncora do Ciclo N) − Prescricao.Data (âncora do Ciclo N−1)`; compara-se a `Protocolo.IntervaloCiclosEmDias`: se `ABS(diasDecorridos − IntervaloCiclosEmDias)` for maior que `Protocolo.ToleranciaEntreCiclos`, conta como não aderente. Não se aplica à âncora do Ciclo 1 (C1D1), já coberta pela condição 1.

  > Exemplo: `SessoesSugerido = "1,8,15"`, `IntervaloCiclosEmDias = 21`. C1D1 (Ciclo 1, âncora) em 01/01. Ciclo 1/Sessao 8 (3a) realizada em 09/01: `diasDecorridos = 8`, `ABS(8−8) = 0` → aderente. Âncora do Ciclo 2 (3b) realizada em 23/01: `diasDecorridos (vs. âncora do Ciclo 1) = 22`, `ABS(22−21) = 1` → compara com `ToleranciaEntreCiclos`. Ciclo 2/Sessao 8 (3a) realizada em 31/01: `diasDecorridos (vs. âncora do Ciclo 2 = 23/01) = 8`, `ABS(8−8) = 0` → aderente.

> **Como `Sessao` é definida (fora do escopo deste endpoint, documentado aqui só como contexto):** ao criar uma prescrição, o médico informa "é a sessão N do ciclo X" de um protocolo; o sistema busca o N-ésimo valor de `ProtocoloGrupo.SessoesSugerido` (ex.: N=2 em `"1,8,15"` → valor 8) e grava esse valor bruto em `PrescricaoProtocolo.Sessao` (e X em `Ciclo`) — por isso `Sessao` reinicia a cada ciclo, não é cumulativo desde o C1D1. Existe também um processo de criação em massa: ao definir o protocolo no plano terapêutico com o número de ciclos do tratamento, o sistema gera de uma vez todas as prescrições cruzando ciclo e sessão — cada uma com sua própria configuração de medicamentos/procedimentos (não é atribuição meramente classificatória).

> **[PROPOSTA] Colunas novas em `Protocolo` (GescomClienteAlfa):** `ToleranciaEntreSessoes` e `ToleranciaEntreCiclos` (int, dias de tolerância — a primeira para desvios dentro do mesmo ciclo, a segunda para desvios que atravessam fronteira de ciclo). Substituem a proposta anterior de uma coluna única (`ToleranciaDiasAdesao`, v4.30) — são dois parâmetros distintos, não um só. Não existem hoje na Biblioteca de Schema (DDL) nem no `EsquemaGemed21.csv` — a criar antes da implementação deste KPI.

#### Domínio: Situação da Prescrição (`Prescricao.Situacao`, GescomClienteAlfa)

`Situacao` é `char(1) NOT NULL` em `Prescricao` (confirmado na Biblioteca de Schema — DDL); a DDL não traz um `CHECK` explícito listando os valores permitidos, então o domínio abaixo foi informado pelo negócio, fora da DDL (origem no Histórico de Versões). É um fluxo próprio da prescrição, distinto da situação da agenda associada (domínio `SituacaoAgenda`, já mapeado neste documento) — uma prescrição pode estar numa situação diferente da agenda que a originou, e as duas não avançam necessariamente juntas.

| Situação | Nome | Descrição |
|---|---|---|
| `P` | Confirma Prescrição | Precede a situação `G`; ocorre quando um agendamento é feito com a opção "Sem Prescrição" — o Gemed cria junto a prescrição nesta situação (com o protocolo e CID do agendamento), como pendência para o médico revisar e confirmar medicamentos/doses. |
| `G` | Gerada | Situação logo após o médico salvar (ou confirmar, quando veio de agenda "Sem Prescrição") a prescrição; permanece "Gerada" mesmo depois de agendada. |
| `A` | Admitida | A agenda vinculada à prescrição teve a recepção confirmada (paciente recepcionado). Prescrição fica disponível para a enfermagem no processo de Confirmação de Quimioterapia (ou Central de Atendimentos). |
| `Q` | Confirmada/Finalizada | Informada pela enfermagem após concluída a infusão/quimioterapia (atendimento finalizado). Enquanto não confirmada, a prescrição não cai no faturamento. Nesta situação, fica disponível tanto para a enfermagem (pode desfazer a confirmação, se necessário) quanto para o faturamento (Consulta de Folha de Gastos, aba "Prescrições para Faturamento"). |
| `F` | Faturada | Quando o faturamento termina de lançar gastos e ajustes na fatura (fatura marcada como "Fecha Fatura"). |
| `O` | Aguardando Orçamento | Disparada pelo botão de solicitar orçamento no prontuário; prescrições nesta situação entram no processo de Orçamento para o faturamento. |
| `E` | Atendimento Externo | Substitui a situação `G` logo após o médico salvar a prescrição, quando o convênio do paciente está configurado para permitir essa opção. Indica que o paciente fará o tratamento/quimioterapia fora da clínica — a prescrição não segue para as demais situações (não fica disponível para agendamento, enfermagem ou faturamento). |
| `C` | Cancelada | Cancelável pelo prontuário (que tem filtro próprio para consultar prescrições canceladas) ou ao cancelar a agenda do paciente. |

Motivador de documentar aqui: o KPI "Em Tratamento" (`PatientInTreatmentCount()`, acima) já filtra prescrições por `Situacao <> 'C'`, e `TreatmentPlanAdherenceCount()` filtra por `Situacao IN ('P','G','A')` (excluindo `C`, cancelada, desde esta versão) para detectar prescrição passada não realizada — este domínio documenta o que cada um desses códigos significa, em vez de deixá-los como letras sem contexto no meio das funções de cálculo.

#### Filtros do endpoint

- `Paciente.IdChProfissional={IdChProfissional do médico logado}` - Pacientes em que o médico é o médico assistente

---

### 4.6 Endpoint: GET /api/v1/cockpit/doctor/kpis/prescription-alerts

Retorna o indicador de Alertas de Prescrição (urgentes, a revisar, a assinar nos próximos 7 dias).

#### Tabelas envolvidas

| Tabela               | Banco             | Função no endpoint                                                        |
| --------------------- | ------------------ | ---------------------------------------------------------------------------- |
| Prescricao             | GescomClienteAlfa | Tabela principal — prescrições em `Situacao` P/G/A/O (RN-CM-020), médico prescritor |
| PrescricaoAssinatura   | GescomClienteAlfa | Histórico de assinaturas/revisões — usada para determinar se a prescrição está revisada |
| Agenda                 | GescomClienteAlfa | Data/hora agendada da aplicação (via `Prescricao.IdAgenda`)                   |
| Paciente               | GescomClienteAlfa | Médico assistente (`IdChProfissional`) e exclusão lógica                      |
| IpParametro            | GescomClienteAlfa | Definição dos parâmetros `NumeroDiasRevisaoPrescricoes` (já existente no sistema atual) e `PrescricaoTempoAssinatura` (novo), ambos em dias |
| IpParametroChave       | GescomClienteAlfa | Valor de cada parâmetro por clínica (`IdChave` = `Clinica.IdChClinica`) |
| Clinica                | GescomClienteAlfa | Uma linha por clínica; as de `{ClinicasDoMedico}` (D-CM-066) são a base de `{LimitesPorClinica}` (E-CM-032); `Agenda.IdChClinica` aponta para ela |
| ClienteEmpresa         | IpSeguranca       | Fuso (`FusoHorarioOficial`, IANA) da clínica em que o usuário está logado (D-CM-062) |

#### Mapa de campos

| Campo              | Tipo | Banco                          | Observação |
| :------------------ | :--- | :------------------------------ | :--------- |
| `urgent`            | int  | PrescriptionUrgentCount()       | |
| `toReview`          | int  | PrescriptionToReviewCount()     | |
| `toSignNext7Days`   | int  | PrescriptionToSignNext7DaysCount() | nome mantido por compatibilidade; conta dentro da janela da clínica |
| `toSignWindowDays`  | int  | `COALESCE((SELECT TRY_CONVERT(int, ipc.Valor) FROM IpParametroChave ipc WHERE ipc.IdParametro = 'PrescricaoTempoAssinatura' AND ipc.IdChave = {IdClinica}), 7)` | janela de assinatura em dias, da clínica em que o médico está logado (D-CM-063; as clínicas do cliente podem ter janelas diferentes e o campo é um só), com `{IdClinica}` = `Clinica.IdChClinica` da clínica logada; o front monta o rótulo "p/ assinar (próx. N dias)" |

#### Funções de cálculo

PrescriptionToReviewCount() — conta as prescrições "a revisar" do médico logado (RN-CM-020, Precedência 2), restritas a prescrições em que o médico logado é o prescritor ou o médico assistente do paciente. A regra segue 2 níveis de precedência, nunca avaliados ao mesmo tempo — **desde a v4.39, sem nenhum conceito de validade/vencimento de assinatura** (abolido; até a v4.38, uma assinatura "vencida" — mais de `PrescricaoValidadeAssinatura` dias — devolvia a prescrição para "a assinar"; essa verificação não existe mais):

- **Precedência 1 (RN-CM-020) — já foi assinada, em qualquer momento:** verifica 2 fontes, por compatibilidade com prescrições gravadas antes da adoção da tabela de assinaturas atual (RN-CM-029) — `PrescricaoAssinatura` com `TipoAssinatura = 'A'` (fonte moderna), ou o par legado `Prescricao.DataAssinaturaPrescricao`/`Prescricao.IdChProfissionalAssinou` (mesmo evento de assinatura — `IdChProfissionalAssinou` é quem assinou, `DataAssinaturaPrescricao` é quando). Basta a existência de qualquer uma das 2 — não há mais cálculo de idade/recência. Uma prescrição sem nenhuma das 2 fontes não é "a revisar" — é "a assinar" (RN-CM-020, Precedência 1; ver `PrescriptionToSignNext7DaysCount()` abaixo).
- **Precedência 2 (RN-CM-020) — aplicação dentro da janela de antecedência de revisão (`NumeroDiasRevisaoPrescricoes`, padrão 1 dia), sem confirmação de revisão:** só avaliada quando a Precedência 1 acima é satisfeita. Reaproveita `PrescricaoAssinatura` de novo — um registro com `TipoAssinatura = 'R'` é uma confirmação de revisão ("Marcar como Revisada"). Diferente da assinatura, essa confirmação não tem prazo de validade: se existir QUALQUER registro `R` para a prescrição, a revisão já está feita e a prescrição não conta como "a revisar" (nem volta a ser "a assinar" — simplesmente não aparece nesta contagem nem na de `PrescriptionToSignNext7DaysCount()`), para sempre.
- **Exceção (a) — assinatura dentro da janela de revisão (D-CM-040, E-CM-019):** a prescrição não conta como "a revisar" quando existe assinatura do médico (`PrescricaoAssinatura` com `TipoAssinatura = 'A'`, ou `Prescricao.DataAssinaturaPrescricao`) com data igual ou posterior à meia-noite, no fuso da clínica logada (D-CM-062), do dia da aplicação menos N dias — isto é, feita depois de a janela de revisão daquela prescrição ter aberto. A assinatura dispensa a revisão. Com N = 1 e aplicação no dia 11, vale assinatura de 10 às 00:00 em diante; assinatura do dia 9 não dispensa. `PrescricaoAssinatura.Data` é `datetime` sem fuso e é tratada como hora local da clínica; essa leitura, e a conversão no SQL, são escolha do Designer a confirmar (E-CM-019).
- **Exceção (b) — administração iniciada (D-CM-039, E-CM-018):** a prescrição não conta como "a revisar" quando `Agenda.DhInfusaoIniUTC` (agenda da prescrição) está preenchido. Vale só para "a revisar"; "a assinar" com administração iniciada continua na lista (ver `PrescriptionToSignNext7DaysCount()`).
- **Pré-requisito — `Prescricao.Situacao` "em andamento":** só entram no cálculo prescrições com `Situacao` em `P`/`G`/`A`/`O` (domínio completo na Seção 4.5, "Domínio: Situação da Prescrição"). Motivo: sem esse filtro, e sem mais o mecanismo de expiração para "limpar" o histórico, prescrições já `Q` (finalizada) ou `F` (faturada) há muito tempo, mas nunca formalmente revisadas, ficariam acumulando na lista "a revisar" indefinidamente — achado confirmado contra a massa de referência no momento da regra; os números atuais da massa estão nos Cenários de Teste.

A janela que dispara "a revisar" (RN-CM-020) — descrita na Seção 1 como "dias de calendário" (padrão: 1 dia) — não é um valor fixo no código: é um parâmetro configurável por clínica, mesmo padrão já usado em 4.5 (`TempoPacienteAtivoAgenda`/`TempoPacienteAtivoPrescricao`): `IpParametro.NumeroDiasRevisaoPrescricoes` → `IpParametroChave.Valor` (dias). A janela é em dias de calendário no fuso da clínica em que o usuário está logado (D-CM-023, D-CM-062): com N dias e hoje sendo o dia 10, entram as aplicações de todos os dias até o fim do dia 10+N (N = 1: dias 10 e 11, até 23:59:59), mais as anteriores — não há limite para trás. Um dia, portanto, não são 24 horas. Ver "Parâmetro de configuração — NumeroDiasRevisaoPrescricoes" logo abaixo. Este parâmetro só entra na Precedência 2 — a janela de "a assinar" usa o parâmetro distinto `PrescricaoTempoAssinatura` (também em dias, ver subseção abaixo).

```sql
SELECT COUNT(*)
FROM Prescricao p
INNER JOIN Agenda a ON a.IdAgenda = p.IdAgenda AND a.RemovidoEm IS NULL   -- exclusão lógica da agenda
INNER JOIN Paciente pc ON pc.IdPaciente = p.IdPaciente AND pc.RemovidoEm IS NULL   -- exclusão lógica do paciente
INNER JOIN {LimitesPorClinica} lc ON lc.IdChClinica = a.IdChClinica   -- janelas da clínica de cada prescrição e filtro das clínicas do médico (D-CM-060, D-CM-062, D-CM-066, E-CM-032); ver "Cálculo do limite"
WHERE p.Situacao IN ('P','G','A','O')                                -- "em andamento" (RN-CM-020) — não Q/F/E/C
  AND (
        EXISTS (SELECT 1 FROM PrescricaoAssinatura pa
                WHERE pa.IdPrescricao = p.IdPrescricao AND pa.TipoAssinatura = 'A')
        OR p.DataAssinaturaPrescricao IS NOT NULL                    -- compatibilidade legada, RN-CM-029 (par com IdChProfissionalAssinou — mesmo evento de assinatura)
        OR p.IdChProfissionalAssinou IS NOT NULL                     -- redundante com a condição acima na prática (mesmo evento, sempre preenchidos juntos) — mantido por segurança contra dado legado incompleto
      )                                                              -- Precedência 1 (RN-CM-020): já foi assinada, por qualquer uma das 2 fontes (moderna + par legado) — sem cálculo de idade/vencimento
  AND a.DhAgendaIniUTC < lc.LimiteRevisaoUTC                        -- Precedência 2: até o fim do dia (hoje + N), sem piso, com N da clínica da prescrição (D-CM-060)
                                                                     -- lc.LimiteRevisaoUTC = meia-noite local de (hoje + N + 1) no fuso da clínica em que o usuário está logado, em UTC, calculada pela aplicação para cada clínica (D-CM-062, E-CM-032)
                                                                     -- lc.N = COALESCE((SELECT TRY_CONVERT(int, ipc.Valor) FROM IpParametroChave ipc
                                                                     --               WHERE ipc.IdParametro = 'NumeroDiasRevisaoPrescricoes' AND ipc.IdChave = lc.IdChClinica), 1)  -- default 1 dia (padrão do Gemed 2.1)
                                                                     -- {InicioJanelaRevisaoLocal} = DATEADD(DAY, -lc.N, CAST(<DhAgendaIniUTC no fuso da clínica da prescrição> AS date)): meia-noite local do dia da aplicação menos N dias.
                                                                     --   Ex.: CAST(a.DhAgendaIniUTC AT TIME ZONE 'UTC' AT TIME ZONE {FusoWindowsLogin} AS date), com {FusoWindowsLogin} = nome Windows do fuso IANA da clínica logada (`ClienteEmpresa.FusoHorarioOficial`), traduzido pela aplicação (E-CM-019, D-CM-062). Valor calculado POR LINHA (depende da `DhAgendaIniUTC` de cada prescrição e do N da clínica dela)
  AND NOT EXISTS (
        SELECT 1 FROM PrescricaoAssinatura pa2
        WHERE pa2.IdPrescricao = p.IdPrescricao
          AND pa2.TipoAssinatura = 'R'                                -- Precedência 2: nenhuma confirmação de revisão jamais registrada (sem prazo de validade)
      )
  AND NOT EXISTS (
        SELECT 1 FROM PrescricaoAssinatura pa3
        WHERE pa3.IdPrescricao = p.IdPrescricao
          AND pa3.TipoAssinatura = 'A'
          AND pa3.Data >= {InicioJanelaRevisaoLocal}                  -- Exceção (a): assinatura feita depois de a janela de revisão abrir dispensa a revisão (D-CM-040, E-CM-019)
      )
  AND (p.DataAssinaturaPrescricao IS NULL OR p.DataAssinaturaPrescricao < {InicioJanelaRevisaoLocal})   -- idem, para o par legado (RN-CM-029)
  AND a.DhInfusaoIniUTC IS NULL                                       -- Exceção (b): administração iniciada retira só "a revisar" (D-CM-039, E-CM-018)
  AND (p.IdChProfissionalMed = {IdChProfissional do médico logado}
       OR EXISTS (SELECT 1 FROM Paciente pac WHERE pac.IdPaciente = p.IdPaciente
                  AND pac.IdChProfissional = {IdChProfissional do médico logado}
                  AND pac.RemovidoEm IS NULL))
```

> Nota: a cláusula de escopo acima é fechada (só prescritor OU assistente) — o KPI é uma contagem pessoal de "quanto precisa da sua atenção", diferente do toggle "Todos" do painel (Seção 4.7), que deliberadamente amplia para qualquer médico das clínicas em que o médico logado atende porque ali o médico está decidindo ajudar a revisar colegas. Não existe (nem deveria existir) uma permissão RBAC de "revisor geral" que amplie o KPI — isso inflaria a contagem pessoal com prescrições de pacientes sem nenhum vínculo com o médico logado.

#### Parâmetro de configuração — NumeroDiasRevisaoPrescricoes

A janela de disparo de "a revisar" usa o parâmetro `NumeroDiasRevisaoPrescricoes`, que já existe no sistema atual (catálogo de parâmetros `ParametrosGemedLegado.csv`: "Informa o número máximo de dias a frente que o revisor visualiza prescrições para revisar", tipo numérico) e é usado pela Central do Médico do sistema atual. Não é uma tabela nem um parâmetro novo: o valor por clínica fica em `IpParametroChave` (`IdParametro` = `NumeroDiasRevisaoPrescricoes`, `IdChave` = `{Id da Clínica}`, `Valor` = número de dias, `IdChave2` = `NULL`), com a definição em `IpParametro`.

**Comportamento se o parâmetro não existir:** se a linha em `IpParametroChave` (ou em `IpParametro`) não existir para a clínica, o sistema assume 1 dia (ver `COALESCE` na query acima). O código do sistema atual assume 2 dias nessa situação; o Gemed 2.1 define 1 dia como padrão próprio (D-CM-006), valendo outro valor só se o parâmetro da clínica o definir. Com o padrão, uma prescrição assinada e ainda não revisada aparece em "a revisar" a partir da meia-noite do dia anterior à aplicação (no padrão de 1 dia), isto é, com antecedência de cerca de 24 a 48 horas conforme a hora. Se o valor gravado não for numérico, `TRY_CONVERT` o descarta e vale o padrão de 1 dia.

**Cálculo do limite (por clínica; D-CM-060, D-CM-062, D-CM-066, E-CM-032):** o médico vê as prescrições das clínicas em que atende, e cada clínica tem os próprios parâmetros. Por isso a aplicação monta, a cada chamada, a tabela `{LimitesPorClinica}` com uma linha para **cada clínica de `{ClinicasDoMedico}`** (toda `Agenda.IdChClinica` aponta para `Clinica`). O `INNER JOIN` com essa tabela é também o filtro de clínicas: prescrição de clínica em que o médico não atende não tem linha e não entra (D-CM-066). Cada linha traz: `IdChClinica`, `N` (`NumeroDiasRevisaoPrescricoes`, padrão 1), `S` (`PrescricaoTempoAssinatura`, padrão 7) e os limites `LimiteRevisaoUTC` e `LimiteAssinaturaUTC` = meia-noite local do dia (hoje + N + 1) ou (hoje + S + 1), **no fuso da clínica em que o usuário está logado** (`ClienteEmpresa.FusoHorarioOficial`, IANA), convertida para UTC; o "hoje" também é o da clínica logada (D-CM-062), para o médico ver um só dia em toda a tela. A prescrição é ligada à linha da clínica dela por `Agenda.IdChClinica`: só N e S vêm da clínica da prescrição, o fuso é um só. A condição é `Agenda.DhAgendaIniUTC < lc.LimiteRevisaoUTC` (ou `lc.LimiteAssinaturaUTC`). Os ids de clínica seguem a cadeia descrita no início desta Seção (D-CM-065). A forma de entregar a tabela ao SQL (parâmetro de tabela, tabela temporária ou `VALUES`) é escolha de implementação e não muda a regra. Os limites são calculados fora do SQL para respeitar mudança de horário de verão. **[PROPOSTA]** O desenho da tabela é do Designer (E-CM-032). Mesma ideia do sistema atual (dias de calendário, de hoje até hoje + N); a Central não usa mais janela contínua em horas (D-CM-023).

**Não criar `PrescricaoTempoRevisao`:** o parâmetro existente `NumeroDiasRevisaoPrescricoes` cobre a necessidade; nenhum seed novo é necessário para a janela de revisão.

#### Parâmetro de configuração — PrescricaoTempoAssinatura

**[PROPOSTA]** A janela de antecedência de "a assinar" (RN-CM-020, Precedência 1 — "aplicação hoje ou nos próximos 7 dias de calendário", D-CM-037) precisa existir como parâmetro real de banco, mesmo padrão do parâmetro acima — `IpParametro`/`IpParametroChave`, 2 linhas novas de dado (seed), não uma mudança de schema. **Retira e substitui `PrescricaoValidadeAssinatura`** (criado na v4.30 deste documento para a janela de validade/vencimento de assinatura, hoje abolida — RN-CM-020, v4.39 do 01-Definição): como `PrescricaoValidadeAssinatura` nunca chegou a ser aplicado em produção em nenhum cliente (**[PROPOSTA]** em todo o histórico deste documento), a substituição não tem custo de migração — não existe seed a desfazer. Nome escolhido pelo Designer (convenção "Tempo" + conceito), a confirmar:

`IpParametro` (linha nova):

| Campo | Valor |
| --- | --- |
| `IdParametro` | `PrescricaoTempoAssinatura` |
| `Descricao` | `Dias de calendário a partir de hoje em que prescrição nunca assinada aparece em "a assinar"` |
| `Dominio` | `NULL` |
| `UsuarioControla` | `S` |
| `Tipo` | `N` |

`IpParametroChave` (linha nova, valor por clínica — exemplo com o padrão-sugerido de 7 dias, correspondente à regra da Seção 1):

| Campo | Valor |
| --- | --- |
| `IdParametro` | `PrescricaoTempoAssinatura` |
| `IdChave` | `{Id da Clínica}` |
| `Valor` | `7` |
| `IdChave2` | `NULL` |

**Comportamento se o parâmetro não existir:** se a linha em `IpParametro` ou em `IpParametroChave` não existir para a clínica, o sistema assume 7 dias como default — ver o `COALESCE` do SQL de `PrescriptionToSignNext7DaysCount()` abaixo. O seed abaixo (Seção "Seeds da Funcionalidade") deve ainda assim ser aplicado em todo cliente, pelo mesmo motivo do parâmetro anterior.

**Atenção Dev/DevOps:** mesma observação do parâmetro anterior — dado novo de configuração, não migration de schema; incluir na documentação de deploy/seed de cada cliente. Ver "Seeds da Funcionalidade" abaixo. **Não criar** parâmetro de validade da assinatura (`PrescricaoValidadeAssinatura`): o conceito de assinatura vencida não existe (RN-CM-020).

PrescriptionUrgentCount() — soma duas contagens, ambas restritas ao médico logado (prescritor ou assistente) e filtradas por `hoursRemaining < 12` (RN-CM-011): (a) `PrescriptionToReviewCount()` restrita a `hoursRemaining < 12`; (b) `PrescriptionToSignNext7DaysCount()` (ver abaixo — já cobre as 2 fontes de RN-CM-029, RN-CM-020 Precedência 1, sem mais nenhuma condição de assinatura vencida) restrita a `hoursRemaining < 12`; o corte do limite de assinatura da clínica (`lc.LimiteAssinaturaUTC`) já cobre qualquer aplicação a menos de 12h (supondo S ≥ 1, o que o parâmetro não garante: com S = 0 uma aplicação a menos de 12h, mas no dia seguinte, ficaria fora; se o valor 0 ou negativo for possível, validar o mínimo de 1 dia ou aceitar a divergência), então o KPI urgente e a lista de 4.7 contam as mesmas prescrições. `hoursRemaining` = `DATEDIFF(MINUTE, SYSDATETIMEOFFSET(), a.DhAgendaIniUTC) / 60` (inteiro truncado, como em 4.7); a classificação "urgente" usa os minutos (`< 720`), sem arredondar.

PrescriptionToSignNext7DaysCount() — prescrições "a assinar" (RN-CM-020, Precedência 1) em que o médico logado é o prescritor ou o médico assistente do paciente (`Prescricao.IdChProfissionalMed = {médico logado}` OU `Paciente.IdChProfissional = {médico logado}` — mesmo critério de "Meus" usado por `PrescriptionToReviewCount()`, ver 4.7), com `Agenda.DhAgendaIniUTC` até o fim do dia (hoje + X) no fuso da clínica logada, em dias de calendário (D-CM-037) — `lc.LimiteAssinaturaUTC` = meia-noite local de (hoje + X + 1) no fuso da clínica logada, em UTC, calculada pela aplicação por clínica (ver "Cálculo do limite" acima) (sem piso — aplicações já atrasadas continuam elegíveis indefinidamente, mesma regra de `PrescriptionToReviewCount()`), X = `PrescricaoTempoAssinatura` da clínica da prescrição (parâmetro novo, default 7 dias — ver subseção acima; nome mantido "Next7Days" por compatibilidade com o contrato de API já existente, mesmo o valor agora sendo configurável). `Prescricao.Situacao IN ('P','G','A','O')` (RN-CM-020), `Agenda.RemovidoEm IS NULL` e `Paciente.RemovidoEm IS NULL`, e nenhuma das 2 fontes de assinatura existe (nem `PrescricaoAssinatura.TipoAssinatura = 'A'`, nem o par legado `Prescricao.DataAssinaturaPrescricao`/`IdChProfissionalAssinou` — RN-CM-029). **Não há mais ramo (b) de "assinatura vencida que volta a ser a assinar"** — abolido na v4.39; uma vez assinada, a prescrição nunca mais aparece aqui, só em `PrescriptionToReviewCount()` (ou em nenhuma das duas, se já revisada, assinada depois de a janela de revisão abrir, com administração iniciada ou fora da janela de revisão). Uma prescrição "a assinar" com administração iniciada (`Agenda.DhInfusaoIniUTC` preenchido) **continua** nesta contagem: sem assinatura em procedimento é falha grave de processo e deve ficar visível (D-CM-039, E-CM-018).


SQL de referência de `PrescriptionToSignNext7DaysCount()` (montado a partir das regras acima; as condições de assinatura são as mesmas do SQL de `PrescriptionToReviewCount()`, negadas):

```sql
SELECT COUNT(*)
FROM Prescricao p
INNER JOIN Agenda a ON a.IdAgenda = p.IdAgenda AND a.RemovidoEm IS NULL
INNER JOIN Paciente pc ON pc.IdPaciente = p.IdPaciente AND pc.RemovidoEm IS NULL
INNER JOIN {LimitesPorClinica} lc ON lc.IdChClinica = a.IdChClinica   -- janelas da clínica de cada prescrição e filtro das clínicas do médico (D-CM-060, D-CM-062, D-CM-066, E-CM-032)
WHERE p.Situacao IN ('P','G','A','O')
  AND NOT EXISTS (SELECT 1 FROM PrescricaoAssinatura pa
                  WHERE pa.IdPrescricao = p.IdPrescricao AND pa.TipoAssinatura = 'A')   -- fonte moderna
  AND p.DataAssinaturaPrescricao IS NULL                                                -- par legado (RN-CM-029)
  AND p.IdChProfissionalAssinou IS NULL
  AND a.DhAgendaIniUTC < lc.LimiteAssinaturaUTC                       -- até o fim do dia (hoje + S), sem piso, com S da clínica da prescrição: já atrasadas continuam elegíveis (D-CM-037, D-CM-060)
                                                                         -- lc.S = COALESCE((SELECT TRY_CONVERT(int, ipc.Valor) FROM IpParametroChave ipc
                                                                         --               WHERE ipc.IdParametro = 'PrescricaoTempoAssinatura' AND ipc.IdChave = lc.IdChClinica), 7)  -- default 7 dias
                                                                         -- lc.LimiteAssinaturaUTC = meia-noite local de (hoje + S + 1) no fuso da clínica em que o usuário está logado, em UTC (D-CM-062, E-CM-032)
                                                                         -- sem filtro de DhInfusaoIniUTC: "a assinar" com administração iniciada continua contada (D-CM-039)
  AND (p.IdChProfissionalMed = {IdChProfissional do médico logado}
       OR pc.IdChProfissional = {IdChProfissional do médico logado})                    -- prescritor ou médico assistente (v4.46)
```

#### Filtros do endpoint

- Escopo do médico logado, fechado nas 3 métricas: prescritor (`Prescricao.IdChProfissionalMed`) OU médico assistente do paciente (`Paciente.IdChProfissional`) para `toReview` e a parcela de revisão de `urgent`; o mesmo critério (prescritor OU médico assistente) vale para `toSignNext7Days` e para a parcela de assinatura de `urgent`, desde a v4.46. O KPI nunca inclui prescrições de outros médicos sem esse vínculo — ver nota acima sobre a diferença em relação ao toggle "Todos" do painel (Seção 4.7).

---

### 4.7 Endpoint: GET /api/v1/cockpit/doctor/prescriptions

Retorna a lista de prescrições pendentes (assinatura e revisão) do médico logado, para o painel e para a tela dedicada.

#### Tabelas envolvidas

| Tabela               | Banco             | Função no endpoint                                          |
| --------------------- | ------------------ | -------------------------------------------------------------- |
| Prescricao             | GescomClienteAlfa | Tabela principal — versão ativa (`Situacao <> 'C'`); `Identificacao` já traz protocolo + ciclo/sessão formatados |
| PrescricaoAssinatura   | GescomClienteAlfa | Determina se a prescrição está assinada e/ou revisada (ver 4.6) |
| Paciente               | GescomClienteAlfa | Nome social/civil do paciente e médico assistente               |
| Agenda                 | GescomClienteAlfa | Data/hora agendada da aplicação e clínica (`IdChClinica`)       |
| Chave                  | GescomClienteAlfa | Nome curto da clínica (campo `clinic`)                           |

#### Domínio — PrescricaoAssinatura.TipoAssinatura

| Valor | Significado                                                            |
| ----- | ------------------------------------------------------------------------ |
| `A`   | Assinatura do médico (assinatura original da prescrição ou de uma nova versão gerada por edição) |
| `R`   | Confirmação de revisão sem alteração da prescrição ("Marcar como Revisada", RN-CM-020 Precedência 2) |

> Nota: o esquema também sugere os tipos `F` (farmácia) e outros, usados por outras funcionalidades fora do escopo do Cockpit — não detalhados aqui.

> Nota — compatibilidade legada (RN-CM-029): para prescrições gravadas antes da adoção desta tabela, `Prescricao.DataAssinaturaPrescricao` (a data) e `Prescricao.IdChProfissionalAssinou` (o profissional) formam um par — o mesmo evento legado de assinatura, não duas fontes independentes; juntos, valem como se fosse um registro `TipoAssinatura = 'A'` em `PrescricaoAssinatura`, mas nenhum dos dois gera uma linha real ali — são lidos diretamente de `Prescricao` nas queries de 4.6. Desde a v4.39 (01-Definição), nenhuma das 2 fontes (moderna + par legado) precisa de uma data para calcular idade/vencimento — a presença de qualquer uma já basta (RN-CM-020 não usa mais o conceito de assinatura vencida).

#### Mapa de campos

| Campo                        | Tipo    | Banco | Observação |
| :----------------------------- | :------ | :---- | :--------- |
| `id`                           | int     | Prescricao.IdPrescricao | identifica diretamente a versão ativa — não há campo de número de versão a considerar à parte |
| `clinic`                       | string \| null | `Prescricao.IdAgenda` → `Agenda.IdChClinica` → `Chave.Descricao` | Nome curto da clínica da agenda da prescrição; `null` quando o médico atende em uma só clínica, igual ao `clinic` de 4.1 (RN-CM-032, E-CM-028, D-CM-066) |
| `patient.id`                   | int     | Prescricao.IdPaciente | |
| `patient.preferredName`        | string  | Paciente.NomeSocial | mesmo padrão de `appointments` (4.1) e `procedures` (4.3) — enviado sempre, mesmo vazio, para o front sinalizar nome social ao médico |
| `patient.legalName`            | string  | Paciente.Nome | enviado sempre, junto com `preferredName` |
| `prescriptionCode`             | string  | Prescricao.Identificacao | substitui os antigos `protocol`/`cycle` — já vem formatado do banco (ex: "CARBO-TAXOL C3D1"), sem necessidade de montar a string a partir de `Protocolo`/`PrescricaoProtocolo` |
| `scheduleDate`                 | datetime| Agenda.DhAgendaIniUTC, via Prescricao.IdAgenda | convertida para o fuso da clínica na exibição |
| `hoursRemaining`               | int     | calculado: `DATEDIFF(MINUTE, SYSDATETIMEOFFSET(), Agenda.DhAgendaIniUTC) / 60` — divisão inteira do T-SQL, que trunca em direção a zero (ex.: 90 min → 1; -90 min → -1) (D-CM-026); a classificação "urgente" (`< 12`) usa os minutos (`< 720`), então um tempo de 11h59 é urgente mesmo aparecendo como 11 | menor ou igual a zero quando a hora da aplicação já passou (a janela não tem piso — a prescrição segue na lista até ser assinada ou revisada, e conta como urgente, `< 12`, RN-CM-011) |
| `minutesRemaining`             | int     | calculado: `DATEDIFF(MINUTE, SYSDATETIMEOFFSET(), Agenda.DhAgendaIniUTC)` | menor ou igual a zero quando a hora da aplicação já passou; base dos textos de tempo restante e "atrasada" no card (D-CM-013, D-CM-041) |
| `type`                         | string  | calculado | `to_sign` se RN-CM-020 Precedência 1 se aplica — nunca assinada (checando as 2 fontes de RN-CM-029, ver 4.6) e aplicação dentro da janela de `PrescricaoTempoAssinatura` (padrão 7 dias de calendário); `to_review` se RN-CM-020 Precedência 2 se aplica — já assinada (qualquer época), aplicação dentro da janela de `NumeroDiasRevisaoPrescricoes` (padrão 1 dia), sem nenhuma confirmação de revisão (`TipoAssinatura = 'R'`) jamais registrada (ver PrescriptionToReviewCount, 4.6), sem assinatura feita depois de a janela de revisão abrir e sem administração iniciada (exceções (a) e (b) de 4.6); em ambos os casos, `Prescricao.Situacao IN ('P','G','A','O')` (filtro de RN-CM-020) |
| `isPreferredReviewer`          | bool    | calculado | `true` se médico logado = `Prescricao.IdChProfissionalMed` OU = `Paciente.IdChProfissional`; presente para as duas categorias (`to_sign` e `to_review`) — usado pelo front para a borda "Meu" em ambos os tipos de card. Os campos `prescriberName`/`attendingDoctorName` cogitados antes foram removidos — o booleano basta para o front |

#### Filtros do endpoint

- `type = to_sign`: mesma condição de `PrescriptionToSignNext7DaysCount()` (Seção 4.6 — RN-CM-020 Precedência 1: nunca assinada, 2 fontes de RN-CM-029) AND `Agenda.DhAgendaIniUTC < lc.LimiteAssinaturaUTC` — até o fim do dia (hoje + `PrescricaoTempoAssinatura` da clínica da prescrição), em dias de calendário no fuso da clínica logada (D-CM-062) (parâmetro por clínica, ver 4.6 — não mais um corte de 7 dias fixo; D-CM-037). Inclui as com administração iniciada (D-CM-039). Essa janela evita inflar a lista com prescrições distantes no tempo, sem relevância imediata para o médico.
- `type = to_review`: condição completa de `PrescriptionToReviewCount()` (Seção 4.6 — RN-CM-020 Precedência 1 e 2) — i.e., `Situacao IN ('P','G','A','O')` (filtro de RN-CM-020), já assinada (qualquer época) por uma das fontes de RN-CM-029 (Precedência 1, sem verificação de validade/vencimento), aplicação dentro da janela de `IpParametro.NumeroDiasRevisaoPrescricoes` (parâmetro por clínica, ver 4.6), sem nenhuma confirmação de revisão (`TipoAssinatura = 'R'`) jamais registrada (Precedência 2, também sem prazo de validade), sem assinatura feita depois de a janela de revisão abrir (exceção (a), D-CM-040) e sem administração iniciada (exceção (b), D-CM-039) — mesmas condições do SQL de `PrescriptionToReviewCount()`.
- Escopo de médico (`reviewScope`), aplicado igualmente às duas categorias — qualquer médico pode assinar ou revisar uma prescrição, não só o prescritor original (a 4.6 é sempre "mine" nas duas funções, porque cada uma é uma contagem pessoal do KPI; o painel de prescrições permite alternar):
    - `reviewScope = mine` (default): adiciona `(Prescricao.IdChProfissionalMed = {médico logado} OR Paciente.IdChProfissional = {médico logado})`, tanto a `to_sign` quanto a `to_review`
    - `reviewScope = all`: sem restrição adicional de médico em nenhuma das duas categorias — todas as prescrições pendentes, de todos os médicos, mas só das clínicas em que o médico logado atende (D-CM-066)
- Sem parâmetro `type`: união das duas categorias, ordenada por `minutesRemaining` ascendente, desempate pelo nome do paciente — `COALESCE(NULLIF(Paciente.NomeSocial, ''), Paciente.Nome)`, comparado sem diferenciar maiúsculas e acentos (collation do banco, pt-BR) — e, para o mesmo nome, por `id` (RN-CM-011, D-CM-047, E-CM-027; `hoursRemaining` é truncado e não distingue -40 min de +20 min); `reviewScope` aplicado às duas partes da união, conforme acima
- `Agenda.RemovidoEm IS NULL` (agenda da prescrição, via `Prescricao.IdAgenda`) e `Paciente.RemovidoEm IS NULL` — exclusão lógica, nos dois tipos
- `Prescricao.Situacao IN ('P','G','A','O')` em ambos os casos (RN-CM-020) — como há no máximo 1 prescrição ativa por cadeia de versões dentro dessas situações, este filtro também garante que apenas a versão vigente e ainda "em andamento" apareça na lista, sem risco de duplicidade
- Escopo de clínicas (D-CM-066, RN-CM-032): só entram as prescrições das clínicas em que o médico atende — o `INNER JOIN` com `{LimitesPorClinica}` (uma linha por clínica de `{ClinicasDoMedico}`; 4.6) faz o filtro, tanto em `reviewScope = mine` quanto em `all`; a clínica de cada uma vem de `Agenda.IdChClinica` (campo `clinic`); as janelas de "a assinar" e "a revisar" usam os parâmetros `PrescricaoTempoAssinatura` e `NumeroDiasRevisaoPrescricoes` da clínica de cada prescrição (`Agenda.IdChClinica`) (D-CM-060, D-CM-062, E-CM-032).

---

> **Nota:** os endpoints de ação de prescrições (GET /prescriptions/{id}, POST /sign, PATCH /review, PUT /prescriptions/{id}) pertencem a um documento de processo de Prescrições à parte, fora do escopo deste documento. RN-CM-020 continua na Seção 2 porque alimenta o KPI/painel deste Cockpit (Seções 4.6/4.7). O esquema já suporta assinatura/revisão via `PrescricaoAssinatura.TipoAssinatura` (`A`/`R`) e versionamento via `PrescricaoVersao`, sem necessidade de tabela nova.

### 4.8 Endpoint: GET /api/v1/cockpit/doctor/conversations

Retorna as conversas não finalizadas do médico logado — lidas e não lidas (painel compacto do Cockpit — ver escopo em RN-CM-015 e Seção 2.5), com base no modelo de Conversas e Alertas (`IP.Mensageria`). O schema desse domínio (`GescomClienteAlfa.mensageria`) passou a ter DDL real na Biblioteca de Schema (DDL) do projeto (`GescomMensageria.sql`) a partir de 28/08/2026 — antes disso, o mapeamento abaixo era baseado só na descrição do documento externo "Conversas-e-Alertas", sem o DDL para conferir nomes e tipos de campo exatos. Esta versão corrige os campos que o DDL real revelou estarem descritos incorretamente (ver "Resolução do nome do autor" abaixo e o changelog desta versão). Diferente das demais tabelas mapeadas neste documento, as tabelas de mensageria abaixo pertencem ao mesmo banco operacional do cliente (`GescomClienteAlfa`), mas são de um schema separado (`mensageria`), não do schema `dbo` assistencial.

#### Tabelas envolvidas

| Tabela               | Banco             | Função no endpoint                                                                 |
| --------------------- | ------------------ | ---------------------------------------------------------------------------------- |
| Conversa               | GescomClienteAlfa | Tabela principal — thread, assunto, status, prazo, origem, paciente relacionado, criação (desempate da ordem) |
| ConversaDestinatario   | GescomClienteAlfa | Snapshot de quem recebeu a conversa — usada para filtrar pelo médico logado e para saber se há outros participantes (autoenvio, `readByOthers`) |
| ConversaLeitura        | GescomClienteAlfa | Determina se a conversa está lida para o médico logado e se a última mensagem dele foi lida pelos outros participantes (`readByOthers`) |
| Mensagem               | GescomClienteAlfa | Conteúdo e autor da última mensagem da thread (preview do card)                     |
| Indicadores, TermoTraducao | IpTerminologia | Nome do grau de urgência (`Dominio` = `GrauUrgencia`), traduzido para o idioma da clínica **[PROPOSTA]** (D-CM-088) |
| Equipe                 | GescomClienteAlfa | Nome de exibição da equipe (`Nome`) — usado no sufixo "(Equipe)" do autor quando aplicável (ver "Resolução do nome da equipe" abaixo) |
| EquipeMembro            | GescomClienteAlfa | Determina se o autor da mensagem é membro da equipe destino da conversa (`Conversa.EquipeId`) — decide se o sufixo de equipe aparece |
| SegUsuario             | GescomClienteAlfa | Ponte legada entre o identificador local (int) e o GUID do IPSeguranca (ver resolução de nome abaixo) |
| Paciente               | GescomClienteAlfa | Nome social/civil do paciente relacionado, quando `Conversa.IdPaciente` existe      |
| Clinica                | GescomClienteAlfa | Clínica de origem da conversa: `Clinica.IdEmpresa` = `Conversa.IdEmpresa` (E-CM-033; confirmado em D-CM-069) → `IdChClinica` |
| Chave                  | GescomClienteAlfa | Nome curto da clínica (`Chave.Descricao` de `Clinica.IdChClinica`) |

> **Resolução do nome do autor (DDL real de `mensageria`, disponibilizado em 28/08/2026):** `Mensagem.IdUsuarioAutor`, `Conversa.IdUsuarioRemetente`, `Conversa.IdUsuarioDestino`, `ConversaDestinatario.IdUsuario` e `EquipeMembro.IdUsuario` são todos `int`, não GUID — o mesmo tipo de identificador legado local já usado em `Profissional.IdUsuario`/`Prescricao.IdUsuarioAssinatura`. O DDL real desmente a suposição de que esses campos armazenam diretamente o GUID de `IPSeguranca.dbo.Usuario.Id`. A triangulação correta é: `IdUsuarioAutor` (int) = `SegUsuario.IdUsuario` (smallint, PK — join dentro do próprio `GescomClienteAlfa`) → `SegUsuario.SegurancaUsuarioId` (GUID; nome de campo conforme "Log de Mudanças Estruturais do Banco de Dados", que prevalece sobre a Biblioteca de Schema (DDL) para este campo específico) → nome de exibição em `IPSeguranca.dbo.Usuario.Apelido` (mesmo GUID). A mesma triangulação vale para `IdUsuarioRemetente`, `IdUsuarioDestino` e `ConversaDestinatario.IdUsuario`.

> **Resolução do nome da equipe:** o autor de uma mensagem ganha o sufixo da equipe quando `Conversa.DestinoTipo = 'Team'` E existe uma linha em `EquipeMembro` com `EquipeId = Conversa.EquipeId`, `IdUsuario` igual ao `SegUsuario.IdUsuario` do autor resolvido acima, e vínculo vigente **na data de envio da mensagem** (não o vínculo atual no momento em que o card/thread é exibido): `AdicionadoEmUtc <= Mensagem.CriadoEmUtc AND (RemovidoEmUtc IS NULL OR RemovidoEmUtc > Mensagem.CriadoEmUtc)`. Quando as condições são verdadeiras, o nome de exibição final é `Usuario.Apelido` + " (" + `Equipe.Nome` + ")" (ex.: "Sandra (Farmácia)"); caso contrário, é só `Usuario.Apelido`. **Decidido nesta versão** (resolve o `[PROPOSTA]` da v4.24): o vínculo é sempre o da época do envio — alguém que já saiu da equipe mantém o sufixo nas mensagens antigas que escreveu como membro dela, e alguém que entrou depois não ganha o sufixo retroativamente em mensagens anteriores à sua entrada.

O ícone continua binário: bell (Lucide) para `OrigemTipo = 'System'`, ícone genérico de pessoa para `OrigemTipo = 'User'`, sem variação por equipe (recepção/farmácia/enfermagem) — a identificação da equipe, quando aplicável, é resolvida como texto (acima), não como ícone. Diferente do que versões anteriores deste documento afirmavam, a tabela `Equipe` (e `EquipeMembro`) **é** necessária neste endpoint — não apenas no backend de roteamento — porque a exibição do sufixo "(Equipe)" no `authorName` depende dela (ver "Resolução do nome da equipe" acima).

#### Mapa de campos

| Campo                    | Tipo    | Banco | Observação |
| :-------------------------- | :------ | :---- | :--------- |
| `id`                        | uuid    | Conversa.Id | |
| `subject`                   | string  | Conversa.Assunto | `nvarchar(200)`, obrigatório no banco (Mapeamento da Mensageria, 4.1) |
| `sourceType`                | string  | calculado a partir de Conversa.OrigemTipo | `System` se `OrigemTipo = 'System'`; `User` se `OrigemTipo = 'User'` (D-CM-042) |
| `authorName`                | string  | `IPSeguranca.dbo.Usuario.Apelido` do autor da mensagem de maior `Sequencia`, via `SegUsuario` (ver "Resolução do nome do autor" acima); com sufixo `" (" + Equipe.Nome + ")"` quando `Conversa.DestinoTipo = 'Team'` e o autor é membro dessa `Equipe` (ver "Resolução do nome da equipe" acima); ou "Sistema" quando `sourceType = System` | |
| `lastMessage`               | string  | Mensagem.Conteudo (maior Sequencia da Conversa) | |
| `lastMessageAt`             | datetime| Mensagem.CriadoEmUtc (mesma linha) | |
| `deadline`                  | datetime| Conversa.PrazoEmUtc | pode ser nulo |
| `urgencyLevel`                   | object\|null | `Conversa.GrauUrgencia` (chave) e nome em `IpTerminologia.dbo.Indicadores` (`Dominio` = `GrauUrgencia`) com `TermoTraducao` (idioma da clínica, DT-011, DT-012) | **[PROPOSTA]** coluna `varchar(10) NULL` ainda a criar (Mapeamento da Mensageria, L-09); chaves `routine`, `urgent`, `asap`, `stat` em minúsculas (D-MSG-046); `null` quando a coluna é nula (D-CM-088) |
| `status`                    | string  | Conversa.Status | mapeado para `sent`/`read`/`responded` (ver 2.9; D-CM-087) |
| `unread`                    | bool    | calculado: `Conversa.UltimaSequencia > ConversaLeitura.UltimaSequenciaLida` (ou sem `ConversaLeitura`) | usado para destaque visual e camada de prioridade (RN-CM-015); não filtra o resultado |
| `authorIsMe`                | bool    | calculado: `Mensagem.IdUsuarioAutor` (maior `Sequencia`) = `SegUsuario.IdUsuario` do médico logado | `false` em alerta do sistema (autor nulo) |
| `readByOthers`              | bool \| null | calculado **[PROPOSTA]**, sem coluna nova (mesma regra da Mensageria, Mapeamento 4.1): `NULL` quando `Conversa.OrigemTipo = 'System'` ou quando o autor da última mensagem não é o médico logado; senão `1` quando existe `ConversaLeitura` de outro usuário (`IdUsuario` diferente do médico) com `UltimaSequenciaLida >= Mensagem.Sequencia` da última mensagem, ou quando a conversa não tem outro participante (o remetente é o médico e nenhum `ConversaDestinatario` é outro usuário: autoenvio, inclusive conversa de equipe em que o médico é o único membro); senão `0` | Junções com `IdEmpresa` e `ConversaId`, como as demais tabelas de `mensageria` |
| `patient`                   | object|null | Paciente, via Conversa.IdPaciente | mesmo padrão `preferredName`/`legalName` dos demais endpoints; `null` quando `IdPaciente` é nulo ou o paciente tem `RemovidoEm` preenchido |
| `clinic`                    | string \| null | `Conversa.IdEmpresa` → `Clinica.IdEmpresa` → `Clinica.IdChClinica` → `Chave.IdChave` → `Chave.Descricao` | Nome curto da clínica de origem da conversa (D-CM-064), mesma origem do `clinic` de 4.1; `null` quando o médico atende em uma só clínica (mesma regra de 4.1, D-CM-066). `Conversa.IdEmpresa` é o `SegEmpresa.IdEmpresa` (D-CM-069; P-CM-052) |

#### Filtros do endpoint

- `ConversaDestinatario.IdUsuario = {médico logado}` — recebida diretamente ou via snapshot de equipe (`EquipeOrigemId`)
- Escopo de clínicas (D-CM-066, RN-CM-032): `Conversa.IdEmpresa IN (SELECT IdEmpresa FROM {ClinicasDoMedico})` — o médico vê as conversas das clínicas em que atende (`Conversa.IdEmpresa` = `SegEmpresa.IdEmpresa`, D-CM-069); o cliente vem sempre da sessão validada, nunca escolhido livremente pelo cliente da API (conforme fronteira de responsabilidade do IPSeguranca), e o vínculo do médico com a conversa continua pelo filtro de destinatário acima. **Schema (Biblioteca de Schema (DDL), `GescomMensageria.sql`):** `Conversa`, `ConversaDestinatario`, `ConversaLeitura`, `Mensagem`, `ConversaAcao`, `OutboxEvento`, `Equipe` e `EquipeMembro` têm `IdEmpresa` NOT NULL e chaves estrangeiras compostas (por exemplo `Id` + `IdEmpresa`): as junções entre essas tabelas devem usar as duas colunas, e o filtro de `IdEmpresa` sai só da lista, não das junções. O card mostra a clínica de origem (D-CM-064, campo `clinic`)
- `Conversa.Status IN ('Sent', 'Read', 'Responded')` — exclui `'Resolved'` e `'Dismissed'`
- `Conversa.UltimaSequencia > ConversaLeitura.UltimaSequenciaLida`, OU não existe `ConversaLeitura` para o médico logado — calcula o campo `unread`, usado como critério de ordenação (não filtra o WHERE).
- Ordenação (RN-CM-015; RN-MSG-002 da Mensageria; D-CM-087, D-CM-088): `unread` descendente (não lidas primeiro); depois o grau, descendente (`CASE Conversa.GrauUrgencia WHEN 'stat' THEN 4 WHEN 'asap' THEN 3 WHEN 'urgent' THEN 2 WHEN 'routine' THEN 1 ELSE 0 END`, o mesmo que a `Ordem` de `Indicadores`; nulo vale 0); depois a chave `CASE WHEN Conversa.PrazoEmUtc > lastMessageAt THEN Conversa.PrazoEmUtc ELSE lastMessageAt END` descendente (com prazo nulo, vale `lastMessageAt`); empate: `Conversa.CriadoEmUtc` descendente; por fim `Conversa.Id`, para a ordem ser determinística

---

### 4.9 Endpoints de ação: GET /conversations/{id}, POST /read, POST /reply, PATCH /resolve, PATCH /discard

#### Tabelas envolvidas

| Tabela               | Banco             | Função no endpoint                                                                 |
| --------------------- | ------------------ | ---------------------------------------------------------------------------------- |
| Conversa               | GescomClienteAlfa | Status atual, `UltimaSequencia`                                                     |
| Mensagem               | GescomClienteAlfa | Thread completa (GET detalhe); nova linha inserida em `POST /reply`                 |
| ConversaLeitura        | GescomClienteAlfa | Atualizada em `POST /read`                                                          |
| ConversaAcao           | GescomClienteAlfa | Histórico append-only — nova linha em toda ação (leitura, resposta, resolução, descarte) |
| OutboxEvento           | GescomClienteAlfa | Evento assíncrono gravado na mesma transação de qualquer mudança de estado (fora do escopo deste front, mencionado por completude) |

#### Efeitos em BD por endpoint de ação

Escopo de clínicas (D-CM-066): as ações só valem para conversas de clínicas em que o médico atende (`Conversa.IdEmpresa IN (SELECT IdEmpresa FROM {ClinicasDoMedico})`), como a lista (4.8); uma ação sobre conversa de outra clínica é recusada, mesmo que o id seja conhecido. Como `IdEmpresa` é NOT NULL e faz parte das chaves estrangeiras compostas, toda linha nova (`Mensagem`, `ConversaLeitura`, `ConversaAcao`, `OutboxEvento`) grava o `IdEmpresa` **da conversa**, não o da clínica da sessão; do contrário a chave estrangeira falha ou a linha cai na clínica errada.


- **GET /conversations/{id}:** somente leitura — retorna todas as `Mensagem` da conversa ordenadas por `Sequencia`, mais os dados de `Conversa` (assunto, status, prazo, paciente); `status` no contrato usa `responded` para `'Responded'` (D-CM-087). Para cada mensagem, resolve o sufixo de equipe do autor do mesmo jeito que o endpoint de lista (Seção 4.8, "Resolução do nome da equipe") — a thread completa também mostra "Sandra (Farmácia)" quando aplicável, não só o card compacto. No contrato, `messages[].type` tem um único valor, `Response` (D-CM-042): o tipo de mensagem não distingue mais inicial, resposta e atualização.
- **POST /read:** insere ou atualiza `ConversaLeitura` (chave `ConversaId` + `IdUsuario`) com `UltimaSequenciaLida = Conversa.UltimaSequencia`; insere uma linha em `ConversaAcao` (`Tipo` = `Read`); se `Conversa.Status = 'Sent'`, atualiza para `'Read'` — nunca regride um status já mais avançado (`'Responded'` permanece `'Responded'`).
- **POST /reply:** insere uma nova linha em `Mensagem` (`Sequencia = Conversa.UltimaSequencia + 1`, `IdUsuarioAutor` = médico logado, `Conteudo` do request, `Tipo` = `'Response'`); atualiza `Conversa.UltimaSequencia`; insere ou atualiza `ConversaLeitura` do autor com `UltimaSequenciaLida` = a nova `Sequencia` (a própria resposta conta como leitura, Especificação 2.5; sem isso, a conversa ficaria não lida para quem respondeu); atualiza `Conversa.Status` para `'Responded'`; insere uma linha em `ConversaAcao` (`Tipo` = `'Response'`; `MensagemId` da nova mensagem); grava `OutboxEvento` (TipoEvento=`ConversaRespondida`). O valor `'Reply'`, antes um segundo valor válido para `Mensagem.Tipo`/`ConversaAcao.Tipo` no mesmo `CHECK`, foi abolido — `'Response'` é o único valor usado para a ação de responder, resolvendo a pendência **[A DEFINIR]** da v4.24 (ver Log de Mudanças Estruturais do Banco de Dados). O `CHECK` de `Mensagem.Tipo` deve aceitar **só `'Response'`** (D-CM-052; hoje a Biblioteca de Schema (DDL) ainda mostra `'Initial'`, `'Update'` e `'Reply'`): mudança de banco a aplicar, registrada no Log de Mudanças Estruturais do Banco de Dados.
- **PATCH /resolve:** atualiza `Conversa.Status` para `'Resolved'`; insere uma linha em `ConversaAcao` (`Tipo` = `'Resolve'`); grava `OutboxEvento` (TipoEvento=`ConversaResolvida`). Não altera nem exclui `Mensagem`.
- **PATCH /discard:** atualiza `Conversa.Status` para `'Dismissed'`; insere uma linha em `ConversaAcao` (`Tipo` = `'Dismiss'`); grava `OutboxEvento` (TipoEvento=`ConversaDescartada`). Mesma observação — nada é excluído fisicamente. O estado fica em `Conversa`, não em `ConversaDestinatario`: resolver ou descartar vale para todos os destinatários — a conversa sai da lista de todos (endereçada a uma equipe ou a mais de uma pessoa), pois a lista filtra por `Conversa.Status`.

#### Fora do escopo deste documento

A criação de conversas (envio inicial por um usuário) e a publicação de alertas automáticos (`AlertaPublicacao`, com chave de idempotência) partem de outros módulos ou telas — o Cockpit é consumidor/participante das conversas, não o ponto de origem. O cadastro e a gestão de equipes (criar/renomear/desativar uma `Equipe`, adicionar ou remover membros em `EquipeMembro`) também partem de outro módulo — o Cockpit só *lê* `Equipe`/`EquipeMembro` para resolver o sufixo do nome do autor (Seção 4.8), nunca escreve nelas. O processamento assíncrono via `OutboxEvento` pertence ao domínio do `IP.Mensageria` e também não é especificado aqui.

---

### 4.10 Endpoint: GET /api/v1/patients/search

Retorna pacientes que casam com o termo digitado no campo de busca do cabeçalho (RN-CM-004). A correspondência aproximada (pesos de proximidade fonológica e semântica) usa um mecanismo já existente no sistema, fora do escopo desta especificação — este mapeamento cobre apenas os campos de busca direta e de resposta que esse motor já existente consome/retorna.

#### Tabelas envolvidas

| Tabela   | Banco             | Função no endpoint                                          |
| -------- | ------------------ | ------------------------------------------------------------ |
| Paciente | GescomClienteAlfa | Tabela única consultada — todos os campos de busca e resposta vêm dela |

#### Mapa de campos

| Campo                  | Tipo   | Banco                 | Observação                                                                                          |
| ----------------------- | ------ | ---------------------- | ----------------------------------------------------------------------------------------------------- |
| `id`                    | int    | Paciente.IdPaciente   |                                                                                                        |
| `name`                  | string | Paciente.Nome          | segue o mesmo padrão de `preferredName`/`legalName` dos demais endpoints quando `Paciente.NomeSocial` existir |
| `medicalRecordNumber`   | string | Paciente.NumProntuario |                                                                                                        |
| `gender`                | char   | Paciente.Sexo          | domínio 'M'/'F'                                                                                       |
| `birthDate`             | data   | Paciente.Nascimento    | campo de busca quando a entrada começa com dígito (RN-CM-004)                                        |
| `motherName`            | string | Paciente.NomeMae       | campo de busca quando a entrada começa com letra (RN-CM-004)                                         |
| `cpf`                   | string | Paciente.CPF           | campo de busca quando a entrada começa com dígito (RN-CM-004)                                        |

#### Filtros do endpoint

- Sem filtro por médico logado — diferente dos demais painéis do Cockpit, a busca do cabeçalho não é restrita aos pacientes do médico, pois ele pode precisar localizar qualquer paciente da clínica.
- Mínimo de 2 caracteres (RN-CM-004) antes de disparar a busca.
- `Paciente.RemovidoEm IS NULL` — paciente excluído logicamente não aparece na busca.

#### Nota sobre o motor de correspondência aproximada

O `EsquemaGemed21.csv` não tem uma coluna dedicada de correspondência fonética/semântica em `Paciente` — o motor de busca aproximada citado em RN-CM-004 já existe e está em produção fora deste documento; presume-se que ele consulta os mesmos campos mapeados acima, mas o algoritmo em si (ranking, tolerância, índice usado) não é redefinido aqui.

---

### Tabelas novas

Nenhuma proposta por esta funcionalidade. Usa `IpTerminologia.DomEstadoAgendaConsulta` (4.1) e `IpTerminologia.DomEstadoAgendaProcedimento` (4.3), criadas em 27/10/2026 (estrutura no Log de Mudanças Estruturais do Banco de Dados, 27/10/2026: `CodEstado char(3)` PK, `Descricao varchar(30)`, `CorClaroTexto`, `CorClaroFundo`, `CorEscuroTexto`, `CorEscuroFundo` `char(7)`); ainda não estão na Biblioteca de Schema (DDL), que precisa de nova extração. São base global: as cores valem para todas as clínicas (DT-016). O indicador `EstadoAgendaConsulta` de `IpTerminologia.Indicadores`, que guardava a cor do status em `Caracteristica`, será descontinuado depois que estas tabelas estiverem em uso (D-CM-081); a Central não o lê.

### Campos novos

| Tabela | Campo | Tipo | Finalidade | Definição completa |
| --- | --- | --- | --- | --- |
| `ClienteEmpresa` (IpSeguranca) | `FusoHorarioOficial` | `varchar(64)`, NOT NULL depois de preencher as clínicas existentes | Fuso da clínica em convenção IANA (ex.: `America/Sao_Paulo`), usado em "hoje" e em todos os horários de 4.1 a 4.4 e 4.7 no lugar do deslocamento `FusoOficial` (`smallint`), que não identifica o fuso | Agenda do Médico, Mapeamento de Banco de Dados, "Campos novos"; migração a criar (Log de Mudanças Estruturais do Banco de Dados, 07/10/2026); não repetida aqui (Lição #10 da Skill Designer) |

### Índices

**[PROPOSTA]** O índice proposto na Agenda do Médico (`IX_Agenda_IdChRecurso_TipoAgenda_DhAgendaIniUTC`, Mapeamento da Agenda, "Índices"; a confirmar com o time de banco) atende também os filtros de 4.1 e 4.2 (`IdChRecurso`, `TipoAgenda`, dia de `DhAgendaIniUTC`). A Central lê mais colunas de `Agenda` (`DhChegadaUTC`, `DhAtendimentoIniUTC`, `DhAtendimentoFimUTC`, `Atendimento`, `PrimeiraConsulta`): se o time de banco as incluir no `INCLUDE`, ajusta-se essa única definição para as duas funcionalidades, sem criar um segundo índice. 4.3 e 4.4 filtram por `TipoAgenda` e dia, sem o médico — o índice não os atende como está; avaliar com o time de banco.

### Seeds da Funcionalidade

Lista consolidada de todo dado (seed) que precisa existir em produção — não schema novo, mas linhas de dado que este documento pressupõe — para que a funcionalidade se comporte como especificado. Pensada como checklist de deploy para Dev/DevOps: cada linha aponta para a seção com a definição completa dos campos, não a repete (evita duplicar a mesma informação em dois lugares — Lição #10 da Skill Designer).

| Seed | Tabela(s) | Por cliente ou global? | Status | Detalhe completo |
| --- | --- | --- | --- | --- |
| `NumeroDiasRevisaoPrescricoes` — janela (em dias) antes da aplicação em que uma prescrição assinada vira "a revisar" (RN-CM-020) | `IpParametro` (1 linha) + `IpParametroChave` (1 linha por clínica) | Por cliente (`IpParametroChave`); a linha de `IpParametro` é única, compartilhada entre clientes | Parâmetro já existente no sistema atual (não é seed novo); conferir a presença da linha de `IpParametro` nas bases novas. Padrão de 1 dia no código (Gemed 2.1) se a linha de `IpParametroChave` (ou a de `IpParametro`) não existir para a clínica; o código do sistema atual assume 2 | Seção 4.6, "Parâmetro de configuração — NumeroDiasRevisaoPrescricoes" |
| `PrescricaoTempoAssinatura` — janela (em dias) a partir de agora em que uma prescrição nunca assinada aparece em "a assinar" (RN-CM-020) | `IpParametro` (1 linha) + `IpParametroChave` (1 linha por clínica) | Por cliente (`IpParametroChave`); a linha de `IpParametro` é única, compartilhada entre clientes | **[PROPOSTA]** — ainda não aplicado em produção. Não há parâmetro de validade da assinatura (RN-CM-020). Default de 7 dias no código se a linha de `IpParametroChave` (ou a de `IpParametro`) não existir para a clínica, mas o seed deve ser aplicado mesmo assim | Seção 4.6, "Parâmetro de configuração — PrescricaoTempoAssinatura" |
| Cores dos status de consulta (6) e de procedimento (8) nos dois temas | `DomEstadoAgendaConsulta` e `DomEstadoAgendaProcedimento` (IpTerminologia) | Global | Tabelas criadas; conferir os valores contra o Design System, seção 4 | 4.1 e 4.3, campo `status.colors` |
| Nome dos status de consulta e de procedimento em pt-BR, en-US e es-419 | `TermoTraducao` (IpTerminologia), `Origem` = nome da tabela de estado | Global | Existentes nos 3 idiomas — conferir | 4.1 e 4.3, campo `status.name` |

Nenhum outro seed é necessário por este documento — os demais parâmetros citados (`TempoPacienteAtivoAgenda`, `TempoPacienteAtivoPrescricao`, Seção 4.5, em dias) já existem em produção, não são propostos aqui; seed de referência em `Infra-estrutura/(BD-M) Dados incluídos obrigatórios.md`.

**Modo de assinatura (RN-CM-012) — [A DEFINIR]:** a assinatura com ou sem certificado digital é definida por configuração do cliente, mas o parâmetro/tabela que guarda essa configuração ainda não foi identificado. Não mapeado neste documento.

---

### METADADOS FINAIS

- Prioridade: Alta (Core do Sistema)
- Complexidade: Média/Alta (Devido às integrações em tempo real)
- Próximos Passos:
    1. Homologação do serviço de atualização automática.
    2. Teste de carga para múltiplos médicos acessando simultaneamente.
    3. Validação da assinatura digital em ambiente de sandbox.
    4. ~~Definir ícones definitivos para os KPI cards~~ — feito no Protótipo v3.14 (biblioteca Lucide, via `lucide.js`).
    5. ~~Substituir ícones placeholder de comunicação por SVGs da biblioteca Lucide~~ — feito no Protótipo v3.14 (`bell` para alertas de sistema, `user` para conversas com pessoa).
    6. Validar acessibilidade (WCAG) com usuários reais — navegação por teclado dos títulos como links, toggles pill e KPI cards já implementada no Protótipo v3.14 (tabindex/role/Enter-Space), falta validação formal.
    7. Testar usabilidade do título como link com seta no hover com médicos.
    8. Validar touchpoints de workflow com médicos em ambiente real (shadowing).
    9. Mapear touchpoints de workflows ainda não documentados (ex: workflow de alta, workflow de intercorrência).
    10. Especificar os demais itens do menu do usuário: autocadastro/edição de perfil, dados de faturamento e pagamento (conta bancária, PIX) e dados de especialidade (CRM, RQE) — hoje o menu só tem o seletor "Tema" e "Sair".

---

## Histórico de Versões

- **v6.0** (29/10/2026) — Abertura da versão 6.0 a pedido do solicitante: a série v5 foi fechada na v5.20, que foi para desenvolvimento, e fica em `Histórico/` sem alterações. Esta versão é, por ora, cópia da v5.20: nenhuma mudança de regra, tela, contrato ou schema. As mudanças da v6 serão descritas para o time de desenvolvimento em `Para Desenvolvedores/Central do Médico - mudanças da v6 - guia para desenvolvimento.md`. Acompanha 01-Definição v6.0, 02-Especificação v6.0, 03-Protótipo v6.0, 04-Mapeamento v6.0, Cenários de Teste v6.0 e 05-Registro de Decisões v1.20.
- **v5.20** (29/10/2026) — Acompanha as versões v5.20 dos demais documentos e 05-Registro de Decisões v1.19. Decisão D-CM-090: o campo da conversa passa de `urgency` a `urgencyLevel` (4.8). Sem outra mudança.
- **v5.19** (29/10/2026) — Acompanha 01-Definição v5.19, 02-Especificação v5.19, 03-Protótipo v5.19, Cenários de Teste v5.19 e 05-Registro de Decisões v1.18. Decisão D-CM-088. 4.8: campo `urgency` (`Conversa.GrauUrgencia` e `Indicadores`/`TermoTraducao`) e a ordem pelo grau antes da data; chaves em minúsculas. Depende da criação da coluna (L-09 da Mensageria). Sem mudança nas ações (4.9); o botão do card (D-CM-089) é só de interface.
- **v5.18** (28/10/2026) — Acompanha 01-Definição v5.18, 02-Especificação v5.18, 03-Protótipo v5.18, Cenários de Teste v5.18 e 05-Registro de Decisões v1.17. Decisões D-CM-086 e D-CM-087 (05 - Registro de Decisões v1.17). 4.8: novos campos `subject` (`Conversa.Assunto`), `authorIsMe` e `readByOthers` (calculado, sem coluna nova, **[PROPOSTA]** como na Mensageria); `status` com `responded`; nova ordenação (não lidas; depois o maior entre a última mensagem e o prazo, decrescente; empate por `Conversa.CriadoEmUtc`), D-CM-087. 4.9: o detalhe da conversa traz o assunto; `POST /reply` grava a leitura do autor (a resposta conta como leitura), que o cálculo de `unread` e de `readByOthers` usa. Desempate final da ordem por `Conversa.Id`. Revisão por crítico técnico (subagente) aplicada antes de fechar. Até a v5.17, o valor de `'Responded'` era `replied`.
- **v5.17** (28/10/2026) — Acompanha 01-Definição v5.17, 02-Especificação v5.17, 03-Protótipo v5.17, Cenários de Teste v5.17 e 05-Registro de Decisões v1.16. Decisões D-CM-084 e D-CM-085 (05 - Registro de Decisões v1.16). Sem mudança de mapeamento: só o número da versão (as mudanças são de nome exibido e de botões).
- **v5.16** (27/10/2026) — Acompanha 01-Definição v5.16, 02-Especificação v5.16, 03-Protótipo v5.16, Cenários de Teste v5.16 e 05-Registro de Decisões v1.15. Decisões D-CM-077 a D-CM-083 (05 - Registro de Decisões v1.15). (1) "Tabelas novas": o indicador `EstadoAgendaConsulta` será descontinuado depois da implementação nova (D-CM-081). (2) nomes de status como cadastrados em `TermoTraducao` — "Em atendimento", "Em preparo" e "Em aplicação" (antes "Em Atendimento", "Preparo" e "Em administração"; D-CM-082), nas tabelas de `ProcedureStatus()`. (3) menções de proveniência de decisões ("solicitante") retiradas do corpo, que passam a existir só aqui e no Registro de Decisões (D-CM-083).
- **v5.15** (27/10/2026) — Acompanha 01-Definição v5.15, 02-Especificação v5.15, 03-Protótipo v5.15, Cenários de Teste v5.15 e 05-Registro de Decisões v1.14. Decisões D-CM-073 a D-CM-076 (05 - Registro de Decisões v1.14). (1) 4.1 e 4.3: o campo `status` vira objeto — `status.domain` (calculado), `status.name` (`TermoTraducao`, `Termo` = `CodEstado`, `Origem` = nome da tabela) e `status.colors` (tabelas `IpTerminologia.DomEstadoAgendaConsulta` e `DomEstadoAgendaProcedimento`; DT-016, D-CM-073); tabelas novas em "Tabelas envolvidas"; tabelas "Status × `CodEstado`" em "Domínios". (2) `ProcedureStatus()` (4.3 e 4.4): status 3 volta a `administered` (D-CM-074). (3) Nova seção "Tabelas novas" e duas linhas em "Seeds da Funcionalidade" (cores e nomes; global).
- **v5.14** (26/10/2026) — Acompanha 01-Definição v5.14, 02-Especificação v5.14, 03-Protótipo v5.14, Cenários de Teste v5.14 e 05-Registro de Decisões v1.13. Decisões D-CM-070 a D-CM-072. (1) Bloco "Perda de acesso" da Seção 4 corrigido: perde-se agendas e prescrições, não pacientes; "Em Tratamento", adesão e busca seguem sem filtro por clínica; `Paciente.IdChClinica` não é usado pela Central (D-CM-070). (2) Nome do campo de `SegEmpresa` fixado no do DDL, `SegurancaClienteEmpresaId` (D-CM-071); sai o aviso de dois nomes. (3) `Clinica.FusoHorario` registrada como descontinuada, com remissão ao Log (D-CM-072). SQL e filtros não mudam.
- **v5.13** (26/10/2026) — Acompanha 01-Definição v5.13, 02-Especificação v5.13, 03-Protótipo v5.13, Cenários de Teste v5.13 e 05-Registro de Decisões v1.12. Decisões D-CM-067 a D-CM-069. (1) Bloco "Clínicas em que o médico atende": a proposta E-CM-034 vira decisão (D-CM-067); o parágrafo das alternativas da DDL é trocado por duas notas, "Fora desta regra" (`Recurso.IdChClinica` e `RecursoHorario` são da marcação de agendas) e "Perda de acesso" (D-CM-068). (2) `Conversa.IdEmpresa` = `SegEmpresa.IdEmpresa` confirmado (D-CM-069); a troca por `IdChClinica` ficou adiada (P-CM-052). (3) Fonte do fuso confirmada: `ClienteEmpresa.FusoHorarioOficial`; nenhuma consulta lê `Clinica.FusoHorario` (D-CM-069). (4) Nome do campo de `SegEmpresa`: o solicitante deu dois nomes; fica o do DDL (P-CM-050). SQL e filtros não mudam.
- **v5.12** (26/10/2026) — Acompanha 01-Definição v5.12, 02-Especificação v5.12, 03-Protótipo v5.12, Cenários de Teste v5.12 e 05-Registro de Decisões v1.11. Decisão em 05 - Registro de Decisões v1.11 (D-CM-066): o médico vê só as clínicas em que atende. (1) Novo bloco no início da Seção 4, "Clínicas em que o médico atende": tabela `{ClinicasDoMedico}` montada a partir do acesso do usuário às clínicas (`ClientePerfilAcessoUsuario`, mesmo critério de `ClinicVisible()` da Agenda) e da cadeia de D-CM-065 — **[PROPOSTA]** E-CM-034, a confirmar. (2) 4.1, 4.3 e 4.4: filtro `Agenda.IdChClinica IN` essa tabela no lugar de "sem filtro de clínica"; `clinic` nulo quando o médico atende em uma só clínica. (3) 4.6 e 4.7: `{LimitesPorClinica}` passa a ter uma linha por clínica do médico, e o `INNER JOIN` vira também o filtro (vale para "Meus" e "Todos"). (4) 4.8: filtro `Conversa.IdEmpresa IN` e ações restritas às mesmas clínicas. (5) Nota do KPI de alertas: o toggle "Todos" amplia para médicos das clínicas do médico logado.
- **v5.11** (26/10/2026) — Acompanha 01-Definição v5.11, 02-Especificação v5.11, 03-Protótipo v5.11, Cenários de Teste v5.11 e 05-Registro de Decisões v1.10. Decisões em 05 - Registro de Decisões v1.10 (D-CM-061 a D-CM-065). (1) Novo bloco no início da Seção 4, "Ligação entre a clínica do login e a base do cliente": `ClienteEmpresa.Id` = `SegEmpresa.SegurancaClienteEmpresaId` → `SegEmpresa.IdEmpresa` → `Clinica.IdEmpresa` → `Clinica.IdChClinica` = `Agenda.IdChClinica` = `IpParametroChave.IdChave` (D-CM-065). (2) 4.6 e 4.7: "hoje" e o fim do dia das duas janelas no fuso da clínica logada (D-CM-062); `{LimitesPorClinica}` passa a ter uma linha por `Clinica` do banco do cliente (sem `FusoWindows`; o fuso é o da clínica logada), o que também encerra o risco de o `INNER JOIN` perder prescrição; `{FusoWindowsLogin}` no lugar de `lc.FusoWindows` na exceção (a). (3) 4.5 e 4.6: `{IdClinica}` e `toSignWindowDays` são da clínica logada, sem "interino" (D-CM-063). (4) 4.8: novo campo `clinic` na lista de conversas, com `Clinica` e `Chave` nas tabelas (D-CM-064, E-CM-033). (5) 4.1: contagem das clínicas do cliente por `Clinica` ligada a `SegEmpresa`. (6) E-CM-030 vira D-CM-061.
- **v5.10** (26/10/2026) — Acompanha 01-Definição v5.10, 02-Especificação v5.10, 03-Protótipo v5.10, Cenários de Teste v5.10 e 05-Registro de Decisões v1.9. Decisões em 05 - Registro de Decisões v1.9 (D-CM-055 a D-CM-060). (1) 4.3: campo `clinic` (E-CM-030) e escopo sem filtro de clínica; 4.4: mesmo escopo no indicador. (2) 4.1: `clinic` nulo quando o cliente tem uma só clínica. (3) 4.6 e 4.7: as janelas de assinatura e de revisão usam os parâmetros da clínica de cada prescrição, por meio de uma tabela por clínica entregue pela aplicação (`{LimitesPorClinica}`, E-CM-031); o `toSignWindowDays` e o "Em Tratamento" (4.5) usam a clínica de entrada, interino em P-CM-048. (4) 4.8: sem filtro por `Conversa.IdEmpresa`; o médico vê as conversas de todas as clínicas do cliente (D-CM-060).
- **v5.9** (26/10/2026) — Acompanha 01-Definição v5.9, 02-Especificação v5.9, 03-Protótipo v5.9, Cenários de Teste v5.9 e 05-Registro de Decisões v1.8. Decisões D-CM-047 a D-CM-054. (1) 4.1 e 4.7: campo novo `clinic` (de `Agenda.IdChClinica`, `null` com uma só clínica) e nota de que as consultas de 4.1, 4.2 e as prescrições de 4.7 não têm filtro de clínica — todas as clínicas do cliente (D-CM-050; E-CM-028, E-CM-029); parâmetros por clínica em aberto (P-CM-046). (2) 4.7: desempate pelo nome do paciente e, depois, por `id` (D-CM-047, E-CM-027). (3) 4.9: o `CHECK` de `Mensagem.Tipo` passa a aceitar só `'Response'` (D-CM-052); mudança de banco a aplicar, registrada no Log de Mudanças Estruturais. Sem outra mudança de schema nem de contrato.
- **v5.8** (26/10/2026) — Acompanha 01-Definição v5.8, 02-Especificação v5.8, 03-Protótipo v5.8, Cenários de Teste v5.8 e 05-Registro de Decisões v1.7. Decisão em 05 - Registro de Decisões v1.7 (D-CM-046). Sem mudança de schema nem de contrato.
- **v5.7** (21/10/2026) — Acompanha 01-Definição v5.7, 02-Especificação v5.7, 03-Protótipo v5.7, Cenários de Teste v5.7 e 05-Registro de Decisões v1.6. Decisão em 05 - Registro de Decisões v1.6 (D-CM-045). Sem mudança de schema nem de contrato.
- **v5.6** (21/10/2026) — Acompanha 01-Definição v5.6, 02-Especificação v5.6, 03-Protótipo v5.6, Cenários de Teste v5.6 e 05-Registro de Decisões v1.5. Decisão em 05 - Registro de Decisões v1.5 (D-CM-044, decisões transversais DT-011 e DT-012). Nos três blocos "Internacionalização": idioma = o da clínica atual (era "Idioma do cliente"); sem tradução, vai a chave e o back-end registra anomalia, sem queda para pt-BR. Sem mudança de schema nem de contrato. `TermoTraducao.Termo` passa a `varchar(128)` (Log de Mudanças, 21/10/2026) — sem efeito nos termos usados pela Central.
- **v5.5** (14/10/2026) — Acompanha 01-Definição v5.5, 02-Especificação v5.5 e 03-Protótipo v5.5. Decisão em 05 - Registro de Decisões v1.4 (D-CM-043). Sem mudança de schema nem de contrato: a versão alinha a Central ao Design System de 14/10/2026 (cores).
- **v5.4** (13/10/2026) — Acompanha 01-Definição v5.4, 02-Especificação v5.4 e 03-Protótipo v5.4. Decisões em 05 - Registro de Decisões v1.3 (D-CM-027 a D-CM-042, E-CM-018 a E-CM-021). Sem mudança de schema. (1) 4.3 e 4.4: `ProcedureStatus()` passa a ser avaliada em ordem, com coluna Ordem (D-CM-030); `administered` vira `completed`; `firstTime` (`Agenda.PrimeiraConsulta = 'S'`) em `procedures` (D-CM-029); `startTime` em "HH:mm" no fuso da clínica (D-CM-042); o total de 4.4 inclui os finalizados (D-CM-038). (2) 4.6 e 4.7: janela de "a assinar" em dias de calendário, com `{LimiteAssinaturaUTC}` (D-CM-037); "a revisar" exclui a prescrição assinada depois de a janela abrir (exceção (a), D-CM-040, E-CM-019) e a de administração iniciada (exceção (b), D-CM-039, E-CM-018); "a assinar" com administração iniciada continua contada; `minutesRemaining` é a base dos textos de tempo (D-CM-041). (3) 4.8 e 4.9: `sourceType` `User | System` e `messages[].type` = `Response` (D-CM-042). (4) Revisão por Dois Críticos: `type` de 4.7 com as exceções (a) e (b); `{InicioJanelaRevisaoLocal}` marcado como valor por linha; texto do KPI `urgent` alinhado ao SQL; tabela `Paciente` listada em 4.6; contagem da massa desatualizada retirada do corpo.
- **v5.3** (10/10/2026) — Acompanha 01-Definição v5.3 e 02-Especificação v5.3. Decisões em 05 - Registro de Decisões v1.2 (D-CM-017 a D-CM-026, E-CM-016 e E-CM-017). Revisão por Dois Críticos (técnico e negócio/UX) antes de fechar: ver o Histórico do Registro. Sem mudança de schema. (1) 4.6: o limite de "a revisar" passa a `Agenda.DhAgendaIniUTC < {LimiteRevisaoUTC}`, meia-noite local de (hoje + N + 1) no fuso da clínica (D-CM-023); o texto da diferença para o sistema atual foi reescrito. (2) 4.1: `elapsedMinutes` e `overtime` (D-CM-017); `tags` retorna ordenada por finalidade e sigla (D-CM-022); vigência de tag por data, início e fim inclusivos (D-CM-021). (3) 4.7: `minutesRemaining` (E-CM-016). (4) Prescrições sem `IdAgenda` ficam fora (D-CM-025). (5) O painel "Pacientes em procedimento" substitui "Procedimentos hoje" (D-CM-019).
- **v5.2** (09/10/2026) — Acompanha 01-Definição v5.2 e 02-Especificação v5.2. Decisões em 05 - Registro de Decisões v1.1. Revisão por Dois Críticos (técnico e negócio/UX) antes de fechar: achados aplicados; os que dependem do solicitante estão em P-CM-019 a P-CM-027. Sem mudança de schema. (1) 4.6: padrão de 1 dia no `COALESCE` de `NumeroDiasRevisaoPrescricoes` (com `TRY_CONVERT`), seeds e textos; o sistema atual assume 2; `toSignWindowDays` com SQL executável; SQL de referência de `PrescriptionToSignNext7DaysCount()` incluído; `Paciente.RemovidoEm` no SQL de "a revisar"; texto de escopo alinhado à v4.46 (prescritor ou médico assistente nas três métricas). (2) 4.7: `hoursRemaining` negativo, inteiro truncado em direção a zero, classificação sobre minutos (E-CM-015); `type` com padrão de 1 dia. (3) 4.3: `progress` = `CAST(ROUND(DATEDIFF(MINUTE, DhAtendimentoIniUTC, agora) * 100.0 / NULLIF(TempoAplicacao, 0), 0) AS int)`, nulo sem tempo (D-CM-015, E-CM-012). (4) 4.1 (`tags`): `DataFim` igual ao início de hoje ainda vale hoje (E-CM-014).
- **v5.1** (09/10/2026) — Acompanha 01-Definição v5.1 e 02-Especificação v5.1; decisões em 05 - Registro de Decisões. (1) 4.2: `delayed` passa a exigir `DhAtendimentoIniUTC` nulo — consulta em atendimento é pendente, não atrasada (D-CM-008). (2) 4.1: `patient.diagnosis` só retorna diagnóstico cujo `PEP.DataInicioUTC` é até o fim de hoje no fuso da clínica (D-CM-010); `PEP.DataInicio`/`DataFim` não são lidos, só os campos UTC (D-CM-009). (3) 4.6 e Seeds: `PrescricaoTempoRevisao` (proposto, nunca aplicado) substituído por `NumeroDiasRevisaoPrescricoes`, parâmetro já existente no sistema atual, com padrão de 2 dias na ausência da linha; a janela continua contínua em horas, e não em dias de calendário como no sistema atual (D-CM-006, E-CM-005); o parâmetro deixa de ser um seed novo. (4) 4.9: o estado de resolvida/descartada fica na `Conversa`, e vale para todos os destinatários (D-CM-011). (5) Termo "Procedimentos hoje" no lugar de "Procedimentos na Clínica" no corpo (D-CM-005). (6) Corpo atemporal: retiradas algumas frases "nesta versão" das seções de prescrição; as demais, e as menções a versões antigas de documentos nas seções de mensageria e de pendências, seguem como estavam. (7) Revisão por Dois Críticos (09/10/2026): crítico técnico e crítico de negócio/UX sobre o pacote v5.1; o Designer conferiu cada achado contra os arquivos antes de aplicar. Aplicado: `IpParametro.Descricao` do seed de `PrescricaoTempoAssinatura` reduzida para caber em varchar(100); descrição de `Prescricao` na 4.6 alinhada ao filtro P/G/A/O; `TRY_CONVERT(int, ...)` na leitura de `NumeroDiasRevisaoPrescricoes`; redação atemporal dos parágrafos dos parâmetros e da resolução do nome do autor. Em aberto: escopo "prescritor ou assistente" × "apenas prescritor" nas funções de prescrição (P-CM-005).

- **v5.0** (08/10/2026) — Abertura da versão 5.0 a pedido do solicitante ("Pode ajustar todos os pontos pendentes derivadas das outras funcionalidades"). Acompanha 01-Definição v5.0 e 02-Especificação v5.0. Atende as pendências que a Agenda do Médico (Mapeamento v1.6, Pendências, item 2), o Log de Mudanças Estruturais do Banco de Dados (07 e 08/10/2026) e o Design System deixaram para a Central: (1) **Bloqueio de horário (RN-CM-030):** o filtro `Paciente.Paciente <> 'B'` passa a descrever o bloqueio feito pela Recepção (RN-AGM-019); sai a remissão a "mecanismo e seed a detalhar" na Agenda (RN-AGM-004, retirada, e RN-AGM-011). O filtro em si não muda. (2) **Tags (4.1 e 4.3):** `tags` passa de lista de nomes para lista de objetos `code`/`description`/`color`, como na Agenda (o Mapeamento já dizia que cor e descrição vêm de `ProgramaApoio`); entra a origem do prontuário, `PEPProgramaApoioPaciente` → `PEP`, com a vigência de `PEP.DataInicioUTC`/`DataFimUTC` (campos `datetimeoffset NULL` confirmados no `catalog.json` da Biblioteca de Schema — a afirmação contrária, que constava na Agenda e no Log de 08/10, não procede) e regra de intervalo para períodos com hora; a tag VIP vem de `ProgramaApoioPaciente` e `Paciente.PacienteVip` deixa de ser lido, **seguindo a decisão do solicitante registrada na Agenda do Médico (Mapeamento v1.6: "desconsidere o campo `Paciente.PacienteVip`, por enquanto")** — o achado sobre a origem de VIP, que estava **[A DEFINIR]**, fica encerrado também aqui; tabelas `PEP`, `PEPProgramaApoioPaciente`, `ProgramaApoio` e `ProgramaApoioPaciente` entram em "Tabelas envolvidas" de 4.1 e 4.3. A nota sobre a cor da tag VIP foi reescrita sem referência a versões (o histórico está nas entradas anteriores). (3) **Exclusão lógica:** regra geral no início da Seção 4 (`RemovidoEm IS NULL` nas tabelas que têm o campo, conferidas na Biblioteca de Schema — `Agenda`, `Paciente`, `PEP`, `PEPDiagnostico`, `ProgramaApoio`, `ProgramaApoioPaciente`; `Prescricao`, `PacienteFoto` e `PEPProgramaApoioPaciente` não têm) e filtro explícito em 4.1 a 4.5, 4.6 (consulta de revisão) e 4.7; `patient.diagnosis` ignora `PEP` e `PEPDiagnostico` excluídos; também em `PatientInTreatmentCount()`, `TreatmentPlanAdherenceCount()`, `PrescriptionToSignNext7DaysCount()`, 4.8 (paciente da conversa) e 4.10 (busca de pacientes). O `Status` A/I do legado não é usado como critério de ativo. (4) **Fuso:** `ClienteEmpresa.FusoOficial` (deslocamento) dá lugar a `ClienteEmpresa.FusoHorarioOficial` (IANA) em 4.1, 4.3 e 4.4; nova subseção "Campos novos" remete à definição na Agenda. (5) **`AppointmentStatus()`:** passa a ser avaliada em ordem de precedência (canceled, finished, consultation, checked, waiting, scheduled), a mesma da Agenda; resolve a sobreposição de `Situacao = 'V'` com fim preenchido e início vazio (vale `finished`). (6) **Índices:** nova subseção, **[PROPOSTA]**, remetendo ao índice proposto na Agenda (a confirmar com o time de banco) e avaliando se atende 4.1 a 4.4. (7) Próximos passos: o menu do usuário passa a ter "Tema" e "Sair". (8) **Correções de consistência** achadas na revisão: `firstTime` passa a `boolean` (`Agenda.PrimeiraConsulta = 'S'`), como na Especificação e na Agenda; `prefferedName` corrigido para `preferredName`; ids de `procedures` e `prescriptions` (4.3, 4.7) passam a `int` (`Prescricao.IdPrescricao`/`IdPaciente` são `int` no DDL; o exemplo de 4.3 trazia `uuid`); `DhAtendimentoFim` (coluna inexistente) corrigido para `DhAtendimentoFimUTC` em `ProcedureStatus()`; `Conversa.PacienteId` corrigido para `Conversa.IdPaciente` (4.8). **Não aplicado:** limpeza das referências a versões anteriores ("nesta versão", "v4.39" etc.) que ainda existem nas seções de prescrições e de mensageria — fora do escopo desta rodada.

- **v4.48** (29/09/2026) — Acompanha 01-Definição v4.42 (RN-CM-030) e 02-Especificação v4.37: (1) novo filtro `Paciente.Paciente <> 'B'` nos endpoints `GET /appointments` (4.1) e `GET /kpis/appointments-today` (4.2), com `Paciente` incluído nas tabelas envolvidas de 4.2 — o registro de bloqueio de horário da Agenda do Médico é gravado como agenda de consulta (`TipoAgenda = 'C'`, default decidido pelo solicitante) com o paciente reservado "B" (`Paciente.Paciente = 'B'`, campo já confirmado na Biblioteca de Schema — DDL, na Definição v1.2 da Agenda do Médico), então o filtro por `TipoAgenda` sozinho não o excluía; o filtro é pelo paciente reservado, não pelo convênio reservado, por ser o identificador já usado como chave de junção do endpoint. Outros endpoints deste documento não precisam do filtro: 4.3/4.4 filtram outros tipos de agenda, e 4.5 só conta agendas com `Situacao = 'V'` (check-in), que um bloqueio nunca recebe — **suposição do Designer, a confirmar na Seção 4 da Agenda do Médico**, que definirá a `Situacao` gravada no bloqueio; (2) campo `type` de 4.1 renomeado para `attendanceMode` (mesma origem, `Agenda.Atendimento`).

- **v4.47** (29/09/2026) — Acompanha 01-Definição v4.41: 2 esclarecimentos do solicitante, ambos sem mudança de comportamento observável no painel. **(1) `IdChProfissionalAssinou`/`DataAssinaturaPrescricao` são um par, não 2 fontes independentes:** RN-CM-029 (Precedência 1) verificava "3 fontes" de assinatura — `PrescricaoAssinatura.TipoAssinatura = 'A'`, `Prescricao.DataAssinaturaPrescricao`, `Prescricao.IdChProfissionalAssinou` — tratando os 2 campos legados como independentes, um deles "sem data própria" (**[A DEFINIR]** desde a v4.28). O solicitante esclareceu que são o mesmo evento de assinatura: `IdChProfissionalAssinou` é quem assinou, `DataAssinaturaPrescricao` é quando. Reescrito para "2 fontes" (moderna + par legado) em toda a Seção 4.6/4.7 (prosa de `PrescriptionToReviewCount()`, `PrescriptionUrgentCount()`, `PrescriptionToSignNext7DaysCount()`, comentários da SQL, Nota de compatibilidade legada, mapa de campos e filtro `to_sign` do endpoint). A SQL não muda (o `OR` das 2 colunas legadas é mantido por segurança contra dado legado incompleto, mas os comentários deixam de descrevê-las como independentes). **(2) Resolução do nome da equipe — `[PROPOSTA]` da v4.24 decidida:** o solicitante escolheu a alternativa de vínculo histórico — o sufixo "(Equipe)" no `authorName` passa a checar `EquipeMembro.AdicionadoEmUtc <= Mensagem.CriadoEmUtc AND (RemovidoEmUtc IS NULL OR RemovidoEmUtc > Mensagem.CriadoEmUtc)`, refletindo o vínculo na época do envio da mensagem, em vez do vínculo atual no momento em que o card/thread é exibido. Isso preserva o sufixo em mensagens antigas de quem já saiu da equipe, e não aplica o sufixo retroativamente a mensagens anteriores à entrada de um membro novo. Revisão por Dois Críticos: não aplicável — esclarecimentos/decisões diretas do solicitante sobre pontos já documentados como em aberto.
- **v4.46** (29/09/2026) — Acompanha 01-Definição v4.40 e 02-Especificação v4.36: 2 correções do solicitante sobre achados que a v4.45 havia deixado em aberto. **(1) Escopo de `to_sign` corrigido:** até esta versão, `PrescriptionToSignNext7DaysCount()` e o filtro `to_sign` do endpoint (4.7) restringiam a assinatura ao próprio prescritor (`IdChProfissionalMed = médico logado`, citando RN-CM-012) e nunca eram afetados por `reviewScope` — o solicitante esclareceu que isso está errado: qualquer médico pode assinar uma prescrição. `to_sign` passa a usar o mesmo critério "mine"/"all" de `to_review` (`reviewScope`): `mine` = `IdChProfissionalMed = médico logado` OU `Paciente.IdChProfissional = médico logado`; `all` = sem restrição. Campo `isPreferredReviewer` (4.7), antes presente só em `type = to_review`, passa a ser calculado também para `type = to_sign`. A citação a RN-CM-012 para essa restrição nunca esteve correta (essa regra trata do modo de assinatura — certificado ou sessão —, não de quem pode assinar). **(2) `TreatmentPlanAdherenceCount()` (Seção 4.5) não deve mais contar prescrições canceladas:** a Condição 2 ("prescrição esperada não realizada") incluía `Situacao = 'C'` (Cancelada) na lista de situações que contam como não-aderência — o solicitante esclareceu que uma sessão cancelada é uma decisão deliberada, não uma omissão, e não deveria contar. Lista `Situacao IN ('P','G','A','C')` corrigida para `IN ('P','G','A')`; texto do comentário ("não chegou a Q/F"), que o solicitante não entendeu, reescrito em linguagem mais direta. Ambas as correções vieram de esclarecimento direto do solicitante sobre achados já relatados na v4.45 (crítico técnico) — não de interpretação nova. Revisão por Dois Críticos: não aplicável nesta rodada.
- **v4.45** (28/09/2026) — Acompanha 01-Definição v4.39: RN-CM-020 simplificada, abolindo o conceito de "assinatura vencida" (v4.30-v4.44). Reescritos `PrescriptionToReviewCount()` (SQL e prosa), `PrescriptionUrgentCount()` e `PrescriptionToSignNext7DaysCount()` (Seção 4.6), e os filtros/mapa de campos do endpoint `/prescriptions` (Seção 4.7): a Precedência 1 ("a assinar") não verifica mais idade de assinatura, só existência (3 fontes de RN-CM-029); a Precedência 2 ("a revisar") não verifica mais validade da confirmação de revisão, só existência de QUALQUER registro `TipoAssinatura = 'R'`. Isso resolve, por tabela, a ambiguidade **[A DEFINIR]** aberta desde a v4.28 sobre `Prescricao.IdChProfissionalAssinou` sem data própria (sem vencimento a calcular, essa fonte passa a valer como as demais). **Parâmetro `PrescricaoValidadeAssinatura` (v4.30) retirado** — sua única função (janela de validade da assinatura) deixou de existir; como nunca foi aplicado em produção em nenhum cliente (**[PROPOSTA]** durante toda sua existência neste documento), a remoção não tem custo de migração. **Novo parâmetro `PrescricaoTempoAssinatura` [PROPOSTA]** — mesmo padrão `IpParametro`/`IpParametroChave` de `PrescricaoTempoRevisao`, default 7 dias — passa a governar a janela de "a assinar" (`PrescriptionToSignNext7DaysCount()`), que até esta versão usava um corte de 7 dias fixo no código, sem parâmetro (pedido explícito do solicitante: "parâmetro — se não existir, vamos criar"). **Nome `PrescricaoTempoAssinatura` é uma decisão do Claude nesta versão** (convenção "Tempo" + conceito, espelhando `PrescricaoTempoRevisao`), não um nome pedido pelo solicitante — sujeito a ajuste. **Novo filtro de `Prescricao.Situacao` (RN-CM-020), também pedido pelo solicitante nesta rodada:** `PrescriptionToReviewCount()`, `PrescriptionToSignNext7DaysCount()` e o endpoint `/prescriptions` passam a exigir `Situacao IN ('P','G','A','O')` — motivado por um achado ao conferir a nova regra contra `CargaCMZero.xlsx`: sem esse filtro, e sem o mecanismo de expiração para "limpar" o histórico, 34 das 74 prescrições da massa (incluindo registros de dez/2025, já finalizados/faturados) apareceriam em "a revisar" para sempre, por não existir nenhum registro `TipoAssinatura = 'R'` na massa hoje; com o filtro de situação, apenas 8 prescrições "em andamento" qualificam — quantificação completa e por médico nos Cenários de Teste v1.19. **Precedente:** entre 25-27/09/2026, uma simplificação semelhante já havia sido tentada e revertida a pedido do solicitante ("me precipitei" — ver Histórico/04 - Mapeamento de Banco de Dados v4.40 - REVERTIDA); confirmado explicitamente com o solicitante nesta rodada que o pedido atual (mais específico: parâmetros nomeados, filtro de situação, janela de "a assinar" parametrizada) é deliberado. Acompanha 02-Especificação v4.35, 01-Definição v4.39 e Cenários de Teste v1.19 (03-Protótipo sem mudança de conteúdo — apenas sincronização de cabeçalho). Revisão por Dois Críticos (crítico técnico): 2 correções — (1) alinhado o texto de `PrescriptionToSignNext7DaysCount()` (Seção 4.6) ao do filtro `to_sign` (Seção 4.7): a janela usa só limite superior, sem piso em "agora" — aplicações já atrasadas continuam elegíveis, como já valia para `PrescriptionToReviewCount()`; (2) mantida a formulação anterior do filtro `to_sign` (nunca afetado por `reviewScope`). Achados adicionais registrados para decisão do solicitante, fora do escopo desta rodada (não introduzidos por ela): possível inconsistência entre a restrição de `to_sign` ao próprio prescritor e o comportamento observado no toggle "Todas" nos Cenários de Teste (prescrições 61/71 cruzando entre médicos); badge do cartão de Benjamin Thompson C1D2 em CT-PRES-02.1/02.2 a conferir contra a massa real; e inclusão de `Situacao = 'C'` na Condição 2 de `TreatmentPlanAdherenceCount()` (Seção 4.5, código anterior a esta rodada) — ver relatório completo do crítico técnico.


- *(sincronização, 28/09/2026)* — sem mudança de conteúdo deste documento; apenas a linha de cabeçalho "Documentos desta funcionalidade" atualizada para apontar Especificação v4.34 e Protótipo v3.18 (mensagem de estado vazio dividida por estado do toggle nos painéis de Prescrições Pendentes e Procedimentos na Clínica — ver Histórico desses dois documentos).

- **v4.44** (28/09/2026) — Contexto acrescentado ao achado em aberto da v4.43 (Seção 4.1, campo `tags`): o solicitante classificou `Paciente.PacienteVip` como um conceito legado, de uso futuro incerto — não uma confirmação de que a associação `vip`↔paciente venha desse campo (bit), nem uma decisão de descontinuá-lo. A nota da Seção 4.1 foi reescrita para refletir essa posição; o achado continua em aberto e a lógica documentada (UNION entre `Paciente.PacienteVip` e `ProgramaApoioPaciente`/`ProgramaApoio`) não muda nesta rodada, apesar de a massa real já usar apenas o segundo mecanismo para os 2 pacientes VIP cadastrados (3 e 12). Acompanha Cenários de Teste v1.17 (nova pendência aberta em "Pendências de Massa de Dados", para que a QA não monte casos de teste assumindo `Paciente.PacienteVip` como fonte).

- **v4.43** (28/09/2026) — Correção pedida pelo solicitante: a cor da tag VIP é verde, `#80FF38`, conforme cadastrada em `ProgramaApoio` — confirmado contra `CargaCMZero.xlsx` (`IdProgramaApoio=1`, Nome='VIP'). Isso contradiz a nota da Seção 4.1 (campo `tags`) que, desde sempre neste documento, dizia que `vip` "usa o token de destaque do Design System, Seção 2.8" — uma suposição que nunca teve entrada própria no Histórico como decisão confirmada, e que a Seção 2.8 da Especificação (conferida nesta rodada) não sustenta. Corrigido: cor/descrição de `vip` também vêm de `ProgramaApoio` (mesma fonte das outras 7 tags), mantendo intacta a exceção de `vip` aos filtros de `MostrarEm` e vigência (regra de negócio à parte, RN-CM-025, confirmada e revisada em versões anteriores — sem mudança aqui). **Achado em aberto, sinalizado mas não resolvido nesta rodada:** a massa real já tem `vip` associado a pacientes via `ProgramaApoioPaciente` (mesmo mecanismo das outras tags, não via `Paciente.PacienteVip`) — ver nota completa e pendência de confirmação na Seção 4.1. Acompanha 02-Especificação (sem mudança de conteúdo nesta rodada) e Cenários de Teste v1.16 (3 ocorrências de "VIP (lilás-claro)" corrigidas para "VIP (verde)") e 03-Protótipo v3.17 (`.chip-vip` corrigido de azul para verde).

- **v4.42** (28/09/2026) — Decisões do solicitante sobre os pontos em aberto da v4.41: (1) `PatientInTreatmentCount()` (4.5): default de 180 dias quando `TempoPacienteAtivoAgenda`/`TempoPacienteAtivoPrescricao` não existirem para a clínica — implementado via `COALESCE(..., 180)` nas duas subconsultas, substituindo o `[A DEFINIR]`; (2) divergência de valor resolvida — o correto é 180 dias; o seed de `Infra-estrutura/(BD-M) Dados incluídos obrigatórios.md` (que gravava 120) foi corrigido, com a cópia anterior arquivada em `Infra-estrutura/Histórico/`; (3) janela de `PrescricaoTempoRevisao` confirmada como contínua a partir do momento atual (1 dia = 24 horas), como já descrito na v4.41 — sem mudança de texto.

- **v4.41** (28/09/2026) — Pedido do solicitante, após levantamento dos parâmetros usados pela Central do Médico. (1) **Seção 4.5, `PatientInTreatmentCount()`:** a query de exemplo usava `dateadd(day,-180, ...)` fixo — passa a ler `TempoPacienteAtivoAgenda` e `TempoPacienteAtivoPrescricao` de `IpParametroChave` (mesmo padrão de 4.6); unidade dos dois parâmetros definida como dias; corrigido o erro de digitação `IpParametroTempoPacienteAtivoPrescricao` → `IpParametro.TempoPacienteAtivoPrescricao`; `IpParametro`/`IpParametroChave` (GescomClienteAlfa, conferidas na Biblioteca de Schema — DDL, `gescomAlfa.sql`) adicionadas às Tabelas envolvidas. Registrados como pendência: comportamento quando esses parâmetros não existem para a clínica ([A DEFINIR]) e a divergência entre o seed de `Infra-estrutura/(BD-M) Dados incluídos obrigatórios.md` (120 dias) e o padrão de RN-CM-024 (180 dias). (2) **`PrescricaoTempoRevisao` em dias (Seção 4.6, 4.7 e Seeds):** unidade muda de horas para dias; `DATEADD(HOUR, ..., 24)` → `DATEADD(DAY, ..., 1)`; seed `Valor` 24 → 1; `Descricao` do `IpParametro` ajustada; explicitado que a janela é contínua a partir de agora (1 dia = 24 horas), não por dia de calendário — mantém o comportamento anterior. `IpParametro`/`IpParametroChave` adicionadas às Tabelas envolvidas de 4.6. (3) **Modo de assinatura (RN-CM-012):** registrado como [A DEFINIR] ao final de "Seeds da Funcionalidade" — o parâmetro que guarda a configuração ainda não é conhecido.

- *(sincronização, 28/09/2026)* — sem mudança de conteúdo deste documento; apenas a linha de cabeçalho "Documentos desta funcionalidade" atualizada para apontar Especificação v4.31 (janela de antecedência de revisão de RN-CM-020 descrita como parâmetro por clínica, `PrescricaoTempoRevisao`, em vez de 24h fixo).

- **v4.40** (28/09/2026) — Reescrita das funções de assinar/revisar (4.6) e do endpoint 4.7, para RN-CM-020 (Precedência 1/2, reescrita) e RN-CM-029 (nova) em 01-Definição v4.36, corrigindo uma rodada anterior (25-27/09/2026) revertida por ser precipitada. Mudanças: (1) `PrescriptionToReviewCount()` reescrita para separar claramente as duas precedências — a idade que importa para "vencida → a assinar" (Precedência 1) é a mais recente entre `PrescricaoAssinatura.TipoAssinatura = 'A'` e o campo legado `Prescricao.DataAssinaturaPrescricao` (RN-CM-029, via `OUTER APPLY`/`UNION ALL`, não mais um `COALESCE` simples); a existência de `TipoAssinatura = 'R'` dentro da mesma janela de validade agora é checada separadamente (Precedência 2, "já foi revisada") em vez de estar misturada na mesma cláusula `NOT EXISTS` que também comparava `TipoAssinatura = 'A'`. (2) `PrescriptionToSignNext7DaysCount()` reescrita — antes só cobria "nunca assinada"; agora cobre a Precedência 1 completa de RN-CM-020 (nunca assinada OU assinada mas vencida), usando a mesma lógica de idade de `PrescriptionToReviewCount()` com o sinal invertido. (3) `PrescriptionUrgentCount()` atualizada para reaproveitar `PrescriptionToSignNext7DaysCount()` em vez de duplicar sua própria checagem de "não assinada" desatualizada. (4) Domínio de `PrescricaoAssinatura.TipoAssinatura` (4.7) ganhou nota explicando que `Prescricao.DataAssinaturaPrescricao`/`IdChProfissionalAssinou` não geram linha real na tabela — são lidos diretamente de `Prescricao`. (5) Campo `type` e filtros `to_sign`/`to_review` do endpoint 4.7 atualizados para apontar para as funções corrigidas, em vez de repetir a lógica antiga inline. **[A DEFINIR]** registrado em RN-CM-029/01-Definição: `Prescricao.IdChProfissionalAssinou`, sem data própria, é tratado como assinatura sem vencimento enquanto não houver confirmação do solicitante em contrário — refletido nas queries acima (`assinatura.UltimaData IS NULL` como caso separado, nunca reprovado por idade). Nenhuma tabela nova — `PrescricaoAssinatura.TipoAssinatura = 'R'` já estava documentado no domínio desde antes desta versão, só não tinha uma query própria que o isolasse da checagem de vencimento da assinatura original. Verificado contra a massa real (`CargaCMZero.xlsx`, data-base 24/09/2026 12:00): das prescrições rastreadas em `Cenários de Teste`, só a 70 muda de classificação (sai de "a assinar" — via RN-CM-029, sua assinatura mais recente, `DataAssinaturaPrescricao` de 18/09, está dentro da validade, mas a aplicação está a mais de 24h, então também não entra em "a revisar"); nenhum registro `TipoAssinatura = 'R'` existe na massa hoje, então a Precedência 2 não reclassifica nenhuma prescrição adicional nesta rodada — cobertura de dado real para esse caminho fica pendente para uma carga futura. Detalhamento completo: `Cenários de Teste v1.10`. Revisão por Dois Críticos (crítico técnico): 1 correção — a primeira versão desta query usava `COALESCE` simples entre a subquery de `PrescricaoAssinatura` e `DataAssinaturaPrescricao`, o que escolhe o primeiro valor não nulo em vez do mais recente; corrigido para `UNION ALL` + `MAX()`, garantindo que a data mais recente das duas fontes prevalece mesmo quando a fonte legada é a mais nova (caso real da prescrição 70).

- *(sincronização, 25/09/2026)* — sem mudança de conteúdo deste documento; apenas a linha de cabeçalho "Documentos desta funcionalidade" atualizada para apontar Definição v4.35 e Especificação v4.30 (RN-CM-027 e RN-CM-028, ordenação do Painel de Procedimentos e tag "1ª Consulta" — ver Histórico desses dois documentos).
- *(sincronização, 24/09/2026)* — sem mudança de conteúdo deste documento; apenas a linha de cabeçalho "Documentos desta funcionalidade" atualizada para apontar Especificação v4.29 (confirmação do critério de desempate das conversas lidas no painel de Mensagens — ver Histórico desse documento).
- **v4.39** (24/09/2026) — Esclarecimento do solicitante, fechando um ponto que eu tinha sinalizado como não confirmado na v4.38: `DhAtendimentoIniUTC` marca o início real do atendimento — preparo do paciente, pré-medicações etc. — que antecede o início da infusão propriamente dita; `DhInfusaoIniUTC`/`DhInfusaoFimUTC` (usados só em `ProcedureStatus()`) marcam início/fim do primeiro medicamento do protocolo, que ocorre depois desse preparo. Confirma que `startTime` (Seção 4.3) já usa o campo correto (atendimento, não infusão) — não muda a fórmula, só documenta o porquê. Observação acrescentada ao Mapa de Campos. Sem mudança de RN/regra de negócio (01/02/03 não mudam — apenas cabeçalho sincronizado). Revisão por Dois Críticos: não aplicável (esclarecimento factual, sem decisão nova a revisar).
- **v4.38** (23/09/2026) — Pedido do solicitante, complementando RN-CM-026 (01-Definição v4.34) e Seção 2.6 (02-Especificação v4.28): fórmula de `startTime` (4.3) ganhou um terceiro ramo, avaliado primeiro — quando o procedimento está Finalizado (`Situacao='V' AND DhAtendimentoFimUTC IS NOT NULL`, mesma condição de `finished` em `ProcedureStatus()`), o campo passa a trazer `Agenda.DhAtendimentoFimUTC` em vez de `DhAtendimentoIniUTC`. Observação adicionada sinalizando que o campo continua se chamando `startTime` por herança do contrato já em uso, mesmo passando a carregar um horário de término em um dos casos — não renomeado nesta revisão, por ser mudança de contrato fora do pedido desta rodada. Revisão por Dois Críticos (crítico técnico): nenhuma correção necessária.
- *(sincronização, 23/09/2026)* — sem mudança de conteúdo deste documento; apenas a linha de cabeçalho "Documentos desta funcionalidade" atualizada para apontar Definição v4.33, Especificação v4.27 e Protótipo v3.15 (RN-CM-011 — texto simples para tempo restante ≥24h no painel de Prescrições Pendentes, sem badge — ver Histórico desses três documentos). Sem mudança de Seção 4: a lógica é só de apresentação sobre o campo `hoursRemaining`, que já existia.
- **v4.37** (23/09/2026, mesmo dia) — Correção do solicitante: o filtro de `MostrarEm` dos dois endpoints (Consultas, Seção 4.1; Procedimentos na Clínica, Seção 4.3) usava o bit 4 (`& 8`, "Central do Médico") desde a v4.34 — presumido por ser o nome da própria tela, sem confirmar com o solicitante. Está errado: os dois painéis são, na prática, agendas do dia (de consultas e de quimioterapia/infusão), e o bit que rege a exibição é o 6 (`& 32`, "Agendas"), independente de qual tela os hospeda; corrigido nos dois endpoints (Mapa de Campos e Filtros). Confirmado com o solicitante que o segundo painel (Procedimentos na Clínica) segue a mesma regra do primeiro, em vez de perguntar duas vezes. Tabela de domínio (Seção 4.1) ajustada: removida a nota errada "(este documento)" do bit 4; anotado no bit 6 que é o usado pelos dois endpoints daqui; acrescentada nota explícita de que o bit 4 não é usado por nenhum endpoint deste documento, e que sua aplicação em outro lugar do Cockpit (se houver) não foi confirmada nesta rodada — para não deixar a impressão de que a investigação cobriu mais do que cobriu. RN-CM-025 (01-Definição) não muda — a regra de negócio ("habilitada para esta tela") continua correta em linguagem de negócio, só o bit técnico por trás dela mudou. Revisão por Dois Críticos (crítico técnico): conferido que a correção não deixou nenhuma menção residual a `& 8`/bit 4 em nenhum dos dois endpoints, nem no exemplo `MostrarEm = 38` (que já usava bit 6, não precisou mudar); nenhuma correção adicional necessária.
- **v4.36** (23/09/2026) — Pedido do solicitante: harmonizada a convenção de borda de `ProgramaApoio.DataFim` — passa a ser inclusiva (`hoje <= DataFim`), igual a `ProgramaApoioPaciente.DataFim`; era exclusiva (`hoje < DataFim`) desde a v4.35, quando as duas tabelas foram documentadas lado a lado com convenções diferentes (cada uma seguindo o que havia sido informado literalmente para aquele campo, sem harmonizar por conta própria). Atualizados: tabela de domínio (4 casos), Mapa de Campos (campo `tags`) e Filtros do endpoint, Seção 4.1; Seção 4.3 referencia 4.1, sem alteração própria. Sem mudança em `MostrarEm` nem no filtro de vigência da atribuição ao paciente (`ProgramaApoioPaciente`, inalterado). RN-CM-025 (01-Definição) não muda — a regra de negócio já era atemporal, sem mencionar a convenção de borda; ajuste é só técnico. Revisão por Dois Críticos (crítico técnico): conferido que a mudança não afeta a lógica combinada com `MostrarEm` nem o caso `DataInicio`+`DataFim` (agora ambos os limites inclusivos), nenhuma correção necessária.
- **v4.35** (22/09/2026) — Pedido do solicitante, complementando a v4.34 no mesmo tema: adicionado o filtro de vigência da própria tag, `ProgramaApoio.DataInicio`/`DataFim` (ambos `datetime NULL`, confirmados na Biblioteca de Schema — DDL, `gescomAlfa.sql`) — distinto do filtro de vigência da atribuição ao paciente (`ProgramaApoioPaciente.DataInicio`/`DataFim`), já existente e sem mudança. Criada, na Seção 4.1 (Domínios), a tabela dos 4 casos (nenhuma data / só início / só fim / ambas) com a condição de exibição de cada um — caso "só início" confirmado pelo solicitante como `hoje >= DataInicio` (a leitura literal da primeira formulação dada pelo solicitante nesta rodada, `DataInicio > hoje`, contradizia a lógica simétrica dos outros dois casos — perguntado antes de escrever, não presumido). Sinalizada explicitamente a diferença de convenção de borda entre esta tabela e `ProgramaApoioPaciente` (`DataFim` exclusivo aqui vs. inclusivo lá) — mantida como informada por cada campo, não harmonizada por conta própria. Filtro adicionado aos dois endpoints (Seções 4.1 e 4.3), combinado por E com o filtro de `MostrarEm` já existente; não se aplica à tag `vip`. RN-CM-025 (01-Definição v4.32) reescrita em paralelo. Revisão por Dois Críticos (crítico técnico): conferida a coerência das duas convenções de borda documentadas lado a lado, nenhuma correção necessária.
- **v4.34** (22/09/2026) — Pedido do solicitante: documentado o filtro de exibição de tags de atenção, ausente até esta versão — o campo `tags` (Seções 4.1 e 4.3) buscava todas as tags ativas do paciente (`Paciente.PacienteVip` UNION `ProgramaApoioPaciente`/`ProgramaApoio`) sem checar em quais telas cada tag foi habilitada. Adicionado o filtro `(ProgramaApoio.MostrarEm & 8) = 8` aos dois endpoints (bit 4 do domínio — Central do Médico), à tabela "Mapa de campos" (campo `tags`) e à lista "Filtros do endpoint" de cada um; comportamento passa a ser efetivamente implementado, não só documentado. Criada, na Seção 4.1 (Domínios), a primeira tabela completa do domínio `ProgramaApoio.MostrarEm` deste projeto — mapa de bits de 6 telas com função definida (prontuário, cadastro do paciente, recepção, Central do Médico, confirmação de quimio/Central de atendimento, agendas) e 2 bits (7 e 8) ainda sem função, informado pelo solicitante. O rótulo do bit 5 (valor 16) ficou registrado com dois termos — "Confirmação de quimio" (já registrado antes no Histórico da Agenda do Médico, RN-AGM-006, v1.2) e "Central de atendimento" (informado pelo solicitante nesta rodada) — sem decidir qual é o nome correto/oficial da tela; nenhuma das duas fontes foi descartada, e a divergência foi sinalizada ao solicitante antes de escrever a tabela, sem invenção de qual estaria certa. RN-CM-025 (01-Definição v4.31) reescrita em paralelo para refletir a regra em linguagem de negócio, sem nomes de campo nem aritmética de bits (Lição #20 da Skill Designer). Revisão por Dois Críticos (crítico técnico, Skill Designer) sobre a Seção 4.1/4.3: conferida a aritmética da tabela de domínio contra o exemplo do próprio solicitante (`MostrarEm = 38` = bits 2+3+6 = 32+4+2, cadastro do paciente + recepção + agendas — bate); conferido que o filtro `(MostrarEm & 8) = 8` não é aplicado à tag `vip` (correto, `vip` não vem de `ProgramaApoio`); conferido que 4.3 referencia 4.1 em vez de duplicar a tabela (Lição #10). Nenhuma correção necessária.
- **v4.33** (16/09/2026) — Quarta correção de `TreatmentPlanAdherenceCount()` (Seção 4.5): o solicitante explicou como `PrescricaoProtocolo.Sessao` é de fato definida — o médico informa "é a sessão N do protocolo X, ciclo Y" e o sistema copia o N-ésimo valor de `ProtocoloGrupo.SessoesSugerido` para `Sessao` (e Y para `Ciclo`); `Sessao` portanto REINICIA a cada ciclo (não é cumulativo desde o C1D1, como a v4.32 assumia por interpretação própria, não confirmada — e que estava errada). Isso quebrava a condição 3 para sessões de `Ciclo > 1`: comparar `diasDecorridos` (contado desde o C1D1 global, sem limite) direto contra `Sessao` (sempre um valor pequeno, reiniciado por ciclo) não fecha a conta a partir do 2º ciclo. Corrigido com âncora LOCAL por ciclo: cada Ciclo N tem sua própria âncora (a linha de `PrescricaoProtocolo` daquele ciclo com o menor `Sessao`, isto é, a 1ª sessão do ciclo); a condição 3 passa a ter duas partes — 3a) sessão comum do ciclo: `diasDecorridos` contado desde a âncora do MESMO ciclo, comparado a `Sessao` com `ToleranciaEntreSessoes`; 3b) início de um novo ciclo (a própria âncora, exceto a do Ciclo 1): `diasDecorridos` entre a âncora do ciclo atual e a do ciclo anterior, comparado a `Protocolo.IntervaloCiclosEmDias` com `ToleranciaEntreCiclos`. Isso também dá finalmente um uso concreto e coerente para `ToleranciaEntreCiclos`, que nas versões v4.31/v4.32 não tinha um cenário claro de aplicação. Confirmado pelo solicitante com exemplo numérico completo (`SessoesSugerido="1,8,15"`, `IntervaloCiclosEmDias=21`) antes de escrever no documento. Tabela "Tabelas envolvidas": descrição de `ProtocoloGrupo.SessoesSugerido` corrigida (não é mais "valor cumulativo", é o valor bruto indexado por sessão). Nota sobre criação de prescrições (individual e em massa) adicionada como contexto, fora do escopo deste endpoint.
- **v4.32** (16/09/2026) — Terceira correção de `TreatmentPlanAdherenceCount()` (Seção 4.5), pedido do solicitante ao revisar a v4.31: (1) a condição 1 (C1D1 real vs. previsto) não pode comparar contra o agendamento (`Agenda`) sozinho — ela só é possível quando o paciente tem um plano terapêutico (`PEPProjetoTerapeuticoProtocolo`) para o mesmo protocolo da prescrição real; quando existe, compara-se o C1D1 real (`Agenda.DhAgendaIniUTC`) com o C1D1 planejado (`PEPProjetoTerapeuticoProtocoloProgramacao.DataEstimada`, `Ciclo=1`/`Sessao=1`); quando não existe plano para aquele protocolo, a condição não se aplica ao paciente. `PEP`, `PEPProjetoTerapeutico`, `PEPProjetoTerapeuticoProtocolo` e `PEPProjetoTerapeuticoProtocoloProgramacao` voltam à tabela "Tabelas envolvidas", agora só para esta condição. (2) A condição 3 estava mais complicada do que deveria: `Sessao`, em `PrescricaoProtocolo`, já é o total de dias corridos esperados desde o C1D1 (não um deslocamento só dentro do ciclo) — a comparação é direta, `ABS(diasDecorridos − Sessao)` contra a tolerância, sem a fórmula `(Ciclo−1) × IntervaloCiclosEmDias + Sessao` que a v4.31 usava (removida); a escolha entre `ToleranciaEntreSessoes`/`ToleranciaEntreCiclos` pelo ciclo da linha-alvo continua igual. Exemplo numérico do solicitante incorporado ao texto para eliminar ambiguidade futura.
- **v4.31** (16/09/2026) — Segunda correção de conceito em `TreatmentPlanAdherenceCount()` (Seção 4.5), pedido do solicitante ao revisar a v4.30: o mecanismo adotado na v4.30 (comparar a sessão realizada com `PEPProjetoTerapeuticoProtocoloProgramacao.DataEstimada`) dependia de um plano terapêutico formal (`PEPProjetoTerapeutico`) ter sido criado — na prática isso falha porque (a) nem sempre há vínculo entre a prescrição real e a sessão projetada do plano, e (b) muitos médicos não chegam a criar um plano terapêutico no sistema. Substituído por um cálculo ancorado na prescrição C1D1 real (`PrescricaoProtocolo` com `Ciclo=1`/`Sessao=1`), sem depender de nenhum plano: para cada prescrição real vinculada a protocolo, compara-se a diferença de dias em relação ao C1D1 com o valor de `Sessao` (que já armazena dias corridos desde o C1D1, não um índice ordinal — esclarecido pelo solicitante), somado a `(Ciclo−1) × IntervaloCiclosEmDias` quando a sessão está em outro ciclo. Duas tolerâncias distintas substituem a proposta anterior de uma única `ToleranciaDiasAdesao`: `Protocolo.ToleranciaEntreSessoes` (desvio dentro do mesmo ciclo) e `Protocolo.ToleranciaEntreCiclos` (desvio que atravessa fronteira de ciclo) — ambas **[PROPOSTA]**, esclarecido pelo solicitante que são dois parâmetros, não um só. A condição 1 (C1D1 vs. agendado) e a condição 2 (prescrição esperada não realizada) tiveram mecânica inalterada; a condição 1 continua com sua origem ("data prevista" = agendamento em `Agenda`) marcada como interpretação não confirmada nesta rodada, sinalizada no corpo do documento. Tabela "Tabelas envolvidas" da Seção 4.5 atualizada: `PEPProjetoTerapeutico`, `PEPProjetoTerapeuticoProtocolo` e `PEPProjetoTerapeuticoProtocoloProgramacao` removidas (não são mais usadas por este endpoint); `PrescricaoProtocolo` e `ProtocoloGrupo` adicionadas. RN-CM-023 da Definição (v4.28) reescrita em paralelo para refletir o novo cálculo em linguagem de negócio, sem nomes de campo.
- **v4.30** (16/09/2026) — Rodada de correções após a primeira revisão de versão aprovada (Agente Técnico, ver Skill Designer). (1) Correção de conceito em `TreatmentPlanAdherenceCount()` (Seção 4.5): a 2ª das três condições de não adesão comparava incorretamente o intervalo entre sessões consecutivas a `Protocolo.IntervaloCiclosEmDias` (campo que é o intervalo ENTRE CICLOS, não entre sessões de um mesmo ciclo — apontado pelo solicitante). Reescrita para comparar a data real de cada sessão realizada com `PEPProjetoTerapeuticoProtocoloProgramacao.DataEstimada` (campo já existente na Biblioteca de Schema — DDL, confirmado nesta versão), que já materializa a data prevista de cada sessão a partir de `ProtocoloGrupo.SessoesSugerido`. Tabela "Tabelas envolvidas" da Seção 4.5 atualizada — `Protocolo`, `PEPProjetoTerapeutico`, `PEPProjetoTerapeuticoProtocolo` e `PEPProjetoTerapeuticoProtocoloProgramacao` já eram usadas na função mas não apareciam listadas ali. (2) Criado o parâmetro `IpParametro.PrescricaoValidadeAssinatura` (Seção 4.6) para a "janela de 7 dias" de validade de assinatura/revisão de RN-CM-020, que até esta versão era um `DATEADD(DAY, -7, ...)` fixo no SQL de `PrescriptionToReviewCount()` — mesmo padrão de `PrescricaoTempoRevisao`; adicionado à tabela "Seeds da Funcionalidade". (3) Removidas as ocorrências de "confirmado com/pelo solicitante" e afins do corpo da Seção 4 (proveniência de decisão não deve aparecer fora do Histórico — ver Lições Aprendidas da Skill Designer); o conteúdo de cada uma está preservado nas entradas de Histórico correspondentes (v4.26/v4.27, acima, para os parâmetros; este parágrafo, para a lógica "OU" de três condições e a coluna `ToleranciaDiasAdesao`, ambas [PROPOSTA] já registradas na Seção 4.5 desde antes da divisão em 4 arquivos). (4) "Próximos Passos" (itens 4 e 5) marcados como feitos — ícones Lucide implementados no Protótipo v3.14; item 6 ajustado para refletir que a navegação por teclado já foi implementada, restando apenas validação formal com usuários.
- **v4.25** (29/08/2026) — Arquivo criado pela divisão do documento único `CM_CockPit_do_medico_v4_25.md` em 4 arquivos por seção (ver Skill Designer, "Organização Física: Pasta por Funcionalidade", Lição #18). Conteúdo desta seção sem alteração de substância em relação à v4.25 do documento original — apenas reorganização física. O histórico de revisões anterior a esta divisão (v4.0 a v4.25, incluindo o racional de cada mudança) está preservado integralmente no documento original, arquivado em `Histórico/CM_CockPit_do_medico_v4_25 (documento único, antes da divisão em 4 arquivos).md`.
- **v4.26** (30/08/2026) — Pedido do solicitante: a janela de "1 dia (24h)" que dispara o estado "a revisar" de uma prescrição (RN-CM-020) precisa existir como parâmetro real de banco, não só como texto de regra de negócio — mesmo padrão já usado para outros parâmetros por clínica neste documento (`TempoPacienteAtivoAgenda`/`TempoPacienteAtivoPrescricao`, Seção 4.5). Adicionado, na Seção 4.6: (1) referência ao parâmetro `IpParametro.PrescricaoTempoRevisao` → `IpParametroChave.Valor` na query de `PrescriptionToReviewCount()`, substituindo o `DATEADD(HOUR, 24, ...)` fixo; (2) nova subseção "Parâmetro de configuração — PrescricaoTempoRevisao" **[PROPOSTA]**, com a definição completa das 2 linhas de dado necessárias (`IpParametro`: `IdParametro='PrescricaoTempoRevisao'`, `Descricao`, `Dominio=NULL`, `UsuarioControla='S'`, `Tipo='N'`; `IpParametroChave`: `IdParametro='PrescricaoTempoRevisao'`, `IdChave={Id da Clínica}`, `Valor=24`, `IdChave2=NULL`) e um alerta explícito para Dev/DevOps sobre a necessidade de incluir esse seed na documentação de deploy de cada cliente. Não é uma tabela nova — `IpParametro`/`IpParametroChave` já existem. Filtro `type = to_review` do endpoint 4.7 atualizado para referenciar o mesmo parâmetro em vez de "D-1" fixo.
- **v4.27** (30/08/2026) — Pedido do solicitante, resolvendo o `[A DEFINIR]` da v4.26: (1) comportamento definido para quando `IpParametro`/`IpParametroChave` de `PrescricaoTempoRevisao` não existir para a clínica — default de 24h, confirmado pelo solicitante — implementado na query de `PrescriptionToReviewCount()` (Seção 4.6) via `COALESCE(..., 24)` em vez do `SELECT` direto; parágrafo "Atenção Dev/DevOps" reescrito para descrever esse comportamento e remover a pendência; (2) nova subseção "Seeds da Funcionalidade" ao final da Seção 4, consolidando em uma tabela todo dado (não schema) que precisa existir em produção para a funcionalidade funcionar como especificado — pedido explícito do solicitante ("seria bom termos uma subseção com os seeds da funcionalidade"). Por ora contém 1 linha (`PrescricaoTempoRevisao`, **[PROPOSTA]**), apontando para a Seção 4.6 em vez de repetir a definição (Lição #10).
- **v4.29** (15/09/2026) — Pedido do solicitante: documentado o domínio completo de `Prescricao.Situacao` (P/G/A/Q/F/O/E/C — Confirma Prescrição, Gerada, Admitida, Confirmada/Finalizada, Faturada, Aguardando Orçamento, Atendimento Externo, Cancelada), na Seção 4.5 (KPI "Em Tratamento"), onde o campo já era usado em dois filtros (`PatientInTreatmentCount()`, `TreatmentPlanAdherenceCount()`) sem a semântica de cada código estar registrada. Confirmado contra a Biblioteca de Schema (DDL): `Prescricao.Situacao` é `char(1) NOT NULL`, consistente com os 8 códigos de uma letra informados pelo solicitante; a DDL não tem `CHECK` explícito listando os valores, então o domínio em si vem do solicitante, não da DDL. Deixado explícito que este domínio é distinto do da agenda associada (`SituacaoAgenda`) — uma prescrição não avança necessariamente junto com sua agenda.
- **v4.28** (11/09/2026) — Correção pontual do solicitante: o valor de `PrescricaoAssinatura.TipoAssinatura` para a assinatura do médico está errado em todo o documento — era `'M'`, o correto é `'A'`. Corrigido em todas as ocorrências da Seção 4.6 (prosa e SQL de `PrescriptionToReviewCount()`, `PrescriptionUrgentCount()`, `PrescriptionToSignNext7DaysCount()`) e da Seção 4.7 (tabela de domínio, mapa de campos, filtros do endpoint, nota final). Não é o mesmo valor de `PrescricaoAssinaturaEnfermagem.TipoAssinatura = 'E'` (tabela distinta, confirmada na Biblioteca de Schema/DDL) — apenas o `'M'`→`'A'` de `PrescricaoAssinatura` estava incorreto. Nenhuma outra seção deste conjunto de documentos (01, 02, 03) referenciava esse campo — correção isolada à Seção 4.
