#はじめに
本記事では、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構成」画面が立ち上がりますので、それぞれ接続情報を入力します。
各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」に設定してください。
次に、3つの「DataGridView」にデータソースを設定します。下記のように「DataGridView」を選択した際に表示される「▶」をクリックして、「データソースの選択」の「(なし)」右の下矢印をクリックして、「プロジェクトデータソースの追加」リンクをクリックします。
「データソース構成ウィザード」が立ち上がりますので「データベース」―「データセット」を選択して進みます。
次の「データ接続の選択」ウィンドウでは「新しい接続」をクリックして、さらに「データソースの選択」ウィンドウから「Microsoft ODBCデータソース」を選択して「OK」ボタンをクリックします。
続いて「接続の追加」ウィンドウでは「ユーザーデータソース名またはシステムデータソース名を使用」のドロップダウンリストから作成済みの今回使用するデータソースを選択して「テスト接続」をクリックします。テスト接続に成功したら「OK」ボタンをクリックします。3つのDataGridViewには、それぞれ下記のデータソースを指定します。
- フォーム左側タブページ1 : CData PCASales Source
- フォーム右側タブページ1 : CData Saleforce Source
- フォーム右側タブページ2 : CData kintone Source
プロジェクト内のアプリケーション構成ファイルに保存する接続文字列に適当な名前をつけて、「次へ」を進みます。
選択したデータソースが持つテーブルのリストが表示されるので適当なテーブルをチェックし、データセット名を入力して「完了」ボタンをクリックします。
3つの各データソースで使用するテーブルと、そのデータセット名は下記のとおりです。このデータセット名は、c#プログラムで参照するため、正しく設定してください。
-
PCA Sales
- テーブル名 : MasterTMS
- データセット名 : DataSetPCASales
-
Salesforce
- テーブル名 : Account
- データセット名 : DataSetSalesforce
-
kintone
- テーブル名 : _顧客管理_営業支援パック
- データセット名 : DataSetKintone
データソース設定が完了すると、下記のように、そのDataGridViewにヘッダー行が表示されます。
このままでは不要な項目が多く見づらいため、各DataGridViewの不要なカラムを非表示にします。DataGridViewを選択した際に表示される「▶」をクリックして、そこで表示されるメニューから「列の編集」をクリックします。「列の編集」ウィンドウが開きますので、下記のように不要なフィールド名を選択後、「削除」ボタンをクリックします。
各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にデータソースがバインドされ、レコードの指定した項目のみが表示されます。
#ボタンの配置と同期処理の実装
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」双方で試してみてください。
「kintone」タブでは成功しますが、「Salesforce」タブでは、「列'IsDeleted'にnullsを使用することはできません」というエラーが発生し失敗します。
このエラーは、SalesforceタブのDataGridViewにバインドされたデータソース「DataSetSalesforce」のNull値を許容しないフィールドがNullのまま、レコードが追加されたことが原因です。今回扱うデータは会社名、郵便番号、住所、TEL、FAXのみで、その他のフィールドは初期化もしないため、原因となっている「DataSetSalesforce」のフィールドのプロパティを変更します。
Visual Studioの「ソリューションエクスプローラー」から「DataSetSalesforce.xsd」を右クリックし、表示されるメニューから「デザイナーの表示」を選択します。
下記のようなペインが表示されます。ここに表示されているフィールド名をクリックするとプロパティが表示されます。これを見ると、「IsDeleted」フィールドの「AllowDBNull」プロパティはFalseなので、これをTrueに変更します。この他に「OwnerId」、「CreatedDate」、「CreatedById」、「LastModifiedDate」、「LastModifiedById」、「SystemModstamp」、これらのフィールドについても、同様に「AllowDBNull」プロパティをTrueに変更します。
プロジェクトをリビルド後、アプリケーションを起動し、「Salesforce」タブのDataGridViewにレコードが追加できることを確認してください。
次に「kintone」タブを選択し、追加済みのレコードを選択し、“>>”ボタンをクリックしてみてください。
「From1.cs」内の下記のUpdateするところで「[RecordId]列は読み取り専用です。この列の値を更新出来ません。」というエラーが発生します。これは、kintone側のレコード毎に保有のキー項目であるRecordIdは更新出来ないため発生します。そこで、必要な項目のみ更新するように変更します。
先ほどと同様に、Visual Studioの「ソリューションエクスプローラー」から「DataSetKintone.xsd」を右クリックし、表示されるメニューから「デザイナーの表示」を選択します。その後、下記のように、「顧客管理(営業支援パック) Table Adapter」のアイコンをクリックして、右下の「プロパティ」が「顧客管理(営業支援パック) Table Adapter」になった事を確認して、「UpdateCommand」のプラスボタンを開き、「CommandText」を右側の「...」をクリックします。
クエリビルダーが開きますので、下記のようにクエリを変更します。
UPDATE Kintone."顧客管理(営業支援パック)"
SET 郵便番号 = ?, 住所 = ?, TEL = ?, FAX = ?
WHERE (RecordId = ?)
プロジェクトをリビルド後、アプリケーションを起動し、もう1度、「kintone」タブを選択し、追加済みのレコードを選択し、“>>”ボタンをクリックしてみてください。「kintone」タブのDataGridViewの当該レコードの「Revision」が2になれば成功です。
kintoneにログインし、テーブルを見るとデータが追加されていることが分かります。
実は、この「kintone」タブで起こったエラーは「Salesforce」でも発生します。そのため、同様の手順でクエリビルダーを起動し、下記のようにクエリを編集してください。
UPDATE Salesforce.Account
SET BillingStreet = ?, BillingPostalCode = ?, Phone = ?, Fax = ?
WHERE (Id = ?)
最後に「PCASales」タブから「Salesforce」タブに、追加済みのレコードも含め、全てのレコードをコピーしてみます。
実行が完了したら、Salesforceにログインしデータを確認します。Salesforce側で追加したデータを確認できたら完成です。
#まとめ
本記事では、PCA商魂・商管DXの顧客データを、Salesforceとkintoneに同期するWindowsフォームのC#アプリケーションを作成する手順について解説しました。このように、CData ODBCドライバを使うと、最小限のプログラミングでSaaS間のでデータを連携させることができます。プログラムは、こちらからダウンロードいただけます。