Thursday, September 5, 2019

Attach documents in Dynamics Ax programmatically in x++ code. different options

I had to do some custom development to upload/Attach document in Ax programmatically from a webApi. So I did some research on what is the best way to it.

here I share the different options I found to do it.

OPTION 1
USE \Classes\DocuActionArchive\add (it extends DocuActionFile)

NOTE, is the preferred way, but doesn't let you customize anything. the description of the file is the file name

static void JobEF_DocumentInsert(Args _args)
{
    DocuType docuType;
    DocuTypeId docuTypeId;
    DocuRef docuRef;
    DocuValue docuValue;
    DocuActionArchive archive;

    Filename filename;

    HcmDiscussion hcmDiscussion;

    docuTypeId = 'AppraisalEnrico';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('xx', HcmWorker::findByPersonnelNumber('xx').RecId);

    filename = @'\\...\Myfile.PNG';

    if ( ! hcmDiscussion ) return;

    docuType = DocuType::find(docuTypeId);

    ttsBegin;

    docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
    docuRef.RefTableId   = hcmDiscussion.TableId;
    docuRef.RefRecId     = hcmDiscussion.RecId;
    docuRef.TypeId       = docuType.TypeId;
    docuRef.insert();

    archive = new DocuActionArchive();

    archive.add(docuRef, filename);

    ttsCommit;
}

OPTION 2
USE \Classes\DocuActionFile\insertDocuValue

Note: seems quite good option. you can't use this method directly as it is an abstract class, use \Classes\DocuActionArchive\

static void JobEF_DocumentInsert2(Args _args)
{
    DocuType docuType;
    DocuTypeId docuTypeId;
    DocuRef docuRef;
    DocuValue docuValue;
    DocuActionArchive archive;

    Filename filename;

    HcmDiscussion hcmDiscussion;

    docuTypeId = 'AppraisalEnrico';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('xx', HcmWorker::findByPersonnelNumber('xx').RecId);

    filename = @'\\...\Myfile.PNG';

    if ( ! hcmDiscussion ) return;

    docuType = DocuType::find(docuTypeId);

    ttsBegin;

    docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
    docuRef.RefTableId   = hcmDiscussion.TableId;
    docuRef.RefRecId     = hcmDiscussion.RecId;
    docuRef.TypeId       = docuType.TypeId;
    docuRef.Name         = strFmt('prova - %1', Docu::getFileName(filename));
    docuRef.insert();

    archive = new DocuActionArchive();

    archive.insertDocuValue(docuRef, filename);

    ttsCommit;
}

OPTION 3
NOTE: is using Docu::insertFile. more complex.

