Analytics APIとHighchartsでグラフを作ろう

この記事はForce.com Advend Calender 2013 9日目エントリーとなります。
http://atnd.org/events/45110

今年のネタを考えるにあたって、去年は何を書いたのかなーと調べてみたら「項目セット」と「Visualforce Charting」について書いていました。
グラフネタと言えば、Winter'14からはAnalytics API(日本語名:分析 API)が公開されているのを思い出しました。
ということで、今年はAnalytics APIを使ってグラフを作成してみます。
グラフはJavaScriptのライブラリであるHighchartsを使う事にします。

まず先にVisualforceで作った完成系のイメージです。

左上にプルダウンを作って、集計値を動的に変更できるようにしてみました。

Salesforceの設定でレポートを作る

さて、ここから作り方です。
まずはベースとなるレポートを作る必要があります。
今回は商談のマトリックスレポートで、行を取引先名、列をフェーズにし、金額、期待収益、レコード数を集計するレポートにしました。

Visualforceで画面を作る

Visualforceの全体は最後にして、ここからはポイントとなるところを見ていきましょう。
今回使ったのはjQueryHighchartsForce.com-JavaScript-REST-Toolkitのライブラリです。
Force.com-JavaScript-REST-Toolkit 通称ForceTKはRESTベースであるAnalytics APIを使う時に便利です。
jQuery以外は静的リソースに置いて、それを読み込んでいます。

<apex:includeScript value="//code.jquery.com/jquery-1.10.1.min.js" />
<apex:includeScript value="{!$Resource.highcharts}" />
<apex:includeScript value="{!$Resource.forcetk}" />

Analytics APIで対象とするレポートIDですが、今回はベタで書いています。実際はURLパラメータで受け取ったり、カスタム表示ラベルに持たせる等するのが良いでしょう。
続いてREST APIでアクセスするためにForceTKを使ってセッション情報をセットします。

<script type="text/javascript">
  $j = jQuery.noConflict();
  var repot = null;

  $j(document).ready(function(){
    var reportId = '00OA00000058IIx'; //マトリックスレポートID
    var client = new forcetk.Client();
    client.setSessionToken('{!$Api.Session_ID}');

ForceTKのclientからAnalytics APIのエンドポイントを指定してJSON形式のレスポンスを受け取ります。
Analytics APIにはいくつかエンドポイントがあり、describeを指定してレポートのメタデータを受け取ったり、サマリーレベルの集計を受け取ったり、rowレベルのデータを受け取ったり、あるいは同期/非同期を選択することも可能です。

    client.ajax("/v29.0/analytics/reports/"+reportId+"?includeDetails=true", function(response){
      report = response;

実際に作ってみる時にはAnalytics APIの開発者ガイドを参照してみてください。
そのうち日本語化されるかもしれませんが、今日時点では本家USのdeveloperforceサイトに英語版が置いてあります。

次に左上に表示する集計値の選択リストを表示するために、aggregatesから集計項目を取ってきます。
また、選択リストのonChangeイベントとしてグラフ作成用のfunctionをセットしておきます。

      $j.each(report.reportMetadata.aggregates, function(index, agg) {
        $j("#selectAgg").append('<option value="'+index+'">' +
          report.reportExtendedMetadata.aggregateColumnInfo[agg].label +
          '</option>');
      });
      $j("#selectAgg").change(createReport);

      createReport();
    });
  });

