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

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

---

## 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. Campos novos: o fuso IANA da clínica (`ClienteEmpresa.FusoHorarioOficial`, IpSeguranca) e o padrão de auditoria em `UnidadeAtendimento` — ver "Campos novos".

Regras do sistema aplicadas aqui:

- **Exclusão lógica:** um registro excluído tem `RemovidoEm` preenchido (com `UsuarioIdRemovido`); toda consulta ignora registros com `RemovidoEm` preenchido. O campo `Status` com `A`/`I` (ativo/inativo) 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.
- **Clínicas do cliente:** as clínicas que estão no mesmo banco de cliente (`ClienteBD`).

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) |
| 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 horário (IANA, RN-AGM-018) 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 |
| `timeZone` | string (IANA) | `ClienteEmpresa.FusoHorarioOficial` (IpSeguranca) da clínica em que o usuário entrou | Campo novo (ver "Campos novos"); ex.: `America/Sao_Paulo`. Mesma linha de `ClienteEmpresa` que dá o idioma (`IdiomaOficial`) |
| `serverNow` | data/hora | relógio do servidor, convertido para `timeZone` | ISO 8601 com deslocamento |
| `appointments[].id` | int | `Agenda.IdAgenda` | |
| `startTime` | horário | `Agenda.DhAgendaIniUTC` (NULL possível na DDL), convertido para `timeZone` | 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), convertido para `timeZone` | **[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. Positivo só quando `date` é hoje (em `timeZone`), o horário já passou, `Agenda.Situacao IN ('A','V')` e `DhAtendimentoIniUTC IS NULL`; em qualquer outro caso, `0` — inclusive canceladas |
| `firstTime` | boolean | `Agenda.PrimeiraConsulta = 'S'` | Domínio `S`/`N` |
| `tags[].code` | string | `ProgramaApoio.Sigla` | Texto do chip |
| `tags[].description` | string | `ProgramaApoio.Descricao` | Dica e rótulo acessível |
| `tags[].color` | string | `ProgramaApoio.Cor` | Hexadecimal, fundo do chip (padrão `.chip-tag`) |
| `tags` (seleção) | — | `ProgramaApoioPaciente` (por `IdPaciente`) → `ProgramaApoio` | Filtros de RN-CM-025 (Central do Médico, 4.1): `(MostrarEm & 32) = 32`, vigência da tag (`ProgramaApoio.DataInicio`/`DataFim`) e da atribuição (`ProgramaApoioPaciente.DataInicio`/`DataFim`), **avaliadas no dia `date`** (RN-AGM-006), e `RemovidoEm IS NULL` nas duas tabelas. Tag VIP: origem **[A DEFINIR]** — ver "Pendências", item 1 |
| `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 tenha sido excluída logicamente depois — 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 tenha sido excluída depois |
| `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. 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)`) | Exibida mesmo que a classificação tenha sido excluída depois (mesmo raciocínio de `clinic`) |
| `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). Também não são usados `ClienteEmpresa.FusoOficial` (`smallint`, deslocamento) nem `Clinica.FusoHorario` (deslocamento, legado) — o fuso vem de `ClienteEmpresa.FusoHorarioOficial`.

#### 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 nesta ordem: 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 — 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 — "clínicas do cliente".
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
  AND uc.RemovidoEm   IS NULL
  AND cpau.RemovidoEm IS NULL
  AND ce.RemovidoEm   IS NULL;
```

Mesmo critério de "clínica acessível" do Check-in de Usuários (RN-CIU-009, Épico 1), restrito ao banco atual. Não há caso de consulta do médico numa clínica a que ele não tem acesso: o profissional só tem horário (e, portanto, agenda) nas clínicas a que está vinculado.

`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
WHERE IdClinica  = @IdChClinica
  AND StConsulta = 'S'
  AND RemovidoEm IS NULL;     -- campo novo nesta tabela (ver "Campos novos")
