General journal creation, posting, customer settlement process

Helper class to reverse financial transactions produced by project invoice posting:
- creates general journal transactions based on project invoice voucher
- posts general journal
- settles customer transaction from project invoice with reversal transaction from general journal

/// <summary>
/// Helper class to reverse project invoice voucher transactions and match those, which relates to customer
/// </summary>
class VKProjectInvoicePostingHelper
{
    LedgerJournalTrans  paymLedgerJournalTrans;

    /// <summary>
    /// Entry point to execute reversal and matching process
    /// </summary>
    /// <param name = "_projProposalJour">ProjProposalJour record</param>
    public void reverseTranasctions(ProjProposalJour _projProposalJour)
    {
        setPrefix("Invoice reversal process");

        if (_projProposalJour && _projProposalJour.ProjInvoiceId)
        {
            ProjInvoiceJour     projInvoiceJour     = ProjInvoiceJour::find(_projProposalJour.ProjInvoiceId, _projProposalJour.InvoiceDate);
            LedgerJournalTable  ledgerJournalTable  = this.createJournal(projInvoiceJour);

            if (ledgerJournalTable)
            {
                info(strFmt("Reversing general journal %1", ledgerJournalTable.JournalNum));
                this.postGeneralJournal(ledgerJournalTable);
                this.settleTransactions(projInvoiceJour, paymledgerJournalTrans);
            }
        }
    }

    /// <summary>
    /// Creates general journal based on project invoice voucher transactions
    /// </summary>
    /// <param name = "_projInvoiceJour">ProjInvoiceJour record</param>
    /// <returns>LedgerJournalTable record</returns>
    public LedgerJournalTable createJournal(ProjInvoiceJour _projInvoiceJour)
    {
        GeneralJournalEntry         generalJournalEntry;
        GeneralJournalAccountEntry  generalJournalAccountEntry;
        LedgerJournalTable          ledgerJournalTable;
        LedgerJournalTrans          ledgerJournalTrans;

        while select generalJournalEntry
                where generalJournalEntry.SubledgerVoucher == _projInvoiceJour.LedgerVoucher
                join generalJournalAccountEntry
                    where generalJournalAccountEntry.GeneralJournalEntry == generalJournalEntry.RecId
        {
            if (!ledgerJournalTable)
            {
                ledgerJournalTable = this.createJournalHeader(_projInvoiceJour);
            }
                
            ledgerJournalTrans.clear();
            ledgerJournalTrans.JournalNum             = ledgerJournalTable.JournalNum;
            ledgerJournalTrans.TransDate              = _projInvoiceJour.InvoiceDate;
            if (LedgerPostingType::CustBalance == generalJournalAccountEntry.PostingType)
            {
                ledgerJournalTrans.AccountType            = LedgerJournalACType::Cust;
                ledgerJournalTrans.LedgerDimension        = LedgerDynamicAccountHelper::getDynamicAccountFromAccountNumber(_projInvoiceJour.InvoiceAccount, ledgerJournalTrans.AccountType);
                ledgerJournalTrans.DefaultDimension = LedgerDimensionFacade::getDefaultDimensionFromLedgerDimension(generalJournalAccountEntry.LedgerDimension);
            }
            else
            {
                ledgerJournalTrans.AccountType            = LedgerJournalACType::Ledger;
                ledgerJournalTrans.LedgerDimension        = generalJournalAccountEntry.LedgerDimension;
            }
            if (LedgerPostingType::Tax == generalJournalAccountEntry.PostingType)
            {
                TaxTrans taxTrans;
                select firstonly taxTrans
                    where taxTrans.Voucher              == _projInvoiceJour.LedgerVoucher
                        &&taxTrans.TransDate            == generalJournalEntry.AccountingDate
                        &&taxTrans.SourceCurrencyCode   == generalJournalAccountEntry.TransactionCurrencyCode
                        &&taxTrans.SourceTaxAmountCur   == generalJournalAccountEntry.TransactionCurrencyAmount;

                if (taxTrans)
                {
                    ledgerJournalTrans.TaxGroup     = taxTrans.TaxGroup;
                    ledgerJournalTrans.TaxItemGroup = taxTrans.TaxItemGroup;
                    ledgerJournalTrans.TaxCode      = taxTrans.TaxCode;
                }
            }
            //ledgerJournalTrans.OffsetAccountType      = ledgerJournalTransOrig.OffsetAccountType;
            //ledgerJournalTrans.OffsetLedgerDimension  = ledgerJournalTransOrig.OffsetLedgerDimension;
            //ledgerJournalTrans.OffsetDefaultDimension = ledgerJournalTransOrig.OffsetDefaultDimension;

            ledgerJournalTrans.CurrencyCode           =   _projInvoiceJour.CurrencyId;
            if (generalJournalAccountEntry.IsCredit)
            {
                ledgerJournalTrans.AmountCurDebit   = abs(generalJournalAccountEntry.AccountingCurrencyAmount);
            }
            else
            {
                ledgerJournalTrans.AmountCurCredit  = abs(generalJournalAccountEntry.AccountingCurrencyAmount);
            }

            //addition fields
            //ledgerJournalTrans.Approver           = HcmWorker::userId2Worker(curuserid());
            //ledgerJournalTrans.Approved           = NoYes::Yes;
            ledgerJournalTrans.Txt                = strFmt("Reverse invoice %1", _projInvoiceJour.ProjInvoiceId);
            ledgerJournalTrans.SkipBlockedForManualEntryCheck = true;

            ledgerJournalTrans.defaultRow();
            ledgerJournalTrans.insert();
            if (LedgerPostingType::CustBalance == generalJournalAccountEntry.PostingType)
            {
                paymledgerJournalTrans.data(ledgerJournalTrans);
            }
        }
        return ledgerJournalTable;
    }