グラフ生成用functionではまずX軸を生成します。今回で言うと取引先名ですね。
レポートの行となる集計はgroupingsDownから取得できます。
今回は1階層となるマトリックスですが、複数階層のサマリー形式レポートの場合も、groupingsDownを辿って行くことで情報を取得できます。

  var createReport = function(){
      var series = [];
      var category =[];
      
      // X軸のカテゴリを生成
      $j.each(report.groupingsDown.groupings, function(index, grouping){
        category.push(grouping.label);
      });

画面左上の集計値選択プルダウンから、ユーザが選択した集計値を取得します。
集計値の名称等の情報はreportExtendedMetadata.aggregateColumnInfoに入っています。

      // 選択値から集計値情報を取得
      var aggIndex = $j('#selectAgg').val();
      var columnInfo = report.reportExtendedMetadata.aggregateColumnInfo[report.reportMetadata.aggregates[aggIndex]];

次に実際の集計値が入っているfactMapから値を取り出します。
factMapは縦横1階層のマトリックスレポートで言うと、左上が"0!0"、そこから下に"0!1","0!2"..というキーで値を持っています。
左上から右に行くと"1!0","2!0"...となり、合計行は"T!T"となります。
途中端折っていますが、イメージ的には以下の感じですね。

グルーピングがネストすると、"0_1!T"とか"2_2!3-1"のようになるようです。
そのへんの詳細は開発者ガイドをご確認ください。

ということで、Highcharts用のdataを生成するために、縦横をループで回してデータを作っています。

      // マトリックスの縦と横を回しながら集計値を取得
      $j.each(report.groupingsAcross.groupings, function(i1, g1){
  
        series.push(new Object());
        series[i1].name = g1.value;
        series[i1].data = [];

        $j.each(report.groupingsDown.groupings, function(i2, g2){
          series[i1].data.push(report.factMap[g2.key.toString()+"!"+g1.key.toString()].aggregates[aggIndex].value);
        });
      });

後はグラフを生成して終わりです。
デザイン部分はHighchartsのDemoサイトからパクってきたのをコピペして、titleとかxAxis、yAxisのtitle、seriesあたりに作っておいた値をセットしています。

      //グラフ生成
      $j('#container').highcharts({
        chart: {
          type: 'column'
        },
        title: {
          text: columnInfo.label + 'のグラフ'
        },
        xAxis: {
          categories: category
        },
        yAxis: {
                min: 0,
                title: {
                  text: columnInfo.label
                },
                stackLabels: {
                  enabled: true,
                  style: {
                      fontWeight: 'bold',
                      color: (Highcharts.theme && Highcharts.theme.textColor) || 'gray'
                  }
              }
            },
            legend: {
              align: 'right',
              x: -70,
              verticalAlign: 'top',
              y: 20,
              floating: true,
              backgroundColor: (Highcharts.theme && Highcharts.theme.legendBackgroundColorSolid) || 'white',
              borderColor: '#CCC',
              borderWidth: 1,
              shadow: false
            },
            tooltip: {
              formatter: function() {
                  return '<b>'+ this.x +'</b><br/>'+
                      this.series.name +': '+ this.y +'<br/>'+
                      'Total: '+ this.point.stackTotal;
              }
            },
            plotOptions: {
              column: {
                  stacking: 'normal',
                  dataLabels: {
                      enabled: true,
                      color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white'
                  }
              }
            },
        series: series,
      });
  }

全体のソースコードはこちらに置いてあります。
https://gist.github.com/isanuki/7839374

補足

Analytics APIはレポートから結果を取得するので、通常のAPI制限に加えてレポート系の独自な制限がかかります。

  • 最近参照した 200 個までのレポートを取得できます。
  • 詳細レコードデータを含む、レポートの最初の 2000 行の結果を取得できます。条件を使用して結果を絞り込み、最も有益な結果を取得できます。
  • レポートの実行時に20 個までのカスタム項目条件を追加できます。これには、ソースレポートに保存された既存のカスタム項目検索条件が含まれます。
  • 列として含まれる項目数が 100 個までのレポートの要求を送信できます。
  • 非同期レポート結果へのアクセスは、24 時間あたり、任意の回数実行できます。
  • データを絞り込む場合、クロス条件と標準レポート条件は使用できません。

※Winter'14 リリースノートより抜粋

開発者ガイドにはもっと細かく記載されています。
これら制限もそうですが、当然ながら組織のAPIコール数の制限にもカウントされるので、Analytics APIを使う場合はこの辺も頭に入れて開発しましょう。
APIコール数の制限緩和を期待しつつ・・)

あと、今回使ったHighchartsライブラリは商用利用する場合はライセンスを購入する必要があります。
この辺、詳しくは公式サイトをご確認ください。

まとめ

Analytics APIを使うことでアプリケーション側で分析する部分を作らないで済むのは便利ですし、モバイルアプリケーションでグラフを作る際にもRESTベースのAnalytics APIでレポートから結果を受け取り、グラフを表示することが簡単にできます。
また、様々なJavaScriptのライブラリを使うことで、よりリッチなグラフにすることも可能です。
一昔前は「Salesforceは分析系が弱い」という声もありましたが、これらAPIを使うことで、より柔軟に開発が出来そうですね。
ぜひ、使ってみてください。

項目セットを使ってみよう

皆さん、項目セット使ってますか。
結構前に追加された機能ではあるんですが、私の周りでは使っている人をあんまり見かけないです。
新機能として追加された時に、あんまり脚光を浴びるようなものではなかったせいでしょうか。
いや、きっとみんな存在を知らないだけで、実はこんな機能あったらなーと思っているかもしれません。
そこで、今回は項目セットの使い方をまとめてみたいと思います。

項目セットを使う理由

項目セットとは1つのオブジェクト内の項目を1つのまとまりとし、それをApexやVisualforceから扱うことのできる機能です。

