GrialApl, Transacción Visual

De GrialWIKI


Contenido

Control Principal: GrialCont / GrialApl

El Control GrialCont representa visualmente una transacción, estandarizando el flujo del programa en todos los módulos del sistema. Adicionalmente brinda el servicio de seguridad, conexión y acceso a los controles registrados.

Control GrialCont:


El control determina un flujo de programa estandarizado:

Codificación Estándar: Inicialización, Centrado y Finalización

Componentes Adicionales Requeridos

Dentro de cada GrialCont debe colocarse un objeto "Frame" de VB con el nombre [Container_Frm], dicho frame contendrá todos los controles de edición involucrados en la transacción.

Al iniciarse una transacción, se hace visible el frame "Container_Frm" (y se dispara el evento ContainerFrmShow), permitiendo la operación del usuario.

Al finalizar la transacción, se oculta dicho frame (y se dispara el evento ContainerFrmHide).

Si al iniciar una transacción, durante el evento ButtonClick, se establece el estado de sólo visualización (NewState=VIEW_MODE), se mostrará el Container_Frm pero deshabilitado.

En caso de tener más de un objeto GrialCont dentro de un mismo módulo, debe crearse un Container_Frm para cada uno, generando así un array de frames Container_Frm(0..n), uno dentro de cada uno de los objetos GrialCont.

Método Initialize

Se debe invocar el método GrialCont.Initialize en el evento UserDocument_Show (primer evento una vez mostrada la pantalla del módulo).

Formato:

  Sub GrialCont.Initialize ( UserDocument_Name As String, Parent_LocationURL As String )

El método Initialize:

  • Establece los accesos de seguridad según el usuario
  • Prepara la conexión para el acceso a datos
  • Prepara la configuración de los controles registrados

En el mismo momento, se deben dejar visibles sólo aquellos botones de la barra superior contemplados en el código.

Ejemplo:

