プログラマ38の日記

主にプログラムメモです。

雑記: メモリを追加してとても快適になりました

メモリ4GBでHDDのPCを使っています。
Windows7をWindows10にアップデートをしたものでだいぶ古くなっていた(2013年購入)のですが、このタイミングで8GBのメモリを追加し、12GBのメモリとなりました。

 

今までは、ブラウザでタブを20,30と開いていくとメモリが限界に達していましたが、12GBにしたことでまだまだ余裕があります。

 

HDDなのでPCの起動や、アプリケーションの初回の起動は遅いといえば遅いですがまず気になりません。

キーボードも何も問題がなく、画面も綺麗です。Let's note最高です。

 

 

Let's noteに慣れすぎて他のPCのキーボードだとすごいストレスがあるので、Let's noteのキーボードの外付けみたいなのがあったら買いたいです。探してみよっと。

 

Salesforce: lightning:picklistPathを使った2段階のステータス表示の実装

f:id:crmprogrammer38:20200426163432p:plain

前に上記のような2段階でのステータス表示について、lightning:picklistPathを使ったアイデアを書きました。
 

crmprogrammer38.hatenablog.com

 

そのアイデアの実装内容になります。

実装内容

  •  パスによる1段階目のステータスと、lightning:picklistPathによる2段階目のステータスを表示
  • 2段階目のステータスは1段階目のステータスの連動項目のみを表示
  • 2段階目のステータスのリスト値をクリックすると確認ダイアログを表示後、値を変更する。

オブジェクト項目

オブジェクト:サブのパス(SubPath__c)
選択リスト(制御):ステータスLv1(StatusLv1__c)
選択リスト(連動):ステータスLv2(StatusLv2__c)

選択リストの連動関係

ステータスLv1 ステータス1 ステータス2 ステータス3 ステータス4 ステータス5
ステータスLv2 ステータスA        
ステータスB        
ステータスC ステータスC      
ステータスD ステータスD      
ステータスE ステータスE ステータスE    
  ステータスF ステータスF ステータスF  
  ステータスG ステータスG ステータスG  
    ステータスH ステータスH  
    ステータスI ステータスI ステータスI
      ステータスJ ステータスJ
      ステータスK ステータスK

 

ステータスLv2の連動設定に対応する選択リスト項目

ステータスLv2_1(StatusLv2_1__c):ステータス1の連動項目
ステータスLv2_2(StatusLv2_2__c):ステータス2の連動項目
ステータスLv2_3(StatusLv2_3__c):ステータス3の連動項目
ステータスLv2_4(StatusLv2_4__c):ステータス4の連動項目
ステータスLv2_5(StatusLv2_5__c):ステータス5の連動項目

作成したトリガ

今回の実装では、ステータスLv2の連動設定に対応する選択リスト項目を用意したので、その項目は常にステータスLv2と同期するようにトリガを作成した。

trigger SubPathTrigger on SubPath__c (before insert , before update) {

    for(SubPath__c newrow : Trigger.New ){
        //常に同期する
        newrow.StatusLv2_1__c = newrow.StatusLv2__c;
        newrow.StatusLv2_2__c = newrow.StatusLv2__c;
        newrow.StatusLv2_3__c = newrow.StatusLv2__c;
        newrow.StatusLv2_4__c = newrow.StatusLv2__c;
        newrow.StatusLv2_5__c = newrow.StatusLv2__c;
    }
}

作成したクラス

lightning:picklistPathの表示で、どの選択リスト項目を表示するかの制御と lightning:picklistPath上で選択したリスト値を更新する処理を作成した。

