+ Start a Discussion
Roman RauerRoman Rauer 
Hi there :)

i'm currently working on an native Android app for my company and ran into some problems with Salesforce lately.

I hope i can find some help here.

What i want to achieve:
The company has a lot of Accounts in Salesforce with 3 important fields for the app: Name, Business (Workshop or Parts Dealer) and location(latitude, longitude)
I would like to show those Accounts(Workshops/Parts Dealers) as markers on a google map in my Android app based on a radius around the user's current location. So it would be more than sufficient to get the data as JSON or XML(i read about sObjects, which would be nice too)

The app will be freely available on Google Play Store and every user should be able to see all the Workshops/Parts dealers around the world.

The problem i'm facing is that i can't find a way to fetch the data inside my app without authenticating every user with a Salesforce-Login. 
Which API is the best to use in this case?

It would be so awsome if anybody could help me with this problem.

What i tried so far: 
- SalesforceMobileSDK: If i extend SalesForceApplication i always end up with the Salesforce-Login Screen.
It seems that every client has to be authenticated for API-calls to work. I tried using the method "peekUnauthenticatedRestClient", but this method only works on full path URL's(e.g. "https://api.spotify.com/v1/search?q=James%20Brown&type=artist"), which isn't really practically for my Use-case.

- I feel like i read nearly all docs about salesforce api, but can't quite get my head around how to solve this problem, although it seems like to be a pretty common use-case.  

- would a salesforce-apex method which would select all records inside a set radius around the user's location be accessable without authentication?

Thanks for your help in advance!

Roman
Best Answer chosen by Roman Rauer
Santosh Bompally 8Santosh Bompally 8
Follow this -> 
1) Write a Restservice to expose your data. 
@RestResource(urlMapping='/Account')
global with sharing class MyRestResource {

 
    @HttpGet
    global static List<Account> doGet() {
        RestRequest req = RestContext.request;
        RestResponse res = RestContext.response;
       List< Account>  result = [SELECT Id, Name, Business,Location FROM Account ];
        return result;
    }
  
}

2)  Create a Community and provide access to apex class and account object to the Guest user profile. 
    Samplecommunity url -> https://somedomain.cs16.force.com/communityname

3) Go to workbench and test your API using rest explorer 
    relative url ->  /services/apexrest/Account
4) Your public url will now be   https://somedomain.cs16.force.com/communityname/services/apexrest/Account 

Mark as Best Answer if it helps. 

Thanks, 
Santosh 

 
Nevin O'Regan 3Nevin O'Regan 3 
Hi guys,

I've created a custom screen flow and I want to add it to a community page, launched from a custom button on the Homepage. 
The flow guides users through creating a case. I am getting the following error when I add the component to the page: "Action failed: c:CustomCaseCommunityComponent$controller$init [flowData is not defined]"

This is my component. 

<aura:component implements="flexipage:availableForAllPageTypes,flexipage:availableForRecordHome,forceCommunity:availableForAllPageTypes,force:hasRecordId,force:lightningQuickActionWithoutHeader" access="global">

    <aura:handler name="init" value="{!this}" action="{!c.init}"/>
    <lightning:flow aura:id="flowData"/>
    
</aura:component>


({
init : function (component) {
var flow = component.find(flowData);
flow.startFlow(CaseFlow);
},
})
Best Answer chosen by Nevin O'Regan 3
SUCHARITA MONDALSUCHARITA MONDAL
Nevin,
Just update your code as below:
var CaseFlow = component.find("flowData");
flow.startFlow(CaseFlow);  

Thanks,
Sucharita
 
Best Answer chosen by Malgorzata Miaskiewicz
Malgorzata MiaskiewiczMalgorzata Miaskiewicz
We went live with domain change last week and it seems that all the links of Knowledge articles are automatically redirected to the new domain. Every works just fine without any additional action on our side. 
rima khanrima khan 
Hi!
I registered with trailhead.
I’m an SDR looking to expand my Salesforce skill set. Let’s pretend I have none. Particularly looking for the basics around reporting, and any other trails that may be beneficial to spend some time with.
My goal is to have a competent understanding of SF to build my sales as I enter a closing role and the relevant tools in SF that will help me gain an advantage.
Thanks in advance!
Best Answer chosen by rima khan
manasa udupimanasa udupi
Hi Rima,

Below are few trailhead links, hope it helps:)

