+ Start a Discussion
Nishant SharmaNishant Sharma 
Not sure, what is wrong with following code. While validating, it give me following error:
The campingList component isn't handing the added item correctly.

Here is the code:
1. camping.cmp
<aura:component >
    <c:campingHeader />
    <c:campingList />
</aura:component>
2. CampingList.cmp
<aura:component controller="CampingListController">
    
    <aura:attribute name="items" type="Camping_Item__c[]"/>
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" action="{!c.handleAddItem}" event="c:addItemEvent"/>
    
    <c:campingListForm />
    
    <div class="slds-card slds-p-top--medium">
        <header class="slds-card__header">
            <h3 class="slds-text-heading--small">Expenses</h3>
        </header>
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="item">
                    <c:campingListItem item="{!item}"/>
                    <br/>
                </aura:iteration>
            </div>
        </section>
    </div>
    
</aura:component>

3. campingListController.js
({
    // Load expenses from Salesforce
    doInit: function(component, event, helper) {
        
        // Create the action
        var action = component.get("c.getItems");
        
        // Add callback behavior for when response is received
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                component.set("v.items", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
        
        // Send action off to be executed
        $A.enqueueAction(action);
    },
    
    handleAddItem : function(component, event, helper) {
        
        var action = component.get("c.saveItem");
        action.setParams({
            "item": JSON.parse(JSON.stringify(event.getParam("item")))
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                var items = component.get("v.items");
                items.push(response.getReturnValue());
                component.set("v.items", items);
            }
        });
        $A.enqueueAction(action);
    }
})

4. componentListHelper.js
({
    createCamping: function(component,item) {
        
        var action = component.get("c.saveItem");
        action.setParams({
            "item": JSON.parse(JSON.stringify(item))
        });
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                var items = component.get("v.items");
                items.push(response.getReturnValue());
                component.set("v.items", items);
            }
        });
        $A.enqueueAction(action);
    }
})
5. campingListForm.cmp
<aura:component >
    
    <aura:attribute name="newItem" type="Camping_Item__c"
                    default="{ 'sobjectType': 'Camping_Item__c',
                             'Name': '',
                             'Packed__c': false,
                             'Price__c': '0',
                             'Quantity__c': '0' }"/>
    
    <aura:registerEvent name="addItem" type="c:addItemEvent"/>
    
    <div aria-labelledby="newitemform">
        <fieldset class="slds-box slds-theme--default slds-container--small">
            
            <legend id="newitemform" class="slds-text-heading--small 
                                            slds-p-vertical--medium">
                Add Camping Item
            </legend>
            
            <form class="slds-form--stacked">
                
                <div class="slds-form-element slds-is-required">
                    <div class="slds-form-element__control">
                        <ui:inputText aura:id="name" label="Camping Item Name"
                                      class="slds-input"
                                      labelClass="slds-form-element__label"
                                      value="{!v.newItem.Name}"
                                      required="true"/>
                    </div>
                </div>
                
                <div class="slds-form-element">
                    <ui:inputCheckbox aura:id="packed" label="Packed?"
                                      class="slds-checkbox"
                                      labelClass="slds-form-element__label"
                                      value="{!v.newItem.Packed__c}"/>
                </div>
                
                <div class="slds-form-element">
                    <div class="slds-form-element__control">
                        <ui:inputCurrency aura:id="price" label="Price"
                                          class="slds-input"
                                          labelClass="slds-form-element__label"
                                          value="{!v.newItem.Price__c}" />
                        
                    </div>
                </div>
                
                <div class="slds-form-element">
                    <div class="slds-form-element__control">
                        <ui:inputNumber aura:id="quantity" label="Quantity"
                                        class="slds-input"
                                        labelClass="slds-form-element__label"
                                        value="{!v.newItem.Quantity__c}"/>
                        
                    </div>
                </div>
                
                <div class="slds-form-element">
                    <ui:button label="Create Camping Item"
                               class="slds-button slds-button--brand"
                               press="{!c.submitForm}"/>
                </div>
                
            </form>
            
        </fieldset>
    </div>
    
