GrialApl, Transacción Visual

De GrialWIKI

(Diferencias entre revisiones)
Línea 24: Línea 24:
Ejemplo:
Ejemplo:
-
GrialCont1.Initialize UserDocument.Name, Parent.LocationUrl
+
    GrialCont1.Initialize UserDocument.Name, Parent.LocationUrl
El método Initialize:
El método Initialize:
Línea 139: Línea 139:
     PreEjec20_grq.Init "select * from sic_docu_01 where ID_PREEJEC20=" & IDPreEjec20
     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_grq.Init "select * from sic_docu_02 where RELA_PREEJEC20=" & IDPreEjec20
-
     PreEjec21_scb.ExtraFilterCondition = "RELA_PREEJEC20=" & IDPreEjec20" 'Grilla Detalle
+
     PreEjec21_scb.ExtraFilterCondition = "RELA_PREEJEC20=" & IDPreEjec20  'Grilla Detalle
     Grial_Cont.LoadData PreEjec20_grq, PreEjec21_grq, PreEjec21_scb
     Grial_Cont.LoadData PreEjec20_grq, PreEjec21_grq, PreEjec21_scb
     If IDPreEjec20 = -1 Then ' Alta
     If IDPreEjec20 = -1 Then ' Alta
-
         PreEjec20_scb.PrepareAddNew   ' Combo superior
+
         PreEjec20_scb.PrepareAddNew 'Combo superior
-
         PreEjec20_grq.PrepareAddNew   'Recordset Troncal en memoria
+
         PreEjec20_grq.PrepareAddNew 'Recordset Troncal en memoria
         With PreEjec20_grq.Rst
         With PreEjec20_grq.Rst
-
             !ID_PREEJEC20 = -1             'Datos que solo se asignan en un Alta
+
             !ID_PREEJEC20 = -1     'Datos que solo se asignan en un Alta
             !PREEJEC20_FAPL = Now
             !PREEJEC20_FAPL = Now
         End With
         End With
Línea 162: Línea 162:
         AssignTextField PreEjec20_Cod_txt, !PREEJEC20_COD
         AssignTextField PreEjec20_Cod_txt, !PREEJEC20_COD
         SysWflo06_scb.CurrentValue =!RELA_SYSWFLO06
         SysWflo06_scb.CurrentValue =!RELA_SYSWFLO06
-
         Fapl_Lb = FormatDate( !PREEJEC20_FAPL )
+
         Fapl_Lb = FormatDate(!PREEJEC20_FAPL )
     End With
     End With
End Sub
End Sub
Línea 322: Línea 322:
Todos los controles de un documento que posean estos sufijos serán buscados en el repositorio de Controles Registrados para asignarles la configuración correspondiente.
Todos los controles de un documento que posean estos sufijos serán buscados en el repositorio de Controles Registrados para asignarles la configuración correspondiente.
-
Nota: otros sufijos estándar son:         
+
Para otros tipos de controles se deben usar los siguiente ‘’’’sufijos’’’’:         
-
*'''        _grq: Grial Query'''
+
*'''        _grq o Grq: Grial Query'''
*'''        _gpr: Grial Print'''
*'''        _gpr: Grial Print'''
*'''        _ggp: Grial Grid Print'''
*'''        _ggp: Grial Grid Print'''
Línea 339: Línea 339:
Formato:
Formato:
-
  '''Sub \[GrialCont.LoadData-Controles/GrialApl.doc\] ( ParamArray RegistredControls() As Variant )'''
+
  '''Sub GrialCont.LoadData ( ParamArray RegistredControls() As Variant )'''
Ejemplo:
Ejemplo:
Línea 439: Línea 439:
Formato:
Formato:
-
'''Sub ChangeMainRst ( MainRecordset As Recordset, ViewRecordset As Recordset
+
  Sub ChangeMainRst ( MainRecordset As Recordset, ViewRecordset As Recordset
                                       , ID_Field As String
                                       , ID_Field As String
                                       ,  [CreateRecords As Boolean = Falso]
                                       ,  [CreateRecords As Boolean = Falso]
-
                                         , [PreCancelModifications As Boolean = Verdadero])'''
+
                                         , [PreCancelModifications As Boolean = Verdadero])