https://trailhead.salesforce.com/en/content/learn/modules/sales_admin_sales_reports_for_lex
https://trailhead.salesforce.com/en/content/learn/modules/sales-activity-analysis
Paul TolentinoPaul Tolentino 
Do we have any documentation on Javascript support for Channel Menu.  We are looking for  JavaScript documentation for Web snap ins. 
Best Answer chosen by Paul Tolentino
VinayVinay (Salesforce Developers) 
Hi Paul,

I found below related article regarding channel Menu feature.

https://developer.salesforce.com/docs/atlas.en-us.omni_channel_dev.meta/omni_channel_dev/service_presence_console_api_lightning.htm
https://releasenotes.docs.salesforce.com/en-us/spring20/release-notes/rn_embedded_service_channel_menu.htm

Please mark as Best Answer if it was helpful so that it can help others in the future.

Thanks,
Vinay Kumar
Gaurav Srivastava_SDFCGaurav Srivastava_SDFC 
After importing all Contacts, it is still showing this error. I have tried many times but stuck with the same error. In other org it is working as expected but I am facing this issue in my org. Do not know what is happening here!
Best Answer chosen by Gaurav Srivastava_SDFC
PratikPratik (Salesforce Developers) 
Hi Gaurav,

Thanks for the information. I will suggest you to try this in fresh DE org, it might be the existing record causing the issues in your org.

Thanks,
Pratik
Ryan Adams 173Ryan Adams 173 
Please help me resolve this challenge:

https://trailhead.salesforce.com/modules/apex_integration_services/units/apex_integration_soap_callouts

The Challenge is as follows:

Generate an Apex class using WSDL2Apex and write a test class.
Generate an Apex class using WSDL2Apex for a SOAP web service, write unit tests that achieve 100% code coverage for the class using a mock response, and run your Apex tests.

Use WSDL2Apex to generate a class called 'ParkService' in public scope using this WSDL file. After you click the 'Parse WSDL' button don't forget to change the name of the Apex Class Name from 'parksServices' to 'ParkService'.
Create a class called 'ParkLocator' that has a 'country' method that uses the 'ParkService' class and returns an array of available park names for a particular country passed to the web service. Possible country names that can be passed to the web service include Germany, India, Japan and United States.
Create a test class named ParkLocatorTest that uses a mock class called ParkServiceMock to mock the callout response.
The unit tests must cover all lines of code included in the ParkLocator class, resulting in 100% code coverage.
Run your test class at least once (via 'Run All' tests the Developer Console) before attempting to verify this challenge.

The error I receive when checking the challencge is:

Challenge Not yet complete... here's what's wrong:
Executing the 'country' method on 'ParkLocator' failed. Make sure the method exists with the name 'country', is public and static, accepts a String and returns an array of Strings from the web service.

Here is the code I am using:
public class ParkLocator {
    public static String[] country(String ctry) {
        ParkService.ParksImplPort prk = 
            new ParkService.ParksImplPort();
        return prk.byCountry(ctry);
    }
}

and
 
@isTest
global class ParkServiceMock implements WebServiceMock {
   global void doInvoke(
           Object stub,
           Object request,
           Map<String, Object> response,
           String endpoint,
           String soapAction,
           String requestName,
           String responseNS,
           String responseName,
           String responseType) {
        // start - specify the response you want to send
        ParkService.byCountryResponse response_x = 
            new ParkService.byCountryResponse();
            
        List<String> myStrings = new List<String> {'Park1','Park2','Park3'};
    
        response_x.return_x = myStrings;
        // end
        response.put('response_x', response_x); 
   }
}

and
 
@isTest
private class ParkLocatorTest  {
    @isTest static void testCallout() {              
        // This causes a fake response to be generated
        Test.setMock(WebServiceMock.class, new ParkServiceMock());
        // Call the method that invokes a callout
        List<String> result = new List<String>();
        List<String> expectedvalue = new List<String>{'Park1','Park2','Park3'};
        
        result = ParkLocator.country('India');
        // Verify that a fake result is returned
        System.assertEquals(expectedvalue, result); 
    }
}

Any help which can be provided is greatly appreciated.  If you could advise me at raadams173@gmail.com if you reply with a solution, I can log in to check it.

Thanks.

Ryan
Best Answer chosen by Ryan Adams 173
Amit Singh 1Amit Singh 1
Hello Ryan,

Use below code for ParkLocator class.
public class ParkLocator {
    public static String[] country(String country){
        ParkService.ParksImplPort parks = new ParkService.ParksImplPort();
        String[] parksname = parks.byCountry(country);
        return parksname;
    }
}
If this not resolves the problem then use a new Developer Org for completing the Challenge.

