Thursday, March 17, 2022

Lookup, JumpRef, Modified for a form data source field through CoC

 As  explained in his old article https://community.dynamics.com/365/financeandoperations/b/ievgensaxblog/posts/d365foe-how-to-override-form-data-source-field-lookup-method, it is much better to override methods directly on a data source field than on its linked form controls.

I just want to re-iterate it and place here code snippets.


  • User can add new control using form personalization and this control won’t support overridden logic. It could be critical if you are restricting field lookup values or adding validations.
  • One form could have several controls that refers to one data source field so you have to duplicate your code.
  • Number of delegates are limited as well.
So, say we need to implement Lookup, JumpRef, and Modified methods for a custom field on CustInvoiceTable data source of CustFreeInvoice form. Implement these three aforementioned methods, e.g., directly in an extension to the form class.

 
/// <summary>
/// Extension of form <c>CustFreeInvoice</c>
/// </summary>
[ExtensionOf(formStr(CustFreeInvoice))]
final class EOGCustFreeInvoice_Form_Extension
{
    public void eogAssignedBankAccountIdModified(FormDataObject _targetField)
    {
       <logic>
    }
    public void eogAssignedBankAccountIdJumpRef(FormDataObject _targetField)
    {
       <logic>
}
    // Different parameter here!
    public void eogAssignedBankAccountIdLookup(FormStringControl _callingControl)
    {
       <logic>
    }

Now simply override them for the field when the data source is initialized.
 
/// <summary>
/// Free text invoice form event handler;
/// </summary>
public class EOGCustFreeInvoice_Form_EventHandler
{
    [FormDataSourceEventHandler(formDataSourceStr(CustFreeInvoice, CustInvoiceTable), FormDataSourceEventType::Initialized)]
    public static void eogCustInvoiceTable_OnInitialized(FormDataSource _sender, FormDataSourceEventArgs _e)
    {
        FormRun         eogFormRun                  = _sender.formRun();
        FormDataObject  eogCustomField              = _sender.object(fieldNum(CustInvoiceTable, eogCustomField));
fdoEOGAssignedBankAccountId.registerOverrideMethod(methodStr(FormDataObject, jumpRef), formMethodStr(CustFreeInvoice, eogAssignedBankAccountIdJumpRef), eogFormRun); fdoEOGAssignedBankAccountId.registerOverrideMethod(methodStr(FormDataObject, lookup), formMethodStr(CustFreeInvoice, eogAssignedBankAccountIdLookup), eogFormRun); fdoEOGAssignedBankAccountId.registerOverrideMethod(methodStr(FormDataObject, modified), formMethodStr(CustFreeInvoice, eogAssignedBankAccountIdModified), eogFormRun); }

Monday, March 7, 2022

How to populate custom fields in GeneralJournalAccountEntry from LedgerJournalTrans for Ledger, Customer, Vendor, and Bank account type

 Say, we need to add a new field EOGUniqueId to LedgerJournalTrans and then have it populated in GeneralJournalAccountEntry, once a General journal posted.

In other words the field value must be transferred from a general journal line to a related voucher transaction.

Generally speaking there are two different ways how GL transactions created in D365FO: via Source document framework and via LedgerVoucherObject. Moreover, one transaction may be a result of summarization of multiple documents. So, this approach works for this particular scenario, when GL transactions come from a general journal. The proposed solution covers Ledger, Customer, Vendor, and Bank types. You can elaborate it for Project, Fixed Asset, etc. Check their appropriate classes.

LedgerJournalCheckPost class creates one transaction per a line of Ledger type, two if the latter has an offset info.

When it comes to other transaction type, first, a transaction in CustTransVendTransBankTrans etc is created, and then based on the latter a new transaction is added.



So, we creates the following extensions.

Tables.


Classes.




Below, you can find code snippets for each of them.

EOGBankVoucher_Extension

/// <summary>
/// Extension of BankVoucher class to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
/// </summary>
[ExtensionOf(classStr(BankVoucher))]
final class EOGBankVoucher_Extension
{
    public EOGUniqueId eogUniqueId;

    /// <summary>
    /// Gets or sets the transaction EOGUniqueId.
    /// </summary>
    /// <param name = "_parm">EOGUniqueId</param>
    /// <returns>EOGUniqueId</returns>
    public EOGUniqueId EOGParmUniqueId(EOGUniqueId _parm = eogUniqueId)
    {
        eogUniqueId = _parm;

        return eogUniqueId;
    }