    /// <summary>
    /// Creates general journal header
    /// </summary>
    /// <param name = "_projInvoiceJour">ProjInvoiceJour record</param>
    /// <returns>LedgerJournalTable record</returns>
    public LedgerJournalTable createJournalHeader(ProjInvoiceJour _projInvoiceJour)
    {
        ProjParameters      projParameters = ProjParameters::find();
        LedgerJournalTable  ledgerJournalTable;
 
        if (!projParameters.VKJournalName)
        {
            throw Error("Please specify project sales invoice reversal general journal name");
        }

        ledgerJournalTable.clear();
        ledgerJournalTable.initValue();
        ledgerJournalTable.JournalNum = JournalTableData::newTable(ledgerJournalTable).nextJournalId();
        ledgerJournalTable.initFromLedgerJournalName(projParameters.VKJournalName);
        // journal description, should be after initFromLedgerJournalName
        ledgerJournalTable.Name = strFmt("Reverse invoice %1", _projInvoiceJour.ProjInvoiceId);
        ledgerJournalTable.CurrencyCode = _projInvoiceJour.CurrencyId;
        ledgerJournalTable.insert();
 
        return ledgerJournalTable;
    }

    /// <summary>
    /// Posts general journal
    /// </summary>
    /// <param name = "_ledgerJournalTable">LedgerJournalTable record</param>
    void postGeneralJournal(LedgerJournalTable _ledgerJournalTable)
    {
        LedgerJournalCheckPost  ledgerJournalCheckPost;
 
        ledgerJournalCheckPost  = ledgerJournalCheckPost::newLedgerJournalTable(_ledgerJournalTable, NoYes::Yes);
        ledgerJournalCheckPost.runOperation();
        //ledgerJournalCheckPost.run();
    }