</aura:component>
6. campingListFormController
({    
    
    submitForm : function(component, event, helper) {
        
        var validCamping = true;

        // Name must not be blank
        var nameField = component.find("name");
        var expname = nameField.get("v.value");
        if ($A.util.isEmpty(expname)){
            validCamping = false;
            nameField.set("v.errors", [{message:"Camping Item name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }

        
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        if ($A.util.isEmpty(price) || isNaN(price) || (price <= 0.0)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping Item price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
        var quantityField = component.find("quantity");
        var quantity = quantityField.get("v.value");
        if ($A.util.isEmpty(quantity) || isNaN(quantity) || (quantity <= 0)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping Item quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }

        if(validCamping){
            
            helper.createItem(component);
        }
    },
})

7. campingListFormHelper.js
({
    
     createItem : function(component) {
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
        component.set("v.newItem",
                     { 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': 0,
                    'Quantity__c': 0});
    }
})

8. addItemEvent.evt
<aura:event type="COMPONENT" description="Event template">
    <aura:attribute name="item" type="Camping_Item__c"/>
</aura:event>

It works well, when I test this app. any thought? Can anyone share the working code for this module?


 
Best Answer chosen by Nishant Sharma
Nayana KNayana K
Seems handleAddItem method body of campingListController.js should be :
 
handleAddItem: function(component, event, helper) {
        var item = event.getParam("item");
                
        var action = component.get("c.saveItem");
//json stringify is not needed I think.       
action.setParams({
            "item": item
        });
        
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {        
                var items = component.get("v.items");
                items.push(item);
                component.set("v.items",items);
            }
        });
        $A.enqueueAction(action);
    }

Just for reference I will post whole code :

campingList.cmp
<aura:component controller="CampingListController">
    
    <aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
    <aura:handler name="addItem" event="c:addItemEvent" action="{!c.handleAddItem}"/>
    
    <div class="slds-page-header" role="banner">

      <div class="slds-grid">

        <div class="slds-col">

          <p class="slds-text-heading--label">Camping Items</p>

          <h1 class="slds-text-heading--medium">My Camping Items</h1>

        </div>

      </div>

    </div>

      
  <div aria-labelledby="newitemform">

      <fieldset class="slds-box slds-theme--default slds-container--small">
    
        <c:campingListForm />
    
      </fieldset>

	</div>
    
    
     <aura:attribute name="items" type="Camping_Item__c[]"/>

    <div class="slds-card slds-p-top--medium">
        <header class="slds-card__header">
            <h3 class="slds-text-heading--small">Camping List Items</h3>
        </header>
        
        <section class="slds-card__body">
            <div id="list" class="row">
                <aura:iteration items="{!v.items}" var="item">
                    <c:campingListItem item="{!item}"/>
                </aura:iteration>
            </div>
        </section>
    </div>

</aura:component>

campingListController.js
({
    
    doInit: function(component, event, helper) {
    
        var action = component.get("c.getItems");
    
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {
                component.set("v.items", response.getReturnValue());
            }
            else {
                console.log("Failed with state: " + state);
            }
        });
    
        $A.enqueueAction(action);
    },    
    
    handleAddItem: function(component, event, helper) {
        var item = event.getParam("item");
                
        var action = component.get("c.saveItem");
        action.setParams({
            "item": item
        });
        
        action.setCallback(this, function(response){
            var state = response.getState();
            if (component.isValid() && state === "SUCCESS") {        
                var items = component.get("v.items");
                items.push(item);
                component.set("v.items",items);
            }
        });
        $A.enqueueAction(action);
    }
    
})

campingListHelper.js
({
	createItem : function(component, item) {
        var action = component.get("c.saveItem");
    action.setParams({
        "item": item
    });
    action.setCallback(this, function(response){
        var state = response.getState();
        if (component.isValid() && state === "SUCCESS") {
            var items = component.get("v.items");
            items.push(response.getReturnValue());
            component.set("v.items", items);
            component.set("v.newItem",{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Quantity__c': 0,
                    'Price__c': 0,
                    'Packed__c': false });
        }
    });
    $A.enqueueAction(action);
		/*var theItems = component.get("v.items");
 
        // Copy the expense to a new object
        // THIS IS A DISGUSTING, TEMPORARY HACK
        var newItem = JSON.parse(JSON.stringify(item));
 
        theItems.push(newItem); 
        component.set("v.items", theItems);
       console.log('theItems helper==',theItems);*/
    
	},
    
    validateItemForm: function(component) {

    // Simplistic error checking
    var validExpense = true;

    // Name must not be blank
    var fld = component.find("ciName");
    var fldVal = fld.get("v.value");
    if ($A.util.isEmpty(fldVal)){
        validExpense = false;
        fld.set("v.errors", [{message:"Name can't be blank."}]);
    }
    else {
        fld.set("v.errors", null);
    }

    // Amount must be set, must be a positive number
    var quantFld = component.find("ciQuantity");
    var quantFldVal = quantFld.get("v.value");
    if ($A.util.isEmpty(quantFldVal) || isNaN(quantFldVal) || (quantFldVal <= 0.0)){
        validExpense = false;
        quantFld.set("v.errors", [{message:"Enter qunatity."}]);
    }
    else {
        // If the amount looks good, unset any errors...
        quantFld.set("v.errors", null);
    }
        
        // Amount must be set, must be a positive number
    var priceFld = component.find("ciPrice");
    var priceFldVal = priceFld.get("v.value");
    if ($A.util.isEmpty(priceFldVal) || isNaN(priceFldVal) || (priceFldVal <= 0.0)){
        validExpense = false;
        priceFld.set("v.errors", [{message:"Enter price."}]);
    }
    else {
        // If the amount looks good, unset any errors...
        priceFld.set("v.errors", null);
    }
    
    return(validExpense);
}
})
campingListForm.cmp
<aura:component >
    
     <aura:attribute name="newItem" type="Camping_Item__c"
     default="{ 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': '0',
                    'Quantity__c': '0' }"/>
	<aura:registerEvent name="addItem" type="c:addItemEvent"/>
    
  <div aria-labelledby="newitemform">
      <fieldset class="slds-box slds-theme--default slds-container--small">
    
        <legend id="newitemform" class="slds-text-heading--small 
          slds-p-vertical--medium">
          Add Camping Item
        </legend>
    
        <form class="slds-form--stacked">
    
          <div class="slds-form-element slds-is-required">
              <div class="slds-form-element__control">
                  <ui:inputText aura:id="name" label="Camping Item Name"
                      class="slds-input"
                      labelClass="slds-form-element__label"
                      value="{!v.newItem.Name}"
                      required="true"/>
              </div>
         </div>
            
          <div class="slds-form-element">
              <ui:inputCheckbox aura:id="packed" label="Packed?"
                  class="slds-checkbox"
                  labelClass="slds-form-element__label"
                  value="{!v.newItem.Packed__c}"/>
          </div>
            
        <div class="slds-form-element">
              <div class="slds-form-element__control">
                  <ui:inputCurrency aura:id="price" label="Price"
                      class="slds-input"
                      labelClass="slds-form-element__label"
                      value="{!v.newItem.Price__c}" />
    
              </div>
          </div>
    
         <div class="slds-form-element">
              <div class="slds-form-element__control">
                  <ui:inputNumber aura:id="quantity" label="Quantity"
                      class="slds-input"
                      labelClass="slds-form-element__label"
                      value="{!v.newItem.Quantity__c}"/>
    
              </div>
          </div>
    
          <div class="slds-form-element">
              <ui:button label="Create Camping Item"
                  class="slds-button slds-button--brand"
                  press="{!c.submitForm}"/>
          </div>
    
        </form>
    
      </fieldset>
