Data Providers - Más conocimientos Introduciremos nuevos conocimientos sobre el uso de los Data Providers. Para ello propondremos una implementación práctica en nuestra aplicación. Supongamos que la agencia de viajes decide que todos los clientes que han contratado más de 3 excursiones, recibirán una tarjeta especial de tipo “Full Services”, que les permitirá disfrutar de todos los servicios en forma gratuita. Y en caso de haber contratado 3 o menos excursiones, recibirán la tarjeta de tipo “Partial Services”. Contamos entonces con la transacción “Customer” que permite registrar para cada cliente sus datos junto con la lista de excursiones realizadas: Page 1 Y la transacción “ServiceCard” que permite definir las tarjetas: Video filmado con GeneXus X Evolution 2 – upgrade2 Cada tarjeta tiene un identificador que se autonumera, un cliente… y hemos definido el atributo ServiceCardType basado en el dominio enumerado Type, que admite solamente los valores “Full” o “Partial”. Hemos definido también el web panel WPCards, que simplemente ofrece un botón que disparará el proceso automático de generación y visualización de nuevas tarjetas. ¿Qué deberá suceder al presionar el botón? Para todos aquellos clientes que aún no tengan tarjeta emitida, se les deberá crear una tarjeta del tipo que corresponda, teniendo en cuenta la cantidad de excursiones que han contratado. Propondremos una solución usando un Data Provider que cargue y devuelva la colección de tarjetas a ser generada, y luego recorreremos la colección y grabaremos las tarjetas en la base de datos. ¿Y cómo hacemos esto? En primer lugar, configuramos la transacción ServiceCard como Business Component para grabar las tarjetas haciendo uso del concepto de Business Component. Después, creamos un objeto de tipo Data Provider de nombre DPCards y arrastramos la transacción ServiceCard sobre el source de ese Data Provider. Video filmado con GeneXus X Evolution 2 – upgrade2 Page Una sintaxis idéntica a la que se genera cuando arrastramos un tipo de datos estructurado sobre el source de un Data Provider… y con esta práctica vemos que también podemos arrastrar una transacción que ha sido declarada como Business Component. 2 ¿Qué estamos viendo? En este caso particular, no hemos definido un SDT en la base de conocimiento, pero la sintaxis nos permite deducir que se cargará una estructura en memoria con ítems de igual nombre y tipo que los atributos de la transacción ServiceCard. Recordemos que estos no son atributos: sino los ítems de la estructura que se cargará en memoria. Bien. Hasta ahora la idea entonces, es que se cargarán en memoria tarjetas. ¿Pero a partir de dónde y con qué condiciones y particularidades? Necesitamos recorrer la tabla CUSTOMER, filtrar los clientes que aún no tienen una tarjeta emitida y para ellos agregar una tarjeta en la colección. La tabla base de un Data Provider, es decir la tabla que navegará, se determina por los atributos que referenciemos del lado derecho de los signos de asignación. Al ítem CustomerId, es claro que le vamos a asignar el atributo CustomerId. Al ítem ServiceCardId no le necesitamos asignar ningún valor específico, porque recordemos que esta estructura fue arrastrada de 1 transacción BC.. y por lo tanto este ítem está basado en el atributo ServiceCardId, que pertenece al dominio Id y su propiedad autonumber está configurada en Yes. Page Observemos cuales son los atributos que figuran del lado derecho de los signos de asignación. Solamente CustomerId, porque los atributos que están dentro de la fórmula, no se tienen en cuenta para la determinación de esta tabla base sino que determinan la tabla a ser navegada por la fórmula. 3 Ahora veamos que a ServiceCardType le podemos asignar el valor que retorne una fórmula condicional, es decir que la fórmula devolverá el tipo “Full” o “Partial” de acuerdo a la cantidad de excursiones que ha contratado el cliente: Video filmado con GeneXus X Evolution 2 – upgrade2 Así que la tabla navegada por el Data Provider es CUSTOMER. Pero no queremos navegar todos los clientes y para cada uno cargar una tarjeta, sino que queremos recorrer solamente aquellos clientes que aún no tienen tarjeta. Los Data Providers permiten en su sintaxis, que les incluyamos todas las cláusulas permitidas en For each, así que agregaremos esta cláusula Where : De esta forma entonces logramos navegar todos los clientes cuya cantidad de tarjetas es 0, o sea, que no tienen tarjetas. Pero observemos un poco más esta cláusula Where: Dado que el único atributo referenciado está dentro de una fórmula, entonces no participa en la determinación de la tabla base del Data Provider. De haberse evaluado directamente, entonces si hubiera participado. Bien. Observemos el Data Provider, y veamos que podemos simplificarlo un poco más. Cuando un ítem en la estructura de un Data Provider se carga con un atributo de igual nombre, entonces podemos abreviar la declaración de esta forma: Ahora analicemos la salida, el output, del Data Provider: Al haber arrastrado la transacción ServiceCard, automáticamente, la propiedad Output quedó asociada a ella. Video filmado con GeneXus X Evolution 2 – upgrade2 Page La estructura que estamos cargando no representa una colección. Solamente representa una instancia en memoria, con la estructura de 1 registro de la tabla asociada a la transacción ServiceCard. 4 ¿Y la propiedad Collection? Sin embargo nosotros necesitamos obtener una colección de tarjetas generadas, entonces configuramos la propiedad Collection con valor True… e indicamos un nombre para la colección que devolverá cargada este Data Provider: Volvamos ahora al web panel para invocar al Data Provider. En el evento Enter asociado al botón, declaramos una variable &ServiceCardCollection que GeneXus automáticamente define como una colección de ServiceCard al tener el término Collection en su nombre. El evento Enter entonces queda simplemente así: Event Enter &ServiceCardCollection=DPCards() End Event Vayamos ahora al form del web panel, e insertemos la variable &ServiceCard. Por tratarse de una variable colección, automáticamente GeneXus entiende que debe mostrar el contenido en un grid. Video filmado con GeneXus X Evolution 2 – upgrade2 Page No. Por ahora las tarjetas están cargadas en memoria y hemos mostrado el contenido de la colección. 5 Ahora bien, ¿esto alcanza para que las tarjetas devueltas por el Data Provider efectivamente se graben en la tabla SERVICECARD asociada a la transacción ServiceCard? Cuando estudiamos la utilización de transacciones como BC, vimos que para grabar debemos usar el método Save y luego ejecutar el comando Commit. Así que nos está faltando recorrer la colección devuelta por el Data Provider y proceder a grabar cada elemento de la colección como registro en la tabla física. Y posteriormente a la grabación de todas las tarjetas, vamos a declarar el comando Commit. Para recorrer entonces la colección de tarjetas devuelta por el Data Provider, contamos con el comando For elemento in Colección. Esta variable &oneCard debe ser definida. Representa a cada elemento que se va iterando de la colección. Vamos a definirla: Ahora volvamos al evento para completar el código que recorre la colección: Event Enter &ServiceCardCollection= DPCards() For &oneCard in &ServiceCardCollection &oneCard.Save() Endfor Commit End Event Ahora sí el desarrollo de lo solicitado está completo. Presionemos F5 para ver el comportamiento. Page 6 Ejecutamos el web panel. Presionamos el botón. Y vemos en la grilla la lista de tarjetas que se generaron. Video filmado con GeneXus X Evolution 2 – upgrade2 Vamos ahora a ejecutar la transacción Service Card para verificar que las tarjetas han sido realmente grabadas como registros. Ahora bien. Puede surgirnos la pregunta: ¿Qué sucederá si volvemos a presionar el botón “Generate Cards” en el web panel? ¿Se volverán a crear las tarjetas para los mismos clientes? No, porque en el Data Provider filtramos que solamente queríamos navegar aquellos clientes sin tarjeta. Vamos a comprobarlo: Presionamos otra vez el botón, y vemos que no se generaron tarjetas esta vez. Para finalizar, vale mencionar que hay otras soluciones para resolver este mismo requerimiento en GeneXus. En esta implementación hemos usado el concepto de Business Component para actualizar la base de datos, aprovechando los beneficios que los mismos nos proveen y hemos combinado su uso con el hecho de cargar previamente una estructura colección en memoria con los datos a grabar. Page 7 El uso de un Data Provider para esto, es muy sencillo y nos ahorra escribir código explícito. Video filmado con GeneXus X Evolution 2 – upgrade2