Saltar al contenido

Cómo Desarrollar en Smart Processing (Documentación para Desarrolladores)

Estado: 01.08.2026 • Tiempo de lectura: ~8 minutos

Smart Processing proporciona puntos de extensión donde puedes implementar lógica personalizada para iniciar tus propios procesos o adaptar el manejo a los requisitos del cliente. Este documento explica el concepto clave de desarrollo: trabajar a través de la sesión activa y activar codeunits personalizados en el proceso correspondiente utilizando los datos de la sesión.

1. Concepto Central: Trabajar a Través de la Sesión de Procesamiento Activa

Smart Processing ejecuta una “sesión de borrador activa” mientras se procesa un documento. Durante ese tiempo, se mantiene un estado relevante de manera central (contexto del documento, contexto de la plantilla, líneas coincidentes, desviaciones y datos intermedios utilizados por la interfaz de usuario y la tubería de procesamiento).

Como desarrollador, el enfoque recomendado es:

  • Leer el contexto de procesamiento actual de la sesión.
  • Realizar tu lógica utilizando el contexto y las referencias proporcionadas.
  • Escribir resultados/estado de nuevo en la sesión cuando el flujo de trabajo lo necesite.
  • Confiar en la plataforma para mantener la interfaz de usuario y el procesamiento alineados a través de la arquitectura de la sesión.

Esto hace que las personalizaciones sean estables y evita un estado inconsistente entre lo que los usuarios ven y lo que tu lógica cambió.


2. El Objeto Central: “SIM_DI Process Session SI”

El codeunit “SIM_DI Process Session SI” es el punto de entrada central para acceder y manipular la sesión activa de Smart Processing. Actúa como el centro para obtener datos de tiempo de ejecución relevantes (documento entrante, plantilla de proceso, búferes de línea, metadatos de desviación) y para comunicar resultados de vuelta a la tubería (por ejemplo, qué encabezado fue procesado).

Resumen de funciones públicas (de los ejemplos proporcionados)

La siguiente tabla enumera las funciones públicas que se utilizan realmente en tu código de ejemplo. Si una función existe en múltiples variantes (sobrecargas), los parámetros que pueden no estar presentes en cada variante se marcan como opcionales.

