lunes, 27 de junio de 2011

Events Framework (Como Disminuir el Impacto de las Personalizaciones)

Una de las grandes desventajas de los procesos de “Customización” de los aplicativos PEOPLESOFT  es que se modifica el código estándar que viene con la aplicación,  lo cual deriva en un aumento considerable de la complejidad de los procesos de actualización del aplicativo, llegando hasta al punto de que las empresas optan por eliminarlos de su estrategia debido al costo que estos implicarían. 

Otra desventaja es que en algunos casos la modificación del código estándar entregado  por Oracle en dentro de sus aplicativos PEOPLESOFT lleva a la perdida de la garantía.

Una vez en la que estuve involucrado en un proceso de implantación de Oracle e-Business, me enteré de la existencia de algunas herramientas que hacen el proceso de “customización” de las aplicaciones de Oracle e-Business totalmente independiente del estándar, a continuación las enuncio (No se desesperen después de algunos renglones más entraremos en materia con PEOPLESOFT):

  • Oracle Alert: Esta utilidad permite crear acciones a partir eventos que se ejecuten contra la base de datos (Actualizaciones, inserts, deletes etc). ¿En qué se diferencia esto de crear un trigger en la base de datos?  En que Oracle cancela la garantía de sus productos cuando uno utiliza triggers en la base de datos. Adicionalmente toda la administración y el desarrollo de estos comportamientos se puede realizar directamente a través del aplicativo (Resulta que para esta característica PEOPLESOFT tiene su par).
  • Flexfields: Está característica permite a e-Business personalizar sus objetos del negocio sin la necesidad de modificar el estándar, por ejemplo si quieres agregar un nuevo campo a un proveedor, es solo configurar un flexfield y listo,  no tienes que hacer ningún desarrollo (Lo más cercano que he encontrado en people es la página de  personalización del componente de información de proveedores).
  • Personalizaciones: Cada Form de Oracle Ebusiness tiene una opción de personalización,  esta opción permite modificar la interacción entre el usuario y el aplicativo, agregando mensajes,  cálculo de campos,  errores, etc. (Nada parecido he visto en peoplesoft)
Luego de ver y utilizar estas herramientas busque dentro de PEOPLESOFT algunos equivalentes y encontré un Equivalente al Oracle Alert y es el Events and Notifications Framework.

En esta entrada  a nosotros nos atañe la parte de Events. Según el peoplebook Events es una característica que permite definir, implementar y ejecutar lógica del negocio para eventos del negocio (un Evento del Negocio es por ejemplo la creación de un comprobante).
Para que nos puede servir a nosotros está utilidad (No desesperen, más adelante les mostrare un ejemplo de la  utilización):
  • Podemos independizar el código que nosotros creamos del código estándar que viene con PEOPLESOFT. Algunas ventajas de este proceso serían:
o   Esto disminuirá el esfuerzo de los futuros upgrades.
o Volverá nuestro código transportable: cada vez que he hecho una customización en los proyectos la he perdido debido a lo difícil que se hace mudarla a las nuevas implementaciones debido a que incluye modificaciones a objetos estándar, y estos cambian de versión a versión etc. Con el Events Framework el código se encuentra en Applications Packages independientes que se pueden mover sin necesidad de transportar objetos estándar

  • Múltiples comportamientos para un mismo evento del negocio:

o   Podemos crear diferentes códigos o diferentes versiones del mismo código que se ejecuta para cada evento del negocio: por ejemplo creamos un código que al crear un cliente envíe una notificación al administrador de clientes de la empresa; además otro código que llame a un servicio web para crear el cliente en otro repositorio
o   Podemos administrar que códigos queremos que se ejecuten dependiendo de las necesidades de cada cliente: resulta que nuestro primer cliente desea que se llame el servicio web y que envíe la notificación, el segundo cliente solo quiere llamar al servicio web y el tercer cliente quiere el comportamiento estándar,  solo con activar o desactivar el código se pueden obtener estos 3 comportamientos.

  • El Events Framework permite monitorear y mantener detalladamente los eventos generados.

o   Se puede dejar un rastro de la ejecución de cada evento con información de su  ejecución.
o   Si un evento falla se puede reiniciar sin afectar el proceso estándar, por ejemplo si el llamado al web service que se hace al crear un cliente falla porque el servidor destino no estaba disponible en el momento,  podemos relanzar el evento desde el monitor cuando el servidor esté disponible.


