Salesforce assigning Service Territories to Assets based on coordinates - salesforce

I want to assign service territories to Assets, based on a Geolocation field whihc containts two coordinates.
With my below attempt I keep getting the error: Error: Compile Error: Unexpected token ','. at line 14 column 163
I can't seem to find the actual error in my code.
My first thought that I did not have a Relationship between the Asset and the Map Polygon object, but there is a lookup relationship between them.
trigger AssetTerritoryAssignment on Asset (before insert, before update) {
// Create a list to store the Ids of the Assets that need to be updated
List<Id> assetIds = new List<Id>();
// Loop through the trigger records to collect the Ids of the Assets that need to be updated
for (Asset asset : Trigger.new) {
if (asset.gps_fix__c != null && asset.Polygon_Relationship__c != null) {
assetIds.add(asset.Id);
}
}
// Use a map to store the Ids of the territories that contain the Assets
Map<Id, Id> territoryIdMap = new Map<Id, Id>();
//Use a query to find the territories that contain the Assets
for(Asset a: [SELECT Id, gps_fix__c, Service_Territory__c, Polygon_Relationship__c FROM Asset WHERE Id IN :assetIds]) {
FSL__Polygon__c polygon = [SELECT Id FROM FSL__Polygon__c WHERE Id =: a.Polygon_Relationship__c AND ST_WITHIN(a.gps_fix__c, FSL__KML__c) = true limit 1];
if(polygon != null) {
territoryIdMap.put(a.Id, polygon.Id);
}
}
// Create a list of Assets to update
List<Asset> assetsToUpdate = new List<Asset>();
// loop through the assets and update their territory
for (Asset asset : [SELECT Id, Territory__c FROM Asset WHERE Id IN :assetIds]) {
if (territoryIdMap.containsKey(asset.Id)) {
asset.Territory__c = territoryIdMap.get(asset.Id);
assetsToUpdate.add(asset);
}
}
update assetsToUpdate;
}

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.

How to retrieve objectID from within a class on parse server using swift

Alright so i'm starting to get a headache here... Once my user sign onto my app i create a new class on parse with the name PFUser.Current()?.objectId! Within this class i store all the users information, and the user can acces it from any device since his user ID never changes. One of the things i store in the user class is an array which leads me to my question: I want to be able to acces this array but the only way this can be done is by using:
var query = PFQuery(className: "\(PFUser.current()?.objectId!)") // Calling the user class
query.getObjectInBackground(withId: "") { (object: PFObject?, error: Error?) in
if error != nil {
print(error)
} else {
print(object)
}
}
The Problem is that i do not know how to retrieve the objectID from within the class UserObjectID.
I found some code that lets me retrieve my user list and append usernames and iDs to an array:
var query = PFUser.query()
query?.findObjectsInBackground { (objects, error) in
if let users = objects {
for object in users {
if let user = object as? PFUser {
self.usernames.append(user.username!)
self.userids.append(user.objectId!)
}
}
}
}
How can i do a similar search, and find all the objectIDs within my individual userClass instead of the superUser class?
Are you sure you want to create a new class for each user? you going to end up with an enormous amount of tables with 1 row in each. Best to add your array as a column in the current _User table or create a new table and save all user arrays in it with a pointer.
That being said if do and your new table is named from the objectId of the user then you should be able to query that class with:
var userId: String = ""
let userClassName = PFUser.current()!.objectId!
let query = PFQuery(className: userClassName)
// as there is only 1 row get the first
query.getFirstObjectInBackground { (object, error) in
if error != nil || object == nil {
print("nothing here try again")
} else {
userId = object?.objectId
}
}
If you have a table with many users and a pointer then use the following lines as a guide
let userObjectId = PFUser.current()!.objectId!
query.whereKey("yourPointerName", equalTo: PFObject(outDataWithClassName: "theClassYouWantToQuery", objectId: userObjectId))

Apex - Retrieving Records from a type of Map<SObject, List<SObject>>

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!

how to aggregate records in a list

I have a batch program that I need to aggregate (rollup) several currency fields by a specific contact and fund. I need a fresh set of eyes on this as I can't figure out how I need to correctly rollup the fields by the contact and fund.
Here is my current code in the batch program:
for (My_SObject__c obj : (List<My_SObject__c>)scope) {
if(!dbrToContactMap.isEmpty() && dbrToContactMap.size() > 0) {
if(dbrToContactMap.containsKey(obj.DBR__c)) {
List<Id> contactIds = dbrToContactMap.get(obj.DBR__c);
for(Id contactId : contactIds) {
My_Rollup__c rollup = new My_Rollup__c();
rollup.Fund_Name__c = obj.FundName__r.Name;
rollup.DBR__c = obj.DBR__c;
rollup.Contact__c = contactId;
rollup.YearToDate__c = obj.YearToDate__c;
rollup.PriorYear__c = obj.PriorYear__c;
rollupsToInsert.add(rollup);
}
}
}
}
if(!rollupsToInsert.isEmpty() && rollupsToInsert.size() > 0) {
insert rollupsToInsert;
}
I'm iterating over the scope of records, which is about 275,000 records that are returned. I have a map that returns a list of contacts by the key field in my scope.
Now, currently, as I loop over each contact, I put those into a list and insert. The problem is I need to aggregate (rollup) by fund and contact.
For example, let's say the map returns the contact for "John Doe" ten times for the specific key field. So, there are going to be 10 related records on "John Doe's" contact record.
The problem is that the same fund can be in those 10 records, which need to be aggregated.
So, if "Fund A" shows up 5 times and "Fund B" shows up 3 times and "Fund C" shows up 2 times, then I should only have 3 related records in total on the "John Doe" contact record that are aggregated (rolled up) by the fund.
I haven't been able to figure out how to do the rollup by fund in my list.
Can anyone help?
Any help is appreciated.
Thanks.
The key here is to use a stringified index key for your map, instead of using a raw Sobject ID. Think of this as a composite foreign key, where the combined values from a Fund and a DBR are joined. Here is the basic idea:
Map<String, My_Rollup__c> rollupMap = new Map<String, My_Rollup__c>();
for (My_SObject__c obj : (List<My_SObject__c>) scope) {
// .. stuff ..
String index = '' + new String[] {
'' + obj.FundName__r.Id,
'' + obj.DBR__c,
'' + contactId
};
// Find existing rollup, or create new rollup object...
My_Rollup__c rollup = rollupMap.get(index);
if (rollup == null) {
rollup = new My_Rollup__c();
rollupMap.put(index, rollup);
}
// Aggregate values..
}
// And save
if (rollupMap.isEmpty() == false) {
insert rollupMap.values();
}
The result is that you combining all the different "keys" that makeup a unique rollup record into a single stringified key, and then using that stringified key as the index in the map to enforce uniqueness.
The example above is incomplete, but you should be able to take it from here.

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

Resources