Adding the Payment Advice to AX 2012 Print Management

By Laura Lake | May 5, 2014

In AX 2012 Print Management, the Payment Advice is not a supported document type out of the box. We recently had a customer request to add this feature so that their vendors could receive the Payment Advice via email. This blog describes the changes needed to meet this requirement. Adding the Payment Advice to AX 2012 Print Management is only one part of the solution. Because the Payment Advice report is designed to print all vendors at once, rather than running the report for each vendor separately, modifications must be made to ensure that each vendor receives a separate pdf file with only his own Payment Advice.

It is not the intention of this blog to rehash the detailed information in the AX 2012 Print Management Integration Guide. Instead, I wanted to expand on the whitepaper with the specific code needed for the Payment Advice. With this in mind, I will not go into a detailed explanation of every class and method, but will only highlight a few important details.

First, it may be helpful to envision the hierarchy as you work through this code.

For the payment advice, we want users to be able to set the preferences at the document level and at the vendor level, so the hierarchy looks like this…

Purch (accounts payable node)

VendTable (vendor node)

BankPaymAdvice (new doc node)

The following existing AX objects were modified for this feature:

Enums:

PrintMgmtNodeType - Added BankPaymAdvice

PrintMgmtDocumentType - Added BankPaymAdvice

Tables:

BankPaymAdviceVendTemp - Added field VendAccount, Added Relation VendTable

PrintMgmtReportFormat - Methods\populate – modified to support the new doc type

Classes:

PrintMgmtNode - Methods\construct – modified for the new node type

PrintMgmtNode_Purch - Methods\getDocumentTypes – modified for the new doc type

PrintMgmtNode_VendTable - Methods\getDocumentTypes – modified for the new doc type

PrintMgmtHierarchy_Purch:

Methods\getNodesImplementation – add BankPaymAdvice as a supported node

Methods\getParentImplementation – add support for BankPaymAdvice (see Objects of Interest)

PrintMgmtDocType:

Methods\getDefaultReportFormat – return the report name for BankPaymAdvice

Methods\getQueryTableId– modified to add the BankPaymAdviceVendTmp table for BankPaymAdvice

VendOutPaym_NACHA - Add the Use Print Management field to the dialog and pass along to the controller class

Methods modified:

ClassDeclaration

Dialog

getFromDialog

init

printPaymAdvice

unpack

The follow custom objects were created for this feature:

Classes:

PrintMgmtNode_BankPaymAdvice – supporting class for the new node type

FDBankPaymAdviceVendController – controller class for the report. Extends SrsPrintMgmtController.

All of the code changes are included below:

Table: PrintMgmtReportFormat

Method\Populate: added the BankPaymAdvice doc type

addAX(PrintMgmtDocumentType::PurchRFQ);

addAX(PrintMgmtDocumentType::PurchRFQAccept);

addAX(PrintMgmtDocumentType::PurchRFQReject);

addAX(PrintMgmtDocumentType::PurchRFQReturn);

addAX(PrintMgmtDocumentType::PurchaseOrderConfirmationRequest);

//Stoneridge Software Begin

addAX(PrintMgmtDocumentType::BankPaymAdvice);

//Stoneridge Software End

 New Classes:

The out of box report uses a controller class that extends SRSReportRunController. This new class is basically a modified copy of BankPaymAdviceVendController extending SrsPrintMgmtController. The entire class is included here.

public class FDBankPaymAdviceVendController extends SrsPrintMgmtController

{

VendOutPaym                 vendOutPaym;

List                       vendOutPaymList;

str                         paymRef;

VendTrans                  vendTransOriginalInvoice;

BankPaymAdviceVendTmp       bankPaymAdviceVendTmp;

CompanyInfo                 companyInfo;

VendTable                   vendTable;

LedgerJournalTrans         ledgerJournalTrans;

SpecTrans                  specTrans;

VendTransOpen               vendTransOpen;

VendTrans                   vendTrans;

Common                     transtable;

UsePrintMgmt               paymAdvUsePrintMgmt;

str                         newPaymRefLable;

Amount                     settledAmount;

CurrencyCode               settledCurrencyCode;

str                         groupBy;

int                         currentSessionId;

PrintCopyOriginal                           printCopyOriginal;

LogisticsAddressing         companyAddress;

#define.ReportName('BankPaymAdviceVend.Report')

}

private str currentSessionId()

{

return int2str(currentSessionId);

}

private VendCashDiscAmount getCashDiscAmount()