Bueno ahora si manos a la obra, vamos a realizar un ejemplo de cómo generar, ejecutar, administrar y monitorear un Evento dentro de nuestro aplicativo PEOPLESOFT.

El evento que vamos a manipular va a ser la creación de un nuevo cliente dentro de PEOPLESOFT. Al momento de la creación de un cliente vamos a simular la creación del mismo cliente en 2 aplicativos externos a PEOPLESOFT. Esto podríamos hacerlo llamando 2 servicios web, uno para cada aplicativo que necesitemos, pero en este caso, con el fin de no salirnos del tema y de simplificar nuestro ejemplo lo aremos insertando la información del cliente en un registro para cada uno de los supuestos aplicativos.
 
  • El Primer paso es crear el evento que vamos a manipular en nuestro ejemplo: para eso vamos a Inicio – Componentes de Empresa – Eventos y Notificaciones – Entorno de Eventos – Registro de Eventos y añadimos un valor (Por Ejemplo “NUEVO_CLIENTE”).
Evento
NUEVO_CLIENTE
Descr
Creación de Clientes
Descripción
Se dispara al momento de crear un nuevo cliente en el aplicativo.
Conexión Activada
Verdadero (Esto activa el log de los eventos)


  • El segundo paso es crear los Gestores (El código que queremos que se ejecute al momento de crear un cliente). Un gestor es un Aplication Class, el cual se puede encontrar dentro de cualquier application package, pero debe incluirse dentro de un Subpackage llamado Handlers. A continuación podemos ver un ejemplo de cómo debe quedar la estructura:



  • Cada application Class que vayamos a usar como gestor de nuestro evento debe tener un un método llamado ProcessEvent que recibirá como parámetro un objeto de tipo EOEN_EVENT_MANAGER:Base:baseEvent.  Nuestra Application Class debe quedar de la siguiente forma: 

import EOEN_EVENT_MANAGER:Base:baseEvent;
import EOEN_EVENT_MANAGER:Base:Types:ExceptionType;

class GestorEvent1
   /** Dummy Constructor. */
   method GestorEvent1();
   /** Process Event. */
   method ProcessEvent(&inEvent As EOEN_EVENT_MANAGER:Base:baseEvent);
private
   method Trace(&inHeader As string, &inDetail As string);
  
   instance EOEN_EVENT_MANAGER:Base:baseEvent &myEvent;
  
end-class;

method GestorEvent1
end-method;

method ProcessEvent
   /+ &inEvent as EOEN_EVENT_MANAGER:Base:baseEvent +/
  
   Local Record &recContext, &recAplicacion1;
  
  
   /* the following lines are required only if you want to pass detail information
    about Errors, Warnings  or other messages back to the Event Framework */
   Local EOEN_EVENT_MANAGER:Base:Types:ExceptionType &myExceptionType = create EOEN_EVENT_MANAGER:Base:Types:ExceptionType();
  
      &myEvent = &inEvent;
  
   If Not &inEvent.HasContextRecord Then
      &myExceptionType.MESSAGE_SET_NBR = 18137;
      &myExceptionType.MESSAGE_NBR = 6509;
      &myExceptionType.MSG_SEVERITY = "E";
      &myExceptionType.MESSAGE_TEXT = MsgGetText(18137, 6509, "no message found", &inEvent.EventID, &inEvent.EventNode);
      &myExceptionType.EXPLAIN_TEXT = "";
      &inEvent.HandlerStatus.AddException(&myExceptionType);
   Else
      try
        
         &recContext = &inEvent.ContextRecord;
        
         &myExceptionType.MESSAGE_SET_NBR = 0;
         &myExceptionType.MESSAGE_NBR = 0;
         &myExceptionType.MSG_SEVERITY = "M";
         &myExceptionType.MESSAGE_TEXT = MsgGetText(0, 0, "SE ha insertado el cliente: %1", &recContext.CUST_ID.Value);
         &myExceptionType.EXPLAIN_TEXT = "";
         &inEvent.HandlerStatus.AddException(&myExceptionType);
        
         &recAplicacion1 = CreateRecord(Record.PE_APP1_TBL);
         &recContext.CopyFieldsTo(&recAplicacion1);
         &recAplicacion1.Insert();
        
        
         %This.Trace("ProcessEvent", "Se insertó el registro para el cliente: " | &recContext.CUSTID.Value);
        
        
      catch Exception &ex
         &myExceptionType.MESSAGE_SET_NBR = 0;
         &myExceptionType.MESSAGE_NBR = 0;
         &myExceptionType.MSG_SEVERITY = "E";
         &myExceptionType.MESSAGE_TEXT = MsgGetText(0, 0, "Al intentar insertar el cliente %1 se genero la excepción %2", &recContext.CUST_ID.Value, &ex.ToString());
         &myExceptionType.EXPLAIN_TEXT = "";
         &inEvent.HandlerStatus.AddException(&myExceptionType);
      end-try
   End-If;
