NunezERP
← Volver a los artículos

Registrar automáticamente diarios en Business Central mediante colas de trabajo

Cómo registrar automáticamente los apuntes de un diario en Business Central mediante una cola de trabajo programada, sin intervención del usuario.

Publicado el

Inicia sesión para exportar este artículo

Recientemente trabajé en un proyecto donde integramos todo tipo de documentos y apuntes contables en Business Central mediante API. Básicamente, lo que hacíamos es importar la información desde sistemas externos y crear todos los registros estándar necesarios para posteriormente registrarlos desde BC utilizando las colas de trabajo (antiguas colas de proyecto).

Ya hemos hablado en este artículo sobre el registro automático de documentos, aunque en él nos centrábamos en documentos de compra y venta, pero no en los diarios generales. ¿Qué ocurre cuando queremos integrar directamente apuntes contables en un diario y registrarlos automáticamente?

Podríamos desarrollar una API que ejecutase directamente el registro desde fuera, aunque personalmente no es una opción que me guste demasiado. Es posible que esos apuntes se tengan que revisar antes del registro o que, simplemente, no queramos exponer un procedimiento de registro a una aplicación externa. Lo interesante sería que la integración se limitase a crear los apuntes en el diario y que Business Central se encargase posteriormente de registrarlos, utilizando sus propios procesos estándar.

Registro de fondo en contabilidad

Revisando las configuraciones estándar de Business Central, me encontré con el campo “Registrar con cola de trabajos”, dentro de “Configuración contabilidad”.

ConfConta

A simple vista, parece que funciona igual que el registro de documentos mediante colas de trabajo. Es decir, activamos la configuración y Business Central se encarga de registrar automáticamente los diarios. Pero no es exactamente así.

Esta funcionalidad está pensada principalmente para permitir que un usuario registre un diario en segundo plano, sin tener que esperar a que finalice el proceso. Por ejemplo, imaginemos una empresa que importa un diario con miles de líneas correspondientes a nóminas o movimientos contables. El contable revisa la información y le da a “Registrar”.

En función de la configuración, el comportamiento será diferente:

  • Sin activar “Registrar con cola de trabajos”: el registro se ejecutará directamente en la sesión del usuario, que tendrá que esperar a que finalice.

  • Con la opción activada: Business Central enviará el registro a una cola de trabajo para procesarlo en segundo plano, permitiendo que el usuario continúe trabajando.

Es una funcionalidad interesante, pero no resuelve exactamente nuestro problema. Seguimos necesitando que alguien entre en el diario y lance el registro.

Lo que buscamos es que una cola de trabajo pueda ejecutarse de forma programada y registrar los apuntes sin intervención del usuario.

Utilizar la codeunit 250 para registrar diarios

Revisando la documentación de Microsoft sobre el funcionamiento de las colas de trabajo, encontré una posible solución utilizando la codeunit estándar 250 “Gen. Jnl.-Post via Job Queue”.

Esta codeunit es precisamente la que utiliza Business Central para registrar diarios mediante colas de trabajo. Podemos consultar la documentación oficial aquí:

Codeunit Job Queue Dispatcher y evento OnBeforeExecuteJob

Funcionamiento de las colas de trabajo en Business Central

En principio, parece que funcionará, ya que simplemente creamos una cola de trabajo, indicamos que ejecute la codeunit 250 y configuramos cuándo queremos que se registre el diario. Sin embargo, si intentamos ejecutarla directamente, nos encontraremos con un error, ya que esta codeunit necesita recibir un valor en el campo “Record ID to Process” de la tabla “Job Queue Entry”.

De hecho, si revisamos su código estándar, vemos que nada más comenzar la ejecución realiza la siguiente comprobación:

trigger OnRun()
var
    GenJrnlLine: Record "Gen. Journal Line";
    OrigGenJrnlLine: Record "Gen. Journal Line";
    GenJrnlTemplate: Record "Gen. Journal Template";
    BatchPostingPrintMgt: Codeunit "Batch Posting Print Mgt.";
    RecRef: RecordRef;
    RecRefToPrint: RecordRef;
    PrintPostDocuments: Boolean;
begin
    Rec.TestField("Record ID to Process");

Es decir, antes de realizar cualquier operación, comprueba que el campo “Record ID to Process” tenga un valor.

Si configuramos manualmente una cola de trabajo para ejecutar esta codeunit, pero no le proporcionamos ese identificador, obtendremos el siguiente error:

ErrorEstandarPrimero

Por tanto, no basta con indicar la codeunit 250 en la cola de trabajo. Necesitamos pasarle también el identificador del registro que queremos procesar. Y aquí es donde necesitaremos realizar una pequeña personalización ya que no hay forma de asignar el valor de “Record ID to Process” mediante la interfaz de usuario.

Crear una codeunit propia para registrar el diario

Una opción sería olvidarnos de la codeunit 250 y desarrollar nuestra propia codeunit para registrar directamente las líneas de un diario.

Por ejemplo, podríamos implementar algo tan sencillo como esto:

codeunit 50100 "XXX Post Gen Journal Queue"
{
    trigger OnRun()
    var
        GenJournalLine: Record "Gen. Journal Line";
        GenJnlPostBatch: Codeunit "Gen. Jnl.-Post Batch";
    begin
        GenJournalLine.SetRange("Journal Template Name", 'GENERAL');
        GenJournalLine.SetRange("Journal Batch Name", 'DEFAULT');

        if GenJournalLine.FindFirst() then
            GenJnlPostBatch.Run(GenJournalLine);
    end;
}

Con este código filtramos las líneas del diario GENERAL, sección DEFAULT, y ejecutamos la codeunit estándar encargada del registro.