</div>

</aura:component>

campingListFormController.js
({    
    
    submitForm : function(component, event, helper) {
        
        var validCamping = true;

        // Name must not be blank
        var nameField = component.find("name");
        var expname = nameField.get("v.value");
        if ($A.util.isEmpty(expname)){
            validCamping = false;
            nameField.set("v.errors", [{message:"Camping Item name can't be blank."}]);
        }
        else {
            nameField.set("v.errors", null);
        }

        
        var priceField = component.find("price");
        var price = priceField.get("v.value");
        if ($A.util.isEmpty(price) || isNaN(price) || (price <= 0.0)){
            validCamping = false;
            priceField.set("v.errors", [{message:"Camping Item price can't be blank."}]);
        }
        else {
            priceField.set("v.errors", null);
        }
        
        var quantityField = component.find("quantity");
        var quantity = quantityField.get("v.value");
        if ($A.util.isEmpty(quantity) || isNaN(quantity) || (quantity <= 0)){
            validCamping = false;
            quantityField.set("v.errors", [{message:"Camping Item quantity can't be blank."}]);
        }
        else {
            quantityField.set("v.errors", null);
        }

        if(validCamping){
            
            helper.createItem(component);
            
        }
        
    },
})

campingListFormHelper.js
({

     createItem : function(component) {
        var newItem = component.get("v.newItem");
        var addEvent = component.getEvent("addItem");
        addEvent.setParams({"item" : newItem});
        addEvent.fire();
        component.set("v.newItem",
                     { 'sobjectType': 'Camping_Item__c',
                    'Name': '',
                    'Packed__c': false,
                    'Price__c': 0,
                    'Quantity__c': 0});
    }

})


 
Liam JeongLiam Jeong 
Hey folks,

I would like to create a custom report type for Approval History(ProcessInstanceStep).

My first approach was creating a trigger or record-triggered flow on the ProcessInstanceStep object.
But for some reason, Salesforce doesn't allow me to create a trigger on the object.

Background information,
There are more than 2,000 submitted approvals daily based.
The client wants to maximize declarative tools' usability.(Not Programatical solution)

How should I resolve this issue?

Much obliged, in advance.
Liam
Best Answer chosen by Liam Jeong
Naveen KNNaveen KN
Thanks for the details, Liam.

I was in the assumption that it is for a single object. Then, it will be difficult to handle all the approval processes(multiple objects) from a single place.

I think we have to Go with Batch apex now!

--
Naveen 
 
jonathanrico.jonathanrico. 

Hello Everyone,

 

Does anyone know if its possible to bring the translated value of a Picklist field in a Text Formula Field.

 

I currently have a status picklist field in Object A. Object B is a detail of Object A and I need to display the value of the status field in Object B.

 

I need to support multiple languages, however, when using translations the picklist label translates perfectly at the Object A level but the child records of Object B display the original value.

 

