Tuesday 13 March 2012

Save an Microsoft Dynamics AX 2009 report to a PDF file (Second Part) - Save the file to a network location

In this part of the article we'll continue to build our functionality to save an Axapta report to a PDF format, then save this file to a network location, and then send it as an attachment in outlook.

The firt part of this series in here http://daynamicsaxaptaforum.blogspot.in/2012/03/save-microsoft-dynamics-ax-2009-report.html

In the prior article I created a job to save an Axapta report to PDF into a local drive ... C:\. In this article, the code from the last article is integrated within the Run() method of a class I created called SalesConfirmReportEmail.

The SalesConfirmReportEmail class gets executed when a a user wants to send and post an Order Acknowledgement on a Sales Order. I will not discuss how the code works from the moment the user clicks ok in the SalesEditLines form, but I will point out the path that the code follows:

So, the user opens the Sales Order form, finds a record, goes to Posting > Acknowledgement , does whatever he/she needs to do and clicks OK.

In my implementation, the user can choose the output for the order confirmation report. This is, in the DocDestination drop down list he/she can choose Preview, Print, and/or Email. In this case the user wants to print the report.


As soon as the user clicks OK, the code goes through the following:
  • Classes\FormMenuButton\Clicked
  • Classes\FormFunctionButtonCOntrol\Clicked
  • Classes\SalesFormLetter\Main
  • Classes\SalesFormLetter\MainOnServer
  • Classes\SalesFormLetter\Run
  • Classes\SalesFormLetter_Confirm\printJournal
  • Data Dictionary\Tables\CustConfirmJour\Methods\printJournal
  • Classes\SalesConfirmReportEmail\postingPrint
  • Classes\SalesConfirmReportEmail\run
  • Classes\SysInetMail\SendMailAttach
  • Classes\SysINetOutlook\AddAttachment
The following is the code of CustConfirmJour class, which is where I call the SalesConfirmReportEmail class

void printJournal(SalesFormLetter   salesFormLetter = null,
                  RecordSortedList  journalList     = null)
{
    Args            parameters = new Args();
    MenuFunction    salesConfirmMenu;
    ;

                              
    if(salesFormLetter && salesFormLetter.SalesParmUpdate().DocDestination == glPrintType::Printer)
    {
        salesConfirmMenu = new MenuFunction(menuItemoutputStr(SalesConfirmation), MenuItemType::Output);

        parameters.caller(salesFormLetter);
        if (journalList)
        {
            parameters.object(journalList);
        }
        else
        {
            parameters.record(this);
        }
        salesConfirmMenu.run(parameters);
    }
    else
    {
        //Calling my report confirmation class
       //Notice that I'm passing the salesformletter class and journal list
        SalesConfirmReportEmail::postingPrint(salesFormLetter,journalList);
    }
}

The following is the code for the SalesConfirmReportEmail class. It is going to be long.

class SalesConfirmReportEmail
{
    RecordSortedList        journalList;
    glPrintType             printType;
    PrintJobSettings        printJobSetting;
    FileName                fileName;
    FilePath                filePath;
   
    //Earias - 2/22/2011
    SalesTable              salesTable;
    CustConfirmJour         custConfirmJour;
    DirPartyId              dirPartyId;
    SalesQuotationTable     salesQuotationTable;
    Filename                prtFile;
    Filepath                prtFilePath;
    Email                   email;
    Email                   CCEmail;
    Telefax                 fax;
    SysINetMail_GLBTS       mail;
    str                     _mailBody;
}

Email CCEMail(SalesTable    _salesTable)
{
    Email   _ccEmail;
    EmplTable   _emplTableOutside;
    EmplTable   _emplTableInside;
    EmplId      _emplIdInside;
    ;

    _emplIdInside = EmplTable::userId2EmplId(_salesTable.SalesResponsible);

    if (_salesTable.SalesTaker == _emplIdInside)
        _ccEmail = Empltable::find(_salesTable.SalesTaker).email();     //outside salesperson set with emplId
    else
    {
        _ccEmail = Empltable::find(_salesTable.SalesTaker).Email();     //outside salesperson set with emplId

        if (!_ccEmail)
            _ccEmail = Empltable::find(_emplIdInside).Email(); //inside salesperson set with UserId
        else
            _ccEmail += "; " + Empltable::find(_emplIdInside).Email(); //inside salesperson
    }

    return _ccEmail;
}

public FileName parmFileName(FileName _fileName = fileName)
{
    ;
    fileName = _fileName;

    return fileName;
}


glPrintType parmglPrintType(glPrintType _printType = printType)
{
    ;
    printType = _printType;
    return printType;
}

void parmJournalList(Common _common)
{
    ;
    journalList = FormLetter::createJournalListCopy(_common);

}

Container parmPrintJobSettings(PrintJobSettings _printJobSetting = printJobSetting)
{

    ;
    return printJobSetting.packPrintJobSettings();
}