例えば、Salesforceと言えば、項目が簡単に追加できると言われており、ユーザーやお客様なんかは
「後から簡単に項目追加できるんでしょ?この属性も追加してよ」なんてことはよくあります。
たしかにオブジェクトに項目を追加すること自体は簡単にできますし、標準レイアウトであれば項目を追加することも問題ありません。

ただ、そこにApexやVisualforceが絡んでくると修正範囲は広がってしまい、そう簡単なものではなくなってきます。
運用を考えると単にプログラムを修正するだけではなく、ドキュメントの修正、再テスト、承認、リリース判定、デプロイ・・・などなど

また、プログラムに手を入れるということは、IT部門を持たないユーザーの場合はなかなか難しく、項目1個増やすだけなのに
ベンダーへの発注が必要になってしまいます。

こういったことを避けるために、常に開発者は「メンテナンスビリティの高いシステムを作る」という意識を持つべきです。
Force.comで言うと、今回の「項目セット」や「カスタム設定」「カスタム表示ラベル」「静的リソース」あたりを使っている組織は
メンテナンスがしやすい傾向があります。

項目セットを作成する

項目セットはまず箱となるセット自体を作成します。

次に項目セットに追加する項目を入れていきます。
ページレイアウト編集画面のような同じみの画面ですね。
各項目はここで並べた順番で取得できるので、並び順も考えて配置しましょう。

ここで項目をダブルクリックすると、必須の定義ができます。
これはページレイアウトと同じで、この項目セット上で必須かどうか定義するものです。
ただし、ここで必須にしたからと言って、この項目セットを使った時に必ず必須になるというものではなく、必須にするにはVisualforce上でrequiredを指定します。
サンプルではとりあえず商談名だけ必須にしておきます。

ここまでやると項目セットの作成は完了です。
次は各パターンに分けて使い方を紹介します。

outputFieldとinputFieldの場合

StandardControllerでoutputFieldとinputFieldを使った単純なパターンです。

<apex:page standardController="Opportunity">
  <apex:form >
    <apex:pageBlock mode="edit" title="商談 項目セットサンプル画面">

      <apex:pageMessages />
      <apex:pageBlockButtons >
        <apex:commandButton value="保存" action="{!save}"/>
        <apex:commandButton value="キャンセル" action="{!cancel}" />
      </apex:pageBlockButtons>

      <apex:pageBlockSection columns="2" title="outputFieldのサンプル">
        <apex:repeat value="{!$ObjectType.Opportunity.FieldSets.Set01}" var="s"> 
          <apex:outputField value="{!Opportunity[s]}"/>
        </apex:repeat>
      </apex:pageBlockSection>

      <apex:pageBlockSection columns="2" title="inputFieldのサンプル">
        <apex:repeat value="{!$ObjectType.Opportunity.FieldSets.Set01}" var="s"> 
          <apex:inputField value="{!Opportunity[s]}" required="{!s.Required}"/>
        </apex:repeat>
      </apex:pageBlockSection>

    </apex:pageBlock>
  </apex:form>
</apex:page>

項目セットには$ObjectType.[オブジェクト名].FieldSets.[項目セット名]でアクセスしています。
また、inputfiledにはrequiredを指定して、先ほど作った項目セット側で必須定義したものを必須入力にしています。
項目側が必須になっているようなフェーズ項目等は、項目セット側で必須じゃなく設定することもできますが、
当然ながら保存時にエラーになります。
inputField側でrequiredを指定しなければ、項目側の必須設定によって、赤マークが出たりするようです。

Visualforceページ名に?id=[商談SFID] を付けて見るとこんな感じ

outputTextとinputTextの場合

outputTextとinputTextを使う場合はラベル名を引っ張ってこないといけないので、$ObjectType.Opportunity.Fields[s].labelとかで取ります。

<apex:page standardController="Opportunity">
  <apex:form >
    <apex:pageBlock mode="edit" title="商談 項目セットサンプル画面">

      <apex:pageMessages />
      <apex:pageBlockButtons >
        <apex:commandButton value="保存" action="{!save}"/>
        <apex:commandButton value="キャンセル" action="{!cancel}" />
      </apex:pageBlockButtons>

      <apex:pageBlockSection columns="2" title="outputTextのサンプル">
        <apex:repeat value="{!$ObjectType.Opportunity.FieldSets.Set01}" var="s"> 
          <apex:outputText value="{!Opportunity[s]}" label="{!$ObjectType.Opportunity.Fields[s].label}" />
        </apex:repeat>
      </apex:pageBlockSection>

      <apex:pageBlockSection columns="2" title="inputTextのサンプル">
        <apex:repeat value="{!$ObjectType.Opportunity.FieldSets.Set01}" var="s"> 
          <apex:inputText value="{!Opportunity[s]}" label="{!$ObjectType.Opportunity.Fields[s].label}" required="{!s.Required}"/>
        </apex:repeat>
      </apex:pageBlockSection>

    </apex:pageBlock>
  </apex:form>
