Clone an opportunity and its lineitems programatically in salesforce - salesforce

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;
}

Related

Update Parent Object Field in Apex Trigger

I have three objects
1)Truck__c
2)Booking__C
3)Payment___C.
Truck and Booking has master detail relationship where Truckk is master and Booking is detail.
Booking and payment has lookup relationship where Booking is parent and payment is child.
I want to update a field present isBooked(checkbox) in Truck based on the value present in a field remainingAmount in payment object. I have added trigger on Payment object like below
trigger TestTrigger on Payment__c (after insert) {
if(Trigger.isAfter && Trigger.isUpdate){
for (Payment__c pay:Trigger.New)
{
payId.add(pay.id);
paidAmount =Integer.valueOf(pay.Paid_Amount__c);
}
Map <id,Booking__c> bookingMap = new Map <id,Booking__c> ();
for (Booking__c obj: [Select Id, Booking_ID__c from Booking__c where Booking_ID__c in:payId])
{
bookingMap.put(obj.Booking_ID__c, obj);
}
for(Booking__c objBooking:matchingIdsMap){
Truck__c truckObj = [Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id];
//Integer paidAmount = Payment__c.Paid_Amount__c;
Integer totalAmount = Integer.valueOf(truckObj.Price__c);
Integer remainingAmount = totalAmount-paidAmount;
If(remainingAmount == 0){
truckObj.Booked__c = true
}
update truckObj;
}
}
}
Here first I am getting payment ID's and based on that I am fetching Booking objects which is lookup parent of payment. After this I am trying to fetch the truck object which is master of Booking. But I don't know how to query on this as where clause in query giving error
Truck__c truckObj = [Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id];
Please note there is no direct relationship between Truck and Payment
How can I fetch truck object
Thanks in Advance
Short answer: while referring to parent fields, you should use the relationship name, so Truck__r instead of Truck__c.
Anyway it is not the only problem with that code.
Long answer:
your trigger is in after insert, but you check for an after update event: if(Trigger.isAfter && Trigger.isUpdate). Probably you want to run this trigger in both after insert and after update.
You never declared payId nor paidAmount which you use in your first for-loop. Anyway paidAmount would just hold the last value, which probably you won't need.
[Select Id, Booking_ID__c from Booking__c where Booking_ID__c in:payId] this query should return an empty list because in payId you've stored the ids of Payment__c, that is a child of Booking__c, while in the first loop you should have stored the ids of parents Booking__c
[Select id,Price__c from Truck__c where Truck__c.id = objBooking.Truck__c.id] Here there is no reason to write where Truck__c.id It should be just WHERE Id = :objBooking.Truck__c.
Beware: putting a SOQL in a loop you will easily hit the Governor Limit about SOQL, which will raise a System.LimitException: Too many SOQL queries: 101.
The same goes by putting a DML in a loop.
I'm going to assume that the API Name of the lookup fields is the same of the parent object, so that a Booking__c field exists on Payment__c object and a Truck__c exists on Booking__c object.
If I got the logic right about how setting the flag on Truck object, this should be the trigger code.
trigger TestTrigger on Payment__c (after insert, after update) {
if(Trigger.isAfter && (Trigger.isInsert || Trigger.isUpdate)) {
Map<Id, List<Payment__c>> mapBookingIdPaymentList = new Map<Id, List<Payment__c>>();
for (Payment__c pay : Trigger.New) {
List<Payment__c> paymentList = mapBookingIdPaymentList.get(pay.Booking__c);
if (paymentList == null) {
paymentList = new List<Payment__c>();
mapBookingIdPaymentList.put(pay.Booking__c, paymentList);
}
paymentList.add(pay);
}
Map<Id, Decimal> mapTruckPrice = new Map<Id, Decimal>();
Map<Id, Integer> mapTruckRemainingAmount = new Map<Id, Integer>();
for (Booking__c booking: [SELECT Id, Truck__c, Truck__r.Price__c FROM Booking__c WHERE Id IN :mapBookingIdPaymentList.keySet()]) {
mapTruckPrice.put(booking.Truck__c, booking.Truck__r.Price__c);
Integer sumOfRemainingAmount = mapTruckRemainingAmount.containsKey(booking.Truck__c) ? mapTruckRemainingAmount.get(booking.Truck__c) : 0;
for (Payment__c pay : mapBookingIdPaymentList.get(booking.Id)) {
sumOfRemainingAmount += pay.Paid_Amount__c != null ? pay.Paid_Amount__c.intValue() : 0;
}
mapTruckRemainingAmount.put(booking.Truck__c, sumOfRemainingAmount);
}
List<Truck__c> trucksToUpdate = new List<Truck__c>();
for (Id truckId : mapTruckPrice.keySet()) {
// There is no need to query a record just to update it if you already have its Id.
Truck__c truck = new Truck__c(Id = truckId);
truck.Booked__c = mapTruckPrice.get(truckId) - mapTruckRemainingAmount.get(truckId) == 0;
trucksToUpdate.add(truck);
}
update trucksToUpdate; // dml outside the loop
}
}
By the way, you should move the logic in an handler class, following the best practices.