end-method;

method Trace
   /+ &inHeader as String, +/
   /+ &inDetail as String +/
   If &myEvent.HandlerTraceEnabled Then
      &myEvent.HandlerStatus.AddTraceEntry(&inHeader, &inDetail);
   End-If;
end-method;

  • En el método ProcessEvent escribimos el código que deseamos que se ejecute al momento de llamar el Evento. El Objeto &inEvent de tipo baseEvent tiene una propiedad llamada “ContextRecord” que contiene el registro para el cual se está generando el evento. En nuestro ejemplo el ContextRecord se referirá al registro CUSTOMER que se está insertando dentro de nuestra base de datos.

  • Para poder realizar registros en el log, tanto de mensajes como de errores (Al registrar errores el Events Framework nos permitirá relanzar los eventos), el parámetro &inEvent posee el método AddExceptions que recibe como parámetro un objeto de tipo EOEN_EVENT_MANAGER:Base:Types:ExceptionType. Con este método podemos agregar mensajes de severidad “E” (errores), “M” (mensajes) y “W” (Advertencias). Si agregamos algún mensaje de tipo “E” el Event Framework automáticamente marcará el evento como finalizado con error.
  • La otra característica que posee el Event Framerwork es la activación del trace del proceso, para eso creamos el método Trace dentro de nuestro gestor.

  • El segundo gestor va a ser exactamente igual, lo único que vamos a cambiar es el registro en el que vamos a insertar la información.

  • Ahora tenemos que agregar los gestores al Evento que creamos previamente, en la mismo componente que se encuentra en la ruta a Inicio – Componentes de Empresa – Eventos y Notificaciones – Entorno de Eventos – Registro de Eventos buscamos el Evento NUEVO_CLIENTE y le agregamos los gestores para que quede de la siguiente forma:
 


  • Activamos para los gestores el registro de todos los mensajes para que no solo incluya los errores en el log sino todos los mensajes que vamos a generar.
  • Ahora vamos a hacer el llamado de nuestro evento, para esos debemos ir al registro CUSTOMER, ahí es donde debemos registrar el llamado a nuestro evento. Vamos al campo setid y al evento SavePostChange y agregamos el siguiente código:
 import EOEN_MVC:EOEN_MODEL:EOENInterface;

Local EOEN_MVC:EOEN_MODEL:EOENInterface &myEvent;
Local Record &recContext;

If GetRow().IsNew Then
   &myEvent = create EOEN_MVC:EOEN_MODEL:EOENInterface("", 0);
  
   &recContext = GetRecord();
   &myEvent.AddContextRecord(&recContext);
   &myEvent.RaiseEvent("NUEVO_CLIENTE");
End-If;

  • Y listo, con lo anterior ya nuestro sistema empieza a generar eventos cada vez que creemos un nuevo cliente. Además el evento va a quedar registrado en el Monitor de Eventos que se encuentra en Inicio – Componentes de Empresa – Eventos y Notificaciones – Entorno de Eventos - Monitor de Eventos. El monitor se verá de la siguiente forma cada vez que alguien ejecute el evento:



  • Si damos clic en gestores podemos ver el estado de nuestro evento.

  • Como se puede ver uno de nuestros gestores finalizó en error, esto sucedió porque inserte la siguiente línea antes de llamar el insert del record.  Esta es una de las mayores ventajas que veo de este framework, ya que si nos equivocamos a la hora de escribir el gestor, y en algún momento se genera una excepción dentro del éste, tenemos la capacidad de ejecutar nuevamente el gestor (Opción Reejecutar) luego de corregir el código.

insert throw CreateException(0, 0, "El servicio no se encuentra disponible en el momento");

  • Eliminamos la línea de código que se agregó para generar el error y reejecutamos el gestor.  Luego podemos ver que ya se finalizó correctamente la ejecución.  Todo esto sin la necesidad de detener los procesos de los usuarios.