public without sharing class SubPathPickList {
    
    //ステータスLv1の値に応じて表示する選択リストのフィールドを返す
    @AuraEnabled
    public static String getLv2PickFieldName(String salesforceId){    
        
        List rows = [select Id, StatusLv1__c From SubPath__c where Id =:salesforceId ];
        
        Map<String, String> fieldMapForLv1 = new Map<String,String>();
        fieldMapForLv1.put('ステータス1','StatusLv2_1__c');
        fieldMapForLv1.put('ステータス2','StatusLv2_2__c');
        fieldMapForLv1.put('ステータス3','StatusLv2_3__c');
        fieldMapForLv1.put('ステータス4','StatusLv2_4__c');
        fieldMapForLv1.put('ステータス5','StatusLv2_5__c');
        
        String fieldName = 'StatusLv2_1__c';
        
        if( rows.isEmpty() == false){
            if( fieldMapForLv1.get(rows[0].StatusLv1__c) != null ){
                fieldName = fieldMapForLv1.get(rows[0].StatusLv1__c);
            }
        }
        
        return fieldName;
    }
    
    //ステータスLv2の値を更新
    @AuraEnabled
    public static Boolean setLv2PickFieldValue(String salesforceId, String pickValue){    
        
        List rows = [select Id From SubPath__c where Id =:salesforceId ];
        
        if( rows.isEmpty() == true){
            return false;
        }
        
        rows[0].StatusLv2__c =pickValue ;
        
        try{
            update rows;
            return true;
        } catch( Exception ex){
            return false;
        }
    }
}

作成したLightning Component

モーダル確認ダイアログ用にbodyとfooterを用意

[ConfirmDialogBody] Component
<aura:component>
  <aura:attribute name="messageText" type="String"></aura:attribute>
<div class="slds-align_absolute-center">{!v.messageText}</div>

</aura:component>

[ConfirmDialogFooter] Component
<aura:component>
     <aura:attribute name="modalResult" type="String"></aura:attribute>
    <lightning:overlaylibrary aura:id="overlayLib"></lightning:overlaylibrary>
  <lightning:button variant="neutral" label="キャンセル" onclick="{! c.handleCancel }"></lightning:button>
    <lightning:button variant="brand" label="OK" onclick="{! c.handleOK }"></lightning:button>
</aura:component>

[ConfirmDialogFooter] Component
({
  handleOK: function(cmp, event, helper) {
    cmp.set("v.modalResult", "OK");
    cmp.find("overlayLib").notifyClose();
  },
  handleCancel: function(cmp, event, helper) {
    cmp.set("v.modalResult", "Cancel");
    cmp.find("overlayLib").notifyClose();
  }
});    

Lightningページ上に表示するLightning Component


[SubPath] Component
<aura:component
  controller="SubPathPickList"
  implements="flexipage:availableForAllPageTypes,force:hasRecordId"
  access="global"
>
  <lightning:overlayLibrary aura:id="overlayLib" />
  <aura:attribute name="recordId" type="String" />
  <aura:attribute name="modalResult" type="String" />

  <aura:attribute name="pickField" type="String" default="StatusLv2_1__c" />

  <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
  <aura:handler event="force:refreshView" action="{!c.doInit}" />

  <lightning:picklistPath
    aura:id="picklistPath"
    recordId="{!v.recordId}"
    variant="non-linear"
    picklistFieldApiName="{!v.pickField}"
    onselect="{!c.handleSelect}"
  ></lightning:picklistPath>
</aura:component>

[SubPath] Controller
({
  doInit: function(component, event, helper) {
    var recordId = component.get("v.recordId");
    var fetchEventAction = component.get("c.getLv2PickFieldName");
    fetchEventAction.setParams({ salesforceId: recordId });
    fetchEventAction.setCallback(this, function(response) {
      var fieldName = response.getReturnValue();
      component.set("v.pickField", fieldName);
    });

    $A.enqueueAction(fetchEventAction);
  },

  handleSelect: function(component, event, helper) {
    var recordId = component.get("v.recordId");
    var stepName = event.getParam("detail").value;

    $A.createComponents(
      [
        [
          "c:ConfirmDialogBody",
          { messageText: "ステータスLv2を" + stepName + "に更新しますか?" }
        ],
        [
          "c:ConfirmDialogFooter",
          { modalResult: component.getReference("v.modalResult") }
        ]
      ],
      function(components, status) {
        if (status == "SUCCESS") {
          component.find("overlayLib").showCustomModal({
            header: "ステータスLv2を更新",
            body: components[0],
            footer: components[1],
            showCloseButton: true,
            closeCallback: function() {
              if (component.get("v.modalResult") == "OK") {
                var updateAction = component.get("c.setLv2PickFieldValue");
                updateAction.setParams({
                  salesforceId: recordId,
                  pickValue: stepName
                });
                updateAction.setCallback(this, function(response) {
                  if (
                    response.getState() == "SUCCESS" &&
                    response.getReturnValue()
                  ) {
                    $A.get("e.force:refreshView").fire();

                    //refreshViewと同じタイミングでtoastの再描画を防ぐ
                    window.setTimeout(
                      $A.getCallback(function() {
                        var toastEvent = $A.get("e.force:showToast");
                        toastEvent.setParams({
                          message: stepName + "に更新しました。",
                          type: "success"
                        });
                        toastEvent.fire();
                      }),
                      1000
                    );
                  } else {
                    var toastEvent = $A.get("e.force:showToast");
                    toastEvent.setParams({
                      message: stepName + "の更新に失敗しました。",
                      type: "error"
                    });
                    toastEvent.fire();
                  }
                });

                $A.enqueueAction(updateAction);
              }
            }
          });
        }
      }
    );
  }
});