Función Qué hace Parámetros (opcionales marcados) Retorna
InitSession Inicializa la sesión con un documento entrante y resuelve la plantilla de proceso relacionada. InboundDocument ninguno
InitSession Inicializa la sesión con un documento entrante y una plantilla de proceso proporcionada explícitamente (y borra primero los datos de la sesión anterior). InboundDocument, ProcessTemplate ninguno
GetInboundDocument Recupera el documento entrante almacenado en la sesión actual. var InboundDocument ninguno
GetProcessTemplate Recupera la plantilla de proceso almacenada en la sesión actual. var ProcessTemplate ninguno
ClearProcessSession Borra todo el contexto de la sesión, incluyendo el documento entrante, la plantilla de proceso, las claves de mapeo y los datos de proceso. - ninguno
ClearProcessData Borra solo los datos de procesamiento (encabezados/filas/comentarios temporales, registros de búsqueda mapeados, id de encabezado procesado) y borra datos coincidentes. - ninguno
SetProcessedHeaderRecordId Almacena la referencia del registro del encabezado del documento objetivo creado/coincidente en la sesión para el procesamiento posterior (por ejemplo, finalización/archivado). RecordId ninguno
GetProcessedHeaderRecordId Devuelve la referencia del registro del encabezado procesado almacenado y lo borra de la sesión después. - RecordId
SetDocumentHeaderVariableRef Almacena una copia de la variable de encabezado temporal proporcionada en la sesión (utilizada como un estado de encabezado de sesión de estilo referencia). var TempDocumentHeader (temporal) ninguno
GetDocumentHeaderVariable Copia los datos actuales del encabezado temporal de la sesión en la variable de registro temporal proporcionada. var TempDocumentHeader (temporal) ninguno
SetDocumentLineVariableRef Almacena una copia de la variable de línea temporal proporcionada en la sesión (utilizada como un estado de línea de sesión de estilo referencia). var TempDocumentLine (temporal) ninguno
GetDocumentLineVariable Copia los datos actuales de la línea temporal de la sesión en la variable de registro temporal proporcionada. var TempDocumentLine (temporal) ninguno
SetRemarkVariableRef Almacena una copia de la variable de comentario temporal proporcionada en la sesión (utilizada como un estado de comentario de sesión de estilo referencia). var DocumentRemark (temporal) ninguno
RemoveDocumentRemark Elimina un comentario de la sesión basado en el número de línea, número de campo y área de validación. LineNo, FieldNo, FieldValidationArea ninguno
GetRemarks Copia todos los comentarios de la sesión en la variable de registro de comentario temporal proporcionada. var DocumentRemark (temporal) ninguno
RemoveMappedLookupRecord Elimina la referencia del registro de búsqueda mapeado para un campo de plantilla dado. TemplateFieldRecordId ninguno
GetMappedLookupRecord Recupera la referencia del registro de búsqueda mapeado para un campo de plantilla dado (si está disponible). TemplateFieldRecordId, var MappedRecordId Boolean
GetDevitatingLinesJsonObject Devuelve el objeto JSON que describe las líneas desviadas y sus campos no coincidentes (incluyendo el nombre de configuración de coincidencia y detalles de discrepancia). - JsonObject
GetExecuteCodeunitRefRecordIds Recupera referencias de registro asignadas para ejecución por un codeunit específico (típicamente líneas coincidentes a procesar). ExecuteCodeunitNo, var RecordIds Boolean
GetMatchedDocumentHeader Recupera la referencia del registro del encabezado del documento coincidente para un nombre de configuración de coincidencia dado (si está disponible). MatchingSetupName, var MatchedDocumentHeaderRecordId Boolean

Nota: Estas son todas las funciones públicas que se pueden confirmar a partir de tus ejemplos. Si proporcionas el código fuente completo del codeunit, ampliaré esta tabla para que incluya todas las funciones públicas.


3. Personalizaciones en el Proceso de Coincidencia

Un escenario común de extensión es activar codeunits personalizados durante la coincidencia, especialmente cuando el usuario completa el borrador y se ha creado el registro del encabezado objetivo.

Un detalle clave de este mecanismo es que el codeunit personalizado se ejecuta con el registro del encabezado del tipo de documento coincidente (por ejemplo, un encabezado de compra). Este registro se convierte en tu ancla estable: puedes derivar contexto de él, crear o importar líneas, actualizar registros relacionados y luego devolver el resultado a la sesión para que la plataforma pueda continuar su flujo de trabajo.


4. Ejemplo de Codeunits de Proceso Personalizado (Código Sin Cambios)

Ejemplo 1: Importar líneas de recibo coincidentes después de la creación del encabezado

