I'm new to SalesForce development. I have a basic Controller method that takes an Id for a "child" record (FundsRequest__c) and returns a Custom object that I define in the Controller class (FundTotals).

The custom object has a constructor method that I'm using to instantiate for the response for the method. For some reason, the values aren't getting set and instead a new FundTotals object is returned with null values for all of the properties. Here's my code:

FundsRequestCostController.cls: (Class under test)

public with sharing class FundsRequestCostsController {
    public static FundTotals getCosts(Id id) {
        ccprm__FundsRequest__c request = [SELECT
                                            WHERE Id = :id];

        FundTotals ft =  new FundTotals(request.ccprm__Campaign__r.NumberOfOpportunities, request.ccprm__Campaign__r.NumberOfWonOpportunities, request.ccprm__TotalCost__c, request.ccprm__RequestedAmount__c);
        System.debug(LoggingLevel.DEBUG, 'FundTotals: ' + ft);
        return ft;


    public class FundTotals {
        public Integer NumberOfOpportunities;
        public Integer NumberOfWonOpportunities;
        public Decimal TotalCost;
        public Decimal RequestedAmount;

        public FundTotals(Integer numberOfOpportunities, Integer numberOfWonOpportunities, Decimal totalCost, Decimal requestedAmount) {
            NumberOfOpportunities   = numberOfOpportunities;
            NumberOfWonOpportunities= numberOfWonOpportunities;
            TotalCost               = totalCost;
            RequestedAmount         = requestedAmount;
            System.debug(LoggingLevel.DEBUG, 'FundTotals(constructor: ' + this);
            System.debug(LoggingLevel.DEBUG, 'numberOfOpportunities: ' + numberOfOpportunities);


FundsRequestCostControllerTest.cls: (Unit test)

private class FundsRequestCostsControllerTest {
    static void testBehavior() {
        Campaign campaign = new Campaign();
        campaign.Name = 'test campaign';
        insert campaign;

        for(Integer i=0; i (lessthan) 10; i++){
            Opportunity opportunity = new Opportunity();
            opportunity.Name = 'test opp' + i;
            opportunity.Amount = 100 + i;
            opportunity.StageName = 'Closed Won';
            opportunity.CloseDate = date.today();
            opportunity.Campaign = campaign;
            insert opportunity;

        ccprm__FundsRequest__c request = new ccprm__FundsRequest__c();
        request.ccprm__TotalCost__c = 1000.00;
        request.ccprm__RequestedAmount__c = 2000.00;
        request.ccprm__Campaign__c = campaign.Id;

        Account account = new Account();
        account.Name = 'test account';

        insert account;

        request.ccprm__Account__c = account.Id;

        insert request;


        FundsRequestCostsController.FundTotals fundTotals = FundsRequestCostsController.getCosts(request.Id);

        System.assertEquals(10, fundTotals.NumberOfOpportunities);
        System.assertEquals(6, fundTotals.NumberOfWonOpportunities);
        System.assertEquals(10, fundTotals.RequestedAmount);
        System.assertEquals(10, fundTotals.TotalCost);


In the Logs, I see that...

System.Debug(... 'FundsTotals: ' + ft)


|USER_DEBUG|[20]|DEBUG|FundTotals: FundTotals:[NumberOfOpportunities=null, NumberOfWonOpportunities=null, RequestedAmount=null, TotalCost=null]


System.debug(... 'numberOfOpportunities: ' + numberOfOpportunities)


DEBUG|numberOfOpportunities: 0

I'm confused by a few things:

  1. Why is the value of the parameter 0? (should be 10 with how I've created 10 opportunities in a for loop)?
  2. Why aren't the values (0) assigned to the FundTotals object (returns with null)?
  • Your question seems incomplete in a few spots. – Adrian Larson Nov 3 '16 at 17:17
  • 1
    I suspect that's because the campaign rollup fields are not calculated in real-time; on the detail page of a real campaign, you'll see a little notice that reads something like "may be delayed by 10 minutes." – sfdcfox Nov 3 '16 at 17:19
  • the code formatting was having an issue with the < symbol in the for loop. I've fudged it for readability – Ian Tunbridge Nov 3 '16 at 17:21
  • @sfdcfox : I see... Any suggestions to get around this? Also, any insight on the second issue? Why is it getting set as null instead of 0? – Ian Tunbridge Nov 3 '16 at 17:22
  • @IanTunbridge I don't, actually. However, I don't have time to replicate this right now, so maybe someone else in the community can help. – sfdcfox Nov 3 '16 at 17:34

Because Apex is case insensitive this:

public FundTotals(Integer numberOfOpportunities, ...) {
    NumberOfOpportunities = numberOfOpportunities;

is assigning the method parameter to itself.

Instead you need to assign the parameter value to the field like this:

public FundTotals(Integer numberOfOpportunities, ...) {
    this.numberOfOpportunities = numberOfOpportunities;

(You could also use different names, but personally I prefer to use the same name and keep a look out for missing this..)

The numberOfOpportunities is zero because that is the automatically assigned default value for that type of field.

  • Yes, I second using the same name and this.property = property. Keeps code cleaner imo. – Adrian Larson Nov 4 '16 at 13:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.