</apex:page>

画面はこんな感じ。

ご覧のとおり、そのまま出しただけだと日付や数値のフォーマットが出来てないし、選択リストや日付型の入力補助もない。
フォーマットはapex:paramとか使ったり、選択リストなんかも出来なくはないけど、項目セットにどんな型が入って来るのかわからないから分岐が面倒くさそう。
じゃあ、inputFieldとoutputField使えばいいじゃんってなるけど、画面のパフォーマンスを考えるとTextを使ったり、あるいは直に書いた方が早いらしい。
その辺は@xlouderさんの「Visualforceの性能検証」が詳しいので、そちらを見て欲しい。
http://www.slideshare.net/hyoshita/performance-of-visualforce-lt-version20121031

用途によって使い分けてもらえばいいと思います。

Apexで使う場合

Schema.FieldSetとSchema.FieldSetMemberメソッドを使って取得することが可能。
使うとこだけ見ると以下のような感じ。

	public List<Schema.FieldSetMember> getFields() {
		return SObjectType.Opportunity.FieldSets.Set01.getFields();
	}

	private Opportunity getOpportunity() {
		String query = 'SELECT ';
		for(Schema.FieldSetMember f : this.getFields()) {
			query += f.getFieldPath() + ', ';
		}
		query += 'Id, Name FROM Opportunity LIMIT 1';
		return Database.query(query);
	}

以上となりますが、このような感じで項目セットを駆使し、メンテナンスがしやすいシステムを作っておけば「Salesforceなんだから項目の追加が簡単なんでしょ?」と言われた時にも、すぐに対応することが可能です。
ぜひ皆様も項目セットを使い倒してみてください!


ちなみにこの記事はForce.com Advent Calendarに参加しています。
http://atnd.org/events/33649

他の方のエントリーもぜひ見てみてください。

Visualforce Chartingで動的なグラフを作ってみる

Visualforceの標準コンポーネントを使ってグラフを作成できるVisualforce ChartingがWinter'13で正式リリースされました。
VFChartを使えば標準レポートやダッシュボードで作成できないグラフが自由自在?に作成できます。
ということで、今日のお題はVFChartです。

例えば「予実管理」なんかはよくある話で、予算に対して実績がどのくらい達成されているのかグラフやゲージで見たいという要件はよくあります。
ただ、これを標準ダッシュボードのゲージでやろうとすると、予算の数値はダッシュボードのコンポーネントに直接入力しなければならず、オブジェクトに登録されている値を予算として集計することは出来ません。
また、棒グラフにしても、標準レポートで作るとしたら、同じオブジェクトに予算と実績の値を持つ必要があります。
(厳密に言えば参照関係があれば大丈夫ですが、実質同じオブジェクトでやるのが現実的ですよね)

これをVFChartならば、予算の値をゲージに持ってくることもできるし、予算と実績の値が別オブジェクトだろうが関係ありません。
ApexやJavaScriptで集計した値を各グラフの縦軸や横軸プロットすることができるので、様々なグラフが作成できます。

こんなゲージとか

エリアチャートも思いのまま

動的なグラフ

さて、本題です。
ここでいう動的なグラフとは、ユーザーが入力した値に基づいて、その場でグラフの値がビヨーンと変わるやつです。
せっかくなのでSitesで公開してみました。
イメージとしてはユーザーが選択した年の月別売上を見るグラフです。
http://isanuki02-terrasky-developer-edition.na14.force.com/

サンプル画像

プルダウンのonchangeでクエリーを投げているせいかちょっとレスポンスが悪いですが、
まぁ、その辺はご愛嬌といったところで。

以下がコードです。
Visualforce

<apex:page controller="DynamicChartController"  showHeader="false" sidebar="false" readOnly="true" title="Dynamic Chart Sample">
<apex:form >
  <apex:selectList value="{!selectYear}" size="1" style="font-size:18px;">
    <apex:selectOptions value="{!yearList}"/>
    <apex:actionSupport event="onchange" action="{!change}" rerender="myChart"/>
  </apex:selectList>
</apex:form>

  <apex:chart height="400" width="700" data="{!Data}" resizable="true" id="myChart" theme="Purple">
    <apex:axis type="Numeric" position="left" fields="amount" title="金額" grid="true" maximum="1000000" />
    <apex:axis type="Category" position="bottom" fields="month" title="月">
    </apex:axis>
    <apex:barSeries orientation="vertical" axis="left" xField="month" yField="amount"/>
  </apex:chart>