El parámetro ID_Field es el nombre del campo que vincula los registros en el Troncal con los registros en el ViewRecordset. En todos los casos, la tabla principal de la vista debe ser la misma tabla del recordset troncal, y también todos los campos que se deseen actualizar deben tener el mismo nombre en la vista y en el troncal.  
El parámetro ID_Field es el nombre del campo que vincula los registros en el Troncal con los registros en el ViewRecordset. En todos los casos, la tabla principal de la vista debe ser la misma tabla del recordset troncal, y también todos los campos que se deseen actualizar deben tener el mismo nombre en la vista y en el troncal.  
Línea 455: Línea 455:
Sub PrepararTransaccion…
Sub PrepararTransaccion…
PreForm20Grq.Init "Select * From PreForm20 " & _
PreForm20Grq.Init "Select * From PreForm20 " & _
-
              " where RELA_PREFORM19=" & IDPreform19Elegido
+
                              " where RELA_PREFORM19=" & IDPreform19Elegido
GrialCont.LoadData ID_Periodos_Scb, Instituciones_Ste, PreForm23Grq
GrialCont.LoadData ID_Periodos_Scb, Instituciones_Ste, PreForm23Grq
Línea 462: Línea 462:
' Actualizo el recordset troncal
' Actualizo el recordset troncal
GrialCont.ChangeMainRst PreForm20_grq.Rst, PreForm20_sgr.Rst, "ID_PREFORM20"
GrialCont.ChangeMainRst PreForm20_grq.Rst, PreForm20_sgr.Rst, "ID_PREFORM20"
 +
' Lo incorporo a la transacción
Txn.Cmd PreForm20_grq.Rst
Txn.Cmd PreForm20_grq.Rst
 +
' Lo envío al server
Txn.Apply GrialCont
Txn.Apply GrialCont
</code>
</code>
Línea 509: Línea 511:
Detalle de cada parte:
Detalle de cada parte:
-
‘’Seccion Opcional’’ (hasta los ‘’:’’)
+
‘''Seccion Opcional'’' (hasta el caracter ‘’’:’’’)
-
Los parámetros (opcionales) para la sección "Param = valor ‘’;’’" son:
+
Los parámetros (opcionales) para la sección "Param = valor '';''" son:
NAME = xxxx  -- Identifica el ítem con un nombre
NAME = xxxx  -- Identifica el ítem con un nombre
Línea 517: Línea 519:
HEADER =  hhhh -- Indica cual es el ítem (identificado previamente mediante NAME = xxxx ) que contiene los registros cabecera. Es necesario sólo cuando la cabecera y el detalle no se pueden colocar uno a continuación de otro en la transacción, o cuando hay más de un detalle para una cabecera.
HEADER =  hhhh -- Indica cual es el ítem (identificado previamente mediante NAME = xxxx ) que contiene los registros cabecera. Es necesario sólo cuando la cabecera y el detalle no se pueden colocar uno a continuación de otro en la transacción, o cuando hay más de un detalle para una cabecera.
-
RelaField= FIELD_NAME / HEADER_NAME -- Se usa para actualizar un registro que tiene más de un campo RELA.  Reemplaza el valor del campo indicado por el valor de ID tomado del header indicado. Por ejemplo: ‘’RelaField= rela_preform21/Pe21’’
+
RelaField= FIELD_NAME / HEADER_NAME -- Se usa para actualizar un registro que tiene más de un campo RELA.  Reemplaza el valor del campo indicado por el valor de ID tomado del header indicado. Por ejemplo: ''RelaField= rela_preform21/Pe21''
SysDateField = FIELD_NAME/FIELD_NAME... -- Se usa para los campos que deben ser actualizados con la fecha del sistema. Reemplaza los campos indicados por el Sysdate de la máquina donde corre el Servidor de Aplicaciones.
SysDateField = FIELD_NAME/FIELD_NAME... -- Se usa para los campos que deben ser actualizados con la fecha del sistema. Reemplaza los campos indicados por el Sysdate de la máquina donde corre el Servidor de Aplicaciones.
-
        Debe indicarse para efectuar un registro preciso de fechas y horas, ya que la máquina cliente donde se prepara la transacción puede tener mal la fecha.  
+
Debe indicarse para efectuar un registro preciso de fechas y horas, ya que la máquina cliente donde se prepara la transacción puede tener mal la fecha.  
En caso de un ALTA, se actualizarán los campos indicados, siempre que contengan valores no-nulos.  
En caso de un ALTA, se actualizarán los campos indicados, siempre que contengan valores no-nulos.  
En caso de MODIFICACION, se actualizarán los campos indicados con la fecha del sistema sólo si fue modificado su valor original
En caso de MODIFICACION, se actualizarán los campos indicados con la fecha del sistema sólo si fue modificado su valor original
-
        En el módulo cliente, debe indicarse siempre el "SysdateField=field1/field2/..." y se deben cargar/modificar los campos utilizando la función "Now" (sysdate de la terminal, valor no-confiable).  