void printJournal(CustConfirmJour _custConfirmJour)
{
     Args            parameters = new Args();
    MenuFunction     salesConfirmMenu;

    ;
    salesConfirmMenu = new MenuFunction(menuItemoutputStr(SalesConfirmation),MenuItemType::Output);
    parameters.caller(this);
    parameters.record(_custConfirmJour);

    salesConfirmMenu.run(parameters);

}

void setJournalList(RecordSortedList _journalList)
{
    journalList = _journalList;
}

static void main (Args _args)
{

    SalesConfirmReportEmail thisClass = new SalesConfirmReportEmail();
  
    ;
    breakpoint;
    if(!_args.caller())
    {
        throw error("Invalid calling location");
    }
   
    thisClass.parmglPrintType(_args.parmEnum());
    thisClass.parmJournalList(_args.record());
    thisClass.run();
}

static void postingPrint(SalesFormLetter      salesFormLetter,
                         RecordSortedList     journalList)
{
    SalesConfirmReportEmail thisClass = new SalesConfirmReportEmail();
    ;
    thisClass.parmglPrintType(salesFormLetter.SalesParmUpdate().DocDestination);
    thisClass.setJournalList(journalList);
    thisclass.run();
}

static glPrintType reportPrintType()
{
    glPrintType printType;
    ;
    return printType;
}

public DirPartyId GetContactEmailFromDirParty(SalesTable _salesTable)
{
    ;
   
    dirPartyId = CustTable::find(_salesTable.CustAccount, false).PartyId;
    return dirPartyId;
}


Now, the Run()  Method is very long. Here there are a few things that I want to explain before I go on. In this method I'm saving my Axapta report in a Network folder.

Yo handle this I did not want to have a constant string where the netwrok address is assigned. The reason is that I also needed to specify an alternative address in case the Network address was not available. So, if the \\NetworkLocation.com\MyFolder is not available, then we will save the file to a temp folder in the user's machine.

For this I created a table names DocuParameters as Shown below:




Then, I'm getting the PrimaryFileOutputPath from this table, if this is not Available, then I'm getting the AlternativeFileOutputPath. I guess there are a  100 ways to do this. I just choose to do it like this as it is more centralize for my purposes.

Also, after the RUN method code, I will add a method that gets the email of an Global Address Book record or PartyID.

NOTE: In the Run() method below I'm using settings that you might not need.

void run()
{
    SalesId             Id;
    int                 i;
    str                 prtFileExt = '.pdf';
    SalesFormLetter     salesFormLetter = SalesFormLetter::construct(DocumentStatus::Confirmation, false);
    PrintJobSettings    printJobSettings = new PrintJobSettings();
    Args                args = new Args();
    DirPartyId          _partyId

    ;

    if(printType == glPrintType::Email || printType == glPrintType::Fax || printType == glPrintType::Printer)
    {
        //This is where I store the Network Address
         prtFilepath = DocUParameters::find().PrimaryFileOutputPath;
         if ( !WinApi::fileExists(prtFilePath) )
         {
            prtFilePath =  DocUParameters::find().AlternateFileOutputPath;
         }
         //Setting the print parameters to file and PDF
         printJobSettings.setTarget(PrintMedium::File);
         printJobSettings.format(PrintFormat::PDF);
    }
    else
    {
        if(printType == glPrintType::Preview)
        {
            printJobSetting = new PrintJobSettings();
           // printJobSetting.unpackPrinterSettings(salesFormLetter::getPrinterSettingsFormletter(DocumentStatus::Invoice));
            printJobSetting.setTarget(PrintMedium::Screen);
        }
    }

    if(journalList)
    {
        if(journalList.first(CustConfirmJour))
        {
            Do
            {
                if(! printType == glPrintType::Preview)
                {
                    prtFile = custConfirmJour.SalesId;
                    printJobSettings.fileName(prtFilePath + prtFile + prtFileExt);
                    salesFormLetter.updatePrinterSettingsFormLetter(printJobSettings.packPrintJobSettings());

                    select firstOnly custConfirmJour
                       where custConfirmJour.salesid == prtFile;

                    args.record(custConfirmJour);
                    args.caller(salesFormLetter);

                    new MenuFunction(menuitemoutputstr(SalesConfirmation), MenuItemType::Output).run(args);
                    //Here we set the file name to the file name and the extension (.pdf)
                    prtFile = prtFile + prtFileExt;
                }
                else if(printType == glPrintType::Preview)
                {
                    printJobSetting = new PrintJobSettings();
                    this.printJournal(CustConfirmJour);
                    return;
                }

                salesTable = CustConfirmJour.salesTable();
               
                if(this.parmglPrintType() == glPrintType::Email)
                {
                    _partyId = this.GetContactEmailFromDirParty(salesTable);
                    info(_partyId);
                    //Here I call a custom method to resolve the email from a Party ID
                    email = salesQuotationTable.contactEmail(_partyId, EmailTypes::OrderAcknowledgement);
                    info(email);
                    if(!email)
                        email = ContactPerson::find(salesTable.QuoteContactPersonId, false).Email;

                    if(!email)
                    {
                        email = CustTable::find(custConfirmJour.OrderAccount, false).Email;

                        //Here I set a custom error method when contacts don't have emails
                       //Look for this code below after this method
                        salesTable.SetContactEmailErrorMessage(prtFile, prtFilePath, _partyId);
                        return;
                    }

                    mail = new SysINetMail_GLBTS();
                    mail.sendMailAttach_OLD(email, CCEmail, 'Ref.: '+ CustConfirmJour.CustomerRef +'// '+ 'Acknowledgement ' + CustConfirmJour.SalesId + '','', true, prtfilePath + prtfile,  prtfile);
                    WinApi::deleteFile(prtfilePath + prtfile);
                }
               
                if ( this.parmglPrintType() == glPrintType::Fax )
                {
                    fax =this.parseFaxNumber( ContactPerson::find(salesTable.quoteContactPersonId,false).TeleFax);
                    if(!fax)                       
                        fax =this.parseFaxNumber(CustTable::find(custConfirmJour.OrderAccount,false).Telefax);

                    email = fax + '@j2send.com';
                }

            }while(journalList.next(custConfirmJour));
        }
    }
}