</apex:page>

Controller

public with sharing class DynamicChartController {

    public static final Integer MONTHS_OF_YEAR = 12;
    public String selectYear {get; set;}
    public List<SelectOption> yearList {get; set;}

    public DynamicChartController(){
        yearList = createYearList();
        selectYear = yearList.get(0).getValue();
    }
    
    public List<SelectOption> createYearList(){
        List<SelectOption> options = new List<SelectOption>();
        // 商談から年のリストを生成
        for(AggregateResult result : [SELECT CALENDAR_YEAR(CloseDate) year 
                                      FROM Opportunity 
                                      WHERE IsWon = true 
                                      GROUP BY CALENDAR_YEAR(CloseDate)
                                      ORDER BY CALENDAR_YEAR(CloseDate)]){
            options.add(new SelectOption(String.valueOf(result.get('year')),
                                         result.get('year')+'年'));
        }
        return options;
    }

    public List<ChartData> getData(){
        List<ChartData> dataList = new List<ChartData>();
        Map<Integer, Double>resultMap = new Map<Integer, Double>();
        
        // 勝ち商談の月ごとの合計値を月=>金額のMapに入れる
        for(AggregateResult result : [SELECT CALENDAR_MONTH(CloseDate) month, SUM(Amount) sumAmount 
                                      FROM Opportunity 
                                      WHERE CALENDAR_YEAR(CloseDate) =: Integer.valueOf(selectYear) 
                                            AND IsWon = true
                                      GROUP BY CALENDAR_MONTH(CloseDate)
                                      ORDER BY CALENDAR_MONTH(CloseDate)]){
            resultMap.put(Integer.valueOf(result.get('month')), 
                          Double.valueOf(result.get('sumAmount')));
        }
        
        // グラフ表示用のデータを12ヵ月分作成することで、商談に売上がない月でもグラフを出す
        for(Integer m=1; m <= MONTHS_OF_YEAR; m++){
            ChartData tmpData = new ChartData(m);
            if(null != resultMap.get(m)){
                tmpData.amount = resultMap.get(m);
            } else {
                tmpData.amount = 0;
            }
            dataList.add(tmpData);
        }
        return dataList;
    }
    
    public PageReference change(){
        return null;
    }
    
    public class ChartData{
        public Integer month{get; set;}
        public Double amount{get; set;}
        public ChartData(Integer month){
            this.month = month;
        }
    }
}

また、標準レポートを使ってグラフを作った時に、「レコードが存在しない集計値の列はグラフが出ない」という問題があります。
例えば月別の売上グラフを出した時に、12月の売上がないとそもそも12月という集計値が存在しないので、標準レポートでは1月〜11月のグラフとして表示されます。
これを標準カスタマイズのみで解決する時は、苦肉の策として1月〜12月までの空データを作っておいたりして、難を逃れます。

これに対してVFChartであればController側の作り次第なので、12月の実績データがなくてもグラフにすることが可能です。
今回のサンプルでも2012年12月のデータは存在しませんが、グラフ表示用の空データを内部的に作っているので、きちんとグラフに0で表示されます。

そんなVisualforce Charting、ダッシュボードに表示することもできますので、標準では難しい要望がある時はぜひ使ってみましょう。

ちなみにこの記事はForce.com Advent Calendarに参加しています。
http://atnd.org/events/33649

他の方のエントリーもぜひ見てみてください。

Salesforce.com認定試験について

上級デベロッパー試験を今月末に受ける。
まーそれはともかく、なんとなくSalesforce.comの認定試験をまとめてみることにした。
先に一言書いておくが、公開されていない試験問題とかは書かないので、その辺は期待しないで欲しい

現在の主な認定資格は以下の3種類。

セールス

いきなりだがセールス試験は受けたことないので分からない
とりあえず「Salesforceを提案するために必要な基礎知識」を問う資格なので、営業向けの資格と言える。
まぁ、この資格を持っていることで、どの程度お客様に対して訴求効果があるかは微妙なとこだが、受験費用も安いし受けてみるのもいいんじゃないだろうか。
それにSFDCのことを良く知らない人や上司に対して「Salesforceの資格持ってまっせ!」というアピールはできるし。

難易度的には(聞いた限り)比較的簡単な方みたい。

コンサルタント

あれ?認定コンサルタント取得試験のページがなくなってるけど、まだあるよね?
「認定コンサルタント資格取得トレーニング」のページはあるし、とりあえずある前提で進めよう。