Bueno algunas consideraciones finales:
  • El integration Broker debe estar activo  para poder utilizar este framework.
  • Se debe verificar que la cola EOEN_MSG_CHNL y las operaciones de servicio asociadas se encuentren activas y en ejecución

Listo eso es todo en esta entrada, espero que les haya parecido interesante y que les sea de utilidad en sus desarrollos.

Para nuestra próxima entrada veremos una forma de hacer notificaciones disminuyendo al máximo el componente técnico.

miércoles, 22 de junio de 2011

Lista de Selección Múltiple en PEOPLESOFT

En los proyectos en los que he trabajado desarrollando personalizaciones para PEOPLESOFT siempre me he encontrado con el siguiente requerimiento:
  • La página de parámetros del reporte a desarrollar debe permitir para el campo X (generalmente un campo XLAT) la selección de uno, todos o múltiples valores.
Como desarrollador asumí que esté tipo de comportamiento es nativo a PEOPLESOFT ya que se puede encontrar en las distintas páginas de búsqueda que genera el aplicativo automáticamente. En el siguiente ejemplo se puede ver el comportamiento citado:


 

Dentro de PEOPLESOFT no existe una forma simple y automática de replicar esté comportamiento, según algunos foros en internet la forma de realizarlo es a través de la utilización del iScrip

A continuación expongo un ejemplo de como realizar una lista de selección multiple dentro de una página de PEOPLESOFT

Lo primero que se debe hacer es crear un record para almacenar la información que se seleccionará. Por ejemplo para la imagen anterior se deberían almacenar los valores para "Comprobante Reclam", "Pago Único" y "Reversión" lo cuales fueron seleccionados por el usuario. El registro se va a llamar Q_MULLIST_TBL y es de tipo table y debe tener los campos que se muestran en la figura. Este registro puede servir para almacenar todos los datos de todas la listas de selección que se utilicen dentro de PEOPLESOFT.

 


 



Generalmente la ejecución de los reportes se hace por control de ejecución y por id de usuario, por esa razón este registro tiene esos 2 campos.

El campo FIELDNAME es para indicar a que campo (de tipo XLAT) pertenecen esos registros.

El campo PARAMETERNAME es un identificador por si es necesario introducir dentro de un mismo control de ejecución múltiples listas de selección para el mismo campo.

Y por último el campo VALUE50_FLD1 es el campo donde se almacenará el valor seleccionado.

Cada vez que se quiere introducir código iScript dentro de una página Peoplesoft se debe tener un campo de tipo HTML Area, este campo permite generar código HTML independiente del COMPONENT BUFFER.

Para lo anterior se debe crear otro record, esta vez de tipo DERIVED/WORK para introducir un campo de tipo HTML Area.

Adicionalmente este record contendrá el campo TREECTLEVENT el cual tendrá como función controlar los eventos que se ejecutarán cuando se modifiquen los datos que se incluyan dentro del campo HTML Area (En nuestro caso cuando se marquen o desmarquen valores dentro de nuestra lista.)

El record se va a llamar Q_HTML_WRK y debe quedar como se ven en la figura:


 


A continuación se describen las funciones que necesarias para generar nuestra lista de selección múltiple. Estas funciones se deben escribir en el evento FIELDFORMULA del campo HTMLAREA del record Q_HTML_WRK

La primera función es la función que permite pintar, cargar los valores seleccionados y actualizar los valores de nuestra lista.
 

