Rutinas de archivo (formulario)
Hay tres formas principales de crear una rutina de archivo, utilizando las funciones AxCadastro, MBrowse o el estándar MVC.
Para la rutinas muy simples, donde toda la regla se encuentra dentro del diccionario de datos (SX3), se utiliza principalmente la rutina AxCadastro, que ofrece el framework necesario para el listado de los registros existentes (browse) y para su mantenimiento (visualización, inclusión, modificación y borrado).
En las rutinas más complejas, donde es interesante hacer varios controles de validación o utilizar más de una tabla (archivo principal-secundario, etc.), se utilizaba con frecuencia la rutina MBrowse, acompañada de las funciones responsables por cada operación.
El MVC se creó con el objetivo de simplificar este proceso, pudiendo utilizarse para cualquier tipo de rutina de archivo, ya sea simple o compleja. Además, el MVC ofrece una serie de ventajas de desempeño y usabilidad. De esta manera, se recomienda fuertemente su utilización en todas las nuevas rutinas de archivo.
Para más detalles y ejemplos de cómo construir una rutina en MVC, consulte la documentación existente en el TDN (busque por "ADVPL utilizando MVC").
Las rutinas de archivo generalmente se dividen en secciones, por ejemplo:
- Browse: En esta sección definimos el Alias (tabla) principal del formulario, la descripción de la rutina y otras opciones referentes al modo de visualización de los registros de la tabla, como por ejemplo: Filtros, preguntas, leyenda y opciones del estándar de operación disponibles.
- Menú de operaciones de la rutina: En esta sección definimos cuáles operaciones estarán disponibles para el usuario final. Las opciones más comunes son: Búsqueda, inclusión, modificación, visualización y borrado.
- Regla de negocio: Aquí definimos la estructura del formulario, cómo se relacionan sus tablas y cuáles son las funciones de validación para cada estructura, así como para todo el formulario. Tenga por costumbre incluir la validación correcta en cada una de las estructuras.
- Interfaz: En esta sección definimos los elementos gráficos que se utilizarán en el formulario y la relación que estos elementos tienen con la sección de regla de negocio. Aunque esta sección permite otros elementos gráficos, evite utilizarlos, porque durante los constantes cambios de versión y release de la línea Microsiga Protheus puede haber necesidad de retrabajo, lo que no ocurre para los elementos del Framework mencionados. Otra razón para no utilizar otros elementos gráficos está en la dificultad de personalización: recuerde que el mayor argumento de venta de la Línea Microsiga Protheus es la flexibilidad, de esta manera, una interfaz no flexible causará retrabajo solicitado por el cliente.
- Validación: En esta sección deben insertarse todas las funciones utilizadas para validación de los formularios.
- Grabación (opcional): Para las rutinas que utilizan AxCadastro, la grabación se realiza automáticamente, al igual que en las rutinas realizadas en MVC. Sin embargo, si es necesario agregar detalles antes o después de la grabación estándar del MVC, se ponen a disposición puntos que el desarrollador puede incrementar a las necesidades de grabación.
- Otras funciones del formulario (opcional): Esta sección abarca las funciones complementarias de la interfaz (VIEW), como por ejemplo, nuevos botones en la barra de herramientas u otros componentes específicos de la interfaz.
Rutinas de consulta
No hay un estándar para la estructura de los programas de consulta, proporcionando al desarrollador libertad para utilizar su creatividad. Sin embargo, algunas reglas deben seguirse:
Las consultas de formulario deben seguir el estándar de los programas de formulario, ejemplo para aquellas aplicadas en la grabación y validación.
Las interfaces de consulta deben ser rápidas, por lo tanto si hay procedimientos que demandan tiempo, pero son opcionales, estas deben activarse bajo demanda, por medio de un botón en la interfaz y tener una regla de procesamiento para indicar al usuario una expectativa de tiempo, así como también, la opción para anular la operación.
Debe evitarse el uso del objeto TreeView y solo podrá utilizarse si el número de elementos definidos en todos sus niveles es pequeño y no tiende a crecer de acuerdo con la base de datos.
Las consultas que exporten datos deben seguir las reglas de privilegio del archivo de usuarios.
Si la consulta maneja muchos datos, es preferible tercerizar esta responsabilidad para la base de datos, valiéndose de queries SQL, cuando estén disponibles. Recuerde que debe respetar los estándares SQL del Microsiga Protheus.
<enlace para el estándar SQL del Microsiga Protheus>
Rutinas de informes
Los programas de informes se dividen en tres secciones y dos tipos:
- Personalizables:
- Sección de inicialización.
- Sección de definición del informe.
- Sección de impresión.
- No personalizables o personalizados:
- Sección de definición del informe.
- Sección de impresión.
Un informe de la línea Microsiga Protheus puede ser de dos tipos (Personalizables y No personalizables), de acuerdo con la necesidad de impresión. Sin embargo, los no personalizables deben utilizarse únicamente para el mantenimiento del legado y en desarrollos específicos, en los que el cliente no puede modificar el layout del informe, como los informes legales ( DANFe, P1/P2/P8/P9, Diario general, Razón contable, etc.).
Rutinas de informes personalizables
En la sección de inicialización de los informes personalizables, función CustomizableReport() , debe verificarse si el informe tiene el tipo personalizable disponible y realizar las llamadas apropiadas, de acuerdo con el caso.
En la sección de definición del informe, función ReportDef(), deben crearse los componentes de impresión, las secciones y las celdas, los totalizadores y otros componentes que el usuario podrá personalizar en el informe.
Los datos suministrados en esta sección se utilizarán para para el montaje de la interfaz estándar del informe, línea del ejemplo.
En la sección de impresión, función ReportPrint(), debe controlarse el flujo del informe, ejecutar las query´s, filtros y el orden definido por los parámetros del informe.
Los parámetros del informe deben crearse utilizando el Archivo de preguntas del diccionario de datos de la Línea Microsiga Protheus (SX1). La no utilización del diccionario puede crear problemas en las programaciones en agenda del informe, así como también, en otras características del producto.
// CustomizableReport.prw
#INCLUDE "TOTVS.CH"
Function CustomizableReport()
Local oReport := Nil
If FindFunction("TRepInUse") .And. TRepInUse()
// Interfaz de impresión
oReport := ReportDef()
oReport:PrintDialog()
Else
R3()
EndIf
Return
Static Function ReportDef()
Local oReport
//---------------------------------------
// Creación del componente de impresión
//---------------------------------------
// TReport():New
// ExpC1 : Nombre del informe
// ExpC2 : Título
// ExpC3 : Pregunta
// ExpB4 : Bloque de código que se ejecutará en la confirmación de la impresión
// ExpC5 : Descripción
//---------------------------------------
oReport := TReport():New( */Nombre del informe/* , */Título del informe/, */*Pregunta/, {|oReport| ReportPrint(oReport)}, */*Descripción del informe/* )
oReport:SetTotalInLine(.F.)
Pregunta(oReport:uParam, .F.)
//---------------------------------------
// Creación de la sección utilizada por el informe
/ /
// TRSection():New
// ExpO1 : Objeto TReport que pertenece a la sección
// ExpC2 : Descripción de la sección
// ExpA3 : Array con las tablas utilizadas por la sección. La primera tabla se considerará como principal para la sección.
// ExpA4 : Array con las órdenes del informe
// ExpL5 : Carga campos del SX3 como celdas
// Preconfigurado : False
// ExpL6 : Carga órdenes del Sindex
// Preconfigurado : False
//---------------------------------------
//---------------------------------------
// Creación de las celdas de la sección del informe
/ /
// TRCell():New
// ExpO1 : Objeto TSection que pertenece a la sección
// ExpC2 : Nombre de la celda del informe. Se consultará el SX3
// ExpC3 : Nombre de la tabla de referencia de la celda
// ExpC4 : Título de la fórmula
// Preconfigurado : X3Titulo()
// ExpC5 : Picture
// Preconfigurado : X3_PICTURE
// ExpC6 : Tamaño
// Preconfigurado : X3_TAMANHO
// ExpL7 : Informe si el tamaño está en pixel
// Preconfigurado : False
// ExpB8 : Bloque de código para impresión.
// Preconfigurado : ExpC2
//---------------------------------------
oSection := TRSection():New({}/*oReport/, */*Nombre de la sección/, */{Tablas de la sección}/, /{Array con las órdenes del informe}/, */Campos del SX3/, */*Campos del SIX/{*})
oSection:SetTotalInLine(.F.)
TRCell():New({}/*oSection/, */*X3_CAMPO/, */*Tabla/, */*Título/, */*Picture/, */*Tamaño/, */*lPixel/, */{|| code-block de impresión }/)
TRFunction():New(oSection:Cell({}/*X3_CAMPO/), */*cID/, "SUM", */*oBreak/, */*cTitle/, */*cPicture/, */*uFormula/, */*lEndSection/, .F./lEndReport/, */*lEndPage/{*})
oSection2 := TRSection():New(oSection, */Nombre de la sección/, */{Tablas de la sección}/, /{Array con las órdenes del informe}/, */Campos del SX3/, */*Campos del SIX/{*})
oSection2:SetTotalInLine(.F.)
TRCell():New({}/*oSection2/, */*X3_CAMPO/, */*Tabla/, */*Título/, */*Picture/, */*Tamaño/, */*lPixel/, */{|| code-block de impresión }/)
TRFunction():New(oSection2:Cell({}/*X3_CAMPO/) ,/* cID /, "SUM", */oBreak/, */*cTitle/, */*cPicture/, */*uFormula/, */*lEndSection/, .F./lEndReport/, */*lEndPage/{*})
Return oReport
Static Function ReportPrint(oReport)
Local cAliasQry := ""
Local lQuery := .F.
#IFNDEF TOP
Local cCondicao := ""
ENDIF
//---------------------------------------
// Transforma parámetros Range en expresión SQL
//---------------------------------------
MakeSQLExpr(oReport:uParam)
//---------------------------------------
// Filtrado del informe
//---------------------------------------
#IFDEF TOP
//---------------------------------------
// Query del informe de la sección 1
//---------------------------------------
lQuery := .T.
oReport:Section(1):BeginQuery()
BeginSQL Alias cAliasQry
SELECT *
FROM %Table:% XXX,%Table:% YYY
WHERE XXX_FILIAL = %XFilial:XXX% AND
XXX.%NotDel% AND
YYY_FILIAL = %XFilial:XXX% AND
YYY.%NotDel%
ORDER BY DAK_COD,DAK_SEQCAR,DAI_SEQUEN,DAI_PEDIDO
EndSQL
//---------------------------------------
// Método EndQuery ( Classe TRSection )
// Prepara el informe para ejecutar el Embedded SQL.
// ExpA1 : Array con los parámetros del tipo Range
//---------------------------------------
oReport:Section(1):EndQuery({}/*Array con los parámetros del tipo Range/{*})
#ELSE
DbSelectArea(cAliasQry)
DbSetOrder(1)
cCondicao := "XXX_FILIAL == '" + XFilial("XXX") + "' "
oReport:Section(1):SetFilter(cCondicao,IndexKey())
oReport:Section(1):Section(1):SetRelation(/{|| cExpSeek }/,{}/*cAlias/,/Order/,/lSeek/{*})
oReport:Section(1):Section(1):SetParentFilter(/{|| lQuery}/)
ENDIF
//---------------------------------------
// Método TrPosition()
// Marca en un registro de otra tabla. La marcación se
// realizará antes de la impresión de cada línea del informe.
// ExpO1 : Objeto Report de la sección
// ExpC2 : Alias de la tabla
// ExpX3 : Orden o NickName de búsqueda
// ExpX4 : String o Bloque de código para búsqueda. El string será macroeje-
// cutado.
//---------------------------------------
TRPosition():New(oReport:Section(1), */cAlias/, */*nOrder/, */{|| cSeek }/)
//---------------------------------------
// Inicio de la impresión del flujo del informe
//---------------------------------------
oReport:SetMeter(cAliasQry->(LastRec()))
If Mod1
oReport:Section(1):Print()
Else
DbSelectArea(cAliasQry)
While !oReport:Cancel() .And. !(cAliasQry)->(Eof())
oReport:Section(1):Section(1):Init()
oReport:Section(1):Section(1):PrintLine()
oReport:Section(1):Section(1):Finish()
DbSelectArea(cAliasDAI)
DbSkip()
oReport:IncMeter()
EndDo
oReport:Section(1):Finish()
oReport:Section(1):SetPageBreak(.T.)
EndIf
Return
Para más detalles y ejemplos de cómo construir una rutina en MVC, consulte la documentación existente en el TDN (busque por "TReport").
Rutinas de informes no personalizables
Los informes no personalizables pueden crearse utilizándose la TMsPrinter para los informes gráficos y el par de funciones SetPrint/SetDefault para los no gráficos.
Independientemente del método utilizado, los informes de este tipo deben utilizar el Archivo de preguntas del diccionario de datos de la Línea Microsiga Protheus (SX1).
A continuación, un ejemplo de una estructura de informe no gráfico:
// NoCustomizableReport.prw
#INCLUDE "TOTVS.CH"
#DEFINE CHRCOMP If(aReturn[4]==1,15,18)
Function NoCustomizableReport()
//----------------------------------------------------------------------------
// Define variables
//----------------------------------------------------------------------------
Local cTitulo := "Titulo del informe" // Titulo del informe
Local cDesc1 := "Descripción 1" // Descripción 1
Local cDesc2 := "Descripción 1" // Descripción 2
Local cDesc3 := "Descripción 1" // Descripción 3
Local cString := "" // Alias utilizado en el filtrado
Local lDic := .F. // Habilita/Deshabilita Diccionario
Local lComp := .T. // Habilita/Deshabilita el formato comprimido/expandido
Local lFiltro := .T. // Habilita/Deshabilita el filtro
Local cFile := "" // Nombre del archivo utilizado en el Spool
Local cProgName := "" // nombre del programa
Private Tamaño := "G" // P/M/G
Private Límite := 220 // 80/132/220
Private aOrdem := {} // Orden del informe
Private cPerg := "" // Pregunta del informe
Private aReturn := { "A rayas", 1, "Administración", 1, 2, 1, "", 1 }
//[1] Reservado para formulario
//[2] Reservado para N§ de copias
//[3] Destinatario
//[4] Formato => 1-Comprimido 2-Normal
//[5] Medios => 1-Disco 2-Impresora
//[6] Puerto o archivo 1-LPT1... 4-COM1...
//[7] Expresión del filtro
//[8] Orden que se seleccionará
//[9]..[10]..[n] Campos que se procesarán (si los hay)
Private lEnd := .F. // Control de anulación del informe
Private m_pag := 1 // Contador de páginas
Private nLastKey := 0 // Controla la anulación de la SetPrint y SetDefault
//----------------------------------------------------------------------------
// Verifica las preguntas seleccionadas
//----------------------------------------------------------------------------
//Pregunta(cPerg,.F.)//----------------------------------------------------------------------------
// Envía al SetPrinter
//----------------------------------------------------------------------------
cFile:=SetPrint( cString,cFile, cPerg, @cTitulo, cDesc1, cDesc2, cDesc3, lDic, aOrdem,;
lComp, Tamaño,, lFiltro)
If nLastKey==27
DbSelectArea(cString)
DbSetOrder(1)
Set Filter to
Return
EndIf
SetDefault(aReturn,cString)
If nLastKey==27
DbSelectArea(cString)
DbSetOrder(1)
Set Filter to
Return
EndIf
RptStatus({|lEnd| ImpDet(@lEnd, cFile, cString, cProgName, cTitulo)}, cTitulo)
Return .T.
Static Function ImpDet(lEnd, cFile, cString, cProgName, cTitulo)
Local li := 100 // Contador de líneas
Local lImp := .F. // Indica si algo se imprimió
Local cbCont := 0 // Número de registros procesados
Local cbText := "" // Mensaje del pie de página
/ /
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
@01234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
Local cCabec1 := ""
Local cCabec2 : ""
DbSelectArea(cString)
SetRegua(LastRec())
DbSetOrder(1)
DbSeek(XFilial())
While !EOF() .And. XFilial(cString) == MV_PAR01
lImp := .T.
If lEnd
@ Prow()+1,001 PSay "ANULADO POR EL OPERADOR"
Exit
EndIf
If ( li > 58 )
li := cabec(cTitulo, cCabec1, cCabec2, cProgName, Tamaño, CHRCOMP)
li++
EndIf
DbSelectArea(cString)
DbSkip()
cbCont++
IncRegua()
EndDo
If lImp
Roda(cbCont, cbText, Tamaño)
EndIf
Set Device To Screen
Set Printer To
If ( aReturn[5] = 1 )
DbCommitAll()
OurSpool(cFile)
EndIf
Ms_Flush()
Return .T.
Rutinas de procesamiento
Durante los últimos años esta fue la estructura que más evolucionó en la Línea Microsiga Protheus. Por ahora, nos concentraremos únicamente en el último estándar.
Los programas de procesamiento tienen una estructura simple, formada por tres elementos:
- Setup.
- Procesamiento.
- Programación en agenda.
En el Setup se muestra la interfaz estándar suministrada por la función FWGridProcess. Le corresponde al desarrollador completar correctamente los parámetros de la rutina.
El montaje de esta interfaz se realiza con base en el diccionario de datos y puede variar según sea el caso. La presentación de los parámetros del diccionario de datos – SX6 y las tablas estándares – SX5 están vinculados al registro de la rutina en el TOTVSPARAM, responsable por la sincronización de la rutinas y los parámetros involucrados.
Es posible definir hasta cinco reglas de procesamiento, sin embargo, se recomienda no utilizar más de dos reglas.
En la sección de procesamiento, función DoTask(), se muestra el flujo de control de la rutina, la actualización de las reglas y el control de anulación del procesamiento.
La sección de programación en agenda, función SchedDef(), contiene los datos que se informan al Schedule de la Línea Microsiga Protheus, para la correcta programación en agenda de la rutina de procesamiento.
// ProcessingRoutine.prw
#INCLUDE "TOTVS.CH"
Function ProcessingRoutine()
oGrid:=FWGridProcess():New( "MATA330", "prueba", "prueba del procesamiento", {|lEnd| DoTask(oGrid,@lEnd)}, "MTA330", "u_testeba2")
oGrid:SetMeters(2)
oGrid:SetThreadGrid(5)
oGrid:Activate()
If oGrid:IsFinished()
Alert("Final")
Else
Alert("Final con error.")
EndIf
Return
Static Function DoTask(oGrid,lEnd)
Local nX := 0
Local nY := 0
oGrid:SetMaxMeter(4, 1, "prueba1")
fuera nX := 1 to 4
oGrid:SetMaxMeter(10, 2, "prueba2")
For nY := 1 To 10
If !oGrid:CallExecute("callexecute is load", If(nX==5 .And. nY==10, 0, 1))
lEnd := .T.
EndIf
oGrid:SetIncMeter(2)
If lEnd
Exit
EndIf
Next nY
If lEnd
Exit
EndIf
oGrid:SetIncMeter(1)
Next nX
Return
Static Function SchedDef()
// aReturn[1] - Tipo
// aReturn[2] - Pregunta
// aReturn[3] - Alias
// aReturn[4] - Array de orden
// aReturn[5] - Título
Return { "R", "PARAMDEF", "SA1", {"Código", "Nombre"}, "Título" }