static void JobEF_DocumentInsert1(Args _args)
{
    DocuType docuType;
    DocuTypeId docuTypeId;
    DocuRef docuRef;
    DocuValue docuValue;
    DocuActionArchive archive;

    Filename filename;
    DocuValueFile file;
    BinData binData;
    boolean fileLocked;

    HcmDiscussion hcmDiscussion;
    #File
    
    //done like: \Classes\TrvImportReceiptsBatch\processFolder
    DocuValueFile getDocuValueFile(FileName _fullPathOfFile)
    {
        binData = new BinData();

        new FileIOPermission(_fullPathOfFile,'r').assert();
        // BP Deviation documented
        binData.loadFile(_fullPathOfFile);
        CodeAccessPermission::revertAssert();
        return binData.getData();
    }
    
    //done like: \Data Dictionary\Tables\DocuValue\Methods\writeDocuValue
    DocuValueFile getDocuValueFile1(FileName _fullPathOfFile)
    {
        binData = new BinData();
        if (isRunningOnServer())
        {
            // Assert permission and get the temp filename
            new FileIOPermission(_fullPathOfFile,#io_read).assert();
            // BP deviation documented
            fileLocked = WinApiServer::fileLocked(_fullPathOfFile);
            CodeAccessPermission::revertAssert();
        }
        else
        {
            // BP deviation documented
            fileLocked = WinApi::fileLocked(_fullPathOfFile);
        }

        // Insert to database
         if (fileLocked)
         {
            info("@SYS72783");
         }
        else
        {
            // LoadFile demands read permission on the file
            new FileIOPermission(_fullPathOfFile, #io_read).assert();
            // BP deviation documented
            if (binData.loadFile(_fullPathOfFile)) //only works if file not locked
            {
                file = binData.getData();
            }
            CodeAccessPermission::revertAssert();
        }
        return file;
    }
    
    docuTypeId = 'AppraisalEnrico';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('123', HcmWorker::findByPersonnelNumber('123').RecId);

    filename = @'\\...\Myfile.PNG';

    file = getDocuValueFile(filename);

    if ( ! hcmDiscussion ) return;

    docuType = DocuType::find(docuTypeId);

    ttsBegin;

    docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
    docuRef.RefTableId   = hcmDiscussion.TableId;
    docuRef.RefRecId     = hcmDiscussion.RecId;
    docuRef.TypeId       = docuType.TypeId;

    docuValue = Docu::insertFile(docuRef, filename, file, true);
    docuRef.ValueRecId = docuValue.RecId;
    docuRef.Name = 'mia prova';

    docuRef.insert();

    ttsCommit;
}

OPTION 4
USE \Classes\DocumentFileHelper\attachDocumentAsUser

SEE EXAMPLE: \Data Dictionary\Tables\RetailDiscountCode\Methods\createBarCodeImage

private void createBarCodeImage()
{
    #define.AttachmentName('BarcodeImage.jpg')

    RetailSharedParameters sharedParams;
    BarcodeSetup barcodeSetup;
    str base64ImageString;
    container attachDocumentParams;
    DocuRef DocuRefTable;
    RecId docuRefRecId;

    if (this.BarCode)
    {
        // Get selected barcode
        select firstOnly BarcodeSetupId from sharedParams
        join RetailBarcodeMask, fontName, fontSize from barcodeSetup
        where sharedParams.barcodeSetupId == barcodeSetup.barcodeSetupId;

        // Get base64 string of barcode image.
        base64ImageString = RetailBarcodeManagement::getBarcodeJpegImageAsBase64String(this.BarCode, barcodeSetup.fontName, barcodeSetup.fontSize);

        // Attach barcode image to the discount code.
        attachDocumentParams = [0, base64ImageString, #AttachmentName, '', DateTimeUtil::utcNow(), this.TableId, this.RecId, curext(), DocuType::typeFile()];
        [docuRefRecId] = DocumentFileHelper::attachDocumentAsUser(attachDocumentParams);

        // Make attached image external.
        select forUpdate DocuRefTable where DocuRefTable.RecId == docuRefRecId;
        // set document description to 'Bar code'
        DocuRefTable.Name = "@RET3053";
        DocuRefTable.Restriction = DocuRestriction::External;
        DocuRefTable.update();
    }
}

OR see \Classes\TrvUnreconciledExpenseService\createAttachments

OR \Classes\TrvReceiptService\createReceiptHelper

private DocuRef createReceiptHelper(RefTableId _tableId, RefRecId _recId, DataAreaId _dataAreaId, str _name, str _documentName, str _documentContents)
{
    Filename documentName;
    FilePath documentUrl;
    RefRecId contentType;
    str documentFile;
    DocumentFileReceiveDate receiveDate;
    DocuTypeId docuTypeId;
    TrvReceiptsHelper trvReceiptsHelper = new TrvReceiptsHelper();
    container args;
    RecId createdRecId;
    DocuRef docuRef;

    [documentName, documentUrl, contentType, receiveDate, documentFile] = DocumentFileHelper::getValidateUnpackedDocumentFileData(
                                                                _documentName, "", "",
                                                                DateTimeUtil::utcNow(), _documentContents);

    docuTypeId = trvReceiptsHelper.getDocuTypeId();

    args = [contentType, documentFile, documentName, documentUrl, receiveDate, _tableId, _recId, _dataAreaId, docuTypeId];

    [createdRecId] = DocumentFileHelper::attachDocumentAsUser(args);

    if (createdRecId != 0)
    {
        docuRef = DocuRef::findRecId(createdRecId, true);

        if (_name != '')
        {
            docuRef.Name = _name;
        }
        else
        {
            docuRef.Name = "@SYS138348";
        }
        docuRef.update();
    }

    return docuRef;
}

NOTE to get the documentContents see: \Classes\TrvReceiptService\getDocumentContents

.... 
    // Get the receipt contents and serialize them.
    binData = DOCommonDocuUtils::GetDocuContent(docuRef);
    if (binData && conLen(binData.getData()) > 0)
    {
        documentContents = binData.base64Encode();
    }

    return documentContents;


------------------------------------------------

Finally I didn’t use none of them but I ended extending an existing class which gives me the advantage to code less (always use existing methods!) and also to upload all the files in a directory in one go. So from the WebApi I create a shared folder where I upload all the documents I want to be attached to an Ax record and my extended class is responsible to Attach the document.
When the process ends Ax deletes the shared folder.

class MYD_HcmDiscussionAttachmentHelper extends TrvImportReceiptsBatch
{
    HcmDiscussion hcmDiscussion;
    DocuTypeId docuTypeId;
}

//for trials only
public static void main(Args _args)
{
    FilePath dirPath;
    HcmDiscussion hcmDiscussion;

    MYD_HcmDiscussionAttachmentHelper cl = new MYD_HcmDiscussionAttachmentHelper();

    dirPath = @'\\...\temp\000198';
    hcmDiscussion = HcmDiscussion::findByDiscussionWorker('000198', HcmWorker::findByPersonnelNumber('123').RecId);

    cl.parmDirPath(dirPath);
    cl.parmHcmDiscussion(hcmDiscussion);
    cl.parmDocuTypeId('AppraisalEnrico');
    cl.run();
}

boolean importFile(Filename _filename, DocuValueFile _file)
{
    DocuRef docuRef;
    DocuValue docuValue;
    boolean ret = false;

    if (hcmDiscussion)
    {
        ttsbegin;
            docuRef.RefCompanyId = hcmDiscussion.DataAreaId;
            docuRef.RefTableId   = hcmDiscussion.TableId;
            docuRef.RefRecId     = hcmDiscussion.RecId;
            docuRef.TypeId       = docuTypeId;

            docuValue = Docu::insertFile(docuRef, _filename, _file, true);
            docuRef.ValueRecId = docuValue.RecId;
            docuRef.Name = System.IO.Path::GetFileNameWithoutExtension(_filename);

            docuRef.insert();
        ttscommit;
        ret = true;
    }

    return ret;
}

public DocuTypeId parmDocuTypeId(DocuTypeId _docuTypeId = docuTypeId)
{
    docuTypeId = _docuTypeId;

    return docuTypeId;
}

public HcmDiscussion parmHcmDiscussion(HcmDiscussion _hcmDiscussion = hcmDiscussion)
{
    hcmDiscussion = _hcmDiscussion;

    return hcmDiscussion;
}

public void run()
{
    boolean result = true;
    try
    {
        super();
    }
    catch
    {
        result = false;
    }

    if (result)
    {
        new FileIOPermission(dirPath,'W').assert();
        System.IO.Directory::Delete(dirPath, true);
        CodeAccessPermission::revertAssert();
    }
}

No comments:

Post a Comment