Receiving errors on Apex class to match fields for two different custom objects - salesforce

I've been stuck for 18 hours on this apex class. I would really appreciate some help to figure out what's wrong. Basically this code should match the fields between two objects and I don't know why I'm receiving the following errors:
Missing return statement required return type: String - Line 2
Expecting '}' but was: 'for' - Line 26
The output should show that when location and position title from Candidate object matches location and title from Position object then it will just show those values that match on a data table in a lightning web component I made on Visual Studio.
I'd like some help improving this code so I can run it on the dev console.
public class Matchposition {
public static String matchPositionsWithCandidate() {
Set<String> statuses = new Set<String> {'New', 'Open'};
List<Position__c> openPositions = [SELECT Id, Name, Location__c, Status__c FROM Position__c WHERE Status__c IN :statuses];
//system.debug(openPositions);
Set<String> openPositionAndLocation = new Set<String>();
Map<String, Position__c> openPositionMap = new Map<String, Position__c>();
for (Position__c position : openPositions) {
openPositionMap.put(position.Name + '-' + position.Location__c, position);
openPositionAndLocation.add(position.Name + '-' + position.Location__c);
}
}
//for(String key : openPositionMap.keySet()) {
// system.debug('*** Start ***');
// system.debug('key :' + key);
// system.debug('value :' + openPositionMap.get(key));
// system.debug('*** End ***');
//}
Map<String, List<Candidate__c>> candidatesMap = new Map<String, List<Candidate__c>>();
List<Candidate__c> candidates = [SELECT Position__c, Location__c, Mobile__c, First_Name__c, Last_Name__c, Email__c FROM Candidate__c];
for (Candidate__c candidate : candidates) {
if(candidatesMap.containsKey(candidate.Position__c + '-' + candidate.Location__c)) {
candidatesMap.get(candidate.Position__c + '-' + candidate.Location__c).add(candidate);
} else {
candidatesMap.put(candidate.Position__c + '-' + candidate.Location__c, new List<Candidate__c> {candidate});
}
}
system.debug('*************** OPEN POSITIONS ***************');
for (String key : openPositionAndLocation) {
system.debug('====> ' + openPositionMap.get(key).Name);
if(candidatesMap.containsKey(key)) {
system.debug('***** Candidates *****');
for(Candidate__c candidate : candidatesMap.get(key)) {
system.debug('------- Name : ' + candidate.First_Name__c + ' ' + candidate.Last_Name__c);
system.debug('------- Email : ' + candidate.Email__c);
system.debug('------- Mobile : ' + candidate.Mobile__c);
}
} else {
system.debug('********* No Candidates');
}
}

One issue you certainly have, apropos of nothing else, is overall structure. Removing the actual lines of code, here's how your class is laid out now:
public class Matchposition {
public static String matchPositionsWithCandidate() {
// Code here
}
// more code here - problem!
}
You have much of your logic loose in the body of your class, which is not valid in Apex. Additionally, the code in the class body references variables declared inside the method matchPositionsWithCandidate(), which are scoped (visible) to that method.
matchPositionsWithCandidate() is also declared to return a string, but in fact returns nothing at all.
Since you're not returning any data anywhere, one step you can take to try to get this code running is to ensure that all of your logic is inside the scope (the curly braces) of the method matchPositionsWithCandidate(), and declare that method to return void, not String.
You may also have unbalanced braces - it looks like there is one missing after the final else - if it wasn't simply omitted in the copy and paste.
I'm not sure why this code exists. While much of the logic is correct, it makes far more sense to model your data with a relationship between Candidate__c and Position__c, rather than just storing a name in both positions. Then, you don't need any "matching" code at all - just a simple SOQL relationship query.

Related

Need to optimize the code for mapping codes to description

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)

Apex, SOQL, add results from database to the List, error

