Tuesday, August 14, 2018

Showing and hiding the tab in Salesforce Lightning Design System in Visualforce page using basic javascript

I am not yet familiar with Salesforce Lightning Design System (SLDS) however I need to start it somewhere.There some requirement come in and it does not have specification on what the UI look likes so I thought why not give SLDS a try.

Before SLDS comes into picture, I experienced using Bootstrap and JQuery. It's simple and easy to use however because it is external javascript library it need to have extra precautions.Sometimes we got issue when the css is conflict.

Still after SLDS , developer need to code javascript in order to enhance user experience for example changing the tab, make the inactive button visible after click as SLDS does not have code for that.It only provide you the CSS how active and disable button looks like.I notice in some demo they use React JS or JQuery.

I love it because it's neat and elegant but I found it hard to customize the theme if our organization has different color scheme from Salesforce. It can be customized using Design Token using Sass, then generate our own CSS zip file. So we can't use tag   for customize CSS. Yet, this  is another new thing to learn.

I am very reluctant to use JQuery in this because I want to avoid third party libraries.While you go through design,  you might notice, all the code that control look and feel in SLDC is defined in class.Therefore we can use Java Script to manipulate CSS to change look and feel.

Here is sample of the tab that I designed using SLDS.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<div class="slds-tabs_card slds-tabs_default">
  <ul class="slds-tabs_default__nav" role="tablist">
    <li id="first_tab_header" class="slds-tabs_default__item slds-is-active" title="First Stage Question" role="presentation">
      <a class="slds-tabs_default__link" href="javascript:showTab('first-tab',this);" role="tab" tabindex="-1" aria-selected="false" aria-controls="second_tab" id="second_tab__item">First Stage Question</a>
    </li>
       <li id="first_tab_header" class="slds-tabs_default__item" title="Second Stage Question" role="presentation">
      <a class="slds-tabs_default__link" href="javascript:showTab('second_tab',this);" role="tab" tabindex="0" aria-selected="true" aria-controls="first_tab" id="first_tab__item">Second Stage Question</a>
    </li>
  </ul>
    <!--first tab start -->

    <div id="first_tab" class="slds-tabs_default__content slds-hide" role="tabpanel" aria-labelledby="first_tab__item">
     <!--content on first tab whatever --->

    </div> 
     <!--first tab end -->
  <!--second tab start -->
   <div id="second_tab" class="slds-tabs_default__content slds-show" role="tabpanel" aria-labelledby="second_tab__item">
     <!--content on second tab whatever --->
   </div>
<!--second tab end -->
</div>

The javascript code as below, it use classList remove and add .


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
function showTab(tabName, element){
    console.log('tabName '+ tabName);
    console.log('Element '+ element);
    if(tabName=='first_tab') {
     document.getElementById('second_tab_header').classList.remove('slds-is-active');     
    document.getElementById('first_tab_header').classList.add('slds-is-active');
        
    document.getElementById('second_tab').classList.remove('slds-show');
    document.getElementById('second_tab').classList.add('slds-hide');
    
    document.getElementById('first_tab').classList.remove('slds-hide');
    document.getElementById('first_tab').classList.add('slds-show');
    
    }
    else{
    document.getElementById('first_tab_header').classList.remove('slds-is-active');     
    document.getElementById('second_tab_header').classList.add('slds-is-active');  
        
    document.getElementById('first_tab').classList.remove('slds-show');
    document.getElementById('first_tab').classList.add('slds-hide');
    document.getElementById('second_tab').classList.remove('slds-hide');
    document.getElementById('second_tab').classList.add('slds-show');
    
    }

}

The javascript code is to change the CSS value that define in class .This is very basic article but I hope it will be helpful for you.Have you tried SLDS in your organization? How do you find it?

Have a nice day.

Reference :
Lightning Design System
Using SLDS in Visualforce Page
How to change customize theme, background color in Visualforce that using Design system?

Tuesday, July 31, 2018

