Salesforce
ODBC
kintone
PCA
CData

PCAクラウドの顧客データを他のSaaSに同期するC#アプリケーションを作成する


はじめに

本記事では、CData ODBCドライバを使って、PCA商魂・商管DXの顧客データを、Salesforce、kintoneと同期するWindowsフォームのC#アプリケーションを作成します。PCA商魂・商管DXは、ピー・シー・エー株式会社が提供するSaaS型の基幹業務ソフトです。


使用するCData製品

※こちらのリンクから30日の評価版、またはベータ版のインストーラを(.exe)をダウンロードできますので、インストールしてください。


前提


  • SalesforceのAPIを利用できる権限のアカウントを保有していること

  • kintoneのアカウントを保有しており、アプリテンプレートの「営業支援パック」でアプリを作成済みである

  • PCA商魂・商管DXのアカウントを保有していること

  • 上記の3つのODBCドライバがインストール済みであること

  • Visual Studio がインストール済みであること ※今回はVisual Studio Community 2017を使用しました


ODBCドライバのDSN設定

各ODBCドライバのインストール後、またはインストール先フォルダにある「ConfigureODBC」を起動すると、下記のような「DSN構成」画面が立ち上がりますので、それぞれ接続情報を入力します。

ConfigureODBC.png

各SaaSの接続情報は下記のとおりです。

PCA Salesの接続情報
設定項目

クライアントID
OAuth Client Id

クライアントシークレット
OAuth Client Secret

コールバックURL
Callback URL

デフォルトデータ領域
Default Data Area

プロダクトコード
Product Code

Web APIサーバのURL
Url

※コールバックURL、クライアントID、クライアントシークレットについては、PCA Web API 利用準備をご覧ください。

Salesforceの接続情報
設定項目

ユーザID
User

パスワード
Password

セキュリティートークン
Security Token

kintoneの接続情報
設定項目

ユーザID
User

パスワード
Password

kintoneのサブドメイン含むサイトURL
Url

※Urlは、https://***.cybozu.com/k/の「/k」は不要です


プロジェクト作成

Visual Studioで「Windowsフォームアプリケーション (.Net Framework)」プロジェクトを新規作成し、コンテナとコントロールを配置していきます。

ここで解説するプログラムは、こちらからダウンロードいただけます。


1.TableLayoutPanelコンテナ

Formのデザイナー画面で、Formを適当な大きさに拡大し、その中に「1行×3列」の「TableLayoutPanel」コンテナを配置します。サイズをFormいっぱいにするため「Dock」プロパティを「Fill」に設定してください。


2.TabControlコンテナ

作成したTableLayoutPanelコンテナの両端の列の中に「TabControl」コンテナを1つずつ配置します。この「TabControl」はどちらもサイズを最大にするため「Dock」プロパティを「Fill」に設定してください。また、左側のTabControlのタブページ数は1、右側のタブページ数は2に設定します。各タブページの見出し(Textプロパティ)は下記のように設定します。


  • フォーム左側タブページ1 : PCASales

  • フォーム右側タブページ1 : Saleforce

  • フォーム右側タブページ2 : kintone


3.DataGridViewの配置とデータソースの設定

これまでの作業でタブページが左側に1つ、右側に2つあります。各タブページの中に「DataGridView」を配置します。サイズは最大にするため「Dock」プロパティを「Fill」に設定してください。

この時点では、Formは下記のように表示されます。

InitialDesign.PNG

次に、3つの「DataGridView」にデータソースを設定します。下記のように「DataGridView」を選択した際に表示される「▶」をクリックして、「データソースの選択」の「(なし)」右の下矢印をクリックして、「プロジェクトデータソースの追加」リンクをクリックします。

DataGridViewDataSource_arrow.png

「データソース構成ウィザード」が立ち上がりますので「データベース」―「データセット」を選択して進みます。

DataSourceWizard.png

DataSourceWizard2.png

