I'm trying to create trigger if record type is Revenue Risk then amount should be saved in negative value, Here's my code in which I'm having error, I tried it two ways, second is in comments.. none of them is working
public with sharing class amountValidator {
//pull data of Opportunity in list
public static void validateAmount (list<Opportunity> oppList){
oppList = [Select amount FROM Opportunity WHERE RecordType.Name IN ('Revenue Risk')];
for(Opportunity opportunities : oppList){
if(oppList.amount >= '0'){
oppList.amount = oppList.amount * '-1';
}
}
/*Map<String,Schema.RecordTypeInfo> rtMapByName = d.getRecordTypeInfosByName();
Schema.RecordTypeInfo rtByName = rtMapByName.get('Revenue Risk');
for(Opportunity each : oppList){
if(rtByName.size == 0){
}
else{
if(oppList.Amount >= 0){
oppList.Amount = oppList.Amount*-1;
}
}
}*/
The error is very clear:
if(oppList.amount >= '0'){ // THIS LINE WILL THROW AN ERROR: 'Comparison arguments must be compatible types: Integer (or Double), String
oppList.amount = oppList.amount * '-1'; // THIS ONE TOO: 'Arithmetic expressions must use numeric arguments'
}
Your second code snippet is also wrong (same for first one).
if(oppList.Amount >= 0){
oppList.Amount = oppList.Amount*-1;
// MUST BE each.Amount = each.Amount * - 1; Please try not to use reserved words as variable names
}
You may want to take a look at a previous post describing strongly typed programming languages: Strongly Typed
Since we can't add comments just yet, we're going to add a new answer:
You're not updating/inserting the updated amount for your opportunity.
The correct way of doing this is to create a separate List of Opportunities (i.e. List oppsToUpdate) and add the updated opportunities to this list.
public static void validateAmount (list<Opportunity> oppList){
oppList = [Select amount FROM Opportunity WHERE RecordType.Name IN ('Revenue Risk')]; // Why are you requerying the Opportunity if you already have it??
List<Opportunity> oppsToUpdate = new List<Opportunity>();
for(Opportunity opportunities : oppList){
if(opportunities.amount > 0){
opportunities.amount = opportunities.amount * -1;
oppsToUpdate.add(opportunities);
}
}
upsert opportunities;
}
Remember to enclose your function with try-catch statements with system debugs to see what's going on with your code.
And this is the link to the input parameter modifications and why this is bad practice: Input Parameters
Working Code:
trigger Risk_NegativeQuantity on OpportunityLineItem (before insert) {
set<id> oppid = new set<id>();
for (OpportunityLineItem oli : trigger.new)
{
oppid.add(oli.opportunityid);
}
Id RevenueRisk= Schema.SObjectType.Opportunity.getRecordTypeInfosByName().get('Revenue Risk').getRecordTypeId();
list<opportunity> opplist = [select id, recordtype.name,recordtypeid from opportunity where id in : oppid ];
for (OpportunityLineItem oli : trigger.new)
{
for (opportunity opp: opplist)
{
if (oli.opportunityid == opp.id)
{
if(opp.recordtype.name == 'Revenue Risk')
{
if(oli.Quantity > 0)
{
oli.Quantity = oli.Quantity * -1;
}
}
}
}
}
}
Related
public static void updatecasefields(List<Case> lstcase) {
//List<Case> lstcase = new list<case>();
ID devRecordTypeId = Schema.SObjectType.Case.getRecordTypeInfosByDeveloperName().get('CRM_CSR_Case').getRecordTypeId();
for (Case cs: lstcase) {
if(cs.ID != null && cs.RecordTypeId == devRecordTypeId) {
}
List<CRM_CasePick__c> Casp = [SELECT Id, CRM_Carrier_Name__c,CRM_LOB__c, CRM_SLA_Turnaround_Time__c,CRM_Category__c, CRM_Issue_Sub_Type__c,CRM_Issue_Type__c,CRM_Turnaround_Time_Days__c FROM CRM_CasePick__c WHERE CRM_Carrier_Name__c = :cs.GiDP_CarrierName__c AND CRM_Category__c = :cs.CRM_Category__c AND CRM_Issue_Type__c = :cs.CRM_Issue_Type__c AND CRM_Issue_Sub_Type__c = :cs.CRM_Issue_Sub_Type__c AND CRM_LOB__c = :cs.CRM_Line_of_Business__c];
for(CRM_CasePick__c CP: Casp) {
cs.CRM_Turnaround_Time_Days__c = cp.CRM_Turnaround_Time_Days__c;
cs.CRM_SLA_Turnaround_time__c = cp.CRM_SLA_Turnaround_Time__c;
}
}
}
Remove the SOQL query from the for loop - best practice is to never run a query within a loop.
Right now it is running that query for every value of the initial list. If the list is over 100 records, it will exceed the governor limit.
So i am new to salesforce and i finished my training and now im working on a project. But on my project i have stumbled on a test class that i am not finding a way to write it, so i would appreciate if anyone can help me figure out a way to write it. Here is the code:
public class AP01_Opportunity
{
//Method to create a new service contract when opportunity = Gagné
public static void CreateContract(List<Opportunity> listOpp, Map<Id, Opportunity> oldMap)
{
//Variable Declaration
ServiceContract sc;
List<ServiceContract> listSCToAdd = new List<ServiceContract>();
List<ContractLineItem> listContractItems = new List<ContractLineItem>();
List<Opportunity> listOppGagne = new list<Opportunity>();
//Loop in list of opportunities
for(Opportunity opp : listOpp)
{
if(opp.StageName == Label.ClotureGagne && !oldMap.get(opp.Id).isWon)
{
listOppGagne.add(opp);
}
}
//check if list has opportunity becoming won
if(listOppGagne.size() > 0){
Map<Id, Opportunity> mapOppGagne = new Map<Id, Opportunity> ([SELECT Id,
Name,
StageName,
Pricebook2Id,
Account.Name,
(SELECT Id,
PricebookEntryId,
PricebookEntry.Name,
Quantity,
UnitPrice
FROM OpportunityLineItems)
FROM Opportunity
WHERE Id in :listOppGagne]);
for( Opportunity opp : listOppGagne )
{
//Create new service contract
sc = new ServiceContract();
sc.Name = opp.Name;
sc.ApprovalStatus = Label.Activated;
sc.OpportunityId__c = Id.valueOf(opp.Id);
sc.Pricebook2Id = opp.Pricebook2Id;
sc.StartDate = Date.today();
listSCToAdd.add(sc);
}
if(listSCToAdd.size() > 0){
insert listSCToAdd;
Opportunity currentOpp;
ContractLineItem cli;
Id oppId;
for(ServiceContract servcont : listSCToAdd)
{
oppId = servcont.OpportunityId__c;
if(mapOppGagne.containsKey(oppId))
{
currentOpp = mapOppGagne.get(oppId);
//copy the oppLineItems per opportunity to the respective Service Contract
for(OpportunityLineItem items : currentOpp.OpportunityLineItems)
{
cli = new ContractLineItem();
cli.PricebookEntryId = items.PricebookEntryId;
cli.Quantity = items.Quantity;
cli.UnitPrice = items.UnitPrice;
cli.ServiceContractId = servcont.Id;
listContractItems.add(cli);
}
}
}
if(listContractItems.size() > 0)
{
insert listContractItems;
}
}
}
}
}
this code is a trigger that creates a new service contract record with contract line items copied from the opportunity line items, when the opportunity stage changes to "Cloturé Gagné" which means closed won in french.
Thank you in advance.
In order to write a simple test class I would recommend you to use the following guide: https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_qs_test.htm
The idea is simple: Lets say you create an Opportunity in your Test class and make an insert or update in your case - your trigger class will automatically fire and run the code from your AP01_Opportunity class. You can put some
System.debug('some message');
to check if your logic works as expected and also which code blocks are executed
I have custom obj "A" and Standard obj Case. Case standard obj has lookup to custom obj "A". there is a field between the two objects called Customer_ID__c. I wrote a Trigger (before Insert, Before Update) to associated the case record to the correct existing custom obj "A" record if "Case.Custom_Id__c" match the one in the Custom obj "A". Unfortunate it is not happening and I'm not sure where to look.
trigger IAACaseRelateASAP on Case (before insert, before update) {
Id recordtypes = [Select Id, name
From RecordType
Where SobjectType = 'Case'
AND Name = 'I Buy'
LIMIT 1].Id;
Set<String> casId = new Set<String>();
for(Case cs : Trigger.new)
{
if(cs.RecordtypeId == recordtypes && cs.Type == 'Contact Me')
{
if(cs.custm_Obj_A_Name__lookupfield__c == null && (cs.Customer_ID__c != null || cs.Customer_ID__c !='0'))
{
casId.add(cs.Customer_ID__c);
}
}
}
system.debug('Case Set Ids' + casId);
List<A__c> aList = [Select Customer_ID__c, Id
From A__c
Where Customer_ID__c IN: casId
AND
A__c != 'Provider'];
System.Debug('equals' + aList);
Map<String, A__c> aMapId = new Map<String, A__c>();
for(A__c aAcct : aList)
{
aMapId.put(aAcct.Customer_ID__c, aAcct);
}
for(Case cas : Trigger.new)
{
if(cas.RecordtypeId == recordtypes && cas.Type == 'Contact Me')
{
if(cas.custm_Obj_A_Name__lookupfield__c == null && (cas.Customer_ID__c != null || cas.Customer_ID__c !='0'))
{
if(aMapId.containsKey(cas.Customer_ID__c))
{
A__c aAcct = aMapId.get(cas.Customer_ID__c);
System.Debug('Case IAA ASAP Account value: ' + asapAcct);
}
}
}
}
}
It might be best when looping through your cases to build your set of Customer_ID__c ids to also build a List of cases with customer ids so that you don't have to loop through the entire new list a second time. There are a couple other issues with the trigger in general but I'll ignore those and just focus on what you asked. Think your issue is that you don't actually set the case field in this area:
if(aMapId.containsKey(cas.Customer_ID__c))
{
A__c aAcct = aMapId.get(cas.Customer_ID__c);
System.Debug('Case IAA ASAP Account value: ' + asapAcct);
}
It should be :
if(aMapId.containsKey(cas.Customer_ID__c))
{
cas.custm_Obj_A_Name__lookupfield__c = aMapId.get(cas.Customer_ID__c).Id;
}
When work order updates to Completed then a new water meter reading record needs to created with two field values from work order and two from another object att. Both work order and water meter reading have a look up(workorder) and master detail relation(water meter reading) with att.Sorry for so much code,but I am really stuck and need help.
trigger CreateWaterMeterReading on sm1e__smWork_Order__c (after update)
{
if (Trigger.new.size() == 1)
{
sm1e__smWork_Order__c wo = Trigger.new[0];
if(wo.sm1e__WO_Type__c == 'Meter Read Move In/Out ' && wo.sm1e__Status__c == 'Completed')
reateNewWaterMeterRead.createWMRforMoveInOrOut(wo.Id);
}
}
--Apex class
public class CreateNewWaterMeterRead {
public static void createWMRforMoveInOrOut(string workorderId)
{
Work_Order__c wo = [Select Equipment__r.Name,Completion_Date__c,Meter_Reading__c from Work_Order__c where Id = : workorderId ];
Equipment__c att = [Select Id,Last_Water_Meter_Reading_Date__c,Last_Water_Meter_Reading__c from Equipment__c where Name = : wo.Equipment__r.Name ];
List<Water_Meter_Readings__c> newwmr = new List<Water_Meter_Readings__c>();
Water_Meter_Readings__c wmr = new Water_Meter_Readings__c();
wmr.Meter__c = att.Id;
wmr.Current_Meter_Reading__c = wo.Meter_Reading__c;
wmr.Current_Read_Date__c = wo.sm1e__Completion_Date__c;
wmr.Prior_Meter_Reading__c = att.Last_Water_Meter_Reading__c;
wmr.Prior_Read_Date__c = att.Last_Water_Meter_Reading_Date__c;
wmr.Source__c = 'Manual Read';
newwmr.add(wmr);
if(newwmr.size() >0)
insert newwmr;
}
--Test Class
isTest(SeeAllData = true)
public class CreateNewWaterMeterReadTest
{
static testmethod void createWMRforMoveInOrOut()
{
Work_Order__c wo = [Select Id,Equipment__r.Name,Completion_Date__c,Meter_Reading__c from Work_Order__c where sm1e__Status__c != 'Completed' AND sm1e__WO_Type__c = 'Meter Read Move In/Out' LIMIT 1];
Equipment__c att = [Select Id,Last_Water_Meter_Reading_Date__c,Last_Water_Meter_Reading__c from Equipment__c where Name = : wo.Equipment__r.Name ];
test.startTest();
wo.Meter_Reading__c = 1317;
wo.sm1e__Status__c = 'Completed';
update wo;
test.stopTest();
System.debug('updated wo');
Water_Meter_Readings__c wmr = new Water_Meter_Readings__c();
System.debug('wmr for test');
wmr.Meter__c= att.Id;
wmr.Current_Meter_Reading__c = wo.Meter_Reading__c;
wmr.Current_Read_Date__c = wo.Completion_Date__c;
System.debug('in between wmr');
wmr.Prior_Meter_Reading__c = att.Last_Water_Meter_Reading__c;
wmr.Prior_Read_Date__c = att.Last_Water_Meter_Reading_Date__c;
wmr.Source__c = 'Manual';
insert wmr;
You've written trigger on after update. But in your test class you're inserting a record and not updating it. That is why your code coverage is 0%. Refer to this link
For executing trigger you must do dml operation for which you've written a trigger. Also you can invoke your class method from test class by creating its instance. Refer above link for the same.
I am trying to write a trigger that will update a RTA field on a parent object with an image from a RTA field on the child object.
I am getting the below error when creating the child object
Apex trigger Updateparent caused an unexpected exception, contact your administrator: Updateparent: execution of AfterInsert caused by: System.StringException: Invalid id: (.....IMAGE IS DISPLAYED HERE....)External entry point.
Info
Man Utd is the child object
Account is the parent object
Image__c is the RTA field on the child object
Copy_image__c is the RTA field on accounts
Here is the trigger code
trigger Updateparent on Man_Utd_1__c (after insert, after update) {
Map<ID, Account> parentAccounts = new Map<ID, Account>();
List<Id> listIds = new List<Id>();
for (Man_Utd_1__c childObj : Trigger.new) {
listIds.add(childObj.Image__c);
}
parentAccounts = new Map<Id, Account>([SELECT id, (SELECT ID, Image__c FROM Man_Utd_s__r) FROM Account WHERE ID IN :listIds]);
for (Man_Utd_1__c manu : Trigger.new) {
Account myParentAccounts = parentAccounts.get(manu.Image__c);
myParentAccounts.Copy_image__c = manu.Image__c;
}
update parentAccounts.values();
}
Can anyone advise on how to rectify this or if it is even possible to do?
EDIT to answer comments
This one works for me, there's a slight modification around the null check. Give it a go? The field is called Image__c both on Account and Contact.
trigger rollupImage on Contact (after insert, after update) {
Set<Account> parents = new Set<Account>();
for (Contact child : trigger.new) {
if(child.AccountId != null && child.Image__c != null){
parents.add(new Account(Id = child.AccountId, Image__c = child.Image__c));
}
}
if(!parents.isEmpty()){
List<Account> toUpdate = new List<Account>();
toUpdate.addAll(parents);
update toUpdate;
}
}
ORIGINAL
The error
It's all written there ;)
List<Id> listIds = new List<Id>();
for (Man_Utd_1__c childObj : Trigger.new) {
listIds.add(childObj.Image__c); // boom happens here?
}
You're assigning rich text area value (very long string with encoded image in it) to Id variable (15 or 18 char special string)
Try like this:
Set<Id> ids = new Set<Id>(); // Set will ensure we won't have duplicate values
for (Man_Utd_1__c childObj : Trigger.new) {
ids.add(childObj.Account__c);
}
Optimization - round 1
In the second loop you're setting the Copy_Image__c field but on the local copy of Account (local variable in the scope of the loop). I'm not sure setting it there will propagate the change to the parent acc (it might be a full copy and not a reference to the item in the map. You could put it back (parentAccounts.put(manu.Account__c, myParentAccounts)) to the map or just do the image assignment directly:
parentAccounts = new Map<Id, Account>([SELECT id FROM Account WHERE ID IN :ids]);
// I've removed the subquery, you don't need it, right?
for (Man_Utd_1__c manu : Trigger.new) {
if(parentAccounts.containsKey(manu.Account__c)){
parentAccounts.get(manu.Account__c).Copy_image__c = manu.Image__c;
}
}
update parentAccounts.values();
Optimization - round 2
It looks bit stupid - we query for Accounts but all we need to fetch is the Id... But we know the Id already, right? So - behold this "pro trick" (shared just because my colleague is a big fan of Man U ;))
trigger Updateparent on Man_Utd_1__c (after insert, after update) {
Set<Account> parents = new Set<Account>();
for (Man_Utd_1__c child : trigger.new) {
if(child.Account__c != null){
parents.add(new Account(Id = child.Account__c, Copy_Image__c = child.Image__c));
}
}
if(!parents.isEmpty()){
List<Account> toUpdate = new List<Account>();
toUpdate.addAll(parents);
update toUpdate;
}
}
Note: Optimization is the key thing while writing trigger.
While writing trigger make sure you follow all the best practises:
Keynotes On Trigger:
OPTIMIZATION - I have specified it in Uppercase and in first line because its the key thing while writing logic
Proper indentation and code commenting, so that anyone who works on the same code will easily understand the logic by just glancing on the comments. Make sure you update lastModifiedBy line on initial comments so that it will be helpful to keep a trace who worked last/updated last and on what date. Better keep proper code comments on every line- IF NEEDED ONLY
Variables with proper names ex: if var of list type then 'lst' prefix or 'List' postfix and then the proper name ex: lstContactsToInsert OR lstContactsToUpdate
Always create a seperate handler class and don't write all the logics on trigger itself 'AS A BEST PRACTISE TO FOLLOW'
Have proper trigger contexts. Know when to use proper trigger contexts through salesforce documentations on trigger. Use 'Before' contexts if it can be handled using this, If Not then go for 'After' contexts so you limit DML's.
Keynotes On Handler:
The First 3 steps as mentioned for trigger applies here as well
Use common private methods where you write logic and use it in other methods
Keynotes On TestClass:
Always write a test class utilizing 'GENERIC TEST DATA CLASS' in your test class.
Use proper test method names like: test_Method_One 'OR' testImageUpdates as such
Use asserts and test all usecases - Both negative and positive tests
#1. Trigger
/**
* #TriggerName : ContactTrigger
* #CreatedBy : Rajesh Kamath
* #CreatedOn : 30-Nov, 2016
* #Description : Trigger to update the image on parent object 'Account' based on the child object 'Contact'
* #LastModified: None
*/
trigger ContactTrigger on Contact(after insert, after update) {
//Instantiate the handler class
ContactTriggerHandler objContactHandler = new ContactTriggerHandler();
/* Trigger Context */
if(Trigger.isAfter) {
//Fire on Insert
if(Trigger.isInsert) {
objContactHandler.onAfterInsert(trigger.new);
}
//Fire on Update
if(Trigger.isUpdate) {
objContactHandler.onAfterUpdate(trigger.new, trigger.oldMap);
}
}
}
#2. Handler
/**
* #HandlerName : ContactTriggerHandler
* #TriggerName : ContactTrigger
* #TestClassName : ContactTriggerHandlerTest [Make sure you create this with the steps at the end of this handler]
* #CreatedBy : Rajesh Kamath
* #CreatedOn : 30-Nov, 2016
* #Description : Trigger to update the image on parent object 'Account' based on the child object 'Contact'
* #LastModified: None
*/
public with sharing class ContactTriggerHandler {
/*
#MethodName : onAfterInsert
#Parameters : List<Contact> lstNewContactRecords
#LastModifiedBy : None [Make sure when anyone updates even this line will be updated for help for other users who work on later phase]
*/
public void onAfterInsert(List<Contact> lstNewContactRecords){
//Call a common method where we have logic
reflectChildImageOnParent(lstNewContactRecords, null);
}
/*
#MethodName : onAfterUpdate
#Parameters : List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords
#LastModifiedBy : None [Make sure when anyone updates even this line will be updated for help for other users who work on later phase]
*/
public void onAfterUpdate(List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords){
//Call a common method where we have logic
reflectChildImageOnParent(lstContactRecords, mapOldContactRecords);
}
/*
#MethodName : reflectChildImageOnParent
#Parameters : List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords
#LastModifiedBy : None [Make sure when anyone updates even this line will be updated for help for other users who work on later phase]
*/
private static void reflectChildImageOnParent(List<Contact> lstContactRecords, Map<Id, Contact> mapOldContactRecords){
/* Local variable declaration */
List<Account> lstAccountsToUpdate = new List<Account>();
for(Contact objContact : lstContactRecords) {
//Execute on insert and update
if((Trigger.isInsert || (Trigger.isUpdate && objContact.Child_Image__c != mapOldContactRecords.get(objContact.Id).Child_Image__c)) && objContact.Account__c != null) {
//List of accounts to update
lstAccountsToUpdate.add(new Account(Id = objContact.Account__c, Parent_Image__c = objContact.Child_Image__c));
}
}
//Update the account records
if(!lstAccountsToUpdate.isEmpty()) {
update lstAccountsToUpdate;
}
}
}
This can be referred for your usecase.
Please mark as 'Best Answer' if this was helpful
loyalty - Child Obj
fields:
lightingdata__Contact__c (relationship)
lightingdata__imagenims__c // Image field
Contact - Parent Obj
field: lightingdata__Imagefromloyalty__c
trigger loyaltytocon on Loyalty__c (after insert, after update) {
Map<id, Contact> conlist;
List<Id> idlist=new List<id>();
for (Loyalty__c loy:trigger.new) {
idlist.add(loy.lightingdata__Contact__c);
}
conlist= new Map<id, contact>([select id, lightingdata__Imagefromloyalty__c from contact where id In : idlist]);
for(Loyalty__c lo:trigger.new){
contact con = conlist.get(lo.lightingdata__Contact__c) ;
system.debug('con data' + con);
con.lightingdata__Imagefromloyalty__c = lo.lightingdata__imagenims__c;
}
update conlist.values();
}
In pareant object field it will return that image filed have URL.
Then you can create one formula filed and just refer (lightingdata__Imagefromloyalty__c) in formul it will display the image.