codeunit 5673320 "SIM_DI Purch.-Get Receipts"
{
    Description = 'Este codeunit se utiliza como un codeunit de importación de coincidencias para recibir líneas de recibo de compra y crear líneas de factura de compra basadas en las líneas de documento coincidentes.';
    TableNo = "Purchase Header"; // <-- Este es el encabezado creado después de completar el procesamiento
    Access = Internal;

    var
        GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Process Session SI";
        GlobalCodeunitGetReceipts: Codeunit "Purch.-Get Receipt";
        GlobalNoPurchaseReceiptLinesFoundLbl: Label 'No hay líneas de recibo de compra que puedan ser procesadas. Por favor, verifica las líneas de recibo de compra y comprueba si ya han sido facturadas.';

    trigger OnRun()
    var
        LocalRecordPurchRcptLine: Record "Purch. Rcpt. Line";
        LocalMatchedLineRecordId: RecordId;
        LocalToExecuteRecordIds: List of [RecordId];
    begin
        // Recibir las líneas coincidentes que necesitan ser ejecutadas
        GlobalCodeunitSIMDIProcessSessionSI.GetExecuteCodeunitRefRecordIds(Codeunit::"SIM_DI Purch.-Get Receipts", LocalToExecuteRecordIds);

        // Filtrar las líneas de recibo de compra basadas en las líneas coincidentes entrantes
        foreach LocalMatchedLineRecordId in LocalToExecuteRecordIds do
            if LocalRecordPurchRcptLine.Get(LocalMatchedLineRecordId) then
                LocalRecordPurchRcptLine.Mark(true);

        // Obtener las líneas de recibo de compra que no han sido facturadas
        LocalRecordPurchRcptLine.MarkedOnly(true);
        LocalRecordPurchRcptLine.SetFilter("Qty. Rcd. Not Invoiced", '<>0');

        // Si no se encuentran líneas de recibo de compra, lanzar un error
        if LocalRecordPurchRcptLine.IsEmpty() then
            Error(GlobalNoPurchaseReceiptLinesFoundLbl);

        // Ejecutar el proceso para obtener líneas de recibo
        GlobalCodeunitGetReceipts.SetPurchHeader(Rec);
        GlobalCodeunitGetReceipts.CreateInvLines(LocalRecordPurchRcptLine);
        Commit();
    end;
}

Este ejemplo muestra un proceso típico impulsado por coincidencias: la sesión proporciona la lista de “registros a ejecutar”, y tu codeunit realiza una importación basada exactamente en esas referencias coincidentes.


Ejemplo 2: Actualizar campos desviados cuando se acepta la desviación

