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

how can i create a batch to send email to workers

(1) ShareShare
ReportReport
Posted on by 1,836
I  was writing a batch job For my report where i am getting the transaction of workers i want to send the email to the workers which i am getting in my report how can i use the code from my dp class and write the service class for batch job can any one guide me on this i have never worked on batch job before , here is my dataprovider class in my report 
 
I have the same question (0)
  • Martin Dráb Profile Picture
    237,978 Most Valuable Professional on at
    Please post your code once more, with line breaks.
  • Dineshkarlekar Profile Picture
    1,836 on at
    /// <summary>
    /// The <c>CustOpenTransPerDateDP</c> class declares the variables that are required for the
    /// <c>CustTransOpenPerDate</c>Microsoft SQL Server Reporting Services report.
    /// </summary>
    [
        SRSReportQueryAttribute(queryStr(DTCustTransOpenPerDate)),
        SRSReportParameterAttribute(classStr(DTCustTransOpenPerDateContract))
    ]
    public class DTCustTransOpenPerDateDP extends SrsReportDataProviderPreProcessTempDB
    {
        DTCustTransOpenPerDateTmp custTransOpenPerDateTmp;
        CustTransDetails custTransDetails;
        CustTable custTable;
        CustTrans custTrans;
        AmountCur amountCurOpen;
        AmountMST amountMSTOpen;
        AmountMSTSecondary amountReportingCurrencyOpen;
        DueDate firstDueDate;
        CustCollectionLetterCode collectionLetterCode;
        DTFinancialDimensionView    financialDim;
        
    
        BillingClassification billingClassification;
        /// <summary>
        ///    Retreives the actual data for the report from the <c>DTCustTransOpenPerDateTmp</c> table.
        /// </summary>
        /// <returns>
        ///    The data for the report in the <c>DTCustTransOpenPerDateTmp</c> table.
        /// </returns>
        [
            SRSReportDataSetAttribute(tablestr(DTCustTransOpenPerDateTmp))
        ]
        public DTCustTransOpenPerDateTmp getCustTransOpenPerDateTmp()
        {
            select custTransOpenPerDateTmp;
            return custTransOpenPerDateTmp;
        }
    
        /// <summary>
        /// Populates the <c>DTCustTransOpenPerDateTmp</c> buffer in preparation for insert.
        /// </summary>
        protected void populateCustTransOpenPerDateTmp()
        {
            custTransOpenPerDateTmp.AccountNum = custTable.AccountNum;
            custTransOpenPerDateTmp.Name = custTable.name();
            custTransOpenPerDateTmp.TransDate = custTrans.TransDate;
            custTransOpenPerDateTmp.Voucher = custTrans.Voucher;
            custTransOpenPerDateTmp.Invoice = custTrans.Invoice;
            custTransOpenPerDateTmp.Txt = custTrans.Txt;
            custTransOpenPerDateTmp.Currency = custTrans.CurrencyCode;
            custTransOpenPerDateTmp.AmountInCurrency = custTrans.AmountCur;        
            custTransOpenPerDateTmp.BalanceInCurrency = amountCurOpen;
            custTransOpenPerDateTmp.Balance = amountMSTOpen;
            custTransOpenPerDateTmp.BalanceInReportingCurrency = amountReportingCurrencyOpen;
            custTransOpenPerDateTmp.DueDate = firstDueDate;
            custTransOpenPerDateTmp.CollectionLetterCode = enum2str(collectionLetterCode);
            custTransOpenPerDateTmp.DTWorker = financialDim.Worker;
            custTransOpenPerDateTmp.DTWorkerName = HcmWorker::findByPersonnelNumber(financialDim.Worker).name();
            // <GBR>
            if (BrazilParameters::isEnabled())
            {
                custTransOpenPerDateTmp.FiscalEstablishmentId = custTrans.fiscalEstablishmentId_BR();
            }
            // </GBR>
            custTransOpenPerDateTmp.BillClassification = billingClassification;
        }
    
        /// <summary>
        ///    Inserts the records into the temporary table.
        /// </summary>
        /// <remarks>
        ///    This method is used to insert the records into the <c>DTCustTransOpenPerDateTmp</c> table.
        /// </remarks>
        protected void insertCustTransOpenPerDateTmp()
        {
            this.populateCustTransOpenPerDateTmp();
    
            custTransOpenPerDateTmp.insert();
        }
    
        /// <summary>
        ///    Processes the business logic of the report.
        /// </summary>
        /// <remarks>
        ///    This method is used to process the report business logic that is used by the
        ///    <c>CustTransOpenPerDate</c> report.
        /// </remarks>
        public void processReport()
        {
            QueryRun queryRun;
            QueryBuildDataSource qbdsCustTrans;
            PerDate perDate;
    
            boolean isPublicSectorScenario = false;
            List listBillingClassification;
    
            DTCustTransOpenPerDateContract contract = this.parmDataContract() as DTCustTransOpenPerDateContract;
    
            custTransOpenPerDateTmp.setConnection(this.parmUserConnection());
    
            queryRun = new QueryRun(this.parmQuery());
    
            listBillingClassification = contract.parmBillingClassification();
            isPublicSectorScenario = CustBillingClassificationReportManager::isPublicSectorScenario(listBillingClassification);
    
            if (isPublicSectorScenario && !BrazilParameters::isEnabled())
            {
                CustBillingClassificationReportManager::setBillingClassificationRanges(queryRun.query().dataSourceTable(tableNum(CustTrans)), listBillingClassification, contract.parmInclTransWithNoBillingClass(), fieldNum(CustTrans, CustBillingClassification));
            }
    
            perDate = contract.parmPerDate();
            custTransDetails = new CustTransDetails(custTrans, this.settlementSumDate());
    
            qbdsCustTrans = SysQuery::findOrCreateDataSource(queryRun.query(), tableNum(CustTrans));
            qbdsCustTrans.addRange(fieldNum(CustTrans, TransDate)).value(queryRange(null, perDate));
            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
            qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(strFmt('>%1', queryValue(perDate)));
    
            while (queryRun.next())
            {
                if (queryRun.changed(tablenum(CustTable)))
                {
                    custTable = queryRun.get(tablenum(CustTable));
                }
    
                custTrans = queryRun.get(tablenum(CustTrans));
                custTransDetails.setCustVendTrans(custTrans);
                custTransDetails.setTransDate(this.settlementSumDate());
                financialDim = queryRun.get(tableNum(DTFinancialDimensionView));
    
                [amountCurOpen, amountMSTOpen] = custTransDetails.amountCurMSTSettled();            
                amountCurOpen = custTrans.AmountCur - amountCurOpen;
                amountMSTOpen = custTrans.AmountMST - amountMSTOpen;
                            
                amountReportingCurrencyOpen = custTrans.ReportingCurrencyAmount - custTransDetails.amountReportingSettled();
    
                if (this.wasCustomerTransactionClosed(custTrans))
                {
                    continue;
                }
    
                custTransDetails.setCustVendTrans(custTrans);
                custTransDetails.setTransDate(dateMax());
                firstDueDate = custTransDetails.firstDueDate();
                collectionLetterCode = custTransDetails.collectionLetterCode();
    
                if (isPublicSectorScenario && !BrazilParameters::isEnabled()
                    && (listBillingClassification || contract.parmInclTransWithNoBillingClass()))
                {
                    billingClassification = CustBillingClassification::find(custTrans.CustBillingClassification).BillingClassification;
                }
                else
                {
                    billingClassification = '';
                }
    
                this.insertCustTransOpenPerDateTmp();
            }
        }
    
        /// <summary>
        /// Was the transaction closed.
        /// </summary>
        /// <param name = "_custTrans">The customer transaction.</param>
        /// <returns>True if the transaction was closed; otherwise false.</returns>
        /// <remarks>
        /// The customer transaction is considered closed when the amount has been fully settled.  The open amount is determined based on the
        /// report open transaction date parameter.  Therefore, if the customer transaction has an amount and the open balance for the transaction
        /// as of the report date is zero, then the transaction is closed at the report date.
        /// </remarks>
        protected boolean wasCustomerTransactionClosed(CustTrans _custTrans)
        {
            boolean wasCustomerTransactionClosed;
    
            if ((_custTrans.AmountCur && amountCurOpen == 0.00)
                || (_custTrans.AmountMST && amountMSTOpen == 0.00))
            {
                wasCustomerTransactionClosed = true;
            }
    
            return wasCustomerTransactionClosed;
        }
    
        /// <summary>
        /// Date used to set how far forward we look for settlements.
        /// </summary>
        protected date settlementSumDate()
        {
            DTCustTransOpenPerDateContract contract = this.parmDataContract() as DTCustTransOpenPerDateContract;
            PerDate perDate = contract.parmPerDate();
            NoYes excludeFutureSettlements = contract.parmExcludeFutureSettlements();
           
            return excludeFutureSettlements ? perDate : dateMax();
        }
    
    }
    hi , martin ,
    thanks for reply,
    do i need to use group by worker in my batch job service class 
    here is my code for Data provider class.
     
  • Martin Dráb Profile Picture
    237,978 Most Valuable Professional on at
    Can you tell us more about the business scenario, please? Sending e-mails from a report data provider class sounds wrong to me. I would rather use the query from the data contract in the controller class, e.g. in afterOperation(). But what exactly you should do obviously depends on business requirements.
     
    In general, you shouldn't try to do everything at once and mix unrelated code together. I would split your task to three separate steps:
     
    Task 1: Sending an email.
    Task 2: Iterating the query, getting e-mail addresses and passing them to Task 1.
    Task 3: Executing Task 2 after printing your report.
     
    You can choose the order in which you implement it. For example, if you want to focus on getting the data (and grouping), forget tasks 1 and 3 for a moment. Also, forget about batch processing for now too. Simply create a new class, add a method accepting DTCustTransOpenPerDateContract object and implement (and test) logic to get e-mail addresses. You can show them in infolog for now, because you don't yet have the logic for sending e-mails.
     
    This splitting to several smaller tasks is crucial. It means that you have manageable pieces of work, you can track what is implemented and what's not, you can test individual components before integrating them with other parts and it also leads to a better structure of the solution (than if you mix all things together).
     
    You shouldn't mix all different topics in this forum either. When you split your work to smaller pieces, you'll be able to create threads focusing on individual problems.
     
    Regarding grouping, QueryBuildDataSource class addGroupByField() method for this purpose.
  • Dineshkarlekar Profile Picture
    1,836 on at
    hi martin 
    thanks for the reply ,
     
    what i have to do is
    1)to get the workers and their transaction in report
    2)to create batch job which will automatically generate the email and send it to workers 
    3) if the data is blank the email should not be sent 
     
    my report is working as expected i.e i have done step one but in batch class i need to write the same logic which i used in the process report of dp class so if i am getting 3 workers in report i should get 3 workers in batch class also after that the email part will come can you plz tell me how can i implement the logic of data provider /process report query in my batch service class so i can get same result as data provider class .
     
    here i will be trying to pass the parameters directly to batch class without opening the dialogue .is that posible 
    plz guide me on this .
    my batch service class is given below 
    inal class DTCustTransOpenSysOperationService extends SysOperationServiceBase
    {
        public void processOperation()
        {
            CustTable       custTable;
            CustTrans       custTrans;
            DTFinancialDimensionView   financialDim;
            Email           email;
    
            while  select * from  custTable
               join  custTrans
                    where custTable.AccountNum == custTrans.AccountNum
               join   financialDim  group by  financialDim.worker
                    where custTrans.DefaultDimension == financialDim.RecordId
                 {
                     email = HcmWorker::findByPersonnelNumber(financialDim.Worker).email();
                 }
            info('%1',email);
        }
    }
    
     
  • Martin Dráb Profile Picture
    237,978 Most Valuable Professional on at
    The obvious bug in your code is that ignores the query used by the report.
     
    If you followed my suggestion, you'd have created a method taking a DTCustTransOpenPerDateContract object as a parameter. Then you'd get the query by calling parmQuery(). But you created a method without any parameter, so can't get any information about what data was used in the report.
     
    I recommend you forget the batch for a moment and focus on the core business logic instead. If you're struggling with relatively simple things, it's not in your interest trying to deal with several such things at once.
  • Dineshkarlekar Profile Picture
    1,836 on at
    hi martin ,
     i am trying to get parametrs from contract but also tried to get the query from parm query but i am getting errors can you plz tell me how can i correct my code .
    final class DTCustTransOpenSysOperationService extends SysOperationServiceBase
    {
        public void processOperation(DTCustTransOpenPerDateContract   _contract)
        {
            
        }
       
            public void run()
            {
                QueryRun                          queryRun ;
                QueryBuildDataSource              qbdsCustTrans;
                PerDate                           perDate;
                DTCustTransOpenPerDateContract   _contract ;
                CustTransDetails                  custTransDetails;
                CustTrans                         custTrans;
    
               queryRun = new  QueryRun(queryStr(DTCustTransOpenPerDate));
    
                perDate = _contract.parmPerDate();
    
                custTransDetails = new CustTransDetails(custTrans, this.settlementSumDate());
    
                qbdsCustTrans = SysQuery::findOrCreateDataSource(queryRun.query(), tableNum(CustTrans));
                qbdsCustTrans.addRange(fieldNum(CustTrans, TransDate)).value(queryRange(null, perDate));
                qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
                qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(strFmt('>%1', queryValue(perDate)));
    
            while (queryRun.next())
            {
                if (queryRun.changed(tablenum(CustTable)))
                {
                    custTable = queryRun.get(tablenum(CustTable));
                }
    
                custTrans = queryRun.get(tablenum(CustTrans));
                custTransDetails.setCustVendTrans(custTrans);
                custTransDetails.setTransDate(this.settlementSumDate());
                financialDim = queryRun.get(tableNum(DTFinancialDimensionView));
    
                [amountCurOpen, amountMSTOpen] = custTransDetails.amountCurMSTSettled();
                amountCurOpen = custTrans.AmountCur - amountCurOpen;
                amountMSTOpen = custTrans.AmountMST - amountMSTOpen;
                            
                amountReportingCurrencyOpen = custTrans.ReportingCurrencyAmount - custTransDetails.amountReportingSettled();
    
                if (this.wasCustomerTransactionClosed(custTrans))
                {
                    continue;
                }
    
                custTransDetails.setCustVendTrans(custTrans);
                custTransDetails.setTransDate(dateMax());
                firstDueDate = custTransDetails.firstDueDate();
                collectionLetterCode = custTransDetails.collectionLetterCode();
    
                if (isPublicSectorScenario && !BrazilParameters::isEnabled()
                    && (listBillingClassification || contract.parmInclTransWithNoBillingClass()))
                {
                    billingClassification = CustBillingClassification::find(custTrans.CustBillingClassification).BillingClassification;
                }
                else
                {
                    billingClassification = '';
                }
    
                this.insertCustTransOpenPerDateTmp();
            }
    
            }
    
        /// <summary>
        /// Date used to set how far forward we look for settlements.
        /// </summary>
        protected date settlementSumDate()
        {
            DTCustTransOpenPerDateContract contract =  _contract as DTCustTransOpenPerDateContract;
            PerDate perDate = contract.parmPerDate();
            NoYes excludeFutureSettlements = contract.parmExcludeFutureSettlements();
           
            return excludeFutureSettlements ? perDate : dateMax();
        }
        
           
    }
     
  • Martin Dráb Profile Picture
    237,978 Most Valuable Professional on at
    Are you 100% sure that you don't want to use the query used by the report? I thought you wanted that, but you don't do it in your code.
    You get the contract object, but you ignore the query there. You take the AOT query DTCustTransOpenPerDate instead, which doesn't contain ranges set by either code or users for the report. Please confirm that ignoring the report query is intentional and not a bug.
     
    Regarding, "i am getting errors", that's not a problem description I can work with. Please give us more information.
  • Dineshkarlekar Profile Picture
    1,836 on at
    i need to use the report query but not getting how to mension it there i have found some code online so i used that query above the dp class but i dont need that as you said , 
     queryRun = new  QueryRun()); 
    do i need to do like this or plz sugest me how can i call my query there .
     
    thanks,
    regards ,
     Dinesh
  • Dineshkarlekar Profile Picture
    1,836 on at
    final class DTCustTransOpenSysOperationService extends SysOperationServiceBase
    {
    
        AmountCur                         amountCurOpen;
        AmountMST                         amountMSTOpen;
        AmountMSTSecondary                amountReportingCurrencyOpen;
        DueDate                           firstDueDate;
        CustCollectionLetterCode          collectionLetterCode;
        DTFinancialDimensionView          financialDim;
        public void processOperation(DTCustTransOpenPerDateContract   _contract)
        {
            contract = _contract;
            return     contract;
        }
       
            public void run()
            {
                QueryRun                          queryRun ;
                QueryBuildDataSource              qbdsCustTrans;
                PerDate                           perDate;
                DTCustTransOpenPerDateContract    _contract = this.processOperation() as DTCustTransOpenPerDateContract; ;
                CustTransDetails                  custTransDetails;
                CustTrans                         custTrans;
                CustTable                         custTable;
              
                queryRun = new  QueryRun());
    
                perDate = _contract.parmPerDate();
    
                custTransDetails = new CustTransDetails(custTrans, this.settlementSumDate());
    
                qbdsCustTrans = SysQuery::findOrCreateDataSource(queryRun.query(), tableNum(CustTrans));
                qbdsCustTrans.addRange(fieldNum(CustTrans, TransDate)).value(queryRange(null, perDate));
                qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(queryValue(dateNull()));
                qbdsCustTrans.addRange(fieldNum(CustTrans, Closed)).value(strFmt('>%1', queryValue(perDate)));
    
            while (queryRun.next())
            {
                if (queryRun.changed(tablenum(CustTable)))
                {
                    custTable = queryRun.get(tablenum(CustTable));
                }
    
                custTrans = queryRun.get(tablenum(CustTrans));
                custTransDetails.setCustVendTrans(custTrans);
                custTransDetails.setTransDate(this.settlementSumDate());
                financialDim = queryRun.get(tableNum(DTFinancialDimensionView));
    
                [amountCurOpen, amountMSTOpen] = custTransDetails.amountCurMSTSettled();
                amountCurOpen = custTrans.AmountCur - amountCurOpen;
                amountMSTOpen = custTrans.AmountMST - amountMSTOpen;
                            
                amountReportingCurrencyOpen = custTrans.ReportingCurrencyAmount - custTransDetails.amountReportingSettled();
    
                if (this.wasCustomerTransactionClosed(custTrans))
                {
                    continue;
                }
    
                custTransDetails.setCustVendTrans(custTrans);
                custTransDetails.setTransDate(dateMax());
                firstDueDate = custTransDetails.firstDueDate();
                collectionLetterCode = custTransDetails.collectionLetterCode();
                
            }
    
           }
       
    
    
        /// <summary>
        /// Was the transaction closed.
        /// </summary>
        /// <param name = "_custTrans">The customer transaction.</param>
        /// <returns>True if the transaction was closed; otherwise false.</returns>
        /// <remarks>
        /// The customer transaction is considered closed when the amount has been fully settled.  The open amount is determined based on the
        /// report open transaction date parameter.  Therefore, if the customer transaction has an amount and the open balance for the transaction
        /// as of the report date is zero, then the transaction is closed at the report date.
        /// </remarks>
        protected boolean wasCustomerTransactionClosed(CustTrans _custTrans)
        {
            boolean wasCustomerTransactionClosed;
    
            if ((_custTrans.AmountCur && amountCurOpen == 0.00)
                || (_custTrans.AmountMST && amountMSTOpen == 0.00))
            {
                wasCustomerTransactionClosed = true;
            }
    
            return wasCustomerTransactionClosed;
        }
    
        /// <summary>
        /// Date used to set how far forward we look for settlements.
        /// </summary>
        protected date settlementSumDate()
        {
            DTCustTransOpenPerDateContract     contract =  this.processOperation() as DTCustTransOpenPerDateContract;
            PerDate perDate = contract.parmPerDate();
            NoYes excludeFutureSettlements = contract.parmExcludeFutureSettlements();
           
            return excludeFutureSettlements ? perDate : dateMax();
        }
    
        
    
            //info('%1',email);
           
    }
    
  • Martin Dráb Profile Picture
    237,978 Most Valuable Professional on at
    The usual approach is having the query in the contract and then simply calling a method of the contract object to get it.
     
    I assumed you did at usual, but unfortunately I don't know how you implemented your DTCustTransOpenPerDateContractc class and also what code you have in DTCustTransOpenPerDateDP.parmQuery(). Please share this information with us. It's not easy to tell you how you should use your code without knowing what code you've written.
     
    Regarding the errors, 'contract' is not declared means that you're referring to a variable called contract, but you've never declared a variable of such a name. But if you want such a variable, you must declare it (as a class-level variable) and then change related code.
     
    The second error is caused by the fact that the return type of processOperation() is void, but you're trying to return a value from it. It seems that your intention was writing something like "parmContract" method with the return type DTCustTransOpenPerDateContract instead of void. Such a method should also have a default value of the parameter, which will fix two other compilation errors.

    Like this:

    public DTCustTransOpenPerDateContract parmContract(DTCustTransOpenPerDateContract _contract = contract)
    {
        contract = _contract;
        return contract;
    }


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
André Arnaud de Calavon Profile Picture

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

#2
Martin Dráb Profile Picture

Martin Dráb 422 Most Valuable Professional

#3
BillurSamdancioglu Profile Picture

BillurSamdancioglu 239 Most Valuable Professional

Last 30 days Overall leaderboard

Product updates

Dynamics 365 release plans