enter image description here
I have been around this for a few days but couldn't find the actual cause of the error. Earlier I had some issues related to importing the methods in js but even after solving I couldn't proceed further. I have checked the errors related to the badge's task which was shown but I couldn't move after one point.
<!--BoatDataService.cls-->
public with sharing class BoatDataService {
public static final String LENGTH_TYPE = 'Length';
public static final String PRICE_TYPE = 'Price';
public static final String TYPE_TYPE = 'Type';
#AuraEnabled(cacheable=true)
public static List<Boat__c> getBoats(String boatTypeId) {
// Without an explicit boatTypeId, the full list is desired
String query = 'SELECT '
+ 'Name, Description__c, Geolocation__Latitude__s, '
+ 'Geolocation__Longitude__s, Picture__c, Contact__r.Name, '
+ 'BoatType__c, BoatType__r.Name, Length__c, Price__c '
+ 'FROM Boat__c';
if (String.isNotBlank(boatTypeId)) {
query += ' WHERE BoatType__c = :boatTypeId';
}
query += ' WITH SECURITY_ENFORCED ';
return Database.query(query);
}
#AuraEnabled(cacheable=true)
public static List<Boat__c> getSimilarBoats(Id boatId, String similarBy) {
List<Boat__c> similarBoats = new List<Boat__c>();
List<Boat__c> parentBoat = [SELECT Id, Length__c, Price__c, BoatType__c, BoatType__r.Name
FROM Boat__c
WHERE Id = :boatId
WITH SECURITY_ENFORCED];
if (parentBoat.isEmpty()) {
return similarBoats;
}
if (similarBy == LENGTH_TYPE) {
similarBoats = [
SELECT Id, Contact__r.Name, Name, BoatType__c, BoatType__r.Name, Length__c, Picture__c, Price__c, Year_Built__c
FROM Boat__c
WHERE Id != :parentBoat.get(0).Id
AND (Length__c >= :parentBoat.get(0).Length__c / 1.2)
AND (Length__c <= :parentBoat.get(0).Length__c * 1.2)
WITH SECURITY_ENFORCED
ORDER BY Length__c, Price__c, Year_Built__c
];
} else if (similarBy == PRICE_TYPE) {
similarBoats = [
SELECT Id, Contact__r.Name, Name, BoatType__c, BoatType__r.Name, Length__c, Picture__c, Price__c, Year_Built__c
FROM Boat__c
WHERE Id != :parentBoat.get(0).Id
AND (Price__c >= :parentBoat.get(0).Price__c / 1.2)
AND (Price__c <= :parentBoat.get(0).Price__c * 1.2)
WITH SECURITY_ENFORCED
ORDER BY Price__c, Length__c, Year_Built__c
];
} else if (similarBy == TYPE_TYPE) {
similarBoats = [
SELECT Id, Contact__r.Name, Name, BoatType__c, BoatType__r.Name, Length__c, Picture__c, Price__c, Year_Built__c
FROM Boat__c
WHERE Id != :parentBoat.get(0).Id
AND (BoatType__c = :parentBoat.get(0).BoatType__c)
WITH SECURITY_ENFORCED
ORDER BY Price__c, Length__c, Year_Built__c
];
}
return similarBoats;
}
#AuraEnabled(cacheable=true)
public static List<BoatType__c> getBoatTypes() {
return [SELECT Name, Id FROM BoatType__c WITH SECURITY_ENFORCED ORDER BY Name];
}
#AuraEnabled(cacheable=false)
public static List<BoatReview__c> getAllReviews(Id boatId) {
return [
SELECT
Id,
Name,
Comment__c,
Rating__c,
LastModifiedDate,
CreatedDate,
CreatedBy.Name,
CreatedBy.SmallPhotoUrl,
CreatedBy.CompanyName
FROM
BoatReview__c
WHERE
Boat__c =:boatId
WITH SECURITY_ENFORCED
ORDER BY
CreatedDate DESC
];
}
#AuraEnabled(cacheable=true)
public static String getBoatsByLocation(Decimal latitude, Decimal longitude, String boatTypeId) {
// Without an explicit boatTypeId, the full list is desired
String query = 'SELECT Name, Geolocation__Latitude__s, Geolocation__Longitude__s FROM Boat__c ';
if (String.isNotBlank(boatTypeId)) {
query += 'WHERE BoatType__c = :boatTypeId ';
}
query += ' WITH SECURITY_ENFORCED ORDER BY DISTANCE(Geolocation__c, GEOLOCATION(:latitude, :longitude), \'mi\') LIMIT 10';
return JSON.serialize(Database.query(query));
}
}
<!--boatSearchResults.html-->
<template>
<lightning-layout multiple-rows>
<!-- Top -->
<lightning-layout-item size="12">
<lightning-card >
<h1 slot="title">Find a Boat</h1>
<!-- New Boat button goes here -->
<h1 slot="actions">
<lightning-button label="New Boat" onclick={createNewBoat}></lightning-button>
</h1>
<p class="slds-var-p-horizontal_small slds-align_absolute-center">
<!-- boatSearchForm component goes here -->
<c-boat-search-form onsearch={searchBoats}></c-boat-search-form>
</p>
</lightning-card>
</lightning-layout-item>
<lightning-layout-item size="12" class="slds-var-p-top_small slds-is-relative">
<template if:true={isLoading}>
<lightning-spinner alernative-text = "Loading" variant= "brand"></lightning-spinner>
</template>
<template if:false={isLoading}>
<c-boat-search-results boat-type-id={boatTypeId} onloading={handleLoading} ondoneloading={handleDoneLoading}></c-boat-search-results>
</template>
</lightning-layout-item>
</lightning-layout>
</template>
<!--boatSearchResults.js-->
import { LightningElement,api,track } from 'lwc';
import { NavigationMixin } from 'lightning/navigation'
export default class BoatSearch extends NavigationMixin(LightningElement) {
#api isLoading = false;
#track boatTypeId = '';
// Handles loading event
handleLoading() {
this.isLoading = true;
}
// Handles done loading event
handleDoneLoading() {
setTimeout(()=>{
this.isLoading = false;}, 1000)
}
// Handles search boat event
// This custom event comes from the form
searchBoats(event) {
this.handleLoading();
this.boatTypeId = event.detail.boatTypeId;
console.log(this.boatTypeId)
this.template.querySelector('c-boat-search-results').searchBoats(this.boatTypeId);
this.handleDoneLoading();
}
createNewBoat() {
this[NavigationMixin.Navigate]({
type:'standard__objectPage',
attributes:{
objectApiName: 'Boat__c',
actionName: 'new'
}
})
}
}
Thanks in advance for the answer
Related
I'm Working through this challenge and appear to have run into an issue i have not found a solution to (nor does it appear that anyone has posted a question on this specifically.)
looking at the log is pretty painful as any System.debug() lines appear to be dropped due to the size of the log being >18MB but it appears there are 40000 rows to be added. Having caved and looked at some of the other code dumps out there i dont see that anyone else looks to have had this issue or made any special arrangements with batching and as far as i can see i haven't done anything drastically different to them (but the subtlety is clearly more than enough to cause an issue).
So if anyone could have a look over this and give me a hint as to where/why i have gone wrong it would be a great help. I'm not looking for a link to a working code set to copy i'm hoping for some understanding as to where my approach is incorrect/causing issues
With my trigger
trigger MaintenanceRequest on Case ( after update) {
List<Case> closedCases = new List<Case>();
for (Case c : Trigger.New){
System.debug('Type :' + c.Type );
System.debug('Equals Repair: ' +c.Type.equalsIgnoreCase('Repair'));
System.debug('Equals Routine Maintenance: ' +c.Type.equalsIgnoreCase('Routine Maintenance'));
if (
(c.Type.equalsIgnoreCase('Repair') || c.Type.equalsIgnoreCase('Routine Maintenance'))
&& c.isClosed == true )
{
closedCases.add(c);
}
}
System.debug(closedCases.size() + ' cases were closed');
if(closedCases.size() > 0){
MaintenanceRequestHelper.createScheduledMaintainance(closedCases);
}
}
and helper
public with sharing class MaintenanceRequestHelper {
public MaintenanceRequestHelper() {
}
public static Map<Id,Integer> getMainainanceCycleForCases(){
List<AggregateResult> maintainTimes = [SELECT Maintenance_Request__c, MIN(Equipment__r.Maintenance_Cycle__c)cycle
FROM Equipment_Maintenance_Item__c
GROUP BY Maintenance_Request__c
];
System.debug(maintainTimes);
Map<Id,Integer> maintinTimeMap = new Map<Id,Integer>();
for( AggregateResult e : maintainTimes ){
maintinTimeMap.put((Id)e.get('Maintenance_Request__c'), ((Double)e.get('cycle')).intValue());
}
System.debug(maintinTimeMap);
return maintinTimeMap;
}
public static Map<Id,List<Id>> getMaintainanceItems(List<Case> closedCases){
// map maintainance_Request_ID, Equipment_Maintenance_Item__c
List<Equipment_Maintenance_Item__c> equipmentMaintainanceList = [SELECT Maintenance_Request__c,
Equipment__r.Id
FROM Equipment_Maintenance_Item__c
Where Maintenance_Request__c IN :closedCases];
Map<Id,List<Id>> maintainance_equipmentMap = new Map<Id, List<Id>>();
for (Equipment_Maintenance_Item__c e:equipmentMaintainanceList){
System.debug('Putting : '+ e.Maintenance_Request__c + ' : ' + e.Equipment__r.Id);
if(maintainance_equipmentMap.containsKey(e.Maintenance_Request__c)){
maintainance_equipmentMap.get(e.Maintenance_Request__c).add(e.Equipment__r.Id);
}else{
maintainance_equipmentMap.put(e.Maintenance_Request__c,new List<id>{e.Equipment__r.Id});
}
System.debug('Map now : '+ maintainance_equipmentMap);
}
System.debug('Returning: ' +maintainance_equipmentMap);
return maintainance_equipmentMap;
}
public static void createScheduledMaintainance(List<Case> closedCaseList) {
System.debug(closedCaseList.size() + ' Cases to create');
Map<Id,Integer> maintainTimeMap = getMainainanceCycleForCases();
Map<Id,List<Id>> maintainanceItems = getMaintainanceItems(closedCaseList); // map maintainance_Request_ID, Equipment_Maintenance_Item__c
List<Case> createNewCases = new List<Case>();
for(Case c : closedCaseList){
Case newCase = new Case();
newCase.Type = 'Routine Maintenance';
newCase.Status = 'New';
newCase.Vehicle__c = c.Vehicle__c;
newCase.Subject = c.Subject;
newCase.Date_Reported__c = Date.today();
newCase.Date_Due__c = Date.today() + maintainTimeMap.get(c.Id);
newCase.ProductId = c.ProductId;
createNewCases.add(newCase);
}
System.debug(createNewCases.size() +' to insert');
list<Equipment_Maintenance_Item__c> itemsListToinsert= new list<Equipment_Maintenance_Item__c>();
if(createNewCases.size()>0){
insert createNewCases;
for(Case ca : createNewCases){
for(Id key: maintainanceItems.keySet()){
List<Id> equipment = maintainanceItems.get(key);
for (Id e : equipment){
Equipment_Maintenance_Item__c newitem = new Equipment_Maintenance_Item__c();
newitem.Equipment__c=e;
newitem.Maintenance_Request__c= ca.Id;
itemsListToinsert.add(newitem);
}
}
}
}
System.debug('itemsListToinsert Size: ' +itemsListToinsert.size());
if (itemsListToinsert.size() >0){
insert itemsListToinsert; //<<<< ERROR TRIGGERS HERE and has ~40000 rows <<<<<
}
}
}
So, It seems i was over complicating the issue to an extent.
Having taken a break and walking through the problem again something just didnt sit right with this block of code.
if(createNewCases.size()>0){
insert createNewCases;
for(Case ca : createNewCases){
for(Id key: maintainanceItems.keySet()){
List<Id> equipment = maintainanceItems.get(key);
for (Id e : equipment){
Equipment_Maintenance_Item__c newitem = new Equipment_Maintenance_Item__c();
newitem.Equipment__c=e;
newitem.Maintenance_Request__c= ca.Id;
itemsListToinsert.add(newitem);
}
}
}
}
For -> For -> For -> create new Equipment_Maintenance_Item__c
With 0 checks to see if this new Equipment_Maintenance_Item__c was needed.
I ended up partly re-writing the class but the main change was re-working the section of code above to
List<Equipment_Maintenance_Item__c> equipmentMaintainanceList = [SELECT Maintenance_Request__c,
Equipment__r.Id
FROM Equipment_Maintenance_Item__c
Where Maintenance_Request__c IN :closedCaseList];
List<Equipment_Maintenance_Item__c> updatedEquipment = new List<Equipment_Maintenance_Item__c>();
for(Case closed : closedCaseList){
for(Equipment_Maintenance_Item__c eqip : equipmentMaintainanceList){
if (eqip.Maintenance_Request__c == closed.Id){
Equipment_Maintenance_Item__c e = eqip.clone(false, false, false, false);
e.Maintenance_Request__c = oldToNewMap.get(closed.Id).Id;
updatedEquipment.add(e);
}
}
}
if (updatedEquipment.size() > 0 ){
insert updatedEquipment;
}
For me the lesson is be very mindful of nested for loops when the outcome is going to come up against some hard and fast governor limits.
I am working on a lightning component that will display a custom, dynamic, list of 'related' records that can be used to display related records in a list on an object that is ALSO a child of the same parent. So, for example, If the Account is the parent record, and custom object 1 and custom object 2 are both children of the account, I want to display the custom object 1 records that are associated with the same account as custom object 2 on the custom object 2 lightning record page. I am using user inputs in the LC canvas and field sets to locate the records and display the columns, respectively.
The component I created works in that it does query and display the records, columns, and field values dynamically. I wanted to also include a clickable link to the custom object 1 records using the 'Name' field. However, I can't seem to dynamically set the typeAttribute of the Name field to display the label of the record. Because I am assigning it as an attribute of the column header, it is only taking the last value in the array.
Is there a way to assign the typeAttribute to the records response, and NOT the column header response?
Here is my code:
Apex Class:
public class DynamicRelatedListController {
#AuraEnabled
public static String fetchParent(String recId, String parentLookup, String objPageName) {
String strSOQL = 'SELECT Id, ' + parentLookup + ' FROM ' + objPageName + ' WHERE Id = \'' + recId + '\' LIMIT 1 ' ;
List <SObject> currentRecord = new List <SObject>(Database.query(strSOQL));
system.debug('Database Result for fetch parent ' + strSOQL);
List <String> parentIds = new List<String>();
for(SObject cr : currentRecord) {
parentIds.add(String.valueOf(cr.get(parentLookup)));
}
String parentId = parentIds[0];
return parentId;
}
#AuraEnabled
public static List < SObject > fetchChildren(String objectName, String criteria, String parentFieldAPIName, String recId, String parentLookup, String objPageName) {
String parentId = fetchParent(recId, parentLookup, objPageName);
// String prefix ='%' + GM_Util.findObjectNameFromRecordIdPrefix(parentId) + '%';
List < SObject > childRecordList = new List < SObject > ();
String strSOQL = 'SELECT Id, ' + parentFieldAPIName + ' FROM ' + objectName + ' WHERE ' + parentFieldAPIName + ' = \'' + parentId + '\'';
if ( String.isNotBlank( criteria ) )
strSOQL += ' ' + criteria;
childRecordList = Database.query(strSOQL);
system.debug('Database Result for fetch children ' + strSOQL);
return childRecordList;
}
#AuraEnabled
public static DataTableDetails fetchRelatedRecs(String recId, String fieldSetName, String criteria, String objectName, String parentFieldAPIName, String objPageName, String parentLookup)
{
DataTableDetails dataTableDtls = new DataTableDetails();
List<GM_Util_Aura.FieldSetMemberWrapperClass> listOfFieldSetMembers = new List<GM_Util_Aura.FieldSetMemberWrapperClass>();
if(fieldSetName != NULL && fieldSetName != ''){
listOfFieldSetMembers = GM_Util_Aura.getFieldSetMemberJSON(objectName, fieldSetName);
}
// List<GM_Util_Aura.FieldSetMemberWrapperClass> listOfFieldSetMembers = GM_Util_Aura.getFieldSetMemberJSON(objectName, fieldSetName);
List<String> WHERE_IN_LIST = new List <String>();
for (SObject crl: fetchChildren(objectName, criteria, parentFieldAPIName, recId, parentLookup, objPageName)) {
WHERE_IN_LIST.add(crl.Id);
}
String SQL = 'SELECT Id ';
if(listOfFieldSetMembers != null && listOfFieldSetMembers.size() > 0){
for(GM_Util_Aura.FieldSetMemberWrapperClass fsmwc : listOfFieldSetMembers){
dataTableDtls.fieldLabelsList.add(fsmwc);
SQL = SQL + ',' + fsmwc.fieldName;
}
}
SQL += ' FROM ' + objectName +' WHERE Id IN : WHERE_IN_LIST' ;
System.debug('final SQL statement (WHERE_IN_LIST): ' + SQL);
dataTableDtls.returnedParentRecords = Database.query(SQL);
System.debug('returned Parent Records are ' + Database.Query(SQL));
// filteredResults = Database.query(SQL);
System.debug('final filtered results are: ' + dataTableDtls);
System.debug('returned field labels are ' + listOfFieldSetMembers);
return dataTableDtls;
}
public class DataTableDetails{
#AuraEnabled
public List<sObject> returnedParentRecords = new List<sObject>();
#AuraEnabled
public List<GM_Util_Aura.FieldSetMemberWrapperClass> fieldLabelsList = new List<GM_Util_Aura.FieldSetMemberWrapperClass>();
}
}
My component helper & cmp code:
({
getDataTable: function(component, event) {
console.log("entered Get Data Table");
var action = component.get("c.fetchRelatedRecs");
action.setParams({
recId: component.get("v.recordId"),
objectName: component.get("v.ObjectAPIName"),
fieldSetName: component.get("v.FieldSetAPIName"),
objPageName: component.get("v.sObjectName"),
parentFieldAPIName: component.get("v.ParentFieldAPIName"),
parentLookup: component.get("v.ParentFieldLookupName"),
criteria: component.get("v.criteria")
});
action.setCallback(this, function(response){
//console.log("this is the fieldSetName " + fieldSetName)
var state = response.getState();
if(state === 'SUCCESS'){
var fieldSetResponse = response.getReturnValue().fieldLabelsList;
var parentRecordsResponse = response.getReturnValue().returnedParentRecords;
console.log("Parent records length " + parentRecordsResponse.length);
console.log("Field Set Length " + fieldSetResponse.length);
console.log("here are the field labels before data transformation " + JSON.stringify(fieldSetResponse));
console.log("Here are all the related records before transformation " + JSON.stringify(parentRecordsResponse));
//this for loop changes all lookup fields to type 'url' for clickable links
for(var i = 0; i < parentRecordsResponse.length; i++ ) {
if(fieldSetResponse[i].fieldName.includes('__r.Name')) {
fieldSetResponse[i].type ='url';
//drop the .Name from the lookup field so that the lightning:datatable can find the field value in the parentRecordsResponse object
fieldSetResponse[i].fieldName = fieldSetResponse[i].fieldName.replace('.Name', '') ;
//this for loop locates the key in the parentRecordsResponse object that matches the lookup field in the fieldSetResponse object.
for(var j = 0; j < parentRecordsResponse.length; j++) {
var arraykeys = Object.keys(parentRecordsResponse[j]);
var arrayvalues = Object.values(parentRecordsResponse[j]);
console.log("Array Keys in iteration "+ arraykeys[j]);
console.log("Array Values " + JSON.stringify(arrayvalues));
//this for loop locates the key in the parentRecordsResponse object that matches the lookup field in the fieldSetResponse object.
for(var h = 0; h <arraykeys.length; h++) {
console.log("Array Keys in iteration h "+ arraykeys[h]);
//fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h].Name };
if(fieldSetResponse[i].fieldName === arraykeys[h]){
//assign the'type attributes' to the url field with the Name value of the lookup field
fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h]['Name'] };
//transform the nested lookup field object to /recordId for the url
parentRecordsResponse[j][arraykeys[h]] = "/" + arrayvalues[h].Id ;
}
}
}
}
}
console.log("here are the field labels after data transformation " + JSON.stringify(fieldSetResponse));
console.log("Here are all the related records " + JSON.stringify(parentRecordsResponse));
//send the responses back to the controller and set them using the 'v.' keys
component.set("v.columnsHeader", fieldSetResponse);
component.set("v.listofRelatedRecords", parentRecordsResponse);
}
else if (state === 'ERROR'){
console.log('::::::::::::: ERROR :::::::::::::');
}
});
$A.enqueueAction(action);
}
})
<aura:component controller="DynamicRelatedListController" implements="flexipage:availableForRecordHome,force:hasRecordId,force:lightningQuickAction,force:hasSObjectName" access="global">
<aura:handler name="init" value="{!this}" action="{!c.doInit}" />
<aura:attribute name="ObjectAPIName" type="String"/>
<aura:attribute name="FieldSetAPIName" type="String"/>
<aura:attribute name="ParentFieldAPIName" type="String"/>
<aura:attribute name="ParentFieldLookupName" type="String"/>
<aura:attribute name="Criteria" type="String"/>
<aura:attribute name="TableName" type="String"/>
<!-- <aura:attribute name="recordId" type="String" /> -->
<aura:attribute name="columnsHeader" type="List"/>
<aura:attribute name="listofRelatedRecords" type="Object"/>
<lightning:card aura:id="lightCard" class="slds-card_boundary" title="{!v.TableName}" iconName="standard:file">
<div style="overflow-x: auto;">
<lightning:datatable data="{!v.listofRelatedRecords}"
columns="{!v.columnsHeader}"
keyField="Id"
hideCheckboxColumn="true"/>
</div>
</lightning:card>
</aura:component>
This is what the end result looks like - note that all of the values ARE different, except for the label of the Name field. The link itself works fine, and navigates to the appropriate record:
I figured it out. At a basic level, this line is missing the attribute of 'fieldName', which takes the API of the field name and applies the value.
So this: fieldSetResponse[i]['typeAttributes'] = {label: arrayvalues[h]['Name'] };
Should be this: fieldSetResponse[i]['typeAttributes'] = {label: fieldName: {API NAME OF FIELD THAT HOLDS THE DISPLAY VALUE} };
Even so, this is not possible to do dynamically because When the fieldName in this case is the lookup field itself, it displays the record Id. I could pass a hard-coded API name, but obviously that would defeat the purpose.
To get around this, I added a generic label (click to view record) and created a formula field on that target object that pulls in the parent record name and added that to my field set.
This is a known issue: https://trailblazer.salesforce.com/ideaView?id=0873A000000lLXYQA2
That could potentially be handled by creating a flattening function (although I haven't tried to implement that yet): https://developer.salesforce.com/forums/?id=9062I000000XtwnQAC
I have an IQueryable from the database, which I map to view model like this:
IQueryable<ItemList> itemsList = query.Select(x =>
new ItemList()
{
Phone = x.Phone,
Email = x.Email,
Fax = x.Fax,
State = x.StateId.HasValue ? new GenericBox() { Text = x.State.Name , Value = x.State.Id } : null,
Type = x.Type,
ChildItems = x.Contact.ChildItems.OrderBy(a => a.Order).Select(a => new GenericBox() { Text = a.Child.FirstName + " " + a.Child.LastName, Value = a.ChildId })
});
The GenericBox class is like this:
public class GenericBox
{
public string Text { get; set; }
public Guid Value { get; set; }
}
Then I transform this IQueryable to pure SQL and execute it with a SqlCommand.
The problem lies in the properties State and ChildItems.
I need to set alias for State properties Text and Value. Because IQueryable set them in the SQL with random generated names:
SELECT
[Project3].[Id] AS [Id1],
[Project3].[Phone] AS [Phone],
[Project3].[Email] AS [Email],
[Project3].[Fax] AS [Fax],
[Project3].[C2] AS [C2],
[Project3].[Name1] AS [Name1],
[Project3].[StateId] AS [StateId],
[Project3].[Type] AS [Type],
[Project3].[C5] AS [C3],
[Project3].[C3] AS [C4],
[Project3].[AgentId] AS [AgentId]
As you can see in the SELECT Name1 is for the State.Text from my class and C4 is for ChildItem.Text.
I need in the SELECT the alias for column Name1 to be Text for example. And the alias for Column C4 I want to be again Text.
I am new to salesforce and I am stuck with a situation here.
I have a class which is scheduled every hour. I hit an account with the below code and an email is sent out to MAROPOST (Marketing automation tool). When this happen I want to track the Account and create a case or a log which says Welcome Email is sent so that I don't hit the same Account again.
Please help. Below is the working class. Please help
public class PD_WelcomeMaroPost {
public static string sendEmailThroughMaro(string myInpEmail) {
string successContacts = '';
string failureContacts = '';
// SQL to fetch FBO who Joined Today
list<Account> conts = new list<Account> ([SELECT name, Email_FLP_com__c,
(SELECT Id
FROM Stripe_Subscriptons__r
WHERE Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false
LIMIT 1)
from account
where ID IN (
select Distributor__c
from Stripe_Subscripton__c
where Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false)
AND Email_FLP_com__c != NULL
LIMIT 100]);
system.debug('>>>>>>>>>>' + conts);
overallEmail myEmail = new overallEmail();
List<Stripe_Subscripton__c> subsToUpdate = new List<Stripe_Subscripton__c>();
for(Account c : conts){
myEmail.email.campaign_id = 172;
myEmail.email.contact.Email = c.Email_FLP_com__c;
myEmail.email.contact.first_name = c.name;
/**MAp<String, String> tags = new Map<String, String>();
tags.put('firstName', c.name);
myEmail.email.tags = tags;**/
system.debug('#### Input JSON: ' + JSON.serialize(myEmail));
try{
String endpoint = 'http://api.maropost.com/accounts/1173/emails/deliver.json?auth_token=j-V4sx8ueUT7eKM8us_Cz5JqXBzoRrNS3p1lEZyPUPGcwWNoVNZpKQ';
HttpRequest req = new HttpRequest();
req.setEndpoint(endpoint);
req.setMethod('POST');
req.setHeader('Content-type', 'application/json');
req.setbody(JSON.serialize(myEmail));
Http http = new Http();
system.debug('Sending email');
HTTPResponse response = http.send(req);
system.debug('sent email');
string resultBodyGet = '';
resultBodyGet = response.getBody();
system.debug('Output response:' + resultBodyGet);
maroResponse myMaroResponse = new maroResponse();
myMaroResponse = (maroResponse) JSON.deserialize(resultBodyGet, maroResponse.class);
system.debug('#### myMaroResponse: ' + myMaroResponse);
if(myMaroResponse.message == 'Email was sent successfully')
successContacts = successContacts + ';' + c.Email_FLP_com__c;
else
failureContacts = failureContacts + ';' + c.Email_FLP_com__c;
}
catch (exception e) {
failureContacts = failureContacts + ';' + c.Email_FLP_com__c;
system.debug('#### Exception caught: ' + e.getMessage());
}
c.Stripe_Subscriptons__r[0].Welcome_Email__c = true;
subsToUpdate.add(c.Stripe_Subscriptons__r[0]);
}
Update subsToUpdate;
return 'successContacts=' + successContacts + '---' + 'failureContacts=' + failureContacts;
}
public class maroResponse {
public string message {get;set;}
}
public class overallEmail {
public emailJson email = new emailJson();
}
public class emailJson {
public Integer campaign_id;
public contactJson contact = new contactJson();
// Public Map<String, String> tags;
}
public class contactJson {
public string email;
public string first_name;
}
}
You're making a callout in a loop, there's governor limit of max 100 callouts. See Limits class to obtain current & max numbers programatically rather than hardcoding it.
Other than that it should be pretty simple change. First add your filter to the query and add a "subquery" (something like a JOIN) that pulls the related list of subscriptions
list<Account> conts = new list<Account> ([SELECT name, Email_FLP_com__c,
(SELECT Id
FROM Stripe_Subscriptions__r
WHERE Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false
LIMIT 1)
from account
where ID IN (
select Distributor__c
from Stripe_Subscripton__c
where Start_Date__c= TODAY
AND Status__c='active'
AND Welcome_Email__C = false)
AND Email_FLP_com__c != NULL
LIMIT 100]);
Then it's just few lines more
List<Stripe_Subscription__c> subsToUpdate = new List<Stripe_Subscription__c>();
for(Account a : conts){
// do your maropost code here
a.Stripe_Subscriptions__r[0].Welcome_Email__c = true;
subsToUpdate.add(a.Stripe_Subscriptions__r[0]);
}
update subsToUpdate;
Of course you might want to set that checkbox to true only if callout went OK ;)
After reading your code, I don't see where you tried to accomplish this. If you post your attempt I'd be glad to help fix it.
Instead I'll give you different logic for what you are trying to do.
1.) create new checkbox field
2.) in batch query where box is not checked
3.) send email
4.) check checkbox
to answer your comment here is some sample code, you will need to fix it yourself, i am just making temp names
for(sobjectname gg:[your query]){
Send email;
gg.checkbox = checked;
update gg;
}
it'd be better to make it bulkified though
list<yourSObject> tobeupdated = new list<yourSObject>([Your query]);
for(yourSObject gg: tobeupdated){
send email;
gg.checkbox = true;
}
update tobeupdated;
I am doing a fairly simple query for contact records using dynamic soql using the following method:
public PageReference contactSearch() {
contactResultSetSize = 0;
if(!String.isEmpty(firstname) || !String.isEmpty(lastname) || !String.isEmpty(company)) {
string soql = 'Select firstname, lastname, account.Name, account.BillingStreet, account.BillingCity, account.BillingState, account.BillingPostalCode From Contact';
String whereClause = '';
if(!String.isEmpty(firstname)) {
whereClause = ' Where firstname like \'%' + firstname + '%\'';
}
if(!String.isEmpty(lastname)) {
if(!String.isEmpty(firstname)) {
whereClause += ' AND lastname like \'%' + lastname + '%\'';
}
else {
whereClause = ' Where lastname like \'%' + lastname + '%\'';
}
}
if(!String.isEmpty(company)) {
if(!String.isEmpty(firstname) || !String.isEmpty(lastname)) {
whereClause += ' AND account.Name like \'%' + company + '%\'';
}
else {
whereClause = ' Where account.Name like \'%' + company + '%\'';
}
}
soql = soql + whereClause;
List<Contact> searchResults = Database.query(soql);
contactResultSetSize = searchResults.size();
if(contactLinesForPage == null) {
contactLinesForPage = new List<ContactWrapper>();
}
for(Contact c : searchResults) {
contactLinesForPage.add(new ContactWrapper(contactLinesForPage.size(), c, ''));
}
}
return null;
}
I am using a wrapper class and contactLinesForPage is a list of my wrapper object:
public List<ContactWrapper> contactLinesForPage {get; set;}
As a user does multiple searches, I don't want to re-add records to the searchResults list. How can I check if a record already exists in my object so I don't have duplicate records returned in the search?
Thanks for any help.
Or you could use a map. Add the ContactWrapper objects to the map. The key to the map is an id. If they add a duplicate contact it will simply overwrite the one already there. Your code would simply be
aMap.put(cw.id, cw); // one line eliminates duplicates.
When you want the list of ContactWrappers, simply return aMap.values();
If you want to abstract the behavior of maintaining the collection of Contacts, create a ContactCollection class and hide the implementation in there. This would provide something more reusable as well as a good pattern for similar situations.
Just add a check if contactLinesForPage allready contains this contact. Something like this:
for(Contact c : searchResults) {
Boolean toInsert = true;
for(ContactWrapper cw : contactLinesForPage){
if(cw.contact.Id == c.Id){
toInsert=false;
}
}
if(toInsert){
contactLinesForPage.add(new ContactWrapper(contactLinesForPage.size(), c, ''));
}
}