Function getHTMLMultiSelect(&str_oprid As string, &str_run_cntl_id As string, &str_recordname As string, &str_fieldname As string, &str_parametername As string, &str_pagename As string, &str_cntrlfield As string, &bol_Init As boolean)

   Local string &str_html;
   Local Field &fld_campo;
   Local string &str_field;
   Local Rowset &rs_Xlat, &rs_selected;
   Local Record &rec_field;
   Local number &nbr_i;
   Local array of string &arr_selected;
  
   &str_html = "<select name='" | &str_parametername | "' onchange=""javascript:submitAction_win0(document.win0,'#ICSetField" | &str_pagename | "." | &str_cntrlfield | ".X1');"" multiple= 'true'>";

   &str_field = &str_recordname | "." | &str_fieldname;
   &fld_campo = GetField(@(&str_field));

   If &fld_campo.IsEditXlat Then
     
      &arr_selected = CreateArrayRept("", 0);
     
     
      If &bol_Init Then
        
         &rs_selected = CreateRowset(Record.Q_MULLIST_TBL);
         &rs_selected.Fill("WHERE FILL.OPRID = :1 AND FILL.RUN_CNTL_ID = :2 AND FILL.FIELDNAME = :3 AND FILL.PARAMETERNAME = :4", &str_oprid, &str_run_cntl_id, &str_fieldname, &str_parametername);
        
         For &nbr_i = 1 To &rs_selected.ActiveRowCount
            &arr_selected.Push(&rs_selected(&nbr_i).GetRecord(1).GetField(Field.VALUE50_FLD1).Value);
         End-For;
      Else
         &arr_selected = %Request.GetParameterValues(&str_parametername);
      End-If;

      &rs_Xlat = CreateRowset(Record.PSXLATITEM);
      &rs_Xlat.Fill("WHERE FILL.FIELDNAME = :1 and EFFDT = (select max(EFFDT) from PSXLATITEM B where B.FIELDNAME = :1 and FILL.FIELDVALUE = B.FIELDVALUE and EFFDT <= %CURRENTDATEIN)", &str_fieldname);

      &nbr_i = 1;
      For &nbr_i = 1 To &rs_Xlat.ActiveRowCount
         &fld_campo.Value = &rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value;
         If &arr_selected.Find(&rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value) <> 0 Then
            &str_html = &str_html | "<option value='" | &rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value | "' selected='true'>" | &fld_campo.LongTranslateValue | "</option>"
         Else
            &str_html = &str_html | "<option value='" | &rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value | "' >" | &fld_campo.LongTranslateValue | "</option>"
         End-If;
        
      End-For;
      &str_html = &str_html | "</select>";
      Q_HTML_WRK.HTMLAREA.Value = &str_html;
     
      If Not &bol_Init Then
         Q_HTML_WRK.TREECTLEVENT.Value = "";
      End-If;
     
     
   End-If;
End-Function;