{

// gets the cash discount that is specified when settling an invoice.

VendTransCashDisc   vendTransCashDisc;

changecompany(vendTransOpen.company())

{

vendTransCashDisc = VendTransCashDisc::findByUseCashDisc(vendTransOpen.TableId,

vendTransOpen.RecId,

vendTransOpen.UseCashDisc,

ledgerJournalTrans.TransDate);

}

return -vendTransCashDisc.CashDiscAmount;

}

protected void initPrintMgmtReportRun()

{

paymAdvUsePrintMgmt = this.parmArgs().parmEnum();

printMgmtReportRun=PrintMgmtReportRun::construct(

PrintMgmtHierarchyType::Purch,PrintMgmtNodeType::BankPaymAdvice,PrintMgmtDocumentType::BankPaymAdvice);

printMgmtReportRun.parmForcePrintJobSettings(!paymAdvUsePrintMgmt);

printMgmtReportRun.parmReportRunController(this);

}

private void insertBankPaymAdviceVendTmp(boolean isSettledVoucherAmount,

boolean isRedrawPayments)

{

Blobdata image;

bankPaymAdviceVendTmp.CompanyPhone     = companyInfo.phone();

bankPaymAdviceVendTmp.CompanyTeleFax   = companyInfo.teleFax();

bankPaymAdviceVendTmp.CompanyGiro       = companyInfo.Giro;

bankPaymAdviceVendTmp.CompanyCoRegNum   = companyInfo.CoRegNum;

bankPaymAdviceVendTmp.CompanyAddress   = companyAddress;

bankPaymAdviceVendTmp.VendName         = DirPartyTable::findRec(vendTable.Party).Name;

bankPaymAdviceVendTmp.VendAddress       = LogisticsPostalAddress::addressFromRecId(vendTrans.RemittanceAddress);

bankPaymAdviceVendTmp.PaymAdviceDate   = systemDateGet();

bankPaymAdviceVendTmp.PaymentReference = paymref;

BankPaymAdviceVendTmp.VendAccount       = VendTable.AccountNum;

if (newPaymRefLable)

{

bankPaymAdviceVendTmp.PaymRefLabel = newPaymRefLable;

}

if (!isRedrawPayments)

{

bankPaymAdviceVendTmp.Invoice       = vendTrans.Invoice;

bankPaymAdviceVendTmp.InvoiceDate   = vendTrans.TransDate;

bankPaymAdviceVendTmp.CurrencyCode = vendTrans.CurrencyCode;

bankPaymAdviceVendTmp.AmountCur     = vendTrans.AmountCur;

bankPaymAdviceVendTmp.CashDisc     = this.getCashDiscAmount();

bankPaymAdviceVendTmp.Balance01     = -specTrans.Balance01;

bankPaymAdviceVendTmp.TransDate     = ledgerJournalTrans.TransDate;

if (isSettledVoucherAmount)

{

bankPaymAdviceVendTmp.SettledAmount   = settledAmount;

}

}

else

{

bankPaymAdviceVendTmp.Invoice       = vendTransOriginalInvoice.Invoice;

bankPaymAdviceVendTmp.InvoiceDate   = vendTransOriginalInvoice.TransDate;

bankPaymAdviceVendTmp.CurrencyCode = vendTransOriginalInvoice.CurrencyCode;

bankPaymAdviceVendTmp.AmountCur     = vendTransOriginalInvoice.AmountCur;

bankPaymAdviceVendTmp.CashDisc     = 0;                                   //we only give the discount once

bankPaymAdviceVendTmp.Balance01     = this.paymentAmountCur();

bankPaymAdviceVendTmp.TransDate     = ledgerJournalTrans.TransDate;

if (isSettledVoucherAmount)

{

bankPaymAdviceVendTmp.SettledAmount   = -settledAmount;

}

}

bankPaymAdviceVendTmp.SessionId           = currentSessionId;

bankPaymAdviceVendTmp.Grouping           = groupBy;

bankPaymAdviceVendTmp.SettledCurrencyCode = settledCurrencyCode;

image = CompanyImage::find(companyInfo.dataAreaId, tablenum(CompanyInfo), companyInfo.RecId).Image;

bankPaymAdviceVendTmp.Image = image;

if (image)

{

bankPaymAdviceVendTmp.PrintImage = NoYesCombo::Yes;

}

else

{

bankPaymAdviceVendTmp.PrintImage = NoYesCombo::No;

}

bankPaymAdviceVendTmp.insert();

}

private AmountCur paymentAmountCur()

{

CustVendTransDetails vendTransDetails = new CustVendTransDetails(vendTransOriginalInvoice);

vendTransDetails.setCustVendTrans(vendTransOriginalInvoice);

return (vendTransOriginalInvoice.AmountCur - vendTransDetails.utilizedCashDisc());

}