認定コンサルタントはインプリのポイントや顧客要件に対してどういった機能を使うと良いか、またはこの機能を使う場合はどういった注意点があるか等、SFDCの導入について幅広く知識を有することが求められる資格だ
ただ、実際の試験内容はどちらかと言うとSalesforceの「コーディングを伴わない基本的なカスタマイズ」が主となってくる。
もしかすると後述するデベロッパー資格とかぶってくる部分が大きいので、セールスフォースは試験体系を見直しているのかもしれない。
・・・それでページがなくなったのか!?

昔はそれなりに難易度の高い試験だったが、今はそうでもないっぽい。
真面目に受ければ合格するだろう。

ちなみにコンサルは認定資格の中で唯一、更新試験がある。
間隔はSFDCのページを見て欲しいが、現在は年に1回程度だ。
まぁ、年に3回もメジャーバージョンアップがあるし、更新試験があるのは当然だろう。
難易度的にはもしかしたら本試験よりも更新試験の方が難しいかもしれない。
ただ、仮に更新試験に落ちても、期間内の試験日程であれば何回でも受けられるみたいだから、それほど心配はしなくて良い。
それに試験はモゴモゴ(以下自主規制)

デベロッパ

デベロッパーは無印と上級の2種類に分かれており、上級を受けるには無印に合格している必要がある。

無印デベロッパ

この資格はコーディングを伴わないセールスフォースの開発(カスタマイズ)について知識を問われるものだ。
細かい出題範囲は試験申し込みページにあるstudy guideを見て欲しいが、オブジェクトの作成やワークフローなどの基本機能や、各機能の制限について等がメインとなってくる。
またApexやVisualforceで開発できる必要はないが、それらがどういったものなのか程度には知っておく必要がある。

study guideからサンプル問題を1問だけ持ってきてみた

ユニバーサルコンテナ社は、人材採用管理アプリケーションにおいてカスタムオブジェクト「募集職種」を使用して募集職種情報を追跡します。募集職種は承認されてから90日後に期限切れとなります。ワークフロールールで、募集職種で指定されている募集担当者に有効期限日の15日前に電子メールを送信するように設定しています。
募集職種の有効期限が30日間延長された場合、何が起こるでしょうか。1つ選択してください。

A. 電子メールは、変更前の有効期限日の15日前に送信される
B. 電子メールは、変更後の有効期限日の15日前に送信される
C. 電子メールは、変更前の有効期限日に送信される
D. 電子メールは送信されない

正解はBである。(ドラッグして反転してください)


正直、範囲も広いし、(追加スピードは遅いが)新機能に関しての問題も入ってくるので、難易度的には前のセールス、コンサルよりも難しい
試験対策としてどうすればいいのかというと、おそらくセールスフォースの機能をひたすら触るのが一番だ。
実際に顧客導入案件をやると分かるが、お客様から「こういう機能が欲しいんだけど」と言われて「あ、それはアノ機能で出来そうだなー」と思い「はい、分かりました!」と言ったはいいが、実際に実装してみたらできなかった!という経験を積んで機能や制限を覚えていく。
私も何度それを繰り返したことか・・・

まぁ、あるいは自分でアプリケーションを構築してみるとかいいかもしれませんね。
フリーのDeveloper Editionを使って家計簿システムを作ってみるとか。

後はSFDCが実施している「システム管理者コース」や「Force.com 基礎」といったトレーニングを受けるのが手っ取り早いかもしれません。
ただ、それなりのお値段しますが。。

上級デベロッパ

これから取る予定なので語れるほど詳しくないのですが、主にプログラミングを伴うSalesforceの開発について、知識を問われます。
これも試験申し込みページにstudy guideがあるので、詳しくはそちらで。

2011年4月1日の時点で(国内で)31名の合格者しかいないし、結構難しい試験なのだと思う。
USなんかでは受注資格として「上級資格者がいること!」みたいなものがあるらしいし、国内でもそういう案件が増えてくるのだろうか。

ちなみにstudy guideに乗っているサンプル問題はこんな感じだ。

以下の Visualforce ページマークアップが存在し、コントローラと各拡張コントローラに「go」という名前のアクションメソッドが含まれると仮定した場合、ユーザがcommandButtonをクリックすると、どのクラスメソッドが呼び出されますか。

<apex:page controller="theController" extensions="ext1, ext2, ext3">
    <apex:form>
        <apex:commandButton value="Go" action="{!go}" rerender="out" />
        <apex:outputPanel id="out">{!output}</apex:outputPanel>
    </apex:form>
</apex:page>

A. theController
B. ext1
C. ext2
D. ext3

正解はBである。(ドラッグして反転してください)


