• Robin Barnwell
  • NEWBIE
  • 39 Points
  • Member since 2014

  • Chatter
    Feed
  • 0
    Best Answers
  • 2
    Likes Received
  • 0
    Likes Given
  • 21
    Questions
  • 37
    Replies
I've got the following lines of code that work just fine when boatId has an Id value (string)
sendMessageService(boatId) { 
    // explicitly pass boatId to the parameter recordId

    const payload = { recordId: boatId };
    publish(this.messageContext, BOATMC, payload);

  }
But when I set boatId to nothing the publish fires but the subscriber never gets the message.  What am I doing wrong, how do I publish a null boatId?
 
So there is a problem that I cannot see how to resolve.  I've worked out most of the elements that I need to do the Churn Rate calculation but there is an error.  Here is the code
 
-- Get the current and previous Qtr Subscriptions
q = load "Beattie_Subs";
Subs = group q by (Subscription_Date_Year, Subscription_Date_Quarter);
Subs = foreach Subs generate Subscription_Date_Year, Subscription_Date_Quarter, count() as CurrQtrSubs, sum(count()) over([-1 .. -1] partition by all order by Subscription_Date_Year) as PrevQtrSubs;

-- Fill the Subs gaps
Subs = fill Subs by (dateCols=(Subscription_Date_Year, Subscription_Date_Quarter, "Y-Q"));

-- Get the current and previous Qtr Cancellations
q = load "Beattie_Subs";
Canc = group q by (Churn_Date_Year, Churn_Date_Quarter);
Canc = foreach Canc generate Churn_Date_Year, Churn_Date_Quarter, count() as CurrQtrCanc, sum(count()) over([-1 .. -1] partition by all order by Churn_Date_Year) as PrevQtrCanc;

-- Fill the Canc gaps
Canc = fill Canc by (dateCols=(Churn_Date_Year, Churn_Date_Quarter, "Y-Q"));

-- Combine them
result = cogroup Subs by ('Subscription_Date_Year', 'Subscription_Date_Quarter') full, Canc by ('Churn_Date_Year', 'Churn_Date_Quarter');

-- Calc churn (to be completed)
result = foreach result generate Subs['Subscription_Date_Year'] as XX, Subs['Subscription_Date_Quarter'] as YY, 
coalesce(sum(Subs[CurrQtrSubs]),0) as 'CurrQtrSubs', 
coalesce(sum(Subs[PrevQtrSubs]),0) as 'PrevQtrSubs', 
coalesce(sum(Canc[CurrQtrCanc]),0) as 'CurrQtrCanc', 
coalesce(sum(Canc[PrevQtrCanc]),0) as 'PrevQtrCanc';
The reasons I can't complete is a problem with the Fill command.  When this is run you get a table that looks like this.  The Previous Quarter is being brought down to the Current Quarter fine.  But where an extra Quarter has been brought in by the Fill it is being skipped.

Step 1

SAQL is not well documented and there are few examples.  How should I structure the logic to work?
I recently did PD2 and one of the questions I'm stuck on.  If you had a large data set that you wanted the user to be able to filter which is most efficent:
1. Rerun the SOQL each time with the new data filters
2. Hold the whole data set in memory and use Apex to filter
3. Use visualforce tags to filter - can't remember which ones

I went for hold in memory and use apex.  Can't find any documentation on best approach for performance.  any help?
I'm getting the following error on Advanced Apex Specialist Superbadge - Step 5.  Any ideas please?
User-added image

Here is the current code segments:

VerifyQuantityOrdered
Public static void VerifyQuantityOrdered(Product2 originalProduct, Product2 updatedProduct, Integer qtyOrdered) {
       	decimal tot = (originalProduct.Quantity_Ordered__c + qtyOrdered);
        
            system.debug('OldVQ ' + originalProduct.Quantity_Ordered__c);
            system.debug('NewVQ ' + updatedProduct.Quantity_Ordered__c);
            system.debug('QTYVQ ' + qtyOrdered);
        
        system.assertEquals(updatedProduct.Quantity_Ordered__c,tot);     
    }
