# Agenda do Médico — Mapeamento de Banco de Dados (v1.0)

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

---

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

Fonte da estrutura: Biblioteca de Schema (DDL), `Para IA/Biblioteca de Schema (DDL)/catalog.json`. Base de referência: **GescomClienteAlfa** (banco já adaptado à arquitetura do Gemed 2.1) e **IpSeguranca**/**IpTerminologia**. Nenhuma tabela nova e nenhum campo novo: tudo o que a tela exibe já existe e é preenchido pelo Agendamento (Recepção).

Itens ainda não confirmados estão marcados **[A DEFINIR]** (falta um fato) ou **[PROPOSTA]** (decisão do Designer a confirmar); a lista consolidada está em "Pendências", ao final.

### 4.1 Endpoint: GET /api/v1/agenda/doctor/day

Retorna as consultas do médico logado num dia (`date`), de qualquer status, de todas as clínicas do cliente (RN-AGM-002).

#### Tabelas envolvidas

| Tabela | Banco | Função no endpoint |
| --- | --- | --- |
| Agenda | GescomClienteAlfa | Tabela principal — uma linha por consulta |
| Paciente | GescomClienteAlfa | Nome, nome social e o indicador de paciente reservado (filtro) |
| PacienteFoto | GescomClienteAlfa | Foto do paciente |
| PEP / PEPDiagnostico | GescomClienteAlfa | Diagnósticos oncológicos do paciente |
| ProgramaApoio / ProgramaApoioPaciente | GescomClienteAlfa | Tags do paciente (RN-AGM-006) |
| Chave | GescomClienteAlfa | Nome curto da clínica e do convênio (RN-AGM-017) |
| Clinica | GescomClienteAlfa | Fuso horário da clínica de cada consulta **[PROPOSTA]** (ver "Referência de tempo") |
| UnidadeAtendimento | GescomClienteAlfa | Nome da unidade e contagem de unidades ativas para consultas (RN-AGM-017, b) |
| AgendaClassificacao / AgendaSubClassificacao | GescomClienteAlfa | Classificação e subclassificação (RN-AGM-008) |
| TermoTraducao | IpTerminologia | Rótulo traduzido do modo de atendimento |
| ClienteEmpresa | IpSeguranca | Idioma e fuso da clínica em que o usuário entrou; contagem de clínicas acessíveis |
| ClientePerfilAcessoUsuario / UsuarioCliente | IpSeguranca | Clínicas do cliente a que o médico tem acesso (RN-AGM-017, a) |

#### Mapa de campos

Os campos herdados da Central do Médico têm a mesma origem do endpoint `GET /api/v1/cockpit/doctor/appointments` (Central do Médico, Mapeamento de Banco de Dados, Seção 4.1) — referenciados, não repetidos.

| Campo | Tipo | Origem | Observação |
| --- | --- | --- | --- |
| `date` | data | parâmetro `date` | Eco do dia pedido |
| `serverNow` | data/hora | relógio do servidor, no fuso de `ClienteEmpresa.FusoOficial` da clínica em que o usuário entrou | Referência de tempo da tela (Especificação, "Referência de tempo") |
| `appointments[].id` | int | `Agenda.IdAgenda` | |
| `startTime` | horário | `Agenda.DhAgendaIniUTC` (NULL possível na DDL) | No fuso de "Referência de tempo". Consulta com `DhAgendaIniUTC` NULL não entra na lista (não tem dia nem posição) |
| `endTime` | horário | `Agenda.DhAgendaFimUTC` (NULL possível na DDL) | No fuso de "Referência de tempo". **[PROPOSTA]** Se NULL, `DhAgendaIniUTC + Agenda.Tempo` minutos (`Tempo`, `numeric(4,0) NOT NULL`, é a duração gravada pela Recepção); se o fim cai no dia seguinte, `"24:00"` (o item termina no fim do dia exibido) |
| `status` | string | calculado — `AppointmentStatus()` | Central do Médico, 4.1 ("Funções de cálculo"): `scheduled`, `waiting`, `checked`, `consultation`, `finished`, `canceled`. Ver "Funções de cálculo", abaixo, sobre precedência |
| `attendanceMode.code` | char | `Agenda.Atendimento` | Domínio `ModoAtendimento` (Central do Médico, 4.1, "Domínios") |
| `attendanceMode.label` | string | `Agenda.Atendimento` → `TermoTraducao.TermoValor` | Mesma tradução da Central: Termo = `Agenda.Atendimento`, Origem = `Indicadores\|ModoAtendimento`, Idioma = `ClienteEmpresa.IdiomaOficial`. **[PROPOSTA]** Sem tradução no idioma da clínica, usar a de pt-BR |
| `delayMinutes` | int | calculado | Minutos entre agora e `DhAgendaIniUTC`, calculados em UTC (a diferença não depende de fuso). Positivo só quando `date` é hoje, o horário já passou, `Agenda.Situacao IN ('A','V')` e `DhAtendimentoIniUTC IS NULL`; em qualquer outro caso, `0` — inclusive canceladas, que esta tela exibe e a Central não |
| `firstTime` | boolean | `Agenda.PrimeiraConsulta = 'S'` | Domínio `S`/`N` |
| `tags` | array | `Paciente.PacienteVip` + `ProgramaApoioPaciente`/`ProgramaApoio` | Origem, filtro `(MostrarEm & 32) = 32`, vigências e exceção VIP: Central do Médico, 4.1 (RN-CM-025). Na Agenda, "hoje" das vigências é o dia exibido (`date`) **[PROPOSTA]**. Cor, descrição e o achado em aberto sobre a origem do VIP: ver "Pendências", item 3 |
| `complement` | string \| null | `Agenda.Complemento` (`varchar(128)`, NULL) | |
| `patient.id` | int | `Agenda.IdPaciente` | |
| `patient.preferredName` / `legalName` | string | `Paciente.NomeSocial` / `Paciente.Nome` | Central do Médico, 4.1 (lá grafado `prefferedName`) |
| `patient.photo` | url \| null | `PacienteFoto` (`Padrao = 'S'`, a mais recente) | Central do Médico, 4.1 e "Infraestrutura de apoio" (MinIO) |
| `patient.diagnosis` | array | `PEPDiagnostico.Diagnostico`, `Tipo = 'O'` | Central do Médico, 4.1 — todos os diagnósticos oncológicos ativos (critério de "ativo" herdado da Central) |
| `clinic` | string \| null | `Agenda.IdChClinica` → `Chave.IdChave` → `Chave.Descricao` | Nome curto da clínica (`Clinica.IdChClinica` é FK para `Portador`, que é FK para `Chave`). `null` quando `ClinicVisible()` é falso (RN-AGM-017, a). **[PROPOSTA]** O nome aparece mesmo que a clínica esteja inativa hoje (`Chave.Status`) — a consulta aconteceu ou acontecerá ali |
| `unit` | string \| null | `Agenda.IdUAtendimento` (NULL possível) → `UnidadeAtendimento.Descricao` (`varchar(30)`) | `null` quando `Agenda.IdUAtendimento` é NULL ou quando `UnitVisible(Agenda.IdChClinica)` é falso (RN-AGM-017, b). **[PROPOSTA]** Quando aparece, o nome é exibido mesmo que aquela unidade específica esteja inativa hoje |
| `insurance` | string | `Agenda.IdChConvenio` (NOT NULL) → `Chave.IdChave` → `Chave.Descricao` | Nome curto do convênio (`Convenio.IdChConvenio` é FK para `Portador` → `Chave`). Sempre preenchido; o particular é um registro de `Convenio` com `Convenio.Convenio = 'T'` e aparece pelo nome curto cadastrado. **[PROPOSTA]** Exibido mesmo que o convênio esteja inativo hoje (`Chave.Status`). O plano (`Agenda.Plano`, `Agenda.IdPlano` → `ConvenioPlano`) não é usado (RN-AGM-017, c) |
| `classification` | objeto \| null | — | `null` quando `Agenda.IdAgendaClassificacao` é NULL (RN-AGM-008) |
| `classification.name` | string | `Agenda.IdAgendaClassificacao` → `AgendaClassificacao.Descricao` (`varchar(20)`) | **[PROPOSTA]** Exibida mesmo que a classificação esteja inativa hoje (`AgendaClassificacao.Status`) |
| `classification.subName` | string \| null | `Agenda.IdAgendaSubClassificacao` (FK) → `AgendaSubClassificacao.Descricao` (`varchar(30)`) | `null` quando não há subclassificação |

Campos de `Agenda` que existem e **não** são usados: `TipoConsulta` (`char(1) NOT NULL`, significado não confirmado), `Plano`, `IdPlano`, `IdChClinicaFat` (clínica de faturamento — a tela mostra a clínica do atendimento, `IdChClinica`), `Data`/`HoraIni`/`HoraFim` (data e horário legados; a tela usa `DhAgendaIniUTC`/`DhAgendaFimUTC`, como a Central).

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

`AppointmentStatus()` — a da Central do Médico (4.1). Precedência adotada aqui **[PROPOSTA]**, porque as condições da Central se sobrepõem num caso: `Situacao = 'V'` com `DhAtendimentoFimUTC` preenchido e `DhAtendimentoIniUTC` NULL casa com `checked` e com `finished` — vale `finished`. As condições são avaliadas de `canceled` para `scheduled` (canceled, finished, consultation, checked, waiting, scheduled). `Agenda.Situacao` é `NOT NULL`; um código fora do domínio `SituacaoAgenda` não entra na lista (filtro abaixo).

`ClinicVisible()` — o médico tem acesso a mais de uma clínica do cliente (RN-AGM-017, a). Calculada uma vez por sessão (pode ficar em cache — ela consulta outro banco e não precisa ser refeita a cada atualização automática):

```sql
-- Verdadeiro quando há mais de uma clínica (ClienteEmpresa) acessível ao usuário
-- no mesmo banco do cliente (ClienteBD) da sessão.
SELECT CASE WHEN COUNT(DISTINCT cpau.ClienteEmpresaId) > 1 THEN 1 ELSE 0 END
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 [PROPOSTA]
  AND uc.RemovidoEm   IS NULL
  AND cpau.RemovidoEm IS NULL
  AND ce.RemovidoEm   IS NULL;
```

O filtro por `ClienteBDId` é **[PROPOSTA]**: "clínicas do cliente" interpretado como as `ClienteEmpresa` do mesmo banco de cliente — a massa de teste (`CargaCMZero.xlsx`) tem duas `ClienteEmpresa` ("Clinica A" e "Clínica B") no mesmo `ClienteBD`. Mesmo critério de "clínica acessível" do Check-in de Usuários (RN-CIU-009, Épico 1), restrito ao banco atual. Ver "Pendências", item 6, sobre o caso de consulta numa clínica a que o médico não tem acesso.

`UnitVisible(IdChClinica)` — a clínica da consulta tem mais de uma unidade ativa para consultas (RN-AGM-017, b). Calculada uma vez por clínica presente no resultado, não por consulta:

```sql
SELECT CASE WHEN COUNT(*) > 1 THEN 1 ELSE 0 END
FROM UnidadeAtendimento                -- sem RemovidoEm na DDL
WHERE IdClinica  = @IdChClinica
  AND StConsulta = 'S'
  AND Status     = @StatusAtivo;       -- [A DEFINIR] valor de "ativa" em UnidadeAtendimento.Status
```

#### Referência de tempo — **[PROPOSTA]**

A agenda reúne clínicas que podem estar em fusos diferentes — a massa de teste tem "Clinica A" (`FusoOficial = -4`) e "Clínica B" (`-3`) no mesmo banco de cliente. A proposta:

- `serverNow` e o que é "hoje": fuso da clínica em que o usuário entrou (`ClienteEmpresa.FusoOficial`), como na Central do Médico.
- Horários e dia de cada consulta: fuso da **clínica da consulta**, `Clinica.FusoHorario` (`varchar(35) NOT NULL`, GescomClienteAlfa) — o horário mostrado é o horário local onde o paciente será atendido. Formato do campo (nome IANA, nome de fuso do Windows ou deslocamento): **[A DEFINIR]** — decide se a conversão pode ser feita no banco (`AT TIME ZONE` do SQL Server aceita só nomes do Windows) ou precisa ser feita na aplicação.

**Esta proposta muda o contrato da Especificação**, que hoje define `startTime`/`endTime` como `HH:MM` "no fuso da clínica", no singular, e usa `serverNow` para a linha "AGORA", a rolagem inicial e o "Hoje": com dois fusos na mesma lista, comparar `serverNow` com `startTime` deslocaria esses elementos. Se aceita, a Especificação passa a mandar também o início e o fim de cada consulta em ISO 8601 com deslocamento (para o front comparar com `serverNow` em UTC) e a mostrar, quando os fusos diferem, a qual fuso o horário se refere.

Alternativa, se a proposta não for aceita: tudo no fuso da clínica do login (comportamento da Central, contrato atual), aceitando que as consultas de uma clínica em outro fuso apareçam com o horário convertido para o fuso do login.

#### Domínios

`SituacaoAgenda` (`Agenda.Situacao`), `ModoAtendimento` (`Agenda.Atendimento`), `TipoAgenda` (`Agenda.TipoAgenda`), `TipoDiagnostico` e `MostrarEm`: definidos uma única vez em Central do Médico, Mapeamento de Banco de Dados, 4.1 ("Domínios") — não repetidos aqui.

Domínios usados só por esta funcionalidade:

| Campo | Valor | Significado | Fonte |
| --- | --- | --- | --- |
| `UnidadeAtendimento.StConsulta` | `S` / `N` | Unidade atende (ou não) consultas | Solicitante; massa de teste (`CargaCMZero.xlsx`) |
| `UnidadeAtendimento.Status` | **[A DEFINIR]** | Unidade ativa / inativa | — |
| `Convenio.Convenio` | `T` | Convênio "Particular" | Solicitante; massa de teste |
| `Convenio.Convenio` | `B` | Convênio reservado (na massa de teste, usado junto com o paciente reservado) | Massa de teste |
| `Convenio.Convenio` | NULL | Convênio comum | Massa de teste — **[A DEFINIR]** se há outros valores em produção |
| `Paciente.Paciente` | `B` | Paciente reservado, que não representa consulta (`char(1) NOT NULL`) | Central do Médico, RN-CM-030; demais valores não mapeados aqui |

#### Filtros do endpoint

Mesmo critério de seleção do endpoint de consultas da Central do Médico (4.1), sem o filtro de "hoje" e sem o filtro de situação:

- `Agenda.IdChRecurso = {IdChProfissional do médico logado}` — mesmo critério da Central; **sem filtro por clínica** (RN-AGM-002).
- `Agenda.TipoAgenda = 'C'` — agendas de consulta.
- `Paciente.Paciente <> 'B'` (via `Agenda.IdPaciente`; `Paciente.Paciente` é `NOT NULL`, então o filtro não descarta pacientes comuns) — exclui registros de agenda com o paciente reservado, que não representam consulta (mesmo filtro da Central, RN-CM-030).
- `Agenda.RemovidoEm IS NULL` — exclusão lógica (o campo existe em `Agenda` na DDL). **[A DEFINIR]** se a Central também deve aplicar este filtro (o Mapeamento dela não o cita).
- `Agenda.Situacao IN ('A','V','C','D','F','I','M','O','T')` — todo o domínio `SituacaoAgenda`; canceladas (inclusive falta) entram, com `status = canceled` (RN-AGM-009/010).
- `Agenda.DhAgendaIniUTC IS NOT NULL` e dia de `DhAgendaIniUTC`, no fuso de "Referência de tempo", igual a `date`. Para usar índice, a consulta filtra primeiro uma faixa UTC ampla (`DhAgendaIniUTC >= @dataInicioUtc - 14h AND DhAgendaIniUTC < @dataFimUtc + 14h`, folga do maior deslocamento de fuso) e aplica o dia exato no fuso como filtro residual.
- Tags: filtros de `MostrarEm` e de vigência da Central (4.1), avaliados no dia `date`.
- Ordenação: `Agenda.DhAgendaIniUTC` crescente.

### 4.2 Endpoint: GET /api/v1/agenda/doctor/days-with-appointments

Retorna as datas, entre `from` e `to` (máximo de 62 dias), com ao menos uma consulta não cancelada do médico (marcador do calendário — RN-AGM-003).

#### Tabelas envolvidas

`Agenda`, `Paciente` (filtro), `Clinica` (fuso da consulta, se a proposta de "Referência de tempo" for aceita) — mesmas de 4.1.

#### Consulta

```sql
-- @fromUtc/@toUtc: início de @from e fim de @to, com folga de 14h para cada lado.
-- LocalDate(): data de DhAgendaIniUTC no fuso de "Referência de tempo" (4.1) —
-- no banco, se Clinica.FusoHorario for nome de fuso do Windows; senão, na aplicação.
SELECT DISTINCT LocalDate(a.DhAgendaIniUTC, c.FusoHorario) AS Data
FROM Agenda a
JOIN Paciente p ON p.IdPaciente  = a.IdPaciente
JOIN Clinica  c ON c.IdChClinica = a.IdChClinica
WHERE a.IdChRecurso = @IdChProfissional
  AND a.TipoAgenda  = 'C'
  AND p.Paciente   <> 'B'
  AND a.RemovidoEm IS NULL
  AND a.Situacao IN ('A', 'V')          -- não cancelada (domínio SituacaoAgenda)
  AND a.DhAgendaIniUTC >= @fromUtc AND a.DhAgendaIniUTC < @toUtc
  AND LocalDate(a.DhAgendaIniUTC, c.FusoHorario) BETWEEN @from AND @to   -- filtro residual
ORDER BY Data;
```

### 4.3 Endpoint: GET /api/v1/agenda/doctor/next-appointment-date

Retorna a data da próxima (`direction = next`) ou anterior (`previous`) consulta não cancelada do médico, a partir de `from` (exclusive), sem limite de horizonte (RN-AGM-003). É chamada a cada carregamento de dia e a cada atualização automática, nas duas direções — precisa ser barata.

```sql
-- next: a primeira consulta depois do fim de @from (com folga de fuso); previous: o inverso, com DESC.
SELECT TOP 1 LocalDate(a.DhAgendaIniUTC, c.FusoHorario) AS Data
FROM Agenda a
JOIN Paciente p ON p.IdPaciente  = a.IdPaciente
JOIN Clinica  c ON c.IdChClinica = a.IdChClinica
WHERE a.IdChRecurso = @IdChProfissional
  AND a.TipoAgenda  = 'C'
  AND p.Paciente   <> 'B'
  AND a.RemovidoEm IS NULL
  AND a.Situacao IN ('A', 'V')
  AND a.DhAgendaIniUTC >= @fromFimUtc - 14h                         -- faixa usa o índice
  AND LocalDate(a.DhAgendaIniUTC, c.FusoHorario) > @from             -- filtro residual
ORDER BY a.DhAgendaIniUTC ASC;
```

Sem a proposta de fuso por clínica, `LocalDate()` usa o fuso da clínica do login e o `JOIN Clinica` sai.

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

Mapeamento da Central do Médico, Seção 4.10 — reutilizado sem mudança (RN-AGM-014).

### Índices

Índices existentes em `Agenda` (Biblioteca de Schema — DDL, GescomClienteAlfa) que tocam o médico ou o tipo de agenda: `IX_Agenda_IdChClinica_IdChRecurso_TipoAgenda_Data`, `IX_Agenda_IdChClinica_IdChRecurso_IdUAtendimento_Data_Situacao`, `IX_Agenda_IdChClinica_TipoAgenda_Situacao_Data`, `IX_Agenda_Situacao_TipoAgenda`, `IX_Agenda_TipoAgenda` e o de FK `FK_Agenda_IdChRecurso`. Todos os compostos começam por `IdChClinica` e usam a data legada `Agenda.Data`, não `DhAgendaIniUTC`. Como esta tela não filtra por clínica (RN-AGM-002) e usa `DhAgendaIniUTC`, nenhum deles cobre as três consultas.

**[PROPOSTA]** índice não único `IX_Agenda_IdChRecurso_TipoAgenda_DhAgendaIniUTC (IdChRecurso, TipoAgenda, DhAgendaIniUTC) INCLUDE (Situacao, IdPaciente, IdChClinica, DhAgendaFimUTC, RemovidoEm)` — atende a faixa de `GET /day`, `days-with-appointments` e o `TOP 1` de `next-appointment-date`; `IdChClinica` no INCLUDE evita ir à tabela base para achar o fuso. A confirmar com o time de banco antes de criar, junto com o impacto em escrita (a `Agenda` é muito gravada pela Recepção).

### Auditoria (NGS1.07.03 / NGS1.07.04)

A leitura da agenda de um dia (`GET /day` ao carregar um dia; o polling do mesmo dia não gera novo evento) e a abertura do PEP a partir dela geram evento de auditoria (Especificação, Conformidade SBIS). O mecanismo e a tabela da trilha de auditoria são transversais ao sistema e não estão mapeados neste documento — ver "Pendências", item 8.

### Tabelas novas

Nenhuma.

### Nota sobre registros de paciente reservado

O filtro `Paciente.Paciente <> 'B'` vem da Central do Médico (RN-CM-030). Esta funcionalidade não cria nem altera esses registros, e não tem seed para eles. A Central (congelada) ainda remete a "mecanismo e seed a detalhar na Seção 4" da Agenda do Médico — remissão que fica sem objeto e deve ser retirada da Central quando ela for descongelada.

### Seeds da Funcionalidade

| Seed | Tabela(s) | Por cliente ou global? | Status | Detalhe completo |
| --- | --- | --- | --- | --- |
| Traduções do domínio `ModoAtendimento` (Presencial, Teleatendimento, Customizado) nos idiomas pt-BR, en-US e es-419 | `TermoTraducao` (IpTerminologia) | Global | Já usado pela Central do Médico — conferir que as três línguas existem | 4.1, campo `attendanceMode.label` |
| Nome curto de cada clínica e de cada convênio, inclusive "Particular" | `Chave.Descricao` (GescomClienteAlfa) | Por cliente | Já em produção (cadastro de clínica e convênio) | 4.1, campos `clinic` e `insurance` |

Nenhum parâmetro novo (`IpParametro`/`IpParametroChave`): a tela não tem limiar configurável próprio.

### Pendências

1. **[A DEFINIR]** Valor de "ativa" em `UnidadeAtendimento.Status` (usado em `UnitVisible()`); a massa de teste não preenche o campo.
2. **[PROPOSTA]** Referência de tempo com várias clínicas: horários e dia de cada consulta no fuso da clínica da consulta (`Clinica.FusoHorario`) — **muda o contrato da Especificação** (início/fim em ISO 8601 por consulta). **[A DEFINIR]** o formato de `Clinica.FusoHorario`.
3. **[PROPOSTA]** Contrato de `tags` com cor e descrição — a Especificação define `tags` como lista de códigos, mas a cor vem do cadastro (`ProgramaApoio.Cor`/`Descricao`, Central 4.1). Proposta: `tags: [{ "code", "label", "color", "description" }]`, com `code` = `ProgramaApoio.IdProgramaApoio`, `label` = `ProgramaApoio.Nome`; quando o VIP chegar pelos dois caminhos (`Paciente.PacienteVip` e `ProgramaApoioPaciente`), aparece uma vez só. Depende do achado em aberto da Central (4.1, campo `tags`) sobre a origem do VIP. Exige ajuste da Especificação.
4. **[PROPOSTA]** Vigência das tags avaliada no dia exibido (`date`), não em "hoje".
5. **[PROPOSTA]** "Clínicas do cliente" = `ClienteEmpresa` do mesmo `ClienteBD` da sessão (em `ClinicVisible()`). Não há campo que ligue `ClienteEmpresa` (IpSeguranca) a `Clinica` (GescomClienteAlfa) — a regra não precisa dessa ligação, mas qualquer funcionalidade futura que precise saber "qual `Clinica` é esta `ClienteEmpresa`" vai precisar.
6. **Risco a decidir:** se o médico tem acesso a uma única clínica, mas a Recepção o agendou também em outra, a lista mistura as duas e `clinic` vem vazio em todas (RN-AGM-017, a, fala em "acesso"). Alternativa: mostrar a clínica quando as consultas do dia vierem de mais de uma clínica.
7. **[PROPOSTA]** Precedência de `AppointmentStatus()` (caso `V` com fim sem início = `finished`) — a sobreposição é herdada da Central e deve ser corrigida lá também.
8. Trilha de auditoria (NGS1.07.03, estágio 1) — mecanismo transversal, não mapeado aqui; precisa existir para a conformidade.
9. **Divergência de tipo registrada:** `Agenda.IdAgendaClassificacao` é `int` e não tem FK, enquanto a PK de `AgendaClassificacao` é `smallint` (`Agenda.IdAgendaSubClassificacao`, por sua vez, tem FK). O join funciona; o risco é de integridade (classificação apagada sem erro). Registrado no Log de Mudanças Estruturais do Banco de Dados como descoberta.
10. **[PROPOSTA]** Índice novo em `Agenda` (ver "Índices").
11. **[A DEFINIR]** Filtro `Agenda.RemovidoEm IS NULL` na Central do Médico (aplicado aqui; não citado no Mapeamento da Central).

---

## Histórico de Versões

- **v1.0** (07/10/2026) — Arquivo criado. Primeira versão da Seção 4, a pedido do solicitante ("podemos ir para a próxima seção"), sobre 01-Definição v1.11, 02-Especificação v1.13 e 03-Protótipo v1.11. Estrutura conferida na Biblioteca de Schema (DDL) — `Agenda`, `Paciente`, `Chave`, `Portador`, `Clinica`, `Convenio`, `UnidadeAtendimento`, `AgendaClassificacao`, `AgendaSubClassificacao` (GescomClienteAlfa); `ClienteEmpresa`, `ClientePerfilAcessoUsuario`, `UsuarioCliente` (IpSeguranca), incluindo nulabilidade, existência de `RemovidoEm` e índices de `Agenda` — e valores de exemplo na massa de teste `CargaCMZero.xlsx` (nomes curtos em `Chave`, `Convenio.Convenio` = `T`/`B`/NULL, `UnidadeAtendimento.StConsulta`, duas `ClienteEmpresa` no mesmo `ClienteBD` com fusos diferentes). Fatos dados pelo solicitante durante a Definição v1.9–v1.10 e usados aqui: nome curto da clínica e do convênio em `Chave.Descricao`; status também em `Chave.Status`; plano escrito em `Agenda.Plano` (não usado); acesso a mais de uma clínica por `ClientePerfilAcessoUsuario`; unidade ativa para consultas por `UnidadeAtendimento.StConsulta = 'S'`; particular com `Convenio.Convenio = 'T'`. Campos e regras herdados da Central do Médico referenciados ao Mapeamento dela (v4.48, Seção 4.1 e 4.10), sem repetição. Achado registrado: `Clinica.IdChClinica` e `Convenio.IdChConvenio` chegam a `Chave` via `Portador` (FKs da DDL) — é esse o caminho do nome curto.
- **v1.0 — Revisão por Dois Críticos** (07/10/2026, mesma versão, antes da entrega ao solicitante) — Crítico técnico (subagente independente, DBA/back-end) sobre esta Seção 4 e o contrato da Especificação; 14 achados, cada um conferido contra a DDL antes de aplicar. **Aplicados:** (1) a proposta de fuso por clínica muda o contrato de `GET /day` (linha "AGORA", rolagem e "Hoje" comparam `serverNow` com `startTime`) — registrado em "Referência de tempo" e na pendência 2; (2) `delayMinutes` positivo para canceladas — passa a exigir `Situacao IN ('A','V')` e sem início de atendimento, calculado em UTC; (3) SQLs de 4.2/4.3 sem a junção do fuso e sem uso de índice — reescritos com faixa UTC com folga e filtro residual, e `TOP 1` em 4.3; (4) vigência das tags quando o dia exibido não é hoje — pendência 4; (5) clínica escondida quando o médico tem acesso a uma só clínica mas foi agendado em outra — pendência 6; `uc.RemovidoEm` e cache de `ClinicVisible()`; (6) exclusão lógica — conferido na DDL: `Agenda`, `Paciente`, `ProgramaApoio` e `ProgramaApoioPaciente` têm `RemovidoEm`; `UnidadeAtendimento`, `Chave`, `Clinica`, `Convenio` e as tabelas de classificação não têm — filtro `Agenda.RemovidoEm IS NULL` acrescentado; (7) tipo dos identificadores: a Especificação dava `uuid`, o mapeamento `int` — Especificação corrigida para `int` (v1.13); (8) `Situacao` fora do domínio e sobreposição de `checked`/`finished` em `AppointmentStatus()` — filtro pelo domínio e precedência (pendência 7); (9) índice proposto ganhou `IdChClinica`, `DhAgendaFimUTC` e `RemovidoEm` no INCLUDE; `UnitVisible()` por clínica distinta; (10) `endTime` com `DhAgendaFimUTC` NULL (a DDL permite) ou atravessando a meia-noite — regra proposta; (11) afirmações vindas só da massa de teste ou de decisão do Designer marcadas como tal (convênio `B`, NULL comum, valor `R` do modo de atendimento, clínica/convênio/classificação inativos); (12) pendências completadas: auditoria, origem do VIP e do `code` das tags, tradução ausente do modo; (13) remissão da Central ao "mecanismo e seed" na Seção 4 da Agenda tratada em nota própria; `classification.name` explicitado. **Não aplicado (verificado como não procedente):** risco de `Paciente.Paciente` NULL descartar pacientes comuns no filtro `<> 'B'` — a DDL define `Paciente.Paciente` como `char(1) NOT NULL`.