I guess formula fields always retrieve the master labels, is there anyway to get around this in order to get the translated value?

 

Thanks in advance!

Best Answer chosen by Admin (Salesforce Developers) 
Ispita_NavatarIspita_Navatar

I think this is an idea for this type of problem faced by another person. You can go and promote the idea and may be in the subsequent releases this may be released as a feature.

 

The following was the response to such a post:-

 

I have investigated this request 
and have found that there is no practical
way of returning the translated value in the
formula field using the standard
functionality of Salesforce.


Something you could look into
would be to use a trigger to return
the translated value by incorporating
the function mentioned on this page:
http://www.salesforce.com/us/developer/docs/api/Content/sforce_a

 

 

Did this answer your question? If not, let me know what didn't work, or if so, please mark it solved.

Durgesh VyasDurgesh Vyas 
I want to disable personalize nav bar options for users.
Best Answer chosen by Durgesh Vyas
Dinesh GopalakrishnanDinesh Gopalakrishnan
Hi Durgesh,

Please check the Below Options or check the Doc from the Below Link
  • If you don’t want your users to personalize the navigation bar for a specific app, disable personalization. From Setup in Lightning Experience, go to the App Manager. For the desired app, select App Options. Select Disable end user personalization of nav items in this app.
  • If you don’t want your users to personalize the navigation bar for any app, disable personalization. From Setup, enter User Interface in the Quick Find box, then select User Interface. Select Disable Navigation Bar Personalization in Lightning Experience. Salesforce recommends disabling navigation personalization by app instead of for the entire org.
https://help.salesforce.com/articleView?id=user_userdisplay_tabs_lex_considerations.htm&type=5

Kindly Mark this as a Best Answer if you Find this Useful!

Thanks
DineshKumar Gopalakrishnan
Oscar Fernando De la CruzOscar Fernando De la Cruz 
Hi everyone, I'm  new on LWC . and  I'm doing  this example to generate a PDF https://santanuboral.blogspot.com/2020/10/lwc-generate-pdf.html
everything works fine  but now Im trying to save the PDF to  the  correspondet contact depending on the ID,  any idea of how I can do that?   this is my code as we can see Im hard coding the id 

 
public with sharing class DisplayRichTextHelper {
    @AuraEnabled
public static Attachment generatePDF(String txtValue){

        Pagereference pg = Page.renderAsPDF;
        pg.getParameters().put('displayText', txtValue);

        Contact con = new Contact(Id='0035f000008RNWCAA4');

        Attachment objAttachment = new Attachment();
        objAttachment.Name = 'J2S.pdf';
        objAttachment.ParentId = con.Id;
        objAttachment.Body = pg.getContentaspdf();
        objAttachment.IsPrivate = false;
        insert objAttachment;
        return objAttachment;
    }
}
 

And i want to change for some like this:
 ApexPages.currentPage().getParameters().get('id');
Best Answer chosen by Oscar Fernando De la Cruz
Maharajan CMaharajan C
Hi Oscar,

I hope you are using this LWC Component in Contact Record Page.

Please add the below changes in your code.

Apex Class:
 
public with sharing class DisplayRichTextHelper {
    
    @AuraEnabled
    public static Attachment generatePDF(String txtValue, String contactId){
        Pagereference pg = Page.renderAsPDF;
        pg.getParameters().put('displayText', txtValue);
        Attachment objAttachment = new Attachment();
        objAttachment.Name = 'J2S.pdf';
        objAttachment.ParentId = contactId;
        objAttachment.Body = pg.getContentaspdf();   
        objAttachment.IsPrivate = false;
        insert objAttachment;
        return objAttachment;
    }
    
}

LWC JS:

1. Add the api in import...

2. Declare the @api recordId;

3. Chnage in generatePDF({txtValue: editor.value , contactId : this.recordId })
import { LightningElement, api } from 'lwc';
import generatePDF from '@salesforce/apex/DisplayRichTextHelper.generatePDF';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';


export default class LWCPDFGenerator extends LightningElement {

    @api recordId;

    allowedFormats =  ['font', 'size', 'bold', 'italic', 'underline', 'strike',
    'list', 'indent', 'align', 'link', 'image', 'clean', 'table', 'header', 'color',
    'background','code','code-block'];

    //this method will display initial text
    get myVal() {
        return '**Generate PDF using LWC Component 2 **';
    }

    attachment; //this will hold attachment reference