```

#### Referência de tempo (RN-AGM-018)

Tudo no fuso da clínica em que o usuário entrou: `serverNow`, o que é "hoje", o dia de cada consulta (filtro de `date`) e os horários `startTime`/`endTime` — inclusive das consultas de outras clínicas do cliente. O fuso vem de `ClienteEmpresa.FusoHorarioOficial` (IpSeguranca; identificador IANA, ex.: `America/Sao_Paulo`) e é devolvido em `timeZone`, para a tela informar qual fuso está exibindo. A conversão de `DhAgendaIniUTC`/`DhAgendaFimUTC` é feita na aplicação (o `AT TIME ZONE` do SQL Server não aceita nomes IANA). Como há um só fuso por requisição, os limites do dia viram uma faixa UTC exata: `@inicioUtc` = 00:00 de `date` em `timeZone`, convertido para UTC; `@fimUtc` = 00:00 do dia seguinte, convertido.

#### 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`) |
| `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 |
| `ClienteEmpresa.FusoHorarioOficial` | identificador IANA | Fuso da clínica (ex.: `America/Sao_Paulo`) | Solicitante — campo novo |

#### 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, regra de todo o sistema (a Central também deve aplicá-la — ver "Pendências", item 4).
- `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 >= @inicioUtc AND Agenda.DhAgendaIniUTC < @fimUtc` (ver "Referência de tempo").
- Tags: filtros de `MostrarEm`, vigências (no dia `date`) e exclusão lógica (ver `tags` no mapa de campos).
- 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).

```sql
-- @fromUtc = 00:00 de @from em timeZone, em UTC; @toUtc = 00:00 do dia seguinte a @to, em UTC.
-- A data local de cada consulta (no fuso timeZone) é calculada na aplicação a partir de DhAgendaIniUTC.
SELECT a.DhAgendaIniUTC
FROM Agenda a
JOIN Paciente p ON p.IdPaciente = a.IdPaciente
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;
-- Resposta: datas distintas (no fuso timeZone), em ordem crescente.
```

### 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: @limiteUtc = 00:00 do dia seguinte a @from (timeZone), em UTC.
SELECT TOP 1 a.DhAgendaIniUTC
FROM Agenda a
JOIN Paciente p ON p.IdPaciente = a.IdPaciente
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 >= @limiteUtc
ORDER BY a.DhAgendaIniUTC ASC;
-- previous: @limiteUtc = 00:00 de @from, em UTC; condição DhAgendaIniUTC < @limiteUtc e ORDER BY DESC.
-- Resposta: a data local (timeZone) do resultado, ou null.
```

### 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).

### Campos novos

| Tabela (banco) | Campo | Tipo | Obrigatoriedade | Motivo | Status |
| --- | --- | --- | --- | --- | --- |
| `ClienteEmpresa` (IpSeguranca) | `FusoHorarioOficial` | `varchar(64)` **[PROPOSTA]** (os identificadores IANA têm até ~32 caracteres) | **[PROPOSTA]** NOT NULL, depois de preencher as clínicas existentes | Fuso da clínica em convenção IANA (RN-AGM-018); a tabela já tem `FusoOficial` (`smallint NOT NULL`, deslocamento), que não identifica o fuso IANA | Decidido pelo solicitante; migração a criar (Log de Mudanças, 07/10/2026) |
| `UnidadeAtendimento` (GescomClienteAlfa) | 16 campos do padrão de auditoria do framework (`CriadoEm` … `IpRemovido`, inclusive `RemovidoEm` e `UsuarioIdRemovido`) | conforme `framework_pattern.md` (Biblioteca de Schema) | conforme o padrão | Exclusão lógica no lugar de `Status` A/I (regra do sistema); usado por `UnitVisible()` | Regra decidida pelo solicitante; migração a criar |

Migração de dados **[PROPOSTA]**: `ClienteEmpresa.FusoHorarioOficial` preenchido para cada clínica (o deslocamento atual não identifica o fuso IANA de forma única — ex.: −03:00 serve a vários fusos —, então o valor é informado na implantação); em `UnidadeAtendimento`, registros com `Status = 'I'` recebem `RemovidoEm` = data da migração.

### Índices

Os campos novos da arquitetura do Gemed 2.1 precisam de índices próprios — decisão do solicitante, registrada no Log de Mudanças Estruturais do Banco de Dados (07/10/2026). Em `Agenda`, os índices existentes (Biblioteca de Schema — DDL) 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`, `FK_Agenda_IdChRecurso` — usam a data legada `Agenda.Data`, não `DhAgendaIniUTC`, e os compostos começam por `IdChClinica`.

Para esta funcionalidade, **[PROPOSTA]** índice não único `IX_Agenda_IdChRecurso_TipoAgenda_DhAgendaIniUTC (IdChRecurso, TipoAgenda, DhAgendaIniUTC) INCLUDE (Situacao, IdPaciente, DhAgendaFimUTC, RemovidoEm)` — atende a faixa de `GET /day`, `days-with-appointments` e o `TOP 1` de `next-appointment-date`. A confirmar com o time de banco, 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 3.

### 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` |
| Fuso IANA de cada clínica | `ClienteEmpresa.FusoHorarioOficial` (IpSeguranca) | Por cliente | **[PROPOSTA]** — campo novo; preencher na implantação de cada clínica | "Campos novos" |
| Sigla, descrição e cor de cada tag exibida em agendas | `ProgramaApoio.Sigla`/`Descricao`/`Cor` (GescomClienteAlfa) | Por cliente | Cadastro existente — conferir que as tags com `MostrarEm & 32` têm os três preenchidos | 4.1, campo `tags` |

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