これも試験対策としてはApexやVisualforceを触るのが一番だろう。
あとは開発者向けトレーニングを受けるか。



・・・まぁ、私もこんなブログを書いているんだったら、上級試験対策しろって話ですよね。

Chatter.comをSIerと顧客間で試用してみた!

前回こっそり書いた「Chatter.comを試用してみた!」は人目に触れると思ってなかったので、わりと概要的なことしか書かなかった。
だが、実はもうちょい面白い使い方をしているので今回はそれを紹介しよう。


Salesforceでは東日本大震災の復興を目的とした利用に限り、ライセンスの90日間無償提供を行っている。
私の会社でもこういったお客様に対してSalesforceの構築をお手伝いをしているが、やはり普段のインプリ以上にスピードを求められるし、プロジェクトメンバーが東京や仙台に散らばっているので「毎週○曜日に定例会を・・」ということも行えない。


そこでChatterでコミュニケーションを取りながらインプリしてみた


以下はその中で気づいた点である。

よかったところ

☆スピード

Salesforceのインプリは1.要件確認 2.実装 3.顧客確認 4.改善のSTEPをいかに早く回して行けるかがポイントとなる。
これを顔合わせを前提として行うとすると、(軽微なものであればその場で実装も可能だが)基本的に少し時間がかかるものは自社に持ち帰って行うことになるので、1〜4までのSTEPは少なくとも数日はかかってしまう。

しかしChatterを使うことでこのサイクルをより短くすることが出来る。
お互いがログインしていればチャットのような勢いで、リアルタイムにコミュニケーションをとり、要件を確認したら「じゃあ、30分くらいで実装できるので、その後確認お願いします。」ということも可能だ。

そうすることで顧客としてもこちらが実装している間、別の作業をすることができ、お互いに時間を効率的に使うことができる。

☆ファイルの共有

通常であればメール等でやり取りすることになるで、何度も同じファイルをもらっているうちにローカル上で「どれが最新のファイルだっけ・・・」というのもよくあることだ。
Chatterのファイル共有を使えばバージョン管理もできるし、もちろんお客様との共有も簡単にできる。
これは非常に便利だ。

☆いいね!

Facebookでお馴染みの機能があるので、「○○の機能を実装しておきました。確認お願いします」なんてメッセージに「いいね!」が複数付いていたらモチベーションがモリモリ上昇!する。

☆課題管理

これは実際に使ったわけではなく「これやったら良かったな〜」というものだ。
ちょっとChatterの機能というよりはForce.comの機能に近いのだが、カスタムオブジェクトで「課題管理」を作成しておき、いつまでに、誰が、何を、どうするか、といったことを管理しておけば、Salesforce上で課題の管理ができる
また、Chatterではレコードをフォローすることもできるので、課題のステータスが変更した時にタイムラインにお知らせを流して周知も行える。

☆心の距離が縮まる

いや、気のせいかもしれない。
でもTwitterなどのSNSに慣れた人間であれば、冗談を飛ばしながらスムーズに活用することができるだろう。

課題点

★見てもらえたか分からない

メッセージに対して何も反応がないと、それを読んでもらえたかどうかが分からない。
まぁこれはメールでも同じなのだが、メールは必読するという暗黙の前提がお互いにあると思う。
しかしChatterにおいては読むつもりではいても、重要なメッセージがタイムラインの中に溺れてしまい、見逃してしまうこともある。
これについては「読んだら必ずコメントをつける」だったり、重要なものについてはメールや電話を使用するというルールが必要だろう。

★いろんな人から要望が出てひっちゃかめっちゃか

通常であればお客様の中で要件をまとめてからMTG等で出してもらうのだが、Chatterでは発言がしやすいせいかいろんな人からいろんな要件が出てくる。
単に要望が多いだけならまだしも、人によって言うことが違ってくるとどうしようもない。
これはChatter上で顧客グループを作成して(必要があれば非公開にして)その中であーだこーだ言ってもらい、そこでまとまった意見を原則として1人が出すようにしてもらうなどのルールを設けることが必要だ。


といった感じでChatterをSIerの立場としてお客様と使用してみたのだが、感触としては・・・いいね!である。
また、今後は課題管理などの導入向け機能をパッケージ化しておくことで、さらに利便性が高まると思う。

次回もチャンスがあればぜひ使ってみたい。

ナレッジマネジメント

いろんな知識の共有

コールセンターでのお客様対応を共有。
ベテラン技術者から経験の浅い要員へ技術を共有。
姑から嫁へ家庭のカレーの味を共有。
北斗神拳を共有。

このように知識の共有は、様々な分野で行われているが、やり方は多種多様だ。
マニュアルを参照できるようにしてあったり、実技を見せるものであったり、俺のやり方を盗め!であったり、一子相伝であったり。。

