+ Start a Discussion
Stéphane C.Stéphane C. 

Quick action to create a quote with a lightning component

Hi,

I try to make a quik action and a lightning component to create a quote :

Component.CMP
<aura:component controller="QuickQuoteController" 
                implements="force:lightningQuickActionWithoutHeader,force:hasRecordId">

    <aura:attribute name="opportunity" type="Opportunity" />
    <aura:attribute name="newQuote" type="Quote"
        default="{ 'sobjectType': 'Quote' }" />
    
    <!-- default to empty record -->
    
    <aura:handler name="init" value="{!this}" action="{!c.doInit}" />

    <!-- Display a header with details about the opportunity -->
    <div class="slds-page-header" role="banner">
        <p class="slds-text-heading_label">{!v.opportunity.Name}</p>
        <h1 class="slds-page-header__title slds-m-right_small
            slds-truncate slds-align-left">Créer un nouveau devis</h1>
    </div>

    <!-- Display the new quote form -->
     <lightning:input aura:id="quoteField" name="name" label="Name"
                      value="{!v.newQuote.Name}" required="true"/>

    <lightning:input aura:id="quoteField" type="date" name="date" label="Date"
                     value="{!v.newQuote.Date}" required="true"/>
    
    <lightning:button label="Cancel" onclick="{!c.handleCancel}" class="slds-m-top_medium" />
    <lightning:button label="Save Quote" onclick="{!c.handleSaveQuote}"
               variant="brand" class="slds-m-top_medium"/>
    
</aura:component>

Controller.JS
({
    doInit : function(component, event, helper) {

        // Prepare the action to load opportunity record
        var action = component.get("c.getOpportunity");
        action.setParams({"opportunityId": component.get("v.recordId")});

        // Configure response handler
        action.setCallback(this, function(response) {
            var state = response.getState();
            if(state === "SUCCESS") {
                component.set("v.opportunity", response.getReturnValue());
            } else {
                console.log('Problem getting opportunity, response state: ' + state);
            }
        });
        $A.enqueueAction(action);
    },

    handleSaveQuote: function(component, event, helper) {
        if(helper.validateQuoteForm(component)) {
            
            // Prepare the action to create the new quote
            var saveQuoteAction = component.get("c.saveQuoteWithOpportunity");
            saveQuoteAction.setParams({
                "quote": component.get("v.newQuote"),
                "opportunityId": component.get("v.recordId")
            });

            // Configure the response handler for the action
            saveQuoteAction.setCallback(this, function(response) {
                var state = response.getState();
                if(state === "SUCCESS") {

                    // Prepare a toast UI message
                    var resultsToast = $A.get("e.force:showToast");
                    resultsToast.setParams({
                        "title": "Quote Saved",
                        "message": "The new quote was created."
                    });

                    // Update the UI: close panel, show toast, refresh quote page
                    $A.get("e.force:closeQuickAction").fire();
                    resultsToast.fire();
                    $A.get("e.force:refreshView").fire();
                }
                else if (state === "ERROR") {
                    console.log('Problem saving quote, response state: ' + state);
                }
                else {
                    console.log('Unknown problem, response state: ' + state);
                }
            });

            // Send the request to create the new quote
            $A.enqueueAction(saveQuoteAction);
        }
        
    },

	handleCancel: function(component, event, helper) {
	    $A.get("e.force:closeQuickAction").fire();
    }

})

