Árvore de páginas

Você está vendo a versão antiga da página. Ver a versão atual.

Comparar com o atual Ver Histórico da Página

Versão 1 Próxima »

Registros

Variables de transporte de campo

Cuando una variable de una rutina transporte algún valor de campo, es importante recordar que su tamaño siempre debe respetar el tamaño del campo. Este caso es común cuando se recibe un contenido de campo de una variable de memoria o de algún GET elaborado específicamente para recibir un valor de campo y de arrays que almacenan un modelo de base de datos.
// VariableTransportFieldSample.prw
nTamPar := TamSX3("E1_PARCELA")[01]
cParcela := PadR(SuperGetMV("MV_1DUP"), nTamPar)

Protección contra desmarcación

En funciones y método se indica el uso de la grabación y restauración de marcación de registros de todas las tablas utilizadas dentro de la función o método de utilidad.
// ProtectionAgainstDislodgmentSample.prw
aArea := GetArea() // Graba el puntero de registro del área activa
If SB1->(DbSeek(XFilial("SB1") + cVar))
// Encontré el registro, por lo tanto hago lo que es necesario
Else
// Genero el retorno negativo de la ejecución del procesamiento.
EndIf
RestArea(aArea) // Restaura el Área almacenada en la Variable aArea

Marcación de registros

DbSeek()

Al ejecutar un DbSeek() verifique siempre si se localizó el registro, por ejemplo:
// DbSeekSample.prw
If SB1->(DbSeek(XFilial("SB1") + cVar))
// Encontré el registro, por lo tanto hago lo que es necesario.
Else
// Genero el retorno negativo de la ejecución del procesamiento.
EndIf
Aunque sea obvia la existencia del registro, haga la prueba para evitar cualquier interrupción indeseada del programa o que se corrompa la integridad de los datos.
La no verificación de la marcación, en gran parte de los casos, no genera error o ningún problema aparente. La principal consecuencia de la no verificación es el uso de información equivocada. Por ejemplo, suponiendo que la rutina esté calculando un contador cualquiera, si no se confirma que el DbSeek encontró el registro correcto, de todas formas se realizará el cálculo, sin embargo, el resultado del cálculo estará equivocado, pues se utilizó información equivocada. Errores de integridad de este tipo son muy difíciles de encontrar y generalmente sólo ocurren en un determinado escenario y ese escenario generalmente no está en la máquina del programador o analista que está verificando la situación.

SoftSeek() y búsqueda con claves parciales

La función DbSeek() tiene la opción "SoftSeek", es decir, determina si se utilizará una búsqueda relativa durante una búsqueda en una base de datos. Si no se encuentra ninguna correspondencia, el puntero de registro se quedará en el próximo registro del índice que tenga un valor más alto que la expresión utilizada en esta función. Esta opción debe utilizarse con la máxima atención, pues si está conectada, podrá localizar un registro equivocado. El mismo principio se aplica cuando una clave parcial se utilizada en el DbSeek sin el SoftSeek activado.

MsSeek()

La función MsSeek(), cuando se utiliza bien, puede disminuir mucho el I/O de disco cuando se ejecuta.
Su principal característica es realizar el cache del resultado del seek, tanto con relación al recno encontrado como con relación al contenido del registro.
El uso incorrecto del MsSeek puede crear una situación de lectura sucia no deseada.
<enlace para el tópico de lectura sucia>

Funciones de marcación de uso restringido

Las siguientes funciones tienen utilización restringida:
// FunctionsRestrictedSample.prw
DbGoTop()
DbSeek(XFilial("SA1"))
¡Importante!
La utilización de estas funciones, solo se justifica cuando se está utilizando algún filtro de lectura o un archivo temporal.
El DbSeek únicamente con la transferencia de XFilial() debe evitarse. Si la clave de búsqueda, incluye otros que no sean solamente la Sucursal, se libera el uso del DbSeek.

Funciones de búsqueda:

Las siguientes funciones son utilidades que sirven en los campos de validación e inicialización estándar del diccionario de datos. No se recomienda el uso de estas utilidades dentro de los códigos fuente, porque realizan tratamientos adicionales de protección e interpretación, que se utiliza dentro de un código fuente y puede sobrecargar innecesariamente el procesamiento.

Marque()

También podemos buscar una información en determinado campo utilizando la función Marque.
Ejemplo:
// PosicioneSample.prw
cDesc := Posicione("SB1", 1, XFilial("SB1") + cCodigo, "B1_DESC")
De esta manera, se realizará una búsqueda en el SB1, en el orden 1, clave de la búsqueda XFilial("SB1") + cCodigo y se retornará el contenido del campo "B1_DESC". Observe que esta función no restaura la marcación original del archivo objetivo (en el caso SB1).
Es necesario transferir la sucursal del archivo a la clave transferida como parámetro, en caso de que exista en la clave del índice.

Importante: El "Marque" es más recomendable utilizar en el diccionario de datos, porque puede existir alguna falla debido a que se utiliza Cache.

ExistCpo()

