Avoiding Hard Coding in Apex following Best Practices - salesforce

In reference to the best practice #10 avoiding hard coding IDs, is my code considered to follow best practice without using Lists or Maps ??
Best Practices Link
trigger RecordTypeTester on Account (before update) {
for (Account acc: trigger.new)
{
if(acc.RecordTypeId == [SELECT Id from RecordType WHERE sObjectType = 'Account' AND IsActive = True AND Name = 'Health Care' LIMIT 1].Id)
{
//some code
}
else if (acc.RecordTypeId == [SELECT Id from RecordType WHERE sObjectType = 'Account' AND IsActive = True AND Name = 'Hi-Tech' LIMIT 1].Id)
{
//some code
}
}
}

Initialize constant first
public final static Id INVOICE_SUMMARY_RECORD_TYPE_ID = Schema.SObjectType.Opportunity.getRecordTypeInfosByName().get('Invoice Summary').getRecordTypeId();
and then, use it in a method
if (oppItem.RecordTypeId != INVOICE_SUMMARY_RECORD_TYPE_ID)

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 delete all records excluding 0th record in array index

As per the code I don't want to delete 0th record and delete rest of the record. But it is deleting all the records!
Kindly assist where I am making the mistake.
Here is the code:
list<account> scope = [Select Id,(Select id,CreatedDate,ebMobile__FileType__c,ebMobile__Account__c from Files__r order by CreatedDate DESC) from account where id in ('0016D00000444','0016D000000ugO')];
Set<Id>OldIds = new Set<Id>();
Set<Id>newIds = new Set<Id>();
Set<Id> rIds = new Set<Id>();
Set<Id> rrIds = new Set<Id>();
list<File__c> listmemb = new list<File__c>();
List<File__c> listmemb3 = new List<File__c>();
List<File__c> listmemb4 = new List<File__c>();
for(Account Acc : scope)
{
for(File__c fi : Acc.ebMobile__Files__r)
{
listmemb = [select id,CreatedDate,ebMobile__FileType__c from File__c where id =: fi.id];
if(fi.ebMobile__Account__c != Null)
{
for(Integer i=0; i<listmemb.size(); i++)
{
if(fi.ebMobile__FileType__c == 'Signature' && i==0)
{
rIds.add(listmemb[0].id); // Exclude 0th record
}
if(i>0 && listmemb[i].ebMobile__FileType__c == 'Signature' || i > 0 && listmemb[i].ebMobile__FileType__c != 'signature')
{
rrIds.add(listmemb[i].id); // Delete all record excluding 0th record
}
if(fi.ebMobile__FileType__c != 'Signature')
{
OldIds.add(fi.id);
}
}
}
}
}
listmemb3 = [Select id,CreatedDate,ebMobile__FileType__c from File__c where id in : OldIds];
listmemb4 = [Select id,CreatedDate,ebMobile__FileType__c from ebMobile__File__c where id in : rrIds];
if(listmemb3.size() > 0 && listmemb4.size()>0)
{
delete listmemb3;
delete listmemb4;
}
}
There are some many unnecessary checks and lists in the code
Let us simplify the stuff:
list scope = [Select Id,(Select id,CreatedDate,ebMobile__FileType__c,ebMobile__Account__c from Files__r order by CreatedDate DESC) from account where id in ('XXXXXXXXXXXXXXXX','XXXXXXXXXXXXXXXX')];//Always keep Ids encrypted while posting on public platform
SetOldIds = new Set();
SetnewIds = new Set();
Set rIds = new Set();
Set rrIds = new Set();
list listmemb = new list();
List listmemb3 = new List();
List listmemb4 = new List();
for(Account Acc : scope)
{
Integer i= 0; //This will be used to exclue the first element
for(File__c fi : Acc.ebMobile__Files__r)
{
//listmemb = [select id,CreatedDate,ebMobile__FileType__c from File__c where id =: fi.id];//You don't need this as you already have fi
//if(fi.ebMobile__Account__c != Null)We are getting fi from Acc so it won't be having ebMobile__Account__c as null, assuming it as lookup
//{
//for(Integer i=0; i {This is syntactically wrong
/* This whole section is not needed as rIds is not being used anywhere
if(fi.ebMobile__FileType__c == 'Signature' && i==0)
{
rIds.add(listmemb[0].id); // Exclude 0th record
}
*/
//if(i>0 && listmemb[i].ebMobile__FileType__c == 'Signature' || i > 0 && listmemb[i].ebMobile__FileType__c != 'signature')
if( i > 0 ) //Just put the check you need
{
rrIds.add(listmemb[i].id); // Delete all record excluding 0th record
}
if(fi.ebMobile__FileType__c != 'Signature')
{
OldIds.add(fi.id);
}
i++;
//}
//}
}
}
listmemb3 = [Select id,CreatedDate,ebMobile__FileType__c from File__c where id in : OldIds];
listmemb4 = [Select id,CreatedDate,ebMobile__FileType__c from ebMobile__File__c where id in : rrIds];
if(listmemb3.size() > 0 && listmemb4.size()>0)
{
delete listmemb3;
delete listmemb4;
}
}
Hope this helps.
#Vishal there are a couple things I'm not really sure of based on your code and context:
What defines 'the first file'? In your second (duplicate) query you don't seem to apply any ordering, so Salesforce might return you the same records in different order based on their database structuring. In your first sub-select query there is.
Is there a specific reason you use if, if, if, instead of if, else if, else if? With this approach you prevent the second and third item to run, even when the first one applied. This will simplify your code as you don't need all duplicate check (i == 0, i > 0 and such)
How is it possible that a different Salesforce record (File vs. ebMobile__File__c) can have the same Salesforce ID? (in your query for listMembers)
Couple suggestions:
Only query the records you really want to delete using Offset (shift starting record)
Please try to avoid doing queries and DML actions in loops, since this is bad for performance, but also might cause running into governor limits
Apply usage of variableBinding, this will ensure no SOQL Injection can be applied (when the account IDs are e.g. fetched from the front-end)
Simplify your logic to do what you really want; if you query only those to delete (see 1.) then you can simply loop ones to determine the Signature condition and then just delete the list of Files per Account. In my perspective, there is no need to query the files again, since you already have their IDs and records, so you can simply specify to delete the retrieved records, right?
Set<Id> accountIds = new Set<Id>{ 'xxxx', 'xxxx' };
List<Account> scope = [SELECT Id,
( SELECT Id, ...
FROM Files__r
ORDER BY CreatedDate DESC
OFFSET 1 )
FROM Account
WHERE Id IN :accountIds];
List<File> filesToDelete = new List<File>();
List<ebMobile__File__c> ebMobileFileToDelete = new List<File>();
for( Integer i = 0, j = scope.size(); i < j; i++ ){
Account acc = scope[ i ];
if( acc.Files__r != null & !acc.Files__r.isEmpty() ){
for( Integer k = 0, l = acc.Files__r.size(); k < l; k++ ){
File f = acc.Files__r[ k ];
if( f.ebMobile__FileType__c != 'Signature' ){
// When not signature, delete the original file
filesToDelete.add( f );
} else{
// Don't delete the File, but delete the EB MobileFile
ebMobileFileToDelete.add( new ebMobile__File__c( Id = f.Id ) );
}
}
}
}
if( !filesToDelete.isEmpty() ){ delete filesToDelete; }
if( !ebMobileFileToDelete.isEmpty() ){ delete ebMobileFileToDelete; }
Please note, I haven't run this code, so it might require some tweaking, but I hope you'll be able to get it all working.
Good luck and enjoy! Reinier

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 - 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!

