Pages

Tuesday, August 16, 2016

Salesforce: Data Import Wizard to update Account

Data Import Wizard may not be so famous compare to Data Loader or other 3rd party tools, such as: dataloader.io or workbench. But, for simplicity and to be used by super user (not system admin), Data Import Wizard may be an option.

Use case: super user need to mass update certain fields. List View is not an option because the fields will be update with different values and for multiple fields.

Step-by-Step:
1. Navigate to Data Import Wizard menu: Setup | Data Management | Data Import Wizard

2. Click Launch Wizard! button

3. Select "Accounts and Contacts" under Standard object, if the user does not see "Accounts and
Contacts", make sure the user have Import Personal Contacts permission.


4. Select "Update existing records" under "What do you want to do?"

5. Leave "Match Contact by" to default value since we are not updating Contact

6. Change "Match Account by" to "Salesforce.com ID" since I have Salesforce Account Id

7. Make sure to select "Update existing Account information"


8. Drag CSV file to upload

9. Click Next to continue

10. Click Change link under "Account: Salesforce.com ID, Contact: Salesforce.com ID"



11. Remove Contact Id from mapping



12. Click Next, then Start Import button


Once done, user should get email from Salesforce with:

Subject: Salesforce import of "filename.csv" has finished. 999 rows were processed.
Content:
Your Accounts import is complete. Here are your results:
Accounts Created: 0
Accounts Updated: 999
Accounts Failed:  0 (We couldn't import these due to errors.) Processed job information for
imported Accounts: No link is available because you do not have the correct permissions to
manage Bulk Api jobs.

If user have Bulk Api jobs permission, they will see:
Accounts Failed:  0 (We couldn't import these due to errors.) Processed job information for
imported Accounts: https://na3.salesforce.com/75017000001eNj3AAE?fromEmail=1


Permissions
- Permission to manage Bulk Api jobs is Manage Data Integrations
- User will only see Data Import Wizard setup menu from Setup | Data Management, if only user have any of the following permissions:
  • Import Custom Objects
  • Import Leads
  • Import Personal Contacts
  • Import Solutions

Reference:

Blog last update: 23 Oct 2016


Sunday, August 14, 2016

Salesforce: Validation Rule in Workflow and Process Builder

Based on Salesforce Triggers and Order of Execution "If the record was updated with workflow field updates, fires before update triggers and after update triggers one more time (and only one more time), in addition to standard validations. Custom validation rules, duplicate rules, and escalation rules are not run again."

This mean, when a user hit save button, all Validation Rules will fired, if nothing fit into custom validation rules defined, workflow will kick-in and if there is any field update, validation rules will not fired again.



Rule: Account Number should be not blank, but workflow able to set Account Number to blank.

Scenario: Initially Account Number is not blank, user edit the Account, and Workflow rule will fired with Field Update action to set Account Number to blank.

Let's debug this workflow:
37.0 APEX_CODE,DEBUG;APEX_PROFILING,DEBUG;CALLOUT,DEBUG;DB,DEBUG;SYSTEM,DEBUG;VALIDATION,DEBUG;VISUALFORCE,DEBUG;WORKFLOW,DEBUG
14:23:35.0 (82155)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
14:23:35.0 (114745)|EXECUTION_STARTED
14:23:35.0 (120056)|CODE_UNIT_STARTED|[EXTERNAL]|Validation:Account:0012800000gyUy6
14:23:35.0 (130074)|VALIDATION_RULE|03d28000000EPIz|Validation_Rule_Check_Account_Number
14:23:35.0 (228425)|VALIDATION_FORMULA|ISBLANK( AccountNumber )|AccountNumber=4
14:23:35.0 (237103)|VALIDATION_PASS
14:23:35.0 (269418)|CODE_UNIT_FINISHED|Validation:Account:0012800000gyUy6
14:23:35.0 (274425)|EXECUTION_FINISHED
14:23:35.30 (30037797)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
14:23:35.30 (30056259)|EXECUTION_STARTED
14:23:35.30 (30061600)|CODE_UNIT_STARTED|[EXTERNAL]|Workflow:Account
14:23:35.30 (50813798)|WF_RULE_EVAL_BEGIN|Workflow
14:23:35.30 (50860348)|WF_CRITERIA_BEGIN|[Account: Ke 4 0012800000gyUy6]|Account Name not blank|01Q28000000Cc09|ON_ALL_CHANGES|0
14:23:35.30 (54819564)|WF_RULE_FILTER|[Account : Account Name not equal to ]
14:23:35.30 (54838482)|WF_RULE_EVAL_VALUE|Ke 4
14:23:35.30 (54843398)|WF_CRITERIA_END|true
14:23:35.30 (73750806)|WF_SPOOL_ACTION_BEGIN|Workflow
14:23:35.30 (73896415)|WF_FIELD_UPDATE|[Account: Ke 4 0012800000gyUy6]|Field:Account: Account Number|Value:null|Id=04Y280000002Bz8|CurrentRule:Account Name not blank (Id=01Q28000000Cc09)
14:23:35.30 (73918213)|WF_ACTION| Field Update: 1;
14:23:35.30 (73920533)|WF_RULE_EVAL_END
14:23:35.30 (88370151)|WF_ACTIONS_END| Field Update: 1;
14:23:35.30 (88385818)|CODE_UNIT_FINISHED|Workflow:Account
14:23:35.30 (88392301)|EXECUTION_FINISHED

legend:
  • Red highlight 03d28000000EPIz = Validation Rule Id
  • Orange highlight 01Q28000000Cc09 = Workflow Rule Id
  • Cyan highlight 04Y280000002Bz8 = Field Update action Id
After workflow action end, execution finished.


Let's convert above Workflow rule to Process Builder with Update Records action, it should be pretty straight forward, let see the result when we edit and save the Account from Salesforce UI:

Workflow Action Failed to Trigger Flow
The record couldn’t be saved because it failed to trigger a flow. A flow trigger failed to execute the flow with version ID 30128000000AgZ7. Contact your administrator for help.

Click here to return to the previous page. 

Note: 301 prefix ID above is refer to an object call "InteractionDefinitionVersion", but you cannot easily know this Id is refer to which Process Builder. 

Instead of getting the same result, we will see above error message. But, only admin who create the Process Builder / Flow will receive fault email with more detail information, such as:

An error occurred at element myRule_1_A1 (FlowRecordUpdate).
UPDATE --- UPDATE FAILED --- ERRORS : (FIELD_CUSTOM_VALIDATION_EXCEPTION) Validation Rule Account_Number is blank
________________________________________
This report lists the elements that the flow interview executed. The report is a beta feature.
We welcome your feedback on IdeaExchange.
Flow Details
Flow Name: Account_Name_not_blank
Type: Workflow
Version: 1
Status: Active
Flow Interview Details
Interview Label: Account_Name_not_blank-1_Account
Current User: ABC Playground (00528000002ILq8)
Start time: 8/14/2016 2:34 PM
Duration: 0 seconds
How the Interview Started
Admin Playground (00528000002ILq8) started the flow interview.
Some of this flow's variables were set when the interview started.
myVariable_old = 0012800000gyUy6AAE
myVariable_current = 0012800000gyUy6AAE
ASSIGNMENT: myVariable_waitStartTimeAssignment
{!myVariable_waitStartTimeVariable} Equals {!Flow.CurrentDateTime}
Result
{!myVariable_waitStartTimeVariable} = "8/14/2016 2:34 PM"
DECISION: myDecision
Executed this outcome: myRule_1
Outcome conditions: and
1. {!myVariable_current.Name} (Ke 4) Does not equal null
Logic: All conditions must be true (AND)
RECORD UPDATE: myRule_1_A1
Find all Account records where:
Id Equals {!myVariable_current.Id} (0012800000gyUy6AAE)
Update the records’ field values.
AccountNumber = 
Result
Failed to update records that meet the filter criteria.
________________________________________
Error Occurred: UPDATE --- UPDATE FAILED --- ERRORS : (FIELD_CUSTOM_VALIDATION_EXCEPTION) Validation Rule Account_Number is blank 

legend:
- Red text = Process Builder API Name
- Blue text = Validation Rule error message

Prefix 301 can be referred to a Process Builder or Flow. The workaround is to use Flow Designer to check if that Id is refer to which Process Builder or Flow -- remember that Process Builder is using Flow as it engine. Follow this Url https://na3.salesforce.com/designer/designer.apexp#Id=30128000000AgZ7, change na3 to your salesforce instance.

In my sample, I see Flow Designer below, it show the Process Builder is Account Name not blank.



Let's analyze this Process Builder debug log:

37.0 APEX_CODE,DEBUG;APEX_PROFILING,DEBUG;CALLOUT,DEBUG;DB,DEBUG;SYSTEM,DEBUG;VALIDATION,DEBUG;VISUALFORCE,DEBUG;WORKFLOW,DEBUG
14:34:47.0 (558162)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
14:34:47.0 (592212)|EXECUTION_STARTED
14:34:47.0 (598100)|CODE_UNIT_STARTED|[EXTERNAL]|Validation:Account:0012800000gyUy6
14:34:47.0 (606907)|VALIDATION_RULE|03d28000000EPIz|Validation_Rule_Check_Account_Number
14:34:47.0 (776191)|VALIDATION_FORMULA|ISBLANK( AccountNumber )|AccountNumber=4
14:34:47.0 (781498)|VALIDATION_PASS
14:34:47.0 (804187)|CODE_UNIT_FINISHED|Validation:Account:0012800000gyUy6
14:34:47.0 (809205)|EXECUTION_FINISHED
14:34:47.59 (59552281)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
14:34:47.59 (59569571)|EXECUTION_STARTED
14:34:47.59 (59575357)|CODE_UNIT_STARTED|[EXTERNAL]|Workflow:Account
14:34:47.59 (82062195)|WF_RULE_EVAL_BEGIN|Workflow
14:34:47.59 (82101694)|WF_CRITERIA_BEGIN|[Account: Ke 4 0012800000gyUy6]|Account_Name_not_blank30128000000AgZ7|01Q28000000Cc0E|ON_ALL_CHANGES|0
14:34:47.59 (82345496)|WF_FORMULA|Formula:ENCODED:[treatNullAsNull]true|Values:
14:34:47.59 (82352535)|WF_CRITERIA_END|true
14:34:47.59 (97686928)|WF_SPOOL_ACTION_BEGIN|Workflow
14:34:47.59 (97718850)|WF_ACTION| Flow Trigger: 1;
14:34:47.59 (97721792)|WF_RULE_EVAL_END
14:34:47.59 (103758514)|WF_FLOW_ACTION_BEGIN|09L28000000Dx9D
14:34:47.105 (105390847)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
14:34:47.105 (105416019)|FLOW_CREATE_INTERVIEW_BEGIN|00D28000001GqzS|30028000000DADN|30128000000AgZ7
14:34:47.105 (328513776)|FLOW_CREATE_INTERVIEW_END|2218d9911a59768798c816adb579156879d9ea4-3fe3|Account Name not blank
14:34:47.328 (328991140)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
14:34:47.328 (329016464)|FLOW_START_INTERVIEWS_BEGIN|1
14:34:47.328 (356679048)|FLOW_START_INTERVIEW_BEGIN|2218d9911a59768798c816adb579156879d9ea4-3fe3|Account Name not blank
14:34:47.328 (357910736)|FLOW_START_INTERVIEW_END|2218d9911a59768798c816adb579156879d9ea4-3fe3|Account Name not blank
14:34:47.632 (632463208)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
14:34:47.632 (632488241)|CODE_UNIT_STARTED|[EXTERNAL]|Validation:Account:0012800000gyUy6
14:34:47.632 (632503881)|VALIDATION_RULE|03d28000000EPIz|Validation_Rule_Check_Account_Number
14:34:47.632 (632613125)|VALIDATION_FORMULA|ISBLANK( AccountNumber )|AccountNumber=null
14:34:47.632 (632625147)|VALIDATION_FAIL
14:34:47.632 (632678806)|CODE_UNIT_FINISHED|Validation:Account:0012800000gyUy6
14:34:47.328 (634577332)|FLOW_ELEMENT_ERROR|UPDATE --- UPDATE FAILED ---  ERRORS :  (FIELD_CUSTOM_VALIDATION_EXCEPTION) Validation Rule Account_Number is blank, |FlowRecordUpdate|myRule_1_A1
14:34:47.328 (663434606)|FLOW_START_INTERVIEWS_END|1
14:34:47.59 (663505014)|WF_FLOW_ACTION_ERROR|09L28000000Dx9D|30028000000DADN|Error executing flow: Account_Name_not_blank, FlowDefId: 30028000000DADN, FlowVersionId: 30128000000AgZ7
14:34:47.59 (663520003)|WF_FLOW_ACTION_ERROR_DETAIL|<b>An unhandled fault has occurred in this flow</b><br>An unhandled fault has occurred while processing the flow.  Please contact your system administrator for more information.
14:34:47.59 (663596389)|WF_FLOW_ACTION_END|09L28000000Dx9D
14:34:47.59 (663613069)|CODE_UNIT_FINISHED|Workflow:Account
14:34:47.59 (665545797)|EXECUTION_FINISHED

legend:
  • Red highlight 03d28000000EPIz = Validation Rule Id, this is the same validation rule
  • Green highlight 30128000000AgZ7= Interaction Definition Version Id, this is the Id show in error message (cannot open directly)
  • Orange highlight 01Q28000000Cc0E = workflow Id, (cannot open directly, because this is Flow)
  • Blue highlight 09L28000000Dx9D = Action Flow Id  (cannot open directly)
  • Yellow highlight 30028000000DADN = Flow Definition Id (cannot open directly)

If we analyze the log above, within Flow Action, it will call Validation Rule and this will stop Process Builder to update record.

Let's compare to a successful Process Builder with Record Update and Validation Rule.
37.0 APEX_CODE,FINEST;APEX_PROFILING,INFO;CALLOUT,INFO;DB,INFO;SYSTEM,DEBUG;VALIDATION,INFO;VISUALFORCE,INFO;WORKFLOW,INFO
15:24:54.0 (95867)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
15:24:54.0 (129750)|EXECUTION_STARTED
15:24:54.0 (135441)|CODE_UNIT_STARTED|[EXTERNAL]|Validation:Account:0012800000gyUy6
15:24:54.0 (146785)|VALIDATION_RULE|03d28000000EPIz|Validation_Rule_Check_Account_Number
15:24:54.0 (309565)|VALIDATION_FORMULA|ISBLANK( Name )|Name=Ke 4
15:24:54.0 (314519)|VALIDATION_PASS
15:24:54.0 (334878)|CODE_UNIT_FINISHED|Validation:Account:0012800000gyUy6
15:24:54.0 (339757)|EXECUTION_FINISHED
15:24:54.30 (30239766)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
15:24:54.30 (30253350)|EXECUTION_STARTED
15:24:54.30 (30258265)|CODE_UNIT_STARTED|[EXTERNAL]|Workflow:Account
15:24:54.30 (42750062)|WF_RULE_EVAL_BEGIN|Workflow
15:24:54.30 (42795063)|WF_CRITERIA_BEGIN|[Account: Ke 4 0012800000gyUy6]|Account_Name_not_blank30128000000AgZ7|01Q28000000Cc0E|ON_ALL_CHANGES|0
15:24:54.30 (43029449)|WF_FORMULA|Formula:ENCODED:[treatNullAsNull]true|Values:
15:24:54.30 (43036888)|WF_CRITERIA_END|true
15:24:54.30 (44033222)|WF_SPOOL_ACTION_BEGIN|Workflow
15:24:54.30 (44056720)|WF_ACTION| Flow Trigger: 1;
15:24:54.30 (44058614)|WF_RULE_EVAL_END
15:24:54.30 (44682997)|WF_FLOW_ACTION_BEGIN|09L28000000Dx9D
15:24:54.46 (46193540)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
15:24:54.46 (46218553)|FLOW_CREATE_INTERVIEW_BEGIN|00D28000001GqzS|30028000000DADN|30128000000AgZ7
15:24:54.46 (47747434)|FLOW_CREATE_INTERVIEW_END|1940b7d42414e1d9dcfb404801f715687e6cde0-3f6f|Account Name not blank
15:24:54.48 (48073487)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
15:24:54.48 (48088578)|FLOW_START_INTERVIEWS_BEGIN|1
15:24:54.48 (48766530)|FLOW_START_INTERVIEW_BEGIN|1940b7d42414e1d9dcfb404801f715687e6cde0-3f6f|Account Name not blank
15:24:54.48 (49602259)|FLOW_START_INTERVIEW_END|1940b7d42414e1d9dcfb404801f715687e6cde0-3f6f|Account Name not blank
15:24:54.91 (91366282)|USER_INFO|[EXTERNAL]|00528000002ILq8|abc@playground.com|Singapore Time|GMT+08:00
15:24:54.91 (91385208)|CODE_UNIT_STARTED|[EXTERNAL]|Validation:Account:0012800000gyUy6
15:24:54.91 (91397228)|VALIDATION_RULE|03d28000000EPIz|Validation_Rule_Check_Account_Number
15:24:54.91 (91481561)|VALIDATION_FORMULA|ISBLANK( Name )|Name=Ke 4
15:24:54.91 (91487538)|VALIDATION_PASS
15:24:54.91 (91509480)|CODE_UNIT_FINISHED|Validation:Account:0012800000gyUy6
15:24:54.48 (109154474)|FLOW_START_INTERVIEWS_END|1
15:24:54.30 (110713941)|WF_FLOW_ACTION_END|09L28000000Dx9D
15:24:54.30 (110757222)|WF_RULE_EVAL_BEGIN|Workflow
15:24:54.30 (110768818)|WF_SPOOL_ACTION_BEGIN|Workflow
15:24:54.30 (110774246)|WF_ACTION| None
15:24:54.30 (110776310)|WF_RULE_EVAL_END
15:24:54.30 (110844992)|WF_ACTIONS_END| Flow Trigger: 1;
15:24:54.30 (110852765)|CODE_UNIT_FINISHED|Workflow:Account
15:24:54.30 (112112867)|EXECUTION_FINISHED




Reference:

Tuesday, August 9, 2016

Salesforce: Auto Enable Marketing User

Use Case: some user need to be auto enable with Marketing User in the user detail because they need to add Contact / Lead to Campaign, see this blog for detail.

Solution:
1. Create Auto-launched Flow
2. Create Process Builder

Auto-launched Flow
We need this flow called by Process Builder because field UserPermissionsMarketingUser in User object is not accessible from Process Builder, you need to have a Record Update process with a variable use as input of User Id.

Vote for this idea to allow Process Builder able to access all fields in the object.


Process Builder
With Process Builder on User creation, call above Flow and pass the User Id to that flow.



With simple Process Builder + Auto-launched Flow above, new user created will be have Marketing User auto check, you can add criteria in Process Builder if need to evaluate certain Profile or Role or etc.

Note: deactivate and re-activate user will not effect Marketing User checkbox, so it is not necessary to run the Process Builder on user update.



Monday, August 8, 2016

#MidwestDreamin16 through my lens

CHICAGO | Navy Pier | 21-22 July 2016

Continuing Salesforce MVP Event #MVPChicago16, within the same week, I attend my 1st ever Salesforce event run by community #MidwestDreamin16 in Chicago, and it is awesome!!

With almost 1,000 people fly from all over Unites States, more than 35 partners (including SalesforceA and Salesforce trust) show case their product, and many break out sessions present by community, including: end users, admins, and developer sessions. If you have ever been in Dreamforce, this is pretty similar to Dreamforce, but in smaller scale and run by community.

I also have a chance to meet with many of my virtual friends in-person.

yay... I am staff!


sessions agenda



volunteering with Andy on staffing Ask the Experts




AppExchange Demo Jam

with fellow UK MVPs

performance by Apex & the Limits

trolley from Intercontinental hotel to Navy Pier

oh no gun!

breakout session

Circles of Success

Awesome admin lunch time trivia games

Congratulations to all #MidwestDreamin16 team for such a great conference. Thanks @ericdresh @DZieg11 @DeniseCarbone4 @NickersUniverse et all, great job!!



Twitter hash-tag: #MidwestDreamin16

Other blogs from other community member or partner: 

#MVPChicago16 through my lens

CHICAGO | W Hotel | 18 - 19 July 2016


This would be my 3rd MVP event, here the re-cap for 2014 and 2015 Summit. It's always good to see MVP peeps from around the world in a place, and this time it is hold in Chicago, yay my first time visit this town.

I'll not write much about the event content as we are under NDA, but in very short, it is about lightning, Lighting, LIGHTNING !!!

Here are some great moments to share:

1st day - registration


with fellow Japanese MVPs


some of Asian MVPs

with #1 salesforce blogger

beach appreciation dinner


starting 2nd day







this is what we call Ohana

Navy Pier dinner




Thank you Salesforce for hosting and make this event happened, and yes Chicago is gorgeous !!!




Twitter hash-tag: #MVPChicago16


Blog from other MVP or Partner: Salesforce MVP Chicago SUMMIT 2016


Thursday, July 7, 2016

Getting Started with Salesforce Visualforce Email Template

Follow the wizard to create Visualforce email template from: SetupCommunication TemplatesEmail Templates | New Template and select Visualforce in Step 1 "Choose the type of email template you would like to create". You will get a basic Visualforce email template with Subject, Recipient Type (Contact, Lead, or User), and Related To Type Object.

<messaging:emailTemplate subject="Account Name" recipientType="User" relatedToType="Account">
<messaging:plainTextEmailBody >
Congratulations!
This is your new Visualforce Email Template.
</messaging:plainTextEmailBody>
</messaging:emailTemplate>



Few simple things you can modify the email template without programming:

1. Change subject to include merge field
Example: <messaging:emailTemplate subject="Account Name" recipientType="User" relatedToType="Account">
to
<messaging:emailTemplate subject="Account Name - {!relatedTo.Name}" recipientType="User" relatedToType="Account">

2. Add a ReplyTo address 
Example: <messaging:emailTemplate subject="Account Name - {!relatedTo.Name}" recipientType="User" relatedToType="Account">
to
<messaging:emailTemplate subject="Account Name - {!relatedTo.Name}" recipientType="User" relatedToType="Account" replyTo="support@acme.com">

3. Change Email format from Text to HTML
Example: <messaging:plainTextEmailBody > to <messaging:htmlEmailBody >

4. Add new line
As this is now HTML email, you can add <br/> for new line.

5. You can add recipient name
Example: <p>Dear {!recipient.name},</p>

6. You can add related field, including custom field
Example: Account Owner: {!relatedTo.CreatedBy.Name}<br/><br/>

7. You can add full URL as email body
Example: URL detail: <a href="{!LEFT($Api.Partner_Server_URL_370, FIND(".com/",$Api.Partner_Server_URL_370)+3)}/{!relatedTo.Id}">{!relatedTo.Name}</a>

8. You can add logic to include field
Example: <apex:outputPanel rendered="{!relatedTo.RecordType.Name='Customer' || relatedTo.RecordType.Name='Alumni'}">
Total Revenue: {!relatedTo.Total_Revenue__c}<br/><br/>
</apex:outputPanel>


Sample of complete email template:
<messaging:emailTemplate subject="Account Name - {!relatedTo.Name}" recipientType="User" relatedToType="Account" replyTo="support@acme.com">
<messaging:htmlEmailBody >
<p>Dear {!recipient.name},</p>
Account Owner: {!relatedTo.CreatedBy.Name}<br/><br/>

<apex:outputPanel rendered="{!relatedTo.RecordType.Name='Customer' || relatedTo.RecordType.Name='Alumni'}">
Total Revenue: {!relatedTo.Total_Revenue__c}<br/><br/>
</apex:outputPanel>

URL detail: <a href="{!LEFT($Api.Partner_Server_URL_370, FIND(".com/",$Api.Partner_Server_URL_370)+3)}/{!relatedTo.Id}">{!relatedTo.Name}</a>

</messaging:htmlEmailBody>
</messaging:emailTemplate>


For advance page with apex controller, you can use Visualforce Component, and use apex class as Controller.
Example: <c:AccountNotification AcctId="{!relatedTo.Id}"/>

In the component specify the apex class as controller
<apex:component controller="AccountEmailNotify" access="global">
</apex:component>


Reference:

Page-level ad