+
En el módulo cliente, debe indicarse siempre el "SysdateField=field1/field2/..." y se deben cargar/modificar los campos utilizando la función "Now" (sysdate de la terminal, valor no-confiable).  
-
        Cuando el servidor actualice el recordset en la base, reemplazará los valores por el sysdate del servidor (valor confiable).
+
Cuando el servidor actualice el recordset en la base, reemplazará los valores por el sysdate del servidor (valor confiable).
Para los campos con nombre: FAPL, FBAJA, FANULADO, WFFECHACURSO, WFFECHA se realizará esta actualización automáticamente.
Para los campos con nombre: FAPL, FBAJA, FANULADO, WFFECHACURSO, WFFECHA se realizará esta actualización automáticamente.
-
‘’Seccion Normal’’ (Separados por ‘’;’’)
+
‘''Seccion Normal'’' (Separados por '’';'’')
'''ID_FieldName: '''
'''ID_FieldName: '''
Línea 547: Línea 549:
'''1.- CABECERA + DETALLE'''
'''1.- CABECERA + DETALLE'''
 +
<code>
     Dim Cabecera_PAREXA02_grq as New GrialQuery
     Dim Cabecera_PAREXA02_grq as New GrialQuery
     Dim Detalle_PAREXA03_grq as New GrialQuery
     Dim Detalle_PAREXA03_grq as New GrialQuery
     ...
     ...
-
 
     Dim Txn As New GrialTransaction
     Dim Txn As New GrialTransaction
Línea 557: Línea 559:
      
      
     GrialCont.Apply Txn
     GrialCont.Apply Txn
 +
</code>
'''2.- CABECERA + DETALLE + Detalle Extra'''
'''2.- CABECERA + DETALLE + Detalle Extra'''
-
 
+
<code>
     Dim Cabecera_EXACDA01_grq as New GrialQuery
     Dim Cabecera_EXACDA01_grq as New GrialQuery
     Dim Detalle_EXACDA03_grq as New GrialQuery
     Dim Detalle_EXACDA03_grq as New GrialQuery
Línea 577: Línea 580:
      
      
     Txn.Apply GrialCont
     Txn.Apply GrialCont
 +
</code>
'''3.- CABECERA, DETALLE + Relación'''
'''3.- CABECERA, DETALLE + Relación'''
-
 
+
<code>
     Dim Tabla_REACDO12_grq as New GrialQuery
     Dim Tabla_REACDO12_grq as New GrialQuery
     Dim Cabecera_EXACDA01_grq as New GrialQuery
     Dim Cabecera_EXACDA01_grq as New GrialQuery
Línea 597: Línea 601:
      
      
     Txn.Apply GrialCont
     Txn.Apply GrialCont
 +
</code>
===Actualización de Campos en un registro, Método UpdateField ===
===Actualización de Campos en un registro, Método UpdateField ===

Revisión de 03:15 8 ago 2009

Contenido

Grial Apl, GrialCont

Descripción General

El Contenedor 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.

El flujo de programa determinado por este control responde al siguiente esquema:

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

Componentes Visuales Requeridos

Dentro de cada GrialCont debe colocarse un objeto "Frame" de VB con el nombre "[Container_Frm-Referencias.doc]", dicho frame contendrá todos los controles 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 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 )

Ejemplo:

   GrialCont1.Initialize UserDocument.Name, Parent.LocationUrl

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

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 Combo 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.

Inicio de la Transacción

Evento ButtonClick

El evento ButtonClick determina el inicio de la transacción. En base a la acción elegida (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 )

Ejemplo:

Se crea una variable global tipo long para almacenar el ID actual en edición (por ejemplo: "Dim IDPreEjec20 as Long", y una variable para llevar los contadores de falsos ID's para las tablas de detalles (Por Ejemplo: "Dim Falso_IDPreEjec21 as Long" si se crean detalles)

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

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
         IDPreEjec20 = -1
         PrepararTransaccion
 
        Case BUTTON_MODIFICATION,BUTTON_DELETE
            If NoDataIn(PreEjec20_scb.CurrentValue) Then 
                ' Si no se ha elegido ningun registro, se cancela
                Cancel= True
                Exit Sub
            End if
            IDPreEjec20 = ID_PreEjec20_scb.CurrentValue
            PrepararTransaccion 
 
        Case Else 'no es un boton 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


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.

Para el caso de alta: Al ser el valor del ID "–1", se recuperarán automáticamente recordsets vacíos (ya que la condición será: Where ID_PREEJEC20 = -1), por lo tanto el procedimiento PrepararTransaccion se deberá invocar el método PrepareAddNew para el combo superior de selección y para cada GrialQuery de tabla troncal. El método PrepareAddNew crea 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 deberán cargarse 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 de la tabla, la fecha de Alta y los campos RELA a la cabecera para las tablas detalle. Es recomendable realizar la carga de los campos inalterables en este punto y sólo para el caso de alta en lugar de hacerlo con el resto de los campos en el momento de aceptar la transacción. De esta manera se evita la posibilidad de modificar dichos campos sobre un registro existente por error de programa.

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_scb.PrepareAddNew 'Combo superior
        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

Al final del procedimiento PrepararTransaccion tendremos los espacios necesarios en memoria (en los registros dentro del Combo y de los recordsets troncales) para operar la transacción. Tendremos entonces la información completa del registro recuperado o un 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. 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 un 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 NO se indicó cancel=true en el 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: ACPETAR o CANCELAR.

Cierre de la Transacción

El evento BottomButtonClick determina la finalización de la transacción. Se dispara cuando el usuario hace clic 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 de la grilla y luego ChangeMainRst para actualizar los recordset troncales 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 (o su equivalente SQLQuery) 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
 
    If GrialButtonCode = BUTTON_ACCEPT Then
 
        Select Case GrialCont.LastButton
            Case BUTTON_NEW, BUTTON_MODIFICATION
                ' Validación y toma de datos 
                TomarDatosdePantalla
                ' Preparo la transaccion
                Txn.Cmd PreEjec20_Grq,"ID_PREEJEC20"      ' Cabecera
                Txn.Cmd PreEjec21_Grq,";RELA_PREEJEC20"   ' Detalle
                 ' Aplico la transacción en el servidor
                GrialCont.Apply Txn
 
                If  GrialCont.LastButton = BUTTON_NEW then 
                    IDPreEjec20 = PreEjec20_grq.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.Cmd PreEjec20_Grq.Rst ' Cabecera
                Txn.Cmd PreEjec21_Grq.Rst ' Detalle
                 ' Aplico la transacción en el servidor
                GrialCont.Apply Txn
                PreEjec20_scb.DeleteRecord  'Elimino registro del combo superior
        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 Cmd. 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).

En el control de errores del método GrialCont_BottomButtonClick debe tomarse en cuenta que el usuario podrá reintentar la transacción, por lo que deben dejarse los recordsets troncales en un estado adecuado como para permitir un reintento de la transacción.

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

Botones Disponibles en la Transacción

En el Inicio del Módulo (evento UserDocument_Show) debe dejarse visibles sólo aquellos botones contemplados en el código

Ejemplo:

Private Sub UserDocument_Show ( . . . 
With GrialCont
     .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:

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

Controles Registrados

Los controles registrados son configuraciones de Combos, Grillas y Trees Grial almacenadas en el servidor de datos. La configuración le indica al control que query realizar para cargar los datos y cómo mostrarlos. Se puede asignar una configuración a un control en un módulo simplemente asignándole la propiedad Name o por la propiedad ConfigurationName. La propiedad ConfigurationName permite tener dos controles (con nombres diferentes) pero que utilicen la misma configuración registrada.

El sufijo del nombre del control registrado determina su tipo:

  • _scb: Standard Combo/Grid
  • _ste: Standard Tree Controller
  • _sgr: Standard Grid Controller

Todos los controles de un documento que posean estos sufijos serán buscados en el repositorio de Controles Registrados para asignarles la configuración correspondiente.

Para otros tipos de controles se deben usar los siguiente ‘’’’sufijos’’’’:

  • _grq o Grq: Grial Query
  • _gpr: Grial Print
  • _ggp: Grial Grid Print
  • _gct: Grial Cross Tree
  • _tvw: TreeView
  • _grx o _grd: Grilla (GridEX de Janus)
  • Rst: Recordset
  • Txt: Text Box
  • Lb : Label
  • Cmd: Command Button
  • Opt: Option Button
  • Chk: CheckBox

Los controles registrados se cargan con datos mediante el método GrialCont.LoadData. Este método acepta una lista de controles registrados (y Objetos GrialQuery) y los carga según el query de la configuración realizando un único acceso al servidor.

Formato:

Sub GrialCont.LoadData ( ParamArray RegistredControls() As Variant )

Ejemplo:

   PreEjec20_grq.Init "Select * from PRE_EJEC_20 where ID_PREEJEC20=" & IDPreEjec20
   GrialCont.LoadData PreEjec20_grq, ID_PreEjec20_Scb, PreEjec21_scb

Manejo de Recordsets, Objeto GrialQuery

El Objeto GrialQuery representa el Recordset resultante luego de la ejecución de un comando. Puede definirse el comando con lenguaje SQL o cualquiera de las extensiones aceptadas por el GrialDataServer. El objeto GrialQuery se inicializa con el comando a ejecutar (método Init) y puede ser cargado mediante el método GrialCont,LoadData.

Por ejemplo:

Dim PreForm23Grq as New GrialQuery
PreForm23Grq.Init "Select * From PreForm23 where RELA_PREFORM22=" &  IDPreform22Elegido
GrialCont.LoadData PreEjec21_Scb, PreForm23Grq
If PreForm23Grq.RecCount = 0 then Err.Raise 5,"No se hallaron datos"

El objeto GrialQuery posee una serie de métodos y propiedades para el manejo del Rst interno, que simplifican manejo de recordsets ADO.

Propiedades:

  • CurrentQuery: Propiedad tipo string conteniendo el texto del comando a ejecutar
  • Rst: Propiedad tipo recordset conteniendo el recordset resultante de la consulta.
  • RecCount: Cantidad de registros en Rst.

Métodos y Funciones:

Function Find(Criteria As String) As Boolean Busca el primer registro que coincida con el criterio especificado, retorna False si no halla ningún registro. Criteria, puede ser: "CAMPO [oper] [Valor]" donde la operación puede ser " =, > , < , >= , <= , LIKE", y pueden combinarse condiciones con los operadores AND y OR

Function Position (ID_Field As String, ID) As Boolean Se posiciona en el registro que cumpla la condición "ID_FIELD = ID"

Function Sum(FieldName As String) Retorna la suma los valores del campo indicado para todos los registros actuales

Function ValueList(FieldName As String, [Separ As String = ","]) Retorna un string con los valores del campo indicado, separados por coma. Es útil para conformar un filtro a se utilizado con la cláusula IN.

Por Ejemplo:

   PreEjec21_scb.ExtraFilterCondition = _ "RELA_EJEREND01 IN ( " _
                       & ER01Habilitadas_Grq.ValueList("ID_EJEREND01") & ")"

Sub MovePreStart() y Function NextRecord() As Boolean MovePreStart posiciona el recordset en una posición anterior al primer registro para iniciar un recorrido de todos los registros. NextRecord avanza un registro y devuelve False en caso que no haya mas registros. Usados en conjunto facilitan un bucle de recorrido de todos los registros del recordset.

Por Ejemplo:

With ER01Habilitadas_Grq
   .MovePreStart
   While .Nextrecord
       'Imprimo cada registro...
       GrialPrint_Gpr.WtCol "Cod", .Rst!EJEREDN01_CODIGO
       GrialPrint_Gpr.WtCol "Descri", .Rst!EJEREDN01_DESCRI
       ...
   Wend
End With
   

Function Modified() As Boolean Retorna true si se ha modificado algún dato dentro del recordset

Function PrepareAddNew() As Boolean Crea un nuevo registro en blanco dentro del recordset

Sub DeleteRecord() Elimina el registro actual del recordset

Sub DeleteAllRecords() Elimina todos los registros del recordset

Todas las funciones de los recordsets ADO, pueden accederse directamente desde el objeto Rst

Por Ejemplo:

With ER01Habilitadas_Grq
   .Rst.Filter = "EJEREND01_PRINCIPAL = 1 " 'Solo los registros principales
   .Rst.Sort = "EJEREND01_CODIGO, EJEREND01_FAPL " 'Ordenado por codigo y Fecha
   .MovePreStart
   While .Nextrecord
       'Imprimo cada registro...
       GrialPrint_Gpr.WtCol "Cod", .Rst!EJEREDN01_CODIGO
       GrialPrint_Gpr.WtCol "Descri", .Rst!EJEREDN01_DESCRI
       ...
   Wend
End With
   

Recordsets Troncales

Para el manejo y actualización de datos dentro del entorno grial, definiremos los conceptos de "ViewRecordset" o "Recordset de Visualización" y "MainRecordset" o "Recordset Troncal".

  • Un "ViewRecordset" surge de una vista definida en la base de datos (Conjunción de varias tablas) y es mostrado al usuario a través de un Control Grial (Combo, Tree o Grilla).
  • Un Recordset "Troncal" contiene generalmente datos de una sola tabla, es recuperado por programa mediante un objeto GrialQuery, y es utilizado internamente para actualizar datos y preparar la transacción para el servidor.

Esta diferenciación es necesaria ya que los ViewRecordset por lo general no son "updateables", es decir, al ser conjunciones de varias tablas, no pueden ser modificados para actualizar la base de datos. En cambio, un recordset "troncal" contiene los datos de una única tabla, incluyendo su ID, por lo que las modificaciones puede ser aplicadas a la Base de Datos. Es este recordset "Troncal" es el que es enviado en la transacción.

Como el usuario ve y opera con los ViewRecordset , es necesario al aceptar la transacción transferir los cambios realizados por el usuario hacia el recordset Troncal. Para esto se utiliza un método del GrialCont llamado "ChangeMainRst"

Formato:

 Sub ChangeMainRst ( MainRecordset As Recordset, ViewRecordset As Recordset
                                      , ID_Field As String
                                      ,  [CreateRecords As Boolean = Falso]
                                       , [PreCancelModifications As Boolean = Verdadero])

El parámetro ID_Field es el nombre del campo que vincula los registros en el Troncal con los registros en el ViewRecordset. En todos los casos, la tabla principal de la vista debe ser la misma tabla del recordset troncal, y también todos los campos que se deseen actualizar deben tener el mismo nombre en la vista y en el troncal. Todas las modificaciones, altas y bajas realizadas sobre el ViewRecordset por el usuario serán duplicadas en el Troncal, sincronizando los registros que posean el mismo valor del campo ID_Field, y transfiriendo todos los valores entre los campos de igual nombre.

Ejemplo:

Dim PreForm23Grq as New GrialQuery
Dim IDPreform19Elegido as Long
 
Sub PrepararTransaccion…
PreForm20Grq.Init "Select * From PreForm20 " & _
                               " where RELA_PREFORM19=" & IDPreform19Elegido
GrialCont.LoadData ID_Periodos_Scb, Instituciones_Ste, PreForm23Grq
 
Sub BottomButtonClick….
Dim Txn As New GrialTransaction
' Actualizo el recordset troncal
GrialCont.ChangeMainRst PreForm20_grq.Rst, PreForm20_sgr.Rst, "ID_PREFORM20"
' Lo incorporo a la transacción
Txn.Cmd PreForm20_grq.Rst
' Lo envío al server
Txn.Apply GrialCont


Actualización de Datos: Objeto GrialTransaction, Objeto SqlQuery

Las transacciones en el Entorno Grial son preparadas en el cliente, y luego enviadas como un conjunto único al Servidor intermedio. El objeto utilizado para componer una transacción es el GrialTransaction. Los objetos de esta clase funcionan como una "pila" en la cual se acumulan los ítems que conforman la transacción recordsets con modificaciones y/o comandos SQL (con las extensiones aceptadas por el GrialDataServer). Una vez armada la transacción, mediante el método GrialCont.Apply se envía el conjunto al servidor para su proceso.

Nota: El Objeto SqlQuery pertenece a una versión anterior y se mantiene por compatibilidad, posee las mismas funciones del objeto GrialTransaction, con la única diferencia del método para aplicar la transacción: El objeto GrialTransaction se aplica con el método Apply en tanto que en el SqlQuery se invoca el método "UpdateRecords"

La transacción se prepara mediante el método "Cmd", que es el que coloca en la pila cada uno de los ítems que la componen. Los ítems deben ser agregados en la transacción en un orden cabecera-detalle, haciendo seguir a cada recordset con registros de cabecera del recordset con registros de detalle dependientes de los anteriores. Por default, al aplicar la transacción, el GrialDataServer asume que los registros de cabecera de un ítem dado se hallan en el ítem inmediato anterior.

Formato: Sub Cmd ( SqlCmd_or_Recordset, [Rela_String As String] ) Sub Item( SqlCmd_or_Recordset, [Rela_String As String] ) Ejemplo:

Dim Txn As New GrialTransaction
Txn.Cmd PreForm20_grq.Rst , "ID_PREFORM20"
Txn.Cmd PreForm21_grq.Rst , "ID_PREFORM21;RELA_PREFORM20"
Txn.Cmd PreForm23_grq.Rst , ";RELA_PREFORM21"
' Aplico la transacción en el Servidor 
Txn.Apply GrialCont

El primer parámetro del método Cmd es un recordset con las modificaciones realizadas o puede ser también un string conteniendo un query o un comando a ejecutar en la BD. El segundo parámetro establece propiedades para el ítem dentro de la transacción, mediante una sintaxis especial. Cuenta con dos secciones separadas el carácter dos puntos (":"), la sección principal indica hasta tres nombres de campo, indicando qué campo es el ID de la tabla, cuál contiene la relación con el recordset de cabecera y opcionalmente que campo representa el RELA_PADRE para las tablas de tipo ARB (árboles). Estos tres nombres de campo se separan por punto y coma (" ; "). Dentro de la transacción se asume que si en un Cmd se indica un campo RELA, la tabla cabecera es la indicada en el ítem inmediato anterior. Esto es consecuente con la necesidad de ordenar en la transacción primero las tablas cabeceras y luego los detalles.

Por ejemplo:

Txn.Cmd PreForm20_grq.Rst , "ID_PREFORM20" ' Cabecera
Txn.Cmd PreForm21_grq.Rst , "ID_PREFORM21;RELA_PREFORM20" ' Detalle de la 20
Txn.Cmd PreForm07_grq.Rst , "ID_PREFORM07;;RELA_PADRE" ' Actualizo el arbol

Existe una sección de datos opcionales, que debe indicarse antes de la sección principal y se separa de la misma mediante dos puntos (":"). La sección opcional contiene propiedades para el ítem de la transacción y da la posibilidad de modificar el orden cabecera-detalle o de actualizar tablas con múltiples campos RELA.

Por ejemplo:

Txn.Cmd PreForm20_grq.Rst , "Name=PF20: ID_PREFORM20 " ' Cabcera
Txn.Cmd PreForm07_grq.Rst , "ID_PREFORM07;;RELA_PADRE" ' Actualizo el arbol
Txn.Cmd PreForm21_grq.Rst , "Header = PF20: ID_PREFORM21;RELA_PREFORM20" 'Detalle

El Formato para el segundo parámetro del método Cmd es: "[ Param = valor, Param = valor... : ] ID_FieldName ; RELA_FieldName ; RELA_PADRE"

Detalle de cada parte:

Seccion Opcional'’' (hasta el caracter ‘’’:’’’)

Los parámetros (opcionales) para la sección "Param = valor ;" son:

NAME = xxxx -- Identifica el ítem con un nombre

HEADER = hhhh -- Indica cual es el ítem (identificado previamente mediante NAME = xxxx ) que contiene los registros cabecera. Es necesario sólo cuando la cabecera y el detalle no se pueden colocar uno a continuación de otro en la transacción, o cuando hay más de un detalle para una cabecera.

RelaField= FIELD_NAME / HEADER_NAME -- Se usa para actualizar un registro que tiene más de un campo RELA. Reemplaza el valor del campo indicado por el valor de ID tomado del header indicado. Por ejemplo: RelaField= rela_preform21/Pe21


SysDateField = FIELD_NAME/FIELD_NAME... -- Se usa para los campos que deben ser actualizados con la fecha del sistema. Reemplaza los campos indicados por el Sysdate de la máquina donde corre el Servidor de Aplicaciones. Debe indicarse para efectuar un registro preciso de fechas y horas, ya que la máquina cliente donde se prepara la transacción puede tener mal la fecha. En caso de un ALTA, se actualizarán los campos indicados, siempre que contengan valores no-nulos. En caso de MODIFICACION, se actualizarán los campos indicados con la fecha del sistema sólo si fue modificado su valor original

En el módulo cliente, debe indicarse siempre el "SysdateField=field1/field2/..." y se deben cargar/modificar los campos utilizando la función "Now" (sysdate de la terminal, valor no-confiable).

Cuando el servidor actualice el recordset en la base, reemplazará los valores por el sysdate del servidor (valor confiable).

Para los campos con nombre: FAPL, FBAJA, FANULADO, WFFECHACURSO, WFFECHA se realizará esta actualización automáticamente.

Seccion Normal'’' (Separados por '’';'’')

ID_FieldName: Nombre del Campo que contiene el ID de la tabla. Se usará para tomar el valor que debe ser grabado en los campos RELA de los siguientes detalles.

RELA_FieldName: Nombre del campo que contiene el ID de la cabecera (por default en el recordset anterior). La combinación de ID_FieldName de un ítem y el RELA_FieldName del ítem siguiente establecen una relación cabecera-detalle.

RELA_PADRE: Cuando un recordset establece relaciones dentro de sí mismo. Por ejemplo una tabla tipo árbol. RELA_PADRE es el nombre del campo que representa la relación padre-hijo dentro de la jerarquía del árbol. La combinación ID_FieldName + RELA_PADRE en un mismo recordset se utiliza para las tablas tipo árbol.


Ejemplos de Armado y Aplicación de Transacciones

1.- CABECERA + DETALLE

    Dim Cabecera_PAREXA02_grq as New GrialQuery
    Dim Detalle_PAREXA03_grq as New GrialQuery
    ...
    Dim Txn As New GrialTransaction
 
    Txn.CMD Cabecera_PAREXA02_grq.Rst,"ID_PAREXA02"
    Txn.CMD Detalle_PAREXA03_grq.Rst,";RELA_PAREXA02"
 
    GrialCont.Apply Txn

2.- CABECERA + DETALLE + Detalle Extra

    Dim Cabecera_EXACDA01_grq as New GrialQuery
    Dim Detalle_EXACDA03_grq as New GrialQuery
    Dim Detalle_EXACDA04_grq as New GrialQuery
    ...
 
    Dim Txn As New GrialTransaction
 
    Txn.CMD Cabecera_EXACDA02_grq.Rst,"Name=CabeceraGeneral:ID_EXACDA01"
 
    ' Este detalle toma el header por default 
    ' (recordset en el CMD anterior)
    Txn.CMD Detalle_EXACDA03_grq.Rst,";RELA_EXACDA01"
 
    ' Este detalle toma el ID del item "CabeceraGeneral"
    Txn.CMD Detalle_EXACDA04_grq.Rst,"Header=CabeceraGeneral;RELA_EXACDA01"
 
    Txn.Apply GrialCont


3.- CABECERA, DETALLE + Relación

    Dim Tabla_REACDO12_grq as New GrialQuery
    Dim Cabecera_EXACDA01_grq as New GrialQuery
    Dim Detalle_EXACDA03_grq as New GrialQuery
    ...
 
    Dim Txn As New GrialTransaction
 
    SqlQ.CMD Tabla_REACDO12_grq.Rst,"Name=TablaDescripciones:;ID_REACDO12"
    SqlQ.CMD Cabecera_EXACDA02_grq.Rst,"ID_EXACDA01"
 
    ' Este detalle toma el header por default (recordset en el CMD anterior) 
    'y además actualiza un rela al primer recordset de la transacción
    SqlQ.CMD Detalle_EXACDA03_grq.Rst, _
        "RelaField=RELA_REACDO12/TablaDescripciones : ; RELA_EXACDA01"
 
    Txn.Apply GrialCont

Actualización de Campos en un registro, Método UpdateField

Para estandarizar la asignación de valores desde la pantalla a los campos en un registro, se utiliza el método UpdateField (del Componente GrialUtilP)

Formato:

Sub UpdateField ( Dest As Object, Source, [ DebeIndicar_CheckMsg As String ] )

El parámetro "Dest" es un objeto tipo Field (dentro de un Recordset). El parámetro "Source" es el dato a colocar en el objeto Field El parámetro Opcional "DebeIndicar_CheckMsg" permite controlar que el valor no sea nulo.

Ejemplo:

With PreForm20_grq.Rst
   UpdateField !PREFORM23_DECRI, DescriTxt, "la descripción"
   UpdateField !RELA_PREFORM23, ID_PreForm23_Scb.CurrentValue, "la unidad de medida"

El parámetro Opcional "DebeIndicar_CheckMsg" facilita la validación automática para evitar que el campo quede por null o vacío, permitiendo realizar la validación y la actualización en un solo paso. Si se especifica El parámetro Opcional "DebeIndicar_CheckMsg", se controlará que el valor a actualizar no sea nulo y en caso contrario se generará un error. En el ejemplo dado, en caso que el TextBox "DescriTxt" este vacío, se generará un error (Run-Time Error 5 de Visual Basic) con el mensaje "Error: Debe indicar la descripción".

Nota: El Método UpdateField no realiza modificaciones en el registro si el valor a actualizar es igual al valor existente en el campo, optimizando así las transacciones al no incluir registros sin modificaciones reales. Debe utilizarse siempre este método en lugar de una asignación directa de un valor al registro.

Se puede modificar el literal "Debe indicar" que se agrega automáticamente el mensaje de error. Para esto debe modificarse la propiedad "DebeIndicarMsg" (Propiedad Pública, as String, del componente GrialUtil). También puede modificarse la propiedad "ValidarTitle" para especificar el título en el mensaje de error (por default es "Validación")

Herramientas personales