I would like to add results from database to the List. But there is a bug.
String str = '\'AOC\',\'BPD\',\'CRE\'';
List<String> lstString = str.split(',');
List<Shop_Product__c> productList = new List<Shop_Product__c>();
Integer i = 0;
for (String record: lstString) {
System.debug('record[' + i + ']: ' + record);
if ((record != null) && (productList != null)) {
productList.add([SELECT Id, Brand__c
FROM Shop_Product__c
WHERE Brand__c = :record
LIMIT 10]);
System.debug('productList[' + i + ']: ' + productList);
System.debug('Here in for ----------------------------------------');
}
++i;
}
Error is System.QueryException: List has no rows for assignment to SObject.
Here is it the explain, but I don't understand what I should do.
The List method add() takes a single sObject. That's why you get the QueryException, just like in the examples you link to.
You can use the addAll() method to create a List<sObject> context, which avoids the exception if there are no responsive records.

Salesforce APEX method not bulkified

I have written an APEX Class that sends an email when a client is released. There is a method that I thought I had bulkified but I was told it does not. This is because this method calls another function which actually does the actual email creation and that is not bulkified. Can someone guide me as to how the SOQL queries can be taken out of the method?
global class LM_ChangeAccountRT {
private static final Profile sysAdmin = [select id from profile where name='System Administrator'];
#AuraEnabled
public static String resendEmails(List<String> accountIdList) {
String response = null;
try {
//Only send emails if user is either an ARMS Administor or System Administrator
if (System.label.ARMS_Administrator_Profile_Id == userinfo.getProfileId() ||
sysAdmin.Id == userinfo.getProfileId()) {
List<Account> accList = [SELECT Id,Client_Released__c, RecordTypeId,Client_Number__c, Client_Released__c, Email_Sent__c FROM Account WHERE Id IN:accountIdList];
for(Account acc: accList){
if (acc.Client_Number__c != null && acc.Client_Released__c && acc.Email_Sent__c == true) {
sendpdfgenerationEmails(acc); //this is the method thats not bulkified.
acc.Email_Sent__c = false;
response = 'Email Sent';
}else {
response= 'Access Denied';
}
}
update accList;
}
}catch(Exception e) {
System.debug(e.getMessage());
response = 'Error sending emails';
}
return response;
}
public static void sendpdfgenerationEmails(Account acc){
system.debug('start of confirmation card and pdf generation');
//Logic to find which VF template is used to send an email.
list<EmailTemplate> templateId = new list<EmailTemplate>();
string temppartner;
String partner_opt_in_attachment;
boolean sendFCAmail;
List<Dealership_PDF_Generation__c> custsettingdata = Dealership_PDF_Generation__c.getall().values();
System.debug('custom setting size = ' + custsettingdata.size());
// Fetch State
if(acc.Dealership_State__c!=null && acc.Dealership_Partner__c!=null)
{
for(Dealership_PDF_Generation__c tempcustsetting :custsettingdata)
{
if(acc.Dealership_Partner__c == tempcustsetting.Dealership_Partner__c && acc.Dealership_State__c==tempcustsetting.State__c && tempcustsetting.State__c=='WA' && acc.Dealership_State__c=='WA'){
//For WA State
// temppartner= '%' + tempcustsetting.TEMPLATE_Unique_name__c + '%';
temppartner= tempcustsetting.TEMPLATE_Unique_name__c;
if(acc.Dealership_Spiff_Payment__c == '% premium'){
partner_opt_in_attachment=tempcustsetting.opt_in_form_premium__c;
}else{
partner_opt_in_attachment=tempcustsetting.opt_in_form_nonpremium__c;
}
}
else if(acc.Dealership_Partner__c == tempcustsetting.Dealership_Partner__c && acc.Dealership_State__c==tempcustsetting.State__c && tempcustsetting.State__c=='TX' && acc.Dealership_State__c=='TX'){
//For TX State
//temppartner= '%' + tempcustsetting.TEMPLATE_Unique_name__c + '%';
temppartner= tempcustsetting.TEMPLATE_Unique_name__c;
if(acc.Dealership_Spiff_Payment__c == '% premium'){
partner_opt_in_attachment=tempcustsetting.opt_in_form_premium__c;
}else{
partner_opt_in_attachment=tempcustsetting.opt_in_form_nonpremium__c;
}
}
else if(acc.Dealership_Partner__c == tempcustsetting.Dealership_Partner__c && acc.Dealership_State__c!=tempcustsetting.State__c && tempcustsetting.State__c!='TX' && acc.Dealership_State__c!='TX' && acc.Dealership_State__c!='WA' &&tempcustsetting.State__c!='WA' ){
//For Non TX State
//temppartner= '%' + tempcustsetting.TEMPLATE_Unique_name__c + '%';
temppartner= tempcustsetting.TEMPLATE_Unique_name__c;
if(acc.Dealership_Spiff_Payment__c == '% premium'){
partner_opt_in_attachment=tempcustsetting.opt_in_form_premium__c;
}else{
partner_opt_in_attachment=tempcustsetting.opt_in_form_nonpremium__c;
}
system.debug('grabbed template: ' + temppartner);
}
if(acc.Dealership_Partner__c != null && temppartner!=null ){
templateId.add([Select id,DeveloperName from EmailTemplate where DeveloperName = :temppartner]); //This will probably cause governor limit issues. First problem
}
if (partner_opt_in_attachment != null) {
StaticResource sr = [Select s.Name, s.Id, s.Body From StaticResource s where s.Name =: partner_opt_in_attachment]; //'static_resource' is the name of the static resource PDF. This is another SOQL query that will cause problems
Blob tempBlob = sr.Body;
Messaging.EmailFileAttachment efa = new Messaging.EmailFileAttachment();
efa.setBody(tempBlob);
efa.setFileName('Opt-in.pdf');
List<Messaging.EmailFileAttachment> attachments = new List<Messaging.EmailFileAttachment>();
attachments.add(efa);
// add attachment to each email
for (Messaging.SingleEmailMessage email : emails) {
email.setFileAttachments(attachments);
}
}
system.debug('email sent: ' + emails.size());
Messaging.sendEmail(emails);
}
}
}
The reason why I am trying to bulkify this is because I have written a APEX scheduler that calls the resendemails method everyday at 7am to check which records need to have an email sent. I am afraid that if there are more than a 100 clients then it will cause problems and not send the emails. Any suggestions on how I can optimize the sendpdfemailgeenration() method?
Thank you
Yes, you are right - your's resendEmails() method is not bulkified.
Firstly, let me explain you why is that:
SOQL to get Accounts
Loop 1 on List of Account records
Call sendpdfgenerationEmails() method
Retrieve list of Dealership_PDF_Generation__c records
Loop 2 on List of Dealership_PDF_Generation__c records
SOQL to get StaticResources - Very bad! It's inside double loop!
Call Messaging.sendEmail() method - Very bad! It's inside double loop!
Update on List of Account records
You need to remember that:
1. You should never do SOQLs in loops! - Limit 100 SOQLs per transaction
2. You should never call Messaging.sendEmail() in loops! - Limit 10 calls per transaction
Now let me guide you how to refactor this method:
#AuraEnabled
public static String resendEmails(List<String> accountIdList) {
// 1. SOQL for List of Account records
// 2. Retrieve list of Dealership_PDF_Generation__c records
// 3. SOQL for List of StaticResources for all Names from Dealership_PDF_Generation__c records
// 4. Declaration of new List variable for Messaging.SingleEmailMessage objects
// 5. Loop 1 on List of Account records
// 6. Call new "prepareEmailsForAccount()" method, which prepares and returns list of Messaging.SingleEmailMessage objects
// 7. Add returned Messaging.SingleEmailMessage objects to list from point 4
// 8. End of loop 1
// 9. Call "Messaging.sendEmail()" method with list from point 4
// 10. Update on List of Account records
}
With this you will avoid SOQLs and calling Messaging.sendEmail() method in loops.

Pagination in Google cloud endpoints + Datastore + Objectify

I want to return a List of "Posts" from an endpoint with optional pagination.
I need 100 results per query.
The Code i have written is as follows, it doesn't seem to work.
I am referring to an example at Objectify Wiki
Another option i know of is using query.offset(100);
But i read somewhere that this just loads the entire table and then ignores the first 100 entries which is not optimal.
I guess this must be a common use case and an optimal solution will be available.
public CollectionResponse<Post> getPosts(#Nullable #Named("cursor") String cursor,User auth) throws OAuthRequestException {
if (auth!=null){
Query<Post> query = ofy().load().type(Post.class).filter("isReviewed", true).order("-timeStamp").limit(100);
if (cursor!=null){
query.startAt(Cursor.fromWebSafeString(cursor));
log.info("Cursor received :" + Cursor.fromWebSafeString(cursor));
} else {
log.info("Cursor received : null");
}
QueryResultIterator<Post> iterator = query.iterator();
for (int i = 1 ; i <=100 ; i++){
if (iterator.hasNext()) iterator.next();
else break;
}
log.info("Cursor generated :" + iterator.getCursor());
return CollectionResponse.<Post>builder().setItems(query.list()).setNextPageToken(iterator.getCursor().toWebSafeString()).build();
} else throw new OAuthRequestException("Login please.");
}
This is a code using Offsets which seems to work fine.
#ApiMethod(
name = "getPosts",
httpMethod = ApiMethod.HttpMethod.GET
)
public CollectionResponse<Post> getPosts(#Nullable #Named("offset") Integer offset,User auth) throws OAuthRequestException {
if (auth!=null){
if (offset==null) offset = 0;
Query<Post> query = ofy().load().type(Post.class).filter("isReviewed", true).order("-timeStamp").offset(offset).limit(LIMIT);
log.info("Offset received :" + offset);
log.info("Offset generated :" + (LIMIT+offset));
return CollectionResponse.<Post>builder().setItems(query.list()).setNextPageToken(String.valueOf(LIMIT + offset)).build();
} else throw new OAuthRequestException("Login please.");
}
Be sure to assign the query:
query = query.startAt(cursor);
Objectify's API uses a functional style. startAt() does not mutate the object.
Try the following:
Remove your for loop -- not sure why it is there. But just iterate through your list and build out the list of items that you want to send back. You should stick to the iterator and not force it for 100 items in a loop.
Next, once you have iterated through it, use the iterator.getStartCursor() as the value of the cursor.