### Pendências

1. **[A DEFINIR]** Origem da tag VIP — `Paciente.PacienteVip` ou `ProgramaApoioPaciente` (achado em aberto na Central do Médico, 4.1, campo `tags`); também define se o VIP passa pelos filtros de `MostrarEm` e de vigência e como evitar que apareça duas vezes.
2. **[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.
3. Trilha de auditoria (NGS1.07.03, estágio 1) — mecanismo transversal, não mapeado aqui; precisa existir para a conformidade.
4. Central do Médico (congelada): aplicar `Agenda.RemovidoEm IS NULL` (e a exclusão lógica nas demais tabelas que consulta) quando for descongelada.
5. **Divergência de tipo registrada:** `Agenda.IdAgendaClassificacao` é `int` e não tem FK, enquanto a PK de `AgendaClassificacao` é `smallint`. O join funciona; o risco é de integridade. Registrado no Log de Mudanças Estruturais do Banco de Dados (07/10/2026).
6. **[PROPOSTA]** Tamanho e obrigatoriedade de `ClienteEmpresa.FusoHorarioOficial`, migração dos dados (ver "Campos novos") e índice novo em `Agenda` (ver "Índices").

---

## 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`.
- **v1.1** (07/10/2026) — Respostas do solicitante às pendências da v1.0. (1) **Exclusão lógica no lugar de `Status`:** no legado, a maioria das tabelas usa `Status` `A`/`I`; a regra está sendo descontinuada e substituída por `RemovidoEm`/`UsuarioIdRemovido`, valendo para todo o sistema — `UnitVisible()` passa a usar `RemovidoEm IS NULL`, o que exige o padrão de auditoria em `UnidadeAtendimento` (que não o tem na DDL — nova subseção "Campos novos"); tags passam a filtrar `RemovidoEm` em `ProgramaApoio` e `ProgramaApoioPaciente`; a Central deve aplicar a regra quando descongelada; pendência do valor ativo de `Status` encerrada. (2) **Fuso:** tudo no fuso da clínica do login — a proposta de fuso por clínica da consulta foi recusada; campo novo `Clinica.FusoHorarioOficial` (IANA, ex.: `America/Sao_Paulo`), que o solicitante informou estar sendo criado para substituir `Clinica.FusoHorario` (deslocamento); `GET /day` devolve `timeZone` (RN-AGM-018) e a tela informa o fuso; SQLs de 4.2/4.3 simplificados para faixa UTC exata, com a conversão na aplicação. Nova pendência: ligação entre `ClienteEmpresa` (clínica do login) e `Clinica`. (3) **Tags:** `code` = sigla (`ProgramaApoio.Sigla`), `description`, `color`; vigências no dia exibido (confirmado); origem do VIP continua indefinida, por decisão do solicitante. (4) **Clínicas do cliente** = mesmo `ClienteBD`, confirmado. (5) **Risco da clínica escondida** retirado: o solicitante esclareceu que a Recepção não consegue agendar o médico numa clínica a que ele não está vinculado, porque ele só tem horários nas clínicas do seu vínculo. (6) **Índices:** registrado que os campos novos da arquitetura do Gemed 2.1 precisam de índices (Log de Mudanças, 07/10/2026). Acompanha 01-Definição v1.12, 02-Especificação v1.14 e 03-Protótipo v1.12.
- **v1.2** (08/10/2026) — Correção do solicitante: o fuso IANA fica em `IpSeguranca.ClienteEmpresa.FusoHorarioOficial`; não há esse campo em `Clinica` (GescomClienteAlfa). `timeZone`, "Tabelas envolvidas", "Referência de tempo", "Domínios", "Campos novos" e "Seeds" passam a apontar para `ClienteEmpresa` — a mesma linha que já dá o idioma da clínica do login. Encerrada a pendência da ligação entre `ClienteEmpresa` e `Clinica` (não é mais necessária para o fuso); pendências renumeradas. Sem mudança de regra, contrato ou tela.

