Apex Trigger with custom objects - salesforce

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

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.

Passing an Array with Objects to another function in GAS

Just to give you a little background on my question:
I am creating a form in Google App Script using the UI Services and I am storing specific calendar events in a dataArray. So the event object is stored in the array. I want to pass this array to the submit function but can't figure out how to go about this because :
I can't add it as a callback element (because it isn't a widget)
I can't store the event object in a widget (i.e. a listbox, etc) and then add that widget as a callback element.
Here is a brief sample of what I am trying to do:
var cal= CalendarApp.getDefaultCalendar();
var event= cal.getEvents(new Date("June 16, 2013 PST"),
new Date("July 22, 2013 PST"));
var specific = new Array;
for( var j=0; j<event.length;j++){
specific.push(event[j]);
//This stores the events in the specific variable
//I want to send this variable (w/ the data) to another function on submit
I would appreciate any suggestions you can lend me.
Thanks!
As a complement to the answer I gave in the comments : "you could also simply store the id and while you read the events again using the same start/end time you can check if an event correspond to the saved ID in a loop... if a match is found with the right ID you are sure it's the right event"
Here is a piece of code I use to modify/update/delete calendar events using their ID as reference. This code is used to delete specific events selected from the spreadsheet, the code to modify events is roughly the same, at least it uses the same ID checking.
...
var cal = CalendarApp.openByName(calName);
if (cal) {
var events = cal.getEvents(new Date(date_deb), new Date(date_fin),{max: 4000}); // stocke tt ds une variable array
var sel= sh.getRange(6,1,sh.getLastRow()-5, 10).getValues();// read data in the SS
for(e=0;e<events.length;++e){
var delFlag = false;
var ID = events[e].getId();
for(n=0;n<sel.length;++n){
if ((sel[n][8] == "x"||sel[n][8] == "X")&&sel[n][9]==ID){ // the ID here is stored in the spreadsheet in column J and I use a 'X' marker to select which event should be deleted
delFlag = true;
sh.getRange(n+6,9,1,2).setBackgroundColor('#ff5500');
SpreadsheetApp.flush();
Logger.log('FLAG '+ e);
break;
}
}
if(delFlag){
try{
var toDelete = events[e].deleteEvent();
++todel;
delFlag = false;
Logger.log('event deleted : '+sel[n][1]);
}catch(Err){Logger.log('Event from a serie already deleted from another occurence')}
}
}
}
var msg = todel + " événement(s) effacé(s) dans l'Agenda '"+calName+"'";
ss.toast("Effacement terminé", msg, 3);
...

Apex Managed Sharing trigger stopped working when changed field from Lookup to Master-Detail

I have two custom objects, listing and transaction. Initially I had a Lookup field on the transaction object to the listing object. I had Apex Managed Sharing set up to share the transaction with several user lookup fields on the related listing object. It was working perfectly.
I changed the field type of the listing field on the transaction object to Master-Detail and now I get the following error every time I try to save a new transaction:
"TransactionApexSharing: execution of AfterInsert caused by: line 1, column 1: trigger body is invalid and failed recompilation: Entity is not org-accessible"
Transaction and Listing objects are both set to Private and I can't find any typos in the code. The trigger hasn't been changed since it was working with the Lookup field.
Here is my code:
trigger TransactionApexSharing on Transaction__c (after insert, after update) {
if(trigger.isInsert || trigger.isUpdate){
Set<id> triggerIds = trigger.newMap.keyset();
List<Transaction__c> listWithParentData = [select Listing__r.Listing_Agent_1__r.id, Listing__r.Listing_Agent_2__r.id, Listing__r.Listing_Agent_3__r.id from Transaction__c where id in :triggerIds];
List<Transaction__Share> tranShrs = new List<Transaction__Share>();
Transaction__Share laShr;
Transaction__Share la2Shr;
Transaction__Share la3Shr;
Transaction__Share saShr;
Transaction__Share sa2Shr;
Transaction__Share sa3Shr;
for(Transaction__c atransaction : listWithParentData){
laShr = new Transaction__Share();
la2Shr = new Transaction__Share();
la3Shr = new Transaction__Share();
laShr.ParentId = atransaction.Id;
la2Shr.ParentId = atransaction.Id;
la3Shr.ParentId = atransaction.Id;
if (atransaction.Listing__r.Listing_Agent_1__c != null)
{
// Set the ID of user or group being granted access
laShr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_1__c;
// Set the access level
laShr.AccessLevel = 'edit';
// Set the Apex sharing reason
laShr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
// Add objects to list for insert
tranShrs.add(laShr);
}
if (atransaction.Listing__r.Listing_Agent_2__c != null)
{
la2Shr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_2__c;
la2Shr.AccessLevel = 'edit';
la2Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(la2Shr);
}
if (atransaction.Listing__r.Listing_Agent_3__c != null)
{
la3Shr.UserOrGroupId = atransaction.Listing__r.Listing_Agent_3__c;
la3Shr.AccessLevel = 'edit';
la3Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(la3Shr);
}
}
for(Transaction__c mytransaction : trigger.new){
// Instantiate the sharing objects
saShr = new Transaction__Share();
sa2Shr = new Transaction__Share();
sa3Shr = new Transaction__Share();
// Set the ID of record being shared
saShr.ParentId = mytransaction.Id;
sa2Shr.ParentId = mytransaction.Id;
sa3Shr.ParentId = mytransaction.Id;
if (mytransaction.Selling_Agent_1_User__c != null)
{
saShr.UserOrGroupId = mytransaction.Selling_Agent_1_User__c;
saShr.AccessLevel = 'edit';
saShr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(saShr);
}
if (mytransaction.Selling_Agent_2_User__c != null)
{
sa2Shr.UserOrGroupId = mytransaction.Selling_Agent_2_User__c;
sa2Shr.AccessLevel = 'edit';
sa2Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(sa2Shr);
}
if (mytransaction.Selling_Agent_3_User__c != null)
{
sa3Shr.UserOrGroupId = mytransaction.Selling_Agent_3_User__c;
sa3Shr.AccessLevel = 'edit';
sa3Shr.RowCause = Schema.Transaction__Share.RowCause.Share_Transaction_with_Agency_Agents__c;
tranShrs.add(sa3Shr);
}
}
// Insert sharing records and capture save result
// The false parameter allows for partial processing if multiple records are passed
// into the operation
Database.SaveResult[] lsr = Database.insert(tranShrs,false);
// Create counter
Integer i=0;
// Process the save results
for(Database.SaveResult sr : lsr){
if(!sr.isSuccess()){
// Get the first save result error
Database.Error err = sr.getErrors()[0];
// Check if the error is related to a trivial access level
// Access levels equal or more permissive than the object's default
// access level are not allowed.
// These sharing records are not required and thus an insert exception is
// acceptable.
if(!(err.getStatusCode() == StatusCode.FIELD_FILTER_VALIDATION_EXCEPTION
&& err.getMessage().contains('AccessLevel'))){
// Throw an error when the error is not related to trivial access level.
trigger.newMap.get(tranShrs[i].ParentId).
addError(
'Unable to grant sharing access due to following exception: '
+ err.getMessage());
}
}
i++;
}
}
}
When you flip the relationship from lookup to master detail a lot changes.
You lose fine grained access to details, that's it. Whatever rights user has to the master - he has them for the details as well (OK, excluding stuff like 'Read/Create/Edit/Delete' which is in Profiles, I'm talking about access to particular record, not general rights).
On the detail "OwnerId" will disappear (can't be queried, described etc)
Which has some finer points if you ever:
need approval process on detail and suddenly you can't use queues, you need to specify users directly.
users ask "what happened to My Transactions" in list views or reports
Last but not least - Detail__Share table disappears as well.
Try editing your trigger in the sandbox (just add 1 space or something), it will probably complain that there's no such table Transaction_Share.
You can either make sure that Agents have edit rights to parent listing (but that means they can edit ANY related transaction) and ditch the trigger or undo the M-D. It's really a case of going back to your users and asking for business logic ;)
Why did you flip it to M-D? Cascade delete, rollups etc. could be done with a bit of code if it turns out you can't afford losing this fine-grained edit access to each transaction.
But after a quick look at your code it seems to me you'll be fine with controlling the access on Listing level and not on the details?

Clone an opportunity and its lineitems programatically in 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;
}

Model won't update

Using cakephp:
I am trying to update customer information and the address the customer is linked to.
such that Customer.address_id = Address.id, and
Customer Model
$belongsTo = 'Address';
From the customers_controller
function profile($id = null)
{
if (empty($this->data['Customer']))
{
$this->Customer->id = $id;
$this->data = $this->Customer->read();
}
else
{
$this->Customer->id = $this->data['Customer']['id'];
$this->Customer->read();
$this->Customer->save($this->data['Customer']);
$this->Customer->Address->save($this->data['Address']);
}
}
Customer correctly updates, but Address always inserts a new row. How do I get this address to update?
first of all, take away lines 11 and 12. those serve no purpose. make sure your view contains form elements for Customer.id and Address.id. If you are just updating the Address you dont need line 13 either. The short answer is that Cakephp will insert row instead of update if the primary key is missing. In your case this means [Address][id].

Resources