Documentos de Académico
Documentos de Profesional
Documentos de Cultura
134
Cliente
Categoria
CategoriaId* CategoriaDescuento
Envio
EnvioFecha* EnvioCargo
Producto
Reglas:
Add( FacturaTotal, ClienteTotalCompras); Error( Stock insuficiente ) if ProductoStock<0; Subtract( FacturaLineaCantidad, ProductoStock);
La forma de programar el comportamiento de las transacciones es definiendo reglas, las cuales se escriben de forma declarativa. A su vez si hay clculos para efectuar, se puede optar por la alternativa de definir atributos frmula. El programador GeneXus en ningn momento especifica la secuencia de ejecucin de las reglas y frmulas definidas en una transaccin, sin embargo al momento de generar, GeneXus determina las dependencias existentes entre las reglas y frmulas definidas. Supongamos que estamos definiendo una aplicacin para una empresa que vende determinados Productoos, y que cuenta con un servicio de entrega a domicilio que lleva la mercadera a sus clientes. Y definimos entre otras, las siguientes 5 transacciones: "Cliente" (para registrar los clientes de la empresa) Categoria (a las que pertenece cada cliente) Envio (envos: guarda un histrico de costos de envo) "Factura" (facturas que se emiten a los clientes) "Producto" (Productoos vendidos por la empresa) Se resalta la estructura de la transaccin "Factura", con sus atributos frmulas (algunas horizontales, otras verticales, otras aggregate/select) y sus reglas declaradas. En qu orden se dispararn las reglas y frmulas de la transaccin "Factura"?
135
rbol de evaluacin
R. Add(FacturaTotal, ClienteTotalCompras); F. FacturaTotal = FacturaSubTotal - FacturaDescuento + FacturaEnvioCargo F. FacturaDescuento = FacturaSubTotal*CategoriaDescuento F. FacturaEnvioCargo = MAX( EnvioFecha, EnvioFecha <= FacturaFecha,,EnvioCargo) F. FacturaSubTotal = SUM( FacturaLineaImporte ) F. FacturaLineaImporte = FacturaLineaCantidad *ProductoPrecio R. Subtract(FacturaLineaCantidad, ProductoStock) ; R. Error( Stock insuficiente) if ProductoStock < 0 ;
ClienteTotalCompras
FacturaTotal
FacturaDescuento FacturaEnvioCargo
error (Stock insuficiente )
FacturaSubTotal
EnvioFecha
FacturaFecha EnvioCargo
CategoriaDescuento
ProductoPrecio
Al momento de generar el programa asociado a la transaccin "Factura", GeneXus extraer las dependencias existentes entre las reglas y frmulas definidas; construir lgicamente un rbol de dependencias (o rbol de evaluacin) que determinar la secuencia de evaluacin. Podemos imaginar que el rbol se ejecuta de abajo hacia arriba, es decir que cada vez que cambia el valor de un atributo, se ejecutan todas las reglas y frmulas que dependen de ese atributo (y que en el rbol se encuentran hacia arriba). Por ejemplo, si cambia la cantidad de una lnea de una factura (FacturaLineaCantidad), como este atributo interviene en la frmula que calcula el importe de la lnea (FacturaLineaImporte), dicha frmula se redisparar. Por cambiar el importe de una lnea, deber redispararse la frmula correspondiente al subtotal de la factura (FacturaSubTotal) y en consecuencia, tambin deber recalcularse la frmula correspondiente al descuento (FacturaDescuento), ya que depende del subtotal. Deber redispararse tambin la frmula correspondiente al total de la factura (FacturaTotal) ya que depende tanto del valor de FacturaSubTotal como del valor de FacturaDescuento. Por ltimo, por cambiar el total tambin se tendr que disparar la regla Add(FacturaTotal, ClienteTotalCompras);. Adems de dispararse todas las frmulas y reglas involucradas en la rama derecha del rbol desde el atributo FacturaLineaCantidad, tambin se dispararn las frmulas y reglas involucradas en la rama izquierda. Es decir, que al cambiar el valor del atributo FacturaLineaCantidad, se redisparar tambin la regla Subtract(FacturaLineaCantidad, ProductoStock); y en consecuencia, por modificar esta regla el valor del atributo ProductoStock, se evaluar si habr que disparar la regla Error(Stock insuficiente) if ProductoStock < 0; Concluyendo, las reglas y frmulas que se definen en una transaccin suelen estar interrelacionadas y GeneXus determina las dependencias entre ellas as como su orden de evaluacin. Observemos las 2 ltimas reglas definidas: Subtract(FacturaLineaCantidad, ProductoStock); Error(Stock insuficiente) if ProductoStock < 0;
136
Estas reglas estn interrelacionadas porque las dos involucran al atributo ProductoStock. Ahora, mientras la segunda solamente consulta su valor, la primera lo actualiza. Entonces, la regla que actualiza al atributo ser la que se disparar primero, y luego se disparar la que lo consulta. Toda regla que actualice el valor de un atributo se disparar antes que una regla que lo consulte (esto se puede observar claramente en el rbol). Por este motivo es que la regla Error consulta si el atributo ProductoStock qued con valor negativo; porque como dijimos la sustraccin se realizar primero. En la programacin clsica se suele consultar primero si alcanza el stock, y en caso de que sea suficiente recin se hace la sustraccin. Por eso quienes estn aprendiendo GeneXus pueden intuitivamente escribir la regla: Error(Stock insuficiente ') if FacturaLineaCantidad > ProductoStock. Esta sintaxis es correcta, sin embargo no es correcta su lgica ya que como venimos explicando, en el rbol de evaluacin determinado por GeneXus primero se disparar la regla Subtract y luego la regla Error; por lo tanto tendremos que especificar que se dispare el mensaje de error si es que qued el stock con valor negativo, dado que ya se habr ejecutado la sustraccin al momento de consultar el valor de ProductoStock. As que la regla que se debe definir es: Error(Stock insuficiente) if ProductoStock < 0; Y no: Error(Stock insuficiente ') if FacturaLineaCantidad > ProductoStock; Cuando se dispara una regla Error, se detiene cualquier actualizacin a la base de datos y se desarma el rbol de evaluacin, quedando todo en el estado anterior a producirse el error. Siguiendo el ejemplo que venamos viendo, si al dispararse la regla Subtract el stock quedara negativo, se disparara la regla Error. Como consecuencia de dispararse la regla Error, se deshara el Subtract que se haba ejecutado, as como todas las dems reglas y frmulas que se hayan ejecutado (reclculo de los atributos FacturaLineaImporte, FacturaSubTotal, ...., ClienteTotalCompras). Cmo podemos ver el orden de evaluacin determinado por GeneXus? Al especificar una o varias transacciones, seleccionando la opcin Detailed Navigation, se detallar en el listado de navegacin resultante el orden de ejecucin de todas las reglas y frmulas definidas en la transaccin.
137
Error Total
ProveedorId* Calculado Ingresado FacturaId* ... FacturaEntTotal Total ingresado FacturaLineaImporte ( ProductoId* FacturaLineaCantidad FacturaLineaPrecio FacturaLineaImporte = FacturaLineaPrecio * FacturaLineaCantidad) ... FacturaCalcTotal = SUM(FacturaLineaImporte) Total calculado
Total
Error(El total ingresado no coincide con el total calculado') if (FacturaEntTotal<>FacturaCalcTotal) On AfterLevel Level ProductoId;
En la mayora de los casos el orden de ejecucin de las reglas definido por GeneXus a partir de nuestras especificaciones es el deseado. Pero en algunos casos podemos querer cambiar el momento de disparo de una regla. Ejemplo: Definimos una transaccin para registrar las facturas que nos entregan nuestros proveedores. El identificador del primer nivel es compuesto por el identificador de proveedor y el identificador de factura, ya que el nmero de factura no nos sirve como identificador nico, porque proveedores distintos pueden repetir el mismo nmero de factura. Para cada factura de un proveedor que se ingrese, nos interesa controlar que el total que venga escrito en la factura (y que se ingresar en el atributo FacturaEntTotal) sea correcto. Para hacer este control, definimos al atributo FacturaCalcTotal como frmula vertical SUM(FacturaLineaImporte), y agregamos una regla Error que se disparar si no coinciden los valores de los atributos FacturaEntTotal y FacturaCalcTotal: Error('El total ingresado no coincide con el total calculado') FacturaEntTotal; if FacturaCalcTotal <>
Si construimos el rbol de evaluacin correspondiente a las frmulas y regla que hemos definido en esta transaccin:
138
vemos que las dependencias indican que cada vez que se agreguen, modifiquen o eliminen valores de los atributos FacturaLineaPrecio e FacturaLineaCantidad en las lneas, se recalcular el valor del atributo FacturaLineaImporte correspondiente; en consecuencia, se recalcular el valor del atributo frmula FacturaCalcTotal que hemos definido para tener el total calculado de la factura; y como este atributo est involucrado en la condicin de disparo de la regla Error, si se cumple dicha condicin de disparo, se disparar la regla Error(El total ingresado no coincide con el total calculado) if FacturaCalcTotal <> FacturaEntTotal. Ahora, prestemos atencin a que la condicin de disparo FacturaCalcTotal <> FacturaEntTotal se va a cumplir repetidamente en la medida que el operador vaya ingresando lneas, porque para cada lnea que se ingrese se calcular el valor del atributo frmula FacturaLineaImporte de la lnea, y en consecuencia se recalcular el valor del atributo frmula FacturaCalcTotal. Pero el valor calculado de este atributo no coincidir con el valor ingresado en el atributo FacturaEntTotal hasta que no se hayan ingresado todas las lneas de la factura; entonces, se disparar la regla Error(El total ingresado no coincide con el total calculado) if FacturaCalcTotal <> FacturaEntTotal;. Concluimos entonces que en este caso no nos sirve lo que determina el rbol de evaluacin, ya que no queremos que se evale la condicin de disparo de la regla Error cada vez que el operador ingrese, modifique o elimine lneas, sino que recin necesitamos que se evale cuando el usuario haya terminado de trabajar con todas las lneas de la factura. GeneXus ofrece eventos o momentos de disparo en las transacciones, que ocurren antes o despus de determinada accin, como la grabacin del cabezal, o de una lnea. Las reglas de las transacciones pueden condicionarse de manera tal de dispararse en el preciso instante en que ocurre alguno de esos eventos de disparo. Siguiendo el ejemplo que venamos viendo, existe un evento de disparo que ocurre luego de iterar en un nivel y salir del mismo. La sintaxis de este evento de disparo es: AfterLevel Level Atributo, debiendo ser Atributo un atributo perteneciente al nivel que se ha iterado y se abandona. De modo que a la regla Error de nuestro ejemplo, le agregaramos este evento de disparo, y quedara definida de la siguiente forma: Error(El total ingresado no coincide con el total calculado) if FacturaCalcTotal<>FacturaEntTotal On AfterLevel Level FacturaLineaPrecio; Con este evento de disparo que hemos agregado a la regla logramos controlar lo que desebamos en el momento adecuado. Adems de este evento de disparo, existen otros que veremos a continuacin.
139
Eventos de disparo