You need to sign in to do that
Don't have an account?
Morgan Marchese
Apex: Populate Data from One Record to Another in the same Object if they have a matching field?
Hi all, I'm new to apex but currently going through some online courses but haven't quite grasped how I can achieve this yet...
Scenario: We have a custom object from our Zuora managed package called 'Quote Charge'. Within this object are many records. Two of these records are technically 'associated' with each other, but not using a lookup field or any traditional SFDC linking. Instead, they have a custom field that shares a unique value between these 2 'associated' records. So, both of these records have a field where the value is "20150814ABCD" for example - that field is unique to these TWO records and will never be found on any other records of this object. One of these 2 records is named "Discount", and the other one can have any varying name.
Goal: I need to, using apex I presume, find these 2 matching records based on that custom field being an exact match between the 2, and then take the discount % from a field on the record named "Discount" and apply it to another custom field on the record that isn't named "Discount"
Can anyone give me a good starting point for this/point me in the right direction? I started writing some code in a class to play around with the idea, but I don't think I'm going about it the best way. Would appreciate some feedback from the SFDC Gurus.
Many thanks,
Morgan
Scenario: We have a custom object from our Zuora managed package called 'Quote Charge'. Within this object are many records. Two of these records are technically 'associated' with each other, but not using a lookup field or any traditional SFDC linking. Instead, they have a custom field that shares a unique value between these 2 'associated' records. So, both of these records have a field where the value is "20150814ABCD" for example - that field is unique to these TWO records and will never be found on any other records of this object. One of these 2 records is named "Discount", and the other one can have any varying name.
Goal: I need to, using apex I presume, find these 2 matching records based on that custom field being an exact match between the 2, and then take the discount % from a field on the record named "Discount" and apply it to another custom field on the record that isn't named "Discount"
Can anyone give me a good starting point for this/point me in the right direction? I started writing some code in a class to play around with the idea, but I don't think I'm going about it the best way. Would appreciate some feedback from the SFDC Gurus.
Many thanks,
Morgan
My assumption is that your code is executing in Before Insert, If yes please use the below code :
public static void linkDiscountsToCharges(List<zqu__QuoteCharge__c> newQuoteCharges)
{
Map<String,zqu__QuoteCharge__c> discountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
Map<String,zqu__QuoteCharge__c> nonDiscountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
for(zqu__QuoteCharge__c qc: newQuoteCharges)
{
if(qc.Name == 'Discount')
{
discountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
else
{
nonDiscountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
}
for(String uniqueValue : discountQuoteCharge.keySet())
{
zqu__QuoteCharge__c productQC = nonDiscountQuoteCharge.get(uniqueValue);
zqu__QuoteCharge__c discountQC = discountQuoteCharge.get(uniqueValue);
productQC.True_Discount__c = discountQC.zqu__EffectivePrice__c; // please use your fields in this line or apply or logic
}
}
Please let me know if I can help you more.
Thanks,
Vishal
All Answers
I am not exactly sure what is your buisness flow means where and when(event) you want to implement your above logic but I have written below code snippet to give you basic idea to start with :
List<Quote_Charge> lstQuoteCharge = [Select Id, Name from Quote Charge where relationship_Field__c = '20150814ABCD' Limit 2];
if(lstQuoteCharge[0].name == 'Discount')
{
//Implement logic to apply discount on lstQuoteCharge[1]
}
else
{
//Implement logic to apply discount on lstQuoteCharge[0]
}
Please let me know if I can help you more.
Thanks,
Vishal
Hi Vishal,
Thanks for your reply - unfortunately I don't think that meets the need.. to be more specific, there could be 20 records added to the object at the same time, and each set of 2 records would have a matching value - so adding the value to a SQL query in the APEX won't help me since the value will change for every set of 2 that is added.
For example:
We create a quote with 10 products on it. When we save it, it creates 20 Quote Charges records (10 products and 10 discounts), each of those 10 Quote Charges has an associated Discount Quote Charge record called 'Discount' that shares that relationship field value... like this:
Product A : 20150814-1111Z
Discount: 20150814-1111Z
Product B: 20150814-2222Z
Discount: 20150814-2222Z
Product C: 20150814-3333Z
Discount: 20150814-3333Z
What I need to do is take data from the associated discount record and populate it to its associated product record. I don't care if that means I use apex to populate the actual fields or if it means I use apex to create a lookup relationship that links these 2 records together so that I can use formula fields, but ultimately the end result is that I need to carry the specific discount % from the discount record to its associated product record.
I'll share some code that I wrote as a start - but I don't know if I'm approaching this the right way:
I'm basically stuck with a situation where I've created a list of all new quote charges where name = Discount, and I've created a list where they DON'T equal discount, but now I don't know how to determine that 2 items in those 2 separate lists are a 'match' to each other, and update the associated non-discount item with data from the discount item.
Does that make more sense?
Please use below code :
public static void linkDiscountsToCharges(List<Zuora__QuoteCharge__c> newQuoteCharges)
{
// Generate a set of Quote Charge IDs
Set<ID> chargeID = new Set<ID>();
for(Zuora__QuoteCharge__c qc: newQuoteCharges)
{
chargeID.add(qc.Id);
}
Map<String,Zuora__QuoteCharge__c> discountQuoteCharge = new Map<String,Zuora__QuoteCharge__c>();
Map<String,Zuora__QuoteCharge__c> noDiscountQuoteCharge = new Map<String,Zuora__QuoteCharge__c>();
for(Zuora__QuoteCharge__c discountQC : SELECT Id, zqu_TimeProductAdded__c, Name FROM Zuora__QuoteCharge__c WHERE Name = 'Discount' AND Id in: chargeID)
{
discountQuoteCharge.put(discountList.zqu_TimeProductAdded__c,discountQC);
}
for(Zuora__QuoteCharge__c nonDiscountQC : SELECT Id, zqu_TimeProductAdded__c, Name FROM Zuora__QuoteCharge__c WHERE Name != 'Discount' AND Id in: chargeID)
{
discountQuoteCharge.put(nonDiscountQC.zqu_TimeProductAdded__c,nonDiscountQC);
}
for(String uniqueValue : discountQuoteCharge.keySet())
{
Zuora__QuoteCharge__c productQC = discountQuoteCharge.get(uniqueValue);
Zuora__QuoteCharge__c discountQC = discountQuoteCharge.get(uniqueValue)
productQC.applyDiscount = discountQC.discount__c; // please use your fields in this line or apply your logic
}
}
}
Please let me know if I can help you more.
Thanks,
Vishal
Please use this code, I have mad esome changes in my last code :
public with sharing class QuoteChargeHandler
{
public QuoteChargeHandler()
{
}
public static void linkDiscountsToCharges(List<Zuora__QuoteCharge__c> newQuoteCharges)
{
// Generate a set of Quote Charge IDs
Set<ID> chargeID = new Set<ID>();
for(Zuora__QuoteCharge__c qc: newQuoteCharges)
{
chargeID.add(qc.Id);
}
Map<String,Zuora__QuoteCharge__c> discountQuoteCharge = new Map<String,Zuora__QuoteCharge__c>();
Map<String,Zuora__QuoteCharge__c> nonDiscountQuoteCharge = new Map<String,Zuora__QuoteCharge__c>();
for(Zuora__QuoteCharge__c discountQC : SELECT Id, zqu_TimeProductAdded__c, Name FROM Zuora__QuoteCharge__c WHERE Name = 'Discount' AND Id in: chargeID)
{
discountQuoteCharge.put(discountQC.zqu_TimeProductAdded__c,discountQC);
}
for(Zuora__QuoteCharge__c nonDiscountQC : SELECT Id, zqu_TimeProductAdded__c, Name FROM Zuora__QuoteCharge__c WHERE Name != 'Discount' AND Id in: chargeID)
{
nonDiscountQuoteCharge.put(nonDiscountQC.zqu_TimeProductAdded__c,nonDiscountQC );
}
for(String uniqueValue : discountQuoteCharge.keySet())
{
Zuora__QuoteCharge__c productQC = nonDiscountQuoteCharge.get(uniqueValue);
Zuora__QuoteCharge__c discountQC = discountQuoteCharge.get(uniqueValue)
productQC.applyDiscount = discountQC.discount__c; // please use your fields in this line or apply or logic
}
}
}
Thanks,
Vishal
I put your code in, and with the exception of a few minor fixes (missing semi-colons and bad field names), it saved without any issues. I added a call to the method in my trigger and then tested it but nothing was updated on my record.. i entered my name into debug to see what was happening and I found this:
15:04:32.180 (1180867737)|SOQL_EXECUTE_BEGIN|[119]|Aggregations:0|SELECT Id, zqu__TimeProductAdded__c, Name FROM zqu__QuoteCharge__c
15:04:32.182 (1182708428)|SOQL_EXECUTE_END|[119]|Rows:0
It seems to be getting cut off at the WHERE clause and not doing the WHERE Name = 'Discount' AND Id in: chargeID
Any idea why?
Please let me know your trigger is executing in After insert or Before Insert?
Thanks,
Vishal
My assumption is that your code is executing in Before Insert, If yes please use the below code :
public static void linkDiscountsToCharges(List<zqu__QuoteCharge__c> newQuoteCharges)
{
Map<String,zqu__QuoteCharge__c> discountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
Map<String,zqu__QuoteCharge__c> nonDiscountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
for(zqu__QuoteCharge__c qc: newQuoteCharges)
{
if(qc.Name == 'Discount')
{
discountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
else
{
nonDiscountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
}
for(String uniqueValue : discountQuoteCharge.keySet())
{
zqu__QuoteCharge__c productQC = nonDiscountQuoteCharge.get(uniqueValue);
zqu__QuoteCharge__c discountQC = discountQuoteCharge.get(uniqueValue);
productQC.True_Discount__c = discountQC.zqu__EffectivePrice__c; // please use your fields in this line or apply or logic
}
}
Please let me know if I can help you more.
Thanks,
Vishal
Yes I am executing before Insert - I thought you couldn't execute this type of code after insert for any object that you were modifying data on because it would be in a read_only status - also since both records are being added simultaneously i thought before insert made the most sense. I'll try your revised code and report back. Thanks for all of your patience!
Success! The last code changes appear to work beautifully in our Sandbox. I'm very grateful for your help in this, and I have a better understanding of how to handle this with keyMaps in the future.
I've marked your answer as Best - cheers and have a great weekend!
Hoping you can help me a bit further with this. I am using the following code based on what you provided last week - and it works perfectly with before insert, but I also realized I want the productQC data to be updated if the related discountQC is updated. I added a 'before update' to my trigger and referenced it to run this class, but it returns an error about de-referencing a null object. I believe it is because in an update scenario, the DISCOUNT record gets updated but the associated PRODUCT record does not, so when the trigger tries to reference productQC it is null.
Can I get this to work with BEFORE UPDATE? Right now it only works with BEFORE INSERT since both records with the uniqueValue are inserted simultaneously. Any thoughts?
I am modifying the code, will provide you in next half an hour.
Thanks,
Vishal
Please use below code and call it in Before Insert and After Update :
public static void linkDiscountsToCharges(List<zqu__QuoteCharge__c> newQuoteCharges)
{
Map<String,zqu__QuoteCharge__c> discountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
Map<String,zqu__QuoteCharge__c> nonDiscountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
for(zqu__QuoteCharge__c qc: newQuoteCharges)
{
if(qc.Name == 'Discount')
{
discountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
else
{
nonDiscountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
}
if(Trigger.IsInsert)
{
for(String uniqueValue : discountQuoteCharge.keySet())
{
if(nonDiscountQuoteCharge.containsKey(uniqueValue))
{
zqu__QuoteCharge__c productQC = nonDiscountQuoteCharge.get(uniqueValue);
zqu__QuoteCharge__c discountQC = discountQuoteCharge.get(uniqueValue);
productQC.True_Discount__c = discountQC.zqu__EffectivePrice__c; // Set True Discount Custom Field on Products to match ZQU Effective Price (Discount) from associated discount record.
productQC.Discount_Period__c = discountQC.zqu__Upto_How_Many_Periods__c; // Set Discount Period Custom Field on Products to Match ZQU How Mnay Periods field from associated discount record.
}
}
}
if(Trigger.isAfter && Trigger.isUpdate && discountQuoteCharge.size()>0)
{
List<zqu__QuoteCharge__c> lstProductToUpdate = new List<zqu__QuoteCharge__c>();
for(zqu__QuoteCharge__c prodQC : [select Id,zqu__TimeProductAdded__c,True_Discount__c,Discount_Period__c,name from zqu__QuoteCharge__c where name != 'Discount' and Id in :discountQuoteCharge.keySet()])
{
if(discountQuoteCharge.containsKey(prodQC.zqu__TimeProductAdded__c))
{
prodQC.True_Discount__c = discountQuoteCharge.get(prodQC.zqu__TimeProductAdded__c).zqu__EffectivePrice__c;
prodQC.Discount_Period__c = discountQuoteCharge.get(prodQC.zqu__TimeProductAdded__c).zqu__Upto_How_Many_Periods__c;
lstProductToUpdate.add(prodQC);
}
}
if(lstProductToUpdate.size()>0)
update lstProductToUpdate;
}
}
Please let me know if I can help you more.
Thanks,
Vishal
Unfortunately the new changes w/ an After Update don't seem to be doing the trick
Here is my trigger:
And I am using your most recent code posted above. I update the discount record but the associated product record remains unchanged and the last modified time is unchanged as well so it's not even editing the record at all.
Debug:
I reviwed the code and it shoud work.
Is it possible for you to share your org for one hour with me, I will check and do changes accordingly. My email id is vishal.er.gupta@gmail.com
If there is any concern, please share complete code of Trigger and Handler, also let me know if there is any other trigger written on zqu__QuoteCharge__c.
Thanks,
Vishal
I am stepping into a meeting right now so I'm worried about giving org access to our SBX while I am away from my desk (no offense, just being cautious). I can share the entire trigger/handler class with you now if that will help - though there isn't much more to it other than what you and I have been working on.
Trigger:
Handler:
The part that I don't understand is why the debug log is showing that the SOQL query was not the same as the one executed in our code:
For some reason, it's stopping after FROM zqu__QuoteCharge__c and not including any of the WHERE arguments in the log? Is that normal?
I have made small change, please use the below handler class :
public with sharing class z_QuoteChargeTriggerHandler {
private Boolean isTriggerExecuting = false;
private Integer batchSize = 0;
private final String debugStr = 'z_QuoteChargeTriggerHandler: ';
//Constructor
public z_QuoteChargeTriggerHandler(boolean isExecuting, integer size){
isTriggerExecuting = isExecuting;
batchSize = size;
}
public void OnBeforeInsert(List<zqu__QuoteCharge__c> newList){
if (newList != null && newList.size() > 0) {
System.Debug(debugStr + 'OnBeforeInsert');
//Holds set of zuora product rate plan charge ids
Set<String> zuoraProductChargeIds = new Set<String>();
//Populate zuoraProductChargeIds
for(zqu__QuoteCharge__c quoteCharge: newList){
if(quoteCharge.zqu__RatePlanCharge_ZuoraID__c != null){
System.Debug(debugStr+'Adding zuora id '+quoteCharge.zqu__RatePlanCharge_ZuoraID__c+' to zuoraProductChargeIds');
zuoraProductChargeIds.add(quoteCharge.zqu__RatePlanCharge_ZuoraID__c);
} else {
System.Debug(debugStr+'quoteCharge Zuora Id is null: '+quoteCharge);
}
}
if (zuoraProductChargeIds.size() > 0) {
//Field Set containing fields to sync
Schema.FieldSet productChargeFieldSet = SObjectType.zqu__ProductRatePlanCharge__c.FieldSets.getMap().get('CustomChargeFields');
if (productChargeFieldSet != null) {
//Field Set information
List<Schema.FieldSetMember> productChargeFields = productChargeFieldSet.getFields();
if (productChargeFields.size() > 0 || Test.isRunningTest()) {
//Set to hold fields to query from Product Rate Plan Charge
Set<String> chargeFields = new Set<String>();
//Cycle through each field in field set and add to query
for (Schema.FieldSetMember productChargeField : productChargeFields) {
chargeFields.add(productChargeField.getFieldPath());
}
if (Test.isRunningTest()) {
chargeFields.add('Name');
}
//Product Charge query string
String chargeQuery = 'SELECT Id, zqu__ZuoraId__c';
//Cycle through Quote Fields Set and build query string
for (String field : chargeFields) {
chargeQuery += ', '+field;
}
chargeQuery += ' FROM zqu__ProductRatePlanCharge__c WHERE zqu__ZuoraId__c IN :zuoraProductChargeIds AND zqu__Deleted__c = false';
System.Debug('chargeQuery: '+chargeQuery);
List<zqu__ProductRatePlanCharge__c> productCharges = Database.query(chargeQuery);
if (productCharges.size() > 0) {
System.Debug(debugStr+'Starting to populate Quote\'s charges');
//Populate Quote's charges
for (zqu__QuoteCharge__c quoteCharge : newList) {
for (zqu__ProductRatePlanCharge__c productCharge : productCharges) {
if (productCharge.zqu__ZuoraId__c != null && productCharge.zqu__ZuoraId__c == quoteCharge.zqu__RatePlanCharge_ZuoraID__c) {
System.Debug(debugStr+'Found matching Product Charge '+productCharge.Id+' for Quote Charge ');
for (String field : chargeFields) {
try {
quoteCharge.put(field, productCharge.get(field));
System.Debug(debugStr+'Field: '+field+', Value: '+productCharge.get(field));
} catch(Exception ex) {
System.Debug(debugStr+'Error pushing field \''+field+'\' from product charge to quote charge');
}
}
break;
} else {
System.Debug(debugStr+'Product Charge and Quote Charge do not match. Skipping to next charge');
}
}
}
System.Debug(debugStr+'Done populating Quote\'s charges');
} else {
System.Debug(debugStr+'Failed to retrieve any ProductRatePlanCharges based on query \''+chargeQuery+'\'');
}
} else {
System.Debug(debugStr+'Field Set is empty. Will not sync custom fields to quote charges');
}
} else {
System.Debug(debugStr+'could not find fieldset \'CustomChargeFields\' on Product Rate Plan Charge object. Will not sync any custom fields');
}
} else {
System.Debug(debugStr+'Failed to find any zuora product charge Ids from Quote\'s charges');
}
} else {
System.Debug(debugStr + 'OnBeforeInsert, newList null or empty');
}
}
public static void linkDiscountsToCharges(List<zqu__QuoteCharge__c> newQuoteCharges)
{
Map<String,zqu__QuoteCharge__c> discountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
Map<String,zqu__QuoteCharge__c> nonDiscountQuoteCharge = new Map<String,zqu__QuoteCharge__c>();
for(zqu__QuoteCharge__c qc: newQuoteCharges)
{
if(qc.Name == 'Discount')
{
discountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
else
{
nonDiscountQuoteCharge.put(qc.zqu__TimeProductAdded__c,qc);
}
}
if(Trigger.IsInsert)
{
for(String uniqueValue : discountQuoteCharge.keySet())
{
if(nonDiscountQuoteCharge.containsKey(uniqueValue))
{
zqu__QuoteCharge__c productQC = nonDiscountQuoteCharge.get(uniqueValue);
zqu__QuoteCharge__c discountQC = discountQuoteCharge.get(uniqueValue);
productQC.True_Discount__c = discountQC.zqu__EffectivePrice__c; // Set True Discount Custom Field on Products to match ZQU Effective Price (Discount) from associated discount record.
productQC.Discount_Period__c = discountQC.zqu__Upto_How_Many_Periods__c; // Set Discount Period Custom Field on Products to Match ZQU How Mnay Periods field from associated discount record.
}
}
}
if(Trigger.isAfter && Trigger.isUpdate && discountQuoteCharge.size()>0)
{
List<zqu__QuoteCharge__c> lstProductToUpdate = new List<zqu__QuoteCharge__c>();
for(zqu__QuoteCharge__c prodQC : [select Id,zqu__TimeProductAdded__c,True_Discount__c,Discount_Period__c,name from zqu__QuoteCharge__c where name != 'Discount' and zqu__TimeProductAdded__c in :discountQuoteCharge.keySet()])
{
if(discountQuoteCharge.containsKey(prodQC.zqu__TimeProductAdded__c))
{
prodQC.True_Discount__c = discountQuoteCharge.get(prodQC.zqu__TimeProductAdded__c).zqu__EffectivePrice__c;
prodQC.Discount_Period__c = discountQuoteCharge.get(prodQC.zqu__TimeProductAdded__c).zqu__Upto_How_Many_Periods__c;
lstProductToUpdate.add(prodQC);
}
}
if(lstProductToUpdate.size()>0)
update lstProductToUpdate;
}
}
}
Please let me know if I can help you more.
Thanks,
Vishal
Can you tell me what changes you made? Did you make changes to the rest of the handler or just changes to the linkDiscountsToCharges class? I want to make sure I properly document and test everything if changes were made outside of the new class.
Thanks for all of your time
Please disregard my question above - I see the area that you highlighted in bold is the area that you changed... and good news is... it appears that fixed it! I am very grateful for your continued assistance getting me through this, you don't know how much it means to know that there are people like you willing to help to such a great degree.
I hope you have a fantastic day!
I am pleased that I assist you and worked with you . Please feel free to let me know if I can help you in future. Just drop me an email.
Thanks,
Vishal