05 ordem execucaoregrase-formulas-cursogxxbr
Upcoming SlideShare
Loading in...5
×
 

05 ordem execucaoregrase-formulas-cursogxxbr

on

  • 402 views

Genexus Course

Genexus Course

Statistics

Views

Total Views
402
Slideshare-icon Views on SlideShare
402
Embed Views
0

Actions

Likes
0
Downloads
10
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    05 ordem execucaoregrase-formulas-cursogxxbr 05 ordem execucaoregrase-formulas-cursogxxbr Document Transcript

    • A forma de programar o comportamento das transações é definindo regras, escritas de forma declarativa. Quando temos cálculos para efetuar, podemos optar pela alternativa de definir atributos fórmulas. Em nenhum momento, o programador GeneXus especifica a seqüência de execução das regras e fórmulas definidas em uma transação. Quando for gerar, o GeneXus determina as dependências existentes entre as regras e fórmulas definidas. Vamos supor que estamos definindo uma aplicação para uma empresa que vende determinados produtos, e que conta com um serviço de entrega a domicílio que leva a mercadoria a seus clientes. E definimos entre outras, as seguintes 5 transações: “Customer” (para registrar os clientes da empresa) “Category” (as que pertencem cada cliente) “Shipping” (envios: guarda um histórico de custos de envio) “Invoice” (faturas emitidas aos clientes) “Product” (produtos que são vendidos pela empresa) Ressaltamos a estrutura da transação “Invoice”, com seus atributos fórmulas e suas regras declaradas. Em que ordens serão disparadas as regras e fórmulas da transação “Invoice”?
    • No momento de gerar o programa associado na transação “Invoice”, o GeneXus determinará as dependências existentes entre as regras e fórmulas definidas; e construirá logicamente uma árvore de dependências (ou árvore de avaliação) que determinará a seqüência de avaliação. Podemos imaginar que a árvore é executada de baixo para cima, cada vez que alteramos algum valor de um atributo, se executam todas as regras e fórmulas que dependem desse atributo (e que na árvore se encontram para cima). Por exemplo, alterando a quantidade de uma linha de uma fatura (InvoiceDetailQuantity), como este atributo interfere na fórmula que calcula o valor da linha (InvoiceDetailAmount), esta fórmula será disparada novamente. Para alterar o valor de uma linha, a fórmula é disparada novamente correspondendo ao subtotal da fatura (InvoiceSubTotal) e como conseqüência, também deverá ser recalculada a fórmula correspondente ao desconto (InvoiceDiscount), já que depende do subtotal. Deverá ser disparada novamente a fórmula correspondente ao total da fatura (InvoiceTotal) já que depende tanto do valor de InvoiceSubTotal como do valor de InvoiceDiscount. Por último, por alterar o total, também será disparada a regra Add(InvoiceTotal, CustomerTotalPurchases). Além de serem disparadas todas as fórmulas e regras envolvidas na parte direita da Árvore desde o atributo InvoiceDetailQuantity, também são disparadas as fórmulas e regras envolvidas na parte esquerda. Ou seja, ao alterar o valor do atributo InvoiceDetailQuantity, será disparada novamente também a regra Subtract(InvoiceDetailQuantity, ProductStock); e em conseqüência, por modificar esta regra o valor do atributo ProductStock verifica a necessidade de disparar a regra Error(‘Insufficient Stock’’) if ProductStock < 0; Concluindo, as regras e fórmulas que se definem numa transação estão inter-relacionadas e GeneXus determina as dependências entre elas assim como sua ordem de avaliação. Observemos as 2 últimas regras definidas: Subtract(InvoiceDetailQuantity, ProductStock); Error(‘Insufficient Stock’) if ProductStock < 0;
    • Estas regras estão inter-relacionadas porque envolvem o atributo ProductStock. Agora, enquanto que a segunda somente consulta seu valor, a primeira o atualiza. Então, a regra que atualiza o atributo será a que vai disparar o primeiro, e em seguida dispara a que o consulta. Toda regra que atualize o valor de um atributo, será disparada antes que uma regra que o consulte (isto pode ser observado claramente na árvore). Por este motivo é que a regra Error consulta se o atributo ProductStock ficou com valor negativo; porque como sabemos que a subtração será realizada primeiro,. Na programação clássica primeiro consulta-se o estoque, a subtração é realizada quando ele for suficiente. Por isso quem está aprendendo GeneXus pode intuitivamente escrever a regra: Error(Insufficient Stock’) if InvoiceDetailQuantity > ProductStock. Esta sintaxe é correta, não é correta sua lógica, já que como temos explicado, na árvore de avaliação determinada pelo GeneXus primeiro é disparada a regra Subtract e depois a regra Error; portanto temos que definir que se dispare a mensagem de error se o estoque ficou o stock com valor negativo, já que será executado a subtração no momento de consultar o valor de ProductStock. Assim que a regra deve definir é: Error(‘Insufficient Stock’) if ProductStock < 0; E não: Error('Insufficient Stock') if InvoiceDetailQuantity > ProductStock; Quando se dispara uma regra Error, se detêm qualquer atualização da base de dados e desarma a árvore de avaliação, ficando no estado anterior ao se produzir o erro. Seguindo o exemplo que estamos vendo, se ao disparar a regra Subtract o stock que ficará negativo, se dispararia a regra Error. Como conseqüência ao se disparar a regra Error, o Subtract que foi executado é desfeito, assim como todas as demais regras e fórmulas que tenham executado (recálculo dos atributos InvoiceDetailAmount, InvoiceSubTotal, ...., CustomerTotalPurchases).
    • Na maioria dos casos a ordem de execução das regras definida pelo GeneXus a partir de nossas especificações é o desejado. Porém em alguns casos podemos querer alterar o momento de disparo de uma Regra. Exemplo: Definimos uma transação para registrar as faturas que recebemos de nossos fornecedores. O identificador do primeiro nível é composto pelo código do fornecedor e o número de fatura, já que o número de fatura não nos serve como identificador único, porque fornecedores distintos podem repetir o mesmo número de fatura. Para cada fatura de um fornecedor que inserimos, nos interessa controlar que o total que venha escrito na fatura (e que será digitado no atributo InvoiceEntTotal) seja correto. Para fazer este controle, definimos o atributo InvoiceCalcTotal como fórmula vertical SUM(InvoiceDetailAmount), e agregamos uma regra Error que será disparada se não coincidir os valores dos atributos InvoiceEntTotal e InvoiceCalcTotal: Error(‘The calculated total doesn’t match with the entered total') if InvoiceCalcTotal <> InvoiceEntTotal; Se construirmos a árvore de avaliação correspondente as fórmulas e regra que definimos nesta transação:
    • vemos que as dependências indicam que cada vez que são inseridos, modificados ou eliminados valores dos atributos InvoiceDetailAmount e InvoiceDetailQuantity nas linhas, recalcula-se o valor do atributo InvoiceDetailAmount correspondente; em conseqüência, recalculamos o valor do atributo fórmula InvoiceCalcTotal que temos definido para ter o total calculado da fatura; e como o atributo fórmula InvoiceCalcTotal está envolvido na condição de disparo da regra Error, cumpre a dita condição de disparo e dispara a regra Error(‘The calculated total doesn’t match with the entered total’) if InvoiceCalcTotal <> InvoiceEntTotal; Agora, prestamos atenção a condição de disparo InvoiceCalcTotal<>InvoiceEntTotal vai ser cumprida repetidamente na medida em que o operador vai inserindo as linhas, porque para cada linha que é inserida, calcula-se o valor do atributo fórmula InvoiceDetailAmount da linha, e como consequência é recalculado o valor do atributo fórmula InvoiceCalcTotal. Mas o valor calculado do atributo InvoiceCalcTotal não coincidirá com o valor inserido no atributo InvoiceEntTotal até que não tenham ingressado todas as linhas da fatura; então, dispara-se a regra Error(‘The calculated total doesn’t match with the entered total’) if InvoiceCalcTotal <> InvoiceEntTotal;. Concluímos então que neste caso, não nos serve o determinado na árvore de avaliação, já que não queremos que seja analisada a condição de disparo da regra Error cada vez que o operador insira, altere ou elimine as linhas, e sim necessitamos que seja analisada quando o usuário tenha terminado trabalhar com todas as linhas da fatura. GeneXus oferece eventos ou momentos de disparo nas transações, que ocorrem antes ou depois de determinada ação, como a gravação do cabeçalho, ou de uma linha. As regras das transações podem serem condicionadas de tal maneira que sejam disparadas no momento específico que ocorre algum desses eventos de disparo. Seguindo o exemplo visto, existe um evento de disparo que ocorre assim que entramos num nível e saímos do mesmo. A sintaxe deste evento de disparo é: AfterLevel Level Atributo, devendo Atributo um atributo pertencente ao nível interagido e que se abandona. De modo que a regra Error de nosso exemplo, agregaríamos este evento de disparo, e ficaria definida da seguinte forma: Error(‘The calculated total doesn’t match with the entered total’) if InvoiceCalcTotal<>InvoiceEntTotal On AfterLevel ProductId; Agregando este evento de disparo a regra controlamos o que se deseja no momento adequado. Além deste evento de disparo, existem outros que veremos em seguida.
    • No momento da confirmação da transação, ocorre uma série de ações que é necessário para poder programar corretamente o comportamento das regras. Para uma transação de dois níveis, poderíamos enumerá-las como segue: • validação dos dados do cabeçalho • gravação física do cabeçalho (seja inserção, modificação ou eliminação) • validação dos dados da primeira linha • gravação física dos dados da primeira linha • validação dos dados da segunda linha • gravação física dos dados da segunda linha • … • validação dos dados da n-ésima linha • gravação física dos dados da n-ésima linha • commit A ação de “validação dos dados do cabeçalho” ocorre após todos serem validados e cada um dos campos informados no cabeçalho. Observar que neste ponto já foi disparado todas as regras que correspondiam aos atributos do cabeçalho e que não tinham evento de disparo associado (exemplo: Default(InvoiceDate, &today)). Imediatamente depois será gravado o registro correspondente ao cabeçalho. Análogo é o caso das linhas: “a validação dos dados de uma linha” ocorre após todos serem validados e cada um dos dados da linha, e também após terem sido disparadas todas as regras correspondentes segundo a árvore de avaliação (exemplo: subtract( InvoiceDetailQuantity, ProductStock)). Imediatamente depois desta ação de validação, será gravado fisicamente o registro correspondente linha. Cada transação, ao terminar de trabalhar com um cabeçalho e suas linhas, realiza um commit (é automático). Será colocado no código gerado por GeneXus, exceto se o analista especifique o contrário, como veremos mais adiante. Isto é, se os dados de duas faturas distintas forem ingressados utilizando a transação “Invoice”, após serem ingressados os dados da primeira, os registros serão comitados, e depois será ingressado o segundo, ao qual seus registros serão comitados. Os eventos de disparo de regras permitem condicionar o disparo de uma regra da transação para que se execute antes ou depois de alguma das ações que acabamos de enumerar. Veremos quando ocorre cada evento de disparo.
    • Evento de disparo: BeforeValidate Este evento de disparo ocorre um pouco antes da informação da instancia que se trabalhe (cabeçalho ou linha) seja validada (ou confirmada). Isto é, ocorrerá antes da ação de “validação do cabeçalho” ou “validação da linha”, correspondente. Observar que aqui também haverão disparadas todas as regras segundo a árvore de avaliação que não estejam condicionadas com nenhum evBeforeento de disparo. Eventos de disparo: AfterValidate, BeforeInsert, BeforeUpdate, BeforeDelete O evento de disparo AfterValidate permite definir que uma regra seja executada imediatamente antes de que gravemos fisicamente cada instancia do nível ao qual está associada a regra, na tabela física correspondente e depois de ter validado os dados desta instancia. Em outras palavras, agregando o evento de disparo AfterValidate a uma regra, a mesma executará, para cada instancia do nível ao qual esteja associada a regra, imediatamente antes de que a instancia seja gravada fisicamente (seja quando insere, altere ou elimine) como registro na tabela física associada ao nível. EXEMPLOS 1. Tem vezes que não contamos com a possibilidade de utilizar a Propriedade Autonumber para numerar de forma automática e correlativa os atributos que são chave primária simples. Tal funcionalidade é prevista pelos administradores de base de dados (DBMSs) e o GeneXus a aproveita e permite usá-la; nos casos em que não trabalhamos com um administrador de base de dados, não temos a possibilidade de selecionar esta facilidade. Nesses casos que não contamos com a possibilidade de utilizar a Propriedade Autonumber e necessitamos numerar de forma automática e correlativa certos atributos, devemos resolver nós mesmos na programação. Para fazê-lo somente definindo uma transação contendo ao menos dois atributos, um para armazenar um literal e outro para armazenar o último número atribuído automaticamente ao atributo descrito pelo literal1; a transação vai fazer com que seja criada uma tabela, e temos que definir um procedimento que consulte essa tabela, obtenha o último número atribuído para o atributo a ser numerado, some um e devolva o próximo número, além de atualizá-lo na tabela. Para chamar ao procedimento de numeração automática se deve definir nas transações que o requerem a seguinte regra: CustomerId = PGetNumber.udp( ‘CUSTOMER’ ) if Insert on AfterValidate; Neste caso se está querendo serializar o atributo CustomerId da transação “Customer” Do mesmo modo, se queremos serializar o identificador de faturas, escreveríamos na transação “Invoice” com a seguinte regra: InvoiceId = PGetNumber.udp( ‘INVOICE’ ) if Insert on AfterValidate; Desta forma definimos que se efetuam numerações automáticas nas transações unicamente quando se realizem inserções (pela condição de disparo: if Insert) e imediatamente antes do registro ser gravado fisicamente cada instancia a ser inserida (pelo evento de disparo: on AfterValidate) através do primeiro nível da transação (porque nas duas regras de chamadas que foram mostradas, tem somente um atributo envolvido que pertence ao primeiro nível das transações “Customer” e “Invoice” respectivamente). O motivo pelo qual agregamos o evento de disparo on AfterValidate a estas regras é para chamar o procedimento de numeração automática imediatamente antes de que seja inserido o registro na base de dados e depois da validação, tentando desta forma ter o maior grau de segurança possível ao número atribuído que será utilizado (e não perder números). Caso a regra fosse disparada sem ter condição, e no caso de faltar alguma validação dos dados do cabeçalho. A resposta é simples: um número seria perdido. Ou seja, se o número da fatura anterior for 5 e o usuário quer informar a seguinte fatura, a regra de atribuição com udp chama o procedimento de numeração que seria disparado assim que ingressar na transação com modo insert, porque o primeiro atributo do cabeçalho é utilizado. O procedimento devolveria o número 6, e se ao validar os dados do cabeçalho encontrar algum error que não permita continuar com o processo, abandonar a transação, por exemplo, esse número 6 seria perdido e a próxima fatura, que deveria ter o número 6 não o terá, e sim o número 7. ______________________________________________________________________________________ __ 1 Quando vimos a regra serial falamos este tema de numeração de cabeçalho, com a transação "Number” que era a que tinha o literal: NumberCode e o último número atribuído a transação correspondente a esse literal: NumberLast
    • Existem 3 eventos de disparo que ocorrem no mesmo momento que o AfterValidate, mas já possuem o modo de forma intrínseca. São eles: BeforeInsert, BeforeUpdate e BeforeDelete. É equivalente escrever a regra apresentada como: InvoiceId = PGetNumber.udp( 'INVOICE') on BeforeInsert; Observar que aqui é redundante condicionar a regra a “If Insert”. Portanto, as seguintes equivalências são válidas: on BeforeInsert ∼ If Insert on AfterValidate on BeforeUpdate ∼ If Update on AfterValidate on BeforeDelete ∼ If Delete on AfterValidate Se tivermos um esquema das ações que rodeiam o disparo do evento, ficam claros os dois sinônimos escolhidos para este evento (AfterValidate e BeforeInsert para modo insert) VALIDAÇÃO DOS DADOS AfterValidate – BeforeInsert – BeforeUpdate – BeforeDelete GRAVAÇÃO DO REGISTRO (insert, update, delete segundo corresponda) 2) Se definirmos uma regra em que incluímos também o evento de disparo on AfterValidate, ou on BeforeInsert, BeforeDelete, BeforeUdate, mas a diferença dos exemplos recém vistos, é referenciado na regra pelo menos um atributo do segundo nível da transação na qual está definindo a regra, a mesma estará associada ao segundo nível1. Portanto, a regra executará imediatamente antes que seja gravada fisicamente cada instancia correspondente ao segundo nível da transação. Eventos de disparo: AfterInsert, AfterUpdate, AfterDelete Assim como existe um evento de disparo que permite definir que determinadas regras sejam executadas imediatamente antes que seja produzida a gravação física de cada instancia de um nível (AfterValidate, BeforeInsert, BeforeUpdate e BeforeDelete), também existem eventos de disparo para definir que certas regras sejam executadas imediatamente depois de que sejam inseridas, modificadas ou eliminadas fisicamente instancias de um nível. Estes eventos são AfterInsert, AfterUpdate e AfterDelete. O evento de disparo AfterInsert permite definir que uma regra execute imediatamente depois de que seja inserida fisicamente cada instancia do nível ao qual está associada a regra; o AfterUpdate depois de atualizar fisicamente a instancia, e o AfterDelete depois de eliminar. EXEMPLOS Vamos supor que na transação “Customer” queremos chamar um relatório que realize a impressão dos dados de cada cliente com o qual trabalhamos por meio da transação. Em que momento devemos realizar as chamadas ao relatório a partir da transação? Caso 1: RPrintCustomer.call( CustomerId ) on AfterValidate; Não é adequado agregar-lhe este evento de disparo a regra de chamadas ao relatório, porque o mesmo seria chamado imediatamente antes da gravação física de cada cliente. Em conseqüência, o relatório não encontraria o cliente com seus dados na tabela CUSTOMER (caso estivesse inserindo um cliente por meio da transação), ou o encontraria com seus dados desatualizados (caso estivesse modificando um cliente por meio da transação). Se na alteração estivesse eliminando um cliente por meio da transação, o relatório encontraria os dados do cliente na tabela CUSTOMER e os listaria justamente antes da atualização física (eliminação). Caso desejarmos emitir uma lista com os dados de cada cliente que forem eliminados, seria adequado definir a seguinte regra: RPrintCustomer.call( CustomerId ) on BeforeDelete; ou equivalente: RPrintCustomer.call( CustomerId ) if delete on AfterValidate; __________________________________________________________________________________________________ 1 Existe outra forma de provocar que uma regra que contem atributos de um nível determinado, se dispare no nível seguinte, por meio de cláusula Level que foi mencionado quando vimos conceitos importantes sobre as regras de transações.
    • para restringir o disparo da regra unicamente quando estamos eliminando um cliente, porque é o único caso em que seria correto utilizar o evento de disparo AfterValidate (já que justamente necessitamos emitir o relatório antes da eliminação). Caso 2: RPrintCustomer.Call( CustomerId ) on AfterInsert; O evento de disparo AfterInsert ocorre imediatamente depois de que inserimos fisicamente cada instancia associada a certo nível da transação (neste caso, como o único atributo envolvido na regra é CustomerId, trata-se de uma regra associada ao primeiro e único nível da transação “Customer”). Como indica claramente pelo seu nome, o evento de disparo AfterInsert somente ocorre ao inserir uma nova instancia (precisamente após ser inserida como registro físico). Utilizando ele quando se agrega o evento de disparo on AfterInsert a uma regra, não é necessário agregar-lhe a condição de disparo if insert. É correto agregar este evento de disparo a regra de chamadas ao relatório, já que o relatório seria chamado imediatamente depois de que fosse inserido fisicamente cada cliente. Assim que o relatório encontrar o cliente com seus dados na tabela CUSTOMER e os imprime. O que deve ficar claro é que com esta definição, o relatório é chamado unicamente logo que realizar inserções. Caso 3: RPrintCustomer.Call( CustomerId ) on AfterUpdate; O evento de disparo AfterUpdate ocorre imediatamente depois que é atualizada fisicamente cada instancia associada a certo nível da transação (neste caso, como o único atributo envolvido na regra é CustomerId, se trata de uma regra associada ao primeiro e único nível da transação “Customer”). É adequado agregar neste evento de disparo a regra de chamadas ao relatório, já que o relatório é chamado imediatamente depois que for atualizado fisicamente um cliente. Assim que o relatório encontrar o cliente com seus dados atualizados na tabela CUSTOMER e os imprimir. O relatório será chamado unicamente após realizar as atualizações. Caso 4: RPrintCustomer.Call( CustomerId ) on AfterDelete; O evento de disparo AfterDelete ocorre imediatamente depois da eliminação física de cada instancia associada a certo nível da transação (neste caso, como o único atributo envolvido na regra é CustomerId, se trata de uma regra associada ao primeiro e único nível da transação “Customer”). Não é adequado agregar este evento de disparo a regra de chamadas ao relatório, porque o relatório é chamado imediatamente depois da eliminação física de cada cliente. Em conseqüência, o relatório não encontra o cliente com seus dados na tabela CUSTOMER. Caso 5: RPrintCustomer.Call( CustomerId ) on AfterInsert, AfterUpdate; RPrintCustomer.Call(CustomerId ) if delete on AfterValidate; Para finalizar, estas duas regras são adequadas para chamar um relatório na transação “Customer”, com o objetivo de imprimir os dados de cada cliente com qual trabalhar, abrangendo os três modos de trabalho. Como podemos observar na primeira regra, é possível incluir vários eventos de disparo separados por vírgula, quando os mesmos aplicam a uma mesma regra. Isto é, é o mesmo que definir duas regras independentes: RPrintCustomer.Call( CustomerId ) on AfterInsert; RPrintCustomer.Call( CustomerId ) on AfterUpdate; que esta regra: RPrintCustomer.Call( CustomerId ) on AfterInsert, AfterUpdate;
    • Caso 6: Se definirmos uma regra a qual incluímos o evento de disparo on AfterInsert, mas a diferença dos exemplos vistos recentemente, é referenciada na regra pelo menos um atributo do segundo nível da transação na qual estamos definindo a regra, a mesma estará associada ao segundo nível. Portanto, a regra é executada imediatamente depois de que inserimos fisicamente cada instancia correspondente ao segundo nível da transação. Analogamente é o caso de disparo de on AfterUpdate e on AfterDelete. Ampliamos o esquema que havíamos efetuado antes, das ações que rodeiam aos eventos de disparos visto até agora: VALIDAÇÃO DOS DADOS AfterValidate – BeforeInsert – BeforeUpdate – BeforeDelete GRAVAÇÃO DO REGISTRO (insert, update, delete segundo corresponda) AfterInsert – AfterUpdate – AfterDelete Este esquema se repete para cada instancia do nível. Por exemplo, pensamos no ingresso das linhas de uma fatura. Este esquema ocorre para cada linha, podemos pensar num loop que se repete até que a última linha seja gravada. A ação depois da última linha seria gravada é quando sai desse nível (neste caso as linhas da fatura). E depois dessa ação, exceto que tenha outro nível, nesse caso voltaria o esquema anterior, ocorrerá o commit que é a última ação de execução. Entre a ação de abandonar o nível, e o commit temos um evento (que admite dois nomes distintos) e outro para depois do commit. São o que veremos na continuação mas que já vamos mostrar no esquema: VALIDAÇÃO DOS DADOS CABEÇALHO AfterValidate – BeforeInsert – BeforeUpdate – BeforeDelete GRAVAÇÃO DO REGISTRO (insert, update, delete segundo corresponda) AfterInsert – AfterUpdate – AfterDelete VALIDAÇÃO DOS DADOS LINHA AfterValidate – BeforeInsert – BeforeUpdate – BeforeDelete GRAVAÇÃO DO REGISTRO (insert, update, delete segundo corresponda) AfterInsert – AfterUpdate – AfterDelete ABANDONAR NÍVEL 2 AfterLevel - BeforeComplete COMMIT AfterComplete
    • Eventos de disparo: AfterLevel, BeforeComplete O evento de disparo AfterLevel permite definir que uma regra seja executada imediatamente depois de terminar de interagir determinado nível. SINTAXE: regra [if condição de disparo] [on AfterLevel Level atributo]; ONDE: regra: é uma regra das permitidas em transações condição de disparo: é uma expressão booleana que permite envolver atributos, variáveis, constantes e funções, assim como os operadores Or, And, Not. atributo: é um atributo pertencente ao nível para o qual se deseja que depois de ser iterado, se execute a regra. FUNCIONALIDADE: Se o atributo que especificamos na continuação do evento de disparo AfterLevel pertencer ao segundo nível da transação, a regra será executada quando terminar de interagir com todas as linhas do segundo nível. E se o atributo especificado em seguida do evento de disparo AfterLevel pertencer ao primeiro nível, - seguindo o mesmo conceito - a regra será executada quando tenha terminado de interagir com todos os cabeçalhos. Observar que isto ocorre no final de tudo, ou seja, uma vez que tivermos inserido todos os cabeçalhos e suas linhas e fechado a transação (nesse momento terão interagido todos os cabeçalhos). Portanto, se o atributo especificado pertencer ao primeiro nível, à regra será disparada uma vez somente antes do Evento Exit (é um evento que executa uma vez quando fechamos uma transação em tempo de execução, como veremos). Exemplo: Rever o exemplo apresentado anteriormente, onde tínhamos uma transação para representar as Faturas dos fornecedores, e onde tínhamos que controlar que o total calculado de cada fatura coincida com o total informado. Tínhamos a regra: Error(‘The calculated total doesn’t match with the entered total ') if InvoiceCalcTotal<>InvoiceEntTotal; que necessitamos que se dispare depois de todas as linhas da fatura do Fornecedor serem informadas. Portanto, o evento de disparo apropriado será AfterLevel att, onde att pode ser qualquer atributo das linhas. O evento de nome BeforeComplete, neste caso, coincide com o AfterLevel. Se observarmos o esquema apresentado na página anterior, podemos ver que o tempo que tem entre sair do último nível e a realização do commit é o instante que ocorrem estes eventos. São 02 (dois) nomes para nos referimos ao mesmo. Cuidado que isto é sempre assim e quando o nível abandonado é o último. Por exemplo uma transação com dois níveis paralelos. Se agregamos ao cliente suas direções de mail e seus números telefônicos (pode ter vários): CustomerId* CustomerName … {CustomerPhone* …} {CustomerEMail* …} O momento em que deverá disparar uma regra condicionada a: On AfterLevel Level CustomerPhone NÃO COINCIDIRÁ com o de uma regra condicionada a on BeforeComplete. Enquanto que a primeira se dispara ao sair do nível dos telefones, e antes de entrar a validar todos os emails, a segunda se disparará depois de sair deste último nível. Neste caso o evento BeforeComplete coincidirá com o AfterLevel Level CustomerEMail. Evento de disparo: AfterComplete Este evento corresponde ao instante de tempo que acontece o commit. Falaremos mais deste evento umas páginas adiantes, quando estudarmos integridade transacional. Se abrir a transação de faturas, se ingressam 3 faturas (cabeçalho e suas respectivas linhas) e fecha a transação, ocorrerão 3 commits (um no final de cada ingresso de cabeçalho + linhas) e 3 eventos AfterComplete.
    • O seguinte exemplo pretende mostrar visualmente em que momentos serão disparadas as regras e fórmulas definidas numa transação. O disparo de regras e fórmulas irá sendo feito de acordo com a árvore de avaliação, seguindo a ordem que esta determina.
    • Regras stand alone As regras stand alone são aquelas que: 1. Podem executar-se com a informação prevista pelos parâmetros recebidos. 2. Não dependem de nada para serem executadas. Exemplos de regras stand alone (poder executar com a informação prevista pelos parâmetros): · &A = parâmetro2; · Msg( ‘...’ ) if parâmetro1 = 7; Exemplos de regras stand alone (não dependem de nada para serem executadas) : · msg( ‘You are in the invoice transaction’); · &A = 7; Portanto, são as primeiras regras que podem ser executadas. Depois da execução das regras stand alone, se executam as regras associadas ao primeiro nível da transação, que não tenham evento de disparo definido, seguindo a ordem de dependências determinado por GeneXus (assim como as fórmulas associadas ao primeiro nível). Como exemplo, se dispara a regra: “Default( InvoiceDate, &Today);” Depois de executadas as regras mencionadas para o cabeçalho, serão executadas todas as regras que tenham como evento de disparo BeforeValidate, já que imediatamente depois ocorre a ação de validação (ou confirmação) da informação desse primeiro nível. Imediatamente depois da validação do primeiro nível se executam as regras associadas ao primeiro nível da transação que incluam em sua definição o evento de disparo AfterValidate, ou os BeforeInsert, BeforeUpdate, BeforeDelete, dependendo do modo em que esteja. Por exemplo: Se não podemos serializar as faturas com a propriedade Autonumber porque o DBMS escolhido não suporta: InvoiceId = PGetNumber.udp(‘INVOICE’) on BeforeInsert;
    • Após a execução das regras associadas ao primeiro nível com alguns destes eventos de disparo executa-se a ação de gravação; ou seja, que gravará fisicamente a instancia correspondente ao primeiro nível da transação como registro físico na tabela correspondente (neste exemplo, na tabela: INVOICE). Imediatamente depois de ter gravado a instancia: · se a gravação correspondeu a uma inserção: executarão as regras associadas ao primeiro nível da transação com evento de disparo AfterInsert. · se a gravação correspondeu a uma atualização: executarão as regras associadas ao primeiro nível da transação com evento de disparo AfterUpdate. · se a gravação correspondeu a uma eliminação: executarão as regras associadas ao primeiro nível da transação com evento de disparo AfterDelete. Sendo uma transação de dois níveis, como neste caso, serão executadas para cada uma das linhas: Em primeiro lugar, as regras associadas ao segundo nível da transação que não tenham evento de disparo definido, seguindo a ordem de dependências determinado pelo GeneXus (assim como as fórmulas associadas ao segundo nível). Exemplos disso são a regra: Subtract( InvoiceDetailQuantity, ProductStock ); a fórmula InvoiceDetailAmount = InvoiceDetailQuantity*ProductPrice. Depois das regras mencionadas executadas para uma linha, serão executadas todas as regras que tenham como evento de disparo BeforeValidate, visto que imediatamente depois ocorre a validação da linha; isto é uma ação que ocorre depois de terminar de trabalhar com a linha. Imediatamente depois da validação da linha, serão executadas as regras associadas ao segundo nível da transação que incluam em sua definição algum dos eventos de disparo: AfterValidate, BeforeInsert, BeforeUpdate, BeforeDelete. Em seguida a execução das regras associadas ao segundo nível com algum destes eventos de disparo será executada a ação de gravação; isto é, gravará fisicamente a instancia correspondente a linha como registro física na tabela correspondente (neste exemplo, na tabela: INVOICEDETAIL). Imediatamente depois de ter gravado a instancia correspondente a linha como registro físico na tabela correspondente: • se a gravação correspondeu a uma inserção: são executadas as regras associadas ao segundo nível da transação com evento de disparo AfterInsert. • se a gravação correspondeu a uma atualização: são executadas as regras associadas ao segundo nível da transação com evento de disparo AfterUpdate. • se a gravação correspondeu a uma eliminação: são executadas as regras associadas ao segundo nível da transação com evento de disparo AfterDelete. Todas estas operações sombreadas de cinza claro, são executadas na ordem descrita, para cada uma das linhas.Após a interação de todas as linhas, podemos supor a existência de uma ação que poderíamos chamar abandono do segundo nível. Após dela são executadas as regras definidas com evento de disparo AfterLevel Level Atributo do 2do nível. Se não existir outro nível, como é o caso do exemplo, então coincidirá com o evento de disparo BeforeComplete
    • Declaração Importante: Todas as operações sombreadas, tanto de cor clara como de cor escura, executam-se unicamente quando for uma transação de dois níveis; de modo que se for uma transação de um nível, sabemos que tais operações não são executadas. O motivo dos dois sombreados distintos, é para diferenciar o conjunto de operações que são executadas para cada uma das linhas (sombreado cor clara) das operações que são executadas somente uma vez ao terminar de interagir com as linhas (sombreado com cor mais escura). Posteriormente, explicaremos as demais operações que são executadas, seja em uma transação de um nível ou de dois. Após de ser executada todas as operações explicadas até o momento, um commit é efetuado. Na continuação serão executadas as regras com evento de disparo AfterComplete. É de fundamental importância que fique claro que todas as operações explicadas, serão executadas na ordem que foi descrito, para cada fatura com a qual se trabalhe por meio da transação “Invoice” (seja quando se insere, modifique ou elimine) Pode ser útil saber que serão ressaltadas em negrito as ações cada vez que se tenha mencionado. As mesmas são: validação, gravação, abandono do segundo nível (sair do segundo nível) e commit. É indispensável assimilar fortemente em que ordem se executam as regras em uma transação, quais são os eventos de disparo disponíveis para atribuir, quando são disparadas exatamente, e que ações ocorrem antes e depois de cada evento de disparo, já que somente conhecendo bem todo este tema, poderá programar o comportamento das transações adequadamente. É fácil compreender que se necessitamos programar determinados controles ou ações nas transações, teremos que saber bem se será feito antes de gravar o cabeçalho, depois de gravar o mesmo, para cada uma das linhas depois que se tenham gravado, ou antes, depois do commit, ou antes, portanto é fundamental ter bem claro todo este tema.
    • Regras com o mesmo evento de disparo Quando em uma transação definimos duas ou mais regras com o mesmo evento de disparo, e não existe nenhuma dependência entre elas, as mesmas serão executadas respeitando a ordem de definição. Exemplos: 1) Se definem as seguintes regras em uma transação: ‘xxx’.Call() on AfterComplete; ‘yyy’.Call() on AfterComplete; Como as duas regras definidas estão condicionadas ao mesmo evento de disparo, e não existe nenhuma dependência entre elas, elas serão executadas na mesma ordem em que foram escritas. 2) Em uma transação, caso seja necessário chamar a um procedimento que realiza determinada validação e retorna um valor ‘S’ ou ‘N’; e se o valor devolvido é ‘N’, aparecerá uma mensagem de erro. Para resolver isto, avaliaremos duas possibilidades: 2.1) Definir as regras: Pgmname.call(CustomerId, &flag) on AfterComplete; Error(“ “) if &flag=”N” on AfterComplete;
    • 2.2) Ou definir as regras: &flag = Pgmname.udp(CustomerId) on AfterComplete; Error(“ “) if &flag = ”N” on AfterComplete; Na primeira alternativa, temos definida uma regra call e uma regra error. Ambas regras tem o mesmo evento de disparo, e aparentemente existiria dependência entre elas, já que a regra de error está condicionada ao valor da variável &flag, e a variável &flag se passa por parâmetro na regra call. Todavia, a dependência pode parecer evidente porque no procedimento programamos a variável &flag, de saída, na seção de regras da transação - que é onde encontram-se as regras vista até então -, o especificador do GeneXus não pode saber se os parâmetros passados em um call são de entrada, de saída, ou de entrada-saída; em conseqüência o especificador não encontrará inter-dependência entre as regras call e error, já que a variável &flag poderia ser passada como variável de entrada ao procedimento, e nesse caso por Exemplo, não haveria uma dependência de que primeiro deve-se executar a regra call e depois a regra error. Concluindo, não se detectam dependências entre as regras call e error da alternativa 2.1), porque as mesmas serão disparadas na Ordem em que estão escritas. É importante ver que, se as regras call e error estiverem escritas em ordem inversa (primeiro a regra error e depois a regra call), em muitos casos o comportamento não será o esperado. A respeito da segunda alternativa, observemos que consiste em uma regra udp e uma regra error. Ambas regras tem o mesmo evento de disparo, e neste caso se existir dependência entre elas, já que a regra error está condicionada ao valor da variável &flag, e como as chamadas ao procedimento realizam-se com udp, para o especificador do GeneXus fica claro que a variável &flag volta modificada do procedimento; portanto, o especificador do GeneXus entende que primeiro deve-se disparar a chamada ao procedimento com udp e depois a regra error, porque a variável &flag é carregada mediante a chamada ao procedimento com udp, e logo que esta variável tenha valor, é que será avaliada a necessidade de disparar a regra error, ou não. No caso 2.2), então, independentemente da ordem de definição de ambas as regras, a chamada ao procedimento com udp é disparada primeiro, e em seguida dispara a regra error (caso cumpra-se a condição de disparo). Por esta razão recomendamos que sempre que se quer definir validações desse tipo, se utilize udp ao invés de call.
    • Nas transações permite-se a programação dirigida por eventos, que é um estilo de programação na qual se define código que permanece ocioso, até que os eventos provocados pelo Usuário ou pelo sistema, provoquem que o código definido seja executado. Os eventos são ações reconhecidas por um objeto que podem acontecer ou não. A cada evento podemos associar o código, que será executado somente caso o evento se produza. O código que pode ser associado a um evento, é escrito de forma procedural; e quando o evento for produzido, o código associado ao mesmo será executado seqüencialmente.
    • Como em Web não se mantém um estado no servidor que permita saber o que está sendo executado no cliente, não é possível saber se está ingressando a primeira instancia de uma fatura, ou se é a n- ésima. Por esta razão, se disparará o evento Start cada vez que se envie ao servidor a informação da instancia com a qual estiver trabalhando. Enquanto que no evento Exit, se excuta por cada iteração, ao final da mesma. O evento TrackContext é aplicado para obter interfaces de usuário sensíveis ao contexto. Programando este evento pode-se receber informação do contexto para depois tomar as decisões necessárias.
    • O evento Start é um evento do sistema, ocorre automaticamente. EXEMPLO: Em uma transação nos interessa capturar a data e hora de entrada da mesma. Para isso no evento Start o atribuímos a uma variável de nome &entrada e tipo de dados DateTime, o resultado da função Now() que devolve a data e hora atual: Event Start &entrada = Now() EndEvent Se executa cada vez que se submeta o form da transação, ou seja, quando o usuário pressionar qualquer botão do form. Notas gerais: No evento Start fundamentalmente se trabalha com variáveis. Já a utilização dos atributos neste evento, seja para avaliá-los e/ou usá-los de algum modo exceto para atualizá-los, se deve considerar que os únicos atributos que estão disponíveis são os que são recebidos por parâmetro na regra parm. Nenhum outro atributo terá valor neste evento, pois todavia não foi editado a instancia da transação.
    • Como pode-se observar na sintaxe, deve-se dar um nome a um evento do usuário, após a palavra Event, que encontra-se entre aspas simples. EXEMPLO: Se deseja que na transação "Invoice", o usuário possa imprimir a fatura com a qual esteja trabalhando, pressionando F7 (isto somente é válido para Win): Event ‘Print Invoice’ // evento definido na transação "Invoice" PrintInvoice.Call( InvoiceId ) EndEvent Como associar um evento de usuário a um controle? Além dos botões, também as imagens e os text blocks admitem a associação de evento de usuário. Para realizar a associação se deve inserir o controle correspondente no form Web e depois nas propriedades do controle, selecionar onde diz OnClickEvent um dos eventos existentes, ou se pode criar um novo.
    • O evento After Trn das transações ocorre imediatamente depois da execução das regras com evento de disparo AfterComplete. Em seguida, o código incluído neste evento, será executado depois que termine cada interação completa por meio da transação (assim que é gravado cada cabeçalho com suas correspondentes linhas como registros físicos nas tabelas que corresponda e de se ter efetuado o COMMIT). Existem as seguintes alternativas para programar comportamentos que desejam ser executados no final de cada interação completa por meio de uma transação: 1. Definir regras individuais com evento de disparo AfterComplete e deixar o evento After Trn sem código 2. Definir todas as sentenças no evento After Trn com estilo procedural, e não definir regras com evento de disparo AfterComplete 3. Definir as duas coisas: algumas regras com evento de disparo AfterComplete e código no evento After Trn Como vemos explicando, primeiro são executadas as regras definidas com evento de disparo AfterComplete, e imediatamente após, executamos as regras com código definido no evento After Trn. Um conceito que é muito importante ter claro é que tanto nas regras com evento de disparo AfterComplete como no evento After Trn, os valores dos atributos do primeiro nível da transação são conhecidos. Ou seja, serão gravados fisicamente os registros correspondentes ao cabeçalho e as linhas de certa interação completa, inclusive se efetuou COMMIT, ainda temos disponíveis os valores dos atributos do primeiro nível, podendo ser utilizados para passar por parâmetro em uma chamada, ou avaliar seu valor, ou usar de algum modo, exceto atualizá-los ¹. ________________________________________________________________________________ 1 Existem dois motivos pelos quais não é possível atualizar atributos em regras com evento de disparo AfterComplete e After Trn. O primeiro motivo é que já foi realizado as gravações correspondentes e incluso o COMMIT já foi realizado, de modo que já é tarde para atribuir valores aos atributos. Além disso, relacionado ao evento After Trn, nos eventos não é permito realizar atribuições aos atributos.
    • Não é permitido atribuir valores a atributos nos eventos. Os valores dos atributos podem ser modificados nas transações: · o usuário final fazendo, em tempo de execução, através do form (somente atributos das tabelas bases associadas a transação, ou aqueles da estendida permitidos pela regra update) · mediante regras definidas pelo programador (a atributos das tabelas bases associadas a transação e suas estendidas) Os eventos Start e Exit são sem tabela base. Com esta expressão nos referimos que nos eventos Start e Exit não tem consulta ativa à base de dados (já que no evento Start ainda não foi feito a consulta e no evento Exit em Win já foi fechado o programa associado a transação e em Web a instancia está sendo fechada e já não dispõe da consulta). Por este motivo é que não conhecemos valores de atributos nos eventos Start e Exit, exceto os recebidos por parâmetro. Ao contrário, os eventos After Trn e de Usuário são com tabela base, quando os mesmos se executam temos uma consulta feita. Então, em particular no evento After Trn, conhecemos os valores dos atributos do primeiro nível (o segundo nível já foi inserido a essa altura e não tem possibilidade de posicionamento em alguma linha em particular); e no que diz respeito aos eventos de Usuário os atributos de todos os níveis 1 estão disponíveis. É fundamental que fique claro, que assim que se disponha dos valores de certos atributos ou outros dependendo do evento, os mesmos poderão ser utilizados para ser analisados e/ou passados por parâmetro a objetos que se chamem, e/ou para alguma outra operação qualquer que não seja atribuir valor. Concluindo, em nenhum evento (não somente das transações, mas em nenhum objeto GeneXus) permite-se realizar atribuições a atributos. ___________________________________________________________________________________________________________________________ 1 Se num evento de usuário são referenciados atributos de um segundo nível ou outro nível subordinado, quando o evento de usuário for executado nos atributos daquela linha se tem um posicionamento; no momento que o evento de usuário é executado serão considerados os valores dos atributos desta linha. Caso o usuário não se posicionou explicitamente em determinada linha, por default a linha que selecionada é a primeira linha, assim serão estes os valores dos atributos considerados.