This method is in my SalesQuotationTable

Email contactEmail(DirPartyId partyId, EmailTypes type, QuotationId quoteId = '')
{
    Email                       email;
    ;
    //Get Email based on DirPartyId
    email = AGO_Utilities::GetContactEmailFromPartyID(partyId, type, quoteId);

    return email;
}

AGO_Utilities::GetContactEmailFromPartyID is a class method - see it below


public static Email GetContactEmailFromPartyID(DirPartyId partyId, EmailTypes type, QuotationId quoteId = '')
{
    DirPartyRelationship        dirPartyRelationship;
    Boolean                     breakProcess = false;
    DirPartyId                  childPartyId;
    ContactPerson               contactPerson;
    ContactPersonId             contactPersonId;
    Email                       email = '';
    ;

    while select dirPartyRelationship where dirPartyRelationship.ParentPartyId == partyId
    {
        childPartyId = dirPartyRelationship.ChildPartyId;
        if(childPartyId)
        {
            switch(type)
            {
                case EmailTypes::SalesQuotes:
                    //Check if sales quote has a contact to it
                    contactPersonId = SalesQuotationTable::find(quoteId).ContactPersonId;
                    if(contactPersonId)
                        select * from contactPerson where contactPerson.ContactPersonId == contactPersonId;
                    else
                        breakProcess = true;
                    break;
                case EmailTypes::Invoice:
                    select * from contactPerson where contactPerson.PartyId == childPartyId &&
                               contactPerson.EmailType_Invoice == NoYes::Yes;
                    break;
                case EmailTypes::OrderAcknowledgement:
                    select * from contactPerson where contactPerson.PartyId == childPartyId &&
                               contactPerson.EmailType_OrderAcknowledgement == NoYes::Yes;
                    break;
                case EmailTypes::ShippingMarks:
                    select * from contactPerson where contactPerson.PartyId == childPartyId &&
                               contactPerson.EmailType_ShippingMarks == NoYes::Yes;
                    break;
                case EmailTypes::ARStatement:
                    select * from contactPerson where contactPerson.PartyId == childPartyId &&
                               contactPerson.EmailType_ARStatement == NoYes::Yes;
                    break;
            }

            if(contactPerson.Email || contactPerson.Email2)
            {
                if(!contactPerson.Email)
                    email =  contactPerson.Email2;
                else
                    email = contactPerson.Email;

                break;
            }

            if(breakProcess)
                break;
        }
    }

    return email;
}

The next method seats in the Sales Table sets  an error message when the contact does not have an email. It creates a Info log where a user can double click on the message and go directly to a record in the Contacts Table

void SetContactEmailErrorMessage(FileName fileName, FilePath filePath, DirPartyId partyId)
{
    ContactPersonId contactPersonId;
    SalesQuotationTable smmQuotationTable;
    SysInfoAction_FormRun   SysInfoAction;
    ;
    contactPersonId = smmQuotationTable.GetContactIdFromPartyID(partyId);
    WinApi::deleteFile(filePath+fileName);
    if(contactPersonId || contactPersonId != '')
    {
        SysInfoAction = SysInfoAction_FormRun::newFormnameDesc(FormStr(ContactPersonLookup), "@AIC586");
        info('@AIC583',"", SysInfoAction_TableField::newBuffer(ContactPerson::find(contactPersonId)));
    }
    else
        Box::info('@AIC584');
}