Trapped in Process fails with error 'The flow failed to access the value because it hasn't been set or assigned' WHILE IT IS NOT


If you have no idea about Known Issue : Process fails with error 'The flow failed to access the value because it hasn't been set or assigned' , it's worth to check it first then you might want to read this entry.
Issue : The user reports that when using Mass Edit to edit Opportunity it will prompt error

Can't Save Record

That's it. No sweet lovely sentence, no flowery and it's leave the mystery for us to dig out the root cause.Typical communication breakdown in love story.

Luckily we have email system whereby we got chance to get the exception email straight in our inbox.Here the error :


Error element myDecision (FlowDecision).
The flow failed to access the value for myVariable_current.Owner.UserRole.DeveloperName because it hasn't been set or assigned.

Just to clarify , yes we use process builder and we are aware of issue above whereby we need to put condition checking in proper order whenever our condition need to check value from other object.This is known issue described here.

A Process that makes a cross-object reference will fail when the foreign key (i.e. relationship field) the reference depends on is not populated.

Our process builder complied with the rules still it happening?

Why  error?

To test , I modified from condition based to formula well something like this.

AND(NOT(ISBLANK([Opportunity].Owner.UserRoleId  )) , OR (  [Opportunity].Owner.UserRole.DeveloperName ='Sales Manager' ,  [Opportunity].Owner.UserRole.DeveloperName ='APAC Manager'') ) 

and pray to God.

Unfortunately it's still happening and I am not able to get much information when Googling.
.
.
.

How the user input?
Using Mass Edit.

Is Process Builder does not support bulk?
Nope- process builder support bulkified .This is delivered in Winter 16. Plus the exception that we face nothing to do with limit.

What is MassEdit?
It is visualforce page that we installed long time ago.

Is it using customize controller?
No, it using standard controller.The save still same like standard, the only thing that different is the UI.No fancy code.
.
.
.
Click on Version Settings of visualforce page.
Version 15.
15...
Change to 42 and pray to God.

Success. No Error.

Yes, upgrade version can solve problem.It will be easier if the error message tell me something wrong with version.

Typical communication-breakdown =)

Thursday, July 05, 2018

Pricebook, Pricebook Entry and Product : Adding custom pricebook in Salesforce

Lately I worked on these object a lot and learned a lot of new things.

Some issues that we face around these object :
  • Some exceptions during data upload  such as :
    • FIELD_INTEGRITY_EXCEPTION .Message :field integrity exception: PricebookEntryId, unknown (versions 3.0 and higher must specify pricebook entry id, others must specify product id)
    • No Standard Price defined
  • Not able to retrieve standard price in test class


Understand the relationship of these object


Ignore Opportunity and Opportunity Line Item- these two object are using Price Book entry .Image credit to ForceTalk forum

PriceBook

  • Pricebook object store a list of products and services for sale.
  • A pricebook can be used in one or more different opportunities.
  • A pricebook has one or more different price entries.
  • Pricebook can be divided into type which are :
    •  Standard Price Book which is the master list of all the products and their default standard price.
    • Custom Price Book is separate list of product with custom price - called list price.


Product2

  • Product object is a catalogue of items or services for sales.
  • A product may contain one or more different sets of prices (PriceBookEntry).

PriceBookEntry

  • PricebookEntry object is a product entry in a pricebook.
  • This object allows products to be linked to standard price book or custom price book.
  • One price book entry is linked to only one product.
  • One price book entry can only appear in one pricebook.
  • One price book entry can be used in multiple line items. This also further creates flexibility for users to set their own prices.

Rules for Data Upload

  • PriceBookentry is junction object that connect Product and PriceBook so the order will be like this :
    • Insert PriceBook
    • Insert Product
    • Insert PriceBookEntry with PricebookId and Product Id

Scenario 1 : I am not able to insert PriceBookEntry for custom PriceBook.

We already have custom PriceBook and Product data inserted but we not able to insert PriceBookEntry to link these two objects

Reason : In order to insert PriceBookEntry for custom PriceBook , those Products must be added to Standard PriceBook first because Standard PriceBook is considered as master price. 

Refer knowledge articles here : 

Scenario 2 : Standard Price is not querable and insertable in test class

Well nothing I can do much because this how it works currently. But I can give some advice :

If the code contains SOQL on Pricebook make sure it get surrounded with try and catch. Add Test.isRunningTest() checking in the code and assign Standard Price Book.
If you have custom price you may want get an idea how to handle catch based on code below

 catch (Exception e){
   if(Test.isRunningTest()){
      List<Pricebook2>  listPB= [ select Id ,Name , IsStandard from Pricebook2 where Name ='Custom'  LIMIT 1];
      mapPriceBook.put('Standard',Test.getStandardPricebookId() );
      mapPriceBook.put('Custom',listPB[0].Id);
    
   }
 }

Also avoid retrieve and using PriceBook object but use Id.

Example of code that you want to avoid.Note that there is no way for you to get away with Test class exception because you only can use Test.getStandardPriceBookId() , not the whole object in test class.


 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
List<Pricebook2> listPB = [select Id ,Name , IsStandard from Pricebook2 where IsStandard = True OR  Name ='Custom'  LIMIT 2];

static  Map<String,Pricebook2> mapPriceBook =new Map<String,Pricebook2> ();

for(Pricebook2 pb : listPB){

if(pb.IsStandard){
  mapPriceBook.put('Standard',pb); //this will not get over the test class
}
else if(pb.Name =='Custom'){
 mapPriceBook.put('Custom',pb);
}

One way to be safe is using Id.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
List<Pricebook2> listPB = [select Id ,Name , IsStandard from Pricebook2 where IsStandard = True OR  Name ='Custom'  LIMIT 2];

static  Map<String,Id> mapPriceBook =new Map<String,Id> ();
             
             for(Pricebook2 pb : listPB){
             
               if(pb.IsStandard){
                  mapPriceBook.put('Standard',pb.Id);
               }
               else if(pb.Name =='Custom'){
                 mapPriceBook.put('Custom',pb.Id);
               }

I think that's all I can think today. I believe you also got some of tips and trick to get over exception related to these objects. Please let me know how you do that?

References


Monday, April 02, 2018

Add Fault Connector in Salesforce Flow Autolaunch


Fault Connector is very useful because now we able to handle the error. We can trace who perform the transaction and what record that cause the error.

The connector is indicated with FAULT like image below.


In some occasion you might face into issue that Fault Connector is not working like below :




Here is the hack. We need to add another element after it because for unknown reason Fault Connector will not appear in the last element. So we need to create another element temporarily like below.

Tips : Use Copy and Paste button to copy from existing element so you don't have create dummy element from start.


Now we can add Fault Connector to the element that we want to.As you can see in the image below, when we drag the connector 'Fault' label appear. Now we can delete the dummy element.

That's it. Now it should works.

More about Flow can be refer to here .

Have a nice day.

Monday, March 05, 2018

Process Builder Bug to Take Note

Well, it is annoying when the process builder does not work for reason that not mentioned in documentation properly.

I decided to compile the list of nonsense difficulties that I found whilst using Process Builder

Using 15 ID in Process Builder(Big No)

I use 15 char ID in Process Builder criterita that I retrieve from Salesforce URL to fire Email Alert.The email does not get fire although Deliverabilities and other setting are correct.

To confirm, I run SOQL with 15 char ID and it returns value. So it leave me clueless why my Process Builder does not work.Oh, hello it just Process Builder but even simple Process Builder does not work!!

It took me some time until I randomly put 18 char ID and it wallaahhh it works.Seriously, is there any documentation say Process Builder only receive 18 char ID ?

Let me know if you find it.For this time being , I will avoid using ID field in Process Builder.


Action Name need to be short ...how SHORT?

You must specify a name for the action cal l.
To avoid this error , please choose Email Alert that popup from the field when you put the name.

You see this error although you already specify  action name for  email alert and wonder ... hey I already put the name!!

This is misleading error because actually it require you to put Action Name short.How short? You need to test it by yourselves.

Anyway, if you desperately need a longer name, put short name.Save then rename it Save.

Close the Process Builder and Open again ...meh

When I renaming Email Alert in separate tab, then add it in Process Builder in separate tab it will throw something
"Could not find Email Alert ..."

Although the Email Alert Name autopopulate in the field, ust close the Process Builder and try again.

It works.Well, seem restart always a solution.

Deploy active Process Builder -- it will not automatically active

Process Builder will not guaranteed to be active automatically when deployed.We need to manually activate Process Builder after we deploy it.


TO BE CONT...
Share with me if you got issue with process builder?

Thursday, February 15, 2018

Apex Webservice (You Call me)

I already write about Apex Call Out when we want to connect with external services either in REST or SOAP here .

If Apex Call Out I use 'I call you' metaphor to represent Salesforce need to contact external service, therefore we need external service info such as - url and set end point url in Remote Site Setting.

While Apex Web Service I use 'You call me' which mean we need to expose our Apex class either as SOAP or REST for external service to connect.

You call me! In order for them to you,
you need to give your phone number

Expose a class as REST Service

  1. Define class with global access modifier .
  2. Use @RestResource annotation at class level to enable it serve as webservice
    • Example : @RestResource(urlMapping='/Case/*')
  3. Define method with global static .
  4. Add an annotation to associate with Http method. Bear in mind on one method is allowed per class. It means there should be only one GET method per class.
  5. Endpoint URL that we need to share to external service will look like this: https://yourInstance.salesforce.com/services/apexrest/Case/*
AnnotationActionDetails
@HttpGetReadReads or retrieves records.
@HttpPostCreateCreates records.
@HttpDeleteDeleteDeletes records.
@HttpPutUpsertTypically used to update existing records or create records.
@HttpPatchUpdateTypically used to update fields in existing records.
Example
@RestResource(urlMapping='Cases/*')
global with sharing class MyRestResource {
    @HttpGet
    global static Case getRecord() {
        // Add your code
    }
}


Expose a class as SOAP Service

  1. Define class with global access modifier .
  2. Define your method with 'webservice static'
  3. You need to generate WSDL and send it to the external developer so they can write integration to connect wit us.
Example
global class Project {
  
   webservice String area; 
   webservice String region; 
   
   //Define an object in apex that is exposed in apex web service
   global class Plan {
      webservice String name;
      webservice Integer planNumber;
      webservice Date planningPeriod;
      webservice Id planId;
   }

   webservice static Plan createProjectPlan(Plan vPlan) {
       
       //A plan maps to the Project__c object in salesforce.com. 
       //So need to map the Plan class object to Project__c standard object
       Project__c proj = new Project__c();
       proj.Name = vPlan.name;
       proj.AccountNumber = String.valueOf(vPlan.planNumber);
       insert proj;
       
       vPlan.planId=proj.Id;
       return vPlan;
  }

Conclusion

This entry explains on custom Apex code for REST and SOAP API and it not the only way for external applications to connect with Salesforce. There a few ways to connect such as using Salesforce's REST and SOAP APIs. It has different strengths to fulfill  business requirement.

References :
Awesome Trailhead Apex Web Services
Still standing : Apex Web Services and Callouts

Friday, February 09, 2018

Apex Call out (I Call You)

Although I have done integration to external service using call out in my previous experience still I have hiccup to grab the concept.So I thought that I should sit down and learn the concept and of course  the best way is  through Salesforce Trailhead  =) .

When it comes to integrate Salesforce with external service we can categorize it into two types :
  1. Apex Call Out
  2. Apex Web Service (I wrote here)
Both can use SOAP and REST protocol which it is advisable to use REST. In case you want to know which one better , please check out this article.

I personally have come out easy way to understand the concept for both category.In my concept, we can assume SOAP and REST is different provider that use different format.

I use 'I Call You' for Apex Call Out and 'You Call Me' for Apex Webservices.

I Call You (Apex Call Out)

well, can you see..my drawing is #awesome =)

I mean Salesforce , You mean external service.
In order to enable this we need to know who we call so we can call them.
They need to let us know how to call them.

You Call Me (Apex Web Service)
The drawing become bigger for unknown reason
You mean External Service, me mean Salesforce.
In order to enable this we need to expose and give information to external service how to call us.

Apex Call Out

Apex Call Out is when we want to use external service , either we want to send or receive data.
I notice that in Trailhead it use webservice call out to define call out that use SOAP and HTTP Call out to define call out that use REST with JSON.
  • Web service callouts to SOAP web services use XML, and typically require a WSDL document for code generation.
  • HTTP callouts to services typically use REST with JSON.Read more...

Regardless what type we still need to define their number(Endpoint Address) in Remote Setting so we can call them.

Apex REST Callouts

Some common HTTP methods
HTTP MethodDescription
GETRetrieve data identified by a URL.
POSTCreate a resource or post data to the server.
DELETEDelete a resource identified by a URL.
PUTCreate or replace the resource sent in the request body.

Basic Steps to implement Apex REST Callouts
  1. Define Endpoint address in Remote Setting.Note the setting below will authorize all subfolders for the endpoint, for example in your code later it will enable you to call https://th-apex-http-callout.herokuapp.com/subfolder1 or https://th-apex-http-callout.herokuapp.com/subfolder2
  2. Make sure external service give you information how to access their data.They should provide you correct URL to retrieve animal data and what parameter they need in order to return you the data. For example, they provide you this url https://th-apex-http-callout.herokuapp.com/animals/'+id which indicate that you can append Id at the back in order for it to return animal data.Without information, you not able to call them.Usually they will provide you documentation on how you able to call their method.
  3. You can create class to get the data or you can run it using Apex Execute Anonymous in Developer Console.
    public class AnimalLocator {
    public class cls_animal {
            public Integer id;  
            public String name; 
            public String eats; 
            public String says; 
        }    
    
    public class JSONOutput{
        public cls_animal animal;
    
     }
    public static String getAnimalNameById(Integer id){
      String stranimal='';
    
     Http http = new Http();
            HttpRequest request = new HttpRequest();
            request.setEndpoint('https://th-apex-http-callout.herokuapp.com/animals/'+id);
            request.setMethod('GET');
            HttpResponse response = http.send(request);
            // If the request is successful, parse the JSON response.
            if (response.getStatusCode() == 200) {
              // Deserializes the JSON string into collections of primitive data types.
              jsonOutput results = (jsonOutput) JSON.deserialize(response.getBody(), jsonOutput.class);
               strAnimal=results.animal.name;
               System.debug(stranimal);
             }
           
    return stranimal;
    }
    
    }
    


Apex SOAP Callouts

In this case, we need to request external party to provide us WSDL so we can generate class from it.
In trailhead , the external service provide calculator.xml like below.

<?xml version='1.0' encoding='UTF-8'?><wsdl:definitions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:tns="http://calculator.services/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:ns1="http://schemas.xmlsoap.org/soap/http" name="CalculatorImplService" targetNamespace="http://calculator.services/">
  <wsdl:types>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://calculator.services/" elementFormDefault="unqualified" targetNamespace="http://calculator.services/" version="1.0">

  <xs:element name="doAdd" type="tns:doAdd"/>

  <xs:element name="doAddResponse" type="tns:doAddResponse"/>

  <xs:element name="doDivide" type="tns:doDivide"/>

  <xs:element name="doDivideResponse" type="tns:doDivideResponse"/>

  <xs:element name="doMultiply" type="tns:doMultiply"/>

  <xs:element name="doMultiplyResponse" type="tns:doMultiplyResponse"/>

  <xs:element name="doSubtract" type="tns:doSubtract"/>

  <xs:element name="doSubtractResponse" type="tns:doSubtractResponse"/>

  <xs:complexType name="doDivide">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:double"/>
      <xs:element minOccurs="0" name="arg1" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="doDivideResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="doSubtract">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:double"/>
      <xs:element minOccurs="0" name="arg1" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="doSubtractResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="doMultiply">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:double"/>
      <xs:element minOccurs="0" name="arg1" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="doMultiplyResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="doAdd">
    <xs:sequence>
      <xs:element minOccurs="0" name="arg0" type="xs:double"/>
      <xs:element minOccurs="0" name="arg1" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

  <xs:complexType name="doAddResponse">
    <xs:sequence>
      <xs:element minOccurs="0" name="return" type="xs:double"/>
    </xs:sequence>
  </xs:complexType>

</xs:schema>
  </wsdl:types>
  <wsdl:message name="doSubtractResponse">
    <wsdl:part element="tns:doSubtractResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="doMultiplyResponse">
    <wsdl:part element="tns:doMultiplyResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="doAddResponse">
    <wsdl:part element="tns:doAddResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="doDivide">
    <wsdl:part element="tns:doDivide" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="doSubtract">
    <wsdl:part element="tns:doSubtract" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="doDivideResponse">
    <wsdl:part element="tns:doDivideResponse" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="doMultiply">
    <wsdl:part element="tns:doMultiply" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:message name="doAdd">
    <wsdl:part element="tns:doAdd" name="parameters">
    </wsdl:part>
  </wsdl:message>
  <wsdl:portType name="Calculator">
    <wsdl:operation name="doDivide">
      <wsdl:input message="tns:doDivide" name="doDivide">
    </wsdl:input>
      <wsdl:output message="tns:doDivideResponse" name="doDivideResponse">
    </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="doSubtract">
      <wsdl:input message="tns:doSubtract" name="doSubtract">
    </wsdl:input>
      <wsdl:output message="tns:doSubtractResponse" name="doSubtractResponse">
    </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="doMultiply">
      <wsdl:input message="tns:doMultiply" name="doMultiply">
    </wsdl:input>
      <wsdl:output message="tns:doMultiplyResponse" name="doMultiplyResponse">
    </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="doAdd">
      <wsdl:input message="tns:doAdd" name="doAdd">
    </wsdl:input>
      <wsdl:output message="tns:doAddResponse" name="doAddResponse">
    </wsdl:output>
    </wsdl:operation>
  </wsdl:portType>
  <wsdl:binding name="CalculatorImplServiceSoapBinding" type="tns:Calculator">
    <soap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http"/>
    <wsdl:operation name="doDivide">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="doDivide">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="doDivideResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="doSubtract">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="doSubtract">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="doSubtractResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="doMultiply">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="doMultiply">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="doMultiplyResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
    <wsdl:operation name="doAdd">
      <soap:operation soapAction="" style="document"/>
      <wsdl:input name="doAdd">
        <soap:body use="literal"/>
      </wsdl:input>
      <wsdl:output name="doAddResponse">
        <soap:body use="literal"/>
      </wsdl:output>
    </wsdl:operation>
  </wsdl:binding>
  <wsdl:service name="CalculatorImplService">
    <wsdl:port binding="tns:CalculatorImplServiceSoapBinding" name="CalculatorImplPort">
      <soap:address location="https://th-apex-soap-service.herokuapp.com/service/calculator"/>
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Basic Steps to implement Apex SOAP Callouts
  1. Define Endpoint address in Remote Setting.
  2. Generate class from WSDL file that you retrieve from external service.Go to Setup > Develop > Apex Classes and hit button Generate from WSDL and wallahhh... the class is ready for you to use.Use the class as usual.

I will write about Apex Web Services later.

Ultimate reference is Apex Integration Services