I have to create a new object and copy all the fields form the old one to a new one and it is a child to opportunity.
The copyfields is a method that copies the fields from one object to another and the function call line in the cloning method is the line where I am getting the exception.
public LQAgreementCloneCtrl(ApexPages.StandardController controller) {
lqa = [Select off1stdate__c, hosp1stdate__c, Zip_Code_New__c, X66_Contract__c,WAWF__c,,AccountRevenue__c
From LQ_Agreement__c Where id=:ApexPages.currentPage().getParameters().get('id')];
o = [Select of_Hospitals__c, X8_Gal__c, X4_Gal__c, X3mo_Avg_LBS_stop__c, X3_Gal__c,
X2_Gal__c, X1st_Pick_Up_Date__c, X17_Gal__c, X12_month_Actual_Stops__c,
X12_mo_Avg_Rev__c, Waste_Destruction_Date__c, WS_Other__c, Vision_Match__c,
Value_analysis_committee__c, AR_FuelFee__c, AR_FixerFee__c, AR_EnergyFee__c, APPROVALS__c,RecordType.Name
From Opportunity WHERE Id=:lqa.Opportunity__c];
}
public PageReference cloning(){
if(lqa.Status__c=='Deal Approved'){
//oclone=o;
//oclone.id=null;
oclone=o.clone();
insert oclone;
System.debug('Oclone>>>>>>>'+oclone);
LQ_Agreement__c lqaclone=new LQ_agreement__c();
//lqaclone=new LQ_Agreement__c();
lqaclone.Opportunity__c=oclone.Id;
System.debug('LQAClone>>>>>'+lqaclone);
lqaclone=copyfields(lqaclone,lqa);
oclone.Name=o.Name+'-Amended';
//Checking the Record type of the original Opportunity to create the new cloned Opp with RecordType of same waste stream + amendment added
if(o.RecordType.Name=='LQ Bio/SMS Renewal'|| o.RecordType.Name=='LQ Bio/SMS New Business' )
oclone.RecordType.Name='LQ BIO/SMS Amendment';
if(o.RecordType.Name=='LQ Haz Waste New Business'|| o.RecordType.Name=='LQ Haz Waste Renewal' )
oclone.RecordType.Name='LQ Haz Waste Amendment';
if(o.RecordType.Name=='LQ RMW New Business'|| o.RecordType.Name=='LQ RMW Renewal' )
oclone.RecordType.Name='LQ RMW Amendment';
if(o.RecordType.Name=='LQ Rx/Pharma New Business'|| o.RecordType.Name=='LQ Rx/Pharma Renewal' )
oclone.RecordType.Name='LQ Rx/Pharma Amendment';
//Checking the Record type of the original LQ Agreement to create the new cloned LQA with RecordType + amendment added
if(lqa.RecordType.Name=='LQ Existing Agreement' || lqa.RecordType.Name=='LQ New Agreement' )
lqaclone.RecordType.Name='LQ New Agreement – Amendment';
if(lqa.RecordType.Name=='LQ Existing Agreement GPO' || lqa.RecordType.Name=='LQ New Agreement GPO' )
lqaclone.RecordType.Name='LQ New Agreement GPO – Amendment';
insert lqaclone;
update oclone;
p=new ApexPages.StandardController(lqaclone).view();
}
else{
System.debug('Inside Else statement');
p=new ApexPages.StandardController(lqa).view();
}
return p;
}
public LQ_Agreement__c copyfields(LQ_Agreement__c lqaclone1,LQ_Agreement__c lqa1){
lqaclone1.Approved_By_RSD__c=lqa1.Approved_By_RSD__c;
lqaclone1.ApprovedByBrent__c=lqa1.ApprovedByBrent__c;
lqaclone1.ApprovedByJIM__c=lqa1.ApprovedByJIM__c;
lqaclone1.ApprovedByVP__c=lqa1.ApprovedByVP__c;
}
I am getting the exception at the function call to copyfields. The exception I get is Attempt to de-reference null object. Copy fields is a big function. I just gave a few lines
Error is after copyfield, my guess is that your oclone.recordType sub-object is not defined, clone on its own does not replicate it, it can only come from SOQL result. Also you cannot assign oclone.recordType.name, to assign record type to clone you must assign proper RecordType Id to oclone.RecordTypeId field.
Not sure why just using the standard SObject.clone(false, true) method wouldn't suffice (false - don't preserve the Id and true - make a true copy, not just a reference). A list of SObjects can similarly be truly cloned with List.deepClone(false) instead of crafting your own clone/copy logic.
In general I'd consider cleaning up your logic so that you can see the flow of your code more clearly, especially in terms of what state and values your variables should have. For example from your code:
LQ_Agreement__c lqaclone=new LQ_agreement__c();
//lqaclone=new LQ_Agreement__c();
lqaclone.Opportunity__c=oclone.Id;
System.debug('LQAClone>>>>>'+lqaclone);
lqaclone=copyfields(lqaclone,lqa);
It seems the following was intended, assuming copyfields returned a new SObject:
LQ_Agreement__c lqaclone = copyfields(lqaclone, lqa);
lqaclone.Opportunity__c = oclone.Id;
But again, the following seems correct to me:
LQ_Agreement__c lqaclone = lqa.clone(false, true);
unless I'm missing some other reason that you preferred to roll your own clone.
Related
I have a Text field that has semicolon separated codes. These code has to be replaced with the description. I have separate map that have code and description. There is a trigger that replace the code with their description. the data will loaded using the dataloader in this field. I am afraid, it might not work for large amount of data since I had to use inner for loops. Is there any way I can achieve this without inner for loops?
public static void updateStatus(Map<Id,Account> oldMap,Map < Id, Account > newMap)
{
Map<String,String> DataMap = new Map<String,String>();
List<Data_Mapper__mdt> DataMapList = [select Salseforce_Value__c,External_Value__c from Data_Mapper__mdt where
active__c = true AND Field_API_Name__c= :CUSTOMFIELD_MASSTATUS AND
Object_API_Name__c= :OBJECT_ACCOUNT];
for(Data_Mapper__mdt dataMapRec: DataMapList){
DataMap.put(dataMapRec.External_Value__c,dataMapRec.Salseforce_Value__c);
}
for(Account objAcc : newMap.values())
{
if(objAcc.Status__c != ''){
String updatedDescription='';
List<String> delimitedList = objAcc.Status__c.split('; ');
for(String Code: delimitedList) {
updatedDescription = DataMap.get(Code);
}
objAcc.Status__c = updatedDescription;
}
It should be fine. You have a map-based access acting like a dictionary, you have a query outside of the loop. Write an unit test that populates close to 200 accounts (that's how the trigger will be called in every data loader iteration). There could be some concerns if you'd have thousands of values in that Status__c but there's not much that can be done to optimise it.
But I want to ask you 3 things.
The way you wrote it the updatedDescription will always contain the last decoded value. Are you sure you didn't want to write something like updatedDescription += DataMap.get(Code) + ';'; or maybe add them to a List<String> and then call String.join on it. It looks bit weird. If you truly want first or last element - I'd add break; or really just access the last element of the split (and then you're right, you're removing the inner loop). But written like that this looks... weird.
Have you thought about multiple runs. I mean if there's a workflow rule/flow/process builder - you might enter this code again. And because you're overwriting the field I think it'll completely screw you over.
Map<String, String> mapping = new Map<String, String>{
'one' => '1',
'two' => '2',
'three' => '3',
'2' => 'lol'
};
String text = 'one;two';
List<String> temp = new List<String>();
for(String key : text.split(';')){
temp.add(mapping.get(key));
}
text = String.join(temp, ';');
System.debug(text); // "1;2"
// Oh noo, a workflow caused my code to run again.
// Or user edited the account.
temp = new List<String>();
for(String key : text.split(';')){
temp.add(mapping.get(key));
}
text = String.join(temp, ';');
System.debug(text); // "lol", some data was lost
// And again
temp = new List<String>();
for(String key : text.split(';')){
temp.add(mapping.get(key));
}
text = String.join(temp, ';');
System.debug(text); // "", empty
Are you even sure you need this code. Salesforce is perfectly fine with having separate picklist labels (what's visible to the user) and api values (what's saved to database, referenced in Apex, validation rules...). Maybe you don't need this transformation at all. Maybe your company should look into Translation Workbench. Or even ditch this code completely and do some search-replace before invoking data loader, in some real ETL tool (or even MS Excel)
I'm trying to combine 3 separate SOQL queries into one, but still end up with 3 separate lists for ease of use and readability later.
List<Object__c> objectList = [SELECT Name, Id, Parent_Object__r.Name, Parent_Object__r.Id,
(SELECT Name, Id FROM Child_Objects__r)
FROM Object__c];
I know I can get a list of child objects thus:
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
}
How would I go about adding the Parent_Object__c records to their own list?
I'm assuming a map could be used to deal with duplicates, but how do I get this Parent_Object__c data into that map?
You are basically there.
All lookup fields are available in your example as object.Parent_Object__r. Use a Set to natively avoid duplicates. No deduping required on your part!
Set<Parent_Object__c> parentObjectSet = new Set<Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
parentObjectSet.add(object.Parent_Object__r);
}
Edit:
As per #eyescream (trust him!) you are indeed better off with a map to avoid duplicates.
So the above code would just be slightly different:
Map<Id, Parent_Object__c> parentObjectMap = new Map<Id, Parent_Object__c>();
List<Child_Object__c> childObjectList = new List<Child_Object__c>();
for(Object__c object : objectList){
childObjectList.addAll(object.Child_Objects__r);
if (object.Parent_Object__r != null) {
parentObjectMap.put(object.Parent_Object__r.Id, object.Parent_Object__r);
}
}
I am using a lead map where the first id represents an Account ID and the List resembles a list of leads linked to that account such as: Map<id, List<Id> > leadMap = new Map< id, List<id> >();
My question stands as following: Knowing a Lead's Id how do I get the related Account's Id from the map. My code looks something like this, The problems is on the commented out line.
for (Lead l : leads){
Lead newLead = new Lead(id=l.id);
if (l.Company != null) {
// newLead.Account__c = leadMap.keySet().get(l.id);
leads_to_update.add(newLead);
}
}
You could put all lead id and mapping company id in the trigger then get the company id
Map<string,string> LeadAccountMapping = new Map<string,string>();//key is Lead id ,Company id
for(Lead l:trigger.new)
{
LeadAccountMapping.put(l.id,l.Company);
}
//put the code you want to get the company id
string companyid= LeadAccountMapping.get(l.id);
Let me make sure I understand your problem.
Currently you have a map that uses the Account ID as the key to a value of a List of Lead IDs - So the map is -> List. Correct?
Your goal is to go from Lead ID to the Account ID.
If this is correct, then you are in a bad way, because your current structure requires a very slow, iterative search. The correct code would look like this (replace your commented line with this code):
for( ID actID : leadMap.keySet() ) {
for( ID leadID : leadMap.get( actId ) ) {
if( newLead.id == leadID ) {
newLead.Account__c = actId;
leads_to_update.add(newLead);
break;
}
}
}
I don't like this solution because it requires iterating over a Map and then over each of the lists in each of the values. It is slow.
If this isn't bulkified code, you could do a Select Query and get the Account__c value from the existing Lead by doing:
newLead.Account__c = [ SELECT Account__c FROM Lead WHERE Id = :l.id LIMIT 1];
However, this relies on your code not looping over this line and hitting a governor limit.
Or you could re-write your code soe that your Map is actually:
Map<ID, List<Leads>> leadMap = Map<ID, List<Leads>>();
Then in your query where you build the map you ensure that your Lead also includes the Account__c field.
Any of these options should work, it all depends on how this code snippet in being executed and where.
Good luck!
I have the following code in ViewModel, I would like to remove a record from an entity but it doesn't work. Can someone please shed some light...?
Usertable users = new Usertable();
users.User_ID = Entity.User_ID;
users.user_role = "Admin";
Entity.CompanyRoles.Remove(users);
Instead if I replace the Remove with Add, it will add one record to the entity.
Only Remove is a concern to me.
First you need to fetch the entity you are about to remove, then remove it, then save your changes to the datacontext:
var userToRemove = Entity.CompanyRoles.Single(cr => cr.user_role == "Admin");
Entity.CompanyRoles.DeleteObject(userToRemove);
Entity.SaveChanges();
Objects are compared by reference, not values, so unless Entity.CompanyRoles contains the exact reference in memory as the object you're trying to remove, it won't find the object in the collection and remove it.
There are two ways to fix it.
The best way is to get a reference to the object in the collection like Paul suggests
var userToRemove = Entity.CompanyRoles.Single(cr =>
cr.user_role == "Admin" && cr.User_ID = Entity.User_ID);
Entity.CompanyRoles.Remove(userToRemove);
Or another method that works is to overwrite the .Equals() of the object to consider them equal if the data is the same, regardless of the memory reference. I would not usually recommend this except in special cases since it affects any operation that compares one copy of this object to another using .Equals()
public override bool Equals(object obj)
{
if (obj == null || !(obj == MyClass))
return false;
var obj2 = obj as MyClass;
return obj2.user_role == this.user_role && obj2.User_ID == this.User_ID;
}
I have a scenario where i need to clone a opportunity and its lineitems when the contract End date is today. The opp line item has field called Product_Family_c. i would have to clone only those opp whose lineitems have renewaltype_c as monthly.
I am stuck at how i can clone over the oli items and assign the new oppty ids to the oli items.
todays=date.today();
system.debug('todays'+todays);
for(opportunity o:[select Auto_Renew__c,Contract_lenght_in_months__c,Contract_End_Date__c,id from opportunity where Auto_Renew__c='Yes' and Contract_End_Date__c =:todays ])
{
SetOppId.add(o.id);
MapOpp.put(o.id,o);
}
system.debug('SetOppId'+SetOppId);
system.debug('MapOpp'+MapOpp);
for(OpportunityLineItem oli:[select OpportunityId from OpportunityLineItem where Product_Family__c='Monthly' and OpportunityId in :SetOppId])
{
SetOppIdtoRenew.add(oli.OpportunityId);
Mapoli.put(oli.id,oli);
}
system.debug('SetOppIdtoRenew'+SetOppIdtoRenew);
for(id a:SetOppIdtoRenew)
{
//MapOpp.get(a).Contract_End_Date__c=MapOpp.get(a).Contract_End_Date__c.addDays(1);
//MapOpp.get(a).Contract_End_Date__c=MapOpp.get(a).Contract_End_Date__c.addMonths(integer.valueof(MapOpp.get(a).Contract_lenght_in_months__c));
Lstopp.add(new opportunity(name=MapOpp.get(a).name+' renewal '+string.valueof(date.today()),
Contract_lenght_in_months__c=MapOpp.get(a).Contract_lenght_in_months__c,
Contract_End_Date__c=MapOpp.get(a).Contract_End_Date__c.addMonths(integer.valueof(MapOpp.get(a).Contract_lenght_in_months__c))+1,
StageName=MapOpp.get(a).StageName,
CloseDate=MapOpp.get(a).CloseDate
// ,<fieldname>=MapOpp.get(a).<fieldname>... for all the fields you ned to copy over
));
}
system.debug('Lstopp'+Lstopp);
insert Lstopp;
You were thinking in the right direction. However, before you assign OppID to line item you must insert the opportunity. I imagine this kind of breaks your transactional control, but you can always use Database save points to make the whole operation atomic.
For example:
SavePoint sp = Database.setSavepoint();
try {
upsert newOpportunities;
// now create line items and assign IDs
}
catch (Exception ex) {
Database.rollback(sp);
// cleanup
}
In order to map old opportunities to new ones, you need a Map<ID, Opportunity> and a List and fill them with the same new opportunities to eb able to reuse new IDs in a mapping manner (I used pseudo code, if you have trouble interpreting it, let me know)
foreach(oldopp) {
newopp = clone oldopp;
list.add(newopp);
map.put(oldopp.id, newopp);
}
upsert list;
// now we have new oppids for cloned items, use them to map
foreach(oldlineitem) {
newlineitem = clone oldlineitem;
newlineitem.OpportunityId = map.get(oldlineitem.OpportunityId).Id;
...
}
upsert listofnewlineitems;
}