あなたのコードには、深刻な問題がいくつかあります。気分を害しないことを願っています。それは素晴らしい要件であり、学習の機会でもあります...
after insert
ここでは意味がありません。定義上、この Opportunity の挿入を終了したばかりの場合、まだ連絡先の役割はありません。*
after update
OKっぽいです。before update
値を入力するだけで、データベースに無料で保存できるため、より良いでしょう。
- このロジックを OpportunityContactRole の同様のトリガーに複製する必要があるかもしれません。コードを複製する代わりに、両方の場所から呼び出すことができるクラスを使用しますか?
LeadSource
Opportunityがnullになる場合にのみ、入力したいと思いますか?
- ループ内に選択があります。これは非常に悪い習慣です ;) トリガーは「一括」ではありません。最大 20 の制限があるため、誰かが 1 つのトランザクションで (たとえば、データ ローダを使用して) 10 件の商談を更新すると、失敗する可能性があります。トリガーでのクエリ。
ORDER BY
などは必要ありませんLIMIT 1
。Salesforce があなたを保護し、1 人の連絡先のみがプライマリになることを許可します。そんな間違いをData Loaderで読み込んでしまいたくても。
- Salesforce でリレーションシップがどのように機能するかについて読むと役立つようです。「ドット表記」とサブクエリは、通常の SQL 言語と比べると少し奇妙に見えますが、これらを使用してコードを大幅に簡素化できます。過去にオブジェクト指向プログラミングを行ったことがある場合に役立ちます。ここにいくつかのガイドがあり、ここに公式ドキュメントがあります
TL;DR
trigger fillLeadSource on Opportunity (before update) {
/* I'm assuming you want to fill LeadSource in only if it was blank.
If that's not the case - just delete this first part of code and in the query instead of ":oppsToFill" bind to ":trigger.new" or
":trigger.newMap.keyset()".
(I know they look weird but you can do it, bind objects/collections in queries that expect Ids / collections of Ids)
*/
Set<Id> oppsToFill = new Set<Id>();
for(Opportunity o : trigger.new){
if(o.LeadSource == null) {
oppsToFill.add(o.Id);
}
}
// Now we'll select all possible contacts wasting only 1 query.
if(!oppsToFill.isEmpty()){
List<OpportunityContactRole> roles = [SELECT OpportunityId, Contact.Name, Contact.LeadSource
FROM OpportunityContactRole
WHERE isPrimary = true AND Contact.LeadSource != null AND OpportunityId IN :oppsToFill];
if(!roles.isEmpty()){
for(OpportunityContactRole ocr : roles){
Opportunity oppToBeFilled = trigger.newMap.get(ocr.OpportunityId);
System.debug('Changing lead source on ' + oppToBeFilled.Name + ' from ' + oppToBeFilled.LeadSource + ' to ' + ocr.Contact.LeadSource + ' (thx to ' + ocr.Contact.Name + ' being the Primary Contact).');
oppToBeFilled.LeadSource = ocr.Contact.LeadSource;
}
}
}
// That's it. If there was a primary contact with Lead source, data will be copied over.
// If there was no primary contact or he didn't have the source filled in - tough luck, we did our best.
// Since it's before update, you get save to database for free.
}
#3 のコメントからの質問に答える編集
before
新しいトリガーには、類似しているが同一ではないコードが必要です (この場合、それがそうであるかどうかはあまり重要ではありませんafter
- 商談を明示的に更新する必要があります)。ここでも、見たいフィールドが直接利用できないため、少し悪いです。OpportunityId、ContactId にはアクセスできますが、Contact.LeadSource にはアクセスできません。このような何かがうまくいくはずです:
trigger ContactRoleRollup on OpportunityContactRole(after insert, after update){
Map<Id,Id> oppToContactMap = new Map<Id, Id>();
for(OpportunityContactRole ocr : trigger.new){
if(ocr.isPrimary){
oppToContactMap.put(ocr.OpportunityId, ocr.ContactId);
}
}
if(!oppToContactMap.isEmpty()){
List<Opportunity> oppsToUpdate = [SELECT Id FROM Opportunity WHERE LeadSource = null AND Id IN :oppToContactMap.keyset()];
Map<Id, Contact> contacts = [SELECT Id, LeadSource FROM Contact WHERE LeadSource != null AND Id IN :oppToContactMap.values()];
for(Opportunity o : oppsToUpdate){
Id contactId = oppToContactMap.get(o.Id);
Contact c = contacts.get(contactId);
if(c != null){
o.LeadSource = c.LeadSource;
}
}
update oppsToUpdate;
}
}
ここで興味深いのは、この更新により商談で古いトリガーが起動されるためです。私の「leadSource が入力されている場合はスキップする」を残していれば問題ありませんが、それでも 2 つのことを調べたいと思うかもしれません:
- クラスでいくつかのヘルパー静的フラグを使用し、それを「skipTriggerOnOpps」と呼びます。OpportunityContactRoles のトリガーでこのフラグを設定し、Opportunity トリガーのコード全体をラップして、すでにリード ソースの同期を処理している場合は実行されないようにします。 .
理論的には、何も変更せずに商談に「触れる」ことができます (この場合、不要な副作用ではなく、古いトリガーを利点として扱います)。私にとっては少し魔法のように見えますが、ここで何が起こっているのかについて十分にコメントされている場合、コードの削減、ロジックの重複の削減、単体テストの削減につながる可能性があります...トリガーである限り機能するafter
ため、連絡先ロールのクエリ変更したばかりの新しい値が表示されます。それはそのように見える必要があります
trigger ContactRoleRollup on OpportunityContactRole(after insert, after update){
Set<Id> oppIds = new Set<Id>();
for(OpportunityContactRole ocr : trigger.new){
if(ocr.isPrimary){
oppIds.add(ocr.OpportunityId);
}
}
if(!oppIds.isEmpty()){
update [SELECT Id FROM Opportunity WHERE LeadSource = null AND Id IN :oppIds];
// That's it. Call update directly on returned list without changing anything, let the other trigger worry about the logic
}
}