Private Sub UserDocument_Show ( . . . 
 
With GrialCont
    .Initialize UserDocument.Name, Parent.LocationUrl
    .CheckVersion App
    .TopBar_AllButtonsVisible False  ' Todos invisibles
    'Habilito sólo Alta, Baja y Modificación
    .ButtonStd_Visible BUTTON_MODIFICATION, True
    .ButtonStd_Visible BUTTON_NEW, True
    .ButtonStd_Visible BUTTON_DELETE, True
End With

También es posible cambiar la descripción de cualquiera de los botones.

Ejemplo:

   .ButtonStd_Caption BUTTON_MODIFICATION, "Seleccionar"
   .ButtonStd_Caption BUTTON_CANCEL, "Cerrar"

Método Center

Se debe invocar este método en el evento UserDocument_Resize para centrar el objeto que contenga al resto de los objetos dentro de la pantalla.

Formato:

Sub GrialCont.Center ( xViewportWidth As Integer, GeneralContainer As Object )

Ejemplo:

GrialCont.Center ViewportWidth, General_GFF
GrialCont.Center ViewportWidth, TabGeneral

En el parámetro [GeneralContainer As Object] se indica el objeto que contiene al resto de los objetos de la función, por ejemplo cuando la aplicación presenta inicialmente varios tabs con un contenedor distinto en cada uno de ellos, será el objeto "Tab" el que contenga toda la aplicación.

Método Finalize

Debe ser invocado en el evento UserDocument_Hide (último evento antes de cerrar el formulario). Realiza descargas de los objetos cargados. Su ausencia puede provocar fallas en la salida del módulo.

Formato:

Sub GrialCont.Finalize()

Ejemplo:

Sub UserDocument_Hide
    GrialCont.Finalize
End Sub

El método Finalize desconecta los controles registrados para evitar errores de protección general (GPF) en el cierre del documento.

Flujo de Programas, de ButtonClick a BottomButtonClick

El flujo del programa estará determinado por los controles GrialApl incluidos en el módulo.

Se presenta a continuación un ejemplo de construcción y codificación de una transacción modelo dentro de un contenedor (GrialApl).

La transacción modelo consiste de un GrialCombo de selección colocado sobre la barra superior de un control GrialApl. Dentro de la zona de edición se colocan los controles necesarios para modificar el conjunto de datos representado por el combo.


Variables Globales

Se crean variables globales para almacenar: el ID actual en edición (por ejemplo: "IDPreEjec20", para llevar los contadores de falsos ID's para las tablas de detalles (Por Ejemplo: "Falso_IDPreEjec21") y objetos GrialQuery como espacio de memoria para editar los regsitros (recordsets troncales)

Ejemplo:

Dim IDPreEjec20 as Long
Dim PreEjec20_grq as New GrialQuery
Dim Falso_IDPreEjec21 as Long
Dim PreEjec21_grq as New GrialQuery

Inicio de la Transacción

Evento ButtonClick

El evento ButtonClick determina el inicio de la transacción. En base al botón pulsado por el usuario (Alta, Baja, Modificación, etc.) se prepararán los datos correspondientes para la transacción.

Formato:

Event ButtonClick ( GrialButtonCode As GrialButtons, Cancel As Integer
                       , NewState As Operation_States )

El usuario selecciona el registro sobre el cual desea operar en el combo, y luego pulsa uno de los botones de la barra superior (Alta, Modificación o Baja) lo que dispara un evento GrialCont_ButtonClick. En este evento se debe verificar que el usuario haya seleccionado correctamente un registro (para los casos de Modificación y Baja) y luego invocar un procedimiento para preparar la transacción (Sub PrepararTransaccion)

La variable global con el ID en edición se cargará al momento del evento GrialCont_ButtonClick con el ID del registro seleccionado en el combo (propiedad CurrentValue del combo). En caso de Alta de un nuevo registro, se utilizará el valor -1 como ID.

Private Sub GrialCont_ButtonClick(ByVal GrialButtonCode As GrialApp.GrialButtons, 
                    Cancel As Integer, NewState As GrialApp.Operation_States)
On Error GoTo ErrH
 
    Select Case GrialButtonCode
 
        Case BUTTON_NEW
            PreEjec20_scb.PrepareAddNew 'Abro un espacio nuevo en el Combo de seleccion
            IDPreEjec20 = -1
            PrepararTransaccion
 
        Case BUTTON_MODIFICATION,BUTTON_DELETE
            If NoDataIn(PreEjec20_scb.CurrentValue) Then Err.Raise 5,,"Debe elegir un registro"
            IDPreEjec20 = ID_PreEjec20_scb.CurrentValue
            PrepararTransaccion 
 
        Case Else 'boton no implementado
            Cancel = True
 
    End Select
 
Exit Sub
 
' ------ Manejo de errores
ErrH:
    MsgError GrialCont  ' Le muestro el error al usuario
    Cancel = True ' Cancelo el evento, la pantalla permanecerá igual
                  ' y el usuario podrá reintentar la operación
End Sub

Preparar Transaccion

El procedimiento "PrepararTransaccion" debe recuperar de la BD un recordset para cada tabla troncal afectada en la transacción. Se cargará un objeto GrialQuery con cada uno de los recordsets troncales necesarios para la transacción. En la condición "where" del recupero de datos se utilizará la variable global con el ID en edición.

Private Sub PrepararTransaccion()
 
    PreEjec20_grq.Init "select * from sic_docu_01 where ID_PREEJEC20=" & IDPreEjec20
    PreEjec21_grq.Init "select * from sic_docu_02 where RELA_PREEJEC20=" & IDPreEjec20
    PreEjec21_scb.ExtraFilterCondition = "RELA_PREEJEC20=" & IDPreEjec20  'Grilla Detalle
    Grial_Cont.LoadData PreEjec20_grq, PreEjec21_grq, PreEjec21_scb
 
    If IDPreEjec20 = -1 Then ' Alta
        PreEjec20_grq.PrepareAddNew 'Recordset Troncal en memoria
        With PreEjec20_grq.Rst
            !ID_PREEJEC20 = -1     'Datos que solo se asignan en un Alta
            !PREEJEC20_FAPL = Now
        End With
    End If
 
    PonerEnPantalla 'Pongo troncal y detalle en pantalla
 
End Sub

Explicación del caso de Alta (ID=-1, nuevo registro):

Al ser el valor del ID = –1, el resultado del select será vacío (ya que la condición será: Where ID_PREEJEC20 = -1), por lo tanto dentro del procedimiento PrepararTransaccion se debe invocar el método PrepareAddNew para el GrialQuery de tabla troncal.
El método PrepareAddNew genera un registro nuevo en el recordset en memoria, creando un espacio en el recordset donde el módulo puede trabajar. Todos los campos son inicializados con el valor NULL, por lo que al mostrar los datos en pantalla, se mostraran todos los datos en blanco.
Luego de preparar el nuevo registro se deberá dar valor a los campos que sólo son cargados en el alta, es decir, aquellos que permanecen inalterables durante toda la existencia del registro. Por lo general estos campos son el ID del registro, la fecha de Alta y los campos RELA a la cabecera (en las tablas de detalle).
Es recomendable realizar la carga de los campos que permanecen inalterables durante toda la existencia del registro en este punto en el cual se "crea" el registro. NO es recomendable hacerlo al momento de aceptar la transacción, ya que por error de programa, se podría modificar el campo ID o el campo FAPL (Fecha de Alta) de un registro antiguo.

Al final del procedimiento PrepararTransaccion tendremos los espacios necesarios en memoria (en los recordsets troncales) para que el usuario pueda operar dentro de la transacción. Tendremos la información completa del registro recuperado o un nuevo registro en blanco para el caso de alta.

En este momento invocaremos un procedimiento para volcar los datos desde el espacio de memoria (registros en los recordsets troncales) hacia los controles de edición en la pantalla. Por norma lo llamaremos PonerEnPantalla.

Private Sub PonerEnPantalla()
    With PreEjec20_grq.Rst
        AssignTextField PreEjec20_Descri_txt, !PREEJEC20_DESCRI
        AssignTextField PreEjec20_Cod_txt, !PREEJEC20_COD
        SysWflo06_scb.CurrentValue =!RELA_SYSWFLO06
        Fapl_Lb = FormatDate(!PREEJEC20_FAPL )
    End With
End Sub

En este procedimiento se usarán la rutinas AssignTextField para volcar datos a controles de texto y FormatDate para volcar datos de tipo fecha en Labels. Por norma, los controles de tipo Textbox que se utilizan para editar campos de la base, deben tener el mismo nombre del campo con el sufijo _txt adicionado, por ejemplo PreEjec20_Descri_txt

Una vez mostrados los datos, el usuario podrá operar con la pantalla de la transacción, hasta que pulse alguno de los botones de la barra inferior (Aceptar o Cancelar) disparándose entonces el evento GrialCont_BottomButtonClick

Evento ContainerFrmShow

El evento ContainerFrmShow sucede posteriormente al inicio de la transacción (Evento ButtonClick) y antes de hacer visibles los controles dentro del GrialCont. Debe utilizarse para realizar ajustes en la pantalla según los datos recuperados de la transacción y habilitar o deshabilitar controles. El evento ContainerFrmShow se ejecuta si y sólo si se ha iniciado satisfactoriamente la transacción, y se está por mostrar la pantalla (es decir, si se recuperaron correctamente los datos desde la base y NO se salió con cancel=true del evento ButtonClick).

El parámetro NewState informa estado elegido durante el evento ButtonClick. Por default toma el valor CONTROLS_ENABLED para el botón de Alta y Modificación y el valor VIEW_MODE (todos los controles deshabilitados) para el botón de Baja.

Dentro de este procedimiento debe deshabilitarse el combo superior, para que el usuario no pueda cambiar el registro elegido mientras está dentro de la transacción.

Formato:

 Event ContainerFrmShow (NewState As Operation_States)

Ejemplo:

Private Sub GrialCont_ContainerFrmShow (NewState As Operation_States)
  PreEjec20_scb.Enabled = False
End sub

Operación del Usuario

Mientras se encuentre habilitado el GrialCont, el usuario podrá operar con los controles de la pantalla. Los botones superiores del GrialCont se deshabilitan automáticamente y se habilitan los botones inferiores, permitiendo al usuario dos opciones: ACEPTAR o CANCELAR.

Cierre de la Transacción

El evento BottomButtonClick determina la finalización de la transacción. Se dispara cuando el usuario hace click en uno de los botones de la barra inferior (ACEPTAR o CANCELAR). Dependiendo de la acción elegida (BUTTON_ACCEPT o BUTTON_CANCEL) se validarán y aceptarán los datos o se cancelará completamente la transacción. En caso que el usuario haya pulsado el botón ACEPTAR se procederá a la validación de los datos ingresados y a su transferencia hacia los recordsets troncales en memoria. La validación y la transferencia de los datos en pantalla hacia los espacios de memoria en el recordset troncal se realizan en un procedimiento que llamaremos TomarDatosdePantalla.

Procedimiento TomarDatosdePantalla

Dentro de este procedimiento usaremos:

  • Para los campos individuales, la rutina UpdateField y UpdateFieldNum que permiten actualizar un campo de un recordset y validar por contenido nulo en la misma operación.
  • Para recuperar datos de una Grilla editable en pantalla, el procedimiento Grid_UpdateUserInput (para cerrar el modo edición) y luego ChangeMainRst para actualizar el recordset troncal desde los registros de la grilla en pantalla.
Private Sub TomarDatosdePantalla()
    With PreEjec20_grq.Rst
        If ValCur(PreEjec20_Porcentaje_Txt) > 100 then 
            Err.Raise 5,,"El Porcentaje debe ser menor que 100"
        End if
        UpdateField !PREEJEC20_PORCENTAJE, valcur(PreEjec20_Porcentaje_Txt)
        UpdateField !PREEJEC20_DESCRI, PreEjec20_Descri_txt,     "una descripción"
        UpdateField !RELA_SYSWFLO06, SysWflo06_scb.CurrentValue 
    End With
 
    ' Aplico los cambios realizados por el usuario en la grilla en el recordset troncal
    PreEjec21_Scb.Grid_UpdateUserInput
    GrialCont.ChangeMainRst PreEjec21_qrq.Rst, PreEjec21_Scb.Rst, "ID_PREEJEC21"
End Sub

(Nota: Todas las validaciones deben realizarse dentro este procedimiento y sólo si el usuario pulsa el botón "Aceptar". NO deben realizarse validaciones en eventos del tipo "_LostFocus" o "_Change").

Evento BottomButtonClick

En el evento GrialCont_BottomButtonClick creamos un objeto del tipo GrialTransaction para preparar la transacción que enviaremos al servidor.

Ejemplo:

Private Sub GrialCont_BottomButtonClick(ByVal GrialButtonCode As GrialApp.GrialButtons _
                                         , Cancel As Integer, NewState As GrialApp.Operation_States)
 
On Error GoTo ErrH
Dim Txn As New GrialTransaction
 
    Select Case GrialButtonCode 
 
    Case BUTTON_ACCEPT
 
        Select Case GrialCont.LastButton
            Case BUTTON_NEW, BUTTON_MODIFICATION
                'Validación y toma de datos 
                TomarDatosdePantalla
                'Preparo la transaccion
                Txn.Add PreEjec20_Grq,"ID_PREEJEC20"      ' Cabecera
                Txn.Add PreEjec21_Grq,";RELA_PREEJEC20"   ' Detalle
                'Aplico la transacción en el servidor
                GrialCont.Apply Txn
 
                If GrialCont.LastButton = BUTTON_NEW then 
                    IDPreEjec20 = Txn.NewIDValue 'Recupero el nuevo ID creado
                End If;
                ' Refresco los cambios en el combo superior
                PreEjec20_scb.LoadOnCurrentRecord IDPreEjec20 ' Refresco el registro del combo 
 
            Case BUTTON_DELETE
                PreEjec20_Grq.DeleteRecord
                PreEjec21_Grq.DeleteAllRecords
                'Preparo la transaccion
                Txn.Add PreEjec20_Grq.Rst ' Cabecera
                Txn.Add PreEjec21_Grq.Rst ' Detalle
                'Aplico la transacción en el servidor
                GrialCont.Apply Txn
                PreEjec20_scb.DeleteRecord  'Elimino registro del combo superior
 
        End Select
 
    Case BUTTON_CANCEL
 
        Select Case GrialCont.LastButton
             Case BUTTON_NEW
                  PreEjec20_scb.CancelAddNew 'deshago el alta en el combo superior
        End Select
 
    End Select
 
End If
 
Exit Sub
 
' ------ Manejo de errores
ErrH:
  MsgError GrialCont 'Le muestro el error al usuario
  If Txn.Commited then resume Next ' Si ya commiteo, ignoro el error
  Cancel = True  'Cancelo el evento de cierre de la pantalla
  'Al cancelar el BottomButtonClick, la pantalla permanecerá igual,
  'y el usuario podrá reintentar la operación
End Sub

Descripción de la operatoria

Luego de recuperar los datos de pantalla, se confecciona la transacción dentro de un objeto GrianTransaction mediante el método Add. Tanto para el Alta de registros como para Modificación, la transacción armada y enviada al servidor es la misma. Los registros deben enviarse en orden, primero los registros de cabecera y luego los registros de detalle y se debe indicar la relación entre los mismos (que campo es ID y qué campo es RELA)

Para el caso de Baja, se eliminarán los registros correspondientes de los recordsets troncales y se envían los recordsets al servidor. Los registros deben enviarse en orden, primero los registros de cabecera y luego los registros de detall, pero no es necesario para la eliminación física de registros especificar la información campos ID y RELA.

La baja física debe utilizarse en caso de excepción. Los controles de FK (Foreign Keys) en la base de Datos evitan que se elimine físicamente un registro si es referenciado desde otra tabla. Este control es realizado automáticamente por la BD.

Si se desea realizar una baja lógica o anulación, debe permitirse al usuario marcar/desmarcar un checkbox de fecha de baja y actualizar el campo de Fecha de Baja Lógica correspondiente en el registro. Un registro dado de baja lógica no podrá ser utilizado para relacionarlo desde otras tablas, pero sí debe aparecer en el combo de selección del módulo principal de la tabla, para permitir su recuperación (desmarcando el checkbox de Fecha de Baja).

Debe tomarse en cuenta que en caso de error, el usuario podrá reintentar la transacción (al salir con Cancel=True, no se cierra la transacción visual), por lo que debe verificarse que el código para BUTTON_ACCPET pueda ser ejecutado más de una vez en caso de reintentos.

Evento ContainerFrmHide

El evento ContainerFrmHide se dispara una vez cerrada la transacción (Luego del evento BottomButtonClick). Debe utilizarse para rehabilitar los controles deshabilitados el inicio de la edición (como un espejo del evento ContainerFrmShow).

Ejemplo:

Private Sub GrialCont_ContainerFrmHide(ByVal ActualState As GrialApp.Operation_States)
  PreEjec20_scb.Enabled = True
End sub

Nota:

No se deben realizarse cargas de datos (LoadData) en el evento ContainerFrmHide. Esto es debido a que el evento ContainerFrmHide se ejecuta una vez al comienzo del modulo (durante el GrialCont.Initialize) y en ese momento no está preparada aún la conexión de datos.

Herramientas personales