Let me know if this helps :)

 
Siva SakthiSiva Sakthi 
Hi, I am new to LWC ​​​​​​, trying dynamically add/remove rows for a table scenario. I can able to add the row by click on the + button. After added that rows I want to enter some values in text boxes and save into account object by click on save buttton. Also do the delete action as well to delete the specific row by click on delete button. 

I am facing issue with save & delete records. How I can solve this? Can anyone give me some guidence to solve.

AddDeleteRow
dynamicAddRow.html

<template>
                  
    <div class="slds-m-around--xx-large">
        <div class="slds-float_right slds-p-bottom_small">
            <h1 class="slds-page-header__title">Add Row
                <lightning-button-icon icon-name="utility:add"  size="large" variant="bare" alternative-text="Add" onclick={addRow}> </lightning-button-icon>
            </h1>
        </div>
        <div class="container-fluid">        
            <table class="slds-table slds-table_bordered slds-table_cell-buffer"> 
                <thead>
                    <tr class="slds-text-title_caps">
                        <th scope="col">
                            <div class="slds-truncate">#</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Account Name">Account Name</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Account Number">Account Number</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Phone">Phone</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Action">Action</div>
                        </th>
                    </tr>
                </thead>   
                <tbody>      
                    
                    <template for:each={accountList} for:item="acc" for:index="index">
                        <tr key={acc.Id}> 
                            <td>{index}</td>                                                  
                            <td>
                                <lightning-input label="Name" value={acc.Name} onchange={handleNameChange}></lightning-input>                               
                            </td>
                            <td>
                                <lightning-input label="Account Number" value={acc.AccountNumber} onchange={handleAccountNumberChange}></lightning-input>                        
                            </td>
                            <td>
                                <lightning-input label="Phone" value={acc.Phone} onchange={handlePhoneChange}></lightning-input>
                            </td>
                            <td>
                                <a onclick={removeRow}> 
                                    <lightning-icon icon-name="utility:delete" size="small" style="margin-top: -4px; margin-right: 0px;" ></lightning-icon>
                                    <span class="slds-assistive-text">Delete</span>
                                </a>
                            </td> 
                        </tr>
                    </template>
                     
                </tbody>
            </table>
            <div class="slds-align_absolute-center slds-p-top_small">                
                <lightning-button name="Save" label="Save" onclick={saveRecord} ></lightning-button>
            </div>
        </div>
    </div>

</template>
 
dynamicAddRow.js

import { LightningElement, track,api } from 'lwc';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import ACCOUNTNUMBER_FIELD from '@salesforce/schema/Account.AccountNumber';
import PHONE_FIELD from '@salesforce/schema/Account.Phone';
import saveAccounts from '@salesforce/apex/AccountController.saveAccounts';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class CreateDynamicRecord extends LightningElement {
    @track accountList = []; 
    @track index = 0;
    @api recordId;
    @track name = NAME_FIELD;
    @track industry = ACCOUNTNUMBER_FIELD;
    @track phone = PHONE_FIELD;

    @api record = {
        firstName : '',
        lastName : '',
        Email : '',
        Phone : '',
        Title : ''
    }

    addRow(){

        this.index++;
   
        this.accountList.push ({
            sobjectType: 'Account',
            Name: '',
            AccountNumber : '',
            Phone: ''
        });

        console.log('Enter ',this.accountList);
        
       // this.accountList.push(this.record);
        //console.log(' After adding Record List ', this.accountList);
    }
    
    removeRow(){

        var index = this.index;
      
        if(this.accountList.length>1)
           this.accountList.splice(index, 1);

        //this.dispatchEvent(new CustomEvent('deleterow', {detail: this.index}));
        //console.log(' After adding Record List ', this.dispatchEvent);
    } 

    acc = {
        Name : this.name,
        AccountNumber : this.accNumber,
        Phone : this.phone
    }

    handleNameChange(event) {
        this.acc.Name = event.target.value;
        console.log("name", this.acc.Name);
    }
    
    handleAccountNumberChange(event) {
        this.acc.AccountNumber = event.target.value;
        console.log("AccountNumber", this.acc.AccountNumber);
    }
    
    handlePhoneChange(event) {
        this.acc.Phone = event.target.value;
        console.log("Phone", this.acc.Phone);
    }
    
    saveRecord(){        
        saveAccounts(this.acc.accountList)
            .then(result => {
                this.message = result;
                this.error = undefined;
                if(this.message !== undefined) {
                    this.acc.Name = '';
                    this.acc.AccountNumber = '';
                    this.acc.Phone = '';
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Success',
                            message: 'Account created successfully',
                            variant: 'success',
                        }),
                    );
                }
                
                console.log(JSON.stringify(result));
                console.log("result", this.message);
                /*console.log(' After adding Record List ', result);
                this.accountList = result;
                console.log(' After adding Record List ', this.accountList);*/
            })
            .catch(error => {
                this.message = undefined;
                this.error = error;
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error creating record',
                        message: error.body.message,
                        variant: 'error',
                    }),
                );
                console.log("error", JSON.stringify(this.error));
            });
    }
      
}
 