protected void preRunModifyContract()

{

Query reportQuery;

 reportQuery = this.parmReportContract().parmQueryContracts().lookup(this.getFirstQueryContractKey());

SrsReportHelper::addParameterValueRangeToQuery(

reportQuery,

tableNum(BankPaymAdviceVendTmp),

fieldNum(BankPaymAdviceVendTmp,SessionId),

this.currentSessionId());

 }

public void processReport(List paramOutPaymlist)

{

boolean ret = false;

boolean calcSum;

VendSettlement vendSettlement;

VendPromissoryNoteInvoice vendPromissoryNoteInvoice;

TaxWithholdTrans taxWithholdTrans;

ListEnumerator li;

VendOutPaymRecord vendOutPaymRecord;

companyInfo = CompanyInfo::find();

companyAddress = companyInfo.postalAddress().Address;

currentSessionId = new xSession().sessionId();

//Delete the previous records if there are existing record corresponding to the current session or there are existing records

//more than two days old.

delete_from bankPaymAdviceVendTmp where ((bankPaymAdviceVendTmp.SessionId == currentSessionId) ||

(bankPaymAdviceVendTmp.DateOfCreation < today() - 2));

li = paramOutPaymlist.getEnumerator();

while (li.moveNext())

{

vendOutPaymRecord = li.current();

ledgerJournalTrans = vendOutPaymRecord.parmCustVendPaym().ledgerJournalTrans();

vendTable = VendTable::findByCompany(vendOutPaymRecord.parmCustVendPaym().ledgerJournalTrans().Company,

vendOutPaymRecord.parmCustVendPaym().ledgerJournalTrans().parmAccount());

this.setPaymReferenceStr();

calcSum = true;

// print a line for each SpecTrans record for the journal line

while select crosscompany specTrans

where specTrans.SpecRecId == ledgerJournalTrans.RecId &&

specTrans.SpecCompany == ledgerJournalTrans.company() &&

specTrans.SpecTableId == tableNum(LedgerJournalTrans) &&

specTrans.RefTableId == tableNum(VendTransOpen)

{

// The report prints from three global table buffers so we need to populate

// the remaining two prior to sending them to the report for printing

vendTransOpen = specTrans.vendTransOpen();

vendTrans = vendTransOpen.vendTrans();

// Remove tax from the payment amount

changecompany (vendTrans.company())

{

select sum(InvoiceTaxWithholdAmount) from taxWithholdTrans

where taxWithholdTrans.JournalNum == ledgerJournalTrans.JournalNum &&

taxWithholdTrans.Invoice   == vendTrans.Invoice;

//vendTrans.AmountCur and spectrans.Balance01 are negative numbers while taxes are postive

vendTrans.AmountCur += taxWithholdTrans.InvoiceTaxWithholdAmount;

specTrans.Balance01 += taxWithholdTrans.InvoiceTaxWithholdAmount;

}

ret = true;

if (calcSum)

{

settledAmount = ledgerJournalTrans.amount();

settledCurrencyCode =ledgerJournalTrans.displayCurrencyCode();

groupBy = vendOutPaymRecord.parmCustVendPaym().ledgerJournalTrans().parmAccount() + settledCurrencyCode;

calcSum = false;

this.insertBankPaymAdviceVendTmp(true,false);

// Sourabh Khosla (Stoneridge Software Inc.) 04/27/2014 FDD:06 (Email Automation) : Start

// Responsible for sending print reports based on the current vendTable account record.

select * from BankPaymAdviceVendTmp

join vendTable where BankPaymAdviceVendTmp.vendAccount == VendTable.accountNum;

{

this.initPrintMgmtReportRun();

printMgmtReportRun.load(BankPaymAdviceVendTmp,BankPaymAdviceVendTmp,CompanyInfo::languageId());

this.outputReports();

}

sleep(1500); // Gives the time to render the report

delete_from BankPaymAdviceVendTmp;

// Sourabh Khosla (Stoneridge Software Inc.) : End

}

else

{

this.insertBankPaymAdviceVendTmp(false,false);

}

}

if (!ret)

{

// this section is run when generating payments from a redraw

// The promissory note has already been settled which is why the noter invoice exists.

// All the data in this select is from the same company and is based on the payment.

while   select specTrans

where   specTrans.SpecTableId == tableNum(LedgerJournalTrans) &&

specTrans.SpecRecId   == ledgerJournalTrans.RecId &&

specTrans.SpecCompany == ledgerJournalTrans.company()

join   TransRecId,RecId from vendSettlement

where   vendSettlement.RecId == specTrans.RefRecId

join   RecId from vendTrans

where   vendTrans.RecId == vendSettlement.TransRecId

join   PromissoryNoteId,InvoiceCompany,InvoiceVoucher,InvoiceDate,InvoiceId from vendPromissoryNoteInvoice

where   vendPromissoryNoteInvoice.PromissoryNoteId == vendTrans.PromissoryNoteID

{

// Here we get the trans settled which may be in a different company and the

// promissory note invoice has the company to look in captured.

changecompany(vendPromissoryNoteInvoice.InvoiceCompany)

{

select firstonly vendTransOriginalInvoice

where vendTransOriginalInvoice.Voucher   == vendPromissoryNoteInvoice.InvoiceVoucher &&

vendTransOriginalInvoice.TransDate == vendPromissoryNoteInvoice.InvoiceDate   &&

vendTransOriginalInvoice.Invoice   == vendPromissoryNoteInvoice.InvoiceId;

}

if (calcSum)

{

settledAmount = ledgerJournalTrans.amount();

settledCurrencyCode =ledgerJournalTrans.displayCurrencyCode();

groupBy = vendOutPaymRecord.parmCustVendPaym().ledgerJournalTrans().parmAccount() + settledCurrencyCode;

calcSum = false;

this.insertBankPaymAdviceVendTmp(true,true);

}

else

{

this.insertBankPaymAdviceVendTmp(false,true);

}

}

}

}

}

