[SalesForce] Pass parameter from lightning component to controller

I try to pass date value to controller parameter and use it as a query but end up with error "attempt to de-reference a null object". Is there something that i missed?

I got an "ERROR" state when execute the method.

enter image description here

Note there are records that match with my query

Component

<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes" access="global" controller="BACSPaymentManagerController">
    <lightning:workspaceAPI aura:id="workspace" />
    <aura:attribute name="BACSPaymentDatetime" type="Datetime" />
    <aura:attribute name="DateDueStart" type="Date"/>
    <aura:attribute name="DateDueEnd" type="Date" />
    <aura:attribute name="NextNDays" type="Integer" />
    <aura:attribute name="BACSConfirmation" type="Boolean" default="false"/>
    <aura:attribute name="BACSConfirmationAnswer" type="Boolean" default="false"/>
    <aura:attribute name="Notification" type="String" default=""/>
    <aura:attribute name="paymentRecords" type="List" />
    <aura:attribute name="selectedRows" type="List" />
    <aura:attribute name="rowsSelected" type="List" />
    <aura:attribute name="columns" type="List" />   
    <aura:handler name="init" action="{!c.init}" value="{!this}" />
    <aura:handler event="aura:waiting" action="{!c.waiting}"/>
    <aura:handler event="aura:doneWaiting" action="{!c.doneWaiting}"/>
    <aura:attribute name="wait" type="String"/>

    <lightning:card title="BACS" iconName="standard:file">
        <!-- INPUT GRID -->
        <div class="slds-grid_vertical slds-gutters">
            <div class="slds-col slds-grid ">
                <div class="slds-col slds-align_absolute-center">
                    <span>Please select payments with due<br/>dates between the following dates</span>
                </div>
                <div class="slds-col slds-grid_vertical">
                    <div class="slds-col">
                        <lightning:input type="date" label="Date Due Start" value="{!v.DateDueStart}" required="true"/>
                    </div>
                    <div class="slds-col">
                        <lightning:input type="date" label="Date Due End" value="{!v.DateDueEnd}" required="true"/>
                    </div>
                    <!--<div class="slds-col">
                        <lightning:input type="Number" label="Next N Days" value="{!v.NextNDays}" required="true"/>
                    </div>-->
                </div>
                <div class="slds-col slds-align_absolute-center">
                    <lightning:button class="slds-button_brand" label="Fetch Payments" onclick="{!c.fetchPayments}"/>
                </div>
            </div>
         </div>
    </lightning:card>
</aura:component>

Client side Controller

({
    init : function(component, event, helper) {
        var today = new Date();
        var monthDigit = today.getMonth() + 1;
        if (monthDigit <= 9) {
            monthDigit = '0' + monthDigit;
        }
        component.set("v.DateDueStart", today.getFullYear() + "-" + monthDigit + "-" + today.getDate());
    },

    fetchPayments : function(component, event, helper) {
        helper.fetchPayments(component, event);
    }
}

Helper

({
    fetchPayments : function(component, event) {
        var startDate = component.get("v.DateDueStart");
        var endDate = component.get("v.DateDueEnd");
        console.log(startDate + ' --'+endDate);
        var action = component.get("c.getPayments");
        action.setParams({"DateDueStart":component.get("v.DateDueStart"), "DateDueEnd":component.get("v.DateDueEnd")});
        action.setCallback(this, function(response){
            console.log(response.getState());
            if(response.getState() == 'SUCCESS'){
                if(response.getReturnValue() != undefined){
                    component.set("v.columns",[
                        {label:'Id', fieldName:'Id',type:'text'},
                        {label:'Payment Id', fieldName:'Name',type:'text'},
                        {label:'Client Reference', fieldName:'Contact__r.Client_Reference__c',type:'text'},
                        {label:'Client Status', fieldName:'Contact__r.Status__c',type:'text'},
                        {label:'Amount', fieldName:'Amount_Paid__c', type:'currency', typeAttribute:{currencyCode:'GBP'}},
                        {label:'Payment Code', fieldName:'Payment_Code__r.Name',type:'text'},
                        {label:'Payment Type', fieldName:'Payment_Type__c',type:'text'},
                        {label:'Date Due', fieldName:'Date_Due__c',type:'date'},
                        {label:'Date Paid', fieldName:'Date_Paid__c',type:'date'}
                    ]);
                    component.set("v.paymentRecords", response.getReturnValue());
                }
            }
        });
        $A.enqueueAction(action);
    }
}

Controller

public class BACSPaymentManagerController {
        @AuraEnabled
        public static List<Payment__c> getPayments(Date DateDueStart, Date DateDueEnd){
            Date startDate = Date.newInstance(DateDueStart.year(),DateDueStart.month(),DateDueStart.day());
            Date endDate = Date.newInstance(DateDueEnd.year(),DateDueEnd.month(),DateDueEnd.day());
            if(DateDueStart != null && DateDueEnd != null){
                List<Payment__c> allFilteredPayments = [SELECT Date_Paid__c, Amount_Paid__c, Name, Name__c, Account_Number__c, Sort_Code__c, Contact__r.Client_Reference__c, Contact__r.Status__c, Contact__r.Client_Reference_BACS__c, Date_Due__c, Payment_Code__r.Name, Payment_Type__c FROM Payment__c WHERE Payment_Type__c = 'BACS' AND Date_Paid__c =NULL AND Confirm_Payment__c = false AND Date_Due__c >= :startDate AND Date_Due__c <=:endDate];
                return allFilteredPayments;      
            }
            else{
                List<Payment__c> allFilteredPayments = [SELECT Date_Paid__c, Amount_Paid__c, Name, Name__c, Account_Number__c, Sort_Code__c, Contact__r.Client_Reference__c, Contact__r.Status__c, Contact__r.Client_Reference_BACS__c, Date_Due__c, Payment_Code__r.Name, Payment_Type__c FROM Payment__c WHERE Payment_Type__c = 'BACS' AND Date_Paid__c =NULL AND Confirm_Payment__c = false];
                return allFilteredPayments;      
            }
        }
    }

Best Answer

This is a classic problem of passing complex data types other than String to Apex controllers from JS. You can refer to such of one posts here.

In your case, even though you get the right values in your JS, when it reaches to your apex method, it never reaches as a valid Date instead as a null. And you can verify this by putting a debug statement to print it in your apex method.

Then when you try to retrieve the values on the line Date startDate = Date.newInstance(DateDueStart.year(),DateDueStart.month(),DateDueStart.day()); and because the DateDueStart was always null, you get the NPE.

One of the approaches (that I have always used to overcome such issues) is that you would need to stringify the date value from your JS controller and then accordingly change that back to a date in your apex method.

So your JS code where you are setting the parameters for apex method becomes as:

action.setParams({"DateDueStart":JSON.stringify(component.get("v.DateDueStart")), "DateDueEnd":JSON.stringify(component.get("v.DateDueEnd"))});

And then in your apex method becomes as:

 @AuraEnabled
 public static List<Payment__c> getPayments(String DateDueStart, String DateDueEnd) {
    DateTime startDate = (DateTime) JSON.deserialize(DateDueStart, DateTime.class);
    ....rest of the code....
}

An additional note, change your logic where you compare the dates for null in your apex.

From: if(DateDueStart != null && DateDueEnd != null)

To: if(startDate != null && endDate != null)

And this is because you are using these variables in your SOQL and not the ones you received as an argument in your method.