Es una solución perfectamente válida, pero personalmente prefiero aprovechar la codeunit 250, que ya incorpora el comportamiento estándar para el registro mediante colas de trabajo. Además, en este caso no necesitamos modificar el proceso de registro. Solamente tenemos que conseguir que reciba el identificador que está esperando.

Evento OnBeforeExecuteJob

La solución que he utilizado consiste en suscribirme al evento “OnBeforeExecuteJob” de la codeunit “Job Queue Dispatcher”.

Cuando configuramos una cola y la establecemos en estado “Preparado”, BC programa una tarea interna utilizando el planificador de tareas de la plataforma. Cuando llega el momento de ejecutarla, entra en juego la codeunit “Job Queue Dispatcher”, que se encarga de gestionar la ejecución del trabajo correspondiente. Y lo interesante es que esta codeunit dispone del evento OnBeforeExecuteJob, que se lanza antes de ejecutar el objeto configurado en la cola de trabajo.

Este evento recibe, además, el registro de la tabla “Job Queue Entry” por referencia, por lo que podemos modificar sus valores antes de que se ejecute nuestra codeunit. En mi caso, lo he utilizado para informar el campo “Record ID to Process”.

Podemos hacerlo con un EventSubscriber como este:

[EventSubscriber(ObjectType::Codeunit,
    Codeunit::"Job Queue Dispatcher",
    'OnBeforeExecuteJob', '', false, false)]
local procedure SetJournalRecordId(
    var JobQueueEntry: Record "Job Queue Entry")
var
    GenJournalLine: Record "Gen. Journal Line";
begin
    if JobQueueEntry."Object Type to Run" <>
       JobQueueEntry."Object Type to Run"::Codeunit
    then
        exit;

    if JobQueueEntry."Object ID to Run" <>
       Codeunit::"Gen. Jnl.-Post via Job Queue"
    then
        exit;

    if JobQueueEntry."Parameter String" <> 'SECURITY_PARAMETER' then
        exit;

    GenJournalLine.SetRange("Journal Template Name", 'GENERAL');
    GenJournalLine.SetRange("Journal Batch Name", 'DEFAULT');
    GenJournalLine.SetRange(
        "Job Queue Status",
        GenJournalLine."Job Queue Status"::" ");

    if not GenJournalLine.FindFirst() then
        Error(
            'No hay líneas disponibles en el diario GENERAL, sección DEFAULT.');

    JobQueueEntry."Record ID to Process" :=
        GenJournalLine.RecordId;
end;

Primero comprobamos que la cola de trabajo ejecuta la codeunit 250 y que el campo Parameter String contiene SECURITY_PARAMETER, para identificar la cola que queremos personalizar.

Después filtramos las líneas del diario con estos valores:

  • Plantilla: GENERAL.

  • Sección: DEFAULT.

  • Estado de cola: vacío.

Y con estos filtros buscamos la primera línea disponible y obtenemos su RecordId.

Finalmente, asignamos ese valor al campo “Record ID to Process” de la cola de trabajo.

De esta forma, cuando Business Central continúe con su ejecución, la codeunit 250 ya tendrá informado el identificador que necesita y podrá ejecutar el proceso estándar de registro.

Un detalle importante: la codeunit 250 utiliza esa línea como referencia y filtra posteriormente por plantilla, sección y número de documento. Por tanto, no registra necesariamente todas las líneas del diario, sino las correspondientes al número de documento seleccionado.

Ejemplo de registro automático de un diario

Primero preparamos un diario general con varios apuntes contables. En este ejemplo tenemos dos números de documento diferentes, cada uno con sus correspondientes líneas contables.

EjemploDiario

Como hemos comentado, la codeunit 250 realiza el registro por número de documento. Por tanto, al seleccionar una línea correspondiente al documento DG26000743, se intentarán registrar las líneas asociadas a ese mismo documento, sin incluir las que pertenecen a OTRO-DOC.

Una vez preparado el diario, creamos una cola con el objeto de tipo Codeunit y el identificador 250, informando el campo “Cadena parámetros” con el valor que hemos definido en nuestro desarrollo: SECURITY_PARAMETER

IndicarParametro

Comprobación de errores durante el registro

Por ejemplo, si las líneas del diario no están cuadradas o contienen algún dato incorrecto, el proceso devolverá los errores correspondientes.

EjemploErrorEnRegistro

Es decir, aunque estemos ejecutando el proceso desde una cola de trabajo, Business Central sigue aplicando sus controles contables habituales.

Registro correcto del diario

Si corregimos los errores y volvemos a ejecutar la cola de trabajo, Business Central comenzará el registro.

RegistrandoDiario

ResultadoFinal

Como vemos, los movimientos se han registrado correctamente y las líneas procesadas han desaparecido del diario, igual que sucedería si hubiésemos realizado el registro manualmente.

Consideraciones adicionales

El primero es que estamos trabajando por número de documento. La codeunit 250 no tiene como objetivo recorrer automáticamente todos los documentos de una sección en una sola ejecución. En nuestro ejemplo, el EventSubscriber busca una línea disponible y el estándar registra las líneas correspondientes a ese documento.

El segundo es el control sobre los apuntes que queremos registrar. En el ejemplo filtramos directamente el diario GENERAL, sección DEFAULT, pero podríamos ampliar la personalización para determinar qué diarios deben procesarse o establecer alguna comprobación adicional.

Es decir, habría que ajustar algunos detalles del desarrollo final, pero esta aproximación es bastante buena para entender cómo funciona el registro en cola sobre los diarios generales.

Una idea para seguir pensando.

Explorar otras lecturas →