I'm having trouble with a Salesforce lead trigger, fired after insert. The lead assignment rules need to be on by default, but I need to bypass the rules for leads that match a certain criteria.
I've seen in the salesforce docs you are able to set assignment rules when inserting/updating leads: http://www.salesforce.com/us/developer/docs/apexcode/Content/apex_methods_system_database_dmloptions.htm#assignmentRuleHeader_section
However, I'm not able to turn off these lead assignment rules:
...
// if criteria is met to skip assignment rules...
Lead tempobj1 = new Lead(id=objLead.id,OwnerId=ConDomainNameAndOwnerIdMap.get(Emaildomain),isOwnerChanged__c = true);
Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.useDefaultRule= false; //it's still running assignment rules!
tempobj1.setOptions(dmo);
...
...
update tempobj1;
I've also tried creating a new lead assignment rule set with no actual rules and set the DML options to use this - but the default assignment rules are still fired! dmo.assignmentRuleHeader.assignmentRuleID = '01Qe00000000HAq';
The order of execution states the record is saved to database (not committed) > after triggers executed > assignment rules executed. So the trigger is run before assignment rules.
I've tried the code on before insert, but get the error 'DML statment cannot operate on trigger.new or trigger.old: Trigger.AssignOwnerToNewLead: line 133, column 1'
Thanks in advance,
Sam
use this before trigger
for (Lead objLead : Trigger.new) {
Database.DMLOptions dmo = new Database.DMLOptions();
dmo.assignmentRuleHeader.useDefaultRule = false;
objLead.OwnerID = objLead.Eloqua_ID__c;
objLead.setOptions(dmo);
}
}
Create a new lead assignment rule with no assignments name it
AssignmentSkip
In apex programmatically set that id on the rule header options
Related
rule "attaching AV and impact rating"
agenda-group "evaluate likelihood"
dialect "java"
when
Application($threatList:getThreatList())
$av:AttackVector()
exists $threat:Application.Threats(impact == "Disclose Information")from $threatList
exists AttackVector($av == AttackVector.REQUEST_MANIPULATION)
then
RiskRating riskRating=new RiskRating($threat.getImpactRating(),$av.getLikelihood(),$av.getName());
insertLogical(riskRating);
end
I am working on getting the object $threat in THEN part of the above-mentioned rule. If I run the above rule, it says:
Rule Compilation error : [Rule name='attaching AV and impact rating']
referee/security/attack/Rule_attaching_AV_and_impact_rating1426933818.java (7:1053) : $threat cannot be resolved
If I loop through it and get the value in the THEN part, it causes a CARTESIAN product and inserts the values a number of times in the session. My rule looks like this when I get the cartesian product.
rule "attaching AV and impact rating"
agenda-group "evaluate likelihood"
dialect "java"
when
Application($threatList:getThreatList())
$av:AttackVector()
exists $threat:Application.Threats(impact == "Disclose Information")from $threatList
$threat:Application.Threats(impact == "Disclose Information")from $threatList
exists AttackVector($av == AttackVector.REQUEST_MANIPULATION)
then
RiskRating riskRating=new RiskRating($threat.getImpactRating(),$av.getLikelihood(),$av.getName());
insertLogical(riskRating);
end
How do I get the value of $threat in THEN part without having the cartesian product?
Remove the exists operation entirely.
rule "attaching AV and impact rating"
agenda-group "evaluate likelihood"
dialect "java"
when
Application($threatList:getThreatList())
$av: AttackVector()
$threat: Application.Threats(impact == "Disclose Information")from $threatList
exists(AttackVector($av == AttackVector.REQUEST_MANIPULATION))
then
RiskRating riskRating=new RiskRating($threat.getImpactRating(),$av.getLikelihood(),$av.getName());
insertLogical(riskRating);
end
exists means "there is a thing in working memory that matches these conditions/looks like this". It's not used to actually extract or provide a reference to that matching instance. Simply remove the operator and it works as you need -- if there is an Application.Threats that matches your conditions, the rule triggers and the matching value is assigned to the $threat variable.
What you're running into is the fact that you have multiple threats that mean your condition, which is why you're having multiple triggers of the rule -- it will trigger once per matching Application.Threats. The exists keyword mitigates this because it only cares that at least one match exists, but you don't get a reference (because if there's four matches which one will be assigned to the variable? it doesn't make sense, logically.)
So you need to change your rule so that it won't fire multiple times and will instead only fire once when it finds a match. Usually you'd do this by making the consequences do something to working memory that makes the rule no longer eligible to be fired. In your example, you insert a RiskRating object; you could, then, check that no risk rating exists in your conditions:
not( RiskRating( /* insert criteria here or leave empty */ ) )
Alternatively you could retract something from working memory that your rule relies on to be present or a match. For example, if you don't need it for anything later on, you could retract the attack vector:
retract( $av )
Yet another option might be to try and update your getThreatList() implementation to return a Set instead so you don't have duplicates (assuming threats are considered duplicates based on the 'impact' field.) Or you could try to remove all Application.Threats instances that match the criteria from the threatlist being returned.
We simply don't know enough about your use case or rule set to know what data you need or what it looks like, but at the end of the day you simply need the rule to fire once and only once, so to do this you need to somehow update the rule to know that it's no longer valid.
I would like the "then" clause to only execute one time, but it is executing for each child object in the list that matches.
If any item in the list meets the condition I want to break out and have only one execution of the then clause.
rule "Profile - Delinquent"
when
$c : CreditReportAll( $creditLiability : creditLiability )
$cs: CreditLiability( paymentPatternData.contains("X")) from $creditLiability
then
CreditUserSegment $cu = new CreditUserSegment();
$cu.setSegmentCode("delinquent");
$c.addUserSegmentToList($cu);
end
In order to only fire the rule once when any item in the $creditLiability list meets the condition, you either need to write your rule such that it doesn't iterate across the list, or you need to update your rule so that once it does fire, it changes the facts in working memory to not allow it to fire again.
No iteration
The easiest way to do this is to change your rule to not iterate across the list. To do this, we use the exists keyword like this:
rule "Profile - Delinquent"
when
$c : CreditReportAll( $creditLiability : creditLiability )
exists( CreditLiability( paymentPatternData.contains("X")) from $creditLiability )
then
CreditUserSegment $cu = new CreditUserSegment();
$cu.setSegmentCode("delinquent");
$c.addUserSegmentToList($cu);
end
The exists keyword will match when the there is at least one element present that matches the required condition. Note that we don't assign a variable to the result anymore, because it doesn't make any sense (eg. there's no assignment of $cs here; it would be ambiguous as to what it would even refer to.)
The downside to this approach is that if you update working memory in any other rule (eg by calling insert, modify, update, and so on), you may end up triggering this rule again because the conditions on the left hand side will still remain valid and matching. To alleviate this you may be able to leverage the no-loop rule attribute (depending on your setup). Otherwise you'll want to update your rule (or data in working memory) so that your rule is no longer valid.
Invalidate the rule
The other way to only trigger the rule once is to make the rule no longer valid to fire after it fires once. A trivial way to do this (likely not best practice in this case) would be to insert a flag into working memory and check on its presence. In this case I will use a simple string "DELINQUENT" as the flag.
rule "Profile - Delinquent"
when
not(String(this == "DELINQUENT"))
$c : CreditReportAll( $creditLiability : creditLiability )
$cs: CreditLiability( paymentPatternData.contains("X")) from $creditLiability
then
CreditUserSegment $cu = new CreditUserSegment();
$cu.setSegmentCode("delinquent");
$c.addUserSegmentToList($cu);
insert("DELINQUENT");
end
When the rule fires, it inserts a string that says "DELINQUENT" into working memory. The rule conditions are such that the rule only fires if this string doesn't exist in working memory. Thus after the first execution, the rule will not execute again unless a rule retracts that string.
This solution increases the memory footprint of the rule execution because there is more information in working memory. However unlike the other solution (which is more elegant), this version will not re-fire if another rule retriggers execution (eg. via update.)
I am using decision tables and would like to trigger one rule per input item.
I am using decision have set the Sequential = true and defined all rules as part of the same ACTIVATION-GROUP.
When I trigger the drools rules engine using below it just evaluates for the first input item and others are ignored. The behavior I want is to evaluate at most 1 rule per input item (rule order defined by the Salience).
kieStatelessSession.execute(inputList)
I can get this working by sending one item at a time to the kieStatelessSession, but would prefer to execute all at once.
I am using Drools verison 6.5.0.FINAL and Java 7.
There is no out of the box support in Drools for what you are trying to achieve. If you want your rules to be evaluated once for each fact, you will need to code it yourself.
One approach could be to have another type of facts to mark when one of the inputs is processed:
declare Marker
fact : Object
end
//Bellow are the rules that should be coming from your decision table.
//Each rule will do whatever it needs to do, and then it will create a
//Marker fact for the fact that was processed.
//These rules now include a "not" Conditional Element to avoid a fact to be
//evaluated more than once.
rule "Rule 1"
salience 100
when
$fact: Object(...) //your conditions
not Marker(fact == $fact)
then
//... Your logic
insert(new Marker($fact));
end
...
rule "Rule 50"
salience 50
when
$fact: Object(...) //your conditions
not Marker(fact == $fact)
then
//... Your logic
insert(new Marker($fact));
end
Hope it helps,
I'm trying to write a rule to calculate prices for an insurance product based on conditions. In the 'when' I'm using an object called AdditionalDriver, which contains the details for drivers other than the policy holder. From this, different prices can be calculated based on whether the additional driver is a parent, friend, spouse etc. See below:
when
AdditionalDriver($relToProp : relationToProposer)
then
String relToProp = $relToProp;
if(!relToProp.equals("P"))
{
//prices
}
end
"P" = parent.
This rule works when an additional driver has been added. However, if there is no additional driver, then the object is empty, and so the rule does not run. What do I need to do to get this rule to run, even when the object is empty?
Thanks in advance.
You should write one rule for each of the relative or acquaintance classes:
when
PolicyHolder( $phid: id )
AdditionalDriver( relationToProposer == "P", belongsTo == $phid )
then
//prices
end
For no additional driver being requested, write a rule
when
PolicyHolder( $phid: id )
not AdditionalDriver( belongsTo == $phid )
then
// cheaper prices
end
Don't use conditional statement in your consequences to further distinguish facts. This is a code smell.
I have created a view in PostgreSQL (9.4), which pulls information from several tables, so as far as I understand, is not updatable unless I use triggers or rules.
For instance, this is the view definition (the tables and everything are located inside a scheme called pra1:
CREATE VIEW pra1.vacunaciones AS
SELECT pra1.responsable.poliza,
pra1.recibe.niño,
pra1.niño.nombre,
pra1.vacunas.nombre_vacuna,
pra1.recibe.fecha,
pra1.recibe.fiebre
FROM pra1.responsable, pra1.recibe, pra1.niño, pra1.vacunas
WHERE pra1.recibe.vacuna = pra1.vacunas.id
AND pra1.niño.id = pra1.recibe.niño
AND pra1.responsable.dni = pra1.niño.responsable;
So if I do
SELECT * FROM pra1.vacunaciones
Everything works fine and I get the information I want to get.
My question: The field coming from pra1.recibe.fiebre is a boolean value. I would like to set a rule, so whenever I do an UPDATEstatement specifying the pra1.recibe.niño value (which is a bigint), then the pra1.recibe.fiebre field corresponding to pra1.recibe.niño toggles from true to false or viceversa.
I got as far as this:
RULE DEFINITION:
CREATE or REPLACE RULE vac_boolean AS
ON UPDATE TO pra1.vacunaciones
DO INSTEAD
UPDATE pra1.recibe SET fiebre (no idea how to follow)
WHERE pra1.recibe.niño =
//I don't know how to specify that it needs to act
on the pra1.recibe.niño value that I pass to the UPDATE.
So after the rule is created I would like to do a short UPDATE statement specifying WHERE pra1.recibe.niño = XXX, and the rule should act on the view and update the pra1.recibe.fiebre toggling its boolean value.
Is this idea easy to accomplish? I am a newcomer to PostgreSQL, and so far I am pleased with it, but there are still many confusing areas I need to understand well.
Maybe someone comes with a better suggestion, but I just realized what I needed:
The rule:
CREATE or REPLACE RULE vacunaciones_fiebre AS
ON UPDATE TO pra1.vacunaciones
DO INSTEAD
UPDATE pra1.recibe SET fiebre = NOT fiebre WHERE niño = NEW.niño;
The update (for instance, to update the status of niño = 4:
UPDATE pra1.vacunaciones SET fiebre = NOT fiebre WHERE niño='4';
Like I said, maybe there is a better way, but this does it.