abcMaster

Clase utilizada para crear otra clase para control general de un módulos basados en un archivo principal con posibles archivos auxiliares.

Principalmente esta clase está diseñada para dar una consulta y mantenimiento de un archivo, pero permite aumentar o quitar cualquier núnero de opciones dentro del menú correspondiente al módulo, además, algunos métodos pueden ser usados de manera general.

Hija de
No tiene clase padre.
Variables
cAlias Alias del archivo principal (solo lectura)
lDlg Indica si la opcion de mantenimiento se realiza en un diálogo (.t.) o sobre el browse de la ventana del módulo
hWnd Hash con el contenido de las ventanas controladas por abcMaster, hay que tener mucho cuidado, porque aunque la variable es de solo lectura, es una referencia al hash usado como control de las ventanas, si se modifica, puede causar errores inesperados (solo lectura)
oEntorno Objeto qEntorno con el control de los archivos .dbf abiertos por el módulo (solo lectura)
oUsr Objeto de la clase qPassword para el control de accesos, este objeto es el mismo para todos los objetos creados directamente o heredados de esta clase
oWnd Objeto de la ventana principal

Métodos
new() Crea un método personalizado a gusto del programador
mant_menu() Crea un menú básico de mantenimiento
mant_button() Crea una bara de botones con las opciones básicas de mantenimiento
mant_menuButton() Crea un menú y barra de botones con las opciones básicas de mantenimiento
mant_click() Es el equivalente de hacer doble click en el registro actual del browse
mant_add() Realiza la opción de agregar un registro nuevo
mant_delete() Marca como borrado un registro
mant_edit() Realiza la opción de editar el registro actual del browse
dbAppend() Recicla un registro borrado si es posible o agrega un registro nuevo al archivo
dbDelete() Borra todos los registros dentro de un rango ORDSCOPE()
catalogo() Crea una ventana MDI hija de mantenimiento con las opciones básicas
catalogoSDI() Crea una ventana SDI demantenimiento con las opciones básicas
validaClave() Compara lo capturado contra el índice del archivo para evitar registros duplicados
refrescaBrowse() Método utilizado para refrescar el browse en una edición sobre el browse (sin diálogo)

Descripción
Esta clase no es de uso directo cuando se emplea para manejar módulos, si se utilizan los métodos para uso general, estos si pueden ser llamados directamente de la clase abcMaster. Para crear módulos siempre se tendrá que crear una clase heredada de ésta, porque los nuevos módulos siempre serán métodos de la nueva clase y deberán tener el nombre del archivo principal utilizado en él.

Esta clase permite crear módulos ABC de mantenimiento sencillo de archivos (altas, bajas y cambios) o cualquier otra cosa que se quiera manejar en un módulo del programa.

Esto permite crear fácilmente un control para una opción, ya que, por ejemplo, ayuda a crear una ventana con un browse principal y un menú y/o barra de botones para el control de la misma, aunque esto ya dependera de la necesidad e imaginación del programador.

Además se encarga de la apertura y cierre de archivos, permitiendo al programador no tener que preocuparse por ello, esta apertura aprovecha la clase qEntorno (comandos DBFS/END DBFS), la cual permite utilizar archivos ya abiertos sin tener que abrirlos varias veces para utilizarlos simplemente para consultas rápidas, o cualquier otra situación que no obligue el mantener un archivo abierto más de una vez, todo esto sin que el programador se preocupe de eso más allá de indicar como deberá ser abierto el archivo.

Otra caracteristica de esta clase es que muchos de estos métodos pueden ser utilizados de manera general, no necesariamente para el control del archivo principal de cada opción.

Las clases heredadas de abcMaster no necesitan reemplazar los métodos ya existentes, tal vez y no se recomienda, los métodos que crean menú o barra de botones, en este caso es mejor crearo otros métodos con otro nombre.

Para crear un nuevo módulo lo único que se necesita es crear un método con el nombre del archivo principal que será llamado por el método new(), y será donde se defina la ventana utilizada o rutina inicial del módulo, y agregar tambien los métodos utilizados para las opciones de este módulo, esto para cada uno de los módulos (archivos) que se quieran manejar.