OrderTests
@isTest
private with sharing class OrderTests {

    @testSetup 
    private static void SetupTestData (){    
    	TestDataFactory.InsertTestData(5);   
    } 

    @isTest
    private static void OrderUpdate_UnitTest (){
        Test.startTest();
    	
        List<Order> OrderList = [select id, name, status from order];

        For (Order ordrec : OrderList) {
            OrderItem oirec = [select id, Pricebookentry.product2Id from orderitem where orderid=:ordrec.id];
			Product2 oldprodrec = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c 
                            	FROM Product2 where id =: oirec.Pricebookentry.product2Id  limit 1];
            ordrec.status = constants.ACTIVATED_ORDER_STATUS;
            update ordrec;
            OrderItem oirec1 = [select id, Pricebookentry.product2Id from orderitem where orderid=:ordrec.id];
			Product2 newprodrec = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c 
                            	FROM Product2 where id =: oirec1.Pricebookentry.product2Id  limit 1]; 
            system.debug('Old ' + oldprodrec.Quantity_Ordered__c);
            system.debug('New ' + newprodrec.Quantity_Ordered__c);
            system.debug('QTY ' + constants.DEFAULT_ROWS);
            TestDataFactory.VerifyQuantityOrdered(oldprodrec,newprodrec,constants.DEFAULT_ROWS);
        }
        Test.stopTest();          
    }
}
Product2Tests
@isTest  (seeAllData=false)
private with sharing class Product2Tests {

    /**
     * @name product2Extension_UnitTest
     * @description UnitTest for product2Extension
    **/
    @isTest
    private static void Product2Extension_UnitTest(){
// Set-up user
        String uniqueUserName = 'standarduser' + DateTime.now().getTime() + '@testorg.com';
        Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
        User u = new User(Alias = 'standt', Email='standarduser@testorg.com',
        	EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
        	LocaleSidKey='en_US', ProfileId = p.Id, TimeZoneSidKey='America/Los_Angeles', UserName=uniqueUserName);

        System.runAs(u) {
        
// When a user first visits the page, there should be multiple rows displayed on the screen. 
// Assert that the size of the productsToInsert list is equal to the DEFAULT_ROWS constant.
//		Test.StartTest(); 
        	Product2 prod = new Product2(name='Test',isActive=true);
        	ApexPages.StandardController stdc = new ApexPages.StandardController(prod);
        	Product2Extension p2x = new Product2Extension(stdc);        
       		System.assertEquals(Constants.DEFAULT_ROWS, p2x.productsToInsert.size());
//		Test.StopTest();  

// When the Add button is clicked, an additional set of rows should be added, 
// so assert that the size of the productsToInsert ** list is double **DEFAULT_ROWS after the button is clicked once. 
//        Test.StartTest();
        	p2x.addRows();
        	System.assertEquals(Constants.DEFAULT_ROWS * 2, p2x.productsToInsert.size());
//		Test.StopTest();          

// Next, test the Save button. Verify that populated rows are saved and unpopulated rows are not saved. 
// Loop through the rows in the productsToInsert list and populate the values of the first 5 records, 
// and then simulate clicking the Save button. Verify that the button worked by asserting that only 5 products were saved.
		integer x = 0; 
        for (Product2Extension.ProductWrapper PTI : p2x.productsToInsert){
            pti.productrecord.name='TESTPRODUCT ' + x;
            pti.productRecord.IsActive = true;
            pti.productRecord.Initial_Inventory__c = 20;
            pti.productRecord.Family = Constants.PRODUCT_FAMILY[0].getValue();           
            pti.pricebookEntryRecord.UnitPrice = 10;
            
        	x++; if (x==5) {break;}
        }

//        Test.startTest();
        	p2x.save();
//        Test.stopTest();
        List<Product2> createdProducts = [SELECT Id FROM Product2];
        System.assertEquals(5, createdProducts.size());

// plus some more test areas        
        p2x.GetFamilyOptions();
        p2x.GetInventory();

    }
    }
}


 
I'm working on Step 9 at the moment and my code passed the Step 7 check, but I noticed something very odd.  Here is the problem:

1. I can add an intial review and the code does as requested and shows the review 
Add Review  Show Boat

Behind the scenes a record is added to the BoatReview__C object
User-added image

2. The problem occurs on adding a second review.  No matter if I change boats or add a review to the existing boat, all subsequent inserts actually update the original review.
Add second review 

Behind the scenes the Insert has actually done an Update on the original record
User-added image

If I change boats and add a review record, the original boat review is updated and the Id and Boat Id stay the same.  It is as though I am not Committing the transaction.

Here is the code:
AddBoatReview.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
	<aura:attribute name="boat" type="Boat__c" access="public"/>
    <aura:attribute name="boatReview" type="BoatReview__c" access="private"
                    default="{'sobjectType':'BoatReview__c', 'Name':'', 'Comment__c':''}"/>    
	<aura:attribute name="boatReviewRecord" type="Object" access="public"/>
    <aura:attribute name="recordError" type="String" access="private"/>  

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
	<aura:registerEvent name="BoatReviewAdded" type="c:BoatReviewAdded"/>    
    
	<force:recordData aura:id="service"
    	layoutType="FULL"  
		fields="Id, Name, Comment__c, Boat__c"
		targetRecord="{!v.boatReviewRecord}"
		targetFields="{!v.boatReview}"
		targetError="{!v.recordError}"
        recordUpdated="{!c.onRecordUpdated}" />    
    
    <lightning:layout multipleRows="true">
		<lightning:layoutItem size="12" padding="around-small">
			<lightning:input name="title" label="Title" value="{!v.boatReview.Name}"/>
		</lightning:layoutItem>
		<lightning:layoutItem size="12" padding="around-small">
			<label class="slds-form-element__label" for="input-id-01">Comment</label>
			<lightning:inputRichText value="{!v.boatReview.Comment__c}" disabledCategories="FORMAT_FONT"/>
		</lightning:layoutItem>
        <lightning:layoutItem size="12" class="slds-align--absolute-center">
			<lightning:button iconName="utility:save" label="Submit" onclick="{!c.onSave}"/>
		</lightning:layoutItem>
	</lightning:layout> 
    
</aura:component>

