Cargar y Descargar Código Personalizado
El sistema de Portales Empresariales que está integrado con Document Central se puede personalizar según el deseo del usuario cuando hay un proceso de carga o descarga utilizando la función de Document Central.
Esta sección proporciona un ejemplo de cómo implementar un código personalizado que se utiliza cuando el usuario carga o descarga documento(s) a través de Document Central.
Ejemplo de Código
En este ejemplo, utilizaremos la tabla Temp en un escenario donde cargar documento(s) completará el Tipo de Contenido para filtrar el contenido por su extensión y también se utiliza para la descarga de archivo(s) también.
AL
codeunit 5492859 "SIM_DPS Sample Upload CU"
{
TableNo = "SIM_DPS Temp Table";
/// <summary>
/// Punto de entrada para el procedimiento de carga personalizado configurado en la configuración de SIM_DPS.
/// Mantiene la muestra dentro de la aplicación DPS y delega la carga real a las aplicaciones de conector instaladas a través de eventos de integración de DPS.
/// </summary>
/// <returns></returns>
trigger OnRun()
var
LocalCodeunitTempBlob: Codeunit "Temp Blob";
LocalSucceedBoolean: Boolean;
LocalUploadEntryNoBigInteger: BigInteger;
LocalErrorText: Text;
LocalHasCachedUploadBlobBoolean: Boolean;
begin
// Comenzar con un resultado negativo para que el conector deba marcar explícitamente la carga como manejada.
LocalSucceedBoolean := false;
// Completar Rec.ContentType cuando DPS no lo pasó, porque el conector DMS utiliza este valor como la categoría de adjunto/tipo de contenido.
EnsureAttachmentCategoryName(Rec);
// Mantener una copia del archivo cargado para que la página de carga de SIM_DPS aún pueda previsualizarlo después de que el conector borre FileData.
LocalHasCachedUploadBlobBoolean := CacheUploadFileData(Rec, LocalCodeunitTempBlob, LocalUploadEntryNoBigInteger);
// Limpiar errores de tiempo de ejecución anteriores para que GetLastErrorText() solo informe errores de la llamada de evento del conector a continuación.
ClearLastError();
// Mantener la muestra en DPS y dejar que las aplicaciones de conector instaladas manejen el objetivo de archivo real.
if not TryUploadThroughConnector(LocalSucceedBoolean, Rec, Rec."Table No." = Database::"SIM_DPS OL") then begin
// TryFunction suprime el error del conector; leerlo de nuevo y mostrarlo como un mensaje para una retroalimentación más clara de la configuración del portal.
LocalErrorText := GetLastErrorText();
if LocalErrorText = '' then
LocalErrorText := GlobalNoConnectorErrorLabelMsg;
Message(GlobalUploadFailedWithReasonLabelMsg, LocalErrorText);
exit;
end;
// Una llamada de evento exitosa aún puede significar que ningún conector aceptó la carga, así que informe eso por separado.
if not LocalSucceedBoolean then
Message(GlobalUploadFailedWithReasonLabelMsg, GlobalNoConnectorErrorLabelMsg);
// El conector archiva el archivo y luego borra SIM_DPS Upload.FileData; restaurarlo para la visualización de documentación/demo.
if LocalSucceedBoolean and LocalHasCachedUploadBlobBoolean then
RestoreUploadFileData(LocalUploadEntryNoBigInteger, LocalCodeunitTempBlob);
end;
/// <summary>
/// Genera el evento de integración de carga de DPS al que se suscriben las aplicaciones de conector.
/// La TryFunction convierte los errores del conector en un fallo booleano para que OnRun pueda mostrar el error con Message() en lugar de Error() duro.
/// </summary>
/// <param name="ParamSucceedBoolean">Establecido en verdadero por el conector cuando la carga se procesó correctamente.</param>
/// <param name="TempParamRecordSIMDPSTempTable">Registro de contexto temporal de DPS que contiene información de tabla, registro, empresa y tipo de contenido.</param>
/// <param name="ParamOrderUploadBoolean">Verdadero cuando la carga pertenece a un flujo de pedido de SIM_DPS OL; falso para cargas normales/manuales.</param>
/// <returns></returns>
[TryFunction]
local procedure TryUploadThroughConnector(var ParamSucceedBoolean: Boolean; var TempParamRecordSIMDPSTempTable: Record "SIM_DPS Temp Table" temporary; ParamOrderUploadBoolean: Boolean)
begin
// Las cargas de pedidos necesitan el evento específico del pedido porque la lógica de carga del conector resuelve el documento objetivo creado de SIM_DPS OL.
if ParamOrderUploadBoolean then
GlobalCodeunitSIMDPSEventPublisher.OnCheckUploadsOrder(ParamSucceedBoolean, TempParamRecordSIMDPSTempTable)
else
// Las cargas normales/manuales utilizan el evento de carga estándar e identifican la carga pendiente por el Número de Entrada.
GlobalCodeunitSIMDPSEventPublisher.OnCheckUploads(ParamSucceedBoolean, TempParamRecordSIMDPSTempTable);
end;
/// <summary>
/// Asegura que el contexto temporal de DPS contenga el nombre de la categoría de adjunto/tipo de contenido.
/// El conector de DMS a DPS lee Rec.ContentType al crear la entrada de Document Central, pero las cargas de pedidos pueden llegar sin este valor establecido.
/// </summary>
/// <param name="TempParamRecordSIMDPSTempTable">Registro de contexto temporal de DPS pasado al código de carga personalizado configurado.</param>
/// <returns></returns>
local procedure EnsureAttachmentCategoryName(var TempParamRecordSIMDPSTempTable: Record "SIM_DPS Temp Table" temporary)
var
LocalRecordSIMDPSUpload: Record "SIM_DPS Upload";
begin
// Si DPS ya proporcionó la categoría/tipo de contenido, manténgalo sin cambios.
if TempParamRecordSIMDPSTempTable.ContentType <> '' then
exit;
LocalRecordSIMDPSUpload.Reset();
// Las cargas manuales pasan el número de entrada de carga en No. BigInt, que es la búsqueda más precisa.
if TempParamRecordSIMDPSTempTable."No. BigInt" <> 0 then
LocalRecordSIMDPSUpload.SetRange("Entry No.", TempParamRecordSIMDPSTempTable."No. BigInt")
else
// Las cargas de pedidos no pasan un número de entrada; están vinculadas al registro de SIM_DPS Upload por ID de Registro.
LocalRecordSIMDPSUpload.SetRange("Record ID", TempParamRecordSIMDPSTempTable.RecID);
// Restringir a la empresa actual y archivos subidos pendientes para que las filas completadas/canceladas no puedan proporcionar una categoría obsoleta.
LocalRecordSIMDPSUpload.SetRange(Company, TempParamRecordSIMDPSTempTable.Company);
LocalRecordSIMDPSUpload.SetRange(Status, LocalRecordSIMDPSUpload.Status::Uploaded);
// Copiar el tipo de contenido de carga en el registro temporal; las aplicaciones de conector luego lo reciben a través del parámetro del evento.
if LocalRecordSIMDPSUpload.FindFirst() then
TempParamRecordSIMDPSTempTable.ContentType := CopyStr(LocalRecordSIMDPSUpload."Content Type", 1, MaxStrLen(TempParamRecordSIMDPSTempTable.ContentType));
end;
/// <summary>
/// Copia el SIM_DPS Upload.FileData original en un Temp Blob antes de que el conector procese la carga.
/// La copia solo es necesaria para las entradas de lista de carga normales, porque la acción Ver lee el archivo de SIM_DPS Upload.FileData.
/// </summary>
/// <param name="TempParamRecordSIMDPSTempTable">Registro de contexto temporal de DPS pasado al código de carga personalizado configurado.</param>
/// <param name="ParamCodeunitTempBlob">Temp Blob que recibe una copia del contenido del archivo cargado.</param>
/// <param name="ParamUploadEntryNoBigInteger">Número de Entrada de la fila de SIM_DPS Upload que debe ser restaurada más tarde.</param>
/// <returns>Verdadero cuando se encontró y almacenó un blob de archivo; de lo contrario, falso.</returns>
local procedure CacheUploadFileData(TempParamRecordSIMDPSTempTable: Record "SIM_DPS Temp Table" temporary; var ParamCodeunitTempBlob: Codeunit "Temp Blob"; var ParamUploadEntryNoBigInteger: BigInteger): Boolean
var
LocalRecordSIMDPSUpload: Record "SIM_DPS Upload";
begin
// Solo las cargas normales pasan un Número de Entrada; las cargas de pedidos pueden contener múltiples archivos vinculados y no se muestran en la página 5163558.
if TempParamRecordSIMDPSTempTable."No. BigInt" = 0 then
exit(false);
if not LocalRecordSIMDPSUpload.Get(TempParamRecordSIMDPSTempTable."No. BigInt") then
exit(false);
if not LocalRecordSIMDPSUpload.FileData.HasValue() then
exit(false);
// Copiar el BLOB antes de que el conector lo borre durante el procesamiento exitoso de la carga.
LocalRecordSIMDPSUpload.CalcFields(FileData);
Clear(ParamCodeunitTempBlob);
ParamCodeunitTempBlob.FromRecord(LocalRecordSIMDPSUpload, LocalRecordSIMDPSUpload.FieldNo(FileData));
ParamUploadEntryNoBigInteger := LocalRecordSIMDPSUpload."Entry No.";
exit(true);
end;
/// <summary>
/// Restaura SIM_DPS Upload.FileData después de una carga exitosa del conector.
/// Esto mantiene la acción Ver de la página de Carga utilizable mientras permite que el conector complete su proceso de archivo.
/// </summary>
/// <param name="ParamUploadEntryNoBigInteger">Número de Entrada de la fila de SIM_DPS Upload a restaurar.</param>
/// <param name="ParamCodeunitTempBlob">Temp Blob que contiene el contenido original del archivo cargado en caché.</param>
local procedure RestoreUploadFileData(ParamUploadEntryNoBigInteger: BigInteger; var ParamCodeunitTempBlob: Codeunit "Temp Blob")
var
LocalRecordSIMDPSUpload: Record "SIM_DPS Upload";
LocalInStream: InStream;
LocalOutStream: OutStream;
begin
if not LocalRecordSIMDPSUpload.Get(ParamUploadEntryNoBigInteger) then
exit;
// No sobrescribir el contenido si otro controlador ya mantuvo o restauró el archivo.
if LocalRecordSIMDPSUpload.FileData.HasValue() then
exit;
ParamCodeunitTempBlob.CreateInStream(LocalInStream);
LocalRecordSIMDPSUpload.FileData.CreateOutStream(LocalOutStream);
CopyStream(LocalOutStream, LocalInStream);
LocalRecordSIMDPSUpload.Modify();
end;
var
GlobalCodeunitSIMDPSEventPublisher: Codeunit "SIM_DPS Event Publisher";
GlobalNoConnectorErrorLabelMsg: Label 'Ningún conector procesó la carga. Por favor, verifique que el conector esté instalado, publicado y configurado.';
GlobalUploadFailedWithReasonLabelMsg: Label 'La carga no pudo ser procesada. Razón: %1', Comment = '%1 = Razón del error';
}
AL
codeunit 5492860 "SIM_DPS Sample Download CU"
{
TableNo = "SIM_DPS Temp Table";
/// <summary>
/// Punto de entrada para el procedimiento de descarga personalizado configurado en la configuración de SIM_DPS.
/// Delegar la lista de archivos y la descarga de archivos a las aplicaciones de conector instaladas a través de eventos de integración de DPS.
/// </summary>
/// <returns></returns>
trigger OnRun()
begin
case UpperCase(Rec.Param) of
// Primera llamada al portal: el conector escribe la lista de archivos XML en Blob.
'GETFILES':
GlobalCodeunitSIMDPSEventPublisher.OnDeserializeDownloadGetFile(Rec);
// Segunda llamada al portal: el conector escribe el contenido del archivo seleccionado en File Blob.
'DOWNLOADFILE':
begin
FetchDocumentAttachment(Rec);
EnsureDownloadedFileWasReturned(Rec);
end;
else
// Las acciones desconocidas no se envían al código del conector porque el conector solo maneja las dos acciones de descarga del portal anteriores.
Message(GlobalUnknownDownloadActionLabelMsg, Rec.Param);
end;
end;
/// <summary>
/// Obtiene el archivo solicitado directamente de la tabla de Adjuntos de Documentos de Business Central.
/// </summary>
/// <returns></returns>
local procedure FetchDocumentAttachment(var TempParamRecordSIMDPSTempTable: Record "SIM_DPS Temp Table" temporary)
var
LocalRecordDocumentAttachment: Record "Document Attachment";
LocalRecordTenantMedia: Record "Tenant Media";
LocalCodeunitDocumentAttachmentMgmt: Codeunit "Document Attachment Mgmt";
LocalRecordRef: RecordRef;
LocalBaseFileNameText: Text;
LocalFileExtText: Text;
LocalInStream: InStream;
LocalOutStream: OutStream;
begin
// Abrir el registro empresarial objetivo basado en el ID de Registro entrante
if not LocalRecordRef.Get(TempParamRecordSIMDPSTempTable.RecID) then
exit;
// Filtrar la tabla de Adjuntos de Documentos para este registro específico
LocalCodeunitDocumentAttachmentMgmt.SetDocumentAttachmentFiltersForRecRef(LocalRecordDocumentAttachment, LocalRecordRef);
// Dividir el nombre de archivo solicitado en Nombre Base y Extensión para coincidir con la estructura de tabla de BC
SplitFilename(TempParamRecordSIMDPSTempTable.Filename, LocalBaseFileNameText, LocalFileExtText);
LocalRecordDocumentAttachment.SetRange("File Name", CopyStr(LocalBaseFileNameText, 1, MaxStrLen(LocalRecordDocumentAttachment."File Name")));
if LocalFileExtText <> '' then
LocalRecordDocumentAttachment.SetRange("File Extension", CopyStr(LocalFileExtText, 1, MaxStrLen(LocalRecordDocumentAttachment."File Extension")));
// Si se encuentra el archivo, transmitir el contenido de Media al File Blob de la Tabla Temp
if LocalRecordDocumentAttachment.FindFirst() then
if LocalRecordDocumentAttachment."Document Reference ID".HasValue then
if LocalRecordTenantMedia.Get(LocalRecordDocumentAttachment."Document Reference ID".MediaId) then begin
LocalRecordTenantMedia.CalcFields(Content);
LocalRecordTenantMedia.Content.CreateInStream(LocalInStream);
TempParamRecordSIMDPSTempTable."File Blob".CreateOutStream(LocalOutStream);
CopyStream(LocalOutStream, LocalInStream);
end;
end;
/// <summary>
/// Divide un nombre de archivo completo en nombre base y extensión para el filtrado de Adjuntos de Documentos.
/// </summary>
/// <returns></returns>
local procedure SplitFilename(ParamFilenameText: Text; var ParamBaseFilenameText: Text; var ParamExtensionText: Text)
var
LocalInteger: Integer;
begin
ParamBaseFilenameText := ParamFilenameText;
ParamExtensionText := '';
for LocalInteger := StrLen(ParamFilenameText) downto 1 do
if ParamFilenameText[LocalInteger] = '.' then begin
ParamBaseFilenameText := CopyStr(ParamFilenameText, 1, LocalInteger - 1);
ParamExtensionText := CopyStr(ParamFilenameText, LocalInteger + 1);
exit;
end;
end;
local procedure EnsureDownloadedFileWasReturned(var TempParamRecordSIMDPSTempTable: Record "SIM_DPS Temp Table" temporary)
begin
if TempParamRecordSIMDPSTempTable."File Blob".HasValue() then
exit;
Error(GlobalEmptyDownloadFileLabelErr, TempParamRecordSIMDPSTempTable.Filename);
end;
var
GlobalCodeunitSIMDPSEventPublisher: Codeunit "SIM_DPS Event Publisher";
GlobalEmptyDownloadFileLabelErr: Label 'La descarga no devolvió contenido de archivo para %1. Por favor, verifique la entrada del documento DMS y la configuración del conector.', Comment = '%1 = Nombre de archivo';
GlobalUnknownDownloadActionLabelMsg: Label 'Acción de descarga desconocida %1.', Comment = '%1 = Acción de descarga';
}