web
You’re offline. This is a read only version of the page.
close
Skip to main content

Notifications

Announcements

No record found.

Community site session details

Community site session details

Session Id :
Finance | Project Operations, Human Resources, ...
Unanswered

Batch parallelism/multithreading in D365 F&O to reduce the execution time

(0) ShareShare
ReportReport
Posted on by 758

Hi Everyone,

I have created one batch job using SysOperation framework.

My service class calls one custom class in which I have passed the contract class as parameter and in custom class I have written all the logic.

The logic starts with few validations based on contract parameters and then if validation passes then it creates one journal line and then iteration of the records (while loop) begins from one custom table, which has more that 10000 records. What have been observed that, it is taking quite longer time to execute.


Can Batch parallelism/multithreading concept be used in such scenario to reduce the execution time?

If yes can anyone please tell me what all classes I need to modify?

My service class looks like below,

/// 
/// 
/// 
class CreateProposalRemittanceService extends SysOperationServiceBase
{
    /// 
    /// Service class for remittance create proposal
    /// 
    /// CreateProposalRemittanceContract
    public void process(CreateProposalRemittanceContract _contract)
    {
        XXXProcessCreateProposal    processCreateProposal = XXXProcessCreateProposal::construct();
        processCreateProposal.createProposal(_contract);
    }

}