AddBoatReviewController.js
({  
    doInit : function(component, event, helper) {       
		helper.onInit(component, event);
    },
   
    onSave : function(component, event, helper) {
//		component.set("v.boatReview.Boat__c",component.get("v.boat.Id"));
			
        component.find("service").saveRecord(function(saveResult) {
           console.log("Add Review status" + saveResult.state)
        		if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") { 
        			var resultsToast = $A.get("e.force:showToast");
                    if(resultsToast != undefined) {    
                        resultsToast.setParams({
                        "title": "Saved",
                        "message": "Boat Review Created"
                        });
						resultsToast.fire(); 
                    } else {
                        alert('Boat Review Created');
                    }
				} else if (saveResult.state === "INCOMPLETE") {
                	console.log("User is offline, device doesn't support drafts.");
        		} else if (saveResult.state === "ERROR") {
            		console.log('Problem saving contact, error: ' +JSON.stringify(saveResult.error));
            	} else {
            		console.log('Unknown problem, state: ' + saveResult.state + ',error: ' + JSON.stringify(saveResult.error));
            	}                
			}
        ); 
		var BoatReviewAdded=component.getEvent("BoatReviewAdded");
        BoatReviewAdded.setParams ({"BoatId" : "ADDED"});
		BoatReviewAdded.fire();
		
        helper.onInit(component,event);   
	},        
    onRecordUpdated : function(component, event, helper) {
        var eventParams = event.getParams();
        if(eventParams.changeType === "CHANGED") {
            var changedFields = eventParams.changedFields;
            var saveResultsToast = $A.get("e.force:showToast");
                if(saveResultsToast!='undefined')
                {
                    saveResultsToast.setParams({
                        "title": "Saved",
                        "message": "Boat Review Saved"
                    });
                    saveResultsToast.fire(); 
                }
                else
                {
                    alert('Boat Review Saved');
                }
        }
    }
})

AddBoatReviewHelper.js
({
	onInit : function(component,event) {
        var boat=component.get("v.boat"); 
//        console.log("We have got the AddReview INIT OK" + boat.Id);  
        component.find("service").getNewRecord("BoatReview__c", // sObject type (entityApiName)
            	null, // recordTypeId
            	false, // skip cache?
            $A.getCallback(function() {
           		var rec = component.get("v.boatReview");
                   var error = component.get("v.recordError");                 
				if(error || (rec === null)) {
//            		console.log("Error initializing record template: " + error);
         		} else {
//                    console.log("We have initialised the AddReview OK" + boat.Id);                 
					component.set("v.boatReview.Boat__c",boat.Id);
         		}
			})
		);   
    }
})

I can complete the SuperBadge becuase the code passed the checker, but be good to know what I got wrong......thanks 


 
I have two Salesforce Orgs.  One is the Identity & Service Provider, the other just the Service Provider.

I can get SAML working between the Orgs.  But setting-up the combined Identity & Service Provider Org fails everytime.

I think this might be an "undocumented feature" and you can't connect them???
I just can't work this out.  I've read and re-read the Salesforce documentation.  All I want to do is connect my Community to the internal Identity Provider.

1. I have a Salesforce Org with a Community, nothing special - community is active and published, standard template, no customization

2. I set-up My Domain and this automatically creates a SAML idetnity provider for the new domain.  It included a self-signed certificate plus metadata end-points for the Domain and the Community Domain.

3. I set-up both domains as Remote Sites so I can then set them up for SSO

4. I enable Single Sign-on and set-up the Community as per the instructions:  https://developer.salesforce.com/docs/atlas.en-us.sso.meta/sso/sso_examples_sf2sf.htm
To set up a community as a service provider, use the community URL under SAML Metadata Discovery Endpoints on the Identity Provider page. Upload the SAML metadata from this URL. Using the metadata populates the service provider’s SAML SSO settings, including the Login URL that points to the community. When you define a connected app on the identity provider, specify this Login URL as the ACS URL.

5. I set-up the community as a connected app and use the Entity ID and HTTPRedirect URL specified

6. I update the community to enable access to this SSO login.  I get it showing on the login screen, but it doesn't work.  What can I do to debug this??

Community Login Page

SSO Page - no login
If I held the user's credentials in Salesforce then used OAuth rather than SAML to connect applications into Salesforce would I be correct in saying that it would not be possible to navigate across those other applications without having to login again on each?

Hence OAuth does not support single sign-on but is more of a hub & spoke set-up.

Or am I wrong and OAuth can support SSO?

A bit stuck :-(
I'm looking to connect a Salesforce Community with a Sitecore website via the CMS Connect feature.  But have been unable to get this working.  Please can I ask some basic questions:

1. What is a root path and how can I find it for Sitecore?

2. The Header & Footer path indicate a need to link to a "header.html" file.  But the website embeds the header in the Body section via CSS.  How can I put this set-up into CMS Connect

Very stuck.
When I was researching this line of code I managed to level-up in my lightning knowledge, but it opened-up new questions.....
var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {inputCmp.showHelpMessageIfInvalid(); return validSoFar && inputCmp.get('v.validity').valid;}, true);
I've worked out what its doing and how it works - tick.  But what is means in Components must have a define list of available Properties & Methods.

Does anyone know where I can find this list?  Reduce is a Javasript method but IsValid() is Salesforce.  Where can I get a definitive list please?
 
