Las transacciones son una serie de modificaciones efectuadas al interior de un proceso sobre datos relacionados. Una transacción no se guarda permanente en la base hasta que la transacción sea validada. Si no se completa una transacción, bien sea porque se cancela o por un evento externo, las modificaciones no se guardan.
Durante una transacción, todos los cambios realizados a los datos de la base dentro del proceso se almacenan localmente en un buffer temporal. Si la transacción se acepta con VALIDATE TRANSACTION, los cambios se guardan de manera permanente. Si la transacción se cancela con CANCEL TRANSACTION, los cambios no se guardan. En todos los casos, ni la selección actual ni el registro actual son modificados por los comandos de gestión de transacciones.
4D soporta transacciones anidadas, es decir, transacciones en varios niveles jerárquicos. El número de subtransacciones autorizadas es ilimitado. El comando Transaction level permite conocer el nivel actual de transacción en el cual se ejecuta el código.
Cuando utiliza transacciones anidadas, el resultado de cada subtransacción depende de la validación o cancelación de la transacción de nivel superior. Si se valida la transacción de nivel superior, los resultados de las subtransacciones se confirman (validación o cancelación). Por el contrario, si la transacción superior se cancela, todas las subtransacciones se cancelan, sin importar sus resultados.
Nota: por razones de compatibilidad, las transacciones anidadas están desactivadas por defecto en las bases de datos convertidas de versiones anteriores a la v11 (ver
Página Compatibilidad).
4D incluye una funcionalidad que le permite suspender y reanudar las transacciones dentro de su código 4D. Cuando se suspende una transacción, puede ejecutar operaciones de forma independiente de la transacción misma y luego reanudar la transacción para validarla o cancelarla como de costumbre. Cuando se suspende la transacción, puede en particular:
- crear o modificar registros fuera de la transacción (por ejemplo, para incrementar un contador de números de facturas),
- iniciar, suspender y/o cerrar otras transacciones.
Para obtener más información sobre este punto, consulte Suspender las transacciones.
En este ejemplo, la base es una sistema de facturación simple. Las líneas de las facturas son almacenadas en una tabla llamada [Linea_Factura], la cual está relacionada con la tabla [Facturas] por una relación entre los campos [Facturas]NoFacturas y [Linea_Factura]NoFacturas. Cuando se añade una factura, se calcula un número único, utilizando el comando Sequence number. La relación entre [Facturas] y [Linea_Factura] es una relación automática Muchos a Uno. La casilla de selección Autoasignar valor en el subformulario está seleccionada.
La relación entre [Linea_Factura] y [Partes] es manual.

Cuando un usuario introduce una factura, se ejecutan las siguientes acciones:
- Adición de un registro en la tabla [Facturas].
- Adición de varios registros en la tabla [Linea_Factura].
- Actualización del campo [Partes]En_Bodega de cada parte listada en la factura.
Este ejemplo es una situación típica en la cual necesita utilizar una transacción. Debe asegurarse de poder guardar todos estos registros durante la operación o de que poder cancelar la transacción si un registro no puede añadirse o actualizarse. En otras palabras, debe guardar los datos relacionados.
Si no utiliza una transacción, no puede garantizar la integridad de datos lógica de su base. Por ejemplo, si un registro de la tabla [Partes] está bloqueado, no podrá actualizar la cantidad almacenada en el campo [Partes]En_Bodega. Este campo se volverá entonces lógicamente incorrecto. La suma de las partes vendidas y restantes en bodega no será igual a la cantidad original introducida en el registro. Puede evitar tal situación utilizando las transacciones.
Hay varias maneras de introducir datos utilizando transacciones:
1. Puede administrar las transacciones utilizando los comandos de transacción START TRANSACTION, VALIDATE TRANSACTION y CANCEL TRANSACTION. Puede escribir, por ejemplo:
READ WRITE([Invoice Lines])
READ WRITE([Parts])
FORM SET INPUT([Invoices];"Input")
Repeat
START TRANSACTION
ADD RECORD([Invoices])
If(OK=1)
VALIDATE TRANSACTION
Else
CANCEL TRANSACTION
End if
Until(OK=0)
READ ONLY(*)
2. Para reducir los bloqueos de registros durante la entrada de datos, puede también seleccionar administrar transacciones a partir del método de formulario y acceder a las tablas en READ WRITE únicamente cuando sea necesario.
Usted efectúa la entrada de datos utilizando el formulario de entrada para [Facturas], el cual contiene la tabla relacionada [Facturas]Lineas en un subformulario. El formulario tiene dos botones: bCancel y bOK, ambos son botones sin acciones.
El bucle de adición se convierte en:
READ WRITE([Invoice Lines])
READ ONLY([Parts])
FORM SET INPUT([Invoices];"Input")
Repeat
ADD RECORD([Invoices])
Until(bOK=0)
READ ONLY([Invoice Lines])
Note que la tabla [Partes] está en modo de acceso "sólo lectura" durante la entrada de datos. El acceso lectura/escritura estará disponible únicamente si los datos se validan.
La transacción se abre en el método de formulario de entrada de la tabla [Facturas]:
Case of
:(Form event code=On Load)
START TRANSACTION
[Invoices]Invoice ID:=Sequence number([Invoices]Invoice ID)
Else
[Invoices]Total Invoice:=Sum([Invoice Lines]Total line)
End case
Si hace clic en el botón bCancel, la entrada y la transacción deben ser canceladas.
Este es el método de objeto del botón bCancel:
Si hace clic en el botón bValidate, la entrada y la transacción deben ser aceptadas. Este es el método de objeto del botón bOK:
Case of
:(Form event code=On Clicked)
$NbLines:=Records in selection([Invoice Lines])
READ WRITE([Parts])
FIRST RECORD([Invoice Lines])
$ValidTrans:=True
For($Line;1;$NbLines)
RELATE ONE([Invoice Lines]Part No)
OK:=1
While(Locked([Parts]) & (OK=1))
CONFIRM("La parte "+[Invoice Lines]Part No+" está en uso. ¿Espera?")
If(OK=1)
DELAY PROCESS(Current process;60)
LOAD RECORD([Parts])
End if
End while
If(OK=1)
[Parts]In Warehouse:=[Parts]In Warehouse-[Invoice Lines]Quantity
SAVE RECORD([Parts])
Else
$Line:=$NbLines+1
$ValidTrans:=False
End if
NEXT RECORD([Invoice Lines])
End for
READ ONLY([Parts])
If($ValidTrans)
SAVE RECORD([Invoices])
VALIDATE TRANSACTION
Else
CANCEL TRANSACTION
End if
CANCEL
End case
En este código, llamamos al comando CANCEL sin importar en cual botón el usuario haga clic. El nuevo registro no está validado por una llamada a ACCEPT, pero por el comando SAVE RECORD. Además, note que SAVE RECORD se llama justo antes que el comando VALIDATE TRANSACTION. Por lo tanto, guardar el registro [Facturas] es en realidad parte de la transacción. Llamar el comando ACCEPTtambién validaría el registro, pero en este caso la transacción será validada antes de que el registro [Facturas] se guarde. En otras palabras, el registro sería guardado fuera de la transacción.
Dependiendo de sus necesidades, puede personalizar su base, como se muestra en estos ejemplos. En el último ejemplo, la gestión de bloqueo de registros de la tabla [Partes] puede desarrollarse más.