    /*This method extracts the html from input rich text 
        and pass this to apex class' method via implcit call
    */
    saveAsPdf(){
        const editor = this.template.querySelector('lightning-input-rich-text');
        //implicit calling apex method
        generatePDF({txtValue: editor.value , contactId : this.recordId })
        .then((result)=>{
            this.attachment = result;
                console.log('attachment id=' + this.attachment.Id);
                //show success message
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'PDF generated successfully with Id:' + this.attachment.Id,
                        variant: 'success',
                    }),
                );
        })
        .catch(error=>{
            //show error message
            this.dispatchEvent(
                new ShowToastEvent({
                    title: 'Error creating Attachment record',
                    message: error.body.message,
                    variant: 'error',
                }),
            );
        })
    }
    
    /*
        This method updates the selected text with defined format
    */
    handleClick() {
        const editor  = this.template.querySelector('lightning-input-rich-text');
        const textToInsert = 'Journey to Salesforce'
        editor.setRangeText(textToInsert, undefined, undefined, 'select')
        editor.setFormat({bold: true, size:24, color: 'green', align: 'center',});
    }
}

In XML File:
 
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>52.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
    </targets>
</LightningComponentBundle>

Thanks,
Maharajan.C
afarafar 
Fairly new to Salesforce and am trying to wrap my head around the system as well as importing data. I don't understand why fields don't match up between accounts and contacts. E.g. contact has a field for "mobile" while account has "phone" and "phone other" and no field for "mobile" or "cell". Also contact has "street 1", "street 2" address while account has "mailing address" and "shipping address". Why don't the fields match up between these two objects? And would it make sense to have them match?

Thanks. 
Best Answer chosen by afar
Maharajan CMaharajan C
Hi,

1. Account and Contact are different Objects in Salesforce... So both the objects had it's own set of fields to capture the data...

2. Account, Contact are Standard Salesforce Objects which will come by default in all Salesforce orgs... In Salesforce we have lookup relationship between Account and Contact... Here Account is Parent and Contact is Child...

3. In business wise Account objects are used for Storing the Company's details ( which is an organization or person involved with your business ) ... And Contact object stores the person informations who associated with an particular account...

4. Account Phone,phone other,Shipping,mailing address  will store particular company related details. Eg (Amazon,  MRF )

5. Contact  street 1 , street 2, Mobile fields are used to store the particular person details who is working under the account.

https://trailhead.salesforce.com/content/learn/modules/accounts_contacts_lightning_experience

Thanks,
Maharajan.C
alxhdezalxhdez 
Class Controller:
public with sharing class controllerquoteVisual {
    
    public String title {get;set;}
    public Quote quote {get;set;}
    public List<quoteLineItem> qliList {get;set;}
    //public List<Product2> products {get; set;}
    
    //Capturar Nombre de familia y lista de productos
    public Map<String,List<ObjectWrapper>> mapFamily {get;set;}
    //public Map<String,List<QuoteLineItem>> mapFamilyqli {get;set;}
    //Captutar Nombre de familia y recuento de Productos que se mostrarán
    public Map<String, Integer> FamilyCountMap{get;set;}
	//Capturar Lista de nombres de familia de productos 
    public List<String> FamilyList {get;set;}
    //ProductList o products ya con los productos que se requieren mostrar
    
    public controllerquoteVisual(){
        
        Id id = ApexPages.currentPage().getParameters().get('id');
        
        //Obtener quote mediante el Id que arroja la visualforce desde quote
        quote = [SELECT Id, Name, TotalPrice, GrandTotal, ExpirationDate, AccountId, Total_Hours__c, OpportunityId, QuoteNumber   
                FROM quote WHERE Id=:id LIMIT 1];
        
        Opportunity opportunity = [SELECT Name FROM Opportunity WHERE Id=:quote.OpportunityId LIMIT 1];
        
        title = opportunity.Name.split('\\|')[0] + opportunity.Name.split('\\|')[1];
        
        Apexpages.currentPage().getHeaders().put('content-disposition', 'inline; filename='+title+ ' - ' +quote.QuoteNumber );
        
        //Obtener lista de quoteLineItems de la actual quote
        qliList = [SELECT Id, Product2Id, Quantity  
                    FROM quoteLineItem WHERE quoteId=:Id];
       
        ///////////////////////////////////////////////
        //Separar productos por tipo de familia 
        mapFamily = new Map<String, List<ObjectWrapper>>(); 
        FamilyCountMap = new Map<String, Integer>();
        FamilyList = new List<String>();
        
        List<quoteLineItem> finalqliList = new List<QuoteLineItem>();
        finalqliList = qliList;
        
        //Agrupar por nombre de familia y preparar los map
        for(QuoteLineItem qq: finalqliList){
            Product2 famObj = [SELECT Id, Name, Family, Description
                   FROM Product2 WHERE Id=:qq.Product2Id];
            if(famObj.Family == null){
                famObj.Family= 'Sin clasificación';
            }
            
            //List<QuoteLineItem> qq = qliList;
            List<ObjectWrapper> proList = new List<ObjectWrapper>();
            //Verificar si el map ya contiene el mismo nombre por familia
            if(mapFamily.containsKey(famObj.Family)){
            	//Recuperar lista de productos existentes
            	proList = mapFamily.get(famObj.Family);
            	//Meter el nuevo producto a la lista
                proList.add(new ObjectWrapper(qq, famObj));
                    
                mapFamily.put(famObj.Family, proList);
                    
                //Almacenar filas por nombre de familia
                FamilyCountMap.put(famObj.Family, proList.size());
            }
            else{
            	//crear nuevo map del nombre de familia //.fammily,name,etc
                //proList.add(famObj);
                proList.add(new ObjectWrapper(qq, famObj));
                mapFamily.put(famObj.Family, proList); 
                    
                //Almacenar filas por nombre de familia
                FamilyCountMap.put(famObj.Family, proList.size());
            }
        	FamilyList = new List<String>(mapFamily.keySet());
    	}
        
    }
    
    public class ObjectWrapper{
        //Campos QuoteLineItems
        //Public Id QliId{get;set;}
        Public Decimal Quantity{get;set;}
        
        //Campos Producto
        Public String Name{get;set;}
        Public String Family{get;set;}
        Public String Description{get;set;}
        
        public ObjectWrapper(QuoteLineItem ql, Product2 pr){
            //this.QliId = ql.Id;
            this.Quantity = ql.Quantity;
            
            this.Name = pr.Name;
            this.Family = pr.Family;
            this.Description = pr.Description;
        }  
    }
}

