您好!
我正在使用Salesforce中的触发器,我一直遇到一个我似乎无法解决的错误,因此我希望有更多经验的人可以帮助我重回正轨。我在谷歌上搜寻并且多次弄乱我的代码结构,但是我找不到有效的算法。
目的:我的任务是编写一个触发器来处理每个开发人员维护案例排名所需的逻辑。每个开发人员都被分配案例,这些案例可能有也可能没有业务确定的优先级。每个开发人员在任何时候都只能优先考虑10个案例。任何其他情况在排名字段中只有一个空值。如果插入,更新或删除具有排名的案例,则分配给具有排名的开发者的所有其他案例必须相应地自动更新。任何排名高于10的案件都将被取消。
问题:我已完成插入和删除触发功能。该部分代码已经过测试,并且运行正常。我知道我的代码可能写得更好,所以我总是接受该领域的建议,但我想要解决的主要问题是触发器内的更新逻辑。
错误:
保存受影响的记录时遇到自定义验证错误。遇到的第一个验证错误是“Apex触发器CaseRankingTrigger导致意外异常,请联系您的管理员:CaseRankingTrigger:执行BeforeUpdate导致:System.DmlException:Update failed。第0行的第一个异常,ID为500M0000006sLVOIA2;第一个错误:SELF_REFERENCE_FROM_TRIGGER,Object( id = 500M0000006sLVO)当前处于触发器CaseRankingTrigger中,因此它无法递归更新自身:[]:Trigger.CaseRankingTrigger:第62行,第1列“。
错误:数据无效。 查看下面的所有错误消息以更正您的数据。 Apex触发CaseRankingTrigger导致意外异常,请联系您的管理员:CaseRankingTrigger:执行BeforeUpdate导致:System.DmlException:Update失败。第0行的第一个例外,ID为500M0000006sLTrIAM;第一个错误:CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY,CaseRankingTrigger:由于以下原因导致的BeforeUpdate执行:System.DmlException:更新失败。第0行的第一个例外,标识为500M0000006sLUaIAM;第一个错误:SELF_REFERENCE_FROM_TRIGGER,Object(id = 500M0000006sLUa)当前处于触发器CaseRankingTrigger中,因此无法递归更新自身:[] Trigger.CaseRankingTrigger:第74行,第1列:[]:Trigger.CaseRankingTrigger:第62行,第1列
代码:
trigger CaseRankingTrigger on Case (before insert, before update, before delete) {
// class level variables
Integer MAX = 10;
Integer MIN = 1;
String dev;
Decimal oldRank;
Decimal newRank;
/***************************************************************************************
* This block of code fires if a new Case is being inserted into the database
***************************************************************************************/
if (trigger.isInsert) {
// iterates through the Cases in the new trigger
for (Case c : trigger.new) {
// sets the developer to the developer on the Case
dev = c.Resource_Assigned__c;
// sets the new rank for the Case being inserted
newRank = c.Case_Rank__c;
// this block of code only fires if the Case rank field is not null - this allows for Cases with no rank to be inserted without affecting any other Case
if (newRank != null) {
// populates a list of Cases assigned to the developer if they have a ranking equal to or greater than the rank of the new Case being inserted
List<Case> devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND Case_Rank__c >= :newRank];
// iterates over the list of Cases and increases their rank value by 1 to create room for the new Case then inserts that list back into the database
for (Case devCase : devCases) {
devCase.Case_Rank__c = devCase.Case_Rank__c + 1;
}
update devCases;
// populates a list of Cases for the assigned developer if they have a ranking greater than the highest rank allowed
devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND Case_Rank__c > :MAX];
// iterates over the list of applicable Cases with a rank greater than 10 and nulls out the rank value then inserts that list back into the database
for (Case devCase : devCases) {
devCase.Case_Rank__c = null;
}
update devCases;
}
}
}
/***************************************************************************************
* This block of code fires if an existing Case is being updated
***************************************************************************************/
else if (trigger.isUpdate) {
for (Case cOld : trigger.old) {
oldRank = cOld.Case_Rank__c;
}
for (Case c : trigger.new) {
dev = c.Resource_Assigned__c;
newRank = c.Case_Rank__c;
if (oldRank == null && newRank != null) {
List<Case> devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND Case_Rank__c >= :newRank];
for (Case devCase : devCases) {
devCase.Case_Rank__c = devCase.Case_Rank__c + 1;
}
update devCases;
devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND (Case_Rank__c > :MAX OR Case_Rank__c < :MIN)];
for (Case devCase : devCases) {
devCase.Case_Rank__c = null;
}
update devCases;
} else {
if (newRank != null) {
List<Case> devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND Case_Rank__c >= :newRank];
for (Case devCase : devCases) {
devCase.Case_Rank__c = devCase.Case_Rank__c + 1;
}
update devCases;
devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND (Case_Rank__c > :oldRank OR Case_Rank__c < :newRank)];
for (Case devCase : devCases) {
devCase.Case_Rank__c = devCase.Case_Rank__c - 1;
}
update devCases;
devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND (Case_Rank__c > :MAX OR Case_Rank__c < :MIN)];
for (Case devCase : devCases) {
devCase.Case_Rank__c = null;
}
update devCases;
}
}
}
}
/***************************************************************************************
* This block of code fires if an existing Case is deleted from the database
***************************************************************************************/
else if (trigger.isDelete) {
// iterates through the Cases in the old trigger
for (Case c : trigger.old) {
// sets the developer to the developer on the Case
dev = c.Resource_Assigned__c;
// sets the old rank value for the Case being deleted
oldRank = c.Case_Rank__c;
// this block of code only fires if the rank field is not null - this allows for Cases with no rank to be deleted without affecting any other Case
if (oldRank != null) {
// populates a list of Cases assigned to the developer if they have a ranking greater than the rank of the Case being deleted
List<Case> devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND Case_Rank__c > :oldRank];
// iterates over the list of applicable Cases and decreases their rank by 1 so there is no gap in rank priority then inserts that list back into the database
for (Case devCase : devCases) {
devCase.Case_Rank__c = devCase.Case_Rank__c - 1;
}
update devCases;
}
}
}
/***************************************************************************************
* Fall Through
***************************************************************************************/
else {}
}
我真的很感谢你们在这个方面能给我的任何帮助!
此致
迈克尔
最终更新:在使用@Egor提供的建议后,我能够完成我的代码。
触发码:
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/09/15
* @brief: This is a trigger for the Case object that will modify the rank of the Cases
* assigned to the developer based on a priority set by the Administrator.
* @files: src\classes\CaseRankTriggerHandler.cls
* src\classes\CaseRankTriggerHandlerTest.cls
* src\layouts\Case-Salesforce Service Ticket.layout
* src\objects\Case.object
* src\workflows\Case.workflow
***************************************************************************************/
trigger CaseRankTrigger on Case (before insert, before update, before delete) {
/*
* The CaseRankTriggerHandler constructor method takes (List<Case>, List<Case>, String)
*/
if (trigger.isInsert) CaseRankTriggerHandler handler = new CaseRankTriggerHandler(trigger.new, trigger.old, 'Insert'); // if a new Case is being inserted into the database
if (trigger.isUpdate) CaseRankTriggerHandler handler = new CaseRankTriggerHandler(trigger.new, trigger.old, 'Update'); // if an existing Case is being updated
if (trigger.isDelete) CaseRankTriggerHandler handler = new CaseRankTriggerHandler(trigger.new, trigger.old, 'Delete'); // if an existing Case is deleted from the database
}
触发处理程序代码:
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/09/15
* @brief: This is a Case object trigger handler class that provides logic to the CaseRankTrigger for manipulating
* the ranks of all Cases assigned to a developer based on a priority that is set by an Administrator.
* @files: src\classes\CaseRankTrigger.cls
* src\classes\CaseRankTriggerHandlerTest.cls
* src\layouts\Case-Salesforce Service Ticket.layout
* src\objects\Case.object
* src\workflows\Case.workflow
***************************************************************************************/
public with sharing class CaseRankTriggerHandler {
// class level variables
private static Boolean firstRun = true;
private static Boolean modify = false;
private static Integer MAX = 10;
private static Integer MIN = 1;
private List<Case> newTrigger {get; set;}
private List<Case> currentTrigger {get; set;}
private List<Case> cases {get; set;}
private List<Case> newList {get; set;}
private List<Case> currentList {get; set;}
private String developer {get; set;}
private Decimal newRank {get; set;}
private Decimal currentRank {get; set;}
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/16/15
* @brief: Class constructor method.
* @return: Void
***************************************************************************************/
public CaseRankTriggerHandler(List<Case> newT, List<Case> oldT, String type) {
if (firstRun) { // makes sure that the trigger only runs once
firstRun = false;
InitializeTrigger(newT, oldT, type); // initializes the trigger
if (developer != null) { // skips trigger if DML is performed on a Case with no developer assigned
ModificationCheck(type); // determines if Cases need to be modified
if (modify) ModificationLogic(type); // modifies Cases if needed
}
}
}
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/16/15
* @brief: The InitializeTrigger method initializes the handler class based on the type of trigger fired.
* @return: Void
***************************************************************************************/
private void InitializeTrigger(List<Case> newT, List<Case> oldT, String type) {
if (type == 'Insert') {
this.newTrigger = newT;
this.developer = newTrigger[0].Resource_Assigned__c;
this.newRank = newTrigger[0].Case_Rank__c;
this.newList = [SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :developer AND Case_Rank__c != null AND Case_Rank__c = :newRank ORDER BY Case_Rank__c];
} else if (type == 'Update') {
this.newTrigger = newT;
this.currentTrigger = oldT;
this.developer = newTrigger[0].Resource_Assigned__c;
this.newRank = newTrigger[0].Case_Rank__c;
this.currentRank = currentTrigger[0].Case_Rank__c;
this.newList = [SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :developer AND Case_Rank__c != null AND Case_Rank__c = :newRank ORDER BY Case_Rank__c];
this.currentList = [SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :developer AND Case_Rank__c != null AND Case_Rank__c = :currentRank ORDER BY Case_Rank__c];
} else if (type == 'Delete') {
this.currentTrigger = oldT;
this.developer = currentTrigger[0].Resource_Assigned__c;
this.currentRank = currentTrigger[0].Case_Rank__c;
}
}
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/16/15
* @brief: The ModificationCheck method ensures various conditions are met, depending on the type
* of trigger that was fired, before modifying the ranks of the Cases assigned to the developer.
* @return: Void
***************************************************************************************/
private void ModificationCheck(String type) {
if (type == 'Insert') {
// the Case being inserted has a new rank not equal to null and if the assigned developer already has a Case with the
// same rank as the new rank, we will proceed to modification, if not the record will be inserted without modification.
if (newRank != null && !newList.isEmpty()) {
modify = true;
}
} else if (type == 'Update') {
// if the Case being updated has ranks with different values in both triggers we will proceed to the next check, if not the record is updated without modification.
if (newRank != currentRank) {
// if the Case being updated has a (new rank equal to null and a current rank not equal to 10) or
// if the Case being updated has a new rank not equal to null, we will proceed to the next check,
// if not the record is updated without modification.
if ((newRank == null && currentRank != 10) || newRank != null) {
// if the assigned developer on the Case being updated already has a Case with the same rank as the new or current rank, we will proceed to modification,
// if not the record is updated without modification.
if (!newList.isEmpty() || !currentList.isEmpty()) {
modify = true;
}
}
}
} else if (type == 'Delete') {
// if the Case being deleted has current rank not equal to null, we will proceed to modification, if not the record is deleted without modification.
if (currentRank != null) {
modify = true;
}
}
}
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/16/15
* @brief: If a Case rank needs to be updated the ModificationLogic method calls the appropriate
* computation method based on trigger type and the values of newRank and currentRank.
* @return: Void
***************************************************************************************/
private void ModificationLogic(String type) {
if (type == 'Insert') {
for (Case c : newTrigger) {
// calls the IncreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than or equal to the new rank.
IncreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c >= :newRank ORDER BY Case_Rank__c]);
}
} else if (type == 'Update') {
for (Case c : newTrigger) {
if (currentRank == null) {
// if the current rank is null - calls the IncreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than or equal to the new rank.
IncreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c >= :newRank ORDER BY Case_Rank__c]);
} else if (newRank == null) {
// if the new rank is null - calls the DecreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than the current rank.
DecreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c > :currentRank ORDER BY Case_Rank__c]);
} else if (newRank > currentRank) {
// if the new rank is greater than the current rank - calls the DecreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank less than or equal to the new rank and greater than to the current rank.
DecreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND (Case_Rank__c <= :newRank AND Case_Rank__c > :currentRank) ORDER BY Case_Rank__c]);
} else if (newRank < currentRank) {
// if the new rank is less than the current rank - calls the IncreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank a.
IncreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :newTrigger AND Resource_Assigned__c = :developer AND (Case_Rank__c >= :newRank AND Case_Rank__c < :currentRank) ORDER BY Case_Rank__c]);
}
}
} else if (type == 'Delete') {
for (Case c : currentTrigger) {
// calls the DecreaseCaseRank method and passes it a list of Cases that are assigned to the developer that have a rank greater than the current rank.
DecreaseCaseRank([SELECT Subject, CaseNumber, Case_Rank__c FROM Case WHERE Id NOT IN :currentTrigger AND Resource_Assigned__c = :developer AND Case_Rank__c > :currentRank ORDER BY Case_Rank__c]);
}
}
}
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/16/15
* @brief: The DecreaseCaseRank method provides the logic required to properly
* decrease or null out the ranks of the Cases assigned the the developer.
* @return: Void
***************************************************************************************/
private void DecreaseCaseRank(List<Case> cases) {
// if the list of Cases passed in by the ModificationLogic method isn't empty then it will iterate through the
// list and decrease their ranks by 1 or null out the rank if it is not within the acceptable limits (1-10).
if (!cases.isEmpty()) {
for (Case c : cases) {
if (c.Case_Rank__c >= 1 && c.Case_Rank__c <= 10) {
c.Case_Rank__c = c.Case_Rank__c - 1;
} else {
c.Case_Rank__c = null;
}
}
update cases;
}
return;
}
/***************************************************************************************
* @author: Michael *REDACTED*
* @email: *REDACTED*
* @date: 11/16/15
* @brief: The IncreaseCaseRank method provides the logic required to properly
* increase or null out the ranks of the Cases assigned the the developer.
* @return: Void
***************************************************************************************/
private void IncreaseCaseRank(List<Case> cases) {
// if the list of Cases passed in by the ModificationLogic method isn't empty then it will iterate through the
// list and increase their ranks by 1 or null out the rank if it is not within the acceptable limits (1-10).
if (!cases.isEmpty()) {
for (Case c : cases) {
if (c.Case_Rank__c >= 1 && c.Case_Rank__c < 10) {
c.Case_Rank__c = c.Case_Rank__c + 1;
} else {
c.Case_Rank__c = null;
}
}
update cases;
}
return;
}
}
答案 0 :(得分:1)
问题是您正在尝试更新已触发触发器的相同案例。
devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND (Case_Rank__c > :MAX OR Case_Rank__c < :MIN)];
for (Case devCase : devCases) {
devCase.Case_Rank__c = null;
}
update devCases
你的select语句也将返回触发此触发器的情况,因为它的Case_Rank__c为null,因此符合条件。与您运行的选择相同,用于更新排名为1-10的案例。
尝试devCases = [SELECT Id, Case_Rank__c FROM Case WHERE Resource_Assigned__c = :dev AND (Case_Rank__c > :MAX OR Case_Rank__c < :MIN) AND Id NOT IN trigger.new];
查看https://help.salesforce.com/apex/HTViewSolution?id=000005278&language=en_US以了解抛出异常的说明