Audit of what records a given user can see in SalesForce.com

I am trying to determine a way to audit which records a given user can see by;
Object Type
Record Type
Count of records
Ideally would also be able to see which fields for each object/record type the user can see.
We will need to repeat this often and for different users and in different orgs, so would like to avoid manually determining this.
My first thought was to create an app using the partner WSDL, but would like to ask if there are any easier approaches or perhaps existing solutions.
Thanks all
I think that you can follow the documentation to solve it, using a query similar to this one:
SELECT RecordId
FROM UserRecordAccess
WHERE UserId = [single ID]
AND RecordId = [single ID] //or Record IN [list of IDs]
AND HasReadAccess = true
The following query returns the records for which a queried user has
read access to.
In addition, you should add limit 1 and get from record metadata the object type,record type, and so on.
I ended up using the below (C# using the Partner WSDL) to get an idea of what kinds of objects the user had visibility into.
Just a quick'n'dirty utility for my own use (read - not prod code);
var service = new SforceService();
var result = service.login("UserName", "Password");
service.Url = result.serverUrl;
service.SessionHeaderValue = new SessionHeader { sessionId = result.sessionId };
var queryResult = service.describeGlobal();
int total = queryResult.sobjects.Count();
int batcheSize = 100;
var batches = Math.Ceiling(total / (double)batcheSize);
using (var output = new StreamWriter(#"C:\test\sfdcAccess.txt", false))
{
for (int batch = 0; batch < batches; batch++)
{
var toQuery =
queryResult.sobjects.Skip(batch * batcheSize).Take(batcheSize).Select(x => x.name).ToArray();
var batchResult = service.describeSObjects(toQuery);
foreach (var x in batchResult)
{
if (!x.queryable)
{
Console.WriteLine("{0} is not queryable", x.name);
continue;
}
var test = service.query(string.Format("SELECT Id FROM {0} limit 100", x.name));
if(test == null || test.records == null)
{
Console.WriteLine("{0}:null records", x.name);
continue;
}
foreach (var record in test.records)
{
output.WriteLine("{0}\t{1}",x.name, record.Id);
}
Console.WriteLine("{0}:\t{1} records(0)", x.name, test.size);
}
}
output.Flush();
}

Resources