Spring MVC + RequestParam as Map + get URL array parameters not working

I'm currently working on an API controller. This controller should be able to catch any - unknown - parameter and put it in a Map object. Now I'm using this code to catch all parameters and put them in a Map
public String processGetRequest(final #RequestParam Map params)
Now the url I call is as follows:
server/api.json?action=doSaveDeck&Card_IDS[]=1&Card_IDS[]=2&Card_IDS[]=3
Then I print out the parameters, for debugging purposes, with this piece of code:
if (logger.isDebugEnabled()) {
for (Object objKey : params.keySet()) {
logger.debug(objKey.toString() + ": " + params.get(objKey));
}
}
The result of that is:
10:43:01,224 DEBUG ApiRequests:79 - action: doSaveDeck
10:43:01,226 DEBUG ApiRequests:79 - Card_IDS[]: 1
But the expected result should be something like:
10:43:XX DEBUG ApiRequests:79 - action: doSaveDeck
10:43:XX DEBUG ApiRequests:79 - Card_IDS[]: 1
10:43:XX DEBUG ApiRequests:79 - Card_IDS[]: 2
10:43:XX DEBUG ApiRequests:79 - Card_IDS[]: 3
Or atleast tell me that the Card_IDS is an String[] / List<String> and therefore should be casted. I also tried casting the parameter to a List<String> manually but that does not work either:
for (Object objKey : params.keySet()) {
if(objKey.equals(NameConfiguration.PARAM_NAME_CARDIDS)){ //Never reaches this part
List<String> ids = (List<String>)params.get(objKey);
for(String id : ids){
logger.debug("Card: " + id);
}
} else {
logger.debug(objKey + ": " + params.get(objKey));
}
}
Could someone tell me how to get the array Card_IDS - It must be dynamically - from the Map params by using the NameConfiguration.PARAM_NAME_CARDIDS?
When you request a Map annotated with #RequestParam Spring creates a map containing all request parameter name/value pairs. If there are two pairs with the same name, then only one can be in the map. So it's essentially a Map<String, String>
You can access all parameters through a MultiValueMap:
public String processGetRequest(#RequestParam MultiValueMap parameters) {
This map is essentially a Map<String, List<String>>. So parameters with the same name would be in the same list.

Resources