I've set-up a basic community and got it running.  But when I drag components onto the Home Page I get a message such as this: 

Community Home Page

What I'd like to do is put the user details on the home page for editing.  Can this be done?
I'm stuck.  I've created a custom VF page that lists some records. 

I want to include the ListView select option so the user can pick some of them and update them, List this....

User-added image
How do I get the checkbox to work for a cutom VF page??
Is it possible to an a filter in this VF code to limit the Contact to just those where coontact customer field AtRisk__C = True.  Don't want to do any more code.

<apex:page standardController="Contact" recordSetVar="cont">
    <apex:pageBlock title="At-risk Contacts">
        <apex:pageBlockTable value="{!cont}" var="c">
            <apex:column value="{!c.name}"/>
        </apex:pageBlockTable>
    </apex:pageBlock>
</apex:page>
In the Not for Profit Starter Pack there is an option to set the format of the Household Name e.g. $LastName Household.  

I've changed the format to $FirstName $LastName Household and that works fine.

Is there an easy way to update all the existing Households to the new format?
In my VisualForce page, if I have more than one apex:actionSupport event="onchange" then the event doesn't fire until a second input component is accessed.  If there is one then it fires immdiately.  If there anyway to make event fire everytime?

VF Code:
<apex:page controller="TextApex" >
<apex:form >
<apex:pageBlock >

<apex:selectRadio id="slctRd" dir="ltr" required="true" layout="pageDirection" value="{!selectedValue}" immediate="true">
<apex:actionSupport event="onchange" action="{!MethodOne}" reRender="test"/>
<apex:selectOptions id="selRdOptn" value="{!Options}"/>
</apex:selectRadio>

<apex:selectRadio id="slctRd1" dir="ltr" required="true" layout="pageDirection" value="{!selectedValue1}" immediate="true">
<apex:actionSupport event="onchange" action="{!MethodTwo}" reRender="test1"/>
<apex:selectOptions id="selRdOptn1" value="{!Options1}"/>
</apex:selectRadio>

<apex:outputText id="test" value="{!test}"></apex:outputText><br/>
<apex:outputText id="test1" value="{!test1}"></apex:outputText>

</apex:pageBlock>
</apex:form>
</apex:page>

Apex:
public class TextApex {

    public String selectedValue {get; set;}
    public List<SelectOption> Options {get; set;}
    public String test {get;set;}
    
    public String selectedValue1 {get; set;}
    public List<SelectOption> Options1 {get; set;}
    public String test1 {get;set;} 
       
    public TextApex() {
        Options = new List<SelectOption>();
        Options.add(new SelectOption('test1', 'test1'));
        Options.add(new SelectOption('test2', 'test2'));

        Options1 = new List<SelectOption>();
        Options1.add(new SelectOption('test3', 'test3'));
        Options1.add(new SelectOption('test4', 'test4'));
    }
    
   public void MethodOne() { test = 'Hello World' + selectedValue ; }
   public void MethodTwo() { test1 = 'Hello World' + selectedValue1 ; }
}
I've created a VisualForce page that creates a CSV file from an Apex query, code below. 

I now want to run this once per hour to export the data to another system.  I plan to use the Apex scheduler for this, so no problem there.

The bit I'm stuck on is the Apex code that will invoke the Visualforce page to create the file.  Anyone done this before please?

VF
<apex:page controller="CSV_create" action="{!say_hello}" contentType="text/csv#{!now() }">
  <apex:repeat value="{!lstAcc}" var="x">
      {!x.id},{!x.name}
  </apex:repeat>
</apex:page>

APEX
public class CSV_create
{    
    public string header{get;set;}
    public List<TheAcc> lstAcc {get; set;}
    public class TheAcc {
        public string id {get; set;}
        public string name {get; set;}       
    }


    public CSV_create(){
            lstAcc = new List<TheAcc>();
    }

    Public void say_hello()
    {
       string queryString = 'select id, name from account';
       List<Account> lstAccs = DataBase.Query(queryString);       
       if(lstAccs.size()>0){
              for(Account Acc :lstAccs){
                  TheAcc a = new TheAcc();
                  a.id = Acc.id;
                  a.name = Acc.name + '\r\n';                  
                  lstAcc.add(a);               
              } 
        } 
    }
}
I'm trying to access the Search bar in Classic with Selenium but cannot seem to find the object.  I've tried a few variants below, how do I access?

driver.findElement(By.name("str")).sendKeys("Hello");
driver.findElement(By.xpath("//*[@id='phSearchInput']")).sendKeys("Hello");
driver.findElement(By.id("phSearchInput")).sendKeys("Hello");
Midway through this module the following Client Controller code is introduced.  Is the Reduce function documented anywhere else so I can understand it better.  The Trailhead explanation doesn't work for me, very stuck

({
    clickCreate: function(component, event, helper) {
        var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            var newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    }
})


 
We set-up Salesforce as an SSO Identity Server and can get user to login from Wordpress as the Service Provider.

But I can't see how users can self-register on Wordpress and be created in Salesforce.  Plus update their details.

Any pointers would be much appreciated.....
I am loading Mailchimp from Salesforce using the sync tool they provide.  In Mailchimp I have a single list with multiple Interests set-up for each of the various Newsletters we send.