次の「データ接続の選択」ウィンドウでは「新しい接続」をクリックして、さらに「データソースの選択」ウィンドウから「Microsoft ODBCデータソース」を選択して「OK」ボタンをクリックします。

ODBCDataSource_arrow.png

続いて「接続の追加」ウィンドウでは「ユーザーデータソース名またはシステムデータソース名を使用」のドロップダウンリストから作成済みの今回使用するデータソースを選択して「テスト接続」をクリックします。テスト接続に成功したら「OK」ボタンをクリックします。3つのDataGridViewには、それぞれ下記のデータソースを指定します。


  • フォーム左側タブページ1 : CData PCASales Source

  • フォーム右側タブページ1 : CData Saleforce Source

  • フォーム右側タブページ2 : CData kintone Source

ODBCDataSource2_alpha3.png

ここでは何もせず「次へ」をクリックします。

DataSource4.png

プロジェクト内のアプリケーション構成ファイルに保存する接続文字列に適当な名前をつけて、「次へ」を進みます。

DataSourceWizard4.png

選択したデータソースが持つテーブルのリストが表示されるので適当なテーブルをチェックし、データセット名を入力して「完了」ボタンをクリックします。

DataSourceWizard5.png

3つの各データソースで使用するテーブルと、そのデータセット名は下記のとおりです。このデータセット名は、c#プログラムで参照するため、正しく設定してください。



  • PCA Sales


    • テーブル名 : MasterTMS

    • データセット名 : DataSetPCASales




  • Salesforce


    • テーブル名 : Account

    • データセット名 : DataSetSalesforce




  • kintone


    • テーブル名 : _顧客管理営業支援パック_

    • データセット名 : DataSetKintone



データソース設定が完了すると、下記のように、そのDataGridViewにヘッダー行が表示されます。

FormDesign2.png

このままでは不要な項目が多く見づらいため、各DataGridViewの不要なカラムを非表示にします。DataGridViewを選択した際に表示される「▶」をクリックして、そこで表示されるメニューから「列の編集」をクリックします。「列の編集」ウィンドウが開きますので、下記のように不要なフィールド名を選択後、「削除」ボタンをクリックします。

FormDesign4_arrow.png

各DataGridViewに表示するフィールドは下記のとおりです。



  • PCA Sales(フォーム左側)


    • TokuisakiCode

    • TokuisakiMei1

    • TokuisakiMei2

    • YubinBango

    • Jyusho1

    • Jyusho2

    • AitesakiTelNo

    • AitesakiFaxNo




  • Salesforce(フォーム右側のタブページ1)


    • Id

    • Name

    • BillingStreet

    • BillingPostalCode

    • Phone

    • Fax




  • kintone(フォーム右側のタブページ2)


    • RecordId

    • Revision

    • 郵便番号

    • 顧客名

    • 住所

    • TEL

    • FAX



以上の設定作業を、3つのDataGridViewに行ってください。完了後は「▶開始」ボタンをクリックして、アプリケーションを起動してみます。下記のように、各DataGridViewにデータソースがバインドされ、レコードの指定した項目のみが表示されます。

TestRun.png


ボタンの配置と同期処理の実装

Formのデザイナー画面で、真ん中の列に「Button」を配置します。そのボタンが中央に来るように「Anchor」プロパティを「None」に設定してください。また「Text」プロパティに“>>”と入力します。最後に、配置したボタンをダブルクリックし、クリックイベントハンドラを追加します。この時、エディターで「Form1.cs」が開かれますので、下記のコードをコピー&ペーストしてください。

        private void button1_Click(object sender, EventArgs e)

{
button1.Enabled = false;

if (tabControl2.SelectedTab.Text == "Salesforce")
{
PCAtoSalesforce();
}
else
{
PCAtoKintone();
}

button1.Enabled = true;

}

