+ Start a Discussion
HARSHIL U PARIKHHARSHIL U PARIKH 

How do I get createdDate in variable in BeforeInsert Trigger?

Hello Developers!

I am trying to write a trigger on opportunity where it would throw an error when user exceeds 10,000 daily limit of opportunity amount. One sales rep can create number of opportunities up to the amount where Amount can not exceed 10,000 per day.

Here is my logic: When there is a new record inserted in system, I am getting that record's userId, and createdDate in variables. Then I have a SOQL which fetches the all opportunities in database which is created by that user and on the today's  date (I am not too sure my SOQL in the code below). Once the records are fetched, I have a loop which sumes up the amount from the fetched opportunities in SOQL and if that sum is greater then 10,000, I am throwing an error.
 

Trigger TenThousandDollarsLimit on Opportunity(Before Insert){
    // We need to write a trigger so one user can not create more then
    // $10,000 of opportunity per day.
    
    User OppOwner;
    ID OppOwnerID;
    Double AmountSum;
    Date TodaysDate;
    
    For(Opportunity opp: Trigger.New){
        
        OppOwner = opp.Owner;
        OppOwnerID = opp.OwnerId;
        TodaysDate = opp.createdDate.date();
        
        
        List<Opportunity> opps;
        opps = [Select Id, Amount FROM Opportunity WHERE OwnerId = :OppOwnerId AND CreatedDate = :TodaysDate];
        
        
        For(Integer I = 0; I < opps.size(); I++)
        {
            AmountSum = opps[I].Amount;
        }
        
        If (AmountSum > 10000){
            opp.addError('You have exceed your daily limit of $10,000');
        }
        else{
            // do nothing
        }
    }
    
}
Here is an Error Message while saving the first record:
Error: Invalid Data. 
Review all error messages below to correct your data.
Apex trigger TenThousandDollarsLimit caused an unexpected exception, contact your administrator: TenThousandDollarsLimit: execution of BeforeInsert caused by: System.NullPointerException: Attempt to de-reference a null object: Trigger.TenThousandDollarsLimit: line 14, column 1


Thank you for the help guys!
Best Answer chosen by HARSHIL U PARIKH
Vivian Charlie 1208Vivian Charlie 1208

Govind,

 

Sorry it what happens when we write code in a notepad and not in Salesforce where we can compile and check. My bad, following is the updated code. TRIED and TESTED

 

trigger OpportunityTrigger on Opportunity (before insert) {
    set<Id> setownerIds = new set<Id>();
    map<Id,Decimal> mapId_Amount = new map<Id,Decimal>();
    for(Opportunity objP : trigger.new){
        setOwnerIds.add(objP.Ownerid);
        if(objP.Amount != null){
            if(!mapId_Amount.containskey(objP.OwnerId)){
                mapId_Amount.put(objP.Ownerid, objP.Amount);
            }else{
                Decimal decCurrentTotal = mapId_Amount.get(objP.OwnerId);
                Decimal newTotal = decCurrentTotal + objP.Amount;
                mapId_Amount.put(objP.Ownerid, newTotal);
            }
        }
    }
    
    list<AggregateResult> lstAgr = [Select OwnerId, SUM(Amount), MAX(CreatedDate) from Opportunity where Createddate = TODAY GROUP BY OwnerId ];
    for(aggregateresult ar : lstAgr){
        System.debug('owner ID*****' + ar.get('OwnerId'));
        System.debug('Total*****' + ar.get('expr0'));
        Id ownId = (Id)ar.get('OwnerId');
        system.debug('****ownId*****'+ownId);
        if(mapId_Amount.containskey(ownId)){
            Decimal total = (Decimal)ar.get('expr0') + mapId_Amount.get(ownId);
            system.debug('New Total**********'+total);
            if(total > 10000){
                system.debug('Finally done***********');
                trigger.new[0].addError('Error Message'); // update this as per your requirement if necessary
            }
        }
    }
}

 

Thanks

Vivian

All Answers

HARSHIL U PARIKHHARSHIL U PARIKH
Well, on line 23 it should be
AmountSum += opps[I].Amount;
And since the created date is always today, I have change the SOQL to as follow,
opps = [Select Id, Amount FROM Opportunity WHERE OwnerId = :OppOwnerId AND CreatedDate = :Date.Today()];
But now trigger is not getting fired. It allows me to enter all opportunites like normal.


 
Vivian Charlie 1208Vivian Charlie 1208