Helper.JS
({
    validateQuoteForm: function(component) {
        var validQuote = true;

        
        // Show error messages if required fields are blank
        var allValid = component.find('quoteField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);

        if (allValid) {
        // Verify we have an opportunity to attach it to
        var opportunity = component.get("v.opportunity");
        if($A.util.isEmpty(opportunity)) {
            validOpportunity = false;
            console.log("Quick action context doesn't have a valid opportunity.");
        }

        return(validOpportunity);
	}
    }
})

Controller.APXC
public with sharing class QuickQuoteController {

    @AuraEnabled
    public static Opportunity getOpportunity(Id opportunityId) {
        // Perform isAccessible() checks here
        return [SELECT Name FROM Opportunity WHERE Id = :opportunityId];
    }
    
    @AuraEnabled
    public static Quote saveQuoteWithOpportunity(Quote quote, Id opportunityId) {
        // Perform isAccessible() and isUpdateable() checks here
        quote.OpportunityId = opportunityId;
        upsert quote;
        return quote;
    }

}
I have an issue and I can't find where it comes :
 
Uncaught Action failed: c:quickQuote$controller$handleSaveQuote [validOpportunity is not defined]

markup://c:quickQuote

Object.validateQuoteForm()@https://org--dev.lightning.force.com/one/components/c/quickQuote.js:89:9
handleSaveQuote()@https://org--dev.lightning.force.com/one/components/c/quickQuote.js:27:19
handleClick()@https://org--dev.lightning.force.com/components/lightning/button.js:1:470

Any idea ?
 
Tuan LuTuan Lu
Looks like you did not define validOpportunity as var. Lightning using strict javascript.
Stéphane C.Stéphane C.
Thank you Tuan.

How can I fix it?
Stéphane C.Stéphane C.
Find my issue....
 
({
    validateQuoteForm: function(component) {
        var validQuote = true;
        
        // Show error messages if required fields are blank
        var allValid = component.find('quoteField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);

        if (allValid) {
        // Verify we have an opportunity to attach it to
        var opportunity = component.get("v.opportunity");
        if($A.util.isEmpty(opportunity)) {
            validQuote = false;
            console.log("Quick action context doesn't have a valid opportunity.");
        }

        return(validQuote);
	}
    }
})

 
Tuan LuTuan Lu
:)
sfdcMonkey.comsfdcMonkey.com
hi ,
update your lightning component js helper code with below code :
({
    validateQuoteForm: function(component) {
        var validQuote = true;

        
        // Show error messages if required fields are blank
        var allValid = component.find('quoteField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);

        if (allValid) {
        // Verify we have an opportunity to attach it to
        var opportunity = component.get("v.opportunity");
        var validOpportunity = true;
           if($A.util.isEmpty(opportunity)) {
             validOpportunity = false;
            console.log("Quick action context doesn't have a valid opportunity.");
        }

        return validOpportunity;
	}
    }
})
i hope it helps you.
  kindly Let me inform if it helps you and close your query by choosing best answer if you got your right answer so it can helps others
thanks
sfdcmonkey.com
 
Stéphane C.Stéphane C.
Hi Piyush,

I have to modify my working code with this :
 
if (allValid) {
        // Verify we have an opportunity to attach it to
        var opportunity = component.get("v.opportunity");
        var validOpportunity = true;
           if($A.util.isEmpty(opportunity)) {
             validOpportunity = false;
            console.log("Quick action context doesn't have a valid opportunity.");
        }

Why?
sfdcMonkey.comsfdcMonkey.com
Why ? because as you mention in your query that you got following error :
Uncaught Action failed: c:quickQuote$controller$handleSaveQuote [validOpportunity is not defined]

so in your javaScript helper code you are not decalre 'validOpportunity' variable and directly assign false value to this, so i just declare validOpportunity variable with default value 'true'

if (allValid) {
// Verify we have an opportunity to attach it to
var opportunity = component.get("v.opportunity");
var validOpportunity = true;
if($A.util.isEmpty(opportunity)) {
   validOpportunity = false;
   console.log("Quick action context doesn't have a valid opportunity.");
}

this will resolve your error issue
Thanks
Stéphane C.Stéphane C.
Piyush,

Ok. I found my error before. Look at the entire post. I have make a mistake with some variable.   ;-)
 
({
    validateQuoteForm: function(component) {
        var validQuote = true;
        
        // Show error messages if required fields are blank
        var allValid = component.find('quoteField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);

        if (allValid) {
        // Verify we have an opportunity to attach it to
        var opportunity = component.get("v.opportunity");
        if($A.util.isEmpty(opportunity)) {
            validQuote = false;
            console.log("Quick action context doesn't have a valid opportunity.");
        }

        return(validQuote);
	}
    }
})

So, I go further with my project, and perhaps you will help me too.   ;-)

Thank you for your return.
Stéphane C.Stéphane C.
How can I put a default value to "{!v.newQuote.Name}"? Same thing with "{!v.newQuote.Date}"?

I made to picklist an I want to make dependencies :
loadModeles: function (component, event, helper) {
        var modeles = [
            { value: "Mod 1", label: "Mod 2" },
            { value: "Mod 2", label: "Mod 2" },
            { value: "Mod 3", label: "Mod 3" }
        ];
        component.set("v.statusModeles", modeles);
    },

    loadColors: function (component, event, helper) {
        var colors = [
            { value: "White", label: "White" },
            { value: "Grey", label: "Grey" },
            { value: "Black", label: "Black" },
        ];
        component.set("v.statusColors", colors);
    },

How can I make that :
- When Mod 1 is selected only White and Grey Colors are avaible ;
- When Mod 2 is selected only White, Grey and Black Colors are avaible ;
- When Mod 3 is selected only White Color is avaible.
Stéphane C.Stéphane C.
Any idea to have a default value like this : name = opportunity's name + " - Quote" ?
kashish choudharykashish choudhary
Try this in the Helper class 
({
validateQuoteForm: function(component) {
var validQuote = true;
// Show error messages if required fields are blank
var allValid = component.find('quoteField').reduce(function (validFields, inputCmp) {
inputCmp.showHelpMessageIfInvalid();
return validFields && inputCmp.get('v.validity').valid;
}, true);
if (allValid)
{
// Verify we have an opportunity to attach it to
var opportunity = component.get("v.opportunity");
if($A.util.isEmpty(opportunity)) {
validOpportunity = false;
console.log("Quick action context doesn't have a valid opportunity.");
}
return(validOpportunity);
} } })