Test Class (68%, wrapper class is missing)
@isTest
public class TestControllerquoteVisual{
	@isTest
    static void testControllerVFP(){
        
        Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book', 
                                       Description = 'Price Book Products', IsActive = true );
        insert pb;
        
        Product2 prod = new Product2(Name = 'Test Product', Description = 'Descripción Test', 
                                     Family = 'Salesforce Service Cloud', IsActive = true);
        insert prod;
        
        List<Pricebook2> standardPbList = [select id, name, isActive from Pricebook2 
                                           where IsStandard = true ];
 
    	List<PricebookEntry> listPriceBook = new List<PricebookEntry>();
     	for(Pricebook2 p : standardPbList ){
      		PricebookEntry pbe = New PricebookEntry ();
      		pbe = new PricebookEntry(Pricebook2Id = p.Id, Product2Id = prod.Id, 
                                     UnitPrice = 10000, IsActive = true, UseStandardPrice = false);
       		listPriceBook.add(pbe);
     	}
        insert listPriceBook;
        
        Opportunity opp = new Opportunity(Name = 'Test Opportunity', 
                                          StageName = 'Discovery', Product_Families__c='Salesforce Service Cloud' ,CloseDate = system.today());
        insert opp;
        
        Quote quttest = new Quote (Name = 'Quote Test' , OpportunityId = opp.id , Pricebook2Id = pb.id );
        insert quttest;
        
        List<QuoteLineItem> listval = new List<QuoteLineItem>();
        for(PricebookEntry pricebook : listPriceBook){
            QuoteLineItem qutlineitemtest = new QuoteLineItem ();
            qutlineitemtest = new QuoteLineItem(QuoteId = quttest.id, Quantity = 3.00,UnitPrice = 12, PricebookEntryId = pricebook.id);
            
            listval.add(qutlineitemtest);
        }
        insert listval;    
            
        QuoteLineItem qlitem = new QuoteLineItem(Quantity = 3.00, UnitPrice = 12 );
        
        
        Test.startTest();
            ApexPages.currentPage().getParameters().put('id', String.valueOf(quttest.Id));
            controllerquoteVisual controllerVfp= new controllerquoteVisual();
            //controllerVfp = new controllerquoteVisual();
        
            controllerquoteVisual.ObjectWrapper wrapper = new controllerquoteVisual.ObjectWrapper(qlitem, prod);
            
            wrapper.Quantity = 3.00;
                
            wrapper.Name = prod.Name;
            wrapper.Description = prod.Description;
            wrapper.Family = prod.Family;
        
        Test.stopTest();
        
    }

}

 
Best Answer chosen by alxhdez
Maharajan CMaharajan C
Hi alxhdez,

Please try the below test class you will get 97%:
 