Hi Govind,

 

Try this

 

trigger OpportunityTrigger on Opportunity (before insert) {
    set<Id> setownerIds = new set<Id>();
    map<Id,Decimal> mapId_Amount = new map<Id,Decimal>();
    for(Opportunity objP : trigger.new){
        setOwnerIds.add(objP.Ownerid);
        if(objP.Amount != null){
            if(!mapId_Amount.containskey(objP.OwnerId)){
                mapId_Amount.put(objP.Ownerid, objP.Amount);
            }else{
                Decimal decCurrentTotal = mapId_Amount.get(objP.OwnerId);
                Decimal newTotal = decCurrentTotal + objP.Amount;
                mapId_Amount.put(objP.Ownerid, newTotal);
            }
        }
    }
    
    list<AggregateResult> lstAgr = [Select OwnerId, SUM(Amount), MAX(CreatedDate) from Opportunity where Createddate = TODAY GROUP BY OwnerId ];
    for(aggregateresult ar : lstAgr){
        System.debug('owner ID' + ar.get('OwnerId'));
        System.debug('Total' + ar.get('expr0'));
        /*
        if(mapId_Amount.containskey(ar.get('Ownerid'))){
            Decimal total = ar.get('expr0') + mapId_Amount.get(ar.get('Ownerid'));
            if(total > 10000){
                trigger.new[0].addError('Error Message'); // update this as per your requirement if necessary
            }
        }
        */
    }
}

 

I first find all owner id's and put the current amount in map of Ownerid and amount.

Aggregate query finds today's Opportunities grouped by the Owner Id. This aggregate result can now be used along with the map to find total and raise error if it exceeds 10000.

Thanks

Vivian

HARSHIL U PARIKHHARSHIL U PARIKH
Thank you for the follow up Vivian,
I am getting a compile Error:
Method does not exist or incorrect signature: void containskey(Object) from the type Map<Id,Decimal> at line 25 column 25
 
Trigger TenThousandDollarsLimit on Opportunity(Before Insert){
    // We need to write a trigger so one user can not create more then
    // $10,000 of opportunity per day.
    
    set<Id> setownerIds = new set<Id>();
    map<Id,Decimal> mapId_Amount = new map<Id,Decimal>();
    for(Opportunity objP : trigger.new){
        setOwnerIds.add(objP.Ownerid);
        if(objP.Amount != null){
            if(!mapId_Amount.containskey(objP.OwnerId)){
                mapId_Amount.put(objP.Ownerid, objP.Amount);
            }else{
                Decimal decCurrentTotal = mapId_Amount.get(objP.OwnerId);
                Decimal newTotal = decCurrentTotal + objP.Amount;
                mapId_Amount.put(objP.Ownerid, newTotal);
            }
        }
    }
    
    list<AggregateResult> lstAgr = [Select OwnerId, SUM(Amount), MAX(CreatedDate) from Opportunity where Createddate = TODAY GROUP BY OwnerId ];
    for(aggregateresult ar : lstAgr){
        System.debug('owner ID' + ar.get('OwnerId'));
        System.debug('Total' + ar.get('expr0'));
        
        if(mapId_Amount.containskey(ar.get('Ownerid'))){
            Decimal total = ar.get('expr0') + mapId_Amount.get(ar.get('Ownerid'));
            if(total > 10000){
                trigger.new[0].addError('Error Message'); // update this as per your requirement if necessary
            }
        }
        
    }
}


 
HARSHIL U PARIKHHARSHIL U PARIKH
And If I don't include those comment out code from line 25 to 30 then it allows me to enter all opps like normal.
Thank you!
Vivian Charlie 1208Vivian Charlie 1208

Govind,

 

Sorry it what happens when we write code in a notepad and not in Salesforce where we can compile and check. My bad, following is the updated code. TRIED and TESTED

 