A continuación se describen los parámetros de la función:  
  • &str_oprid: Usuario que está creando los datos
  • &str_run_cntl_id: Control de ejecución que se está creando
  • &str_recordname: Registro en el que se encuentra el campo XLAT para el cual se va a almacenar la información.
  • &str_fieldname: Campo XLAT para el que se va a almacenar la información
  • &str_parametername: Identificador que se le va a dar al campo
  • &str_pagename: nombre de la página que está ejecutando el control
  • &str_cntrlfield: es el campo de control, generalmente es TREECTLEVENT que es el campo que se utiliza para controlar los eventos que ocurrirán en el HTML Area
  • &bol_Init: Indica si se está inicializando la lista (Rowinit) o si se está modificando (fieldchange)

     

    El objetivo del fragmento que presentando a continuación es crear el código html que va a marcar el campo TREECTLEVENT para ejecutar el evento FIELDCHANGE cuando se dé clic dentro de la lista, la cual está representanda por el comando “<select” el cual indica que se está creando una lista, y el parámetro “multiple=’true’” indica que se pueden seleccionar múltiples valores dentro de ésta. 

    La función submitAction_win0 es una función javascript que provee peoplesoft, y  que permite marcar un control para la ejecución del evento FIELDCHANGE
    &str_html = "<select name='" | &str_parametername | "' onchange=""javascript:submitAction_win0(document.win0,'#ICSetField" | &str_pagename | "." | &str_cntrlfield | ".X1');"" multiple= 'true'>";



    El siguiente fragmento de código verifica si el campo que se está usando es XLAT de no ser así no hará nada. 


       &str_field = &str_recordname | "." | &str_fieldname;
       &fld_campo = GetField(@(&str_field));

       If &fld_campo.IsEditXlat Then

    El código presentando a continuación verifica los valores seleccionados dentro de la lista. Si se está iniciando la lista, se verifican los valores que están guardados en la base de datos, de lo contrario sí se está ejecutando en el FIELDCHANGE se verifican los valores que se encuentran actualmente seleccionados en la lista. En este punto es que entra jugar el iScript, la variable del sistema %Request permite acceder directamente al html, usando esta variable podemos obtener los valores seleccionados actualmente en la página, utilizando la función GetParameterValues obtenemos los valores marcados por el usuario. 


          &arr_selected = CreateArrayRept("", 0);
         
         
          If &bol_Init Then
            
             &rs_selected = CreateRowset(Record.Q_MULLIST_TBL);
             &rs_selected.Fill("WHERE FILL.OPRID = :1 AND FILL.RUN_CNTL_ID = :2 AND FILL.FIELDNAME = :3 AND FILL.PARAMETERNAME = :4", &str_oprid, &str_run_cntl_id, &str_fieldname, &str_parametername);
            
             For &nbr_i = 1 To &rs_selected.ActiveRowCount
                &arr_selected.Push(&rs_selected(&nbr_i).GetRecord(1).GetField(Field.VALUE50_FLD1).Value);
             End-For;
          Else
             &arr_selected = %Request.GetParameterValues(&str_parametername);
          End-If;


    En el siguiente paso se recorren los valores activos del XLAT, escribirlos dentro de la lista usando la opción "<option" y asignar este fragmento de HTML al campo HTMLAREA para que sea visualizado dentro de la página.



          &rs_Xlat = CreateRowset(Record.PSXLATITEM);
          &rs_Xlat.Fill("WHERE FILL.FIELDNAME = :1 and EFFDT = (select max(EFFDT) from PSXLATITEM B where B.FIELDNAME = :1 and FILL.FIELDVALUE = B.FIELDVALUE and EFFDT <= %CURRENTDATEIN)", &str_fieldname);

          &nbr_i = 1;
          For &nbr_i = 1 To &rs_Xlat.ActiveRowCount
             &fld_campo.Value = &rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value;
             If &arr_selected.Find(&rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value) <> 0 Then
                &str_html = &str_html | "<option value='" | &rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value | "' selected='true'>" | &fld_campo.LongTranslateValue | "</option>"
             Else
                &str_html = &str_html | "<option value='" | &rs_Xlat(&nbr_i).GetRecord(1).GetField(Field.FIELDVALUE).Value | "' >" | &fld_campo.LongTranslateValue | "</option>"
             End-If;
          &str_html = &str_html | "</select>";
          Q_HTML_WRK.HTMLAREA.Value = &str_html;




    Por último se reinicia el valor del campo TREECTLEVENT.

    La otra función que se debe desarrollar es la que permite guardar los datos seleccionados a la base de datos


    Function saveHTMLMultiSelect(&str_oprid As string, &str_run_cntl_id As string, &str_fieldname As string, &str_parametername As string)
       Local string &str_html;
       Local Field &fld_campo;
       Local string &str_field;
       Local Rowset &rs_Xlat, &rs_selected;
       Local number &nbr_i;
      
       Local array of string &arr_selected;
       Local Record &rec_tcf_mullist_tbl;
      
       SQLExec("DELETE FROM PS_TCF_MULLIST_TBL WHERE OPRID = :1 AND RUN_CNTL_ID = :2 AND FIELDNAME = :3 AND PARAMETERNAME = :4", &str_oprid, &str_run_cntl_id, &str_fieldname, &str_parametername);
       &arr_selected = %Request.GetParameterValues(&str_parametername);
      
       For &nbr_i = 1 To &arr_selected.Len
          &rec_tcf_mullist_tbl = CreateRecord(Record.TCF_MULLIST_TBL);
          &rec_tcf_mullist_tbl.OPRID.Value = &str_oprid;
          &rec_tcf_mullist_tbl.RUN_CNTL_ID.Value = &str_run_cntl_id;
          &rec_tcf_mullist_tbl.FIELDNAME.Value = &str_fieldname;
          &rec_tcf_mullist_tbl.PARAMETERNAME.Value = &str_parametername;
          &rec_tcf_mullist_tbl.VALUE50_FLD1.Value = &arr_selected [&nbr_i];
         
          &rec_tcf_mullist_tbl.Insert();
         
       End-For;
    End-Function;

    Primero que todo se elimina la información que existe actualmente en la base de datos:



       SQLExec("DELETE FROM PS_TCF_MULLIST_TBL WHERE OPRID = :1 AND RUN_CNTL_ID = :2 AND FIELDNAME = :3 AND PARAMETERNAME = :4", &str_oprid, &str_run_cntl_id, &str_fieldname, &str_parametername);

    Obtenemos la información actualmente seleccionada en la lista, utilizando el objeto iScript %Request:



        &arr_selected = %Request.GetParameterValues(&str_parametername);


    Insertamos los valores seleccionados a la base de datos:



       For &nbr_i = 1 To &arr_selected.Len
          &rec_tcf_mullist_tbl = CreateRecord(Record.TCF_MULLIST_TBL);
          &rec_tcf_mullist_tbl.OPRID.Value = &str_oprid;
          &rec_tcf_mullist_tbl.RUN_CNTL_ID.Value = &str_run_cntl_id;
          &rec_tcf_mullist_tbl.FIELDNAME.Value = &str_fieldname;
          &rec_tcf_mullist_tbl.PARAMETERNAME.Value = &str_parametername;
          &rec_tcf_mullist_tbl.VALUE50_FLD1.Value = &arr_selected [&nbr_i];
         
          &rec_tcf_mullist_tbl.Insert();
         
       End-For;




    Hasta este momento se han creado los objetos que se van a reutilizar en cada una de las páginas donde se haga necesario incluir la lista.

    Los pasos para crear una página que use este control son los siguientes:

    • crear el registro del control de ejecución del reporte para el cual se va a utilizar la página. El registro tiene como mínimo OPRID y RUN_CNTL_ID las cuales deben ser llaves y llaves de búsqueda. El registro que se va a utilizar en el ejemplo se llama Q_PRB_LISTA_TBL
    • crear la página en la cual se va a mostrar la lista de selección. 
    • Agregar a la página la Subpagina PRCSRUNCNTL_SBP y configurar el campo “To” para que apunte al registro Q_PRB_LISTA_TBL
    • Crear un registro de tipo derived que contenga el campo para el cual se hará la lista de selección múltiple. El campo puede ser cualquiera que tenga definido un XLAT, por ejemplo el campo ACCESS_LEVEL. En este caso se utilizará un campo llamado Q_PRUEBA_LISTA y el recrod se llamará Q_PRB_LISTA_WRK
    • Colocar  el campo en la página donde va a ser utilizado el control, debe marcarse como Display Only e Invisible
    • Arrastrar el campo HTMLAREA del record Q_HTML_WRK a la página
    • Arrastrar el campo TREECTLEVENT del record Q_HTML_WRK a la página y lo marcarlo como invisible y Modifiable by Javascript
    • La página debe quedar como se ve en la figura:


     

    • En el evento rowinit del Q_PRB_LISTA_WRK.Q_PRUEBA_LISTA agregar el llamado a la función para inicializar la lista de selección: 
    Declare Function getHTMLMultiSelect PeopleCode Q_HTML_WRK.HTMLAREA FieldFormula;

    getHTMLMultiSelect(Q_PRB_LISTA_TBL.OPRID.Value, Q_PRB_LISTA_TBL.RUN_CNTL_ID.Value, "Q_PRB_LISTA_WRK", "Q_PRUEBA_LISTA", "VALORES", "Q_PRB_LISTA_PAGE", "TREECTLEVENT", True);
    • En el evento SavePostChange del Q_PRB_LISTA_WRK.Q_PRUEBA_LISTA agregar el llamado a la función para guardar los datos de la lista de selección:  
    Declare Function SAVEHTMLMultiSelect PeopleCode Q_HTML_WRK.HTMLAREA FieldFormula;
    SAVEHTMLMultiSelect(Q_PRB_LISTA_TBL.OPRID.Value, Q_PRB_LISTA_TBL.RUN_CNTL_ID.Value, "Q_PRUEBA_LISTA", "VALORES");
    • Crear el Componente, el Menú, registrarlos en el portal y le darles seguridad para poder accederlos.
    • Por último agregar el siguiente código al campo TREECTLEVENT del record Q_HTML_WRK a nivel de componente:
     
    Declare Function getHTMLMultiSelect PeopleCode Q_HTML_WRK.HTMLAREA FieldFormula;
    getHTMLMultiSelect(Q_PRB_LISTA_TBL.OPRID.Value, Q_PRB_LISTA_TBL.RUN_CNTL_ID.Value, "Q_PRB_LISTA_WRK", "Q_PRUEBA_LISTA", "VALORES", "Q_PRB_LISTA_PAGE", "TREECTLEVENT", False);

    • Y listo:


     

    Bueno hasta acá llega la primera entrada de este blog,  espero que está información les sea de mucha utilidad en sus proyectos.  
    Cualquier comentario, sugerencia, duda, pregunta, etc., es bienvenida. Esperen la proxima semana una nueva entrada que tratará sobre una herramienta introducida en PEOPLESOFT 9.0 que permite realizar "Customizaciones" dentro de las aplicaciones disminuyendo el impacto de las mismas y su administración.