Get list of parent records from multilevel SOQL query

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);
}
}

What does the oldmap actually do here? Can someone explain

This is a code to create a new task when stage is inserted or updated to Closed Won
trigger ClosedOpportunityTrigger on Opportunity (after insert, after update) {
List<Task> tl = new List<Task>();
for(Opportunity op : Trigger.new) {
if(Trigger.isInsert) {
if(Op.StageName == 'Closed Won') {
tl.add(new Task(Subject = 'Follow Up Test Task', WhatId = op.Id));
}
}
if(Trigger.isUpdate) {
if(Op.StageName == 'Closed Won'
&& Op.StageName != Trigger.oldMap.get(op.Id).StageName) {
tl.add(new Task(Subject = 'Follow Up Test Task', WhatId = op.Id));
}
}
}
if(tl.size()>0) {
insert tl;
}
}
Here, what does && Op.StageName != Trigger.oldMap.get(op.Id).StageName) do? Why do we use oldMap here?
Trigger.newMap is the map of IDs of new object values. Available in insert, update, and undelete triggers, and 'new' records can only be modified in before triggers.
Trigger.oldMap is the map of IDs of old object values. Available in update and delete triggers only.
if (Trigger.isUpdate) {
// Iterate updated opportunities
for (Opportunity o : Trigger.new) {
// Get the opportunity before update
Opportunity oldOpp = Trigger.oldMap.get(o.Id);
// Check if a value changed
if (o.Some_Value__c == oldOpp.Some_Value__c) {
System.debug('Value did not change.');
} else {
System.debug('Value changed!');
}
}
}
Note: I could have used Trigger.newMap instead of Trigger.new but I'd be looping through Trigger.newMap.values() instead - with the same end result. newMap is just a convenient way of getting the bulkified data in map form instead of a list.
We use old map to compare with the new value of the Some_Value__c custom field. If the two values differ then the field value has changed. Of course, if you read the code in the two if branches, this is obvious.

Apex Trigger with custom objects

2 custom objects
1.Merchandise
2.invoice
whenever a new merchandise is created an auto invoice has to be created.As am new to apex,please bear with me.Any one please correct my code.
code:
trigger createinvoice on Merchandise2__c (after insert,after update) {
list<Invoice2__c>line = new list<Invoice2__c>();
for(Merchandise2__c mer:Trigger.new){
Invoice2__c li = new Invoice2__c();
line =[select id from Invoice2__c ];
li.name = mer.Name;
li.Status__c='open';
li.id = mer.id;
line.add(li);
}
insert line;
}
First remove the after update from your trigger unless you want a new invoice created each time someone updates the Merchandise record.
I'm not sure what you were trying to accomplish with the select line.
"line =[select id from Invoice__c];" this doesn't seem to accomplish anything. Also you're trying to set the id field of the invoice to the id of the merchandise record. You can't do that. You'll need a lookup field on the Invoice record that points to merchandise. Below I called it merchandiseKeyField.
Hope this helps.
trigger createinvoice on Merchandise2__c (after insert,after update) {
//if you don't remove after Update then check which trigger
if(Trigger.IsInsert){
list<Invoice2__c> invoices = new list<Invoice2__c>();
for(Merchandise__c mer: trigger.new){
invoices.add(new Invoice2__c(li.name = mer.name,
li.merchandiseKeyField = mer.id);
}
insert invoices;
}
}
replace your loop with this:
for(Merchandise2__c mer:Trigger.new){
Invoice2__c li = new Invoice2__c();
li.name = mer.Name;
li.Status__c='open';
line.add(li);
}

System.NullPointerException: Attempt to de-reference a null object

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.

Resources