しかし、一昔前の多数のマニュアルワーカーと少数のホワイトカラーで組織された企業においてはこのようなやり方で機能していたが、昨今多くなってきたホワイトカラー(すなわちナレッジワーカー)が多数を占める企業においては、非効率的な知識の共有方法では高度情報化社会を勝ち抜いていくことはできない。

一説には平均的なホワイトカラーは1日の業務時間の4割以上を情報検索や資料作成に費やしていると言われている。企業に蓄積される情報量は日々爆発的に増加しており、この割合は今後ますます高くなると思われる。

いかに効率的に知識を集め、これを効果的に活用するナレッジマネジメントは、企業において非常に重要度が高くなっている。

Salesforceとナレッジ

一般的にナレッジマネジメントとは、組織のメンバーが蓄積してきた業務上の専門知識やノウハウをナレッジと定義し、このナレッジを組織の全員が活用できる共有の資源として整理統合することと言える。

Salesforceにおいてもまさに「ナレッジ」という機能がある。(日本語だと何故か"記事")
これはユーザが様々な情報を作成・管理でき、かつカスタマーポータルやパートナーポータルへ公開することも可能な機能だ。
これはこれで有効な機能であるが、Salesforceはそれ自体がナレッジを登録するための箱となり、特にこのナレッジ機能だけに限らず知識の共有が行える。

商談を受注するためのベストプラクティスであったり、コンテンツでファイルを共有したり、過去にあった問い合わせ情報を管理しておくことも出来る。
ただ、登録自体はそれほど考えることなくボンボン出来るのだが、情報を引き出す仕組みというのは、構築の段階である程度イメージしておかないと難しい

単純にキーワード検索するだけでは、(情報量にもよるが)本当に必要な情報に辿り着くのが困難であり、せっかく溜め込んだナレッジを有効に活用することはできないだろう。

Chatterを使いレコードをフォローすることで、情報が向こうからやってくる仕組みを作ることは一つの方法ではある。
それ以外では、オブジェクト、レコードタイプ、フォルダなど様々な機能を使い、きちんと情報を体系立てて登録することで初めて、有効なナッレジマネジメントのシステムになる。

せっかく溜まったノウハウを使えずにそのままにしておくのはもったいない。
また、ノウハウというのは使ってこそ、研磨されてより精度の高い情報となっていく

Salesforceを利用する場合は、アウトプットもイメージして構築し、誰からもゆるふわ愛されガールなシステムを作って欲しい。

Chatter.comを試用してみた!

Chatterとは

Salesforce.com社が提供する新しいコラボレーションツール、それがChatterである。
簡単に言えば「企業内Twitter」と言えば、どんなものか想像がつくだろう。

じゃあ、TwitterFacebookといった多くのSNSと何が違うのかと言うと、それは同じ企業に属する人がユーザだという点だ。
例えばTwitterで特定分野のことを知りたい場合は、人を探してリストを作成したり、その分野について詳しい人を探す必要がある。
これは何気に面倒な作業だ。
見つけ出したその人が本当に欲しい情報を有する人かは分からない。

しかし、Chatterの場合は違う。
同じ会社にいる人がユーザなので、自分が業務で知りたい情報はほとんどの場合社内の誰かが知っている。
それに初めて行うような案件であっても「誰かこんな事例知らない?」とつぶやくだけで、優しい先人者達がフォローをしてくれる。
また、プロフェッショナルの集まりだからこそ、情報の質がTwitterより断然高いのだ。

ビジネスとプライベートの境目

Chatterを使う場合にだいたい問題点として上がるのが、どこまで仕事と関係のない発言を良しとするかがある。
プロジェクトに関する話は良いとしても、飲み会に関する発言はダメなのか。
趣味のフットサルに関する発言はどうなのか。
Chatterでの発言は社内に貯まっていくノウハウとも言えるので、会社によってはこの雑音を嫌うところもある。

しかし、私は会社の人同士が話すことであれば、例え趣味や飲み会に関する話題であれ、それは有効会社で働いていく上で有効な情報だと思う。
小さい会社ならともかく、それなりに人が多い会社になってくると仕事で絡むことがなければなかなか話す機会もない。
人同士の繋がりが希薄なこんな世の中じゃ・・と言うつもりはないが、それが例えネットやツール上であっても、社内の人と絡むということは仕事を進めていく上で非常に役に立つだろう。

Chatterを使って、どんどん無駄話をすればいいのだ。

そんなわけで、私は今日もChatterを使って無駄な情報、そしてたまに有効な情報を社内に流していく。