private void PCAtoSalesforce()
{
dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(o => o.Selected)
.Select((o, i) => new { Row = (DataSetPCASales.MasterTmsRow)((DataRowView)o.DataBoundItem).Row, Index = i })
.ToList()
.ForEach(selected =>
{
var name = selected.Row.TokuisakiMei1 + (selected.Row.IsTokuisakiMei2Null() ? "" : selected.Row.TokuisakiMei2);
var row = dataSetSalesforce.Account.Rows.Cast<DataSetSalesforce.AccountRow>()
.Where(o => o.Name == name)
.FirstOrDefault();

if (row is null)
{
row = dataSetSalesforce.Account.NewAccountRow();
//row.Id = "";
row.Id = selected.Index.ToString();
}

row.Name = name;
row.BillingPostalCode = selected.Row.IsYubinBangoNull() ? "" : selected.Row.YubinBango;
row.BillingStreet = (selected.Row.IsJyusyo1Null() ? "" : selected.Row.Jyusyo1) + (selected.Row.IsJyusyo2Null() ? "" : selected.Row.Jyusyo2);
row.Phone = selected.Row.IsAitesakiTelNoNull() ? "" : selected.Row.AitesakiTelNo;
row.Fax = selected.Row.IsAitesakiFaxNoNull() ? "" : selected.Row.AitesakiFaxNo;

if (row.Id == selected.Index.ToString())
{
dataSetSalesforce.Account.AddAccountRow(row);
}

accountTableAdapter.Update(dataSetSalesforce);
});

accountTableAdapter.Fill(dataSetSalesforce.Account);
}

private void PCAtoKintone()
{
dataGridView1.Rows.Cast<DataGridViewRow>()
.Where(o => o.Selected)
.Select((o, i) => new { Row = (DataSetPCASales.MasterTmsRow)((DataRowView)o.DataBoundItem).Row, Index = i })
.ToList()
.ForEach(selected =>
{
var name = selected.Row.TokuisakiMei1 + (selected.Row.IsTokuisakiMei2Null() ? "" : selected.Row.TokuisakiMei2);
var row = dataSetKintone._顧客管理_営業支援パック_.Rows.Cast<DataSetKintone._顧客管理_営業支援パック_Row>()
.Where(o => o.顧客名 == name)
.FirstOrDefault();

if (row is null)
{
row = dataSetKintone._顧客管理_営業支援パック_.New_顧客管理_営業支援パック_Row();
row.RecordId = selected.Index
+ (dataSetKintone._顧客管理_営業支援パック_.Rows.Cast<DataSetKintone._顧客管理_営業支援パック_Row>().Max(o => (int?)o.RecordId) ?? 0)
+ 1;
row.Revision = 0;
}

row.顧客名 = name;
row.郵便番号 = selected.Row.IsYubinBangoNull() ? "" : selected.Row.YubinBango;
row.住所 = (selected.Row.IsJyusyo1Null() ? "" : selected.Row.Jyusyo1) + (selected.Row.IsJyusyo2Null() ? "" : selected.Row.Jyusyo2);
row.TEL = selected.Row.IsAitesakiTelNoNull() ? "" : selected.Row.AitesakiTelNo;
row.FAX = selected.Row.IsAitesakiFaxNoNull() ? "" : selected.Row.AitesakiFaxNo;

if (row.Revision == 0)
{
dataSetKintone._顧客管理_営業支援パック_.Rows.Add(row);
}

顧客管理_営業支援パック_TableAdapter.Update(dataSetKintone);
});

顧客管理_営業支援パック_TableAdapter.Fill(dataSetKintone._顧客管理_営業支援パック_);
}

プロジェクトをリビルド後、「▶開始」ボタンをクリックし、アプリケーションを起動します。PCASalesのDataGridViewで適当な行を選択した状態で、“>>”ボタンをクリックしてください。右側の表示されているタブのDataGridViewにそのレコードが追加されれば成功です。今回のプログラムは、右側に表示中のDataGridViewにバインドされているデータソースに対して、データの追加・更新を行いますので、タブを切り替えて「Salesforce」と「kintone」双方で試してみてください。

SyncTest_arrow.png

「kintone」タブでは成功しますが、「Salesforce」タブでは、「列'IsDeleted'にnullsを使用することはできません」というエラーが発生し失敗します。