The initital creation of the Mailchimp Subscriber is fine.  They are added to the List and the relevant Interests.  Users could update their Interest preferences in Mailchimp, but I need to send the update from Saleforce instead. But the mailchimp sync doesn't support updates just inserts of Interests.  The Mailchimp support team confirmed this and suggested if I wanted to send updates use the API.

The best solution I have found is to build an Apex Callout that connects to the Mailchimp REST API direct and updates the specifc subscriber via a REST PATCH request.  A bit like this:

PATCH us16.api.mailchimp.com/3.0/Lists/ace5lkhkjh0a0/members/7949a7471b989ihoh5a9d1cd019521c 
{
        "id": "7949a74745345kje53a5a9d1ihjhh19521c",    
        "email_address": "robin@barnwell.com",
        "interests": {  "0dbefbe5e1": true,        
            "4b753026e3": false,        
            "f1a1024d50": true,        
            "27d6bdd94c": false,        
            "bcba00e738": true,       
            "ea22f7c19b": false,        
            "ff7a174d51": true }}

I would invoke this class through Process Builder when the contact record fields change.

Question is, has anyone already done this and please could you share the code?  

Regards Robin
In my VisualForce page, if I have more than one apex:actionSupport event="onchange" then the event doesn't fire until a second input component is accessed.  If there is one then it fires immdiately.  If there anyway to make event fire everytime?

VF Code:
<apex:page controller="TextApex" >
<apex:form >
<apex:pageBlock >

<apex:selectRadio id="slctRd" dir="ltr" required="true" layout="pageDirection" value="{!selectedValue}" immediate="true">
<apex:actionSupport event="onchange" action="{!MethodOne}" reRender="test"/>
<apex:selectOptions id="selRdOptn" value="{!Options}"/>
</apex:selectRadio>

<apex:selectRadio id="slctRd1" dir="ltr" required="true" layout="pageDirection" value="{!selectedValue1}" immediate="true">
<apex:actionSupport event="onchange" action="{!MethodTwo}" reRender="test1"/>
<apex:selectOptions id="selRdOptn1" value="{!Options1}"/>
</apex:selectRadio>

<apex:outputText id="test" value="{!test}"></apex:outputText><br/>
<apex:outputText id="test1" value="{!test1}"></apex:outputText>

</apex:pageBlock>
</apex:form>
</apex:page>

Apex:
public class TextApex {

    public String selectedValue {get; set;}
    public List<SelectOption> Options {get; set;}
    public String test {get;set;}
    
    public String selectedValue1 {get; set;}
    public List<SelectOption> Options1 {get; set;}
    public String test1 {get;set;} 
       
    public TextApex() {
        Options = new List<SelectOption>();
        Options.add(new SelectOption('test1', 'test1'));
        Options.add(new SelectOption('test2', 'test2'));

        Options1 = new List<SelectOption>();
        Options1.add(new SelectOption('test3', 'test3'));
        Options1.add(new SelectOption('test4', 'test4'));
    }
    
   public void MethodOne() { test = 'Hello World' + selectedValue ; }
   public void MethodTwo() { test1 = 'Hello World' + selectedValue1 ; }
}
We set-up Salesforce as an SSO Identity Server and can get user to login from Wordpress as the Service Provider.

But I can't see how users can self-register on Wordpress and be created in Salesforce.  Plus update their details.

Any pointers would be much appreciated.....
I've got the following lines of code that work just fine when boatId has an Id value (string)
sendMessageService(boatId) { 
    // explicitly pass boatId to the parameter recordId

    const payload = { recordId: boatId };
    publish(this.messageContext, BOATMC, payload);

  }
But when I set boatId to nothing the publish fires but the subscriber never gets the message.  What am I doing wrong, how do I publish a null boatId?
 
I recently did PD2 and one of the questions I'm stuck on.  If you had a large data set that you wanted the user to be able to filter which is most efficent:
1. Rerun the SOQL each time with the new data filters
2. Hold the whole data set in memory and use Apex to filter
3. Use visualforce tags to filter - can't remember which ones

I went for hold in memory and use apex.  Can't find any documentation on best approach for performance.  any help?
I'm getting the following error on Advanced Apex Specialist Superbadge - Step 5.  Any ideas please?
User-added image

Here is the current code segments:

VerifyQuantityOrdered
Public static void VerifyQuantityOrdered(Product2 originalProduct, Product2 updatedProduct, Integer qtyOrdered) {
       	decimal tot = (originalProduct.Quantity_Ordered__c + qtyOrdered);
        
            system.debug('OldVQ ' + originalProduct.Quantity_Ordered__c);
            system.debug('NewVQ ' + updatedProduct.Quantity_Ordered__c);
            system.debug('QTYVQ ' + qtyOrdered);
        
        system.assertEquals(updatedProduct.Quantity_Ordered__c,tot);     
    }