trigger OpportunityTrigger on Opportunity (before insert) {
    set<Id> setownerIds = new set<Id>();
    map<Id,Decimal> mapId_Amount = new map<Id,Decimal>();
    for(Opportunity objP : trigger.new){
        setOwnerIds.add(objP.Ownerid);
        if(objP.Amount != null){
            if(!mapId_Amount.containskey(objP.OwnerId)){
                mapId_Amount.put(objP.Ownerid, objP.Amount);
            }else{
                Decimal decCurrentTotal = mapId_Amount.get(objP.OwnerId);
                Decimal newTotal = decCurrentTotal + objP.Amount;
                mapId_Amount.put(objP.Ownerid, newTotal);
            }
        }
    }
    
    list<AggregateResult> lstAgr = [Select OwnerId, SUM(Amount), MAX(CreatedDate) from Opportunity where Createddate = TODAY GROUP BY OwnerId ];
    for(aggregateresult ar : lstAgr){
        System.debug('owner ID*****' + ar.get('OwnerId'));
        System.debug('Total*****' + ar.get('expr0'));
        Id ownId = (Id)ar.get('OwnerId');
        system.debug('****ownId*****'+ownId);
        if(mapId_Amount.containskey(ownId)){
            Decimal total = (Decimal)ar.get('expr0') + mapId_Amount.get(ownId);
            system.debug('New Total**********'+total);
            if(total > 10000){
                system.debug('Finally done***********');
                trigger.new[0].addError('Error Message'); // update this as per your requirement if necessary
            }
        }
    }
}

 

Thanks

Vivian

This was selected as the best answer
HARSHIL U PARIKHHARSHIL U PARIKH
You rock man!!
Daniel PeñalozaDaniel Peñaloza
Here is my version:
trigger TenThousandDollarsLimit on Opportunity(before insert) {
    // We need to write a trigger so one user can not create more then
    // $10,000 of opportunity per day.
    
    // Get all owner Ids
    Set<Id> setUserIds = new Set<Id>();
    for (Opportunity newOpp : Trigger.new) {
        Id ownerId = newOpp.OwnerId;
        setUserIds.add(ownerId);
    }

    // Get current total amount of Opportunities by User
    List<AggregateResult> groupedAmounts = [
        SELECT OwnerId, SUM(Amount)amount 
        FROM Opportunity
        WHERE OwnerId IN :setUserIds AND CreatedDate = TODAY
        GROUP BY OwnerId
    ];

    Map<Id, Decimal> mapAmountsByUser = new Map<Id, Decimal>();
    for (AggregateResult result : groupedAmounts) {
        Id ownerId = (String) result.get('OwnerId');
        Decimal amount = (Decimal) result.get('amount');
        mapAmountsByUser.put(ownerId, amount);
    }

    // Check new opportunities total amount by user
    for (Opportunity newOpp : Trigger.new) {
        Id ownerId = newOpp.OwnerId;

        if (!mapAmountsByUser.containsKey(ownerId)) {
            mapAmountsByUser.put(ownerId, 0);
        }

        Decimal newTotalAmount = mapAmountsByUser.get(ownerId) + (newOpp.Amount != null ? newOpp.Amount : 0);
        System.debug('Owner = ' + ownerId + ', Amount = ' + newTotalAmount);
        // If total amount exceeds $10,000 add error to Opportunity
        if (newTotalAmount > 10000) {
            newOpp.addError('You have exceed your daily limit of $10,000 --> $' + newTotalAmount);
        // Put new total amount in the Amounts by User map
        } else {
            mapAmountsByUser.put(ownerId, newTotalAmount);
        }
    }
}

Hope it results useful,
Regards
Vivian Charlie 1208Vivian Charlie 1208
Hi Daniel, We discard the additional for loop done for iterating Opportunity records after the aggregation with my approach. With a reduced iteration the processing time is saved tremendously. My solution incorporates the Owner related Amount summary in the initial for loop itself, you perform this as a separate step later which can be optimized.
Daniel PeñalozaDaniel Peñaloza
Totally agree, I've added it because in case of a massive insertion, we need add the error message especifically to the Opportunity that exceeds the amount. It permits partial success, for example when using Databaser.insert(opportunitiesList, false). Let me know if I'm wrong
HARSHIL U PARIKHHARSHIL U PARIKH
I really appreciate all of the responses and knowledge here from both of you Daniel and Vivan. Thank You guys for making this developer community experience really great!