I have the same question (0)
  • André Arnaud de Calavon Profile Picture
    301,130 Super User 2025 Season 2 on at

    Hi Rhushikesh,

    Can you share the code you have in the method XXXProcessCreateProposal.createProposal()? I don't know what exact business logic is added and about that purpose. You only mention that you create 1 journal line and then starts a while loop. You haven't mentioned what is done with the information in the loop.

  • Rhushikesh R Profile Picture
    758 on at

    Hi Andre,

    Below is the class that I have created which is being called in Service class.

    /// 
    /// XXXProcessCreateProposal class of create proposal
    /// 
    class XXXProcessCreateProposal
    {
        public container            vouchersCon;
        public XXXRemittance100     paymentDocNum;
        public LedgerJournalId      journalNum;
        public PaymDate             paymentDate;
        public CustAccount          customerAccount;
        public XXXRemittanceType    remittanceType;
        public NoYesId				markMatchLineItemNumbers;
        public NoYesId				adjustmentDeduction;
        public CurrencyCode		    currency;
        public MCRString20          b2bRemittanceNumber;
        public XXXCustomerRemittanceAdviceHeader remittanceAdviceHeader;
        public XXXB2BRemittanceAdviceHeaders     b2bRemittanceAdviceHeader;
    
        public XXXCustomerRemittanceAdviceParameters  remittanceAdviceParameters;
    
        protected void new()
        {
        }
    
        /// 
        /// get new instance of class XXXProcessCreateProposal
        /// 
        /// a class XXXProcessCreateProposal
        public static XXXProcessCreateProposal construct()
        {
            return new XXXProcessCreateProposal();
        }
    
        /// 
        /// Method to initiate create proposal
        /// 
        /// XXXCreateProposalRemittanceContract
        public void createProposal(XXXCreateProposalRemittanceContract _contract)
        {
            LogText     logMsg;
            boolean     isValid;
    
            #OCCRetryCount
    
            vouchersCon     = list2Con(_contract.parmPaymentTrans());
            paymentDocNum   = _contract.parmPaymentDocument();
            paymentDate     = _contract.parmPaymentDate();
            customerAccount = _contract.parmQueryCustomerAccount();
            remittanceType  = _contract.parmRemittanceType();
            journalNum      = _contract.parmJournalNum();
            currency        = _contract.parmCurrency();
            markMatchLineItemNumbers = _contract.parmMarkMatchLineItemNumbers();
            adjustmentDeduction      = _contract.parmAdjustmentDeduction();
            b2bRemittanceNumber      = _contract.parmB2BRemittanceNumber();
    
            remittanceAdviceParameters = XXXCustomerRemittanceAdviceParameters::find();
            try
            {
                ttsbegin;
                if(remittanceType == XXXRemittanceType::CustomerRemittance)
                {
                    isValid = this.createCustomRemittanceProposal();
                }
                else if(remittanceType == XXXRemittanceType::B2BRemittance)
                {
                    isValid = this.createB2BRemittanceProposal();
                }
    
                if(isValid)
                {
                    logMsg = strFmt("@XXX:ProposalSuccessfullyCreated", journalNum);
                    this.updateJournalHeader(true,logMsg);
                    Info(logMsg);
                }
                else
                {
                    Info("@SYS104318");
                }
                ttscommit;
            }
            catch (Exception::Deadlock)
            {
                retry;
            }
            catch (Exception::UpdateConflict)
            {
                if (appl.ttsLevel() == 0)
                {
                    if (xSession::currentRetryCount() >= #RetryNum)
                    {
                        throw Exception::UpdateConflictNotRecovered;
                    }
                    else
                    {
                        retry;
                    }
                }
                else
                {
                    throw Exception::UpdateConflict;
                }
            }
        }
    
        /// 
        /// validate and create custom remittance proposal
        /// 
        /// is valid
        private boolean createCustomRemittanceProposal()
        {
            int         voucher;
            CustTrans   custTrans;
            AmountCur   totalCustTransAmount;
            boolean     isValid = true;
            LogText     logMsg;
            
            select firstonly remittanceAdviceHeader
                    where remittanceAdviceHeader.PaymentDocumentNumber == paymentDocNum;
    
            //validate Amount
            if(remittanceAdviceParameters.ValidateAmounts == NoYes::Yes)
            {
                for (voucher = 1; voucher <= conLen(vouchersCon); voucher  )
                {
                    select firstonly AmountCur from custTrans
                        where custTrans.RecId == conPeek(vouchersCon, voucher)
                        && custTrans.AccountNum == customerAccount;
    
                    totalCustTransAmount  = custTrans.AmountCur;
                }
    
                if(abs(totalCustTransAmount) != abs(remittanceAdviceHeader.TotalAmountPaid))
                {
                    isValid = false;
                    logMsg  = strFmt("@XXX:PaymentDocumentValidationMsg",
                                        totalCustTransAmount, remittanceAdviceHeader.TotalAmountPaid, paymentDocNum);
                    this.updateJournalHeader(isValid, logMsg);
                }
            }
    
            if(isValid)
            {
                this.updateJournalHeader(isValid, logMsg, remittanceAdviceHeader.TotalAmountPaid);
                this.processCustomRemittanceMatching();
                this.transferUnmarkedCustomRemittanceToDeductions();
                remittanceAdviceHeader.selectForUpdate(true);
                remittanceAdviceHeader.AdviceHeaderStatus = XXXAdviceHeaderStatus::Proposal;
                remittanceAdviceHeader.JournalBatchNumber = journalNum;
                remittanceAdviceHeader.update();
            }
    
            return  isValid;
        }
    
        /// 
        /// validate and create B2B remittance proposal
        /// 
        /// is valid
        private boolean createB2BRemittanceProposal()
        {
            int         voucher;
            CustTrans   custTrans;
            AmountCur   totalCustTransAmount;
            boolean     isValid = true;
            LogText     logMsg;
            
            select firstonly b2bRemittanceAdviceHeader
                    where b2bRemittanceAdviceHeader.PayDocument == paymentDocNum
                    && b2bRemittanceAdviceHeader.B2BRemittanceNumber == b2bRemittanceNumber;
    
            //validate Amount
            if(remittanceAdviceParameters.ValidateAmounts == NoYes::Yes)
            {
                for (voucher = 1; voucher <= conLen(vouchersCon); voucher  )
                {
                    select firstonly AmountCur from custTrans
                        where custTrans.RecId == conPeek(vouchersCon, voucher)
                        && custTrans.AccountNum == customerAccount;
    
                    totalCustTransAmount  = custTrans.AmountCur;
                }
    
                if(abs(totalCustTransAmount) != abs(b2bRemittanceAdviceHeader.TotalAmtPaid))
                {
                    isValid = false;
                    logMsg  = strFmt("@XXX:B2BTotalAmountValidationMsg",
                                        totalCustTransAmount, b2bRemittanceAdviceHeader.TotalAmtPaid, paymentDocNum);
                    this.updateJournalHeader(isValid, logMsg);
                }
            }
    
            if(isValid)
            {
                this.updateJournalHeader(isValid, logMsg, b2bRemittanceAdviceHeader.TotalAmtPaid);
                this.processB2BRemittanceMatching();
                this.transferUnmarkedB2BRemittanceToDeductions();
                b2bRemittanceAdviceHeader.selectForUpdate(true);
                b2bRemittanceAdviceHeader.Status             = XXXAdviceHeaderStatus::Proposal;
                b2bRemittanceAdviceHeader.JournalBatchNumber = journalNum;
                b2bRemittanceAdviceHeader.update();
            }
    
            return  isValid;
        }
    
        /// 
        /// update the journal header
        /// 
        /// valid proposal
        /// Message log
        /// Total amount paid
        private void updateJournalHeader(boolean _isValid, LogText _logMsg, Amount _totalAmountPaid = 0)
        {
            LedgerJournalTable  ledgerJournalTable;
            select firstonly forupdate ledgerJournalTable
                where ledgerJournalTable.JournalNum == journalNum;
            if(!_isValid && ledgerJournalTable)
            {
                ledgerJournalTable.Log               = _logMsg;
                ledgerJournalTable.XXXRemittanceType = remittanceType;
                ledgerJournalTable.update();
            }
            else if(ledgerJournalTable && _isValid && _logMsg == '')
            {
                ledgerJournalTable.XXXRemittanceType  = remittanceType;
                if(remittanceType == XXXRemittanceType::B2BRemittance)
                {
                    ledgerJournalTable.XXXB2BRemittanceNumber  = b2bRemittanceNumber;
                }
                ledgerJournalTable.XXXRemittAdvID     = paymentDocNum;
                ledgerJournalTable.XXXRemitAdvPayDate = paymentDate;
                ledgerJournalTable.XXXRemittAdvAmount = _totalAmountPaid;
                ledgerJournalTable.update();
                this.copyAttachmentToVouchers(ledgerJournalTable);
            }
            else if(ledgerJournalTable && _isValid && _logMsg != '')
            {
                ledgerJournalTable.Log               = _logMsg;
                ledgerJournalTable.update();
            }
        }
    
        /// 
        /// transfer attachments from Journal header to customer transactions vouchers
        /// 
        /// Journal header record
        private void copyAttachmentToVouchers(LedgerJournalTable  _ledgerJournalTable)
        {
            int         voucher;
            CustTrans   custTrans;
    
            if(con2Str(vouchersCon) != '')
            {
                for (voucher = 1; voucher <= conLen(vouchersCon); voucher  )
                {
                    select firstonly custTrans
                        where custTrans.RecId == conPeek(vouchersCon, voucher)
                        && custTrans.AccountNum == customerAccount;
                    if(custTrans)
                    {
                        Docu::copy(_ledgerJournalTable, custTrans);
                    }
                }
            }
        }
    
        /// 
        /// process custom remittance matching
        /// 
        private void processCustomRemittanceMatching()
        {
            LedgerJournalTrans                  ledgerJournalTrans;
            XXXCustomerRemittanceAdviceLines    remittanceAdviceLines;
            CustTrans                           custTrans;
            CustTransOpen                       custTransOpen;
            boolean                             isMarked;
            int                                 voucher;
    
            ledgerJournalTrans = this.createJournalLine();
    
            for (voucher = 1; voucher <= conLen(vouchersCon); voucher  )
            {
                select firstonly AccountNum,Voucher,RecId from custTrans
                    where custTrans.RecId == conPeek(vouchersCon, voucher)
                    && custTrans.AccountNum == customerAccount;
    
                if(custTrans)
                {
                    this.markMatchingVouchers(custTrans, ledgerJournalTrans);
                }
            }
    
            while select remittanceAdviceLines
                where remittanceAdviceLines.PaymentDocumentNumber == paymentDocNum
            {
                custTrans.clear();
                custTrans = this.getMatchingCustTrans(remittanceAdviceLines);
                if(custTrans)
                {
                    Amount amountToSettle;
                    if (adjustmentDeduction == NoYes::Yes)
                    {
                        amountToSettle = remittanceAdviceLines.OriginalAmount;
                    }
                    else
                    {
                        amountToSettle = remittanceAdviceLines.ActualAmountPaid;
                    }
                    [isMarked, custTransOpen] = this.markMatchingTransaction(custTrans, ledgerJournalTrans, amountToSettle);
                    if(isMarked && custTransOpen)
                    {
                        remittanceAdviceLines.selectForUpdate(true);
                        remittanceAdviceLines.IsMarked = NoYes::Yes;
                        remittanceAdviceLines.CustTransRefRecId = custTrans.RecId;
                        remittanceAdviceLines.CustTransOpenRefRecId = custTransOpen.RecId;
                        remittanceAdviceLines.update();
                    }
                }
            }
        }
    
        /// 
        /// process B2B remittance matching
        /// 
        private void processB2BRemittanceMatching()
        {
            LedgerJournalTrans                  ledgerJournalTrans;
            XXXB2BRemittanceAdviceLines         b2bRemittanceAdviceLines;
            XXXB2BRemittanceAdjustment          b2bRemittanceAdjustment;
            XXXB2BRemittanceAdjustment          b2bRemittanceAdjustmentUpd;
            CustTrans                           custTrans;
            CustTransOpen                       custTransOpen;
            boolean                             isMarked;
            int                                 voucher;
    
    
            ledgerJournalTrans = this.createJournalLine();
    
            for (voucher = 1; voucher <= conLen(vouchersCon); voucher  )
            {
                select firstonly AccountNum,Voucher,RecId from custTrans
                    where custTrans.RecId == conPeek(vouchersCon, voucher)
                    && custTrans.AccountNum == customerAccount;
    
                if(custTrans)
                {
                    this.markMatchingVouchers(custTrans, ledgerJournalTrans);
                }
            }
    
            while select b2bRemittanceAdviceLines
                where b2bRemittanceAdviceLines.B2BRemittanceNumber == b2bRemittanceNumber
            {
                custTrans.clear();
                custTrans = this.getMatchingCustTransForB2B(b2bRemittanceAdviceLines);
                if(custTrans)
                {
                    [isMarked, custTransOpen] = this.markMatchingTransaction(custTrans, ledgerJournalTrans, b2bRemittanceAdviceLines.ActualAmtPaid);
                    if(isMarked && custTransOpen)
                    {
                        b2bRemittanceAdviceLines.selectForUpdate(true);
                        b2bRemittanceAdviceLines.IsMarked = NoYes::Yes;
                        b2bRemittanceAdviceLines.CustTransRefRecId = custTrans.RecId;
                        b2bRemittanceAdviceLines.CustTransOpenRefRecId = custTransOpen.RecId;
                        b2bRemittanceAdviceLines.update();
                        
                        // mark related adjustment records
                        if(markMatchLineItemNumbers == NoYes::Yes)
                        {
                            update_recordset b2bRemittanceAdjustmentUpd
                                setting IsMarked = NoYes::Yes
                                where b2bRemittanceAdjustmentUpd.LineItemNumber == b2bRemittanceAdviceLines.LineItemNumber
                                && b2bRemittanceAdjustmentUpd.B2BRemittanceNumber == b2bRemittanceAdviceLines.B2BRemittanceNumber;
                        }
                    }
                }
            }
    
            // run B2B adjustment matching for the records which do not have linked line records in XXXB2BRemittanceAdviceLines
            b2bRemittanceAdviceLines.clear();
            while select b2bRemittanceAdjustment
            where b2bRemittanceAdjustment.B2BRemittanceNumber == b2bRemittanceNumber
                notexists join b2bRemittanceAdviceLines
                where b2bRemittanceAdviceLines.LineItemNumber == b2bRemittanceAdjustment.LineItemNumber
                && b2bRemittanceAdviceLines.B2BRemittanceNumber == b2bRemittanceAdjustment.B2BRemittanceNumber
            {
                custTrans.clear();
                custTrans = this.getMatchingCustTransForB2BAdjust(b2bRemittanceAdjustment);
                if(custTrans)
                {
                    [isMarked, custTransOpen] = this.markMatchingTransaction(custTrans, ledgerJournalTrans, b2bRemittanceAdjustment.AdjAmount);
                    if(isMarked)
                    {
                        b2bRemittanceAdjustment.selectForUpdate(true);
                        b2bRemittanceAdjustment.IsMarked = NoYes::Yes;
                        b2bRemittanceAdjustment.CustTransRefRecId = custTrans.RecId;
                        b2bRemittanceAdjustment.CustTransOpenRefRecId = custTransOpen.RecId;
                        b2bRemittanceAdjustment.update();
                    }
                }
            }
        }
    
        /// 
        /// transfer unmatched custom remittance transactions to deductions
        /// 
        private void transferUnmarkedCustomRemittanceToDeductions()
        {
            XXXCustomerRemittanceAdviceLines        remittanceAdviceLines;
            XXXCustomerRemittanceAdviceDeductions   remittanceAdviceDeductions;
            RecordInsertList deductionsInsertList = new RecordInsertList(tableNum(XXXCustomerRemittanceAdviceDeductions));
    
            //ttsbegin;
            while select remittanceAdviceLines
                where remittanceAdviceLines.PaymentDocumentNumber == paymentDocNum
                && remittanceAdviceLines.IsMarked == NoYes::No
            {
                remittanceAdviceDeductions.clear();
                remittanceAdviceDeductions.PaymentDocumentNumber    = remittanceAdviceHeader.PaymentDocumentNumber;
                if(adjustmentDeduction == NoYes::Yes)
                {
                    remittanceAdviceDeductions.ActualAmountPaid         = remittanceAdviceLines.OriginalAmount;
                }
                else
                {
                    remittanceAdviceDeductions.ActualAmountPaid         = remittanceAdviceLines.ActualAmountPaid;
                }
                remittanceAdviceDeductions.AccountNum               = remittanceAdviceHeader.Payer;
                remittanceAdviceDeductions.CurrencyCode             = remittanceAdviceHeader.CurrencyCode;
                remittanceAdviceDeductions.PostingDate              = remittanceAdviceHeader.PaymentDate;
                remittanceAdviceDeductions.ItemText                 = remittanceAdviceLines.ItemText;
                remittanceAdviceDeductions.InvoiceDate              = remittanceAdviceLines.InvoiceDate;
                remittanceAdviceDeductions.PaymentRefernce          = remittanceAdviceLines.InvoiceNumber;
                remittanceAdviceDeductions.AdviceLineRefRecId       = remittanceAdviceLines.RecId;
                deductionsInsertList.add(remittanceAdviceDeductions);
            }
            deductionsInsertList.insertDatabase();
    
            if(adjustmentDeduction == NoYes::Yes)
            {
                select sum(AdjAmount) from remittanceAdviceLines
                    where remittanceAdviceLines.PaymentDocumentNumber == paymentDocNum;
    
                remittanceAdviceDeductions.clear();
                remittanceAdviceDeductions.PaymentDocumentNumber = remittanceAdviceHeader.PaymentDocumentNumber;
                remittanceAdviceDeductions.ActualAmountPaid      = -(remittanceAdviceLines.AdjAmount);
                remittanceAdviceDeductions.AccountNum            = remittanceAdviceHeader.Payer;
                remittanceAdviceDeductions.CurrencyCode          = remittanceAdviceHeader.CurrencyCode;
                remittanceAdviceDeductions.PostingDate           = remittanceAdviceHeader.PaymentDate;
                remittanceAdviceDeductions.ItemText              = 'Adjustment as a deduction';
                remittanceAdviceDeductions.AdviceLineRefRecId    = remittanceAdviceLines.RecId;
                remittanceAdviceDeductions.insert();
            }
        }
    
        /// 
        /// transfer unmatched B2B remittance transactions to deductions
        /// 
        private void transferUnmarkedB2BRemittanceToDeductions()
        {
            XXXB2BRemittanceAdviceLines             b2bRemittanceAdviceLines;
            XXXCustomerRemittanceAdviceDeductions   remittanceAdviceDeductions;
            XXXB2BRemittanceAdjustment              b2bRemittanceAdjustment;
            RecordInsertList deductionsInsertList = new RecordInsertList(tableNum(XXXCustomerRemittanceAdviceDeductions));
    
            while select b2bRemittanceAdviceLines
                where b2bRemittanceAdviceLines.B2BRemittanceNumber == b2bRemittanceNumber
                && b2bRemittanceAdviceLines.IsMarked == NoYes::No
            {
                remittanceAdviceDeductions.clear();
                remittanceAdviceDeductions.PaymentDocumentNumber = b2bRemittanceAdviceHeader.PayDocument;
                remittanceAdviceDeductions.B2BRemittanceNumber   = b2bRemittanceAdviceHeader.B2BRemittanceNumber;
                remittanceAdviceDeductions.ActualAmountPaid      = b2bRemittanceAdviceLines.ActualAmtPaid;
                remittanceAdviceDeductions.AccountNum            = b2bRemittanceAdviceHeader.Payer;
                remittanceAdviceDeductions.CurrencyCode          = b2bRemittanceAdviceHeader.Currency;
                remittanceAdviceDeductions.PostingDate           = b2bRemittanceAdviceHeader.PayDate;
                remittanceAdviceDeductions.ItemText              = b2bRemittanceAdviceLines.ItemText;
                remittanceAdviceDeductions.InvoiceDate           = DateTimeUtil::date(b2bRemittanceAdviceLines.InvoicedDate);
                remittanceAdviceDeductions.PaymentRefernce       = b2bRemittanceAdviceLines.InvoiceNumber;
                remittanceAdviceDeductions.AdviceLineRefRecId    = b2bRemittanceAdviceLines.RecId;
                deductionsInsertList.add(remittanceAdviceDeductions);
            }
    
    
            while select b2bRemittanceAdjustment
                where b2bRemittanceAdjustment.B2BRemittanceNumber == b2bRemittanceNumber
                && b2bRemittanceAdjustment.IsMarked == NoYes::No
                    notexists join b2bRemittanceAdviceLines
                    where b2bRemittanceAdviceLines.LineItemNumber == b2bRemittanceAdjustment.LineItemNumber
                    && b2bRemittanceAdviceLines.B2BRemittanceNumber == b2bRemittanceAdjustment.B2BRemittanceNumber
            {
                remittanceAdviceDeductions.clear();
                remittanceAdviceDeductions.PaymentDocumentNumber = b2bRemittanceAdviceHeader.PayDocument;
                remittanceAdviceDeductions.B2BRemittanceNumber   = b2bRemittanceAdviceHeader.B2BRemittanceNumber;
                remittanceAdviceDeductions.ActualAmountPaid      = b2bRemittanceAdjustment.AdjAmount;
                remittanceAdviceDeductions.AccountNum            = b2bRemittanceAdviceHeader.Payer;
                remittanceAdviceDeductions.CurrencyCode          = b2bRemittanceAdviceHeader.Currency;
                remittanceAdviceDeductions.PostingDate           = b2bRemittanceAdviceHeader.PayDate;
                remittanceAdviceDeductions.ItemText              = b2bRemittanceAdjustment.AdjustmentReason;
                remittanceAdviceDeductions.PaymentRefernce       = b2bRemittanceAdjustment.AdjustmentDocumentNumber;
                remittanceAdviceDeductions.AdviceLineRefRecId    = b2bRemittanceAdjustment.RecId;
                deductionsInsertList.add(remittanceAdviceDeductions);
            }
    
            if(markMatchLineItemNumbers == NoYes::No)
            {
                while select b2bRemittanceAdjustment
                    where b2bRemittanceAdjustment.B2BRemittanceNumber == b2bRemittanceNumber
                    && b2bRemittanceAdjustment.IsMarked == NoYes::No
                        exists join b2bRemittanceAdviceLines
                            where b2bRemittanceAdviceLines.LineItemNumber == b2bRemittanceAdjustment.LineItemNumber
                            && b2bRemittanceAdviceLines.B2BRemittanceNumber == b2bRemittanceAdjustment.B2BRemittanceNumber
                            && b2bRemittanceAdviceLines.IsMarked == NoYes::Yes
                {
                    remittanceAdviceDeductions.clear();
                    remittanceAdviceDeductions.PaymentDocumentNumber = b2bRemittanceAdviceHeader.PayDocument;
                    remittanceAdviceDeductions.B2BRemittanceNumber   = b2bRemittanceAdviceHeader.B2BRemittanceNumber;
                    remittanceAdviceDeductions.ActualAmountPaid      = b2bRemittanceAdjustment.AdjAmount;
                    remittanceAdviceDeductions.AccountNum            = b2bRemittanceAdviceHeader.Payer;
                    remittanceAdviceDeductions.CurrencyCode          = b2bRemittanceAdviceHeader.Currency;
                    remittanceAdviceDeductions.PostingDate           = b2bRemittanceAdviceHeader.PayDate;
                    remittanceAdviceDeductions.ItemText              = b2bRemittanceAdjustment.AdjustmentReason;
                    remittanceAdviceDeductions.PaymentRefernce       = b2bRemittanceAdjustment.AdjustmentDocumentNumber;
                    remittanceAdviceDeductions.AdviceLineRefRecId    = b2bRemittanceAdjustment.RecId;
                    deductionsInsertList.add(remittanceAdviceDeductions);
                }
            }
    
            deductionsInsertList.insertDatabase();
        }
    
        /// 
        /// create journal line to mark the transactions
        /// 
        /// journal line record
        private LedgerJournalTrans createJournalLine()
        {
            LedgerJournalTable      ledgerJournalTable;
            LedgerJournalName       ledgerJournalName;
            LedgerJournalTrans      ledgerJournalTrans;
            CustTable               custTable;
    
            select firstonly * from ledgerJournalTable
                where ledgerJournalTable.JournalNum == journalNum
                join ledgerJournalName
                where ledgerJournalName.JournalName == ledgerJournalTable.JournalName;
    
    
            NumberSeq               numberSeq   = NumberSeq::newGetVoucherFromId(ledgerJournalTable.NumberSequenceTable);
    
            custTable = CustTable::find(customerAccount);
            ledgerJournalTrans.Voucher                  =   numberSeq.voucher();
            ledgerJournalTrans.initValue();
            ledgerJournalTrans.JournalNum               =   journalNum;
            ledgerJournalTrans.XXXRemittAdvID           =   paymentDocNum;
            ledgerJournalTrans.XXXB2BRemittanceNumber   =   b2bRemittanceNumber;
            ledgerJournalTrans.AccountType              =   LedgerJournalACType::Cust;
            ledgerJournalTrans.Company                  =   curext();
            ledgerJournalTrans.parmAccount(custTable.AccountNum,ledgerJournalTrans.AccountType);
            ledgerJournalTrans.initFromCustTable(custTable);
            ledgerJournalTrans.TransDate                =   DateTimeUtil::getSystemDate(DateTimeUtil::getUserPreferredTimeZone());
            ledgerJournalTrans.OffsetAccountType        =   ledgerJournalName.OffsetAccountType;
            ledgerJournalTrans.OffsetLedgerDimension    =   ledgerJournalName.OffsetLedgerDimension;
            ledgerJournalTrans.DefaultDimension         =   custTable.DefaultDimension;
            //ledgerJournalTrans.OffsetDefaultDimension   =   custTable.DefaultDimension;
            ledgerJournalTrans.TransactionType          =   LedgerTransType::Payment;
            ledgerJournalTrans.XXXCustTransType         = ledgerJournalName.XXXCustTransType;
            ledgerJournalTrans.XXXCreatedThroughBatch   =   NoYes::Yes;
            ledgerJournalTrans.Approved                 = NoYes::Yes;
            ledgerJournalTrans.Approver                 = HcmWorker::userId2Worker(curUserId());
            ledgerJournalTrans.initForCurrency(ledgerJournalTable);
            ledgerJournalTrans.insert();
            return ledgerJournalTrans;
        }
    
        /// 
        /// finds matching CustTrans record by running matching rules
        /// 
        /// Remittance advice line
        /// record of CustTrans
        private CustTrans getMatchingCustTrans(XXXCustomerRemittanceAdviceLines _remittanceAdviceLines)
        {
            XXXCustRemitLinesMatchingRules  remitLinesMatchingRules;
            CustTrans                       custTrans;
            CustTransOpen                   custTransOpen;
            SpecTrans                       specTrans;
    
            container   custTransTypeCon = remittanceAdviceParameters.CustTransTypes;
            container   transTypeCon     = remittanceAdviceParameters.TransTypes;
    
            while select remitLinesMatchingRules
                order by remitLinesMatchingRules.Priority asc
            {
                custTrans.clear();
                custTransOpen.clear();
                specTrans.clear();
    
                boolean skipRecord;
                Types fieldType = this.getFieldType(tableNum(XXXCustomerRemittanceAdviceLines), remitLinesMatchingRules.CustRemitFieldId);
    
                // skip the record if the field value is blank
                switch (fieldType)
                {
                    case Types::String:
                        if(_remittanceAdviceLines.(remitLinesMatchingRules.CustRemitFieldId) == '')
                        {
                            skipRecord = true;
                        }
                        break;
                    case Types::Date:
                        if(_remittanceAdviceLines.(remitLinesMatchingRules.CustRemitFieldId) == dateNull())
                        {
                            skipRecord = true;
                        }
                        break;
                    case Types::Integer:
                    case Types::Real:
                        if(_remittanceAdviceLines.(remitLinesMatchingRules.CustRemitFieldId) == 0)
                        {
                            skipRecord = true;
                        }
                        break;
                }
    
                if(!skipRecord)
                {
                    Query					query = new Query(queryStr(XXXCustOpenTransRemittance));
                    QueryRun                queryRun;
                    QueryBuildDataSource	qbdsCustTrans;
    
                    switch(remitLinesMatchingRules.Operator)
                    {
                        case XXXCustRemitMatchOperator::Equals :
    
                            qbdsCustTrans = query.dataSourceTable(tableNum(CustTrans));
                            qbdsCustTrans.addRange(fieldNum(CustTrans, AccountNum)).value(customerAccount);
    
                            if(con2Str(custTransTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, XXXCustTransType)).value(con2Str(custTransTypeCon));
                            }
    
                            if(con2Str(transTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, TransType)).value(con2Str(transTypeCon));
                            }
                            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
    
                            queryRun = new QueryRun(query);
    
                            while (queryRun.next())
                            {
                                custTrans = queryRun.get(tableNum(CustTrans));
                                if(custTrans.(remitLinesMatchingRules.CustTransFieldId) == _remittanceAdviceLines.(remitLinesMatchingRules.CustRemitFieldId))
                                {
                                    break;
                                }
                            }
    
                            break;
                        case XXXCustRemitMatchOperator::Contains :
    
                            qbdsCustTrans = query.dataSourceTable(tableNum(CustTrans));
                            qbdsCustTrans.addRange(fieldNum(CustTrans, AccountNum)).value(customerAccount);
    
                            if(con2Str(custTransTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, XXXCustTransType)).value(con2Str(custTransTypeCon));
                            }
    
                            if(con2Str(transTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, TransType)).value(con2Str(transTypeCon));
                            }
                            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
    
                            queryRun = new QueryRun(query);
    
                            while (queryRun.next())
                            {
                                custTrans = queryRun.get(tableNum(CustTrans));
    
                                if(custTrans && custTrans.(remitLinesMatchingRules.CustTransFieldId) != '')
                                {
                                    str custTransFieldValue = '*'   custTrans.(remitLinesMatchingRules.CustTransFieldId)   '*';
    
                                    if(_remittanceAdviceLines.(remitLinesMatchingRules.CustRemitFieldId) like custTransFieldValue)
                                    {
                                        break;
                                    }
                                }
                            }
                                          
                            break;
                    }
                }
                if(custTrans)
                {
                    break;
                }
            }
    
            return custTrans;
        }
    
        /// 
        /// finds matching CustTrans record by running B2B matching rules
        /// 
        /// B2B remittance advice line
        /// record of CustTrans
        private CustTrans getMatchingCustTransForB2B(XXXB2BRemittanceAdviceLines _b2bRemittanceAdviceLines)
        {
            XXXB2BRemitLinesMatchingRules   b2bRemitLinesMatchingRules;
            CustTrans                       custTrans;
            CustTransOpen                   custTransOpen;
            SpecTrans                       specTrans;
            
            container   custTransTypeCon    = remittanceAdviceParameters.CustTransTypes;
            container   transTypeCon        = remittanceAdviceParameters.TransTypes;
    
            while select b2bRemitLinesMatchingRules
                order by b2bRemitLinesMatchingRules.Priority asc
            {
                custTrans.clear();
                custTransOpen.clear();
                specTrans.clear();
    
                boolean skipRecord;
                Types fieldType = this.getFieldType(tableNum(XXXB2BRemittanceAdviceLines), b2bRemitLinesMatchingRules.B2BRemitFieldId);
    
                // skip the record if the field value is blank
                switch (fieldType)
                {
                    case Types::String:
                        if(_b2bRemittanceAdviceLines.(b2bRemitLinesMatchingRules.B2BRemitFieldId) == '')
                        {
                            skipRecord = true;
                        }
                        break;
                    case Types::Date:
                        if(_b2bRemittanceAdviceLines.(b2bRemitLinesMatchingRules.B2BRemitFieldId) == dateNull())
                        {
                            skipRecord = true;
                        }
                        break;
                    case Types::Integer:
                    case Types::Real:
                        if(_b2bRemittanceAdviceLines.(b2bRemitLinesMatchingRules.B2BRemitFieldId) == 0)
                        {
                            skipRecord = true;
                        }
                        break;
                }
    
                if(!skipRecord)
                {
                    Query					query = new Query(queryStr(XXXCustOpenTransRemittance));
                    QueryRun                queryRun;
                    QueryBuildDataSource	qbdsCustTrans;
    
                    switch(b2bRemitLinesMatchingRules.Operator)
                    {
                        case XXXCustRemitMatchOperator::Equals :
    
                            qbdsCustTrans = query.dataSourceTable(tableNum(CustTrans));
                            qbdsCustTrans.addRange(fieldNum(CustTrans, AccountNum)).value(customerAccount);
    
                            if(con2Str(custTransTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, XXXCustTransType)).value(con2Str(custTransTypeCon));
                            }
    
                            if(con2Str(transTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, TransType)).value(con2Str(transTypeCon));
                            }
                            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
    
                            queryRun = new QueryRun(query);
    
                            while (queryRun.next())
                            {
                                custTrans = queryRun.get(tableNum(CustTrans));
                                if(custTrans.(b2bRemitLinesMatchingRules.CustTransFieldId) == _b2bRemittanceAdviceLines.(b2bRemitLinesMatchingRules.B2BRemitFieldId))
                                {
                                    break;
                                }
                            }
    
                            break;
                        case XXXCustRemitMatchOperator::Contains :
    
                            qbdsCustTrans = query.dataSourceTable(tableNum(CustTrans));
                            qbdsCustTrans.addRange(fieldNum(CustTrans, AccountNum)).value(customerAccount);
                            if(con2Str(custTransTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, XXXCustTransType)).value(con2Str(custTransTypeCon));
                            }
    
                            if(con2Str(transTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, TransType)).value(con2Str(transTypeCon));
                            }
    
                            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
    
                            queryRun = new QueryRun(query);
    
                            while (queryRun.next())
                            {
                                custTrans = queryRun.get(tableNum(CustTrans));
    
                                if(custTrans && custTrans.(b2bRemitLinesMatchingRules.CustTransFieldId) != '')
                                {
                                    str custTransFieldValue = '*'   custTrans.(b2bRemitLinesMatchingRules.CustTransFieldId)   '*';
    
                                    if(_b2bRemittanceAdviceLines.(b2bRemitLinesMatchingRules.B2BRemitFieldId) like custTransFieldValue)
                                    {
                                        break;
                                    }
                                }
                            }
    
                            break;
                    }
                }
                if(custTrans)
                {
                    break;
                }
            }
    
            return custTrans;
        }
    
        /// 
        /// finds matching CustTrans record by running B2B adjustment matching rules
        /// 
        /// B2B remittance advice adjustment line
        /// record of CustTrans
        private CustTrans getMatchingCustTransForB2BAdjust(XXXB2BRemittanceAdjustment _b2bAdjustRemittanceAdviceLines)
        {
            XXXB2BAdjustRemitLinesMatchingRules   b2bAdjustRemitLinesMatchingRules;
            CustTrans                             custTrans;
            CustTransOpen                         custTransOpen;
            SpecTrans                             specTrans;
    
            container   custTransTypeCon    = remittanceAdviceParameters.CustTransTypes;
            container   transTypeCon        = remittanceAdviceParameters.TransTypes;
    
            while select b2bAdjustRemitLinesMatchingRules
                order by b2bAdjustRemitLinesMatchingRules.Priority asc
            {
                custTrans.clear();
                custTransOpen.clear();
                specTrans.clear();
    
                boolean skipRecord;
                Types fieldType = this.getFieldType(tableNum(XXXB2BRemittanceAdjustment), b2bAdjustRemitLinesMatchingRules.B2BAdjustRemitFieldId);
    
                // skip the record if the field value is blank
                switch (fieldType)
                {
                    case Types::String:
                        if(_b2bAdjustRemittanceAdviceLines.(b2bAdjustRemitLinesMatchingRules.B2BAdjustRemitFieldId) == '')
                        {
                            skipRecord = true;
                        }
                        break;
                    case Types::Date:
                        if(_b2bAdjustRemittanceAdviceLines.(b2bAdjustRemitLinesMatchingRules.B2BAdjustRemitFieldId) == dateNull())
                        {
                            skipRecord = true;
                        }
                        break;
                    case Types::Integer:
                    case Types::Real:
                        if(_b2bAdjustRemittanceAdviceLines.(b2bAdjustRemitLinesMatchingRules.B2BAdjustRemitFieldId) == 0)
                        {
                            skipRecord = true;
                        }
                        break;
                }
    
                if(!skipRecord)
                {
                    Query					query = new Query(queryStr(XXXCustOpenTransRemittance));
                    QueryRun                queryRun;
                    QueryBuildDataSource	qbdsCustTrans;
    
                    switch(b2bAdjustRemitLinesMatchingRules.Operator)
                    {
                        case XXXCustRemitMatchOperator::Equals :
    
                            qbdsCustTrans = query.dataSourceTable(tableNum(CustTrans));
                            qbdsCustTrans.addRange(fieldNum(CustTrans, AccountNum)).value(customerAccount);
    
                            if(con2Str(custTransTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, XXXCustTransType)).value(con2Str(custTransTypeCon));
                            }
    
                            if(con2Str(transTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, TransType)).value(con2Str(transTypeCon));
                            }
                            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
    
                            queryRun = new QueryRun(query);
    
                            while (queryRun.next())
                            {
                                custTrans = queryRun.get(tableNum(CustTrans));
                                if(custTrans.(b2bAdjustRemitLinesMatchingRules.CustTransFieldId) == _b2bAdjustRemittanceAdviceLines.(b2bAdjustRemitLinesMatchingRules.B2BAdjustRemitFieldId))
                                {
                                    break;
                                }
                            }
    
                            break;
                        case XXXCustRemitMatchOperator::Contains :
    
                            qbdsCustTrans = query.dataSourceTable(tableNum(CustTrans));
                            qbdsCustTrans.addRange(fieldNum(CustTrans, AccountNum)).value(customerAccount);
    
                            if(con2Str(custTransTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, XXXCustTransType)).value(con2Str(custTransTypeCon));
                            }
    
                            if(con2Str(transTypeCon) != '')
                            {
                                qbdsCustTrans.addRange(fieldnum(CustTrans, TransType)).value(con2Str(transTypeCon));
                            }
    
                            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
    
                            queryRun = new QueryRun(query);
    
                            while (queryRun.next())
                            {
                                custTrans = queryRun.get(tableNum(CustTrans));
    
                                if(custTrans && custTrans.(b2bAdjustRemitLinesMatchingRules.CustTransFieldId) != '')
                                {
                                    str custTransFieldValue = '*'   custTrans.(b2bAdjustRemitLinesMatchingRules.CustTransFieldId)   '*';
    
                                    if(_b2bAdjustRemittanceAdviceLines.(b2bAdjustRemitLinesMatchingRules.B2BAdjustRemitFieldId) like custTransFieldValue)
                                    {
                                        break;
                                    }
                                }
                            }
    
                            break;
                    }
                }
                if(custTrans)
                {
                    break;
                }
            }
    
            return custTrans;
        }
    
        /// 
        /// mark matching customer transaction
        /// 
        /// record of CustTrans
        /// record of LedgerJournalTrans
        /// amount to settle
        /// container
        private container markMatchingTransaction(CustTrans _custTrans, LedgerJournalTrans _ledgerJournalTrans, Amount _amountToSettle)
        {
            CustTransOpen       custTransOpen;
            boolean             isMarked;
            SpecTrans           specTrans;
    
            select firstonly custTransOpen
                where custTransOpen.RefRecId == _custTrans.RecId
                && custTransOpen.AccountNum == _custTrans.AccountNum;
    
            CustVendOpenTransManager manager = CustVendOpenTransManager::construct(_ledgerJournalTrans);
    
            //check if transaction has already been marked
            if(!manager.getTransMarkedByOtherSpec(custTransOpen) && !manager.getTransMarked(custTransOpen))
            {
                if((_amountToSettle < 0 && _amountToSettle >= custTransOpen.AmountCur)
                    || (_amountToSettle > 0 && _amountToSettle <= custTransOpen.AmountCur))
                {
                    manager.updateTransMarked(custTransOpen, NoYes::Yes);
                    specTrans = manager.getSpecTrans(custTransOpen);
                    if(specTrans)
                    {
                        isMarked = true;
                        specTrans.selectForUpdate(true);
                        specTrans.Balance01 = _amountToSettle;
                        specTrans.update();
                    }
                }
    
            }
    
            return [isMarked, custTransOpen];
        }
    
        /// 
        /// mark matching customer transaction for selected vouchers
        /// 
        /// record of CustTrans
        /// record of LedgerJournalTrans
        private void markMatchingVouchers(CustTrans _custTrans, LedgerJournalTrans _ledgerJournalTrans)
        {
            CustTransOpen       custTransOpen;
    
            select firstonly custTransOpen
                where custTransOpen.RefRecId == _custTrans.RecId
                && custTransOpen.AccountNum == _custTrans.AccountNum;
    
            CustVendOpenTransManager manager = CustVendOpenTransManager::construct(_ledgerJournalTrans);
    
            //check if transaction has already been marked
            if(!manager.getTransMarkedByOtherSpec(custTransOpen) && !manager.getTransMarked(custTransOpen))
            {
                manager.updateTransMarked(custTransOpen, NoYes::Yes);
            }
        }
    
        /// 
        /// Retrieves the base type of a FieldId
        /// 
        /// table Id
        /// field Id
        /// The base type.
        private Types getFieldType(TableId _tableId,FieldId _fieldId)
        {
            DictField   dictField;
    
            Debug::assert(_fieldId);
    
            dictField = new DictField(_tableId, _fieldId);
    
            return dictField.baseType();
    
        }
    
    }

Under review

Thank you for your reply! To ensure a great experience for everyone, your content is awaiting approval by our Community Managers. Please check back later.

Helpful resources

Quick Links

Responsible AI policies

As AI tools become more common, we’re introducing a Responsible AI Use…

Neeraj Kumar – Community Spotlight

We are honored to recognize Neeraj Kumar as our Community Spotlight honoree for…

Leaderboard > Finance | Project Operations, Human Resources, AX, GP, SL

#1
Martin Dráb Profile Picture

Martin Dráb 451 Most Valuable Professional

#2
André Arnaud de Calavon Profile Picture

André Arnaud de Cal... 428 Super User 2025 Season 2

#3
BillurSamdancioglu Profile Picture

BillurSamdancioglu 239 Most Valuable Professional

Last 30 days Overall leaderboard

Product updates

Dynamics 365 release plans