Método de control de módulo
Los métodos creados para el control de los módulos, o sea, los métodos con los nombres de los archivos que serán llamados desde el método new() reciben siempre el alias del archivo principal como primer parámetro, y como segundo parametro en adelante reciben los argumentos enviados en el llamado como segundo argumento en adelante, por ejemplo:

   miAbcMaster():new( "archivo" )              => method archivo( cAlias ) class miAbcMaster
   miAbcMaster():new( "archivo", 8 )           => method archivo( cAlias, nNumero ) class miAbcMaster
   miAbcMaster():new( "archivo", date(), .f. ) => method archivo( cAlias, dFecha, lLogico ) class miAbcMaster


Además en estos métodos se define cual de las tres maneras posibles de trabajar del módulo se va a utilizar:

   1. Ventana para captura líneal (directamente sobre el browse, campo por campo), la ventana debe asignarse al objeto ::oWnd, no es necesario indicar otra cosa, porque por defecto ::lDlg tiene valor de .f.

   2. Ventana para captura en diálogo, la ventana debe asignarse al objeto ::oWnd, hay que indicar ::lDlg := .t. para indicar que se trata de captura en diálogo.

   3. No requiere control de ventana o se prefiere llevar el control manual mediante un diálogo, se omite el uso de la variable ::oWnd

Esto quiere decir que si se quiere utilizar una ventana es necesario asignar la ventana a la variable de instancia ::oWnd, y el método de captura es definido por la variable ::lDlg que indica si será por diálogo (.t.) o lineal (.f.). En cualquiera de estos dos casos NO SE DEBE INDICAR el llamado al método ::oWnd:activate() de la ventana (ACTIVATE WINDOW ::oWnd), ya que este será llamado directamente por el método new() al regresar del método de control del módulo, para poder controlar el cerrado de los archivos, si no se hace uso de ventana, los archivos también seran cerrados al retomar el control el método new().

Captura de registro (modificación o registro nuevo)
La captura para la edición o adición de registros se puede realizar en una función cualquiera, pero es recomendable utilizar un método de la misma clase heredada de abcMaster para poder aprovechar las variables de objeto, tanto de la misma clase abcMaster, como de la nueva clase para el control del módulo. Este método o función debe considerar algunos detalles dependiendo del tipo de captura:

   1. Captura linéal, debe agregarse antes de la línea RETURN un llamado al método ::refrescaBrowse( lRet ), donde lRet indica si la captura se realizó correctamente o si fué cancelada, tambíén RETURN debe regresar lRet, principalmente si se trata de la captura de un registro nuevo, para saber si se continúa con la captura o se interrumpe.

   2. Captura en diálogo, esta opción solamente tiene que considerar lo que devuelve RETURN:
      a) Devuelve un número: en este caso se considera que se realizaron todas las operaciones necesarias, ya sea edición y guardado de datos, agregar nuevo registro, o cualquier otra operación, no se deja nada pendiente, así que lo único que se hace al regresar con el RETURN es acomodar el puntero en el registro indicado en el valor devuelto.
      b) Devuelve un objeto: cuando se devuelve un objeto se envía al objeto un llamado al método save() y se mantiene el puntero en el registro editado (se recomienda el uso de la clase qDbf).
      c) Devuelve NIL, este caso se entiende, aunque no necesariamente es el caso, que se cancela la captura, pero como en el caso a), también puede haberse llevado a cabo todo un proceso de manera correcta, la difrencia de esté caso y el a) es que cuando se devuelve NIL el puntero se queda (o acomoda) en el registro desde el que fue llamado el proceso de captura.
      d) Cualquier otro valor devuelto marcará error en tiempo de ejecución

Reciclaje de registros
Un requisito indispensable para el correcto funcionamiento de esta clase es la existencia de un indice llamado "deleted", el cual deberá ser creado como:

     INDEX ON deleted()TAG deleted FOR deleted()

éste índice se utiliza por abcMaster para reciclar los registros borrados en lugar de agregar un registro nuevo.

Métodos