    /// <summary>
    ///     to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
    /// </summary>
    /// <param name = "_ledgerVoucherObject">
    ///     An instance of <c>LedgerVoucherObject</c> class.
    /// </param>
    /// <param name = "_exchangeRateHelper">
    ///     An instance of <c>CurrencyExchangeHelper</c> class.
    /// </param>
    /// <returns>
    ///     The initialized <c>LedgerVoucherTransObject</c> object.
    /// </returns>
    protected LedgerVoucherTransObject initializeLedgerVoucherTransObjectForPosting(LedgerVoucherObject _ledgerVoucherObject, CurrencyExchangeHelper _exchangeRateHelper)
    {
        LedgerVoucherTransObject ledgerVoucherTransObject = next initializeLedgerVoucherTransObjectForPosting(_ledgerVoucherObject, _exchangeRateHelper);
        if(ledgerVoucherTransObject)
{ ledgerVoucherTransObject.EOGParmUniqueId(_ledgerVoucherObject.EOGParmUniqueId()); } return ledgerVoucherTransObject; } }

EOGCustVendVoucher_Extension

/// <summary>
/// Extension of CustVendorVoucher class to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
/// </summary>
[ExtensionOf(classStr(CustVendVoucher))]
final class EOGCustVendVoucher_Extension
{
    public EOGUniqueId eogUniqueId;

    /// <summary>
    /// Gets or sets the transaction EOGUniqueId.
    /// </summary>
    /// <param name = "_parm">EOGUniqueId</param>
    /// <returns>EOGUniqueId</returns>
    public EOGUniqueId EOGParmUniqueId(EOGUniqueId _parm = eogUniqueId)
    {
        eogUniqueId = _parm;

        return eogUniqueId;
    }

    /// <summary>
    /// to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
    /// </summary>
    /// <param name = "_useSubLedger">A Boolean value that indicates whether the SubLedger is being used.</param>
    /// <param name = "_ledgerDimensionMerged">The <c>LedgerDimensionAccount</c> ledger account record.</param>
    /// <param name = "_ledgerJournalTrans">The <c>LedgerJournalTrans</c> record.</param>
    /// <param name = "_ledgerPostingJournal">The ledger posting journal to use for ledger posting.</param>
    /// <param name = "_custVendTrans">The Map object that contains the value of vendor transaction or customer transaction.</param>
    /// <returns>The instance of <c>LedgerVoucherTransObject</c> class.</returns>
    protected LedgerVoucherTransObject createLedgerVoucherTransObject(boolean _useSubLedger,
                                                                     LedgerDimensionAccount _ledgerDimensionMerged,
                                                                     LedgerJournalTrans _ledgerJournalTrans,
                                                                     LedgerVoucher _ledgerPostingJournal,
                                                                     CustVendTrans _custVendTrans)
    {
        LedgerVoucherTransObject ledgerVoucherTransObject = next createLedgerVoucherTransObject( _useSubLedger, _ledgerDimensionMerged, _ledgerJournalTrans, _ledgerPostingJournal, _custVendTrans);
        if(ledgerVoucherTransObject)
{ ledgerVoucherTransObject.EOGParmUniqueId(eogUniqueId); } return ledgerVoucherTransObject; } }

EOGLedgerJournalCheckPost_Extension

/// <summary>
/// to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
/// </summary>
[ExtensionOf(classStr(LedgerJournalCheckPost))]
final class EOGLedgerJournalCheckPost_Extension
{

    /// <summary>
    /// to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
    /// </summary>
    /// <param name = "_ledgerJournalTrans">
    /// The <c>LedgerJournalTrans</c> record.
    /// </param>
    /// <param name = "_sysModule">
    /// A <c>SysModule</c> enumeration value.
    /// </param>
    /// <returns>
    /// The created <c>LedgerVoucherObject</c> reference.
    /// </returns>
    protected LedgerVoucherObject createPostingReference(LedgerJournalTrans _ledgerJournalTrans, SysModule _sysModule)
    {
        LedgerVoucherObject newVoucher = next  createPostingReference(_ledgerJournalTrans, _sysModule);
        if(newVoucher)
{ newVoucher.EOGParmUniqueId(_ledgerJournalTrans.EOGUniqueId); } return newVoucher; } /// <summary> /// to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans /// </summary> /// <param name = "_postingReference"> /// The <c>LedgerVoucherObject</c> instance. /// </param> /// <param name = "_ledgerJournalTrans"> /// The <c>LedgerJournalTrans</c> record. /// </param> /// <param name = "_sysModule"> /// A <c>SysModule</c> enumeration value. /// </param> /// <returns> /// The updated <c>LedgerVoucherObject</c> reference. /// </returns> protected LedgerVoucherObject updatePostingReference(LedgerVoucherObject _postingReference, LedgerJournalTrans _ledgerJournalTrans, SysModule _sysModule) { next updatePostingReference(_postingReference, _ledgerJournalTrans, _sysModule); if(_postingReference)
        {     _postingReference.EOGParmUniqueId(_ledgerJournalTrans.EOGUniqueId);         } return _postingReference; } }

EOGLedgerJournalTransUpdateBank_Extension

/// <summary>
/// Extension of LedgerJournalTransUpdateBank class to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans 
/// </summary>
[ExtensionOf(classStr(LedgerJournalTransUpdateBank))]
final class EOGLedgerJournalTransUpdateBank_Extension
{