protected void runPrintMgmt()

{

 vendOutPaym = this.parmArgs().caller();

 vendOutPaymList = this.parmArgs().parmObject();

 this.processReport(vendOutPaymList);

 }

private void setPaymReferenceStr()

{

str tmp;

tmp = ledgerJournalTrans.PaymReference;

if (ledgerJournalTrans.BankChequeNum)

{

tmp = ledgerJournalTrans.BankChequeNum;

newPaymRefLable = "@SYS22495" ;

}

if (ledgerJournalTrans.BankPromissoryNoteNum)

{

tmp = ledgerJournalTrans.BankPromissoryNoteNum;

newPaymRefLable = "@SYS71440" ;

}

if (tmp)

{

if (paymRef != '')

{

paymRef += ',' + tmp;

}

else

{

paymRef = tmp;

}

}

}

public static void main(Args _args)

{

SrsPrintMgmtController controller = new FDBankPaymAdviceVendController();

controller.parmReportName(#ReportName);

controller.parmArgs(_args);

controller.parmShowDialog(false);

controller.startOperation();

}

********** 

 

This is a supporting class for the new node type.

class PrintMgmtNode_BankPaymAdvice extends PrintMgmtNode

{

}

protected str getDisplayCaptionImplementation(Common _tableBuffer)

{

;

return(strfmt("@SYS108944", _tableBuffer.caption()));

}

public List getDocumentTypes()

{

List docTypes;

;

docTypes = new List(Types::Enum);

if (isConfigurationkeyEnabled(configurationkeynum(LogisticsBasic)))

{

docTypes.addEnd(PrintMgmtDocumentType::BankPaymAdvice);

}

return docTypes;

}

public int getIconImageResNum()

{

#resAppl

;

return #ImagePrintManagementTrans;

}

public PrintMgmtNodeType getNodeType()

{

;

return PrintMgmtNodeType::BankPaymAdvice;

}

public RefTableId getReferencedTableId()

{

;

return tablenum(BankPaymAdviceVendTmp);

}

Modified Classes:

class PrintMgmtNode, construct method

...

//Stoneridge Software LL April 2014 Begin

case PrintMgmtNodeType::BankPaymAdvice:

return new PrintMgmtNode_BankPaymAdvice();

//Stoneridge Software LL April 2014 End

...

class PrintMgmtHierarchy_Purch

 ...

getNodesImplementation

...

//Stoneridge Software LL April 2014 Begin

supportedNodes.addEnd(PrintMgmtNodeType::BankPaymAdvice);

//Stoneridge Software End

...

getParentImplementation

This is an important method for bringing the pieces together. The vendaccount value in the temp table ties back to the parent node (vendTable)

...

//Stoneridge Software LL April 2014 Begin

case PrintMgmtNodeType::BankPaymAdvice:

BankPaymAdviceVendTmp = _nodeInstance.parmReferencedTableBuffer();

if (strlen(BankPaymAdviceVendTmp.VendAccount) != 0)

{

result.parmReferencedTableBuffer(BankPaymAdviceVendTmp.selectRefRecord(fieldnum(BankPaymAdviceVendTmp, VendAccount)));

}

else

{

result.parmReferencedTableBuffer(null);

}

result.parmNodeDefinition(PrintMgmtNode::construct(PrintMgmtNodeType::VendTable));

break;

//Stoneridge Software LL April 2014End

...

class VendOutPaym_NACHA

class Declaration

DialogField         paymAdviceUsePrintMgmt; //Stoneridge Software LL April 2014

UsePrintMgmt       paymAdvUsePrintMgmt;   //Stoneridge Software LL April 2014

Dialog method

...

dialog.addGroup("@SYS54502");

//Stoneridge Software LL April 2014 Begin

paymAdviceUsePrintMgmt = dialog.addfieldvalue(enumStr(NoYes),NoYes::Yes,"@FDA27");

//Stoneridge Software LL April 2014 End

...

getFromDialog method

...

paymAdvUsePrintMgmt = paymAdviceUsePrintMgmt.value();

...

public void printPaymAdvice()

{

Args               args = new Args();

args.caller(this);

args.parmObject(this.getOutPaymRecords());

args.parmEnumType(enumNum(NoYes));

args.parmEnum(paymAdvUsePrintMgmt); //Stoneridge Software LL April 2014

new MenuFunction(menuitemOutputStr(BankPaymAdviceVend), MenuItemType::Output).run(args);

}

public boolean unpack(container _packedClass)

{

Version version = RunBase::getVersion(_packedClass);

container   base;

boolean     ret;

#LOCALMACRO.CurrentListV3

fileName,

printControlReport,

effectiveEntDate

#ENDMACRO

#define.V3(3)

#LOCALMACRO.CurrentListV4

effectiveEntDate,

paymAdvUsePrintMgmt //Stoneridge Software LL April 2014

#ENDMACRO

}

class PrintMgmtNode_Purch

getDocumentTypes

...

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderInvoice);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderPackingSlip);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderReceiptsList);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderRequisition);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderConfirmationRequest);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQ);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQAccept);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQReject);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQReturn);