OrderTests
@isTest
private with sharing class OrderTests {

    @testSetup 
    private static void SetupTestData (){    
    	TestDataFactory.InsertTestData(5);   
    } 

    @isTest
    private static void OrderUpdate_UnitTest (){
        Test.startTest();
    	
        List<Order> OrderList = [select id, name, status from order];

        For (Order ordrec : OrderList) {
            OrderItem oirec = [select id, Pricebookentry.product2Id from orderitem where orderid=:ordrec.id];
			Product2 oldprodrec = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c 
                            	FROM Product2 where id =: oirec.Pricebookentry.product2Id  limit 1];
            ordrec.status = constants.ACTIVATED_ORDER_STATUS;
            update ordrec;
            OrderItem oirec1 = [select id, Pricebookentry.product2Id from orderitem where orderid=:ordrec.id];
			Product2 newprodrec = [SELECT Family,Id,Name,Quantity_Ordered__c,Quantity_Remaining__c 
                            	FROM Product2 where id =: oirec1.Pricebookentry.product2Id  limit 1]; 
            system.debug('Old ' + oldprodrec.Quantity_Ordered__c);
            system.debug('New ' + newprodrec.Quantity_Ordered__c);
            system.debug('QTY ' + constants.DEFAULT_ROWS);
            TestDataFactory.VerifyQuantityOrdered(oldprodrec,newprodrec,constants.DEFAULT_ROWS);
        }
        Test.stopTest();          
    }
}
Product2Tests
@isTest  (seeAllData=false)
private with sharing class Product2Tests {

    /**
     * @name product2Extension_UnitTest
     * @description UnitTest for product2Extension
    **/
    @isTest
    private static void Product2Extension_UnitTest(){
// Set-up user
        String uniqueUserName = 'standarduser' + DateTime.now().getTime() + '@testorg.com';
        Profile p = [SELECT Id FROM Profile WHERE Name='Standard User'];
        User u = new User(Alias = 'standt', Email='standarduser@testorg.com',
        	EmailEncodingKey='UTF-8', LastName='Testing', LanguageLocaleKey='en_US',
        	LocaleSidKey='en_US', ProfileId = p.Id, TimeZoneSidKey='America/Los_Angeles', UserName=uniqueUserName);

        System.runAs(u) {
        
// When a user first visits the page, there should be multiple rows displayed on the screen. 
// Assert that the size of the productsToInsert list is equal to the DEFAULT_ROWS constant.
//		Test.StartTest(); 
        	Product2 prod = new Product2(name='Test',isActive=true);
        	ApexPages.StandardController stdc = new ApexPages.StandardController(prod);
        	Product2Extension p2x = new Product2Extension(stdc);        
       		System.assertEquals(Constants.DEFAULT_ROWS, p2x.productsToInsert.size());
//		Test.StopTest();  

// When the Add button is clicked, an additional set of rows should be added, 
// so assert that the size of the productsToInsert ** list is double **DEFAULT_ROWS after the button is clicked once. 
//        Test.StartTest();
        	p2x.addRows();
        	System.assertEquals(Constants.DEFAULT_ROWS * 2, p2x.productsToInsert.size());
//		Test.StopTest();          

// Next, test the Save button. Verify that populated rows are saved and unpopulated rows are not saved. 
// Loop through the rows in the productsToInsert list and populate the values of the first 5 records, 
// and then simulate clicking the Save button. Verify that the button worked by asserting that only 5 products were saved.
		integer x = 0; 
        for (Product2Extension.ProductWrapper PTI : p2x.productsToInsert){
            pti.productrecord.name='TESTPRODUCT ' + x;
            pti.productRecord.IsActive = true;
            pti.productRecord.Initial_Inventory__c = 20;
            pti.productRecord.Family = Constants.PRODUCT_FAMILY[0].getValue();           
            pti.pricebookEntryRecord.UnitPrice = 10;
            
        	x++; if (x==5) {break;}
        }

//        Test.startTest();
        	p2x.save();
//        Test.stopTest();
        List<Product2> createdProducts = [SELECT Id FROM Product2];
        System.assertEquals(5, createdProducts.size());

// plus some more test areas        
        p2x.GetFamilyOptions();
        p2x.GetInventory();

    }
    }
}


 
I'm working on Step 9 at the moment and my code passed the Step 7 check, but I noticed something very odd.  Here is the problem:

1. I can add an intial review and the code does as requested and shows the review 
Add Review  Show Boat

Behind the scenes a record is added to the BoatReview__C object
User-added image

2. The problem occurs on adding a second review.  No matter if I change boats or add a review to the existing boat, all subsequent inserts actually update the original review.
Add second review 

Behind the scenes the Insert has actually done an Update on the original record
User-added image

If I change boats and add a review record, the original boat review is updated and the Id and Boat Id stay the same.  It is as though I am not Committing the transaction.

