こんにちは!Kentarouです!
今回は、「freee for Salesforceを使わずにSalesforceとfreeeを連携させる方法」の”後編”です!!まだ”前編”見ていない方は下記ブログをご覧ください。
さっそく今回の記事の内容をざっと紹介します。
ゴールは、Salesforceの取引先をfreeeに連携させること!!
前編では、freeeのアプリケーションを作成し、Salesforceにて外部接続設定をしました。
後編では、Salesforceでコードを書き、Salesforceの取引先をfreeeに連携していきます。
それでは、さっそくいきましょう!
Salesforceの取引先をfreeeに連携する方法 (後編)
1. カスタム設定を作成しfreee事業所IDを設定する
1-1.カスタム設定の設定画面を開く。
1-2. [新規]をクリックし、表示ラベルとオブジェクト名を設定する。
- 表示ラベル:freee連携情報
- オブジェクト名:Freee_Info
1-3.カスタム項目の[新規]をクリックし、カスタム項目を作成する。
- データ型:テキスト
- 項目の表示ラベル:freee事業所ID
- 文字数:255
- 項目名:Freee_Company_Id
1-4.[Manage]をクリックし、作成したカスタム項目にfreee事業所IDを設定し保存する。
2. 取引先オブジェクトにfreeeに連携したいカスタム項目を作成する
freee連携項目 (例)
Salesforce表示名 | Salesforce項目名 |
取引先名 | Name |
SF取引先コード | Sf_Account_Code__c |
freee取引先ID | Freee_Partner_Id__c |
ショートカット1 | Shortcut1__c |
ショートカット2 | Shortcut2__c |
事業所種別 | OrgCode__c |
地域 | CountryCode__c |
正式名称 | LongName__c |
カナ名称 | NameKana__c |
敬称 | DefaultTitle__c |
電話 | Phone |
担当者 氏名 | ContactName__c |
担当者 メールアドレス | ContactEmail__c |
郵便番号 | BillingPostalCode |
都道府県コード | BillingState |
市区町村・番地 | BillingCity + BillingStreet[0] |
建物名・部屋番号など | BillingStreet[1] |
請求書送付方法 | SendingMethod__c |
銀行名 | BankName__c |
銀行名(カナ) | BankNameKana__c |
銀行コード | BankCode__c |
支店名 | BankBranchName__c |
支店名(カナ) | BankBranchKana__c |
支店番号 | BankBranchCode__c |
口座種別 | BankAccountType__c |
口座番号 | BankAccountNumber__c |
受取人名(カナ) | BankAccountKana__c |
受取人名 | BankAccountName__c |
3. Salesforceのコードを書く
3-1.pdAccount2Freee.cmpを作成する
<aura:component controller="pdAccount2Freee"
implements="force:lightningQuickActionWithoutHeader,force:hasRecordId">
<aura:attribute name="account" type="Account" />
<aura:attribute name="isProgress" type="Boolean" default="false" />
<aura:attribute name="isDone" type="Boolean" default="false" />
<aura:attribute name="isError" type="Boolean" default="false" />
<aura:attribute name="errorMessage" type="String" />
<aura:handler name="init" value="{!this}" action="{!c.doRegist}" />
<div class="slds-page-header" role="banner">
<h1 class="slds-page-header__title slds-m-right_small14
slds-truncate slds-align-left">freee取引先連携</h1>
</div>
<div class="slds-container">
<aura:renderIf isTrue="{!v.isProgress}">
<lightning:spinner alternativeText="Progress" size="medium" />
</aura:renderIf>
<aura:renderIf isTrue="{!v.isDone}">
<lightning:icon iconName="utility:check" size="large" />
<p>連携が完了しました</p>
</aura:renderIf>
<aura:renderIf isTrue="{!v.isError}">
<lightning:icon iconName="utility:error" variant="error" size="large" />
<p style="white-space: pre;">{!v.errorMessage}</p>
</aura:renderIf>
</div>
</aura:component>
3-2.pdAccount2FreeeController.jsを作成する
({
doRegist : function(component, event, helper) {
component.set("v.isProgress", true);
const account2Freee = component.get("c.account2Freee");
account2Freee.setParams({
"accountId": component.get("v.recordId")
});
account2Freee.setCallback(this, (res) => {
try {
const state = res.getState();
if (state === "SUCCESS") {
component.set("v.isDone", true);
}
else {
let message = 'エラーが発生しました\n';
console.log(res.getError());
message += res.getError()[0].message;
component.set("v.errorMessage", message);
component.set("v.isError", true);
}
} finally {
component.set("v.isProgress", false);
}
});
$A.enqueueAction(account2Freee);
}
})
3-3.pdAccount2Freee.clsを作成する
public with sharing class pdAccount2Freee {
public virtual class BaseException extends Exception {}
public class OtherException extends BaseException {}
@AuraEnabled
public static void account2Freee(Id accountId) {
Freee_Info__c freeeInfo = Freee_Info__c.getOrgDefaults();
if(String.isBlank(freeeInfo.Freee_Company_Id__c)) {
throw new OtherException('Salesforceに事業所IDが設定されていません。');
}
Account acc = [
select
Id,
Name,
BillingPostalCode,
BillingState,
BillingCity,
BillingStreet,
Phone,
BankAccountKana__c,
BankAccountName__c,
BankAccountNumber__c,
BankAccountType__c,
BankBranchCode__c,
BankBranchKana__c,
BankBranchName__c,
BankCode__c,
BankName__c,
BankNameKana__c,
ContactName__c,
ContactEmail__c,
CountryCode__c,
DefaultTitle__c,
Sf_Account_Code__c,
Freee_Partner_Id__c,
LongName__c,
NameKana__c,
OrgCode__c,
SendingMethod__c,
Shortcut1__c,
Shortcut2__c
from
Account
where
Id = :accountId
];
Integer companyId = getCompany(freeeInfo.Freee_Company_Id__c);
if(!isExist(companyId, acc)) {
registPartner(companyId, acc);
} else {
updatePartner(companyId, acc);
}
}
private static Integer getCompany(String companyId) {
Http http = new Http();
String path = 'callout:Freee/companies/' + companyId;
HttpRequest req = new HttpRequest();
req.setEndpoint(path);
req.setMethod('GET');
HttpResponse res = http.send(req);
if (res.getStatusCode() == 200) {
return Integer.valueOf(companyId);
}
else {
throw new OtherException(getApiErrorMessage(res.getBody()));
}
}
private static Boolean isExist(Integer companyId, Account acc) {
if(acc.Freee_Partner_Id__c == null) return false;
Http http = new Http();
String path = 'callout:Freee/partners/' + acc.Freee_Partner_Id__c;
String parameters = 'company_id=' + companyId;
HttpRequest req = new HttpRequest();
req.setEndpoint(path + '?' + parameters);
req.setMethod('GET');
HttpResponse res = http.send(req);
if(res.getStatusCode() == 200) {
return true;
}
else if(res.getStatusCode() == 404){
return false;
}
else {
throw new OtherException(getApiErrorMessage(res.getBody()));
}
}
private static void registPartner(Integer companyId, Account acc) {
Http http = new Http();
String path = 'callout:Freee/partners';
String parameters = createParameters(companyId, acc, /* isInsert = */ true);
HttpRequest req = new HttpRequest();
req.setEndpoint(path);
req.setMethod('POST');
req.setHeader('Content-Type', 'application/json');
req.setBody(parameters);
HttpResponse res = http.send(req);
if (res.getStatusCode() == 201) {
Map<String, Object> mapBody = (Map<String, Object>)JSON.deserializeUntyped(res.getBody());
Map<String, Object> partner = (Map<String, Object>)mapBody.get('partner');
Object partnerId = (Object)partner.get('id');
acc.Freee_Partner_Id__c = String.valueOf(partnerId);
update acc;
}
else {
throw new OtherException(getApiErrorMessage(res.getBody()));
}
}
private static void updatePartner(Integer companyId, Account acc) {
Http http = new Http();
String path = 'callout:Freee/partners/code/' + acc.Sf_Account_Code__c;
String parameters = createParameters(companyId, acc, /* isInsert = */ false);
HttpRequest req = new HttpRequest();
req.setEndpoint(path);
req.setMethod('PUT');
req.setHeader('Content-Type', 'application/json');
req.setBody(parameters);
HttpResponse res = http.send(req);
if (res.getStatusCode() != 200) {
throw new OtherException(getApiErrorMessage(res.getBody()));
}
}
private static String getApiErrorMessage(String body) {
Map<String, Object> mapError = (Map<String, Object>)JSON.deserializeUntyped(body);
List<Object> lstError = (List<Object>)(mapError.get('errors'));
String errorMessage = 'Freee APIでエラーが発生しました\n';
if(lstError != null) {
for(Object mapErrorContent : lstError) {
List<Object> lstMessage = (List<Object>)((Map<String, Object>)mapErrorContent).get('messages');
for(Object message : lstMessage) {
errorMessage += message.toString() + '\n';
}
}
} else {
String message = (String)((Map<String, Object>)mapError).get('message');
errorMessage += message + '\n';
}
return errorMessage;
}
private static String createParameters(Integer companyId, Account acc, Boolean isInsert) {
String parameters = '{';
parameters += '"company_id": ' + companyId + ',';
parameters += '"name": "' + acc.Name + '",';
if(isInsert) {
parameters += '"code": "' + acc.Sf_Account_Code__c + '",';
}
parameters += '"shortcut1": "' + (String.isBlank(acc.Shortcut1__c) ? '' : acc.Shortcut1__c) + '",';
parameters += '"shortcut2": "' + (String.isBlank(acc.Shortcut2__c) ? '' : acc.Shortcut2__c) + '",';
Integer orgCode = getOrgCode(acc.OrgCode__c);
parameters += '"org_code": ' + orgCode + ',';
String countryCode = getCountryCode(acc.CountryCode__c);
if(countryCode != null) {
parameters += '"country_code": "' + (String.isBlank(countryCode) ? '' : countryCode) + '",';
}
parameters += '"long_name": "' + (String.isBlank(acc.LongName__c) ? '' : acc.LongName__c) + '",';
parameters += '"name_kana": "' + (String.isBlank(acc.NameKana__c) ? '' : acc.NameKana__c) + '",';
parameters += '"default_title": "' + (String.isBlank(acc.DefaultTitle__c) ? '' : acc.DefaultTitle__c) + '",';
parameters += '"phone": "' + (String.isBlank(acc.Phone) ? '' : acc.Phone) + '",';
parameters += '"contact_name": "' + (String.isBlank(acc.ContactName__c) ? '' : acc.ContactName__c) + '",';
parameters += '"email": "' + (String.isBlank(acc.ContactEmail__c) ? '' : acc.ContactEmail__c) + '",';
parameters += '"address_attributes": {';
parameters += '"zipcode": "' + (String.isBlank(acc.BillingPostalCode) ? '' : acc.BillingPostalCode) + '",';
Integer prefCode = getPrefCode(acc.BillingState);
if(prefCode != null) {
parameters += '"prefecture_code": ' + prefCode + ',';
}
if(String.isBlank(acc.BillingStreet)) {
parameters += '"street_name1": "' + (String.isBlank(acc.BillingCity) ? '' : acc.BillingCity) + '",';
parameters += '"street_name2": ""';
} else {
String[] billingStreet = acc.BillingStreet.split('\r\n');
if(billingStreet.size() >= 2) {
parameters += '"street_name1": "' + (String.isBlank(acc.BillingCity) ? '' : acc.BillingCity) + (String.isBlank(billingStreet[0]) ? '' : billingStreet[0]) + '",';
parameters += '"street_name2": "' + (String.isBlank(billingStreet[1]) ? '' : billingStreet[1]) + '"';
} else if (billingStreet.size() == 1) {
parameters += '"street_name1": "' + (String.isBlank(acc.BillingCity) ? '' : acc.BillingCity) + (String.isBlank(billingStreet[0]) ? '' : billingStreet[0]) + '",';
parameters += '"street_name2": ""';
}
}
parameters += '},';
String sendingMethod = getSendingMethodCode(acc.SendingMethod__c);
if(sendingMethod != null){
parameters += '"partner_doc_setting_attributes": {';
parameters += '"sending_method": "' + (String.isBlank(sendingMethod) ? '' : sendingMethod) + '"';
parameters += '},';
}
parameters += '"partner_bank_account_attributes": {';
parameters += '"bank_name": "' + (String.isBlank(acc.BankName__c) ? '' : acc.BankName__c) + '",';
parameters += '"bank_name_kana": "' + (String.isBlank(acc.BankNameKana__c) ? '' : acc.BankNameKana__c) + '",';
parameters += '"bank_code": "' + (String.isBlank(acc.BankCode__c) ? '' : acc.BankCode__c) + '",';
parameters += '"branch_name": "' + (String.isBlank(acc.BankBranchName__c) ? '' : acc.BankBranchName__c) + '",';
parameters += '"branch_kana": "' + (String.isBlank(acc.BankBranchKana__c) ? '' : acc.BankBranchKana__c) + '",';
parameters += '"branch_code": "' + (String.isBlank(acc.BankBranchCode__c) ? '' : acc.BankBranchCode__c) + '",';
String accountType = getBankAccountType(acc.BankAccountType__c);
if(accountType != null) {
parameters += '"account_type": "' + (String.isBlank(accountType) ? '' : accountType) + '",';
}
parameters += '"account_number": "' + (String.isBlank(acc.BankAccountNumber__c) ? '' : acc.BankAccountNumber__c) + '",';
parameters += '"account_name": "' + (String.isBlank(acc.BankAccountKana__c) ? '' : acc.BankAccountKana__c) + '",';
parameters += '"long_account_name": "' + (String.isBlank(acc.BankAccountName__c) ? '' : acc.BankAccountName__c) + '"';
parameters += '}';
parameters += '}';
return parameters;
}
private static Integer getOrgCode(string orgName) {
List<String> orgNames = new List<String> {
'法人',
'個人'
};
Integer orgCode = null;
for(Integer i = 0 ; i < orgNames.size() ; i++) {
if(orgNames[i] == orgName) {
orgCode = i + 1;
break;
}
}
return orgCode;
}
private static String getCountryCode(string countryName) {
if(countryName == '国内') return 'JP';
else if(countryName == '国外') return 'ZZ';
return null;
}
private static String getSendingMethodCode(string sendingMethodName) {
if(sendingMethodName == 'メール') return 'email';
else if(sendingMethodName == '郵送') return 'posting';
else if(sendingMethodName == 'メールと郵送') return 'email_and_posting';
return null;
}
private static String getBankAccountType(string bankAccountTypeName) {
if(bankAccountTypeName == '普通') return 'ordinary';
else if(bankAccountTypeName == '当座') return 'checking';
else if(bankAccountTypeName == '納税準備預金') return 'earmarked';
else if(bankAccountTypeName == '貯蓄') return 'savings';
else if(bankAccountTypeName == 'その他') return 'other';
return null;
}
private static Integer getPrefCode(String prefName) {
List<String> pref = new List<String> {
'北海道',
'青森県',
'岩手県',
'宮城県',
'秋田県',
'山形県',
'福島県',
'茨城県',
'栃木県',
'群馬県',
'埼玉県',
'千葉県',
'東京都',
'神奈川県',
'新潟県',
'富山県',
'石川県',
'福井県',
'山梨県',
'長野県',
'岐阜県',
'静岡県',
'愛知県',
'三重県',
'滋賀県',
'京都府',
'大阪府',
'兵庫県',
'奈良県',
'和歌山県',
'鳥取県',
'島根県',
'岡山県',
'広島県',
'山口県',
'徳島県',
'香川県',
'愛媛県',
'高知県',
'福岡県',
'佐賀県',
'長崎県',
'熊本県',
'大分県',
'宮崎県',
'鹿児島県',
'沖縄県'
};
Integer prefCode = null;
for(Integer i=0 ; i<pref.size() ; i++) {
if(pref[i] == prefName) {
prefCode = i;
break;
}
}
return prefCode;
}
}
4. アクションを作成する
取引先オブジェクトにて[新規アクション]をクリックし、アクションを作成する。
- アクション種別:Lightningコンポーネント
- Lightningコンポーネント:c:pdAccount2Freee
- 表示ラベル:freee連携
- 名前:Freee
5. クイックアクションをページレイアウトに追加する
取引先オブジェクトのページレイアウトにて[クイックアクション]をクリックし、[freee連携]をページレイアウトに追加する。
Salesforceの取引先をfreeeに連携してみる
freee for Salesforceを使わずにSalesforceの取引先をfreeeに連携してみました!!
Salesforceとfreeeを連携したい!
Salesforceの環境構築をしてほしい!
freeeについて教えてほしい!こんなご希望がある方は是非ご相談ください!#freee #Salesforce pic.twitter.com/gXjX5azWof
— 株式会社Crena (@create_new_air) December 19, 2021
おわりに
前編と後編にわたって「Salesforceの取引先をfreeeに連携する方法」をご紹介しました。
かなり工程が多く正直大変だと思います。。。。
ただ、今回紹介した「freeeが公開しているAPIを利用しSalesforceで開発する方法」であれば、freee for Salesforceが利用できないfreeeプランの方でもSalesforceとfreeeが連携できます!!
最後の最後に宣伝にはなりますが、、、
Salesforceとfreeeを連携したい!
Salesforceの環境構築をしてほしい!
freeeについて教えてほしい!
こんなご希望がある方は是非弊社までご相談ください!
kintoneアプリ開発のご依頼は株式会社Crenaへ
会社名 | 株式会社Crena(クレナ) |
---|---|
TEL | 043-388-8819 |
設立 | 2020年8月 |
代表取締役 | 平野 賢太郎 (Hirano Kentaro) |
事業内容 |
クラウドソリューション事業 システム開発事業 ビジネスソリューション事業 |
URL | https://create-new-air.com/ |