@isTest
public class TestControllerquoteVisual{
    @isTest
    static void testControllerVFP(){
        
        Pricebook2 pb = new Pricebook2(Name = 'Standard Price Book', 
                                       Description = 'Price Book Products', IsActive = true );
        insert pb;
        
        Product2 prod = new Product2(Name = 'Test Product', Description = 'Descripción Test', 
                                     Family = 'Salesforce Service Cloud', IsActive = true);
        insert prod;
        
        List<Pricebook2> standardPbList = [select id, name, isActive from Pricebook2 
                                           where IsStandard = true ];
        
        Id standardPBId = Test.getStandardPricebookId();
        
        PricebookEntry pbe = New PricebookEntry (Pricebook2Id = standardPBId, Product2Id = prod.Id, 
                                                 UnitPrice = 10000, IsActive = true, UseStandardPrice = false);
        insert pbe;
        
        Opportunity opp = new Opportunity(Name = 'Test \\| Opportunity', 
                                          StageName = 'Discovery' ,CloseDate = system.today());
        insert opp;
        
        Quote quttest = new Quote (Name = 'Quote Test' , OpportunityId = opp.id , Pricebook2Id = standardPBId );
        insert quttest;
        
        QuoteLineItem qlitem = new QuoteLineItem(QuoteId = quttest.id, Quantity = 3.00,UnitPrice = 12, PricebookEntryId = pbe.id,Product2Id = prod.Id);
        insert qlitem;
        
        QuoteLineItem qlitem1 = new QuoteLineItem(QuoteId = quttest.id, Quantity = 5.00,UnitPrice = 10, PricebookEntryId = pbe.id,Product2Id = prod.Id);
        insert qlitem1;
        
        Test.startTest();
        ApexPages.currentPage().getParameters().put('id', String.valueOf(quttest.Id));
        controllerquoteVisual controllerVfp= new controllerquoteVisual();
        
        controllerquoteVisual.ObjectWrapper wrapper = new controllerquoteVisual.ObjectWrapper(qlitem, prod);
        
        wrapper.Quantity = 3.00;
        
        wrapper.Name = prod.Name;
        wrapper.Description = prod.Description;
        wrapper.Family = prod.Family;
        
        Test.stopTest();
        
    }
    
}

Thanks,
Maharajan.C
Alex Calder 10Alex Calder 10 
I am struggleing understanding how to write test units for this class. 
 
//@RestResource(urlMapping='/Google/*') is used to tell the class that it is a REST resource used for POST GET etc
@RestResource(urlMapping='/Google/*')
global with sharing class GoogleWebHookListener {
    //HttpPost tells the method that it will be a POST method
    @HttpPost
    global static void handlePost() {
        
        //set up varibles for the Lead
        String name;
        String phone;
        String email;
        String postCode;
        String firstName;
        String lastName;
        
        //try to do the JSON deserialization and Lead Creation
        //******EXAMPLE JSON AT BOTTOM*****
        try
        {
            // This gets the body of the webhook makes the body a string and sets the string to the variable 'jsonString'
            String jsonString = RestContext.request.requestBody.toString();
            //This deserializes the JSON based on the the properties setup in the Class GoogleJsonLeadExample
            GoogleJsonLeadExample g = (GoogleJsonLeadExample)JSON.deserialize(jsonString, GoogleJsonLeadExample.class);
            
            //This tests the JSON to see if it is legit.
            if(g.google_key == ''){
                
                //This loops through the JSON array set up in the Class GoogleJsonLeadExample
                
               for(GoogleJsonLeadExample.userData d : g.user_column_data)
               {
                   if(d.column_name == 'Full Name')
                   {
                       //The JSON has Full Name, this breaks Full Name into First and Last Names
                        name = d.string_value;
                        List<String> names = name.split(' ');
                       	firstName = names[0];
                        lastName = names[1];
                           
                   }
                   if(d.column_name == 'User Phone')
                   {
                        phone = d.string_value;
                   }
                   if(d.column_name == 'User Email')
                   {
                        email = d.string_value;
                   }
                   if(d.column_name == 'Postal Code')
                   {
                       postCode = d.string_value;
                   }
               }
              
                //This creates the Lead                 
            Lead detail = new Lead();
            detail.LastName = lastName;
            detail.FirstName = firstName;
            detail.Phone = phone;
            detail.Company = name;
            detail.Email = email;
            detail.OwnerId = '0051Q00000GrANL'; // This Id is the user Hubspot.
            insert detail;
           
            }
                                        
        }
        catch (DMLException e)
        {
            System.debug('The following exception has occurred: ' + e.getMessage());
        }
    }

}

 
Best Answer chosen by Alex Calder 10
Maharajan CMaharajan C
Hi Alex,

Please try the below test class:
 
@isTest
public class GoogleWebHookListenerTest {
    static testmethod void doPostTest(){
        RestRequest request = new RestRequest();
        request.requestUri ='/services/apexrest/Google';
        request.httpMethod = 'POST';
        String str = '{'+ '"lead_id": "TeSter",'+'"user_column_data": ['+'{'+'"column_name": "Full Name",'+'"string_value": "FirstName LastName",'+'"column_id": "FULL_NAME"'+'},'+'{'+'"column_name": "User Phone",'+'"string_value": "+16505550123",'+' "column_id": "PHONE_NUMBER"'+'},'+'{'+'"column_name": "User Email",'+'"string_value": "test@example.com",'+'"column_id": "EMAIL"'+'},'+'{'+'"column_name": "Postal Code",'+'"string_value": "94043",'+'"column_id": "POSTAL_CODE"'+'},'+'{'+'"string_value": "Roofing",'+'"column_id": "SERVICE"'+'}'+'],'+'"api_version": "1.0",'+'"form_id": 18698608977,'+'"campaign_id": 991214827,'+'"google_key": "",'+'"is_test": true,'+'"gcl_id": "CcDdEeFfGgHhIiJjKkLl",'+'"adgroup_id": 20000000000,'+'"creative_id": 30000000000'+'}';
        request.requestBody = Blob.valueof(Str);
        
        RestContext.request = request;
        GoogleWebHookListener.handlePost();
    }
}