AccountController.apex  

public with sharing class AccountController { 
 
    @AuraEnabled( cacheable = true ) 
    public static List< Account > getAccounts() { 
      
        return [ SELECT Id, Name, Industry FROM Account LIMIT 10 ]; 
         
    } 
     
    @AuraEnabled( cacheable = true )
    public static void saveAccounts(List<Account> accList){
        Insert accList;
        /*if(accList.size()>0 && accList != null){
            insert accList;
        }*/
    } 
}
Thanks 
Siva
 
Best Answer chosen by Siva Sakthi
Danish HodaDanish Hoda
Hi Siva,
Please refer below code:
 
html:

<template>
    <div if:true={isLoaded} class="cstm-spinner">
		<lightning-spinner alternative-text="Loading..."></lightning-spinner>
	</div>
                  
    <div class="slds-m-around--xx-large container-fluid">
        <div class="slds-float_right slds-p-bottom_small">
            <h1 class="slds-page-header__title">Add Row
                <lightning-button-icon icon-name="utility:add"  size="large" variant="bare" alternative-text="Add" onclick={addRow}> </lightning-button-icon>
            </h1>
        </div>
        <div class="container-fluid">        
            <table class="slds-table slds-table_bordered slds-table_cell-buffer"> 
                <thead>
                    <tr class="slds-text-title_caps">
                        <th scope="col">
                            <div class="slds-truncate">#</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Account Name">Account Name</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Account Number">Account Number</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Phone">Phone</div>
                        </th>
                        <th scope="col">
                            <div class="slds-truncate" title="Action">Action</div>
                        </th>
                    </tr>
                </thead>   
                <tbody>      
                    
                    <template for:each={accountList} for:item="acc" for:index="indx">
                        <tr key={acc.key} id={acc.key}> 
                            <td>{indx}</td>                                                  
                            <td>
                                <lightning-input data-id={indx} label="Name" value={acc.Name} onchange={handleNameChange}></lightning-input>                               
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Account Number" value={acc.AccountNumber} onchange={handleAccountNumberChange}></lightning-input>                        
                            </td>
                            <td>
                                <lightning-input data-id={indx} label="Phone" value={acc.Phone} onchange={handlePhoneChange}></lightning-input>
                            </td>
                            <td>
                                <lightning-button-icon icon-name="utility:delete"
																		  data-id={indx}       
																		  alternative-text="Delete"     
																		  class="slds-m-left_xx-small"
																		  onclick={removeRow} 
																		  title="Delete"></lightning-button-icon>
                            </td>
                        </tr>
                    </template>
                     
                </tbody>
            </table>
            <div class="slds-align_absolute-center slds-p-top_small">                
                <lightning-button name="Save" label="Save" onclick={saveRecord} ></lightning-button>
            </div>
        </div>
    </div>

</template>
 
js :

import { LightningElement, track,api } from 'lwc';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
import ACCOUNTNUMBER_FIELD from '@salesforce/schema/Account.AccountNumber';
import PHONE_FIELD from '@salesforce/schema/Account.Phone';
import saveAccounts from '@salesforce/apex/AccountController.saveAccounts';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
export default class CreateDynamicRecord extends LightningElement {
    @track accountList = []; 
    @track index = 0;
    @api recordId;
    @track name = NAME_FIELD;
    @track accNumber = ACCOUNTNUMBER_FIELD;
    @track phone = PHONE_FIELD;
    isLoaded = false;

    @api record = {
        firstName : '',
        lastName : '',
        Email : '',
        Phone : '',
        Title : ''
    }

    acc = {
        Name : this.name,
        AccountNumber : this.accNumber,
        Phone : this.phone ? this.phone : "",
        key : ''
    }