Here is the code:
AddBoatReview.cmp
<aura:component implements="force:appHostable,flexipage:availableForAllPageTypes">
	<aura:attribute name="boat" type="Boat__c" access="public"/>
    <aura:attribute name="boatReview" type="BoatReview__c" access="private"
                    default="{'sobjectType':'BoatReview__c', 'Name':'', 'Comment__c':''}"/>    
	<aura:attribute name="boatReviewRecord" type="Object" access="public"/>
    <aura:attribute name="recordError" type="String" access="private"/>  

    <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    
	<aura:registerEvent name="BoatReviewAdded" type="c:BoatReviewAdded"/>    
    
	<force:recordData aura:id="service"
    	layoutType="FULL"  
		fields="Id, Name, Comment__c, Boat__c"
		targetRecord="{!v.boatReviewRecord}"
		targetFields="{!v.boatReview}"
		targetError="{!v.recordError}"
        recordUpdated="{!c.onRecordUpdated}" />    
    
    <lightning:layout multipleRows="true">
		<lightning:layoutItem size="12" padding="around-small">
			<lightning:input name="title" label="Title" value="{!v.boatReview.Name}"/>
		</lightning:layoutItem>
		<lightning:layoutItem size="12" padding="around-small">
			<label class="slds-form-element__label" for="input-id-01">Comment</label>
			<lightning:inputRichText value="{!v.boatReview.Comment__c}" disabledCategories="FORMAT_FONT"/>
		</lightning:layoutItem>
        <lightning:layoutItem size="12" class="slds-align--absolute-center">
			<lightning:button iconName="utility:save" label="Submit" onclick="{!c.onSave}"/>
		</lightning:layoutItem>
	</lightning:layout> 
    
</aura:component>

AddBoatReviewController.js
({  
    doInit : function(component, event, helper) {       
		helper.onInit(component, event);
    },
   
    onSave : function(component, event, helper) {
//		component.set("v.boatReview.Boat__c",component.get("v.boat.Id"));
			
        component.find("service").saveRecord(function(saveResult) {
           console.log("Add Review status" + saveResult.state)
        		if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") { 
        			var resultsToast = $A.get("e.force:showToast");
                    if(resultsToast != undefined) {    
                        resultsToast.setParams({
                        "title": "Saved",
                        "message": "Boat Review Created"
                        });
						resultsToast.fire(); 
                    } else {
                        alert('Boat Review Created');
                    }
				} else if (saveResult.state === "INCOMPLETE") {
                	console.log("User is offline, device doesn't support drafts.");
        		} else if (saveResult.state === "ERROR") {
            		console.log('Problem saving contact, error: ' +JSON.stringify(saveResult.error));
            	} else {
            		console.log('Unknown problem, state: ' + saveResult.state + ',error: ' + JSON.stringify(saveResult.error));
            	}                
			}
        ); 
		var BoatReviewAdded=component.getEvent("BoatReviewAdded");
        BoatReviewAdded.setParams ({"BoatId" : "ADDED"});
		BoatReviewAdded.fire();
		
        helper.onInit(component,event);   
	},        
    onRecordUpdated : function(component, event, helper) {
        var eventParams = event.getParams();
        if(eventParams.changeType === "CHANGED") {
            var changedFields = eventParams.changedFields;
            var saveResultsToast = $A.get("e.force:showToast");
                if(saveResultsToast!='undefined')
                {
                    saveResultsToast.setParams({
                        "title": "Saved",
                        "message": "Boat Review Saved"
                    });
                    saveResultsToast.fire(); 
                }
                else
                {
                    alert('Boat Review Saved');
                }
        }
    }
})

AddBoatReviewHelper.js
({
	onInit : function(component,event) {
        var boat=component.get("v.boat"); 
//        console.log("We have got the AddReview INIT OK" + boat.Id);  
        component.find("service").getNewRecord("BoatReview__c", // sObject type (entityApiName)
            	null, // recordTypeId
            	false, // skip cache?
            $A.getCallback(function() {
           		var rec = component.get("v.boatReview");
                   var error = component.get("v.recordError");                 
				if(error || (rec === null)) {
//            		console.log("Error initializing record template: " + error);
         		} else {
//                    console.log("We have initialised the AddReview OK" + boat.Id);                 
					component.set("v.boatReview.Boat__c",boat.Id);
         		}
			})
		);   
    }
})

I can complete the SuperBadge becuase the code passed the checker, but be good to know what I got wrong......thanks 


 
I have two Salesforce Orgs.  One is the Identity & Service Provider, the other just the Service Provider.

I can get SAML working between the Orgs.  But setting-up the combined Identity & Service Provider Org fails everytime.

I think this might be an "undocumented feature" and you can't connect them???
I just can't work this out.  I've read and re-read the Salesforce documentation.  All I want to do is connect my Community to the internal Identity Provider.

1. I have a Salesforce Org with a Community, nothing special - community is active and published, standard template, no customization

2. I set-up My Domain and this automatically creates a SAML idetnity provider for the new domain.  It included a self-signed certificate plus metadata end-points for the Domain and the Community Domain.

3. I set-up both domains as Remote Sites so I can then set them up for SSO

4. I enable Single Sign-on and set-up the Community as per the instructions:  https://developer.salesforce.com/docs/atlas.en-us.sso.meta/sso/sso_examples_sf2sf.htm
To set up a community as a service provider, use the community URL under SAML Metadata Discovery Endpoints on the Identity Provider page. Upload the SAML metadata from this URL. Using the metadata populates the service provider’s SAML SSO settings, including the Login URL that points to the community. When you define a connected app on the identity provider, specify this Login URL as the ACS URL.

5. I set-up the community as a connected app and use the Entity ID and HTTPRedirect URL specified

6. I update the community to enable access to this SSO login.  I get it showing on the login screen, but it doesn't work.  What can I do to debug this??

Community Login Page