Thanks,
Maharajan.C
Jacky LeeJacky Lee 
Hi,

Within a visual flow, I'm wondering if I can populate a collection variable using semi-colon separated values (;) in a single input text box. For the use case, I'd like to append an ad-hoc Taskray Checklist to a Taskray Task using only the flow interface, but as the items are ad-hoc, I can't use a template for it.

For example, if I input this in ONE long text field:
Apples;
Bananas;
Carrots;


Can I pass it into a collection variable using a loop so it is [Apples, Bananas, Carrots]?

I have simulated this by using LEFT({!Checklist}, FIND(";", {!Checklist}) - 1) to get the first row and adding it to the collection variable, trimming it from {!Checklist} and doing it again for the second row, etc. This creates the collection var as long as I stick to the number of assignments I've used.

But the problem is I can't figure out how to build it into a loop to handle variable number of items. Is there any way to do this using only flow tools?

Thanks in advance :)




 
Best Answer chosen by Jacky Lee
Jacky LeeJacky Lee
Hey Sebastien,

I actually did end up figuring it out, although it is a bit of a lengthy process. The idea is to loop through your input "list" variable, where each loop you isolate one item and add it to a collection variable, then trimming that item from the input variable so that the next loop will return the second item, and so on. 

It's a very roundabout way of simulating a for/while loop that could get the job done in a few lines of code.

Steps:

1. Create a long text input field (var_items) where you enter the list as above on a screen, separated by ( ; ).
2. Create a regular collection variable of type Number (coll_counter). In an Assignment, add to the collection: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9... Or whatever max length you want the list to be. This is so we can artificially loop through var_items x number of times.
3. Also assign a variable (var_remaining) to hold the full var_items text.
4. Set up a Loop which loops through coll_counterIn the case, it will loop 10 times.
5. Here's the tricky bit. In every iteration of the loop, it will do 3 things in a single Assignment step:
    5.1. Create a variable to hold the current item in the list (var_temp). Set it to equal a formula (form_temp) that isolates all text before the first semi-colon.
LEFT({!var_remaining}, FIND(";", {!var_remaining}) - 1)
    5.2. Append (Add) to another collection variable (coll_items) whatever is in var_temp. This is the final collection you can use elsewhere.
    5.3. Edit the remaining text to remove that item you just pulled out. So when it loops again, step 5.1 pulls out item 2, then item 3, etc...
    You can do this by assigning var_remaining to a formula that cuts the first item off:
RIGHT({!var_remaining}, LEN({!var_remaining}) - FIND(";", {!var_remaining}))


Now you are left with coll_items, which is a collection of all the items you just input (without semicolons).
Here's a picture of the flow's items:
 User-added image


I have no idea if this is the easiest way or most concise way, but it has worked for me so far. There is only one minor issue: If you enter the items on separate lines, the collection will store the linebreaks at the front of each item. So when you actually use the collection, it might display weirdly. I have tried stuff like substituting the carriage return with nothing, but it's not too big a deal.

If you want pics of the steps, or have any recommendations please let me know :)

 

Saroj KushwahaSaroj Kushwaha 
Hi,
the challenge is as follows :

Create a camping component that contains a campingHeader and a campingList component.
1.The campingList component contains an ordered list of camping supplies that include Bug Spray, Bear Repellant, and Goat Food.
2.The campingHeader component contains an H1 heading style with a font size of 18 points and displays 'Camping List'.

so i made a lightening application named "camping.app" having code :

<aura:application >
    <br/><br/><br/>
    <c:campingHeader/>
    <c:campingList/>  
</aura:application>


where lightening component "campingHeader.cmp" having code :

<aura:component >
    <h1> Camping List </h1>
</aura:component>

for I have "campingHeader.css" having code :
.THIS {
}

h1.THIS {
    font-size: 18px;
}

and lightening component "campingList.cmp" having code :

<aura:component >
    <ol>
       <li>Bug Spray</li>
       <li>Bear Repellant</li>
       <li>Goat Food</li>      
    </ol>
</aura:component>

when i preview the application it is working nice; but when checking the challenge it says :
"Challenge Not yet complete... here's what's wrong: 
The 'camping' Lightning Component does not include either the campingHeader or campingList component."

please help me know where I m doing wrong. Thanx waiting for your reply
Best Answer chosen by Saroj Kushwaha
Nayana KNayana K
Instead of camping.app, create camping.component :
<aura:component >
    <br/><br/><br/>
    <c:campingHeader/>
    <c:campingList/>  
</aura:component>