//Stoneridge Software LL March 2014 Begin

docTypes.addEnd(PrintMgmtDocumentType::BankPaymAdvice);

//Stoneridge Software End

...

 

class PrintMgmtNode_VendTable

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderInvoice);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderPackingSlip);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderReceiptsList);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderRequisition);

docTypes.addEnd(PrintMgmtDocumentType::PurchaseOrderConfirmationRequest);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQ);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQAccept);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQReject);

docTypes.addEnd(PrintMgmtDocumentType::PurchRFQReturn);

//Stoneridge Software LL March 2014 Begin

docTypes.addEnd(PrintMgmtDocumentType::BankPaymAdvice);

//Stoneridge Software End

}

class PrintMgmtDocType

getDefaultReportFormat()

{

...

case PrintMgmtDocumentType::PurchaseOrderConfirmationRequest:

return ssrsReportStr(PurchPurchaseOrder, Report);

case PrintMgmtDocumentType::PurchRFQ:

case PrintMgmtDocumentType::PurchRFQAccept:

case PrintMgmtDocumentType::PurchRFQReject:

case PrintMgmtDocumentType::PurchRFQReturn:

return ssrsReportStr(RFQSend, Report);

//Stoneridge Software LL March 2014 Begin

case PrintMgmtDocumentType::BankPaymAdvice:

return ssrsReportStr(BankPaymAdviceVend, Report);

//Stoneridge Software End

Class PrintMgmtDocType

getQueryTableId()

...

//Stoneridge Software LL March 2014 Begin

case PrintMgmtDocumentType::BankPaymAdvice:

tableId = tableNum(BankPaymAdviceVendTmp);

break;

//Stoneridge Software End

...

Updates to this post:

  • You will need to modify the menu item to use the new controller class.
  • Also, the new variable for the dialog value “paymAdvUsePrintMgmt” needs to be added to the CurrentList macro in the classDeclaration of VendOutPaym_NACHA.

As noted above, also refer to AX 2012 Print Management Integration Guide for more information on the classes and methods modified here.


Under the terms of this license, you are authorized to share and redistribute the content across various mediums, subject to adherence to the specified conditions: you must provide proper attribution to Stoneridge as the original creator in a manner that does not imply their endorsement of your use, the material is to be utilized solely for non-commercial purposes, and alterations, modifications, or derivative works based on the original material are strictly prohibited.

Responsibility rests with the licensee to ensure that their use of the material does not violate any other rights.

Start the Conversation

It’s our mission to help clients win. We’d love to talk to you about the right business solutions to help you achieve your goals.

Subscribe To Our Blog

Sign up to get periodic updates on the latest posts.

Thank you for subscribing!