SSO Page - no login
I'm looking to connect a Salesforce Community with a Sitecore website via the CMS Connect feature.  But have been unable to get this working.  Please can I ask some basic questions:

1. What is a root path and how can I find it for Sitecore?

2. The Header & Footer path indicate a need to link to a "header.html" file.  But the website embeds the header in the Body section via CSS.  How can I put this set-up into CMS Connect

Very stuck.
When I was researching this line of code I managed to level-up in my lightning knowledge, but it opened-up new questions.....
var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {inputCmp.showHelpMessageIfInvalid(); return validSoFar && inputCmp.get('v.validity').valid;}, true);
I've worked out what its doing and how it works - tick.  But what is means in Components must have a define list of available Properties & Methods.

Does anyone know where I can find this list?  Reduce is a Javasript method but IsValid() is Salesforce.  Where can I get a definitive list please?
 
I've set-up a basic community and got it running.  But when I drag components onto the Home Page I get a message such as this: 

Community Home Page

What I'd like to do is put the user details on the home page for editing.  Can this be done?
I'm trying to access the Search bar in Classic with Selenium but cannot seem to find the object.  I've tried a few variants below, how do I access?

driver.findElement(By.name("str")).sendKeys("Hello");
driver.findElement(By.xpath("//*[@id='phSearchInput']")).sendKeys("Hello");
driver.findElement(By.id("phSearchInput")).sendKeys("Hello");
Midway through this module the following Client Controller code is introduced.  Is the Reduce function documented anywhere else so I can understand it better.  The Trailhead explanation doesn't work for me, very stuck

({
    clickCreate: function(component, event, helper) {
        var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
            // Displays error messages for invalid fields
            inputCmp.showHelpMessageIfInvalid();
            return validSoFar && inputCmp.get('v.validity').valid;
        }, true);
        // If we pass error checking, do some real work
        if(validExpense){
            // Create the new expense
            var newExpense = component.get("v.newExpense");
            console.log("Create expense: " + JSON.stringify(newExpense));
            helper.createExpense(component, newExpense);
        }
    }
})


 
I want to connect Salesforce to Mailchimp so I can send an Apex Callout with a REST PUT request to update a subscriber in Mailchimp.  I've connected Postman (https://www.getpostman.com/) to Mailchimp and go the core REST statement defined and working.  As part of this I needed to set-up the OAuth connection.  This was straight forward to do:
1. In Mailchimp, set-up the connecting App, this generates the Client ID and Client Secet to use.
2. In Postman, request a new Access Token, as shown
User-added image
3. In Postman, use the Access Token as authentication in the REST Header, like this

GET /3.0/Lists/ HTTP/1.1
Host: us16.api.mailchimp.com
Authorization: Bearer c36db89lkjh8hkh8l6ae0005bfc3

I'm really struggling to set-up the same for Salesforce.....  
1. Connected Apps aren't relevant as that is for apps that wish to get data from Salesforce, I want the other way around
2. Named Credentials seems to be the right place but this asks for an Authentication Provider, 
User-added image

3. The Authentication Provider picklist doesn't include Mailchimp, unless there is a "generic" one to use
User-added image
So to get it to work I've had to hard code the Access Token I got for Postman into the request in the Apex Callout as shown

public class Mailchimp {

    public static string  getMailChimp() {          
    Http http = new Http();
    HttpRequest request = new HttpRequest();
    request.setEndpoint('https://us16.api.mailchimp.com/3.0/Lists/'); 
    request.setHeader('Authorization', 'Bearer c36dbf7jhv89jbnjnkuf6a7a16ae0005bfc3');
    request.setMethod('GET');
    HttpResponse response = http.send(request);   
    string a = response.getBody();      
    system.debug('Here you go - ' + a);
    Return a;
    }     
}

What am I missing??
Hi Folks,

I'm trying to implement SF as IDP and I'm encountering a problem with an incorrect login page being served.
Here are the general steps I've taken so far:
  • setup My Domain
  • enable Identity Provider
  • created a custom profile using external identity license
  • created connected app and provision profile to the connected app
  • created users under custom profile created
  • create a new community (used Aloha template)
  • provision the users with the custom profile to access this community
  • customize the login page using Community Builder
  • make sure that the Community Login page is selected under Community Administration
  • activate and publish the community 
  • configured Service Provider with metadata exposed from the Connected App under the 'For Communities' section
The service provider is using the IdP-Initiated Login URL that's listed on the connected app settings page, but it is not being directed to the login page. Instead it's going to the self-registration page. 

Has anyone else come across this problem?

I'm having issues with adding a Menu Item to the Navigation Menu within Community Builder.

User-added image
User-added image

This is what I want: Add a menu item called "My Profile" and have it go directly to the User Profile page.
What's happening: The URL for My Profile requires one or more parameters. Please replace each parameter with the appropriate value.

Name: My Profile

Type: Community Page
Page: User Profile
URL: /profile/:recordId

Hi,

 

I need to construct a CSV file with APEX and am a bit new to this cloud computing.

Do you have any sample code I could get started with that, or a resources that you can point me to develop and generate a  CSV File to send through the Email??

 

Can this be achieved in salesforce by any Way?

  • December 29, 2011
  • Like
  • 0