Retorna si determinada clave existe o no en el archivo.
Ejemplo:
// ExistCPOSample.prw
ExistCpo("SE1", M->EF_PREFIXO + M->EF_TITULO + M->EF_PARCELA, 1)
De esta manera, se realizará una búsqueda en el SE1, en el orden 1, clave: M->EF_PREFIXO+M->EF_TITULO+M->EF_PARCELA. Y retornará si la clave se encuentra o no (. (.T.) o (.F.)
No es necesario transferir la sucursal. Esta se incluirá automáticamente en la clave de búsqueda por la propia función.

Trabamientos / Bloqueos / Locks

Cuando trabajamos en un entorno multiusuario, es necesario un control de bloqueo de esta información para cuando esta se actualice o se borre. Este bloqueo serializa las operaciones, permitiendo que varios usuarios realicen las modificaciones en el mismo registro, pero no al mismo tiempo.

RecLock(cAlias, lAppend)

Tiene la función de crear un registro en blanco para incluir o bloquear el registro actual para edición, en este caso la función ejecuta un refresh del dato, buscando la información más actual en la base. Durante el período que el registro esté bloqueado los otros usuarios pueden accederlo apenas para consulta (vea más adelante – DeadLock - lectura sucia).
Si no es posible el bloqueo del registro, la función interactuará con el usuario, cuestionando si debe permanecer intentando el bloqueo o desistir de la operación.
Ejemplo:
Verifica si el registro existe en la tabla SA1:
// RecLockSample.prw
If !DbSeek(XFilial("SA1") + "000001")
// Si no existe, incluya un registro en blanco y lo bloquea
Reclock("SA1", .T.)
Else
// Bloquea el registro encontrado
Reclock("SA1", .F.)
EndIf

MSUnlock(cAlias)

Libera el registro creado o bloqueado por la RecLock.
Ejemplo:
Verifica si el registro existe en la tabla SA1:
// MsUnlockSample.prw
If !DbSeek(XFilial("SA1") + "000001")
// Si no existe, incluye un registro en blanco y lo bloquea
Reclock("SA1", .T.)
Else
// Bloquea el registro encontrado
Reclock("SA1", .F.)
EndIf
SA1->A1_SALDO := nNovoSaldo
MSUnlock("SA1")
La recomendación es que se utilicen únicamente las funciones RecLock y MsUnlock.

Funciones de trabamiento restringidas

No se recomienda el uso de las siguientes funciones para realizar trabamientos y destrabamientos:

  • DBRLock.
  • DBRUnlock.
  • MSRLock.
  • MSRUnlock.
  • DBUnlock.
  • DBUnlockAll.
  • MultLock(Alias, aChaves, nOrd).
  • SoftLock.
  • MSUnlockAll.
  • MSUnlockSoft.


Estas funciones de trabamiento disponibles solo podrán utilizarse con aprobación de la Ingeniería de Software.

Control de transacción

Cuando se trabaja en un entorno multiusuario, es necesario un control de concurrencia de las actualizaciones realizadas en la base de datos. Control de concurrencia es un método utilizado para garantizar que las transacciones se ejecuten de forma segura y que cumplan las reglas de Atomicidad, Consistencia, Aislamiento y Durabilidad. Una transacción es una unidad que preserva la consistencia. Por lo tanto, es necesario, que cualquier ordenamiento producido al procesarse un conjunto de transacciones concurrentes, sea computacionalmente equivalente a un ordenamiento que ejecute estas transacciones serialmente en algún orden. Se dice que un sistema que garantiza esta propiedad asegura la seriabilidad. La serialización de las operaciones reduce el desempeño de la aplicación, principalmente cuando se realiza en tablas de alta concurrencia.
La línea Microsiga Protheus dispone de un estándar para las operaciones de concurrencia, que tiene como objetivo reducir las ocurrencias de la serialización de la aplicación. Estas operaciones pueden dividirse en:

  • Lectura.
  • Bloqueo de Interfaz.
  • Bloqueo de procesamiento.
  • Bloqueo de transacción.

Lectura

La línea Microsiga Protheus adopta el nivel de aislamiento READ UNCOMMITTED. Este nivel de aislamiento permite lecturas sucias, donde la transacción corriente o las otras pueden visualizar los datos de las transacciones pendientes.
El nivel de aislamiento adoptado garantiza que las lecturas realizadas en la base de datos mediante la aplicación no realicen el bloqueo de registros y consecuentemente no serialicen la aplicación, manteniendo la previsibilidad del desempeño. Sin embargo, se debe tomar cuidado para garantizar la integridad de los datos.
Cuando se adoptan lecturas sucias debe prevenirse que en la actualizaciones, los datos se bloqueen antes de actualizarse o leerse para este fin. La transacción T2 tiene una línea modificada, pero aún no se finalizó. La transacción T1 lee los datos actualizados y los actualiza con base en los datos leídos. La transacción T1 finaliza la transacción y la transacción T2 realiza un Roll Back de los datos. A partir de este momento los datos ya no están íntegros.
Para evitar esta situación, el desarrollador debe indicar la aplicación en el momento del bloqueo, esto se realiza utilizando la función RecLock, de acuerdo con lo siguiente:
// ReadSample.prw
Begin Transaction
RecLock("SB2")
nQtd := SB2->B2_QATU
If nQtd + nSoma <= 0
SB2->B2_QATU := 0
Else
SB2->B2_QATU := nQtd - nSoma
EndIf
MsUnlock()
End Transaction

La función RecLock informa al Framework de la aplicación para realizar el bloqueo del registro marcado. El Framework realiza el bloqueo y actualiza la lectura del registro corriente de la tabla. Las otras transacciones del sistema continúan leyendo el registro bloqueado, pero si intentan bloquear el registro tienen que esperar a que la transacción anterior lo libere, lo que solamente podrá realizarse al final de la transacción.
Otra prevención que debe considerarse en las lecturas sucias son las actualizaciones parciales, resultantes del vínculo de dependencia directa de las tablas. Para un formulario del tipo Master/Detail (encabezado e ítem), la transacción de inclusión de este formulario permite que el registro de la tabla Master exista sin los registros o con una cantidad parcial de los registros de la tabla Detail, ya que es la única forma de garantizar la dependencia de la clave extranjera. Si una rutina de actualización realiza el bloqueo únicamente en los registros Detail no hay cómo garantizar que se consideren todos los registros. Por lo tanto, es necesario bloquear el registro Master, antes del inicio del bloqueo de los registros Detail en cualquier actualización de los registros Detail. Al bloquear el registro Master, se garantiza que ningún registro de la tabla Detail, vinculado al Master, esté en actualización o inclusión. Es un hecho que dependiendo de la rutina desarrollada, es obligatorio iniciar la lectura por la tabla Detail y no por la Master. En estas situaciones debe garantizarse que, antes de cualquier actualización del registro Detail, se bloquee el registro Master antes del registro Detail.
En el ejemplo se mencionan únicamente dos tablas, pero independientemente de la cantidad de tablas involucradas en el formulario, debe bloquearse el registro Master.
Por último, existe la prevención del momento del COMMIT. La arquitectura del Server Application de la línea Microsiga Protheus hace posible que el momento del COMMIT sea automático, de acuerdo con la lógica del programa, además de poder utilizar más de un canal de comunicación con el SGBD para lectura. Si el Server Application utiliza más de un canal para lectura, es posible deducir que los otros canales de lectura solamente "visualizarán" los registros, cuyos datos pasaron por el COMMIT. Para prevenir cualquier tipo de problema en la lectura por medio de querys, es necesario forzar el COMMIT de los datos, como se muestra a continuación.
// CommitSample.prw
SPED050->(DbCommit())
BeginSQL Alias cAlias
SELECT R_E_C_N_O_
FROM SPED050
WHERE ID_ENT = %Exp:cIdEnt% AND
(STATUS = 1 OR STATUS = 2 OR STATUSDPEC = 1) AND
STATUSDPEC <> 5 AND
%NOTDEL%
EndSQL
El nivel de aislamiento READ UNCOMMITTED es el más aconsejable para la aplicación del tipo de aplicación OLTP (On Line Transaction Process), pero corresponde al desarrollador el control del formato de lectura y el bloqueo del registro para actualización.

Bloqueo de interfaz

En un entorno multiusuario la probabilidad de que dos usuarios modifiquen el mismo registro de un archivo, simultáneamente, es muy alta. Por esta razón, es necesario impedir el acceso de uno de ellos.
El Framework de la línea Microsiga Protheus tiene la función Softlock para esta finalidad. Esta función debe utilizarse para bloquear un determinado registro, en actualización, durante las operaciones de interfaz, como se muestra a continuación:
// SoftLockSample.prw
If SoftLock(cAlias)
RegToMemory(cAlias,.F.,lVirtual)
Define MsDialog ___oDlg Of oFather:oWnd From 0, 0 To 0, 0 Pixel STYLE nOR( WS_VISIBLE, WS_POPUP )
aPosEnch := {,,,}
oEnc01:= MsMGet():New( cAlias, nReg, nOpc, ,"CRA", STR0004, aAcho, aPosEnch ,aCpos,,,,cTudoOk,___oDlg ,,lVirtual,.F.,,,,,,,,cTela) //"¿Cuántas modificaciones?"
oEnc01:oBox:align := CONTROL_ALIGN_ALLCLIENT
bEndDlg := {|lOk| If(lOk:=___oDlg:End(), nOpcA:=1, nOpcA:=3), lOk}
___oDlg:nWidth := aDim[4]-aDim[2]
Activate MsDialog __oDlg On Init ( FaMyBar(__oDlg,{|| If(Obligatorio(aGets, aTela) .And. Eval(bOk) .And. Eval(bOk2, nOpc), Eval(bEndDlg), (nOpcA:=3, .F.))},{|| nOpcA := 3, ___oDlg:End()}, aButtons), ___oDlg:Move(aDim[1], aDim[2], aDim[4]-aDim[2], aDim[3]-aDim[1]) )
(cAlias)->(MsGoTo(nReg))
If nOpcA == 1
Begin Transaction
RecLock(cAlias,.F.)
For nX := 1 TO FCount()
FieldPut(nX, M->&(EVAL(bCampo, nX)))
Next nX
If Type("aMemos") == "A"
For nX := 1 to Len(aCampos)
cVar := aMemos[nX][2]
cVar1:= aMemos[nX][1]
MSMM(&cVar1, TamSx3(aMemos[nX][2])[1], , &cVar, 1, , , cAlias, aMemos[nX][1], cAliasMemo)
Next nX
EndIf
End Transaction
EndIf

La función SoftLock garantiza que la interfaz solamente se elabore para el primer usuario que hizo el intento de actualización o borrado. Mientras la transacción no se aborte o concluya, el registro permanecerá bloqueado para los otros. Observe que aunque la función SoftLock realice un bloqueo, el uso de la función RecLock continúa siendo necesario para la transacción.

Bloqueo de procesamiento

En rutinas de procesamiento en entornos multiusuarios es común que dos usuarios soliciten el mismo procesamiento simultáneamente.
Ejemplo: El usuario A y B solicitan la liberación de todos los pedidos pendientes. En esta situación existen dos alternativas:

  • Bloquear la rutina para uso Monousuario.
  • Tratar la concurrencia.

El bloqueo de la rutina para uso Monousuario debe evitarse, pero si es necesario, debe realizarse por medio del uso de un semáforo. La función LockByName es la más recomendada para esta finalidad. Sin embargo, debe tomarse en cuenta el aislamiento necesario. Si la rutina es multiempresa, debe considerarse la empresa, si es multisucursal debe considerarse la sucursal en la formación de la clave de aislamiento. Ejemplo: Para que la rutina MATA330 esté aislada de todas las empresas y sucursales, debe utilizarse el Grupo de empresas en la formación de la clave de aislamiento LockByName("MATA330_"+cEmpAnt). Si solo es necesario el aislamiento de la sucursal, debe utilizarse la sucursal en la formación de la clave de aislamiento LockByName("MATA330_"+cFilAnt)
Tratar la concurrencia en rutinas de procesamiento es una tarea simple, que proporciona un enorme beneficio para el desempeño de la aplicación. Cuando dos usuarios ejecutan la misma rutina simultáneamente es mucho más probable que uno de los usuarios bloquee el registro principal y los otros se queden esperando la liberación del bloqueo. Después de la liberación del bloqueo, uno de los usuarios conseguirá realizar el bloqueo, pero no habrá procesamiento para realizar y el próximo registro ya estará bloqueado. Como se puede ver, no hay ningún beneficio para el sistema, además del perjuicio de consumo del hardware del Application Server.
Para que el sistema se beneficie con este tipo de situación es imprescindible el uso de la función SimpleLock. Esta función evalúa si el registro puede bloquearse y si no lo consigue, la rutina pasará al próximo registro y así sucesivamente, hasta la finalización del procesamiento. La gran ventaja de su uso es que, cuánto más llamadas haga el cliente a la misma rutina, más rápido será el procesamiento de la rutina.

Registros de la tabla

Momento

Llamada 1

Llamada 2

Llamada 3

10

1

Bloquea 10

Bloquea 11

Bloquea 12

11

2

Libera 10

Libera 11

Libera 12

12

3

Finaliza

Finaliza

Finaliza


Observe que este tipo de solución puede utilizarse en varios tipos de rutina de procesamiento, siempre y cuando haya un puntero para ejecución.

Bloqueo de transacción

Las transacciones tienden a serializar el sistema y dejarlo más lento en entornos multiusuario. Para mitigar la serialización son necesarios algunos cuidados.
El primer cuidado a tomarse, es evitar un DeadLock. Un sistema está en estado de DeadLock cuando existe una operación (A) haciendo un bloqueo en un registro (L1) e intentando bloquear otro registro (L2). En este mismo momento existe otra operación (B) bloqueando el registro (L2) e intentando bloquear el registro (L1). En esta situación no hay forma de que el banco resuelva las solicitudes, entonces elige aleatoriamente, una de las conexiones y la finaliza provocando un error para el usuario.

Existen dos maneras de resolver un DeadLock. La primera es garantizar que la transacción no bloquee más de un registro de la misma tabla. Esto puede realizarse reduciendo el tamaño de la transacción, para garantizar y mantener la integridad del sistema.
Ejemplo: Cuando se graba un formulario del tipo Master/Detail puede realizarse una transacción para el Master y el primer registro Detail y otra para los otros ítems.
// DeadlockPreventionSample.prw
For nX := 1 to Len(aCampos)
Begin Transaction
If nX == 1
RecLock("SC5", .T.)
EndIf
RecLock("SC6",.T.)
MaAvalSc6()
End Transaction Lockin "SC5"
Next nX
Begin Transaction
RecLock("SC5")
MaAvalSc5()
End Transaction
Observe que durante todo el proceso, la tabla SC5 está bloqueada y solamente se liberará en la última transacción. Esto lo garantiza el comando CLOSETRANSACTION LOCKIN, que actualiza la transacción, pero mantiene el bloqueo del registro para no generar problemas en otros procesamientos concurrentes, de acuerdo con lo informado anteriormente.
Otra manera de resolver, es utilizando la función MultLock. Esta función garantiza el bloqueo de todos los registros de una misma tabla. Su desventaja es el aumento de la serialización del producto. Por este motivo, debe utilizarse con mucho cuidado y tener presente que siempre hay una alternativa para evitar su uso.
// MultlockSample.prw
If MultLock("SB2",aMults,1)
Begin Transaction
// Hace alguna cosa
End Transaction
EndIf
La selección de uno de los métodos dependerá del tipo de transacción.
Las transacciones de formulario, obligatoriamente deben seguir el primer ejemplo. Las transacciones son más rápidas y el riesgo es la actualización parcial del formulario digitado, sin embargo, este quedara íntegro. Con toda seguridad el usuario final prefiere perder algunos datos a todo.
Sin embargo, existirán casos en que la actualización parcial no proporciona una transacción íntegra. Para estos casos, debe utilizarse "MultLock". Un buen ejemplo son las facturas, que en caso de interrupción, el usuario tiende a anular la factura y rehacerla si faltan ítems, es decir, el modelo anterior no proporciona beneficios al usuario.

Trabajo con archivos (Tablas de datos)

Conceptos de sucursal de uso compartido de archivos

El uso compartido de archivos se basa en el concepto de sucursales. Cada empresa registrada puede tener n sucursales donde los datos incluidos por cada sucursal, como por ejemplo, archivo de productos, cliente, etc., pueden ser compartidos con las otras sucursales. Este recurso solamente es posible debido a la existencia del campo "XX_FILIAL" ("XX" representa el nombre del archivo) en todos los archivos del sistema.
El modo de operación de cada archivo, compartido o exclusivo, está definido en el archivo SX2 del diccionario de datos.
Si el modo de acceso es compartido, cuando un nuevo registro se incluye, el campo XX_FILIAL recibirá el valor " " (dos caracteres en blanco) y su contenido será visible por cualquier usuario de cualquier sucursal.
Si el modo es exclusivo, cuando se incluye un nuevo registro, el campo recibirá el código alfanumérico de la sucursal (en el siguiente ejemplo, "01" o "02" ) y será visible únicamente para los usuarios de la sucursal que este incluyó.
Ejemplo:
El archivo XX está con el modo de acceso definido como exclusivo en el SX2, por lo tanto, sus registros serán visibles únicamente para la sucursal que los incluyó. Vea el siguiente ejemplo:

XX_FILIAL

XX_CODIGO

XX_DESCRI

01

30

DESTORNILLADOR

01

22

DESTORNILLADOR DE ESTRELLA

02

21

TALADRO BOSCH

02

12

TALADRO BLACK & DECKER

01

31

TALADRO DREMEL

01

90

SIERRA DE VAIVÉN

02

48

PLOMADA

02

13

SERRUCHO



Los usuarios de la Sucursal 01 tendrán acceso únicamente a los datos cuyo registro tiene el contenido "01" en el campo XX_FILIAL. Ya los usuarios de la Sucursal 02, accederán a los datos de los registros con el valor "02" en el campo XX_FILIAL.
En la Gestión de empresas, existen algunas particularidades, que deben tomarse en cuenta en su desarrollo. Para una mejor comprensión sobre la función Gestión de empresas, a continuación se realiza una breve explicación de los niveles que estarán disponibles en la Línea de Productos Microsiga Protheus.

  • Grupo de empresas: Nivel superior que controla el diccionario de datos en el Microsiga Protheus. Ejemplo: SX1??0.DBF – el código de grupo sustituirá los puntos de interrogación
  • Empresas: Nivel que identifica las empresas que forman parte del grupo, es decir, ahora es posible utilizar más de una empresa para el mismo diccionario de datos.
  • Unidades de negocio: Nivel que identifica las unidades de negocios de las empresas. De esta manera, es posible definir un nivel de control entre las empresas y sucursales.
  • Sucursal: Nivel que identifica las sucursales de las unidades de negocios o de las empresas.


Al crear estos niveles, podemos utilizar diversas empresas, unidades de negocios y sucursales compartiendo información entre sí.
El único nivel obligatorio para la Línea de Productos Microsiga Protheus es la sucursal con un mínimo de dos dígitos, pudiendo llegar a doce dígitos, los otros niveles deben utilizarse de acuerdo con la necesidad de cada cliente.
A partir de este momento, explicaremos el vínculo entre los niveles:
En el archivo de Grupo de empresas, el administrador del Sistema debe definir el layout utilizado para la cumplimentación de la empresa, unidad de negocios y sucursal, que no debe sobrepasar de doce caracteres.
Ejemplo: EEUUFFFF
En el ejemplo anterior, definimos que la información grabada en los campos sucursales se identificarán de la siguiente manera:
EE – Dos dígitos para la empresa.
UU – Dos dígitos para la unidad de negocios.
FFFF – Cuatro dígitos para la sucursal.
Algunas reglas que los desarrolladores deben seguir:

  1. Utilizar las siguientes directrices para que el programa acepte las modificaciones de tamaño del campo Sucursal:


FWGETTAMFILIAL-> Retorna el tamaño utilizado para la Sucursal (Sustituye el tamaño fijo de 2)
Ejemplo:
// FWGETTAMFILIALSample.prw
AAdd(aStr, {"XU_FILIAL", "C", FWGETTAMFILIAL, 0})
En este caso, todos los lugares que estén utilizando el tamaño 2 fijo deben modificarse para que se haga flexible el cambio de tamaño.
FWGETCODFILIAL -> Retorna el código de la Sucursal marcada (Sustituye la utilización del M0_CODFIL)
A partir del momento que se define que el tamaño del campo SUCURSAL puede aumentarse, el tamaño físico del campo M0_CODFIL será 12, de esta manera, para utilizar el contenido correcto, debe utilizarse la variable FWGETCODFILIAL en lugar del campo M0_CODFIL.
Ejemplo:
// FWGETCODFILIALSample.prw
FWGETCODFILIAL = "1234"
Sucursal con tamaño 4 y sin los caracteres especiales - CORRECTO
Para el tratamiento en diccionarios de datos, no es posible la utilización de los strings informados, pues, esta solo se sustituye en tiempo de compilación. De esta manera, en el caso del diccionario de datos deben utilizarse las siguientes funciones:

  1. FWSizeFilial() -> Retorna el tamaño utilizado para la Sucursal (Sustituye el tamaño fijo de 2)
  2. FWCodFil() -> Retorna el código de la Sucursal marcada (Sustituye la utilización del M0_CODFIL)


  1. Todos los nuevos campos referentes a la Sucursal deben incluirse en el grupo de campo "033" (Sucursal).


  1. Para la actualización de campos concatenados, en el cual, la sucursal forma parte del contenido del campo, debe crearse una función con la sigla del módulo + "UpdFConj()" que retornará un array con la siguiente estructura:


aArray[n,1]=Alias
aArray[n,2]=Campo
aArray[n,3]=Tamaño original del campo (Debe informarse el tamaño estándar y no debe considerarse el aumento o disminución del campo sucursal)
aArray[n,4]=Indica la posición en que se inicia la grabación del campo sucursal (Opcional. En caso de no que no exista este nivel, el campo solamente se aumentará.)
aArray[n,5]=Bloque de códigos con la función de actualización de los datos que se ejecutará cuando la posición 4 sea igual a 0 (cero).
Este bloque de códigos recibirá como parámetro la siguiente información:
cAlias,cField,nRealSize,nFilIni,nOldSize,nNewSize.
Ejemplo de cumplimentación:
// ConcatenatedBranchUpdateSample.prw
{ |cAlias,cField,nRealSize,nFilIni,nOldSize,nNewSize|FunUpdRA( cAlias,cField,nRealSize,nFilIni,nOldSize,nNewSize ) }
Function GPEUpdFConj()
Local aFields := {}
Aadd(aFields, { "SRA", "RA_P1FILP2", 14, 7 } ) // Ejemplo: "PARTE101PARTE2"
Aadd(aFields, { "SRA", "RA_FILUSR", 8, 1 } ) // Ejemplo: "01PARTE2"
Aadd(aFields, { "SRA", "RA_123456", 8, 0, { cAlias, cField, nRealSize, nFilIni, nOldSize, nNewSize| FunUpdRA(cAlias, cField,n RealSize, nFilIni, nOldSize, nNewSize) }} )
Return aFieds
Function FunUpdRA(cAlias, cField, nRealSize, nFilIni, nOldSize, nNewSize)
// Tratamientos necesarios y actualización del contenido en la base de datos
Return

  1. Funciones auxiliares para el desarrollo y estandarización de los programas:


  • FWSizeFilial - Retorna el tamaño del campo Sucursal
  • FWCodFil - Retorna el código de la sucursal
  • FWModeAccess - Retorna el modo de uso compartido
  • FWGrpCompany - Retorna el grupo
  • FWAllGrpCompany - Retorna los grupos de empresas
  • FWCompany - Retorna la empresa
  • FWAllCompany - Retorna las empresas del grupo de empresas
  • FWUnitBusiness - Retorna la unidad de negocio
  • FWAllUnitBusiness - Retorna las unidades de negocios para el grupo y la empresa
  • FWFilial - Retorna la sucursal
  • FWAllFilial - Retorna las sucursales para el grupo de empresas
  • FWLoadSM0 - Carga la información de las sucursales
  • FWSM0Layout - Retorna el layout
  • FWXFilial - Retorna el string
  • FWGrpName - Retorna el nombre del grupo de empresas
  • FWFilialName - Retorna el nombre de la sucursal
  • FWFilRazSocial - Retorna la Razón social
  • FWArrFilAtu - Retorna toda la información referente a la empresa, unidad de negocio y sucursal
  • FWFilialStatus - Retorna el estatus de la sucursal marcada
  • FWCodEmp - Retorna el código de la empresa
  • FWPesqSM0 - Muestra las sucursales disponibles para el grupo de la empresa actual
  • FWUnitName - Retorna el nombre de la Unidad de negocios
  • FWCompanyName - Retorna el nombre de la empresa
  • FWFilExist - Verifica si la sucursal existe
  • FWEmpName - Nombre de la empresa
  • FWFilName - Nombre de la sucursal


  1. Los programas que utilizan la barrida en la tabla SM0 (While), deben utilizar la función FWLoadSM0() que retorna un array con todas la información del SIGAMAT.


Ejemplo:
// FWLoadSM0Sample.prw
aSM0 := FWLoadSM0()
For nI := 1 To Len(aSM0)
//Tratamientos deseados con la información del SIGAMAT
Next nI

  1. Para retornar la información de una sucursal específica, utilice la función FWArrFilAtu().


Ejemplo:
// FWArrFilAtuSample.prw
aFil := FWArrFilAtu(cEmpAnt, cFilAnt)
aFil := FWArrFilAtu("04", "E01U01F03")
7 El entorno Gestión de personal ya utilizaba el concepto de multiempresas, de esta manera, para utilizar la empresa debe verificarse si la empresa existe configurada para el grupo y si no lo está, colar el contenido del propio grupo para mantener el legado.
Ejemplo:
// FWLoadSM0LegacySample.prw
aSM0 := FWLoadSM0()
For nI := 1 To Len(aSM0)
If !Empty(aSM0[nI,SM0_EMPRESA])
cEmp := aSM0[nI,SM0_EMPRESA]
Else
cEmp := aSM0[nI,SM0_GRPEMP]
EndIf
// Utiliza la variable cEmp
Next nI
Estas reglas deben utilizarse en el sistema de personalizaciones. De esta manera es muy importante revisar las personalizaciones de los clientes antes de comenzar a utilizar la Gestión de empresas.
En conclusión, la Gestión de empresas, presente en la Línea de Productos Microsiga Protheus, permite compartir las definiciones del "Modelo de datos" entre las empresas configuradas en el sistema, además de introducir el concepto de Unidad de negocio.

XFilial()

Para que el registro realmente quede disponible para sus respectivas Sucursales, TODAS las rutinas que manejan registros directamente en la base de datos deben verificar la Sucursal por medio de la Función XFilial().
// XFilialSample.prw
SA1->(DbSeek(XFilial("SA1") + cCodigo + cLoja)
En el ejemplo anterior, se muestra que utilizando la función XFilial(), podemos encontrar un registro previendo todo el uso compartido de archivo.
Importante:

  • El campo XX_FILIAL forma parte de la clave de todos los índices del sistema.
  • Jamás utilice una campo sucursal de una tabla para ejecutar un DbSeek() en otra tabla. Pues una tabla podrá compartirse (campo sucursal en blanco), mientras que la otra podrá compartirse (campo sucursal completado).

Archivos e índices temporales

Archivos e índices temporales deben utilizarse con cuidado, pues pueden generar un tiempo de respuesta largo mientras están construyéndose.
En el caso de índices temporales, el procesamiento de la rutina es mucho más rápido (después de su generación), pero de cualquier forma, siempre de preferencia a la utilización de los índices estándar del sistema o de querys.
Utiliza las funciones:
CriaTrab
Criatrab(cAlias,.F.) -> Crea solamente un archivo de índice temporal
Criatrab(cAlias,.T.) - > Crea un archivo de datos y un archivo de índice temporal (la creación del índice temporal no es obligatoria).
IndRegua
Crea efectivamente el índice, a partir del archivo creado con la CriaTrab.
Ejemplo:
Para crear dos índices temporales:
// IndReguaSample.prw
DbSelectArea("SE1")
cIndex := CriaTrab(Nil, .F.)
cIndex2 := CriaTrab(Nil, .F.)
cChave := IndexKey()
IndRegua("SE1", cIndex, "E1_FATURA+E1_NUM+E1_SERIE", , , "Seleccionando Registros...")
IndRegua("SE1", cIndex2, "E1_NUM", , , "Seleccionando Registros...")
nIndex := RetIndex("SE1")
DbSelectArea("SE1")

Creación y borrado de archivos temporales

Cuando se crea un archivo de trabajo o un índice de trabajo (utilizando la función Indregua) es obligatorio que se borren al final del programa.
Ejemplo:
// CreateAndDeleteTemporaryDatabaseFileSample.prw
cArqTmp := CriaTrab( Nil, .T. ) //Creación de archivo
// Haz algún procesamiento
DbCloseArea()
FErase(cArqTmp + GetDbExtension()) // Borrando el archivo
FErase(cArqTmp + OrdBagExt()) // Deletando índice
Importante:
Utilice la función GetDbExtension() para retornar la extensión del archivo de trabajo. No utilice ".dbf", ".dbt", etc, como se muestra a continuación:
// GetDbExtensionSample.prw
FErase(cArqTmp + ".dbf") // Incorrecto.
FErase(cArqTmp + GetDbExtension()) // Correcto.

Utilización de filtros

La utilización de filtros podrá realizarse de dos maneras:
<Ejercicio>Set Filter to
Ejemplo:
// SetFilterSample.prw
DbSelectArea("CV3")
cFilCV3 := XFilial("CV3")
Set Filter To CV3->CV3_FILIAL == cFilCV3 .And.;
CV3->CV3_DTSEQ == dDtCV3 .And.;
CV3->CV3_SEQUEN == cSequenc
// Haz algún procesamiento
Set Filter To
Es importante saber, que es incorrecto utilizar la función dentro de la Sintaxis del "Set Filter", y se recomienda que si es necesario, se utilice la forma del ejemplo:
cFilCV3 := XFilial("CV3")
Set Filter To CV3->CV3_FILIAL == cFilCV3 …
Al final de la utilización del filtro, este debe deshabilitarse, utilizándose una de las siguientes funciones / comandos:

  • DbSetFilter();
  • <Ejercicio>Set Filter to
  • DbClearFilter().


Estándar SQL del Microsiga Protheus

El estándar SQL del Microsiga Protheus se formó con base en la Revisión ANSI de 1992 y algunos comandos específicos de la base de datos, como por ejemplo DB2 y Oracle.
Cláusulas
Las cláusulas son condiciones de modificación utilizadas para definir los datos que se desea seleccionar o modificar en una consulta.

  • § SELECT – Se utiliza al inicio de una línea de comando para seleccionar datos, pudiendo utilizarse dentro de un otro "select" (conocido popularmente como "SubSelect") utilizado dentro de las cláusulas "From", "In" y "Not In"
  • § FROM - Se utiliza para especificar la tabla que seleccionará los registros.
  • § WHERE – Se utiliza para especificar las condiciones que deben reunir los registros que se seleccionarán.
  • § GROUP BY – Se utiliza para separar los registros seleccionados en grupos específicos.
  • § HAVING – Se utiliza para expresar la condición que debe satisfacer cada grupo.
  • § ORDER BY – Se utiliza para ordenar los registros seleccionados con un orden específico.
  • § DISTINCT – Se utiliza para seleccionar los datos sin repetición.


Operadores

  • § INNER
  • § JOIN


Operadores lógicos

  • § AND – Y lógico. Evalúa las condiciones y devuelve un valor verdadero si ambos están correctos.
  • § OR – O lógico. Evalúa las condiciones y devuelve un valor verdadero si alguno está correcto.
  • § NOT – Negación lógica. Devuelve el valor contrario de la expresión


Operadores relacionales
El SQL tiene operadores relacionales, que se utilizan para realizar comparaciones entre valores, en estructuras de control. Estos son:

Operador

Descripción

<

Menor

>

Mayor

<=

Menor o igual

>=

Mayor o igual

=

Igual

<>

Diferente

  • § BETWEEN – Se utiliza para especificar un intervalo de valores.
  • § LIKE – Se utiliza en la comparación de un modelo y para especificar los registros de una base de datos. "Like" + extensión % significa buscar todos los resultados con el mismo inicio de la extensión.
  • § IN - Se utiliza para verificar si el valor buscado está dentro de una lista. Ej.: valor IN (1,2,3,4).


Funciones de agregación
Las funciones de suma se utilizan dentro de una cláusula SELECT en grupos de registros para devolver un único valor que se aplica a un grupo de registros.

  • § AVG – Se utiliza para calcular el promedio de los valores de un campo determinado.
  • § COUNT – Se utiliza para devolver el número de registros de la selección.
  • § SUM – Se utiliza para devolver la suma de todos los valores de un campo determinado.
  • § MAX – Se utiliza para devolver el valor más alto de un campo especificado.
  • § MIN – Se utiliza para devolver el valor más bajo de un campo especificado.


Comandos específicos de la base de datos

  • SUBSTRING
  • EXISTS

Embedded SQL

El objetivo del Embedded SQL es facilitar la escritura y lectura de query's. Se definió una sintaxis para que se pueda escribir la query directamente en el código ADVPL, sin necesidad de quedarse concatenando pedazos de string para componer el string final.
Se recomienda que nuevas querys se desarrollen valiéndose de este nuevo recurso.
// EmbeddedSQLQuery.prw
#INCLUDE "TOTVS.CH"
Function EmbeddedSQLQuery()
BeginSQL Alias cAliasTrb
SELECT R_E_C_N_O_ RECNOSN1
FROM %Table:SN1%
WHERE N1_FILIAL = %XFilial:SN1% AND
N1_CBASE >= %Exp:MV_PAR01% AND
N1_CBASE <= %Exp:MV_PAR02% AND
N1_ITEM >= %Exp:MV_PAR03% AND
N1_ITEM <= %Exp:MV_PAR04% AND
N1_GRUPO >= %Exp:MV_PAR05% AND
N1_GRUPO <= %Exp:MV_PAR06% AND
N1_AQUISIC >= %Exp:MV_PAR07% AND
N1_AQUISIC <= %Exp:MV_PAR08% AND
%Exp:cWhere% AND
%NotDel%
EndSQL
Return

  • Sem rótulos