最後に

lightning:picklistPathの「onselect」アクションでは、選択された選択リスト値のAPI参照名が取得できます。表示ラベルは取得できないので表示ラベルを使いたい場合は、Apexで取得してくるなどの処理が必要になります。

 

Salesforce: lightning:picklistPath について調べたこと

lightning:picklistPath について調べて考えたことのメモです。

 

f:id:crmprogrammer38:20200424092312p:plain

上記のように、lightning:picklistPathは、いわゆるパスをLightning Componentタグで書ける機能で、次の特徴があります。

  1. パスのように表示する選択リストの項目を変数で指定できる
  2. パスの更新ボタンはない
  3. 連動選択リストの連動関係の連動項目(制御される側)でも表示される選択リスト値は連動関係の設定は関係ない
  4. レコードタイプ毎で使える選択リスト値を設定した場合は、そのレコードタイプで表示可能な選択リスト値が表示される

 

最初調べた時は、連動関係を使って2段階のステータスで制御している時にパスを2つ表示できたらいいなという期待をもって調べたのですが、3の連動関係に関係なく選択リストが表示されるのでうーんとなってました。

やりたかったのはこんな感じで、下のパスは連動項目が表示されるイメージです。

f:id:crmprogrammer38:20200424092612p:plain

 

タグをかけばこれが実現できたら便利なのにと思ってましたが、次を実装することで対処できるのでアイデアを残しておこうと思います。(実現するために色々な設定をしていてメンテナンス性は度外視しています)

  1. 連動関係の連動項目を、制御される制御項目毎に別項目として定義する、この項目はレイアウトに表示しない
  2. lightning:picklistPathを使い、制御項目の値に応じて該当する選択リスト項目を表示する
  3. lightning:picklistPathでは更新ボタンがないので、lightning:picklistPath上で選択リストが選択されたらそのタイミングで更新する処理を実装する。(onselectで、確認メッセージ表示と、選択リスト値の更新を行う。選択リスト値を更新する際に、別項目として定義した選択リスト値と、元になる連動関係の連動項目の両方を更新する)
  4. パス上ではなく、連動項目側を変更されたら、連動項目に該当する選択リスト項目も同期するようにトリガを実装する

上記で、2段階のパスが実装できます。

実装の詳細は次で記載します。

 

crmprogrammer38.hatenablog.com

 

Salesforce: 開発中の「セッションの設定」メモ

開発組織でのセッションの設定メモです。

 設定>セキュリティ>セッションの設定から次を設定します。

 

1.「ユーザとしてログインしてから再ログインを強制する」のチェックを外す

f:id:crmprogrammer38:20200422090937p:plain

チェックを外すことで、代理ログイン(内部ユーザではユーザのログインボタン、外部ユーザでは取引先責任者からのログイン)してログアウト後に、代理ログイン前のユーザのログイン状態に戻ります。
[内部ユーザ]

f:id:crmprogrammer38:20200422091405p:plain
[外部ユーザ]
f:id:crmprogrammer38:20200422091241p:plain

 

 チェックをつけると、代理ログインしてログアウトすると、代理ログイン前のユーザからもログアウトされてしまいます。

代理ログインを使ってテストを実施する時など、代理ログインするたびにログインをするのを避けられるためチェックを外します。



2.キャッシュをオフにする。

f:id:crmprogrammer38:20200422092309p:plain

