Magician

Scenario: Enhanced Requirements for Interest Calculation on Account Balances

Overview

The goal is to automate the calculation and distribution of daily interest for accounts based on the total amount associated with their related opportunities. This will ensure that each account’s balance is updated with accrued interest and that each opportunity reflects its portion of the interest.

Requirements

  1. Custom Field Creation:
    • Field Name: Account_Amount__c (Type – Currency)
    • Field Name: No_Of_Opp__c (Type – Number)
    • Description: Account_Amount__c field on the Account object will hold the total balance derived from the sum of all associated Opportunity amounts and No_Of_Opp__c field on Account object will hold the total count of number of Opportunity record associated with the Account.
  2. Interest Calculation:
    • Interest Rate: 1% daily
    • Interest Calculation Formula:
      • Daily Interest = Account_Amount__c * 0.01
    • Process:
      1. Retrieve the current balance from the Account_Amount__c field.
      2. Calculate the daily interest as 1% of the current balance.
      3. Update the Account_Amount__c with the new balance, which includes the calculated interest.
  3. Interest Distribution:
    • Distribution Method:
      • Split the total daily interest equally among all related Opportunities.
    • Process:
      1. Retrieve all Opportunities associated with the Account.
      2. Divide the total daily interest by the number of related Opportunities to determine the interest amount to be added to each Opportunity.
      3. Update each Opportunity’s amount with the calculated interest.
  4. Batch Job Scheduling:
    • Frequency: Every 24 hours
    • Job Details:
      • Create a scheduled batch job to perform the following tasks daily:
        1. Calculate the daily interest based on the current Account_Amount__c.
        2. Update the Account_Amount__c with the new balance including interest.
        3. Distribute the daily interest equally among related Opportunities and update their amounts accordingly.
  5. Implementation Steps:
    1. Custom Field Creation:
      • Add fields Account_Amount__c and No_Of_Opp__c to the Account object.
    2. Batch Apex Class:
      • Develop an Apex Batch class to handle the interest calculation and distribution logic.
    3. Scheduled Job:
      • Schedule the Batch Apex class to run every 24 hours.
    4. Testing:
      • Thoroughly test the batch job to ensure correct calculation and distribution of interest.
  6. Error Handling:
    • Implement error handling and logging to manage any issues that arise during the batch execution, such as data inconsistencies or update failures.

By implementing these requirements, the bank will ensure accurate and timely interest calculations, maintaining financial accuracy and transparency in account balances and opportunity amounts.

# Lets understand the implementation of Batch Class:

global class AccOppBatchClass implements Database.Batchable<sObject>, Schedulable {
    
    global Database.QueryLocator start(Database.BatchableContext BC){
        return Database.getQueryLocator([SELECT Id, Name, Account_Amount__c, No_Of_Opp__c FROM Account]);
    }
    
    global void execute(Database.BatchableContext BC, List<Account> accList){
        Set<id> accId = New Set<Id>();
        for(Account a : accList){
            if(a.id != Null){
                accId.add(a.id);
            }
        }
        
        Map<Id, Decimal> mapOfAccIdVsAmountTotal = new Map<Id, Decimal>();
        Map<Id, Integer> mapOfAccIdVsOppCount = new Map<Id, Integer>();
        
        for(opportunity oppo : [SELECT Id, Amount, AccountId FROM Opportunity WHERE AccountId IN :accId]){
            if(mapOfAccIdVsAmountTotal.containskey(oppo.AccountId)){
                Decimal tempamount = mapOfAccIdVsAmountTotal.get(oppo.AccountId);
                tempAmount = tempAmount + oppo.Amount;
                mapOfAccIdVsAmountTotal.put(oppo.AccountId, tempAmount);
                
                //Logic for counting no. of opportunities per account.
                Integer count = mapOfAccIdVsOppCount.get(oppo.AccountId);
                count = count + 1;
                mapOfAccIdVsOppCount.put(oppo.AccountId, count);
            }
            else{
                mapOfAccIdVsAmountTotal.put(oppo.AccountId, oppo.Amount);
                mapOfAccIdVsOppCount.put(oppo.AccountId, 1);
            }
        }
        List<Account> aList = New List<Account> ();
        
        for(Id acctid : mapOfAccIdVsAmountTotal.keyset()){
            Account acc = New Account();
            acc.id = acctid;
            acc.Account_Amount__c = mapOfAccIdVsAmountTotal.get(acctid);
            acc.Total_Interest__c = 0.01*acc.Account_Amount__c;
            acc.No_Of_Opp__c = mapOfAccIdVsOppCount.get(acctid);
            acc.Account_Amount__c = acc.Account_Amount__c +(0.01*acc.Account_Amount__c);
            aList.add(acc);
        }
        if(aList != null && aList.size() > 0){
            Database.SaveResult[] updateAList = Database.update(aList, false);
            // Iterate through each returned result
            for (Database.SaveResult sr : updateAList) {
                if (sr.isSuccess()) {
                    // Operation was successful, so get the ID of the record that was processed
                    System.debug('Successfully updated Record. Record ID: ' + sr.getId());
                } else { 
                    // Operation failed, so get all errors
                    for(Database.Error err : sr.getErrors()) {
                        System.debug('The following error has occurred.');
                        System.debug(err.getStatusCode() + ': ' + err.getMessage());
                        System.debug('Fields that affected this error: ' + err.getFields());
                    }
                }
            }
        }
        
        //Logic for equally distributing the interest amount.
        List<Opportunity> oppListToBeUpdated = new List<Opportunity>();
        for(Opportunity opp : [SELECT Id, Amount, Account.No_Of_Opp__c, Account.Total_Interest__c FROM Opportunity WHERE AccountId IN :accId]){
            
            if(opp.Account.Total_Interest__c != NULL && opp.Account.No_Of_Opp__c != NULL){
                Decimal amtToBeAdded = (opp.Account.Total_Interest__c / opp.Account.No_Of_Opp__c);
                opp.Amount = opp.Amount + amtToBeAdded;
                oppListToBeUpdated.add(opp);  
            }
        }
        
        if(oppListToBeUpdated != null && oppListToBeUpdated.size() > 0){
            Database.SaveResult[] updateOppList = Database.update(oppListToBeUpdated, false);
            
            // Iterate through each returned result
            for (Database.SaveResult sr : updateOppList) {
                if (sr.isSuccess()) {
                    // Operation was successful, so get the ID of the record that was processed
                    System.debug('Successfully updated Record. Record ID: ' + sr.getId());
                } else { 
                    // Operation failed, so get all errors
                    for(Database.Error err : sr.getErrors()) {
                        System.debug('The following error has occurred.');
                        System.debug(err.getStatusCode() + ': ' + err.getMessage());
                        System.debug('Fields that affected this error: ' + err.getFields());
                    }
                }
            }
        } 
    }
    
    global void finish(Database.BatchableContext BC){
        
        
    }
    global void execute(SchedulableContext BC){
        AccOppBatchClass accBatch = New AccOppBatchClass();
        Database.executeBatch(accBatch);
    }
    
}
Apex