Explorar E-books
Categorias
Explorar Audiolivros
Categorias
Explorar Revistas
Categorias
Explorar Documentos
Categorias
Não homologado
Copyright © 2009 TOTVS S.A. Todos os direitos reservados.
Nenhuma parte deste documento pode ser copiada, reproduzida, traduzida ou
transmitida por qualquer meio eletrônico ou mecânico, na sua totalidade ou em
parte, sem a prévia autorização escrita da TOTVS S.A., que se reserva o
direito de efetuar alterações sem aviso prévio. A TOTVS S.A não assume
nenhuma responsabilidade pelas conseqüências de quaisquer erros ou
inexatidões que possam aparecer neste documento.
TOTVS S.A.
Av. Santos Dumont, 831, Joinville, SC, CEP 89.222-900
i
Índice
CAPÍTULO 1 Conceitos...............................................................................5
Conceitos
5
CAPÍTULO 1
Conceitos
Conceitos
7
CAPÍTULO 2
Concepção do DBO
Neste capítulo estão informações para que o analista possa projetar o DBO, a
fim de permitir que o desenvolvedor possa construir um DBO de forma mais
segura, integra, rápida e eficiente.
O analista deve definir a estrutura de DBOs do sistema. Para tanto, ele pode
seguir a seguinte linha:
1 Como regra inicial podemos ter 1 (um) DBO por tabela de banco de
dados, são os DBOs de entidade;
1.1 Para tabelas que contém muitos campos (ex.: item, emitente, etc.) pode-se
definir mais de um DBO. Por exemplo: teríamos para a tabela Item vários
DBOs de acordo com o grupo de informações organizadas, DBO Item
Básico, DBO Item-Estoque, DBO Item-Compras, etc. Neste caso, a
criação do registro deve ser feita somente no DBO Básico. Os outros
apenas atualizam as informações pertencentes à área.
2 Para processos também podem ser definidos DBOs. Por exemplo:
processo de fechamento contábil, atualização de movimento, etc..
Podemos dizer que cada API é um DBO. Nestes casos, escolhe-se a tabela
mais importante do DBO para nomeá-lo.
10
Quando for necessário criar um novo DBO, deve ser usado o DumpName da
tabela principal para qual está sendo criada esta BO + uma letra (A, B, C, D...).
O DBO deve sempre estar relacionado à tabela principal da mesma.
Exemplo: boad001a.p, boad001b.p ...
Além do DBO, deve se criar um include com o mesmo nome do programa e
terminação “.i”. Este include contém a definição da temp-table de
comunicação dos programas que vão utilizar o DBO.
Exemplo: boad001.i, boad002.i ...
O nome do DBO e o nome dos diretórios devem estar em letra minúscula.
Nos casos onde já existe o BO 1.1 para determinada tabela, e alguns módulos
desejarem utilizá-lo como DBO 2.0 e a equipe responsável pela conversão da
BO 1.1 para DBO 2.0 não tiver disponibilidade para conversão da mesma,
pode-se criar a DBO 2.0, utilizando a nomenclatura BOXX999O.P, onde:
XX999 = dump-name da tabela;
O = letra que indicará que essa DBO é uma DBO que contém a
funcionalidade desejada.
A construção do DBO 2.00 por outra equipe deve ser comunicada à equipe
responsável pela sua manutenção, e ter a aprovação desta, a qual deverá incluir
um comentário no BO 1.1 indicando a existência de uma versão 2.00. O DBO
2.00 deverá ser extremamente simples e ter apenas (por enquanto) métodos de
leitura de registros. É importante lembrar que a equipe responsável pela tabela
do DBO, deverá, no futuro, complementar a sua construção com as regras de
negócio e padrões de arquitetura.
Técnicas
11
somente para a versão 9.0, já que os DBOs podem ser utilizados em versões
diferentes de Progress.
DBO de Entidade
Estes são os DBOs mais comuns e que normalmente estão relacionados a uma
determinada tabela do banco de dados.
São chamados de entidades os objetos do mundo real como Clientes,
Fornecedores, Títulos a Pagar etc. Uma entidade na maioria das vezes é
representada por uma tabela no banco de dados, quando este está totalmente
normalizado.
Quando uma entidade possui no banco de dados mais de uma tabela, é porque
o modelo lógico (entidade) é diferente do modelo físico (tabela). Isto ocorre
geralmente para ganhar performance no acesso ao banco de dados.
Nota: Uma tabela deve ser mantida por um único DBO.
O DBO normalmente se relaciona a uma única tabela e, portanto, esta é uma
etapa bastante simples. Quanto houver mais de uma tabela, devem ser todas
relacionadas, e descritos os campos que as relacionam.
Outros.
O DBO é a forma do client ter acesso aos dados que estão na base de dados.
Por este motivo o DBO deverá determinar quais registros poderão ser
acessados pelo client (segurança) e fornecer meios de acesso que tenham boa
performance.
As constraints são restrições de acesso aos registros e podemos classificá-las
em 4 tipos principais:
Security (Segurança)
Dependendo de quem é o usuário que está logado no sistema, ele poderá ter
acesso a apenas alguns registros da tabela. Ex: Um representante de vendas só
poderá ver os pedidos dele.
Deve ser indicado como será determinada a faixa de registros com base no
usuário. Essa determinação poderá ser direta, comparando o código do usuário
logado a um campo do registro, ou indireta através de uma tabela auxiliar que
determine para cada usuário a faixa de registro que ele tem acesso.
Exemplos:
Técnicas
13
Range (Faixa)
Permite que o usuário selecione valor inicial e final para um determinado
campo reduzindo com isso os registros em que será possível navegar. Essa
constraint é normalmente utilizada por programas de Zoom, e os campos são
os mesmos das classificações do DBO.
Um bom método para determinar as constraints de range é criar uma para cada
classificação (by) que o DBO terá.
Parent or Foreing Key (Pai ou chave estrangeira)
O DBO poderá ter relacionamento com outros DBOs sendo este filho ou a
tabela chave estrangeira daquele. Este tipo de constraint é usado para que
sejam selecionados apenas os registros correspondentes ao DBO pai. Elas
poderão ser constraints de range com valor inicial e final, ou de igualdade
utilizando apenas o código da chave do pai. A escolha entre range e igualdade
deve ser feita no momento da implementação considerando que:
Como range pode já ter sido definida como uma de range em função da
classificação, reduzindo assim o número de métodos de constraint;
Como igualdade pode-se conseguir uma melhor performance pela melhor
utilização dos índices.
Miscelaneous (Diversos)
Podemos ter outros tipos de constraints que permitem reduzir o número de
registros selecionados no banco e, portanto, melhorar a performance tanto na
comunicação "DBO DB" como "Client DBO". Esse tipo de
constraints são normalmente as que permitem realizar filtros nos registros, ou
seja, selecionar registros através de campos lógicos ou indicadores que estão
na tabela. São utilizados também em programas de Zoom e escolhidos pela
seleção de toggle-boxes ( [x] ).
Definindo métodos
Orders
(Classificações)
de negócio
Uma técnica para tornar estas constraints mais flexíveis é fazer um método,
que recebe um valor lógico, para cada toggle, ou seja, para cada valor possível
do campo indicador ou lógico.
Técnicas
15
ENTIDADE
ORDER
A entidade é a própria tabela.
TABELA
ORDER
CAMPOS
LIKE ORDER
STATUS-DESC Baseado no valor de Status: 1 = Open; 2 = Calculated;
3 = Delivered.
Todos os campos da tabela mais o campo status-desc que é calculado.
Validações
Campo Validação
Order-Num Maior que zero; Não pode ser
alterado.
Cust-Num Deve existir na tabela ou DBO de
Customer
Sales-Rep
Status Apenas valores de 1 a 3.
INTEGRIDADE
Sales-Rep é sempre o usuário logado.
Nota: Está sendo assumido, para tornar mais simples o exemplo, que o usuário
logado é sempre um representante (Sales-Rep).
Técnicas
17
Valores iniciais
Campo Calculo do valor inicial
Cust-Num Buscar o cliente principal do
representante na tabela Sales-Rep.
Constraints
Tipo Campos Implementação
Security Sales-Rep = /* Main Block */
Orders
Order Campos Constraints
Implementação (Query)
ByOrder Order-num Sales-Rep (Security)
Order-num (Range)
Cust-num (Parent)
Status (Miscelaneous)
OpenQuerybyOrder:
OPEN QUERY qr{&TableName}
FOR EACH Order NO-LOCK
WHERE Order.Sales-Rep = cSales-Rep
AND Order.Order-Num >= iOrderStart
AND Order.Order-Num <= iOrderFinish
AND Order.Cust-Num >= iCustNumStart
AND Order.Cust-Num <= iCustNumFinish
AND ( lStatusOpen AND Order.Status = 1
OR lStatusCalculated AND Order.Status = 2
OR lStatusDelivered AND Order.Status = 3)
BY Order.Order-Num.
Métodos de Negócio
Método Parâmetros
Função Segurança
Técnicas
19
Lógica
CalculateOrder Nenhum
Update
Verificar se o pedido tem Status = 1 (Open)
Calcular o valor total do pedido somando o valor total de cada item
Somar o valor do pedido ao valor total de pedidos do representante em
SalesRep
Alterar o Status para 2 (Calculated)
DBOs X APIs
Tecnicamente, o DBO é um programa Progress que é executado de forma
persistente e que tem métodos que são executados. A API é um programa
Progress que é executado diretamente, recebendo e devolvendo parâmetros, e
que executa uma função específica.
Os DBOs devem utilizar as APIs que já existem atualmente no produto.
Observe neste diagrama que as APIs podem ser acessadas pelos DBOs.
Técnicas
21
CAPÍTULO 3
Arquitetura do DBO
Um DBO é constituído das seguintes seções:
Definições
No início do DBO defini-se:
&DBOName Nome do DBO
&DBOVersion Versão do DBO
&DBOCustomFunctions Nome das funções customizadas
&TableName Nome da tabela principal do programa
&TableLabel Desc./Label da tabela principal do programa
&QueryName Nome da query principal do programa
Objetos de trabalho
Temp-table RowObject: Esta é uma Temp-table de comunicação. Cada DBO
deve definir uma temp-table para troca de dados com os programas que o estão
utilizando. Normalmente, esta temp-table é definida como a tabela principal do
DBO (LIKE <TableName>).
Temp-table RowErrors: Esta é a temp-table padrão de erros do DBO. Quando
for necessário retornar algum erro, deve-se usar esta temp-table que pode
conter um ou mais erros.
Esta temp-table é definida automaticamente para o DBO. E possui a definição
a seguir:
Campo Tipo Descrição
ErrorSequence Integer Indica a seqüência do erro
ErrorNumber Integer Contém o número do erro
ErrorDescription Character Contém a descrição do erro
ErrorParameters Character Contém os parâmetros do erro
ErrorType Character Indica o tipo do erro
ErrorHelp Character Contém o help do erro
ErrorSubType Character Indica o sub-tipo do erro
Métodos Básicos
Procedures internas padrão do DBO que contém o comportamento básico
esperado de um DBO.
Métodos de Negócio
Os métodos de negócio são definidos pelo analista, para atendimento de
funções específicas relacionadas ao objeto que está se trabalhando.
No DBO os métodos de negócio são procedures internas criadas pelo
desenvolvedor. As regras para criação destes métodos estão descritas a seguir:
Este método deve agir preferencialmente sobre o registro corrente e não
num conjunto de registros. Assim, o programa chamador tem mais
flexibilidade em controlar a operação. Por exemplo: um DBO da tabela
Técnicas
23
item pode ter um cálculo. Esta lógica estaria num método que atuaria
somente no registro corrente. Para processar novo item deve-se posicionar
no próximo (getNext);
Este método deve ser uma procedure interna e deve ter um comentário em
seguida, explicando o que faz o método. Esta descrição deve ser mais que
uma característica técnica, ou seja, deve ajudar ao programador identificar
o que faz o método;
O nome do método deve ter como base: verbo + substantivo(s) + sujeito.
A primeira palavra deve estar em minúsculo e as outras devem ter a
primeira letra em maiúsculo (mesmo padrão adotado na linguagem java).
Além disso, o nome deve estar na língua portuguesa. Desta forma, deve
ficar mais bem documentado do que se trata o método;
Exemplo: calculaMedia, insereOrder, salvaCustomer, etc.
O método deve retornar valores em parâmetros ou temp-tables de saída
(output param). Deve ser utilizado ‘return-value’ para indica se o método
foi processado com sucesso (OK/NOK). Quando ocorrem erros, estes
devem ser inclusos na temp-table RowErrors, se necessário.
Override de Métodos
Em muitos casos faz-se necessário a customização de métodos básicos, tais
como: createRecord, deleteRecord, updateRecord etc, a fim de facilitar este
tipo de customização foi desenvolvida a técnica de Override de Métodos.
Esta técnica executa dois outros métodos definidos pelo desenvolvedor a partir
dos métodos básicos (a seguir está listada uma tabela com os métodos básicos
que possuem override).
A tabela a seguir indica quais os métodos básicos que possuem override e
quais os parâmetros a serem definidos:
Método Parâmetro(s)
getFirst não há
getLast não há
getNext não há
getPrev não há
repositionRecord INPUT pRowid AS ROWID
createRecord* não há
Método Parâmetro(s)
deleteRecord* não há
updateRecord* não há
getRecord não há
setRecord não há
newRecord não há
copyBuffer2TT* não há
copyTT2Buffer não há
getRowid INPUT-OUTPUT pRowid AS ROWID
getBatchRawRecords** não há
getBatchRawRecordsPrev** não há
getBatchRecords** não há
getBatchRecordsPrev** não há
*Nestes métodos, caso o retorno da procedure customizada seja "NOK" a
transação pode ser desfeita.
**Nestes métodos os pontos de override compreendem apenas a transferência
de cada registro individualmente para a temp-table auxiliar.
PROCEDURE beforecreateRecord :
/*--------------------------------------------------------------
Purpose: Override do método createRecord (before)
Parameters:
Notes:
--------------------------------------------------------------*/
RETURN "OK":U.
END PROCEDURE.
PROCEDURE aftercreateRecord :
/*--------------------------------------------------------------
Purpose: Override do método createRecord (after)
Parameters:
Notes:
Técnicas
25
--------------------------------------------------------------*/
RETURN "OK":U.
END PROCEDURE.
Construção do DBO
Para o desenvolvimento do DBO deve ser utilizado o UIB (AppBuilder,
quando utilizado versão 9) do Progress.
Para criação de um DBO o desenvolvedor deve seguir os seguintes passos
(estaremos construindo como exemplo um DBO de manutenção na tabela
Customer da base de dados Sports do Progress):
Nota: Caso seja definido que na utilização do DBO será permitido criar
registros fora da query aberta, deve ser incluído o preprocessador
NewRecordOffQuery. Lembrando que com a inclusão deste preprocessador
não será apresentado erro de reposicionamento de registros na query.
&GLOBAL-DEFINE NewRecordOffQuery YES
Definição da Query
A definição da query padrão está no include method/dboqry.i. Porém, havendo
a necessidade de customizar a definição da query, deve-se retirar a chamada ao
include e definir manualmente a query (lembrando de utilizar o preprocessor
{&QueryName} para defini-la).
Exemplo: A chamada ao include de definição da query {&QueryName}.
/*--- Include com definição da query para tabela {&TableName} ---*/
/*--- Em caso de necessidade de alteração da definição da query,
pode ser retirada a chamada ao include a seguir e em seu lugar
deve ser feita a definição manual da query ---*/
{method/dboqry.i}
Neste caso, a definição da query não foi customizada. Porém, a seguir está um
exemplo no qual a definição da query foi alterada:
Exemplo: Customização da definição da query {&QueryName}.
/*--- Include com definição da query para tabela {&TableName} ---*/
/*--- Em caso de necessidade de alteração da definição da query,
pode ser retirada a chamada ao include a seguir e em seu lugar
deve ser feita a definição manual da query ---*/
DEFINE QUERY {&QueryName}
FOR Customer FIELDS (Cust-Num Name) SCROLLING.
Mesmo quando a definição da query for alterada, deve-se sempre utilizar uma
única tabela e, também, utilizar a opção SCROLLING.
Observação: Para os casos onde é utilizado banco de dados Oracle deve-se
verificar o capítulo de Técnicas / Melhorar Performance em Bancos Oracle.
PROCEDURE openQueryMain:
OPEN QUERY {&QueryName} FOR EACH {&TableName} NO-LOCK.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE setConstraintByName:
RETURN "OK":U.
END PROCEDURE.
PROCEDURE openQueryMain:
OPEN QUERY {&QueryName} FOR EACH {&TableName} NO-LOCK.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE openQueryByName:
OPEN QUERY {&QueryName} FOR EACH {&TableName} NO-LOCK
BY Customer.Name.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE setConstraintMain:
DEFINE INPUT PARAMETER pSalesRep LIKE Customer.Sales-Rep NO-UNDO.
PROCEDURE setConstraintByName:
DEFINE INPUT PARAMETER pSalesRep LIKE Customer.Sales-Rep NO-UNDO.
Técnicas
29
PROCEDURE openQueryMain:
OPEN QUERY {&QueryName} FOR EACH {&TableName} NO-LOCK
WHERE Customer.Sales-Rep = cSalesRep.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE openQueryByName:
OPEN QUERY {&QueryName FOR EACH {&TableName} NO-LOCK
WHERE Customer.Sales-Rep = cSalesRep
BY {&TableName}.Name.
RETURN "OK":U.
END PROCEDURE.
Neste caso, está sendo feita uma seleção por representante (sales-rep). Este
DBO deve estar preparado para navegar somente nos registros de Customer de
um determinado sales-rep que foi informado ao chamar as procedures
setConstraintMain e setConstraintByName. Elas apenas armazenam o
parâmetro recebido na variável que vai ser usada pela openQueryStatic.
Constraints específicos para uma query
Exemplo:
DEFINE VARIABLE cSalesRep LIKE Customer.Sales-Rep NO-UNDO.
DEFINE VARIABLE deCreditLimit LIKE Customer.Credit-Limit NO-UNDO.
PROCEDURE setConstraintMain:
DEFINE INPUT PARAMETER pSalesRep LIKE Customer.Sales-Rep NO-UNDO.
PROCEDURE setConstraintByName:
DEFINE INPUT PARAMETER pSalesRep
LIKE Customer.Sales-Rep NO-UNDO.
DEFINE INPUT PARAMETER pCreditLimit
LIKE Customer.Credit-Limit NO-UNDO.
PROCEDURE openQueryMain:
OPEN QUERY {&QueryName} FOR EACH {&TableName} NO-LOCK
WHERE Customer.Sales-Rep = cSalesRep.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE openQueryByName:
OPEN QUERY {&QueryName FOR EACH {&TableName} NO-LOCK
WHERE Customer.Sales-Rep = cSalesRep AND
Customer.Credit-Limit >= deCreditLimit
BY {&TableName}.Name.
RETURN "OK":U.
END PROCEDURE.
RETURN "NOK":U.
END.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE linkToItem:
/*---------------------------------------------------------------
Purpose: Recebe handle do DBO Item e execute método getKey
Parameters: recebe handle de um DBO
Notes:
----------------------------------------------------------------*/
DEFINE INPUT PARAMETER pHandle AS HANDLE NO-UNDO.
RETURN "OK":U.
END PROCEDURE.
RETURN "OK":U.
END PROCEDURE.
IF CAN-FIND(FIRST RowErrors
WHERE RowErrors.ErrorSubType = "ERROR":U) THEN
RETURN "NOK":U.
RETURN "OK":U.
END PROCEDURE.
IF CAN-FIND(FIRST RowErrors
WHERE RowErrors.ErrorSubType = "ERROR":U) THEN
RETURN "NOK":U.
RETURN "OK":U.
END PROCEDURE.
Criação de erros
Quando o desenvolvedor possui a necessidade da inclusão de erros na temp-
table RowErrors, deve ser utilizado o include method/svc/errors/inserr.i.
O include method/svc/errors/inserr.i é utilizado para realizar a inclusão de
erros Progress ou erros de Produto, e ainda contempla a possibilidade de
inclusão de erros manuais. Para inclusão de Erros Progress e de Produto,
recebe os parâmetros a seguir:
ErrorNumber: número do erro, quando o erro for do tipo Outros;
ErrorType: tipo do erro, podendo ter o valor Progress, Outros, EMS
ou HR (qualquer valor pode ser utilizado nesta descrição);
ErroSubType: sub-tipo do erro, podendo ter o valor Error,
Information ou Warning; o registro incluso somente é considerado
um erro quando seu sub-tipo é Error, caso seja Information ou
Warning, o procedimento continuará;
ErrorParameters: parâmetros do erro, quando o erro for do tipo
Outros estes parâmetros irão ser utilizados na chamadas das
mensagens padrão do Produto.
Exemplo: Inclusão de erros do produto.
{method/svc/errors/inserr.i
&ErrorNumber="1"
&ErrorType="EMS"
&ErrorSubType="ERROR"
&ErrorParameters="'Customer'"}
{method/svc/errors/inserr.i
&ErrorNumber="32"
&ErrorType="HR"
&ErrorSubType="ERROR"
&ErrorParameters="'Order~~~~ ' + STRING(TODAY)"}
{method/svc/errors/inserr.i
&ErrorNumber="42"
&ErrorType="HR"
&ErrorSubType="WARNING"
&ErrorParameters="'Order~~~~Customer'"}
Nestes exemplos temos erros de produto, onde o segundo exemplo traz um
sub-tipo WARNING, sendo assim, a interface poderá tratar e mostrar a
mensagem de erro ao usuário, mas o procedimento não será abortado.
O include pode ser utilizado para realizar a inclusão de erros manuais, ou seja
todos as informações referentes ao erro são informadas pelo desenvolvedor.
Desta forma o include recebe os parâmetros a seguir:
ErrorNumber: número do erro, quando o erro for do tipo Outros;
ErrorType: tipo do erro, podendo ter o valor Outros (qualquer valor
pode ser utilizado nesta descrição);
ErrorSubType: sub-tipo do erro, podendo ter o valor Error,
Information ou Warning; o registro incluso somente é considerado
um erro quando seu sub-tipo é Error, caso seja Information ou
Warning, o procedimento continuará;
ErrorDescription: descrição do erro, na descrição os parâmetros já
devem estar substituídos;
ErrorHelp: help do erro, no help os parâmetros já devem estar
substituídos;
ErrorParameters: parâmetros do erro, quando o erro for do tipo
Outros.
Quando utilizar o parâmetro ErrorDescription, deve-se utilizar o
ErrorSubType.
Exemplo: Inclusão de erros do produto.
IF Customer.Cust-Num <= 0 THEN
{method/svc/errors/inserr.i
&ErrorNumber="112"
&ErrorType="EMS"
&ErrorSubType="ERROR"
&ErrorDescription="Valor inválido para o Cust-Num"
&ErrorHelp="Informe valor maior que 0 para o Cust-Num"}
um novo "DBO de Join" pois cada um suporta apenas uma única definição
de query que é onde se definem as tabelas e sua ordem de encadeamento;
pode haver um ou mais métodos setConstraint, dependendo do número de
campos utilizados nos WHERE do Open Query; a diferença neste caso é
que os campos do setConstraint são utilizados para restrições de várias
tabelas;
gotoKey e getKey: normal, lembrando apenas que se aplicam a tabela
filha;
linkTo<parent>: não construir;
Validações: não precisam ser construídas pois o DBO é apenas para
leitura.
Serviço de Banco
O serviço de Banco pode ser utilizado pelo desenvolvedor para realizar
implementações específicas para um banco de dados.
O uso deste é feito através do preprocessador DBType, que possui o tipo de
banco de dados utilizado pelo DBO.
Exemplo: Impedir que o comando GET LAST seja utilizado quando o banco
não for Progress.
PROCEDURE afterNewRecord:
&IF "{&DBType}":U = "PROGRESS":U &THEN
GET LAST {&QueryName} NO-LOCK.
&ENDIF
END PROCEDURE.
Serviço de Autenticação
O serviço de Autenticação pode ser utilizado pelo desenvolvedor a fim de
obter o valor de algumas variáveis de contexto.
O uso deste é feito através do include method/svc/autentic/autentic.i que
recebe os parâmetros a seguir:
Parâmetro Tipo Descrição
vUserAccessType Integer Contém o tipo de acesso do usuário corrente
vUserEnterprise Integer Contém o código da empresa do usuário
corrente
vUserName Character Contém o código do usuário corrente
vUserCountryTax Integer Contém o código do imposto do país do
usuário corrente
vUserGroupSecurityList Character Contém a lista dos grupos de segurança do
usuário
vHRProgramSecurity Handle Handle do programa de segurança do produto
HR
Serviço de Customização
O serviço de Customização pode ser utilizado pelo desenvolvedor para incluir
novos pontos para que Clientes/Parceiros possam customizar o processamento
do DBO.
Para os DBOs existem dois pontos pré-definidos nos métodos createRecord,
updateRecord e deleteRecord. Estes pontos são executados no início e no final
do método e, ainda, estão dentro da transação do método, permitindo que a
customização possa cancelar a transação.
Quando o desenvolvedor analisar que um método específico deve possuir um
ponto para customização, deve utilizar o include method/svc/custom/custom.i.
Além disso, o desenvolvedor deve verificar se o método pode ser cancelado.
A utilização do include é simples, bastando o desenvolvedor informar o valor
do parâmetro &Event, que indica o nome do evento de customização.
Aconselha-se que a definição do include seja feita nos pontos inicial e final do
método que se deseja customizar. E a nomenclatura para o parâmetro Event
deve ser <before/after><nome-do-método>.
Exemplo: Inclusão de pontos de customização para o método calculateOrder.
PROCEDURE calculateOrder:
{method/svc/custom/custom.i &Event="beforeCalculateOrder"}
...
{method/svc/custom/custom.i &Event="afterCalculateOrder"}
END PROCEDURE.
Nesse exemplo foram inclusos dois pontos para customização. Estes pontos
não permitem que o customizador cancele o método.
Caso o desenvolvedor opte por definir os pontos de customização com opção
de cancelamento, deve ser tratado o RETURN-VALUE após a chamada ao
include e, ainda, a variável lCustomExecuted a fim de identificar se o
problema de customização foi executado.
Exemplo: Inclusão de pontos de customização para o método calculateOrder e
tratamento do retorno.
PROCEDURE calculateOrder:
DO TRANSACTION:
{method/svc/custom/custom.i &Event="beforeCalculateOrder"}
IF lCustomExecuted AND RETURN-VALUE = "NOK":U THEN
UNDO, RETURN "NOK":U.
...
{method/svc/custom/custom.i &Event="afterCalculateOrder"}
IF lCustomExecuted AND RETURN-VALUE = "NOK":U THEN
UNDO, RETURN "NOK":U.
END.
END PROCEDURE.
Técnicas
43
Serviço de Segurança
O serviço de Segurança divide-se em dois estilos: segurança para o DBO, feita
automaticamente, e segurança para método.
Somente a segurança de métodos pode ser utilizada pelo desenvolvedor. O uso
deste é feito através do include method/svc/security/permit.i que recebe o
parâmetro &Method, que contém o nome do método ou um nome que
identifique um grupo de métodos.
Os métodos de navegação (getFirst, getPrev, getNext, getLast e
repositionRecord) e de update (createRecord, deleteRecord e updateRecord) já
possuem segurança padrão definida com os nomes de Navigation, Create,
Delete e Update.
Exemplo: Implementação de segurança para o método calculateOrder.
/* Definitions --- */
&GLOBAL-DEFINE DBOCustomFunctions calculateOrder
PROCEDURE calculateOrder:
{method/svc/permit/permit.i &Method="calculateOrder"}
...
END PROCEDURE.
Assim, a lógica deste método deve estar neste sub-programa que somente
deve ser carregado para memória quando o método for chamado evitando
assim demora no tempo de carga do DBO;
O analista deve levar em consideração a passagem de parâmetros e que
este sub-programa tenha código considerável para ser um sub-programa
(como sugestão no mínimo 60 linhas);
Os sub-programas construídos devem seguir as regras existentes para
DBOs, ou seja, devem receber parâmetros via temp-table e realizar o
retorno de erros da mesma forma. Não podem usar variáveis globais e nem
fazer interação com a tela.
Documentação
Existem duas formas de se criar a documentação de DBOs:
Forma manual: No diretório \\enseada\desems2\ferramentas\ddk2000\
ModeloDocAPI-DBO existe um arquivo chamado
modelo_doc_dbo_manual.doc. Este arquivo é um exemplo de como deve
ser feita a documentação de DBOs, o que deve ser descrito e como.
Triggers de
Controle
Transação
Técnicas
45
Considerações Gerais
Recomenda-se que o nível de transação (Progress) fique limitado ao método.
Cuidando-se para não transformar todo o DBO em uma única transação
(afetando assim o escopo e bloqueio de registro).
Por exemplo: os métodos createRecord, updateRecord e deleteRecord
constituem-se em 3 (três) transações separadas. E seus métodos override estão
neste mesma transação.
Alertamos para o fato de que as transações dos DBOs podem ser afetadas pelas
transações dos programas que utilizam os DBOs.
As regras de negócio devem estar em DBOs e APIs. Não se deve utilizar
Triggers para regras de negócio. Os Triggers podem vir a ser usados para
processos técnicos, mas isto é uma definição da equipe de apoio ao
desenvolvimento.
As regras de negócio existentes atualmente devem ser transferidos
gradualmente para DBOs e APIs.
CAPÍTULO 4
findRowid -
Deve-se executar o método repositionRecord;
getCurrent -
Deve-se executar o método getRecord;
findFirst -
Deve-se executar o método getFirst. O novo DBO somente trabalha com
query;
finLast -
Deve-se executar o método getLast. O novo DBO somente trabalha com
query;
findNext -
Deve-se executar o método getNext. O novo DBO somente trabalha com
query;
findPrev -
Deve-se executar o método getPrev. O novo DBO somente trabalha com
query;
getRowid -
Continua existindo no novo DBO;
setCurrent -
Deve-se executar o método _copyBuffer2TT. Porém, este método é
somente para uso interno dos includes padrão;
prevBrowseNavigation -
Não há método correspondente no novo DBO;
serverSendRows -
Deve-se executar o método getBatchRecords;
validateCreate -
Deve-se primeiramente executar o método createRecord e logo após o
getRowid;
validateDelete -
Deve-se primeiramente executar o método repositionRecord, após o
deleteRecord e por último getRowid;
validateUpdate -
Deve-se primeiramente executar o método repositionRecord, após o
setRecord e por último updateRecord;
openQuery -
Deve-se executar o método openQueryStatic. Porém, o novo método
utiliza strings ao invés de números e desta forma deve-se controlar isto
através de um método proxy definido pelo desenvolvedor.
Método novo:
PROCEDURE openQueryMain:
OPEN QUERY {&Query-Name} FOR EACH Customer NO-LOCK.
END PROCEDURE.
PROCEDURE openQueryByName:
OPEN QUERY {&Query-Name} FOR EACH Customer NO-LOCK BY
Customer.Name.
END PROCEDURE.
ou
RUN openQueryStatic IN THIS-PROCEDURE (INPUT "ByName":U).
Vale salientar que os métodos setConstraint devem ter seus nomes alterados a
fim de possuírem nomenclatura semelhante aos métodos
openQuery<Description> associados. Conforme exemplo a seguir:
PROCEDURE openQueryByName:
...
END PROCEDURE.
PROCEDURE setConstraintByName:
...
END PROCEDURE.
validateCreate
Este método foi dividido em novos métodos, sendo 1 (um) deles definido
dentro do DBO e outro em include padrão.
Neste método, a parte responsável pelas validações específicas à criação
devem ser transferidas para o método beforecreateRecord, as validações
análogas à criação e alteração devem ser transferidas para o método
validateRecord. E ainda, caso ocorra algum erro nas validações, ao final deve
ser retornado um flag "NOK" através do comando RETURN-VALUE.
Quanto aos erros ocorridos, estes devem ser cadastrados na temp-table
RowErrors através da include method/svc/errors/inserr.i.
Estes procedimentos são demonstrados através do exemplo a seguir:
Método antigo:
PROCEDURE validateCreate:
DEFINE INPUT PARAMETER TABLE FOR RowObject.
DEFINE OUTPUT PARAMETER TABLE FOR tt-bo-erro.
DEFINE OUTPUT PARAMETER r-chave AS ROWID NO-UNDO.
FOR EACH tt-bo-erro:
DELETE tt-bo-erro.
END.
IF CAN-FIND(FIRST Customer WHERE Customer.Cust-Num =
RowObject.Cust-Num)
THEN DO:
ASSIGN i-seq-erro = i-seq-erro + 1.
RUN utp/ut-msgs.p (INPUT "MSG":U, INPUT 9999, INPUT "":U).
CREATE tt-bo-erro.
ASSIGN tt-bo-erro.i-sequen = i-seq-erro
tt-bo-erro.cd-erro = 9999
tt-bo-erro.mensagem = RETURN-VALUE.
END.
IF RowObject.Cust-Num <= 0 THEN DO:
ASSIGN i-seq-erro = i-seq-erro + 1.
RUN utp/ut-msgs.p (INPUT "MSG":U, INPUT 9999, INPUT "":U).
CREATE tt-bo-erro.
ASSIGN tt-bo-erro.i-sequen = i-seq-erro
tt-bo-erro.cd-erro = 9999
tt-bo-erro.mensagem = RETURN-VALUE.
END.
RUN validateFields IN THIS-PROCEDURE.
FIND FIRST tt-bo-erro NO-ERROR.
IF NOT AVAILABLE tt-bo-erro THEN DO:
RUN executeCreate IN THIS-PROCEDURE.
ASSIGN r-chave = TO-ROWID(RETURN-VALUE).
END.
END PROCEDURE.
Técnicas
51
Método novo:
Toda a lógica responsável pela criação dos novos registros foi passada para o
método createRecord, que é um método básico ao qual o desenvolvedor não
tem acesso. Este método é responsável pela execução do método
validateRecord e de seus métodos override.
Neste caso é demonstrado somente a transferência das validações. Parte do
código que o desenvolvedor possui acesso.
PROCEDURE validateRecord:
IF RowObject.Cust-Num <= 0 THEN DO:
{method/svc/errors/inserr.i
&ErrorNumber="9999"
&ErrorType="EMS"
&ErroSubType="ERRO"
&ErrorParameters="''"}
END.
IF CAN-FIND(FIRST RowErrors) THEN
RETURN "NOK":U.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE beforecreateRecord:
IF CAN-FIND(FIRST Customer WHERE Customer.Cust-Num =
RowObject.Cust-Num)
THEN DO:
{method/svc/errors/inserr.i
&ErrorNumber="9999"
&ErrorType="EMS"
&ErroSubType="ERRO"
&ErrorParameters="''"}
END.
IF CAN-FIND(FIRST RowErrors) THEN
RETURN "NOK":U.
RETURN "OK":U.
END PROCEDURE.
validateUpdate
Este método foi dividido em novos métodos, sendo 1 (um) deles definido
dentro do DBO e outro em include padrão.
Neste método, a parte responsável pelas validações específicas à alteração
devem ser transferidas para o método beforeUpdateRecord; as validações
análogas à criação e alteração devem ser transferidas para o método
validateRecord. E ainda, caso ocorra algum erro nas validações, ao final deve
ser retornado um flag "NOK":U através do comando RETURN-VALUE.
Quanto aos erros ocorridos, estes devem ser cadastrados na temp-table
RowErrors através da include method/svc/errors/inserr.i.
Estes procedimentos são demonstrados através do exemplo a seguir:
Método antigo:
PROCEDURE validateUpdate:
DEFINE INPUT PARAMETER TABLE FOR RowObject.
DEFINE INPUT PARAMETER r-chave AS ROWID NO-UNDO.
DEFINE OUTPUT PARAMETER TABLE FOR tt-bo-erro.
FOR EACH tt-bo-erro:
DELETE tt-bo-erro.
END.
RUN validateFields IN THIS-PROCEDURE.
FIND FIRST tt-bo-erro NO-ERROR.
IF NOT AVAILABLE tt-bo-erro THEN DO:
FIND {&Table-Name} WHERE ROWID({&Table-Name}) = r-chave
EXCLUSIVE-LOCK NO-ERROR.
IF AVAILABLE {&Table-Name} THEN
RUN executeUpdate IN THIS-PROCEDURE.
END.
END PROCEDURE.
Método novo:
Toda a lógica responsável pela alteração de registros foi passada para o
método updateRecord, ao qual o desenvolvedor não tem acesso. Este método é
responsável pela execução do método validateRecord e de seus métodos
override.
Nesta conversão, o método validateUpdate foi substituído por 1 (um) método
definido pelo usuário e 1 (um) definido em include padrão. E ainda, para
Técnicas
53
validateDelete
Este método foi subdividido em novos métodos, sendo 1 (um) deles definido
dentro do DBO e outro em include padrão.
Neste método, a parte responsável pelas validações específicas à eliminação
devem ser transferidas para o método beforeDeleteRecord. E ainda, caso
ocorra algum erro na validação, ao final deve ser retornado um flag "NOK":U
através do comando RETURN-VALUE.
Quanto aos erros ocorridos, estes devem ser cadastrados na temp-table
RowErrors através da include method/svc/errors/inserr.i.
Método antigo:
PROCEDURE validateDelete:
DEFINE INPUT-OUTPUT PARAMETER r-chave AS ROWID NO-UNDO.
DEFINE OUTPUT PARAMETER TABLE FOR tt-bo-erro.
FIND {&TABLE-NAME} WHERE ROWID({&TABLE-NAME}) = r-chave
EXCLUSIVE-LOCK NO-ERROR.
IF AVAILABLE {&TABLE-NAME} THEN
RUN executeDelete.
IF l-query THEN
GET NEXT {&QUERY-NAME} NO-LOCK NO-WAIT.
ELSE
FIND NEXT {&TABLE-NAME} NO-LOCK NO-ERROR.
IF NOT AVAILABLE {&TABLE-NAME} THEN DO:
IF l-query THEN
GET PREV {&QUERY-NAME} NO-LOCK NO-WAIT.
ELSE
FIND PREV {&TABLE-NAME} NO-LOCK NO-ERROR.
END.
ASSIGN r-chave = ROWID({&TABLE-NAME}).
END PROCEDURE.
Método novo:
Toda a lógica responsável pela eliminação de registros foi passado o método
deleteRecord, que é um método básico que o desenvolvedor não tem acesso.
Porém, este método é responsável pela execução de seus métodos override.
Nesta conversão, o método validateDelete foi substituído por 1 (um) método
definido pelo usuário e 1 (um) definido em include padrão. E ainda, para
realizar a eliminação de registro deve-se utilizar o método deleteRecord.
Conforme exemplo a seguir:
/* Pode-se utilizar 1 (um) dos métodos de navegação: getFirst,
getLast, getNext, getPrev ou repositionRecord, conforme a
implementação */
RUN repositionRecord IN THIS-PROCEDURE (INPUT r-Rowid).
RUN deleteRecord IN THIS-PROCEDURE.
validateFields
Este método foi substituído pelo método validateRecord, que é definido pelo
desenvolvedor.
Neste método, estão as validações análogas aos métodos de createRecord e
updateRecord. E ainda, caso ocorra algum erro na validação, ao final deve ser
retornado um flag "NOK":U, através do comando RETURN-VALUE, ou um
flag "OK":U caso não ocorre nenhum erro.
Quanto aos erros ocorridos, estes devem ser cadastrados na temp-table
RowErrors através da include method/svc/errors/inserr.i.
Estes procedimentos são demonstrados através do exemplo a seguir:
Método antigo:
PROCEDURE validateFields:
FIND FIRST RowObject NO-ERROR.
IF RowObject.Cust-Num <= 0 THEN DO:
ASSIGN i-seq-erro = i-seq-erro + 1.
RUN utp/ut-msgs.p (INPUT "MSG":U, INPUT 9999, INPUT "":U).
CREATE tt-bo-erro.
ASSIGN tt-bo-erro.i-sequen = i-seq-erro
tt-bo-erro.cd-erro = 999
tt-bo-erro.mensagem = RETURN-VALUE.
END.
IF RowObject.Name = "":U OR RowObject.Name = ? THEN DO:
ASSIGN i-seq-erro = i-seq-erro + 1.
RUN utp/ut-msgs.p (INPUT "MSG":U, INPUT 9999, INPUT "":U).
Técnicas
55
CREATE tt-bo-erro.
ASSIGN tt-bo-erro.i-sequen = i-seq-erro
tt-bo-erro.cd-erro = 999
tt-bo-erro.mensagem = RETURN-VALUE.
END.
END PROCEDURE.
Método novo:
PROCEDURE validateRecord:
IF RowObject.Cust-Num <= 0 THEN
{method/svc/errors/inserr.i
&ErrorNumber="9999"
&ErrorType="EMS"
&ErroSubType="ERRO"
&ErrorParameters="''"}
IF RowObject.Name = "":U OR RowObject.Name = ? THEN
{method/svc/errors/inserr.i
&ErrorNumber="9999"
&ErrorType="EMS"
&ErroSubType="ERRO"
&ErrorParameters="''"}
IF CAN-FIND(FIRST RowObject) THEN
RETURN "NOK":U.
RETURN "OK":U.
END PROCEDURE.
57
CAPÍTULO 5
PROCEDURE openQueryMain:
IF cSales-Rep = "" THEN
OPEN QUERY {&QueryName}
FOR EACH Customer NO-LOCK.
ELSE
OPEN QUERY {&QueryName}
FOR EACH Customer
WHERE Customer.Sales-Rep = cSales-Rep
NO-LOCK.
END PROCEDURE.
PROCEDURE openQueryByName:
IF cSales-Rep = "" THEN
OPEN QUERY {&QueryName}
FOR EACH Customer NO-LOCK BY Customer.Name.
ELSE
OPEN QUERY {&QueryName}
FOR EACH Customer
WHERE Customer.Sales-Rep = cSales-Rep
NO-LOCK BY Customer.Name.
END PROCEDURE.
PROCEDURE openQuerySalesRep:
OPEN QUERY {&QueryName}
FOR EACH Customer
WHERE Customer.Sales-Rep = cSales-Rep
NO-LOCK.
END PROCEDURE.
PROCEDURE setConstraintSalesRep:
DEFINE INPUT PARAMETER pSales-Rep LIKE Customer.Sales-Rep NO-
UNDO.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE linkToSalesRep:
DEFINE INPUT PARAMETER pHandle AS HANDLE NO-UNDO.
PROCEDURE local-create-record:
DEFINE VARIABLE iCust-Num AS INTEGER NO-UNDO.
Método Novo:
DEFINE BUFFER bfCustomer FOR Customer.
PROCEDURE beforeCreateRecord:
DEFINE VARIABLE iCust-Num AS INTEGER NO-UNDO.
Método Novo:
PROCEDURE before_copyTT2Buffer:
ASSIGN RowObject.Credit-Limit = RowObject.Credit-Limit *
1.10.
RETURN "OK":U.
END PROCEDURE.
IF adm-new-record THEN
IF CAN-FIND(Customer WHERE Customer.Cust-Num = iCustNum)
THEN DO:
RUN utp/ut-msgs.p (INPUT "SHOW":U,
INPUT <ErrorNumber>
INPUT <ErrorParameters>).
RETURN "ADM-ERROR":U.
END.
Técnicas
61
Método Novo:
PROCEDURE beforeCreateRecord:
IF CAN-FIND(Customer WHERE Customer.Cust-Num =
RowObject.Cust-Num) THEN DO:
{method/svc/errors/inserr.i
&ErrorNumber="9999"
&ErrorType="EMS"
&ErroSubType="ERRO"
&ErrorParameters="''"}
RETURN "NOK":U.
END.
RETURN "OK":U.
END PROCEDURE.
RETURN "ADM-ERROR":U.
END.
END PROCEDURE.
OBSERVAÇÃO: O utilitário UTP/UT-MSGS.P era utilizado em
SmartObjects, porém, não deverá ser utilizado em DBOs, conforme
conceito da tecnologia de DBOs.
Método Novo:
PROCEDURE validateRecord:
IF RowObject.Name = "":U THEN
{method/svc/errors/inserr.i
&ErrorNumber="9999"
&ErrorType="EMS"
&ErroSubType="ERROR"
&ErrorParameters="''"}
RETURN "OK":U.
END PROCEDURE.
RETURN "ADM-ERROR":U.
END.
Método Novo:
PROCEDURE beforeDeleteRecord:
IF CAN-FIND(FIRST Order OF RowObject) THEN
{method/svc/errors/inserr.i
&ErrorNumber="9999"
Técnicas
63
&ErrorType="EMS"
&ErroSubType="ERRO"
&ErrorParameters="''"}
RETURN "OK":U.
END PROCEDURE.
Método Novo:
DEFINE BUFFER bfCustomer FOR Customer.
PROCEDURE beforeCreateRecord:
FIND LAST bfCustomer NO-LOCK NO-ERROR.
IF AVAILABLE bfCustomer THEN
ASSIGN RowObject.Cust-Num = bfCustomer.Cust-Num + 1.
ELSE
ASSIGN RowObject.Cust-Num = 1.
RETURN "OK":U.
END PROCEDURE.
Método Novo:
PROCEDURE afterDeleteRecord:
FOR EACH Order OF RowObject:
DELETE Order.
END.
RETURN "OK":U.
END PROCEDURE.
Método Novo:
PROCEDURE validateRecord:
IF RowObject.Name = "":U THEN
{method/svc/errors/inserr.i
&ErrorNumber="9999"
&ErrorType="EMS"
&ErroSubType="ERRO"
&ErrorParameters="''"}
RETURN "OK":U.
END PROCEDURE.
PROCEDURE before_copyTT2Buffer:
IF RowObject.Address2 = "":U THEN
ASSIGN RowObject.Address2 = RowObject.Adress.
RETURN "OK":U.
END PROCEDURE.
PROCEDURE beforeUpdateRecord:
IF RowObject.Cust-Num <> Customer.Cust-Num THEN
FOR EACH Order OF Customer:
ASSIGN Order.Cust-Num = RowObject.Cust-Num.
END.
END PROCEDURE.
67
CAPÍTULO 6
Serviços Padrão
O que são serviços? Nada mais são, do que programas/procedures internas que
podem estar disponíveis para o DBO conforme a configuração feita para o
Produto.
Os serviços padrão disponíveis ao DBO são:
Implementações conforme tipo de Banco de Dados;
Customização para Parceiros/Clientes;
Erros do Produto;
Tratamento de erros Progress;
Segurança para o programa DBO;
Segurança aos métodos do programa DBO.
Estes serviços são criados e manutenidos por uma equipe de administração do
produto.
A configuração destes serviços é feito no include dboconfig/svcdesfs.i.
Conforme código a seguir:
/*--- Com base no {&DBType} serão executados procedimentos para
corrigir deficiências do produto PROGRESS, quando utilizar
banco de dados não PROGRESS ---*/
&GLOBAL-DEFINE DBType <tipo-do-banco-de-dados>
68
Serviço de Banco
Este serviço é definido através do preprocessador {&DBType} que contém o
tipo de banco de dados do produto.
Através deste preprocessador são executados lógicas/métodos alternativos a
fim de corrigir deficiências do produto Progress quando utiliza banco de dados
não Progress. Algumas destas implementações já estão feitas dentro dos
includes padrão do DBO.
Exemplo: Definindo valor para o preprocessador {&DBType}.
&GLOBAL-DEFINE DBType ORACLE
Serviço de Customização
Este serviço é definido através do preprocessador {&SOCustom} que contém
o nome do programa responsável pelos programas de customização do DBO.
Através deste preprocessador é executado o programa de tratamento de
customizações.
Além disso, este serviço é usado internamente nos métodos createRecord,
updateRecord e deleteRecord. Nestes métodos é executado o Serviço de
Customização no ponto inicial e final do método. Mas o desenvolvedor pode
optar por incluir manualmente a chamada ao Serviço de Customização.
Técnicas
69
Include: methos/svc/custom/custom.i
&IF "{&SOCustom}":U <> "":U &THEN
IF NOT VALID-HANDLE(hSOCustom) OR
hSOCustom:FILE-NAME <> "{&SOCustom}":U THEN
RUN {&SOCustom} PERSISTENT SET hSOCustom.
Serviço de Erros
Este serviço é definido através do preprocessador {&SOError} que contém o
nome do programa responsável por retornar informações sobre mensagens de
erro do Produto e tratamento de mensagens de erro Progress.
Através deste preprocessador é executado o programa de tratamento de erros
do produto e erros Progress.
O programa de serviço de erros é executado persistente, e deve possuir um
método chamado getMessageInformation, para realizar o tratamento de erros
do produto, com a definição de parâmetros a seguir:
DEFINE INPUT PARAMETER pErrorNumber AS INTEGER NO-UNDO.
DEFINE INPUT PARAMETER pErrorParameters AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAMETER pErrorDescription AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAMETER pErrorHelp AS CHARACTER NO-UNDO.
Include: methos/svc/errors/errors.i
&IF "{&SOErrors}":U <> "":U &THEN
IF NOT VALID-HANDLE(hSOErrors) THEN
RUN {&SOErrors} PERSISTENT SET hSOErrors.
{method/svc/errors/errors.i
&ErrorNumber="<ErrorNumber>"
&ErrorParameters="<ErrorParameters>"
&vErrorDescription="cErrorDescription"
&vErrorHelp="cErrorHelp"}
Include: methos/svc/errors/errorspsc.i
&IF "{&SOErrors}":U <> "":U &THEN
IF NOT VALID-HANDLE(hSOErrors) THEN
RUN {&SOErrors} PERSISTENT SET hSOErrors.
{method/svc/errors/errorspsc.i
&ErrorNumber="<ErrorNumber>"
&vErrorDescription="cErrorDescription"
&vErrorHelp="cErrorHelp"}
Para incluir erros na RowErrors a partir de uma api deve-se utilizar a include
method/svc/errors/inserrapi.i, com os mesmos preprocessadores da
method/svc/errors/inserr.i, porém com um preprocessador a mais: &Handledbo
– Este preprocessador deve receber a variável que contém o handle do dbo
onde serão criados os erros.
{method/svc/errors/inserrapi.i &Handledbo="h-bo"
&ErrorNumber=1
&ErrorType="EMS"
&ErrorSubType="Error"
&ErrorDescription="Estado utilizado por
customer"}
Serviço de Segurança
Este serviço é definido através do preprocessador {&SOSecurity} que contém
o nome do programa responsável por verificar a segurança do DBO.
Além disso, este serviço é usado internamente nos includes padrões. Nesses, é
feita a verificação de segurança do programa DBO e caso não tenha permissão
de execução é executado automaticamente o método destroy.
O programa de Segurança é executado persistent, e deve possuir um método
chamado verifySecurity com a definição de parâmetros a seguir:
DEFINE INPUT PARAMETER pDBOName AS CHARACTER NO-UNDO.
DEFINE INPUT PARAMETER pDBOHandle AS HANDLE NO-UNDO.
DEFINE OUTPUT PARAMETER pFunctionsPermited AS CHARACTER NO-UNDO.
DEFINE OUTPUT PARAMETER pVerifyOK AS LOGICAL NO-UNDO.
Include: method/svc/security/security.i
&IF "{&SOSecurity}":U <> "":U &THEN
IF NOT VALID-HANDLE(hSOSecurity) THEN
RUN {&SOSecurity} PERSISTENT SET hSOSecurity.
sendMessage
Realiza o envio da mensagem.
Sintaxe:
RUN sendMessage IN <handle Message Broker>
(INPUT pSendindMessage X-DOCUMENT,
OUTPUT pReturnMessage X-DOCUMENT).
Parâmetros:
pSendingMessage: Mensagem que deve ser enviado pelo Message Broker
a destinatários pré-definidos.
pReturnMessage: Mensagem de retorno indicando sucesso ou erro no
envio da mensagem.
Formato da mensagem XML requerida por pSendingMessage:
<DATASUL-MESSAGE>
<TOPIC>
<MODE>
</DATASUL-MESSAGE>
getSendMode
Indica as formas de envio que serão utilizadas pelo Message Broker para uma
mensagem com o tópico informado.
Sintaxe:
RUN getsendMode IN <handle Message Broker>
(INPUT pTopic CHAR,
OUTPUT pSendMode CHAR).
Parâmetros:
pTopic: Tópico para o qual deseja-se saber os modos de envio.
pSendMode: Forma de envio da mensagem:
“”: Não existe assinatura para o tópico;
Técnicas
75
Técnicas
77
CAPÍTULO 7
Técnicas
findFirstMain:
FIND FIRST {&TableName} NO-LOCK
WHERE {&TableName}.ep-codigo = i-empresa NO-ERROR.
findLastMain:
FIND LAST {&TableName} NO-LOCK
WHERE {&TableName}.ep-codigo = i-empresa NO-ERROR.
findNextMain:
FIND NEXT {&TableName} NO-LOCK
Técnicas
79
findPrevMain:
FIND PREV {&TableName} NO-LOCK
WHERE {&TableName}.ep-codigo = i-empresa NO-ERROR.
findFirstEmpresaEstab:
FIND FIRST {&TableName} NO-LOCK
WHERE {&TableName}.ep-codigo = i-empresa AND
{&TableName}.cod-estabel = c-cod-estab NO-ERROR.
findLastEmpresaEstab:
FIND LAST {&TableName} NO-LOCK
WHERE {&TableName}.ep-codigo = i-empresa AND
{&TableName}.cod-estabel = c-cod-estab NO-ERROR.
findNextEmpresaEstab:
FIND NEXT {&TableName} NO-LOCK
WHERE {&TableName}.ep-codigo = i-empresa AND
{&TableName}.cod-estabel = c-cod-estab NO-ERROR.
findPrevEmpresaEstab:
FIND PREV {&TableName} NO-LOCK
WHERE {&TableName}.ep-codigo = i-empresa AND
{&TableName}.cod-estabel = c-cod-estab NO-ERROR.
O motivo para a criação de tantos métodos de navegação é que, caso tenha
sido utilizada a abertura de query Main, devesse fazer a navegação através
de FINDs seguindo as restrições de aberturas dessa query. Da mesma
forma para a outra opção de abertura de query, chamada EmpresaEstab.
Então, sempre que o usuário utilizar um dos métodos de navegação, a
execução será encaminhada para o método findXXXQueryName
correspondente.