codeunit 5673350 "SIM_DI Update Deviating Fields"
{
    Description = 'Este codeunit se puede utilizar para actualizar las líneas de documento vinculadas con los valores entrantes para los campos que han sido desviados durante el proceso de coincidencia y donde se acepta la desviación.';
    Access = Public;

    var
        GlobalCodeunitSIMDIProcessSessionSI: Codeunit "SIM_DI Process Session SI";
        GlobalCodeunitSIMCOREEvaluate: Codeunit "SIM_CORE Evaluate";
        GlobalCodeunitSIMCOREJSON: Codeunit "SIM_CORE JSON";

    trigger OnRun()
    var
        TempLocalRecordSIMDITempDocumentLine: Record "SIM_DI Temp. Document Line" temporary;
        LocalRecordSIMDIProcessTemplate: Record "SIM_DI Process Template";
        LocalRecordSIMDIInboundDocument: Record "SIM_DI Inbound Document";
        LocalCodeunitSIMDILogManagement: Codeunit "SIM_DI Log Management";
        LocalDocumentHeaderRecordId: RecordId;
        LocalRecordId: RecordId;
        LocalRecordRef: RecordRef;
        LocalFieldRef: FieldRef;
        LocalRecordIdText: Text;
        LocalDevitatingLinesJsonObject: JsonObject;
        LocalLineMatchingValidationFieldsJsonObject: JsonObject;
        LocalJsonToken: JsonToken;
        LocalFieldNoInteger: Integer;
        LocalFieldNoText: Text;
        LocalCellIndexInteger: Integer;
        LocalMatchingSetupNameText: Text;
        LocalOldValueText: Text;
        LocalNewValueText: Text;
        LocalValueVariant: Variant;
        LocalDevitatingFieldUpdateLbl: Label 'La línea %1 ha sido actualizada.\El campo "%2" ha cambiado de valor "%3" a valor "%4".', Comment = '%1 = Número de Línea, %2 = Nombre del Campo, %3 = Valor Antiguo, %4 = Nuevo Valor';
    begin
        /// Recibir los datos de la sesión de proceso
        LocalDevitatingLinesJsonObject := GlobalCodeunitSIMDIProcessSessionSI.GetDevitatingLinesJsonObject();
        GlobalCodeunitSIMDIProcessSessionSI.GetDocumentLineVariable(TempLocalRecordSIMDITempDocumentLine);
        GlobalCodeunitSIMDIProcessSessionSI.GetInboundDocument(LocalRecordSIMDIInboundDocument);
        GlobalCodeunitSIMDIProcessSessionSI.GetProcessTemplate(LocalRecordSIMDIProcessTemplate);

        // Iterar sobre los 
        foreach LocalRecordIdText in LocalDevitatingLinesJsonObject.keys() do begin
            if not Evaluate(LocalRecordId, LocalRecordIdText) then continue;
            if not LocalRecordRef.Get(LocalRecordId) then continue;

            // Obtener el registro de línea actual
            LocalDevitatingLinesJsonObject.Get(LocalRecordIdText, LocalJsonToken);
            // Obtener el nombre de configuración de coincidencia
            LocalMatchingSetupNameText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'MatchingSetupName');

            // Obtener los campos no coincidentes como un objeto json
            if not LocalJsonToken.AsObject().Get('MissMatchedFields', LocalJsonToken) then continue;
            LocalLineMatchingValidationFieldsJsonObject := LocalJsonToken.AsObject();
            // Iterar sobre los campos no coincidentes y actualizar la línea de documento vinculada con el valor entrante si se acepta la desviación
            foreach LocalFieldNoText in LocalLineMatchingValidationFieldsJsonObject.keys() do begin
                if not Evaluate(LocalFieldNoInteger, LocalFieldNoText) then continue;
                LocalLineMatchingValidationFieldsJsonObject.Get(LocalFieldNoText, LocalJsonToken);

                if not GlobalCodeunitSIMCOREJSON.GetJsonPathValueBoolean(LocalJsonToken.AsObject(), 'AcceptDeviation') then
                    continue;
                if not Evaluate(LocalCellIndexInteger, GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'CellIndex')) then
                    continue;

                LocalFieldRef := LocalRecordRef.Field(LocalFieldNoInteger);
                LocalNewValueText := GlobalCodeunitSIMCOREJSON.GetJsonPathValueText(LocalJsonToken.AsObject(), 'IncomingValue');
                LocalOldValueText := LocalFieldRef.Value;

                // Actualizar el campo con el valor entrante
                GlobalCodeunitSIMCOREEvaluate.EvaluateVariable(
                   LocalNewValueText,
                   Format(LocalFieldRef.Type),
                   LocalValueVariant,
                   false
               );
                LocalFieldRef.Validate(LocalValueVariant);
                LocalRecordRef.Modify();

                // Agregar entrada de registro
                LocalCodeunitSIMDILogManagement.AddInformationInTheLog(
                    StrSubstNo(LocalDevitatingFieldUpdateLbl, Format(LocalRecordIdText), LocalFieldRef.Caption, LocalOldValueText, LocalNewValueText),
                    'SIM_DI Update Deviating Fields',
                    LocalRecordSIMDIInboundDocument."Entry No."
                );
            end;

            // Recordar el id del registro del encabezado del documento para archivar el documento más tarde
            GlobalCodeunitSIMDIProcessSessionSI.GetMatchedDocumentHeader(LocalMatchingSetupNameText, LocalDocumentHeaderRecordId);
        end;

        // Establecer el id del registro del encabezado del documento coincidente en la sesión
        GlobalCodeunitSIMDIProcessSessionSI.SetProcessedHeaderRecordId(LocalDocumentHeaderRecordId);
        Commit();
    end;
}

Este ejemplo ilustra el “manejo de desviaciones impulsado por la sesión”: la sesión proporciona un conjunto de datos de desviación estructurado; tu código aplica las desviaciones aceptadas, registra lo que cambió y reporta el encabezado procesado de vuelta en la sesión.


5. Puntos de Personalización Adicionales

Hay más lugares donde se pueden integrar codeunits personalizados, pero el principio guía sigue siendo constante: utiliza la sesión para recuperar el contexto de procesamiento activo y para escribir de nuevo los resultados relevantes para el flujo de trabajo. Esto mantiene tus extensiones consistentes con el flujo de Smart Processing y previene la desviación de la interfaz de usuario/proceso.