    addRow(){

        this.index++;
        //var i = JSON.parse(JSON.stringify(this.index));
        var i = this.index;
   
        /*this.accountList.push ({
            sobjectType: 'Account',
            Name: '',
            AccountNumber : '',
            Phone: '',
            key : i
        });*/
        this.acc.key = i;
        this.accountList.push(JSON.parse(JSON.stringify(this.acc)));

        console.log('Enter ',this.accountList);
        
       // this.accountList.push(this.record);
        //console.log(' After adding Record List ', this.accountList);
    }
    
    removeRow(event){
        this.isLoaded = true;
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        if(this.accountList.length>1){
            this.accountList.splice(key, 1);
            this.index--;
            this.isLoaded = false;
        }else if(this.accountList.length == 1){
            this.accountList = [];
            this.index = 0;
            this.isLoaded = false;
        }

        //this.dispatchEvent(new CustomEvent('deleterow', {detail: this.index}));
        //console.log(' After adding Record List ', this.dispatchEvent);
    } 

    

    handleNameChange(event) {
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].Name = event.target.value;
        //this.acc.Name = event.target.value;
        //console.log("name", this.acc.Name);
    }
    
    handleAccountNumberChange(event) {
        /*this.acc.AccountNumber = event.target.value;
        console.log("AccountNumber", this.acc.AccountNumber);*/
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].AccountNumber = event.target.value;
    }
    
    handlePhoneChange(event) {
        /*this.acc.Phone = event.target.value;
        console.log("Phone", this.acc.Phone);*/
        var selectedRow = event.currentTarget;
        var key = selectedRow.dataset.id;
        var accountVar = this.accountList[key];
        this.accountList[key].Phone = event.target.value;
    }
    
    saveRecord(){        
        saveAccounts({accList : this.accountList})
            .then(result => {
                this.message = result;
                this.error = undefined;
                if(this.message !== undefined) {
                    this.acc.Name = '';
                    this.acc.AccountNumber = '';
                    this.acc.Phone = '';
                    this.dispatchEvent(
                        new ShowToastEvent({
                            title: 'Success',
                            message: 'Account created successfully',
                            variant: 'success',
                        }),
                    );
                }
                
                console.log(JSON.stringify(result));
                console.log("result", this.message);
                /*console.log(' After adding Record List ', result);
                this.accountList = result;
                console.log(' After adding Record List ', this.accountList);*/
            })
            .catch(error => {
                this.message = undefined;
                this.error = error;
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Error creating record',
                        message: error.body.message,
                        variant: 'error',
                    }),
                );
                console.log("error", JSON.stringify(this.error));
            });
    }
      
}
 
AccountController.apex  

public with sharing class AccountController { 
 
    @AuraEnabled( cacheable = true ) 
    public static List< Account > getAccounts() { 
      
        return [ SELECT Id, Name, Industry FROM Account LIMIT 10 ]; 
         
    } 
     
//( cacheable = true ) doesn't support DML operations
    @AuraEnabled
    public static void saveAccounts(List<Account> accList){
        Insert accList;
        /*if(accList.size()>0 && accList != null){
            insert accList;
        }*/
    } 
}

 
Manjunath S 85Manjunath S 85 

Hi Trailblazers,

I am stuck in Step 3 of the Advanced Apex Specialist badge where I'm getting this error:

"Ensure that the getFamilyOptions method returns the correct picklist values."

I have only the 4 picklist values: 
User-added image

The getter method for the FamilyOptions property is defined here:

public static List<SelectOption> GetFamilyOptions(){
        List<SelectOption> familySelections = new List<SelectOption>();
        familySelections.add(new SelectOption(Constants.SELECT_ONE,Constants.SELECT_ONE));
        for(Schema.PicklistEntry ple : Constants.PRODUCT_FAMILY){
            familySelections.add(new SelectOption(ple.getLabel(),ple.getValue()));
        }
        system.debug('>>>>> familySelections >>>>>>>> '+familySelections);
        return familySelections;
    }

Can somebody please help me in figuring out what I am doing wrong here?
Best Answer chosen by Manjunath S 85
Manjunath S 85Manjunath S 85
Hi,

I had mistakenly marked the method static. Making it a instance method worked and I'm now able to proceed.
Sharmeela ManivannanSharmeela Manivannan 
I am hit with other issue 'Ensure that page performance is improved by only rerendering the table and error messages when a use clicks the Add button.' I have used actionRegion to rerender the needed table,chart and messages.still it shows issues.
Best Answer chosen by Sharmeela Manivannan
Zachery EngmanZachery Engman
You have to be sure the Add button is only re-rendering the two items, messages and table:  reRender="pageMessages,orderItemTable"  and that the Save button is only reRendering the chart:  reRender="inventoryChart"  (assuming that is the id you gave it)