    /// <summary>
    /// to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
    /// </summary>
    /// <param name="_ledgerJournalTrans">
    /// The <c>LedgerJournalTrans</c> record to pass the transaction data.
    /// </param>
    /// <param name="_taxAmount">
    /// The tax amount.
    /// </param>
    /// <param name="_taxWithholdAmount">
    /// The tax withhold amount.
    /// </param>
    /// <param name="_defaultDimension">
    /// The default dimension.
    /// </param>
    /// <param name="_ledgerJournalType">
    /// The journal type..
    /// </param>
    /// <param name="_skipDimensionValidation">
    /// The flag skip Dimension Validation.
    /// </param>
    /// <returns>
    /// The new <c>BankVoucher</c> class instance.
    /// </returns>
    protected BankVoucher initBankVoucher(  LedgerJournalTrans _ledgerJournalTrans,
                                            TaxAmount _taxAmount,
                                            real _taxWithholdAmount,
                                            DimensionDefault _defaultDimension,
                                            LedgerJournalType _ledgerJournalType,
                                            boolean _skipDimensionValidation)
    {
        BankVoucher bankVoucher = next initBankVoucher(_ledgerJournalTrans, _taxAmount, _taxWithholdAmount, _defaultDimension, _ledgerJournalType, _skipDimensionValidation);
        if(bankVoucher)
        {     bankVoucher.EOGParmUniqueId(_ledgerJournalTrans.EOGUniqueId);         } return bankVoucher; } }

EOGLedgerVoucherObject_Extension

/// <summary>
/// Extension of LedgerVoucherOject class to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
/// </summary>
[ExtensionOf(classStr(LedgerVoucherObject))]
final class EOGLedgerVoucherObject_Extension
{
    public EOGUniqueId eogUniqueId;

    /// <summary>
    /// Gets or sets the transaction EOGUniqueId.
    /// </summary>
    /// <param name = "_parm">EOGUniqueId</param>
    /// <returns>EOGUniqueId</returns>
    public EOGUniqueId EOGParmUniqueId(EOGUniqueId _parm = eogUniqueId)
    {
        eogUniqueId = _parm;

        return eogUniqueId;
    }

}

EOGLedgerVoucherTransObject_Extension

/// <summary>
/// Extension of LedgerVoucherTransObject class to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
/// </summary>
[ExtensionOf(classStr(LedgerVoucherTransObject))]
final class EOGLedgerVoucherTransObject_Extension
{
    public EOGUniqueId eogUniqueId;

    /// <summary>
    /// Gets or sets the transaction EOGUniqueId.
    /// </summary>
    /// <param name = "_parm">EOGUniqueId</param>
    /// <returns>EOGUniqueId</returns>
    public EOGUniqueId EOGParmUniqueId(EOGUniqueId _parm = eogUniqueId)
    {
        generalJournalAccountEntry.EOGUniqueId  = _parm;
        return generalJournalAccountEntry.EOGUniqueId;
    }

