HR_INTERVALOAGEND - Intervalo mínimo entre agendamentos de prova (locação)
Tela: MFA001 - Parâmetros de Faturamento Localização: Parâmetros → Faturamento → aba Locações (
pagLocacaoemUMFA001.dfm:10630-10631,Caption = 'Locações') →GroupBox5 ' Parâmetros de Locações '→ campo Intervalo Minímo entre Agendamentos (labellbl62emUMFA001.dfm:10744-10756,Caption = 'Intervalo Minímo entre Agendamentos', posicionado emLeft=32, Top=191) Tabela:PARMFATUR.HR_INTERVALOAGENDTipo: Hora / Intervalo (DOM_TIMEno DDL —docs/schema/tables/PARMFATUR.sql:174);TTimeFieldno client (UdmMFA001.pas:225); editado viaTwwDBDateTimePicker edtHR_INTERVALOAGEND(UMFA001.dfm:11000-11024) comUnboundDataType = wwDTEdtTimeeDisplayFormat = 'HH:mm:ss', posicionado emLeft=221, Top=187(imediatamente ao lado do labellbl62) Valor padrão:NULL— oAfterInsertdetabPARMFATURemUdmMFA001.pas:434não atribui default para este campo (a inspeção dos 150+ defaults doAfterInsertnão listaHR_INTERVALOAGEND). O DDLDOM_TIMEemPARMFATUR.sql:174também não defineDEFAULT, então o parâmetro nasce vazio quando uma nova PARMFATUR é criada. Validação (MFA001): não háOnValidateparatabPARMFATURHR_INTERVALOAGENDemUdmMFA001.pas(grepHR_INTERVALOAGENDemUMFA001.pas/UdmMFA001.pasretorna apenas a declaração do field na linha 225 e uma regra de navegaçãoActiveControl.Name = 'edtHR_INTERVALOAGEND'emUMFA001.pas:1166-1170que direciona TAB para a aba de Layout — nenhum handler de validação de conteúdo).
O que o parâmetro faz
HR_INTERVALOAGEND é o intervalo mínimo de tempo (hora/minuto/segundo) que deve existir entre dois agendamentos de prova numa mesma empresa. Faz parte do bloco de Parâmetros de Locações da MFA001 (ao lado de QT_MAXPROVAS, HR_INTERVALORET, NR_DIASRETIRADA, etc.) e é usado pela stored procedure VALIDA_INTERVALOAGENDA para decidir se um novo agendamento de data de prova (LOCACAO.DT_PROVA) pode ser confirmado.
A procedure recebe a data/hora que se pretende agendar (I_DT_PROVA), o número da locação atual (I_NR_LOCACAO) e o próprio valor do parâmetro (I_HR_INTERVALOAGEND). Ela calcula uma janela de exclusão antes e depois de DT_PROVA, somando e subtraindo o intervalo em horas/minutos/segundos, e verifica se já existe alguma locação (LOCACAO) com DT_PROVA caindo dentro dessa janela. Se existir, devolve O_ST_DISPONIVEL = 'N', indicando que o horário escolhido conflita com outra prova já agendada e que o operador precisa escolher outro.
O objetivo operacional é distribuir as provas de locação ao longo do dia, evitando que o mesmo atendente receba dois clientes para prova muito próximos um do outro.
Telas impactadas
| Módulo | Código | Papel |
|---|---|---|
| Parâmetros de Faturamento | MFA001 | Produtora — cadastra/edita o valor no campo edtHR_INTERVALOAGEND (aba Locações, UMFA001.dfm:11000-11024). CRUD da PARMFATUR inclui o campo em SELECT/INSERT/UPDATE/RefreshSQL (UdmMFA001.dfm:239, 1642-1645, 2414, 2820, 3119). |
Observação sobre consumidoras: a stored procedure
VALIDA_INTERVALOAGENDA(Scripts/VALIDA_INTERVALOAGENDA.sql) recebeHR_INTERVALOAGENDcomo parâmetro de entrada (I_HR_INTERVALOAGEND time), portanto cabe à tela chamadora fazer oSELECT HR_INTERVALOAGEND FROM PARMFATUR WHERE CD_EMPRESA = ...e repassar o valor. O grep emsource/não encontrou nenhuma referência aVALIDA_INTERVALOAGENDAem código Delphi (source/**/*.pas,source/**/*.dfm) — a procedure existe no banco mas o call site no cliente não está implementado (ou foi removido) nesta branch. Qualquer tela que passe a agendar locações/provas e invocar a procedure será a consumidora natural (tipicamente telas do grupo de locação como MPD043 ou telas que manipulemLOCACAO.DT_PROVA).
Procedures impactadas
| Procedure | Arquivo | Papel |
|---|---|---|
VALIDA_INTERVALOAGENDA | Scripts/VALIDA_INTERVALOAGENDA.sql | Recebe I_HR_INTERVALOAGEND e verifica se existe LOCACAO com DT_PROVA dentro da janela [DT_PROVA − intervalo, DT_PROVA + intervalo] para a mesma CD_EMPRESA. Retorna O_ST_DISPONIVEL = 'S' ou 'N'. |
Campos envolvidos
| Campo | Tabela | Papel |
|---|---|---|
HR_INTERVALOAGEND | PARMFATUR | Intervalo mínimo entre agendamentos (este parâmetro) |
CD_EMPRESA | PARMFATUR / LOCACAO | Filtro — a validação é feita por empresa |
DT_PROVA | LOCACAO | Data/hora da prova comparada com a janela [DT_PROVA ± HR_INTERVALOAGEND] |
NR_LOCACAO | LOCACAO | A locação atual é excluída da busca de conflitos (NR_LOCACAO <> :I_NR_LOCACAO) |
ST_LOCACAO | LOCACAO | Locações com ST_LOCACAO IN ('C','D') (Cancelada / Devolvida) são ignoradas na checagem |
QT_MAXPROVAS | PARMFATUR | Parâmetro irmão na mesma aba — controla quantas provas simultâneas são permitidas no mesmo horário |
Comportamento / Uso
Valor = NULL (em branco)
| Aspecto | Comportamento |
|---|---|
Em VALIDA_INTERVALOAGENDA (Scripts/VALIDA_INTERVALOAGENDA.sql:38-43) | IF (I_HR_INTERVALOAGEND IS NULL) THEN ... SUSPEND; EXIT; |
| Mensagem devolvida | O_DS_MENSAGEM = ' VALIDA_INTERVALOAGENDA: É Obrigatório informar o intervalo entre os Agendamentos. Verifique! ' |
O_ST_DISPONIVEL | Não é alterado de 'S' (valor default na linha 22) — mas a procedure sai antes de fazer a varredura, com mensagem de erro preenchida |
Resultado esperado: sem o parâmetro preenchido, a procedure rejeita o agendamento devolvendo mensagem de obrigatoriedade. Ou seja, para que a validação de intervalo funcione (em qualquer sentido), o parâmetro precisa estar preenchido em MFA001 → Locações.
Valor = 00:00:00
| Aspecto | Comportamento |
|---|---|
IF (I_HR_INTERVALOAGEND IS NULL) | Falso (00:00:00 ≠ NULL no Firebird) — segue em frente |
| Janela calculada (linhas 48-55) | V_HORA = V_MINUTO = V_SEGUNDO = 0 → V_DT_INTERVALOPROVA = I_DT_PROVA (sem adição) |
BETWEEN I_DT_PROVA AND V_DT_INTERVALOPROVA | Janela colapsa num único instante — só pega locações com DT_PROVA exatamente igual a I_DT_PROVA |
O_ST_DISPONIVEL | 'N' apenas se houver outra locação no mesmíssimo timestamp; caso contrário 'S' |
Resultado esperado: equivalente a desligar o espaçamento entre agendamentos — apenas o choque exato no mesmo segundo é detectado.
Valor > 00:00:00
| Aspecto | Comportamento |
|---|---|
| Extração dos componentes (linhas 48-50) | V_HORA = EXTRACT(HOUR ...), V_MINUTO = EXTRACT(MINUTE ...), V_SEGUNDO = EXTRACT(SECOND ...) |
| Janela posterior (linhas 52-55) | V_DT_INTERVALOPROVA = I_DT_PROVA + V_HORA h + V_MINUTO min + V_SEGUNDO s |
| 1ª busca (linhas 59-67) | SELECT SUM(L.NR_LOCACAO) FROM LOCACAO L WHERE L.CD_EMPRESA = I_CD_EMPRESA AND L.DT_PROVA BETWEEN I_DT_PROVA AND V_DT_INTERVALOPROVA AND L.ST_LOCACAO NOT IN ('C','D') AND L.NR_LOCACAO <> I_NR_LOCACAO — detecta provas agendadas depois de I_DT_PROVA dentro da janela |
| Curto-circuito | Se V_CONTROLA > 0 → O_ST_DISPONIVEL = 'N'; SUSPEND; EXIT; |
| Janela anterior (linhas 78-81) | V_DT_INTERVALOPROVA = I_DT_PROVA − V_HORA h − V_MINUTO min − V_SEGUNDO s |
| 2ª busca (linhas 84-92) | Mesma query, agora com BETWEEN V_DT_INTERVALOPROVA AND I_DT_PROVA — detecta provas antes de I_DT_PROVA dentro da janela |
| Retorno final | O_ST_DISPONIVEL = 'N' se houve conflito em qualquer das duas buscas, senão 'S' |
Observações importantes:
- A procedure usa
EXECUTE STATEMENTcom concatenação de valores (linhas 59-66 e 84-92). Mesmo que os parâmetros sejam numéricos/datas de fonte confiável, o padrão viola a regra de segurança do projeto (rules/sql/quality.md— SQL injection); em código novo deve ser refatorado para query estática com parâmetros:I_CD_EMPRESA,:I_DT_PROVA, etc. - A janela inclui os extremos (
BETWEENé inclusivo). Uma prova agendada exatamente na ponta da janela (por ex.,I_DT_PROVA + HR_INTERVALOAGENDao segundo exato) também é considerada conflito. - Locações
ST_LOCACAO IN ('C','D')(Cancelada, Devolvida) são descartadas — apenas provas ativas entram no teste. - A própria locação em edição é excluída pela cláusula
L.NR_LOCACAO <> :I_NR_LOCACAO, permitindo que o operador reconfirme a locação sem que ela conflite consigo mesma.
Simulação passo a passo
Pré-requisitos do cenário:
PARMFATUR.HR_INTERVALOAGEND = 00:30:00(30 minutos) definido em MFA001 → Locações paraCD_EMPRESA = 1.- Empresa 1 já tem duas locações com prova agendada:
- Locação 2001 →
DT_PROVA = 15/05/2026 14:00:00,ST_LOCACAO = 'P'. - Locação 2002 →
DT_PROVA = 15/05/2026 15:10:00,ST_LOCACAO = 'P'.
- Locação 2001 →
- Operador está criando a Locação 2003 e tenta agendar
DT_PROVA = 15/05/2026 14:20:00.
-
Em MFA001 → aba Locações → informe
Intervalo Minímo entre Agendamentos = 00:30:00. Salve (F6). Valor grava emPARMFATUR.HR_INTERVALOAGEND. -
Ao tentar confirmar a prova da Locação 2003, a tela consumidora executa:
EXECUTE PROCEDURE VALIDA_INTERVALOAGENDA(1, -- I_CD_EMPRESATIMESTAMP '2026-05-15 14:20:00', -- I_DT_PROVA2003, -- I_NR_LOCACAOTIME '00:30:00' -- I_HR_INTERVALOAGEND); -
Dentro da procedure:
- Nenhum
I_*éNULL→ passa pelos trêsIF IS NULLdas linhas 24-43. V_HORA = 0,V_MINUTO = 30,V_SEGUNDO = 0.- Janela posterior:
V_DT_INTERVALOPROVA = 15/05/2026 14:50:00. Query busca locações entre14:20:00e14:50:00(empresa 1, não canceladas, ≠ 2003). Nenhuma — a Locação 2001 está em14:00(antes) e a 2002 em15:10(depois). - Janela anterior:
V_DT_INTERVALOPROVA = 15/05/2026 13:50:00. Query busca locações entre13:50:00e14:20:00. Acha a Locação 2001 (14:00:00).V_CONTROLA = 2001 > 0. - Seta
O_ST_DISPONIVEL = 'N',SUSPEND,EXIT.
- Nenhum
-
A tela consumidora recebe
ST_DISPONIVEL = 'N'e deve bloquear/avisar o operador que já existe prova agendada nos 30 minutos anteriores. -
O operador ajusta para
14:35:00e reenvia. Agora:- Janela posterior:
[14:35, 15:05]— não pega 2002 (15:10está fora). OK. - Janela anterior:
[14:05, 14:35]— não pega 2001 (14:00está fora). OK. O_ST_DISPONIVEL = 'S'. Prova pode ser agendada.
- Janela posterior:
Resultado esperado: o parâmetro distribui as provas no tempo, rejeitando agendamentos que fiquem a menos de HR_INTERVALOAGEND de qualquer outra prova ativa na mesma empresa — seja antes ou depois do horário proposto.
Casos de borda
| Cenário | Comportamento |
|---|---|
| Locações em empresas diferentes | Ignoradas (filtro L.CD_EMPRESA = :I_CD_EMPRESA em ambas as buscas). Cada empresa tem sua própria janela. |
Locações com ST_LOCACAO = 'C' (Cancelada) ou 'D' (Devolvida) | Ignoradas (filtro L.ST_LOCACAO NOT IN ('C','D')). |
| Mesmo número de locação em edição | Ignorada (filtro L.NR_LOCACAO <> :I_NR_LOCACAO) — permite reconfirmar sem conflito consigo mesma. |
Nova locação ainda sem NR_LOCACAO gerado | Passe um valor que não exista na base (ex.: 0) para que o filtro <> não exclua nenhuma locação existente. |
CD_EMPRESA ou DT_PROVA não informados | A procedure retorna mensagem de obrigatoriedade em O_DS_MENSAGEM (linhas 24-36) e sai sem alterar O_ST_DISPONIVEL (que permanece 'S'). |
Prova agendada exatamente na ponta da janela (14:20:00 − 00:30:00 = 13:50:00) | Conflito — BETWEEN é inclusivo no Firebird. |
Referências no Código Fonte
Delphi — MFA001 (cadastro do parâmetro)
| Arquivo | Linhas | Papel |
|---|---|---|
source/MFA001/UMFA001.dfm | 10630-10631 | TTabSheet pagLocacao com Caption = 'Locações', ImageIndex = 3 |
source/MFA001/UMFA001.dfm | 10633-10645 | TGroupBox GroupBox5 ' Parâmetros de Locações ' (container visual) |
source/MFA001/UMFA001.dfm | 10744-10756 | TLabel lbl62 com Caption = 'Intervalo Minímo entre Agendamentos', Left=32, Top=191 |
source/MFA001/UMFA001.dfm | 11000-11024 | TwwDBDateTimePicker edtHR_INTERVALOAGEND — DataField = 'HR_INTERVALOAGEND', DataSource = dmMFA001.dsPARMFATUR, UnboundDataType = wwDTEdtTime, DisplayFormat = 'HH:mm:ss', Left=221, Top=187 |
source/MFA001/UMFA001.pas | 339 | Declaração do componente visual edtHR_INTERVALOAGEND: TwwDBDateTimePicker |
source/MFA001/UMFA001.pas | 1166-1170 | Handler de navegação TAB — quando ActiveControl.Name = 'edtHR_INTERVALOAGEND', direciona para paginas.ActivePage := pagLAYOUT e foca edtCD_LAYPEDVENDA |
source/MFA001/UdmMFA001.pas | 225 | Declaração tabPARMFATURHR_INTERVALOAGEND: TTimeField |
source/MFA001/UdmMFA001.pas | 434 | tabPARMFATURAfterInsert não atribui default para HR_INTERVALOAGEND (o campo nasce NULL) |
source/MFA001/UdmMFA001.dfm | 239 | SelectSQL: lista PARMFATUR.QT_MAXPROVAS, PARMFATUR.HR_INTERVALOAGEND, PARMFAT... |
source/MFA001/UdmMFA001.dfm | 1642-1645 | Definição do TTimeField com FieldName = 'HR_INTERVALOAGEND', Origin = '"PARMFATUR"."HR_INTERVALOAGEND"' |
source/MFA001/UdmMFA001.dfm | 2414 | InsertSQL: inclui HR_INTERVALOAGEND na cláusula de colunas do INSERT |
source/MFA001/UdmMFA001.dfm | 2820 | UpdateSQL: HR_INTERVALOAGEND = :HR_INTERVALOAGEND, |
source/MFA001/UdmMFA001.dfm | 3119 | RefreshSQL: PARMFATUR.QT_MAXPROVAS, PARMFATUR.HR_INTERVALOAGEND, PARMFAT... |
DDL
docs/schema/tables/PARMFATUR.sql:174—HR_INTERVALOAGEND DOM_TIME,(semNOT NULL, sem default → permiteNULL).
SQL — Procedures
| Arquivo | Linhas | Papel |
|---|---|---|
Scripts/VALIDA_INTERVALOAGENDA.sql | 1-102 | Procedure VALIDA_INTERVALOAGENDA completa |
Scripts/VALIDA_INTERVALOAGENDA.sql | 7 | Parâmetro de entrada I_HR_INTERVALOAGEND time |
Scripts/VALIDA_INTERVALOAGENDA.sql | 38-43 | Validação IS NULL → retorna mensagem de obrigatoriedade |
Scripts/VALIDA_INTERVALOAGENDA.sql | 48-50 | Extração de HOUR / MINUTE / SECOND do intervalo |
Scripts/VALIDA_INTERVALOAGENDA.sql | 52-67 | Janela posterior (DT_PROVA → DT_PROVA + intervalo) e busca por conflito |
Scripts/VALIDA_INTERVALOAGENDA.sql | 78-98 | Janela anterior (DT_PROVA − intervalo → DT_PROVA) e busca por conflito |
Scripts/VALIDA_INTERVALOAGENDA.sql | 69-74, 94-99 | Se encontrou locação conflitante → O_ST_DISPONIVEL = 'N' e sai |
Observação sobre chamadores
O grep VALIDA_INTERVALOAGENDA em D:/Desenvolvimento/JunsoftERP_V2/source/ não retornou nenhuma chamada a esta procedure em código Delphi (.pas/.dfm). A procedure existe no banco e foi claramente projetada para validar agendamentos de prova de locação, mas o call site no cliente não foi localizado nesta branch — pode ter sido removido em refactor ou estar ainda por integrar. Quando for integrada, a tela chamadora deverá:
- Ler
PARMFATUR.HR_INTERVALOAGEND(via dataset já aberto ouSELECTdedicado) filtrando porCD_EMPRESA. - Chamar
EXECUTE PROCEDURE VALIDA_INTERVALOAGENDA(:CD_EMPRESA, :DT_PROVA, :NR_LOCACAO, :HR_INTERVALOAGEND). - Bloquear/avisar baseado em
O_ST_DISPONIVELe exibirO_DS_MENSAGEMquando presente.