上の、「パフォーマンスを向上させるためにブラウザの安全で永続的なキャッシュを有効にする」と「Lightning コンポーネントフレームワークコンテンツ配信ネットワーク (CDN) を有効化」のチェックを外します。

チェックをつけたままだと、LightningComponentの画面(COMPONENT)の修正や、画面レイアウト、Lightningページなどの修正の反映に多少時間がかかってしまいます。

設定を変更後にすぐ確認を行うためチェックを外します。

 

 

Salesforce:コミュニティページをメタデータで移行する

コミュニティページやコミュニティの設定をメタデータで移行する方法のメモです。

 

f:id:crmprogrammer38:20200421140942p:plain

まず、上記の通り設定>コミュニティ>コミュニティ設定 から「ExperienceBundle メタデータ API を有効化」をチェックいれます。

チェックをいれると、メタデータでコミュニティのメタデータを取得したり、デプロイできるようになります。

 

メタデータのタイプは、「ExperienceBundle」で、フォルダは「experiences」になります。

 

取得したメタデータをデプロイすることで他の環境にコミュニティページなどを移送することができます。

移送した際につまづいた箇所がありました。

それは コミュニティ/config/languages.json

 "defaultLabel" : "Japanese - 日本語",

となっていた箇所でエラーとなり、

 "defaultLabel" : "Japanese",

 に変更したら成功しました。(全角文字や記号はエラーになるようです)

Salesforce: 数式の 「15 オブジェクトの参照」の制限

複雑なオブジェクト構造で数式を作成する場合に気を付ける制限があります。

それは1つの数式で参照できるオブジェクト数は15オブジェクトの制限です。

(サポートに依頼すると20まで拡張できるようです)

 

数式で制限を超えると次のようなメッセージが表示されます。

f:id:crmprogrammer38:20200420084529p:plain

 

そして、ここの「15 オブジェクトの参照」の数のカウントですが、オブジェクトの数ではなく参照の数になります。

例えば、作成者の姓と、更新者の姓の2項目(CreatedBy.LastName, LastModifiedBy.LastName )を使った場合、オブジェクトは両方同じユーザですが、オブジェクトの参照数は2となります。

 

最後に

数式は後から追加していくことが多く、このエラーが発生してしまうとオブジェクト構造を見直したり、当初は想定していなかったトリガ/プロセスビルダーなどが必要になったりします。

ある程度設計時からこの制限を見越しておくのがいいと思いますが、なかなかそこまで頭がまわらないことが多いです。

Salesforce: 監査項目の設定についてのメモ

監査項目の設定で、作成日などをセットすることができます。
調べた結果の備忘としてメモします。

 

関連するSalesforceのHelp

ヘルプ | トレーニング | Salesforce


Salesforceの設定

ユーザインターフェースから”「レコードの作成時に監査項目を設定」および「無効な所有者のレコードを更新」ユーザ権限を有効化” をチェックする

f:id:crmprogrammer38:20200416082525p:plain

権限を付与したいユーザのプロファイル、または権限セットで”レコードの作成時に監査項目を設定”をチェックする

[プロファイル]

f:id:crmprogrammer38:20200416085251p:plain
[権限瀬尾っと]

f:id:crmprogrammer38:20200416085633p:plain


監査項目の設定はAPIでのインサート時のみ

データローダなどAPIでのインサート時のみセットできます。新規データと更新データが混在するデータの場合は、新規データだけで監査項目のセットを行う必要があります。

 

監査項目で設定できる項目

オブジェクト 項目ラベル 項目名
対象のオブジェクト ※ 作成日 CreatedDate
  作成者 ID CreatedById
  最終更新日 LastModifiedDate
  最終更新者 ID LastModifiedById
リード 取引開始日 ConvertedDate
  取引開始済みの取引先 ID ConvertedAccountId
  取引開始済みの取引先責任者 ID ConvertedContactId
  取引開始済みの商談 ID ConvertedOpportunityId
ケース 完了日 ClosedDate


※ 作成日/作成者 ID/最終更新日/最終更新者 ID がセットできるオブジェクトは全てではありません。ヘルプに詳細の記載がありますが、次のようなオブジェクトにはセットできません。

[監査項目をセットできないオブジェクト例]
納入商品
キャンペーン
商談商品
品目名スケジュール
注文
注文商品
商品