    /// <summary>
    /// Settles customer transactions
    /// </summary>
    /// <param name = "_projInvoiceJour">ProjInvoiceJour record</param>
    /// <param name = "_paymLedgerJournalTrans">LedgerJournalTrans record</param>
    void settleTransactions(ProjInvoiceJour _projInvoiceJour, LedgerJournalTrans _paymLedgerJournalTrans)
    {
        CustTable custTable = CustTable::find(_projInvoiceJour.InvoiceAccount);

        // find related transaciton
        LedgerJournalTrans  paymLedgerJournalTransLoc;
        CustTransOpen       invCustTransOpen, paymCustTransOpen;
        CustTrans           invCustTrans, paymCustTrans;

        // Given the payment LedgerJournalTrans, find the related CustTrans which has CustTransOpen
        select firstonly paymLedgerJournalTransLoc
            where   paymLedgerJournalTransLoc.RecId  == _paymLedgerJournalTrans.RecId
            join    paymCustTrans
            where   paymCustTrans.Voucher         == paymLedgerJournalTransLoc.Voucher
                &&  paymCustTrans.AccountNum      == custTable.AccountNum
                &&  paymCustTrans.RecId           == paymLedgerJournalTransLoc.CustTransId
                &&  paymCustTrans.TransType       == LedgerTransType::GeneralJournal
            join    paymCustTransOpen
            where   paymCustTransOpen.RefRecId    == paymCustTrans.RecId;

        // Find the related invoice to be settled against the payment journal in the query above
        select firstonly invCustTrans
            where   invCustTrans.AccountNum      == custTable.AccountNum
                &&  invCustTrans.Voucher         == _projInvoiceJour.LedgerVoucher
                &&  invCustTrans.TransType       == LedgerTransType::Project
            join    invCustTransOpen
            where   invCustTransOpen.RefRecId    == invCustTrans.RecId;


        // Settlement
        SpecTransExecutionContext specTransExecutionContext = SpecTransExecutionContext::newFromSource(custTable);
        //SpecTransManager          specTransManager          = SpecTransManager::construct(specTransExecutionContext.parmSpecContext());
        SpecTransManager          specTransManager          = SpecTransManager::newFromSpec(specTransExecutionContext.parmSpecContext());
        Amount                    settleAmount              = invCustTransOpen.AmountCur;

        //Prevent over settle
        if(settleAmount > (-paymCustTransOpen.AmountCur))
        {
            settleAmount = (-paymCustTransOpen.AmountCur);
        }

        //Payment
        specTransManager.insert(paymCustTransOpen.DataAreaId,
                                            paymCustTransOpen.TableId,
                                            paymCustTransOpen.RecId,
                                            -settleAmount,
                                            paymCustTrans.CurrencyCode);
            
        //Invoice
        specTransManager.insert(invCustTransOpen.DataAreaId,
                                            invCustTransOpen.TableId,
                                            invCustTransOpen.RecId,
                                            settleAmount,
                                            invCustTrans.CurrencyCode);
                
        //Settle
        if(CustTrans::settleTransaction(specTransExecutionContext, CustTransSettleTransactionParameters::construct()))
        {
            info(strFmt("Settlement completed for Invoice %1, Journal reversal payment %2, Amount %3", _projInvoiceJour.ProjInvoiceId, _paymLedgerJournalTrans.JournalNum, settleAmount));
        }
        else
        {
            throw Error("Settlement failed");
        }
    }

}

Test:
internal final class VKTest
{
    /// <summary>
    /// Class entry point. The system will call this method when a designated menu
    /// is selected or when execution starts and this class is set as the startup class.
    /// </summary>
    /// <param name = "_args">The specified arguments.</param>
    public static void main(Args _args)
    {
        VKProjectInvoicePostingHelper  projectInvoicePostingHelper = new VKProjectInvoicePostingHelper();
        ProjProposalJour                projProposalJour = ProjProposalJour::find('VK-000301');
        ttsbegin;
        projectInvoicePostingHelper.reverseTranasctions(projProposalJour);
        ttscommit;

        info('done');
    }

}



 

Search

About

DaxOnline.org is free platform that allows you to quickly store and reuse snippets, notes, articles related to Dynamics AX.

Authors are allowed to set their own AdSense units and "buy me a coffee" link.
Join us.

Blog Tags