new( caArchivos [, xPar1,...xParN)
Este método crea un módulo personalizado, a difrencia de los métodos catalogo() y catalogoSDI(), este método permite personalizar el módulo al gusto del programador.
Devuelve: Una referencia al objeto, la cual no sirve de mucho porque al al regresar quiere decir que ya se realizó el proceso del módulo
caArchivos - Si es cadena de caracters indica el nombre del archivo que se va a utilizar en esta opción.
- Si es matriz, revisa cada elemento de la matriz, el cual debe ser o una cadena de caracteres o una matriz, cada elemento que sea una cadena de caracteres será el nombre de un archivo que será abierto, cada elemento que sea una matriz deberá contener entre uno y tres elementos, si no se indican los todos los elementos, los elementos faltantes serán remplazados por su valor por omisión, donde:
El primer elemento indica el nombre de un archivo que será abierto
El segundo elemento depende de si se trata del primer archivo o del segundo en adelante, para el primer archivo indica si se abre de manera exclusiva (.t.) o compartida (.f.), por omisión es compartido, para el resto de los archivos indica si se abre el archivo para uso de este modulo (.t.) o si se abre bajo el control de archivos que se abren solo una vez y se comparten entre todos los módulos (.f.) por omisión es compartido (ver qEntorno).
El tercer elemento indica el RDD utilizado para ese archivo, por omisión se utiliza el RDD utilizado por el programa.
xPar1 a xParN Parámetros adicionales para el método utilizados por el programador

mant_menu( cTitulo, nDerechos, cBotones )
Crea un menú básico de mantenimiento para agregarse al menú que se esté definiendo, esto quiere decir que solamente indica la opción del menú, permitiendo agregar mas opciones al menú de la ventana, por ejemplo:
menu oMenu
     ::mant_menu( "&Mi opción", 12, "I" )
     menuItem "&Otra opción" action accion()
endMenu
Devuelve: NIL
cTitulo Texto que se utilizará para indicar la opción del menú, si se omite se utiliza "&Edición"
nDerechos Opción utilizada para la seguridad, está pensado para trabajar con un objeto de la qPassword, el cual debe asignarse a la variable oUsr, si no se asigna, siempre será verdadero
cBotones Si se envía "I" agrega un botón de impresión, el cual imprime un reporte sencillo de lo mostrado en el browse

mant_button( oWnd, nDerechos, cBotones )
Crea una bara de botones con las opciones básicas de mantenimiento, si se utiliza una ventana de la famila tWAbreBrow, se aprovecha la ventaja de los botones qBtnBmp.
Devuelve: NIL
oWnd Ventana a la que se agrega la barra de botones
nDerechos Opción utilizada para el control de derechos de usuario, está pensado para trabajar con un objeto de la qPassword, el cual debe asignarse a la variable oUsr, si no se asigna, siempre será verdadero
cBotones Si se envía "I" agrega un botón de impresión, el cual imprime un reporte sencillo de lo mostrado en el browse

mant_menuButton( cTitulo, oWnd, nDerechos, cBotones )
Crea un menú y barra de botones con las opciones básicas de mantenimiento, aunque realmente es un proceso independiente, el resultado es el mismo que combinar los métodos mant_menu() y mant_button(), si se utiliza una ventana de la famila tWAbreBrow, se aprovecha la ventaja de los botones qBtnBmp.
Devuelve: NIL
cTitulo Texto que se utilizará para indicar la opción del menú, si se omite se utiliza "&Edición"
oWnd Ventana a la que se agrega la barra de botones
nDerechos Opción utilizada para la seguridad, está pensado para trabajar con un objeto de la qPassword, el cual debe asignarse a la variable oUsr, si no se asigna, siempre será verdadero
cBotones Si se envía "I" agrega un botón de impresión, el cual imprime un reporte sencillo de lo mostrado en el browse

mant_click()
Es el equivalente a dar doble click sobre el registro actual del browse
Devuelve: El valor devuelto por la función asignada al doble click del browse.

mant_add( bNew )
mant_add( bNew, oBrw, lDlg ) (opcional para uso general)

Activa la opción para agregar un registro nuevo al archivo .dbf principal del módulo, esta opción puede ser utilizada para otros archivos desde otros browses siempre y cuando se indique el objeto del browse afectado, del cual, entre otras cosas, será tomado el alias del archivo afectado.
Devuelve: NIL
bNew Code block que se evalúa antes de grabar la información en un registro nuevo, si no se envía realiza el proceso de manera normal
oBrw Objeto de la ventana heredada de la clase tWAbreBrow o del objeto del browse que se utiliza, si no se envía utiliza el de la ventana principal del módulo (::oWnd)
lDlg Indica si la captura será por diálogo (.t.) o sobre el browse (.f.), si no se envía utiliza lo indicado en ::lDlg

mant_delete()
mant_delete( oBrw ) (opcional para uso general)

Marca como borrado un registro, esta opción puede ser utilizada para otros archivos y desde otros browses siempre y cuando se indique el objeto del browse afectado, del cual, entre otras cosas, será tomado el alias del archivo afectado.
Devuelve: NIL
oBrw Objeto de la ventana heredada de la clase tWAbreBrow o del objeto del browse que se utiliza, si no se envía utiliza el de la ventana principal del módulo (::oWnd)

mant_edit()
mant_edit( oBrw, lDlg ) (opcional para uso general)

Activa la opción para editar el registro actual, esta opción puede ser utilizada para otros archivos desde otros browses siempre y cuando se el objeto del browse del archivo que quiere editar, del cual, entre otras cosas, será tomado el alias del archivo afectado.
Devuelve: NIL
oBrw Objeto de la ventana heredada de la clase tWAbreBrow o del objeto del browse que se utiliza, si no se envía utiliza el de la ventana principal del módulo (::oWnd)
lDlg Indica si la captura será por diálogo (.t.) o sobre el browse (.f.), si no se envía utiliza lo indicado en ::lDlg
lDlg Indica si la captura será por diálogo (.t.) o sobre el browse (.f.), si no se envía utiliza lo indicado en ::lDlg

dbAppend()
dbAppend( xArchivo ) (opcional para uso general)
Agrega un registro en blanco, ya sea reciclando los registros eliminados o agregando un registro en blanco, esta opción puede ser utilizada para otros archivos siempre y cuando se indique el archivo que se desee afectar.
Devuelve: Valor lógico que indica si se agregó o no el registro
xArchivo Indica el alias al que se le agregará el registro en blanco, si no se envía, se utiliza el archivo principal del módulo, el archivo puede ser indicado enviando el alias del archivo o un objeto qDbf

dbDelete( xVal0, xVal1 )
dbDelete( xVal0, xVal1, cAlias ) (opcional para uso general)

Borra todos los registros dentro de un rango ORDSCOPE(), esta opción puede ser utilizada para otros archivos siempre y cuando se indique el alias del archivo afectado.
Devuelve: Un valor lógico que indica si se terminó correctamente el proceso de borrado.
xVal0 Indica el valor inicial que será utilizado en ordScope( 0, xVal0 ), si se omite, se utilizará el filtro ordScope() que se tenga activo en ese momento, o todo el archivo si fuese el caso.
xVal1 Indica el valor final que será utilizado en el ordScope( 1, sVal1 ), si se omite, se utilizará el valor que se haya indicado en xVal0, siempre y cuando se haya indicado, en caso contrario se borrará todo el archivo
cAlias Indica el álias que será utilizado para realizar el borrado de registos, si no se envía se utilizará el archivo principal del módulo

catalogo( cAlias, cTexto, nSeguridad, oWnd, cBotones )
catalogoSDI( cAlias, cTexto, nSeguridad, oWnd, cBotones )

Crea un módulo ABC (Altas, Bajas y Cambios) simple de un archivo. El uso de estos métodos es ser llamados desde el método del módulo directamente, por ejemplo:

   miAbcDbf():new( "datos", oWnd )

sería llamado así:

   method datos( cAlias, oWnd) inline ::catalogo( cAlias, "Mis datos",, oWnd )

La difrencia entre catalogo() y catalogoSDI es que catalogo() crea el módulo en una ventana MDI hija, mientras que catalogoSDI() crea el módulo en una ventana SDI independiente.
Devuelve: NIL
cAlias Alias del archivo que se va a procesar
cTexto Texto utilizado en el título de la ventana y para la opción del menú
nDerechos Opción utilizada para la seguridad, está pensado para trabajar con un objeto de la qPassword, el cual debe asignarse a la variable oUsr, si no se asigna, siempre será verdadero
oWnd Ventana a la que pertenece la nueva ventana
cBotones Si se envía "I" agrega un botón de impresión, el cual imprime un reporte sencillo de lo mostrado en el browse

validaClave( xClave, cIndice, lNuevo, cTxt )
validaClave( xClave, cIndice, lNuevo, cTxt, cAlias ) (opcional para uso general)

Revisa que no exista una clave duplicada con la clave capturada sobre el índice indicado.
Devuelve: Valor lógico que indica que no existe duplicidad de un registro con la misma clave
xClave Clave para revisar si existe duplicidad, no es forzoso que sea de un solo campo, se revisa contra la llave del índice, así que puede contener cualquier expresión válida que concuerde con el índice
cIndice Indice sobre el cual se realiza la busqueda, si no se envía, se realizará sobre el índice actual
lNuevo Indica si se trata de un registro nuevo o de una modificacion, si no se envía se considera como una modificación
cTxt Texto que se mostrará en caso de encontrar un registro con la misma clave, si no se envía se utiliza el mensaje predefinido
cAlias Archivo sobre el cual se realiza la busqueda, si no se envía, se realiza sobre el archivo principal del módulo

refrescaBrowse( lRet )
refrescaBrowse( lRet, oBrw ) (opcional para uso general)

Método utilizado para refrescar el browse en la captura líneal.
Devuelve: NIL
lRet Indica si la captura fué realizada con exito (.t.) o fue cancelada (.f.)
oBrw Ventana heredada de tWAbreBrow o browse sobre el cual se realiza la captura, si no se envía se utiliza la ventana del módulo principal (::oWnd)


Ejemplo

#INCLUDE "fivewin.ch"
#INCLUDE "qSoft.ch"

function main
   local oWnd
   local aArray := {}
   define window oWnd menu oMenu() mdi
   activate window oWnd
return nil

function oMenu( bMenu )
   local oMenu
   local oWnd := wndMain()
   menu oMenu
      IF bMenu <> nil
         eval( bMenu )
      ENDIF
      menuItem "&Nombres"  action miAbcMaster():new( "nombres", .t., oWnd )                  // Abre el archivo nombres
      menuItem "&Dias"     action miAbcMaster():catalogo( "dias", "Días",, oWnd )            // Abre el archivo dias en una opción simple de mantenimiento
      menuItem "&Mascotas" action miAbcMaster():new( { "mascotas", "nombres" }, oWnd  )      // Abre el archivo mascotas y el archivo nombres lo abre si no está abierto como auxiliar en otra opción
      menuItem "&Empleos"  action miAbcMaster():new( { "empleos", { "nombres", .f., "dbfcdx" }, "A", date(), 8 ) // Abre el archivo empleos y el archivo nombres lo abre si no está abierto como auxiliar en otra opción
   endMenu
return oMenu

class miAbcMaster from abcMaster
   method menuBotton // Función que crea un menú personalizado, la usamos en el método nombres()

   // Ejemplo para captura en línea
   method nombres
   method nombres_click

   // Ejemplo para captura en diálogo
   method mascotas
   method mascotas_click

   // Ejemplo para proceso sin ventana
   method empleos
endClass

method menuButton( cTitulo ) class abcMaster
   local oBar
   define buttonBar oBar 3D top of oWnd
   oBar:bMMoved   := { || cursorHand() }
   oBar:bRClicked := { | nRow, nCol, nFlags | oWnd:showMenu( nRow, nCol, nFlags, oBar ) }
   menuItem cTitulo
      menu
         menuItem button "&Agregar"   OF oBar STRING btnBmp_abc( "nuevo"  )  noborder action ::mant_add()
         menuItem button "&Modificar" OF oBar STRING btnBmp_abc( "editar" )  noborder action ::mant_edit()
         menuItem button "&Eliminar"  OF oBar STRING btnBmp_abc( "borrar" )  noborder action ::mant_delete()
         separator
         menuItem button "A&lerta"    OF oBar STRING btnBmp_abc( "mensaje" ) noborder action msgInfo( "Alerta" )
      endMenu
return nil

method nombres( cAlias, lValor, oWnd ) class miAbcMaster
   local this := self
   msgInfo( lValor ) // .t.
   define window browse ::oWnd alias cAlias ;
      menubar oMenu( {|| this:menuButton( "&Nombres" ) } ) title " Nombres" ;
      head "Código", "Nombre", "Apellido" ;
      fields ( cAlias )->codigo, ( cAlias )->nombre, ( cAlias )->apellido ;
      on dblClick this:nombres_click( lNew ) ;
      index "Código", "Nombre", "Apellido" to "codigo", "nombre", "apellido" set "apellido" seek upper( xSeek )
return nil

method nombres_click( lNew ) class miAbcMaster
   local xVar
   local lRet := .f.
   switch ::oWnd:nColAct
      case 1
         IF lNew
            xVar := ( ::cAlias )->codigo
            lRet := ::oWnd:lEditCol( 1, @xVar, "@!", { | oGet | ::validaClave( oGet:varGet(), "codigo", .t. ) } ) .AND. ( ::cAlias )->( rLock() )
            if lRet
               ( ::cAlias )->codigo := xVar
            endIf
         ENDIF
         exit
      case 2
         xVar := ( ::cAlias )->nombre
         lRet := ::oWnd:lEditCol( 2, @xVar, "@!" ) .AND. ( ::cAlias )->( rLock() )
         if lRet
            ( ::cAlias )->nombre := xVar
         endIf
         exit
      case 3
         xVar := ( ::cAlias )->apellido
         lRet := ::oWnd:lEditCol( 3, @xVar, "@!" ) .AND. ( ::cAlias )->( rLock() )
         if lRet
            ( ::cAlias )->apellido := xVar
         endIf
         exit
   end switch
   ::refrescaBrowse( lRet )   // Refresca el browse
return lRet

method mascotas( cAlias, oWnd ) class miAbcMaster
   local cNombres := ::oEntorno:alias( "nombres" )
   local this     := self
   set relation dueno into ( cNombres )
   define window browse ::oWnd alias cAlias ;
      menubar oMenu( {|| this:mant_menuButton( "&Mascotas", ::oWnd,, "I" ) } ) title " Mascotas" ;
      head "Nombre", "Raza", "Dueño" ;
      fields ( cAlias )->Nombre, ( cAlias )->raza, ( cNombres )->dueno ;
      on dblClick this:mascotas_click( lModif, lNew ) ;
      index "Nombre", "Dueño" to "nombre", "dueno" set "nombre" seek upper( xSeek )
   ::lDlg := .t.  // Indica que la captura es en diálogo
return nil

method mascotas_click( lModif, lNew ) class miAbcMaster
   local xRet, oDlg
   local oDbf := qDbf():new( ::cAlias )
   IF lNew
      oDbf:blank()
   ENDIF
   define dialog oDlg resource "mascotas"
   redefine get oDbf:nombre of oDlg id 101 picture "@!" valid ::validaClave( oDbf:nombre, "nombre", lNew )  // no permite nombres repetidos, pero si permite el cambiar de nombre
   redefine get oDbf:raza   of oDlg id 102 picture "@!"
   redefine get oDbf:dueno  of oDlg id 103 picture "@!"
   redefine button id 1 action if( lValidaAceptar( oDlg ), ( xRet := oDbf, oDlg:end() ), )
   redefine button id 2 action oDlg:end() cancel
return xRet // Si se acepta regresa el objeto, si se cancela devuelve nil

method empleos( cAlias, cPar1, dPar2, nPar3 ) class miAbcMaster
   ? cAlias, cPar1, dPar2, nPar3, ::oEntorno:alias( "nombres" )
return nil