Salesforce Lightning Experienceで差し込み印刷機能を作成してみた。

はじめまして。エス・エム・エス エンジニアの高瀬です。
弊社では「Lightning Experience(LEX)」への移行作業を行っています。
その中で標準機能として利用できなくなる差し込み印刷について個別の開発を担当しました。

代替案を探していた時に見つけたのが「docxtemplater」でした。
さらにはVisualforceで帳票出力をしているサイトもあったのでこちらを参考にとりあえずWordファイルを出力するものは簡単にできました。
しかしここから汎用的に作り替えていく部分が思った以上に難航し、かなりの時間を費やしてしまいましたが無事にSalesforceの差し込み印刷と同等の機能を作成することができました。

参考:https://qiita.com/k-o/items/0a840f96d77cb66f7b6a

参考サイトのサンプルからの主な変更点

  1. loadFileの引数のファイルをURL直書きではなく動的に渡せるように修正(sforce.connection.remoteFunctionを使用)
  2. setDataの中身を動的に渡せるように修正(VF2枚で対応)
  3. Wordはファイルオブジェクトで管理しやすいように修正
  4. 選択リストで選択したファイルの使用項目を画面に表示(動的に表示)
  5. 使用項目をJSON形式で静的リソース管理
①ファイルの渡し方
  • JSZipUtils.getBinaryContentでbody作成してた部分を自力で作成することにした(URLでファイルを上手く指定できなかったため)
  • ドメインURLとファイル名をそれぞれ取得して引数として渡す
  • JSでOAuth認証行うのでSFのSESSIONIDも取得して渡す
  • コンテンツバージョンオブジェクトにカスタム項目を追加して対象オブジェクトやファイル名を判別できるようにした
②setData
  • 値の再読み込みができなかったため、setDataに渡す内容が確定してから別のVFに渡すことで画面表示時に値を取得できるようにした(画面表示用のVFとダウンロード用のVFに分けた)
  • 静的リソースにJSON形式で使用するオブジェクトと項目の情報を保持

APEXクラス

//definationStrの中身をVFに渡す
private String definationStr = '';
public String retDefination{
        get{
            return definationStr;
        }
        set{

        }
    }
    ~省略~
    //Apexクラスでダウンロードページに渡すためのJSON文字列を生成する
    //Wordファイル毎に"CustomObject1__c.Field1__c"の形で静的リソースで値を管理しているので
    //画面上で選択されたファイル情報から今回使用する項目を取得してSOQLを発行する
    
for(String itemName : fileUnitMap.keySet()){
    ~省略~
    itemNameUnitMap = (Map<String, Object>)fileUnitMap.get(itemName);               
    //"CustomObject1__c.Field1__c"を.を起点に分割
    List<String> tmp = itemName.split('\\.');
    objectName = tmp.get(0);
    fieldName = tmp.get(1);
    soqlResultObject = (SObject)soqlResultMap.get(objectName);
    Schema.DisplayType fldType = Schema.getGlobalDescribe().get(objectName).getDescribe().fields.getMap().get(fieldName).getDescribe().getType();
    String val = String.valueOf(soqlResultObject.get(fieldName));

    //VFでエラーがでないように、改行とダブルクォートをエスケープしてから渡す
    val = val.replaceAll('\\r\\n', '\\\\r\\\\n');
    val = val.replaceAll('"', '\\\\"');
    //first_name: 'Lightning貴志'(Wordファイルに記載した名前:値)
    String insertValue = '"'+itemName + '":"' + val +'"';
    //マッピングをしてリストに格納
    definationStr += (insertValue + ',');
}

~省略~

VFページ

<script>
            window.onload = function () {   
                let restApi = new RestAPI('{!GETSESSIONID()}');
                let fileName = (document.getElementById("page:form:pageBlock:selectList")).value;
                let outputFileName = '{!getOutputFileName}';
                let url = '{!getURLInfo}';

                //静的リソース名を引数で渡す
                restApi.apiCall(fileName,url).then((result) => {
                    var zip = new JSZip(result);
                    var doc = new Docxtemplater().loadZip(zip);
                    doc.setOptions({linebreaks: true});
                    doc.setData({     
                        {!retDefination} ←←←ここに「first_name: 'Lightning貴志'」の形式で値が渡る
                    });
                    
                    ~省略~

                    var out = doc.getZip().generate({
                        type: "blob",
                        mimeType: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
                    }) //Output the document using Data-URI
                    //saveAs(out, outputFileName + '.docx'); //ファイル名設定
                    //画面からのダウンロードはせずにメール送信する
                });
            }  
        </script>

運用してみて

ここで完成形を使用してもらったのですが、運用上の都合で修正しなければならない点がでてきました。

標準の差し込み印刷だとメールが届いてそこにWordファイルが添付される仕様は手間だと思い、ボタン押下時に画面上でファイルのダウンロードができるのは便利かと思い実装したのですが
弊社の運用ではメール送信が必要であったため作成したファイルをメール送信するように変更しました。
結果としてかなり標準の差し込み印刷に近い形での実装になったと思います。

とりあえずはClassic時の標準差し込み印刷と使い勝手が大きく変わらないように実装しましたが、今後はLEXのメリットを取り入れてより使いやすくなるよう改修していけたらと思います。

Pocket
LINEで送る