SalesforceError1.PNG

このエラーは、SalesforceタブのDataGridViewにバインドされたデータソース「DataSetSalesforce」のNull値を許容しないフィールドがNullのまま、レコードが追加されたことが原因です。今回扱うデータは会社名、郵便番号、住所、TEL、FAXのみで、その他のフィールドは初期化もしないため、原因となっている「DataSetSalesforce」のフィールドのプロパティを変更します。

Visual Studioの「ソリューションエクスプローラー」から「DataSetSalesforce.xsd」を右クリックし、表示されるメニューから「デザイナーの表示」を選択します。

xsdEditor.PNG

下記のようなペインが表示されます。ここに表示されているフィールド名をクリックするとプロパティが表示されます。これを見ると、「IsDeleted」フィールドの「AllowDBNull」プロパティはFalseなので、これをTrueに変更します。この他に「OwnerId」、「CreatedDate」、「CreatedById」、「LastModifiedDate」、「LastModifiedById」、「SystemModstamp」、これらのフィールドについても、同様に「AllowDBNull」プロパティをTrueに変更します。

xsdEdit2.png

プロジェクトをリビルド後、アプリケーションを起動し、「Salesforce」タブのDataGridViewにレコードが追加できることを確認してください。

次に「kintone」タブを選択し、追加済みのレコードを選択し、“>>”ボタンをクリックしてみてください。

UpdateError2.PNG

「From1.cs」内の下記のUpdateするところで「[RecordId]列は読み取り専用です。この列の値を更新出来ません。」というエラーが発生します。これは、kintone側のレコード毎に保有のキー項目であるRecordIdは更新出来ないため発生します。そこで、必要な項目のみ更新するように変更します。

先ほどと同様に、Visual Studioの「ソリューションエクスプローラー」から「DataSetKintone.xsd」を右クリックし、表示されるメニューから「デザイナーの表示」を選択します。その後、下記のように、「顧客管理(営業支援パック) Table Adapter」のアイコンをクリックして、右下の「プロパティ」が「顧客管理(営業支援パック) Table Adapter」になった事を確認して、「UpdateCommand」のプラスボタンを開き、「CommandText」を右側の「...」をクリックします。 

xsdEdit3_arrow.png

クエリビルダーが開きますので、下記のようにクエリを変更します。

UPDATE                    Kintone."顧客管理(営業支援パック)"

SET 郵便番号 = ?, 住所 = ?, TEL = ?, FAX = ?
WHERE (RecordId = ?)

QueryBuilder.PNG

プロジェクトをリビルド後、アプリケーションを起動し、もう1度、「kintone」タブを選択し、追加済みのレコードを選択し、“>>”ボタンをクリックしてみてください。「kintone」タブのDataGridViewの当該レコードの「Revision」が2になれば成功です。

UpdateSucceed.PNG

kintoneにログインし、テーブルを見るとデータが追加されていることが分かります。

kintoneCloud.PNG

実は、この「kintone」タブで起こったエラーは「Salesforce」でも発生します。そのため、同様の手順でクエリビルダーを起動し、下記のようにクエリを編集してください。

UPDATE                    Salesforce.Account

SET BillingStreet = ?, BillingPostalCode = ?, Phone = ?, Fax = ?
WHERE (Id = ?)

最後に「PCASales」タブから「Salesforce」タブに、追加済みのレコードも含め、全てのレコードをコピーしてみます。

SyncSucceed.PNG

実行が完了したら、Salesforceにログインしデータを確認します。Salesforce側で追加したデータを確認できたら完成です。

SalesforceCloud.PNG


まとめ

本記事では、PCA商魂・商管DXの顧客データを、Salesforceとkintoneに同期するWindowsフォームのC#アプリケーションを作成する手順について解説しました。このように、CData ODBCドライバを使うと、最小限のプログラミングでSaaS間のでデータを連携させることができます。プログラムは、こちらからダウンロードいただけます。