    /// <summary>
    /// to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans
    /// </summary>
    /// <param name="_ledgerJournalTrans">
    /// A <c>LedgerJournalTrans</c> record buffer.
    /// </param>
    /// <param name="_taxAmount">
    /// The tax amount; optional. Defaults to 0. Will be deducted from the regular amount of the <c>LedgerJournalTrans</c> record buffer.
    /// </param>
    /// <param name="_bridging">
    /// true if the transation will be bridged; otherwise, false. optional.
    /// </param>
    /// <param name="_intercompanyRecIds">
    /// A container that holds the RecIds of <c>LedgerJournalTrans</c> records that are for intercompany accounts.
    /// </param>
    /// <param name="_reversalsMayExist">
    /// true if reversals may exist for this <c>LedgerJournalTrans</c> record; otherwise, false. Optional.
    /// </param>
    /// <param name="_forcedExchangeRate">
    /// true if the provided <c>LedgerJournalTrans</c> exchange rates should be used for accounting and reporting amount
    /// calculations; otherwise, false. Optional.
    /// </param>
    /// <returns>
    /// A new <c>LedgerVoucherTransObject</c> object.
    /// </returns>
    public static LedgerVoucherTransObject newTransLedgerJournal(
                                                                    LedgerJournalTrans  _ledgerJournalTrans,
                                                                    TaxAmount           _taxAmount,
                                                                    boolean             _bridging,
                                                                    container           _intercompanyRecIds,
                                                                    boolean             _reversalsMayExist,
                                                                    boolean             _forcedExchangeRate)
    {
        LedgerVoucherTransObject ledgerVoucherTransObject = next newTransLedgerJournal(_ledgerJournalTrans, _taxAmount, _bridging, _intercompanyRecIds, _reversalsMayExist, _forcedExchangeRate);
        if(ledgerVoucherTransObject)
        {     ledgerVoucherTransObject.EOGParmUniqueId(_ledgerJournalTrans.EOGUniqueId);         } return ledgerVoucherTransObject; } /// <summary> /// Gets the <c>LedgerPostingTransactionTmp</c> record for the current object. /// </summary> /// <returns> /// The <c>LedgerPostingTransactionTmp</c> record. /// </returns> public LedgerPostingTransactionTmp getLedgerPostingTransaction() { LedgerPostingTransactionTmp ledgerPostingTransaction = next getLedgerPostingTransaction(); ledgerPostingTransaction.EOGUniqueId = generalJournalAccountEntry.EOGUniqueId; return ledgerPostingTransaction; } /// <summary> /// Chain of comand method to populate the Unique Id /// </summary> /// <param name = "_ledgerPostingTransaction"></param> /// <param name = "_projectPostingTransaction"></param> public void initFromLedgerPostingTransaction(LedgerPostingTransactionTmp _ledgerPostingTransaction,LedgerPostingTransactionProjectTmp _projectPostingTransaction) { next initFromLedgerPostingTransaction(_ledgerPostingTransaction,_projectPostingTransaction); generalJournalAccountEntry.EOGUniqueId = _ledgerPostingTransaction.EOGUniqueId; } /// <summary> /// to populate EOGUniqueId field in GeneralJournalAccountEntry from LedgerJournalTrans /// amount, an accounting currency amount, and a ledger posting reference for defaulting. /// </summary> /// <param name="_defaultLedgerPostingReference"> /// The ledger posting reference used for defaulting. /// </param> /// <param name="_postingType"> /// The posting type of the general journal entry. /// </param> /// <param name="_ledgerDimensionId"> /// The dimension attribute value combination of the general journal entry. /// </param> /// <param name="_transactionCurrencyCode"> /// The currency code of the general journal entry. /// </param> /// <param name="_transactionCurrencyAmount"> /// The amount in the transaction currency. /// </param> /// <param name="_accountingCurrencyAmount"> /// The amount in the accounting currency. /// </param> /// <param name="_currencyExchangeHelper"> /// An <c>CurrencyExchangeHelper</c> object initialized for the current LedgerVoucherTransObject. /// </param> /// <returns> /// A new instance of the LedgerVoucherTransObject class. /// </returns> /// <remarks> /// The default ledger posting reference is used to set the transaction type. /// </remarks> public static LedgerVoucherTransObject newTransactionAccountingAmountsDefault( LedgerVoucherObject _defaultLedgerPostingReference, LedgerPostingType _postingType, RecId _ledgerDimensionId, CurrencyCode _transactionCurrencyCode, Money _transactionCurrencyAmount, MoneyMST _accountingCurrencyAmount, CurrencyExchangeHelper _currencyExchangeHelper) { LedgerVoucherTransObject postingTrans; postingTrans = next newTransactionAccountingAmountsDefault(_defaultLedgerPostingReference, _postingType, _ledgerDimensionId, _transactionCurrencyCode, _transactionCurrencyAmount, _accountingCurrencyAmount, _currencyExchangeHelper);         if(postingTrans)
        {     postingTrans.EOGParmUniqueId(_defaultLedgerPostingReference.EOGParmUniqueId());         } return postingTrans; } }

I wish to credit the following articles I used:

http://axforum.info/forums/showthread.php?t=74038

https://allaboutdynamic.com/2018/06/25/d365-ax7-update-custom-fields-in-custtrans-vendtrans-from-ledgerjournaltrans-during-the-posting-of-journal/

http://axwiki.blogspot.com/2017/01/customize-field-in-ledgerjournaltabletr.html