• Roger Wicki
  • NEWBIE
  • 130 Points
  • Member since 2014

  • Chatter
    Feed
  • 0
    Best Answers
  • 3
    Likes Received
  • 2
    Likes Given
  • 41
    Questions
  • 116
    Replies
Dear Community

Since there are way too few options to do literally anything with Account Team Members, we want to use the following approach:
  • Have a custom object "AccountTeamMemberTracking__c" that is a sort of snapshot of all Account Team Member entries
  • Have a custom object "AccountTeamHistory__c" that is supposed to store changes to Account Team Members
  • Use a scheduled Apex class to daily compare the Account Team Members against the AccountTeamMemberTracking__c to find changes, additions and deletions and add that as entries to the AccountTeamHistory__c
I was thinking:
  • If I look for Account Team Member records with created date today, I will obviously find insertions of new records that I need to add to the history and to the AccountTeamMemberTracking__c.
  • If I look for Account Team Member records with change date today, I will obviously find those that changed.
    • To compare them to AccountTeamMemberTracking__c, I need a good query for this
  • If I look for AccountTeamMemberTracking__c records, for which there are no Account Team Member records, I detect a deletion of an Account Team Member. For that I need a good query.
I thought I could simply store the Account Team Member Id on the AccountTeamMemberTracking__c object as a unique case sensitive string, but it appears that the SOQL "IN" only works on IDs for Semi-Joins.

I had the following in place (incomplete):
global class AccountTeamMemberTracking implements Schedulable
{
	@TestVisible private map<Id, AccountTeamMember> accountTeamMembers;
	@TestVisible private list<AccountTeamMemberTracking__c> trackingRecords;
	@TestVisible private list<AccountTeamHistory__c> accountTeamHistoryRecords;
	
	global void execute(system.SchedulableContext sc) {
		accountTeamMembers = new map<Id, AccountTeamMember>(queryChanges());
		trackingRecords = queryTrackings(accountTeamMembers.keySet());
		accountTeamHistoryRecords = new list<AccountTeamHistory__c>();
		handleChanges();
		
		trackingRecords = queryDeletions();
	}
	
	@TestVisible private list<AccountTeamMember> queryChanges() {
		return [SELECT	Id,
						UserId,
						TeamMemberRole,
						AccountId,
						LastModifiedById,
						LastModifiedDate
				FROM AccountTeamMember
				WHERE Id IN (SELECT AccountTeamMemberId__c FROM AccountTeamMemberTracking__c) AND LastModifiedDate = :system.today()];
	}
	
	@TestVisible private list<AccountTeamMemberTracking__c> queryTrackings(set<Id> accountTeamMemberIds) {
		return [SELECT	Id,
						TeamRole__c,
						TeamMemberId__c,
						AccountTeamMemberId__c,
						AccountId__c
				FROM AccountTeamMemberTracking__c WHERE AccountTeamMemberId__c IN :accountTeamMemberIds];
	}
	
	@TestVisible private list<AccountTeamMemberTracking__c> queryDeletions() {
		return [SELECT	Id,
						TeamRole__c,
						TeamMemberId__c,
						AccountTeamMemberId__c,
						AccountId__c
				FROM AccountTeamMemberTracking__c WHERE AccountTeamMemberId__c NOT IN (SELECT Id FROM accountTeamMember)];
	}
	
	@TestVisible private void handleChanges() {
		for ( AccountTeamMemberTracking__c atmt : trackingRecords ) {
			// add the history records
		}
	}
}
I receive errors on line 17 and 37:
User-added image

So what I was thinking now is that I could use a combination of UserId and AccountId to find matching records for a user can only appear once on an Account as a Team Member.  The problem is, I don't know how to do that. I would try to go for following, but I don't know whether they get the result I need:
@TestVisible private list<AccountTeamMember> queryChanges() {
		return [SELECT	Id,
						UserId,
						TeamMemberRole,
						AccountId,
						LastModifiedById,
						LastModifiedDate
				FROM AccountTeamMember
				WHERE UserId IN (SELECT TeamMemberId__c FROM AccountTeamMemberTracking__c) AND AccountId IN (SELECT AccountId__c FROM AccountTeamMemberTracking__c) AND LastModifiedDate = :system.today()];
	}

	@TestVisible private list<AccountTeamMemberTracking__c> queryDeletions() {
		return [SELECT	Id,
						TeamRole__c,
						TeamMemberId__c,
						AccountTeamMemberId__c,
						AccountId__c
				FROM AccountTeamMemberTracking__c WHERE TeamMemberId__c NOT IN (SELECT UserId FROM accountTeamMember) AND AccountId__c NOT IN (SELECT AccountId FROM accountTeamMember)];
	}

Does the AND here work as I need? I need to know the Account Team Members that have their UserId and AccountId matching on the same record of an AccountTeamMemberTracking__c. What I fear my code does is to check whether the UserId exists in the AccountTeamMemberTracking__c and to check whether the AccountId exists there, but not necessarily on the same record, which would be not what I want.


I hope I wrote that understandable. If you have any questions, please don't hesitate to ask.
Dear community

I am seeking for ways to improve my code's performance. One step would be optimise the custom metadata. Basically i use custom metadata to store a number of values, which i need to check against in the code. I need stuff like maps or MasterLabel String sets. Currently, I loop over all records and assign them however I see fit. I do however not like that I need a loop for that. I was searching and there is next to no information available. I found this though:
http://releasenotes.docs.salesforce.com/en-us/spring15/release-notes/rn_forcecom_development_additional_custom_metadata.htm

This article mentions getAllNames(), getByName(), etc as methods. These are what I need. Sadly they don't work. The article states, that I would have to sign up for a pilot program for custom metadata. Given that custom metadata is around for almost 3 years, I suppose if these aforementioned methods would be implemented in the final product, they should be available and documented.

Does anybody know more to this topic?

Best
Roger
Dear community

I need to send an email to owners of assets when certain criteria are met. I have a custom child object to the asset, called an asset reminder, which stores information about what kind of reminder (email template), what date and what interval the notification should be sent. I use scheduled apex to send these emails every night.

This is my code:
/**
*	Prepares an email per reminder
*/
@TestVisible private void prepareNofifications() {
	for ( AssetReminder__c reminder : assetReminders ) {
		transient Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
		message.setTemplateId(reminder.EmailTemplateId__c);
		message.setTargetObjectId(assets.get(reminder.AssetId__c).OwnerId); // assets is of type map<Id, Asset>
		message.setWhatId(reminder.AssetId__c);
		message.setSaveAsActivity(false);
		emails.add(message);
	}
}

However I receive an error when it executes:
Reminder Dispatcher could not send out reminders.
Exception Type: System.EmailException
Exception Cause: null
Exception Message: SendEmail failed. First exception on row 0; first error: INVALID_ID_FIELD, WhatId is not available for sending emails to UserIds.: [whatId, 02i0E000000U7N4]

Reading up on that error shows that if I specify a WhatId, I can not use a UserId for the TargetObjectId. On one hand this is a total no brainer and on the other hand my template uses asset and user merge fields. What am I supposed to do? I can not simply replace that stuff with a contact.

Does anybody have a workaround for this?

Best
Roger
Dear community

I am building a public visualforce page via site and I face some difficulties from internal testing to testing on the site. First of all I noticed that references to my sObject field were simply removed from the interface. No worries, I replaced them with simple input fields. But what I can't figure out is how I can load my custom font, saved as static resource into the public site. It worked with internal views but not on the site. Furthermore, I want to display an image that is stored as attachment depending on the parameter passed in the URL.

CSS-snippet:
/*---- General CSS ----*/
@font-face {
	font-family: Gravur; src: url('/resource/Gravur_CondensedLight');
}

@font-face {
	font-family: Gravur; font-weight: bold; src: url('/resource/Gravur_CondensedBold');
}

/*---- Page CSS ----*/
html
{
	font-family: Gravur;
	font-size: 16px;
}

/* And more irrelevant down below */

My Controller-snippet:
public class PartyRSVPController
{
	public String cmid {get;set;}
	public Boolean rendered {get; set;}
	public Boolean renderedError {get; set;}
	public Boolean success {get; set;}
	public CampaignMember aMember {get; set;}
	public Integer numGuests {get; set;}
	public Campaign aCampaign {get; set;}
	public String campaignImagePath {get; set;}
	public StaticResource res;
	
	public PartyRSVPController(){
		aMember = new CampaignMember();
		aCampaign = new Campaign();
		rendered = true;
		success = false;
		res = new StaticResource();
	}
	
	public void init() {
		cmid = System.currentPageReference().getParameters().get('cmid');
		if ( cmid == null || cmid.equals('') ) {
			rendered = false;
		}
		
		try {
			aMember = queryCampaignMember(cmid);
			aCampaign = queryCampaign(aMember.CampaignId);
		} catch(system.QueryException qe) {
			rendered = false;
		}
		
		campaignImagePath = '/servlet/servlet.FileDownload?file=' + aCampaign.CampaignImageId__c;
		renderedError = !rendered;
	}
}

My page-snippet
<apex:page showHeader="false" sidebar="false" showChat="false" controller="PartyRSVPController"  standardStylesheets="false" docType="html-5.0" title="Party RSVP" action="{!init}">
<apex:pageMessages />
	<apex:stylesheet value="{!$Resource.PartyRSVP}" />
	<apex:form styleClass="form">
		<apex:pageBlock >
			<section id="page_header">
				<apex:image value="{!$Resource.ArchitonicLogo}" alt="Architonic Logo"/>
			</section>
		</apex:pageBlock>
		<apex:pageBlock rendered="{!rendered}">
			<section id="page_content">
				<aside>
					<!-- apex:image url="{!URLFOR($Action.Attachment.Download, aCampaign.CampaignImageId__c)}" alt="Campaign Image" styleClass="image" /-->
					<apex:image value="{!campaignImagePath}" alt="Campaign Image" styleClass="image" />
				</aside>
				<article>
					Dear {!Salutation}<br/>
					<br/>
					We are delighted to have you at our event <b>{!aCampaign.CampaignNamePublic__c}</b>! Please let us know whether you bring any guests and how many you bring:<br/>
					<div>
						<apex:input value="{!numGuests}" type="number" styleClass="inputField" label="Guests"/>
						<apex:commandButton id="SubmitButton" title="RSVP" value="RSVP" action="{!submit}"/>
					</div>
				</article>
			</section>
		</apex:pageBlock>
	</apex:form>
</apex:page>

Generally, the stylesheet loadsbecause it displays my tags correctly, but the font doesn't load. As you can see I use font face for using fonts for both bold and normal display. I do suspect that the simple "src:url( '/resource/Gravur_CondensedLight')" is the source of the problem. I do however not know how to deal with it.

This is what it looks like:
User-added image

And this is how it is supposed to look like:
User-added image

Note that now we see a different font. As stated before, the image is saved as attachment on a sObject and it is (so far) referenced with the "/servlet/servlet.FileDownload?file=" syntax by passing the image id.

While I know that I can save the image as static resource and then display it accordingly, it is not an option due to the workflow requiring the users to be able to define the image dynamically without having access to static resources.

Does anybody have experience with this? Any hints towards a solution? Thanks ahead.

Best
Roger
Dear community

I have a need of capturing input from clients in Salesforce via passing a parameter in the URL. I know that I can do this via the Force.com domain by publishing a site as I have done this before. Back then, our needs were specific and I didn't think globally enough to name the force.com domain something useful. So I can't do it this way because it is locked to the old domain name. I created a ticket with Support so they change it but I don't know how cooperative they will be and how fast they can do this.
In any way, there needs to be a different possibility. We have Partner Communities available and I know that in general one can serve content to the public over community pages, built with Community Builder. Generally I have a multitude of options here but none of them seem to work. I can put the Visualforce page directly in a page by making the visualforce page available for lightning. But then I can not gather the URL parameter because the Visualforce page is served as an iFrame within the lightning DOM. Another option is to serve the Visualforce page within a lightning component where I can pass the parameter. However, even though the Community page is public and the component is generally visible, the visualforce page inside the component requires login and thus doesn't display to the public.

I don't want to have anything super fancy. I just need to capture the input easily. What is the best way to do that? What steps would I need to undertake to convert my visualforce page to a native lightning thing?


Background info:
We are building our RSVP page and we will send out a mailing with a link to the (to be) public visualforce page in order to capture their participation. The participation is then saved on the campaign member. That's why we need a recipients' ID to pass to the visualforce page. Currently the link will look like
https://<domain>/apex/PartyRSVP?cmid=<hashed campaign member ID>
Dear community

I was writing the test class for one of my handlers when I stumbled across a weird error. I have two custom objects: Expense__c and ExpensePeriod__c. The Expense Period is a time in which you accumulate expenses and where sums are calculated. It can have 0-n Expense__c records where the actual amount is located. The main idea of the class is to roll up sums of a formula field, which is not possible without apex. First off the code pieces.

Method to test:
/**
*	Checks updated values whether their credit card fee has been changed
*/
@TestVisible private void extractRelevantUpdates() {
	for ( Expense__c expense : newExpenses ) {
		if ( expense.CHF_Credit_Card_Fee__c != oldExpenses.get(expense.Id).CHF_Credit_Card_Fee__c ) {
			expensePeriodMap.put(expense.ExpenseAccount__c, new ExpensePeriod__c(Id = expense.ExpenseAccount__c, Sum_Foreign_Currency_Amounts_CHF__c = 0.0));
		}
	}
}
This method merely checks, whether there has been a change in the field that is summed up. If it is, it prepares an expense period record for updating.

Here is the test method:
private static testMethod void testExtractRelevantUpdates() {
	ExpenseHandlerRollUp ehru = new ExpenseHandlerRollUp();
    // ArcUtil is a Helper class containing this method for easier to remember record type id retrieval by name
	Id creditCardCashRecordType = ArcUtil.getRecTypeMap(Schema.SObjectType.Expense__c).get('CHF Architonic Credit Card').getRecordTypeId();
	
	list<Expense__c> newExpenses = new list<Expense__c>{
		new Expense__c(Id = 'a05000000012345', ExpenseAccount__c = 'a04000000012345', RecordTypeId = creditCardCashRecordType, Foreign_Currency__c = true, AmountNumber__c = 500.00),
		new Expense__c(Id = 'a050000000abcde', ExpenseAccount__c = 'a040000000abcde', RecordTypeId = creditCardCashRecordType, Foreign_Currency__c = true, AmountNumber__c = 0.00),
		new Expense__c(Id = 'a050000000ABCDE', ExpenseAccount__c = 'a040000000ABCDE', RecordTypeId = creditCardCashRecordType, Foreign_Currency__c = true, AmountNumber__c = 500.00),
		new Expense__c(Id = 'a05000000000000', ExpenseAccount__c = 'a04000000000000', RecordTypeId = creditCardCashRecordType, Foreign_Currency__c = true, AmountNumber__c = 500.00)
	};
	map<Id, Expense__c> oldExpenses = new map<Id, Expense__c>{
		'a05000000012345' => new Expense__c(Id = 'a05000000012345', ExpenseAccount__c = 'a04000000012345', RecordTypeId = creditCardCashRecordType,
											Foreign_Currency__c = true, AmountNumber__c = 0.00),
		'a05000000012345' => new Expense__c(Id = 'a05000000012345', ExpenseAccount__c = 'a04000000012345', RecordTypeId = creditCardCashRecordType,
											Foreign_Currency__c = true, AmountNumber__c = 500.00),
		'a050000000ABCDE' => new Expense__c(Id = 'a050000000ABCDE', ExpenseAccount__c = 'a040000000ABCDE', RecordTypeId = creditCardCashRecordType,
											Foreign_Currency__c = true, AmountNumber__c = 250.00),
		'a05000000000000' => new Expense__c(Id = 'a05000000000000', ExpenseAccount__c = 'a04000000000000', RecordTypeId = creditCardCashRecordType,
											Foreign_Currency__c = true, AmountNumber__c = 500.00)
	};

	for ( Expense__c exp : newExpenses ) {
		exp.recalculateFormulas();
	}
	
	for ( Expense__c exp : oldExpenses.values() ) {
		exp.recalculateFormulas();
	}

system.assert(oldExpenses.get('a05000000000000').CHF_Credit_Card_Fee__c != null && oldExpenses.get('a05000000000000').CHF_Credit_Card_Fee__c != 0.0, 'recalc formulas failed');
	
	ehru.setValues(newExpenses, oldExpenses);
	ehru.extractRelevantUpdates();
	system.assertEquals(3, ehru.expensePeriodMap.size(), 'Not exactly 3 expense periods created');
}
The error appears on the first recalculateFormulas() call. I did a search and it says that the method does not recalculate cross object formulas. Here is the full error message:
System.NoDataFoundException: Data Not Available: The data you were trying to access could not be found. It may be due to another user deleting the data or a system error.
Class.ExpenseHandlerRollUpTest.testExtractRelevantUpdates: line 50, column 1

The formula in question is as follows:
IF(
  AND(
    RecordType.Id = "012D0000000KFlx", /* CHF Credit Card */
    Foreign_Currency__c
  ),
  ROUND(2 * ROUND($Setup.ArchitonicAG__c.CSBankCharge__c * AmountNumber__c, 2), 1)/2,
  NULL
)
The problem might effectively be, that referencing a custom setting value might cause this error. But it doesn't make any sense to me from the naming of the error. I also tried inserting a custom setting record prior to recalculating formulas, but that didn't help either. (If you wonder what the round calculations are: it is to round to our currency system, where the smallest possible value is 0.05 as opposed to 0.01 like cents in most currencies)

Do you have ideas on how to solve it without inserting the test data? I want to write efficient code and inserting objects even in test classes fires all possible triggers of objects involved. And for something they created the recalculateFormulas() method.

Thanks ahead
Dear community

I have a case that I create a callout to an external service that will result in an attachment uploaded to Salesforce. Sometimes, I get a 504 Timeout on the HTTP request but still the attachment is created. So far, I am sending myself an email so I can check whenever an error has occured and if necessary, create the attachment manually. Now I want to implement a feature, that checks on a 504 code whether the attachment has been created and if not, try the call again. The latter poses a problem as @future tagged methods do not allow to call another @future method. So I need a way to kind of tell salesforce to repeatedly try to execute this http request as long as the attachment is not present and max tries has not been exceeded. Optimally, it would also increase the time between tries.

This is what I have:
global class FutureCallout
{
	@TestVisible private static final Integer MAX_TRIES = 8;
	
	@future(callout=true)
	public static void generateCongaDocument(Id opportunityId, Id attachParentId, String sessionId, Integer tries) {
		
		Http http = new Http();
		HttpRequest request = new HttpRequest();
		request.setEndpoint(CongaDocumentCreator.getCongaUrl(opportunityId, true, true, true, sessionId, attachParentId));
		request.setMethod('GET');
		request.setTimeout(120000);
		HttpResponse response;
		if ( !Test.isRunningTest() ) {
			response = http.send(request);
		} else {
			response = new HttpResponse();
			response.setStatusCode(200);
		}
		// Handle erraneous responses
		if (response.getStatusCode() == 504) {
			if ( tries <= MAX_TRIES && !InstallmentCreator.isAttachmentPresent(attachParentId) ) {
				FutureCallout.generateCongaDocument(opportunityId, attachParentId, sessionId, ++tries);
			}
		} else if ( response.getStatusCode() != 200 ) {
			ArcUtil.sendAdminNotification('Document Creation with Barcode for Installment ' + attachParentId + ' failed with HTTP Response Code ' + response.getStatusCode() +
			'. Use the developer console to try creation again. Additional Info:\n\n---\n\nStatus:\n' + response.getStatus() + '\nBody:\n' + response.getBody() +
			'\n\nRequest:\n' + request.getEndpoint());
		}
	}
}
Is there any way to do so? Can I schedule a job somehow and pass the job information to be processed?
Dear community

We are looking for a solution to prevent standard Lead-Account-mapping during conversion. As is, when converting and merging a Lead into an existing Account, an empty Account billing address would be set by the Lead address. While this behaviour is not so bad for completely new Accounts, in a merge it is devestating for us.

It often happens, that active clients still apply on our website, e.g. for an additional product or so. Those clients already have an Account. Usually, we only use the shipping address and only fill in the billing address if it happens to be different. That means, in most cases the billing address is empty. When a Lead is now converted and merged to such an Account and the Lead address differs from the Account's shipping address, we might not realise it until we already sent out the invoice to the wrong place. That is why we need to prevent that standard mapping.

I have looked into solutions with Apex but I am still clueless. According to this entry on stackexchange, at least the custom field mapping is executed already before the before triggers. I guess that standard field mappings also apply then. That leaves me literally no option to prevent the change. Because in the before trigger I would not even see that the billing address is being updated.

Does anybody have an input for this?

Hi community

I am writing a test class for my handler, which checks if the user is allowed to perform CRUD on some child objects if the parent object is in a specific state. I am struggling with one test method only, the one which adds an error to the sObject. Here is my code:

// Maps from OpportunityId to different sObjects like Attachment, OpportunityLineItem, etc.
@TestVisible private map<Id, sObject> parentIdChildMap;
// Opportunity Ids for which no CRUD on child objects are allowed
@TestVisible private set<Id> lockedOpportunitiesIds;
...
...
/*
*	Adds an error to all sObjects which are attached to a yes Opportunity
*/
@TestVisible private void invalidateSObjects() {
	for ( Id opportunityId : lockOpportunitiesIds ) {
		system.debug('Adding Error for object ' +parentIdChildMap.get(opportunityId));
		parentIdChildMap.get(opportunityId).addError('This '
        + parentIdChildMap.get(opportunityId).getSObjectType().getDescribe().getLabel()
		+ '\'s Opportunity is already in a stage where you can no longer add, edit or remove it.');
		system.debug('Error added');
	}
}
...
And my test method:
private static testMethod void testInvalidateSObjects() {
    ArcUtil.setSkipForTest(true);
	Account parent = new Account(Name = 'Test Parent', Type = 'Manufacturer');
	insert parent;
	
	OpportunityStageProtection osp = new OpportunityStageProtection();
	osp.setLockOpportunitiesIds(new set<Id>{'006000000012345'});
	osp.setParentChildMap(new map<Id, sObject>{'006000000012345' => new Attachment(ParentId = parent.Id, Name = 'testSObject', Body = Blob.valueOf('Test Body'))});
	osp.invalidateSObjects();
	insert osp.parentIdChildMap.values();
    ArcUtil.setSkipForTest(false);
}
I expected my code to throw an error upon insertion but it does not. As you might have guessed, the fix typed ID "006000000012345" is a fake Opportunity ID. But in the end it should not matter. The test itself should not cause my trigger and handler to run (I actually forbid that with ArcUtil.setSkipForTest(true)), it should merely recognize that I already added an error to the Attachment record and prevent insertion. My debug log clearly states that the lines for adding the error have run through, so the addError line clearly was executed.

Do you have any insights on that? I want to test my methods locally possible without the use of too many DML that cause triggers to fire on every single object I need to insert & update for tests... Do you know of another way to check if an error has been added? Like sObject.getErrors() or something? There's no such thing on the sObject description though :/
Hi Community

I am new to using Database.update() method and I thought that using the Database class would not throw any kind of Exceptions. I use this as my source information:
https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/langCon_apex_dml_database.htm
@TestVisible private void updateExistingOpportunities() {
	list<Database.SaveResult> results = Database.update(existingOpportunityMap.values());
	String errorMessage = '';
	for ( Integer i = 0; i < results.size(); i++ ) {
		if ( !results.get(i).isSuccess() ) {
			if ( errorMessage.equals('') ) {
				errorMessage = 'Following Opportunities have failed during the update:\n\n';
			}
			errorMessage += existingOpportunityMap.values().get(i).Name;
			errorMessage += '\n';
			errorMessage += 'https://eu4.salesforce.com/';
			errorMessage += existingOpportunityMap.values().get(i).Id;
			errorMessage += 'Errors:\n';
			for ( Database.Error theError : results.get(i).getErrors() ) {
				errorMessage += theError.getStatusCode();
				errorMessage += '\n';
				errorMessage += theError.getMessage();
			}
		}
	}
}
The above is my method I like to test. I want to force running through the result loop with an error so i get maximum code coverage and see that the output is as I desired it. I tried it with an inserted Opportunity of which I tried putting the CloseDate on null. But this throws a DML Exception. I thought I would not get any exceptions, but be able to read out the Database.result...

Any hints on that?
Dear community

We have a scheduled daily job, that clones certain Opportunities. But of course, we also need the OpportunityLineItems (OLI) cloned. In addition, we need no simple clone of the OLI, but we need them to use the same price, same quantity, same currency, same product, but of the "next pricebook". now we have a custom field on Pricebook, that holds a reference to the "next pricebook". So getting that Id in my apex class is fine. I also know how to query the new PricebookEntry records for the cloned OLIs, but I don't know if there is a more efficient way.

User-added image

In the end all I need to query is the PricebookEntryId of the greyed out one. Currently, I use a query that just returns every PricebookEntry of every Currency of all used Products and Pricebooks within the Trigger Context. For large execution context, this query can easily bea really slow and heavy. I wanted to know if there is a more optimal approach without needing to use more loops over collections or more individual queries.

Maybe someone can help me (or say it's not possible).
Hi Community

I wonder what you use as best practice for test classes. I have test methods which run really slowly because they depend on many objects which have to be in place before I start testing. Usually, I create test records within a test class and insert them which also causes all triggers on those object to run. So I wonder if there is a best practice of not using any DML in test classes & methods but instead rely on manually created records withing the test classes. I'd certainly need to simulate an ID creation as most of my code uses map<Id, sObject> containers. Also, how could I handle system and formula fields?

I just wonder if there is anyone with a similar problem who found a solution for optimising test run time.
Hi Community

I'd like to structure my code better into segments that logically work together. I know that in native Java you'd just create different packages and work with those. However, with the Force.com IDE plugin in eclipse this does not seem to be possible. So I also looked into Packages Salesforce offer natively, but those are kind of different.

So my questions for you guys:
  • Do you know if it is even possible to set up package structures like you would in Java?
  • Are Salesforce Packages useful for use as logical collections of code?
  • Has anyone already experience with this kind of problem?
Thanks ;)
Hi Community

I am sure I have had this one before, but no clue on how I solved it if I didn't try a different approach. So the problem is that the String.split() function returns an empty list<String> resp. an empty String[].

This is the code part I'm speaking about:
// oli is a OpportunityLineItem
// ProductFamily__c is a formula and contains:
// TEXT({!PricebookEntry.Product2.Family}) + " - " + TEXT({!PricebookEntry.Product2.SubType__c})
// where SubType__c is a dependent picklist of Family
// COST_UNIT_MAP is a map<String, String> that is already initialised and filled with lots of values

oli.ProductFamily__c != NULL && COST_UNIT_MAP.containsKey(oli.ProductFamily__c.split(' - ').get(0))

This is called in an if statement surrounded by a try block which catches a NullPointerException (complete code of the function below). But the error thrown is a "ListIndexOutOfBounds: 0". I have never seen a split function return an empty list before. Because that's what must happen, otherwise I would not get that error. If I were to call split() on null, it'd throw a null pointer exception. If I use it on any string (even an empty one) i would get that string at least as the first entry in the list. An empty String is also really unprobably because Salesforce saves everything that does not contain a value as null.

Anyone got any idea on what's going on here?

Thanks in advance
Roger


The whole function:
    private void assignCostUnit() {
		try {
			String errorMessage;
			for ( OpportunityLineItem oli : olis ) {
				if ( oli.ProductFamily__c != NULL && COST_UNIT_MAP.containsKey(oli.ProductFamily__c) ) {
					oli.CostUnit__c = COST_UNIT_MAP.get(oli.ProductFamily__c);
				}
				else if ( oli.ProductFamily__c != NULL && COST_UNIT_MAP.containsKey(oli.ProductFamily__c.split(' - ').get(0)) ) {
					oli.CostUnit__c = COST_UNIT_MAP.get(oli.ProductFamily__c.split(' - ').get(0));
				} else {
					errorMessage = (errorMessage != NULL && !errorMessage.equals('') ? (errorMessage + '\nprodFam: ' + oli.ProductFamily__c + ', costUnit: ' + oli.CostUnit__c) : 
									('prodFam: ' + oli.ProductFamily__c + ', costUnit: ' + oli.CostUnit__c));
					system.debug(LoggingLevel.INFO, errorMessage);
				}
			}
			if ( errorMessage != NULL && !errorMessage.equals('') ) {
				ArcUtil.sendAdminNotification(errorMessage);
			}
		} catch(system.NullPointerException npe) {
			String errorMessage = 'Could not assign cost unit. The splitting of the Product Family on Opportunity Line Item with \" - \" failed. Check if the Product Family is ' +
									'not empty\n' + npe.getCause() + '\n' + npe.getStackTraceString();
			system.debug(LoggingLevel.ERROR, errorMessage);
			ArcUtil.sendAdminNotification(errorMessage);
		}
	}

 
Dear community

I am in the need to bundle attachments which are saved to a custom object throughout the week and on weekends make them available for download as an archive file. I intended to create a zip containing the documents but found out Salesforce does not allow creation of zips. So instead I have to look for something that will somehow group those files e.g. in a documents folder (even if I then have duplicate files in SF) and make them available for bunch download. The zipping can still be done after downloading in the end by the user but I do not want the user to have to open every file and download it separately. Any ideas?

Best
Roger
Dear community

I changed a bit of my visualforce controller to make it a bit more logically structured and to fix an undesired behaviour. Doing this kind of broke the whole thing, because now I'm getting an internal server error message...

Here is my VF page:
<apex:page standardController="Opportunity" id="createInstallments" showHeader="false" sidebar="false" extensions="InstallmentCreator" action="{!autoRun}" docType="html-5.0" cache="false">
<apex:pageMessages />
    <apex:form id="inputForm">
        <apex:pageBlock title="Installment Creation" id="pageBlock">
            <apex:pageBlockButtons id="pageBlockButtons">
                <apex:commandButton id="submit" action="{!submit}" value="Submit" title="Submit" disabled="!{!isSubmitAvailable}" />
                <apex:commandButton id="cancel" action="{!cancel}" value="Cancel" title="Cancel" />
            </apex:pageBlockButtons>
            <apex:pageBlockSection columns="1" id="sectionOne">
                 The Amount to be split into rates is {!Opportunity.CurrencyIsoCode} {!Opportunity.Amount_Total__c}.<br/><br/>
                <apex:input type="number" id="numInstallments" label="Number of installments" value="{!numInstallments}" required="true"/>
                <apex:input type="number" id="avgAmount" label="Average installment amount" value="{!avgAmount}" required="true"/>
                <apex:input type="date" id="firstDueDate" label="First due date" value="{!firstDueDate}" required="true"/>
            </apex:PageBlockSection>
            <apex:PageBlockSection id="sectionTwo" columns="2">
                <apex:input type="number" id="interval" label="Interval" value="{!interval}"/>
                <apex:selectList value="{!intervalType}" size="1" label="Interval Type">
                    <apex:selectOption itemValue="Day" itemLabel="Day(s)"/>
                    <apex:selectOption itemValue="Month" itemLabel="Month(s)"/>
                </apex:selectList>
            </apex:pageBlockSection>
        </apex:pageBlock>
    </apex:form>
</apex:page>
It is meant to fetch Opportunity Data upon opening and display it right away (action={!autoRun}). Additionally, if data is in a certain form already, it should automatically process the creation of an opportunity's child object instead of showing the page. The page is accessed by a button click on Opportunity.

Here is my controller extension:
/**
*	@description
*	This class serves as a controller for the corresponding Visualforce Page "Create Installments". The class is intended to be called by a button click
*	on the UI. It is used to create Installment records out of the provided opportunity.
*	
*	@author	wicki
*/
public class InstallmentCreator
{
	public Opportunity opp {get; set;}
	public Date firstDueDate {get; set;}
	public Integer interval {get; set;}
	public String intervalType {get; set;}
	public Integer numInstallments {get; set;}
	public Decimal avgAmount {get; set;}
	public Boolean isSubmitAvailable {get; set;}
	private list<Installment__c> installments;
	private Boolean showError = false;
	private String opportunityId;
		
	/**
	*   In case this Controller is invoked, there is a constructor defined
	*/
	public InstallmentCreator(ApexPages.StandardController controller) {
		opp = (Opportunity)controller.getRecord();
	}
	
	/**
	*	@description	Called by the visualforce page
	*	@return			A page reference to either the page itself, displaying error messages or the calling page
	*/
	public PageReference autoRun() {
		opportunityId = (String)ApexPages.currentPage().getParameters().get('id');
		system.debug(LoggingLevel.ERROR, 'Opportunity Id: ' + opportunityId);
		if ( opportunityId == NULL || opportunityId.equals('') ) {
			return ApexPages.currentPage();
		}
		opp = [ SELECT Id, Payment_Information__c, PayableUntil__c, StageName, InvoiceDate__c, Amount_Total__c, CurrencyIsoCode
     			 FROM Opportunity WHERE Id = :opportunityId LIMIT 1 ];
//		validateAutoRun();
		if ( showError ) {
			return ApexPages.currentPage();
		}
		
// Comment out on using rates
		numInstallments = 1;
		avgAmount = opp.Amount_Total__c;
		intervalType = 'Day';
		interval = 0;
		firstDueDate = (opp.Payment_Information__c.equals('Payable due in advance') || (opp.Payment_Information__c.equals('Rates') && opp.PayableUntil__c == NULL)) ?
			system.today() : opp.PayableUntil__c;
		
		return submit();
	}

	/**
	*	@description	Validates input data and creates Installments if data is valid
	*	@return			A Page Reference to the page itself if the data is invalid or to the calling page if everything went right
	*/
	public PageReference submit() {
		validateSubmit();
		if ( showError ) {
			return ApexPages.currentPage();
		} else {
			createInstallments();
		}
		
		if ( showError ) {
			return ApexPages.currentPage();
		} else {
			return cancel();
		}
	}
	
	public PageReference cancel() {
		PageReference pageRef = new PageReference('/' + opp.Id);
		pageRef.setRedirect(true);
		return pageRef;
	}
	
	private void validateAutoRun() {
		String errorMessage = 'The opportunity does not fulfill all requirements to create Installments!';
		isSubmitAvailable = true;
		if ( !opp.StageName.contains('yes') ) {
			isSubmitAvailable = false;
			errorMessage += '\nStage must be yes!';
		}
		if ( opp.InvoiceDate__c == null ) {
			isSubmitAvailable = false;
			errorMessage += '\nThere has to be an invoice date!';
		}
		if ( opp.Payment_Information__c.equals('Date') && opp.PayableUntil__c == NULL ) {
			isSubmitAvailable = false;
			errorMessage += '\nIf the payment is for a specific date, the date has to be provided!';
		}
		if ( !isSubmitAvailable ) {
			showError = true;
			ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, errorMessage));
		}
	}
	
	private void validateSubmit() {
		if ( opp.Amount_Total__c > numInstallments * avgAmount || numInstallments < 1 || avgAmount < 0 || avgAmount > opp.Amount_Total__c || firstDueDate == NULL ) {
			isSubmitAvailable = false;
			showError = true;
			ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,
				'The number of Installments and the average amount will not produce anything useful. Please use other values!'));
		}
	}
	
	private void createInstallments() {
		installments = new list<Installment__c>();
		Integer installmentNum = 1;
		Decimal oppAmount = opp.Amount_Total__c;
		
		while ( (oppAmount > 0.0 || opp.Amount_Total__c == 0.0) && installmentNum <= numInstallments ) {
			Installment__c installment = new Installment__c();
			installment.Name = '' + installmentNum;
			installment.OpportunityId__c = opp.Id;
			installment.CurrencyIsoCode = opp.CurrencyIsoCode;
			if ( intervalType.equals('Day') ) {
				installment.DueOn__c = firstDueDate.addDays((installmentNum - 1) * interval);
			} else if ( intervalType.equals('Month') ) {
				installment.DueOn__c = firstDueDate.addMonths((installmentNum - 1) * interval);
			} else {
				isSubmitAvailable = false;
				showError = true;
				ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'There was an error with the Interval Type selection.'));
			}
			installment.Amount__c = Math.min(avgAmount, oppAmount);
			oppAmount -= avgAmount;
			installmentNum++;
			installments.add(installment);
		}
		try {
			insert installments;
		} catch(system.DMLException dml) {
			isSubmitAvailable = false;
			showError = true;
			ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Could not insert Installments.\n' + dml));
			system.debug(LoggingLevel.ERROR, 'Could not insert Installments.\n' + dml);
		}
	}
}
The thing is, I want the user to be shown my VF page posted above if the data which is already present is not sufficient. Also, if the page is shown and the page is submitted (future feature) the page should still stay open and show the relevant error messages.


Here is the currently working controller extension:
/**
*  @description
*  This class serves as a controller for the corresponding Visualforce Page "Create Installments". The class is intended to be called by a button click
*  on the UI. It is used to create Installment records out of the provided opportunity.
*  
*  @author  wicki
*/
public class InstallmentCreator
{
  public Opportunity opp {get; set;}
  public Date firstDueDate {get; set;}
  public Integer interval {get; set;}
  public String intervalType {get; set;}
  public Integer numInstallments {get; set;}
  public Decimal avgAmount {get; set;}
  public Boolean isSubmitAvailable {get; set;}
  private list<Installment__c> installments;
  private Boolean isConstructorCalled = false;
  
  /**
  *   In case this Controller is invoked, there is a constructor defined
  */
  public InstallmentCreator(ApexPages.StandardController controller) {
    this.opp = (Opportunity)controller.getRecord();
    isConstructorCalled = true;
  }
  
  /**
  *   Called by the visualforce page 
  */
  public PageReference autoRun() {
    String opportunityId = ApexPages.currentPage().getParameters().get('id');
 
    if (opportunityId == null && !isConstructorCalled) {
      // Display the Visualforce page's content if no Id is passed over
      return ApexPages.CurrentPage();
    } else if ( isConstructorCalled ) {
      opportunityId = opp.Id;
    }
    opp = [ SELECT Id, Payment_Information__c, PayableUntil__c, StageName, InvoiceDate__c, Amount_Total__c, CurrencyIsoCode
      FROM Opportunity WHERE Id = :opportunityId LIMIT 1 ];
    validateAutoRun();
    
// Comment out on using rates
    numInstallments = 1;
    avgAmount = opp.Amount_Total__c;
    intervalType = 'Day';
    interval = 0;
    firstDueDate = (opp.Payment_Information__c.equals('Payable due in advance') || (opp.Payment_Information__c.equals('Rates') && opp.PayableUntil__c == NULL)) ?
      system.today() : opp.PayableUntil__c;
    return submit();
    
// Uncomment on using rates
/*    if ( !opp.Payment_Information__c.equals('Rates') ) {
      numInstallments = 1;
      avgAmount = opp.Amount_Total__c;
      intervalType = 'Day';
      interval = 0;
      firstDueDate = opp.Payment_Information__c.equals('Payable due in advance') ? system.today() : opp.PayableUntil__c;
      return submit();
    }
*/  }
  
  public PageReference submit() {
    validateSubmit();
    createInstallments();
    
    // Redirect the user back to the original page
    PageReference pageRef = new PageReference('/' + opp.Id);
    pageRef.setRedirect(true);
    return pageRef;
  }
  
  public PageReference cancel() {
    PageReference pageRef = new PageReference('/' + opp.Id);
    pageRef.setRedirect(true);
    return pageRef;
  }
  
  private void validateAutoRun() {
    String errorMessage = 'The opportunity does not fulfill all requirements to create Installments!';
    isSubmitAvailable = true;
    if ( !opp.StageName.contains('yes') ) {
      isSubmitAvailable = false;
      errorMessage += '\nStage must be yes!';
    }
    if ( opp.InvoiceDate__c == null ) {
      isSubmitAvailable = false;
      errorMessage += '\nThere has to be an invoice date!';
    }
    if ( opp.Payment_Information__c.equals('Date') && opp.PayableUntil__c == NULL ) {
      isSubmitAvailable = false;
      errorMessage += 'If the payment is for a specific date, the date has to be provided!';
    }
    if ( !isSubmitAvailable ) {
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, errorMessage));
    }
  }
  
  private void validateSubmit() {
    if ( opp.Amount_Total__c > numInstallments * avgAmount || numInstallments < 1 || avgAmount < 0 || avgAmount > opp.Amount_Total__c ) {
      isSubmitAvailable = false;
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR,
        'The number of Installments and the average amount will not produce anything useful. Please use other values!'));
    }
  }
  
  private void createInstallments() {
    installments = new list<Installment__c>();
    Integer installmentNum = 1;
    Decimal oppAmount = opp.Amount_Total__c;
    
    while ( (oppAmount > 0.0 || opp.Amount_Total__c == 0.0) && installmentNum <= numInstallments ) {
      Installment__c installment = new Installment__c();
      installment.Name = '' + installmentNum;
      installment.OpportunityId__c = opp.Id;
      installment.CurrencyIsoCode = opp.CurrencyIsoCode;
      if ( intervalType.equals('Day') ) {
        installment.DueOn__c = firstDueDate.addDays((installmentNum - 1) * interval);
      } else if ( intervalType.equals('Month') ) {
        installment.DueOn__c = firstDueDate.addMonths((installmentNum - 1) * interval);
      } else {
        isSubmitAvailable = false;
        ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'There was an error with the Interval Type selection.'));
      }
      installment.Amount__c = Math.min(avgAmount, oppAmount);
      oppAmount -= avgAmount;
      installmentNum++;
      installments.add(installment);
    }
    try {
      insert installments;
    } catch(system.DMLException dml) {
      isSubmitAvailable = false;
      ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Could not insert Installments.\n' + dml));
      system.debug(LoggingLevel.ERROR, 'Could not insert Installments.\n' + dml);
    }
  }
}

The error I'm getting is:
An internal server error has occurred
An error has occurred while processing your request. The salesforce.com support team has been notified of the problem. If you believe you have additional information that may be of help in reproducing or correcting the error, please contact Salesforce Support. Please indicate the URL of the page you were requesting, any error id shown on this page as well as any other related information. We apologize for the inconvenience. 

Thank you again for your patience and assistance. And thanks for using salesforce.com! 

Error ID: 499064766-251065 (-1373969722)


I have no clue how I can get rid of this error. I tried uncommenting single lines to see where the error might be. I also monitored my user and got the following debug log:
 

35.0 APEX_CODE,DEBUG;APEX_PROFILING,INFO;CALLOUT,NONE;DB,INFO;SYSTEM,INFO;VALIDATION,INFO;VISUALFORCE,NONE;WORKFLOW,ERROR
14:03:19.124 (124061938)|EXECUTION_STARTED
14:03:19.124 (124085918)|CODE_UNIT_STARTED|[EXTERNAL]|066250000000OcX|VF: /apex/CreateInstallments
14:03:19.127 (127236448)|CODE_UNIT_STARTED|[EXTERNAL]|01p250000009e82|InstallmentCreator <init>
14:03:19.127 (127256520)|SYSTEM_MODE_ENTER|true
14:03:19.155 (155103094)|CODE_UNIT_FINISHED|InstallmentCreator <init>
14:03:19.155 (155234067)|CODE_UNIT_STARTED|[EXTERNAL]|01p250000009e82|InstallmentCreator invoke(autoRun)
14:03:19.155 (155622692)|USER_DEBUG|[34]|ERROR|Opportunity Id: 00625000004COln
14:03:19.155 (155987042)|SOQL_EXECUTE_BEGIN|[38]|Aggregations:0|SELECT Id, Payment_Information__c, PayableUntil__c, StageName, InvoiceDate__c, Amount_Total__c, CurrencyIsoCode FROM Opportunity WHERE Id = :tmpVar1 LIMIT 1
14:03:19.162 (162498577)|SOQL_EXECUTE_END|[38]|Rows:1
14:03:19.163 (163507077)|VF_PAGE_MESSAGE|The number of Installments and the average amount will not produce anything useful. Please use other values!
14:03:19.163 (163584357)|CODE_UNIT_FINISHED|InstallmentCreator invoke(autoRun)
14:03:19.164 (164312516)|VF_APEX_CALL|createInstallments|{!autoRun}|PageReference:/apex/CreateInstallments?id=00625000004COln&scontrolCaching=1
14:03:19.165 (165970977)|CUMULATIVE_LIMIT_USAGE
14:03:19.165 (165970977)|LIMIT_USAGE_FOR_NS|(default)|
  Number of SOQL queries: 1 out of 100
  Number of query rows: 1 out of 50000
  Number of SOSL queries: 0 out of 20
  Number of DML statements: 0 out of 150
  Number of DML rows: 0 out of 10000
  Maximum CPU time: 0 out of 10000
  Maximum heap size: 0 out of 6000000
  Number of callouts: 0 out of 100
  Number of Email Invocations: 0 out of 10
  Number of future calls: 0 out of 50
  Number of queueable jobs added to the queue: 0 out of 50
  Number of Mobile Apex push calls: 0 out of 10

14:03:19.165 (165970977)|CUMULATIVE_LIMIT_USAGE_END

14:03:19.166 (166018172)|CODE_UNIT_FINISHED|VF: /apex/CreateInstallments
14:03:19.167 (167136200)|EXECUTION_FINISHED
Dear Community

I have following setup:
@isTest
private class myTestClass {
	/** static helper variables to store data across different test classes to
	/*  accelerate test speed
	*/
	Boolean isInitiated = false;
	//  ...

	@isTest(SeeAllData=true)
	private static void init() {
		// create helper variable's values
		// ...
	}

	static testMethod void testStuff() {
		if ( !isInitiated ) { init(); }
		
		// test all the stuff to be tested
	}
}
Within the init method I use nother helper class I worte independatly to create different sObjects. One of them relies on a custom setting. Now I know that SeeAllData has to be set to true so that the method can access that data, but I do not understand why it doesn't work for the case above. In the documentations, it is not said that the isTest Annotation with the SeeAllData addition may only be used for the actual testMethods... I guess this must be where it fails because otherwise it seems to work correctly...

Any clarification on that?

Hi Community

So I've run into a strange behaviour for a static counter variable in one of my classes. I declare the variable in the beginning of the class:

public class NZZOutputFileGenerator
{
	// ...
	private static Integer lineCounter;
	//...
Then, in a static constructor I assign it its value (I have already tried directly assigning the value upon declaration, nothing changed)
    // ...
    static {
		lineCounter = 1;
		//...
	}
A public method allows the scheduled class to execute the classes function via the following function:
    public void handle() {
		prepareData();
		createFile();
		sendFile();
	}
In the createFile method, I loop over a collection of Data retrieved by the previous method and perform 3 further actions (calling methods, in which I increase the lineCounter)
    private void createFile() {
		for ( Installment__c installment : installmentMap.values() ) {
			fileContent += fileContent.equals('') ? getFileHead(installment.OpportunityId__r.AccountId, installment.OpportunityId__c) :
				fileContent + getFileHead(installment.OpportunityId__r.AccountId, installment.OpportunityId__c);
			fileContent += getFileHeader(installment.OpportunityId__r.AccountId, installment.OpportunityId__c, installment.Id);
			fileContent += getFilePositions(installmentToInstallmentPositionMap.get(installment.Id));
		}
	}
Every of those function contains the following part:
lineItems.add(String.valueOf(lineCounter++));
These are the only occurances of lineCounter in my class.

Now what I wanted is that for every Installment__c in the looped over collection the lineCounter continually increases. However, this is not the case. For every Installment__c in the createFile() method, the lineCounter resets to 1. I have no clue why it does that... a static variable should not change during a session unless explicitly ordered to do so...

This happened to me during testing my code in my sandbox using the ExecuteAnonymous function in the SF Dev Console. Maybe this has an influence...

I appreciate any insights.
Dear Community

I have been using a Class which handles monthly Apex jobs for quite some time but recently, I added in a new Job to execute within the Schedulable class. Since then, I can no longer schedule that class. The error message says that the class needs to have the Schedulable Interface.

User-added image
Of course, the class still has the Schedulable Interface (otherwise it wouldn't even show up in the lookup window above):
global class ApexMonthlyJobs implements Schedulable
{
  /**
    Execute is called when the scheduled time for this class arrives.
    All Classes which have to be executed daily can be initialized and run here
   */
  global void execute(SchedulableContext sc)
  {
    System.debug(LoggingLevel.ERROR, 'ApexMonthlyJobs started');
    // Only execute at the start of every year
    if ( system.today().month() == 1 || Test.isRunningTest() )
    {
      PersonelReminder pr = new PersonelReminder(PersonelReminder.ReminderType.JUBILEE_DATE);
      pr.remind();
      
      PricebookCloner pc = new PricebookCloner();
      pc.clonePB();
    }
  }
}
Is there anything I can do to fix that?

Thanks in advance
Dear Community

I try to implement some fancy stuff for my form but I ssem to oversee something. The first and kind of important part is to rerender a component on the form after certain checkbox is checked.
It should represent the functionality that for one of the choices, we need more input.
Part of my page:
<apex:pageBlockSection collapsible="false" title="3. How is marketing budget used?" id="thirdQuestion">
	<apex:outputPanel id="thirdQuestionOne">
		<apex:selectCheckboxes value="{!selectedBudget}" layout="pageDirection">
			<apex:selectOptions value="{!budget}"/>
			<apex:actionsupport event="onclick" action="{!CheckRendered}" rerender="thirdQuestionTwo" status="status"/>
		</apex:selectCheckboxes>
	</apex:outputPanel><br/>
	<apex:actionStatus id="status" startText="Please wait..." stopText=""/>
	<apex:outputPanel id="thirdQuestionTwo" rendered="{!isSubSelectRendered}">
		<apex:selectCheckboxes value="{!selectedPlatforms}" layout="pageDirection">
			<apex:selectOptions value="{!platforms}"/>
		</apex:selectCheckboxes>
	</apex:outputPanel>
</apex:pageBlockSection>
Important code pieces of my controller:
// Data Variables
public list<String> selectedBudget {get; set;}

// Control Variables
public Boolean isSubSelectRendered {get; set;}

public CancellationReportController()
{
	isSubSelectRendered = false;
	selectedBudget = new list<String>();
}

public list<SelectOption> getBudget()
{
	list<SelectOption> options = new list<SelectOption>();
	options.add(new SelectOption('PRINT','Print'));
	options.add(new SelectOption('FAIR','Fairs'));
	options.add(new SelectOption('ONLINE_SHOP','Investment in own online shop'));
	options.add(new SelectOption('WEBSITE','Investment in own website'));
	options.add(new SelectOption('NOTHING','No activities planned'));
	options.add(new SelectOption('OTHER_PRESENCE','Presence on other platforms (please specify)'));
	return options;
}

public void checkRendered()
{
	if ( new set<String>(selectedBudget).contains('OTHER_PRESENCE') )
	{
		isSubSelectRendered = true;
	}
}
When i simply render the second output panel neglecting the controller boolean value, I see that panel on the page and can use it, so it is set up correctly. What I do not know is where I have to put the action support tag or how I can make it do more than simply displaying the status and actually rerender the second output panel.


The second issue: Reloading the parent window on form close/submit.
I have a save button, which calls the save function in the controller and then closes the form window on completed calculations.
<apex:pageBlockButtons>
	<apex:commandButton action="{!save}" value="Save" id="SaveButton" oncomplete="window.top.close();"/>
</apex:pageBlockButtons>
The form was initially called by a salesforce Formula field using the HYPERLINK() function as below:
HYPERLINK('/apex/CancellationReport?accId=' + Account.Id + '&oppId=' + Id, 'Fill out the Form', '_blank]')

Can anybody tell me how I can achieve the form to reload my initial Salesforce page? The link to open the form can not be a button on the layout; it should be right next to a field.
User-added image

Many thanks ahead ;)
Roger
Dear community

We are looking for a solution to prevent standard Lead-Account-mapping during conversion. As is, when converting and merging a Lead into an existing Account, an empty Account billing address would be set by the Lead address. While this behaviour is not so bad for completely new Accounts, in a merge it is devestating for us.

It often happens, that active clients still apply on our website, e.g. for an additional product or so. Those clients already have an Account. Usually, we only use the shipping address and only fill in the billing address if it happens to be different. That means, in most cases the billing address is empty. When a Lead is now converted and merged to such an Account and the Lead address differs from the Account's shipping address, we might not realise it until we already sent out the invoice to the wrong place. That is why we need to prevent that standard mapping.

I have looked into solutions with Apex but I am still clueless. According to this entry on stackexchange, at least the custom field mapping is executed already before the before triggers. I guess that standard field mappings also apply then. That leaves me literally no option to prevent the change. Because in the before trigger I would not even see that the billing address is being updated.

Does anybody have an input for this?
I've got a proble with my Eclipse (Kepler) IDE. My sandbox where I'm working on was upgraded to summer '14 recently, sort of as a preview instance. So because the server location also changed from cs8 to cs7 I created a new force.com project and downloaded Classes and Triggers, generally the only things I'm working on atm. Then I encountered a problem while saving changes. They were still applied in the Sandbox, but the list of yellow exclamation marks got bigger and bigger. So I thought there may have been an update to the Salesforce Integration part in Eclipse and I updated/reinstalled the plugin. After a restart of Eclipse, I hoped for the error to go away but it didn't. So I downloaded the classes and triggers again in a new project (deleted the old ones) and was sure it would be gone by now. But here's what I get while saving for like an eternity:
User-added image

Again, the changes are taken over in the Sandbox, but this list will only get longer and longer until the important warnings will no longer be separable from those. Is this a server issue, a version conflict, an already known issue, anything?

Thanks for help.
Dear Community

We're almost done with our Web to Lead implementation. Now we just found out that whenever one enters carriage return characters in a text area field on the website, which is going to be written to a text area field in salesforce, there is no lead created after submitting.

Furthermore, Salesforce seems not to be capable to convert php encodings back to the original character. As I'm having a trigger on lead anyway for that reason, I can adapt this, but not supporting carriage returns is a pretty bad problem. I am not even getting a debug log during this test.

Any hints on that?
Dear community

I need to send an email to owners of assets when certain criteria are met. I have a custom child object to the asset, called an asset reminder, which stores information about what kind of reminder (email template), what date and what interval the notification should be sent. I use scheduled apex to send these emails every night.

This is my code:
/**
*	Prepares an email per reminder
*/
@TestVisible private void prepareNofifications() {
	for ( AssetReminder__c reminder : assetReminders ) {
		transient Messaging.SingleEmailMessage message = new Messaging.SingleEmailMessage();
		message.setTemplateId(reminder.EmailTemplateId__c);
		message.setTargetObjectId(assets.get(reminder.AssetId__c).OwnerId); // assets is of type map<Id, Asset>
		message.setWhatId(reminder.AssetId__c);
		message.setSaveAsActivity(false);
		emails.add(message);
	}
}

However I receive an error when it executes:
Reminder Dispatcher could not send out reminders.
Exception Type: System.EmailException
Exception Cause: null
Exception Message: SendEmail failed. First exception on row 0; first error: INVALID_ID_FIELD, WhatId is not available for sending emails to UserIds.: [whatId, 02i0E000000U7N4]

Reading up on that error shows that if I specify a WhatId, I can not use a UserId for the TargetObjectId. On one hand this is a total no brainer and on the other hand my template uses asset and user merge fields. What am I supposed to do? I can not simply replace that stuff with a contact.

Does anybody have a workaround for this?

Best
Roger
Dear community

I am building a public visualforce page via site and I face some difficulties from internal testing to testing on the site. First of all I noticed that references to my sObject field were simply removed from the interface. No worries, I replaced them with simple input fields. But what I can't figure out is how I can load my custom font, saved as static resource into the public site. It worked with internal views but not on the site. Furthermore, I want to display an image that is stored as attachment depending on the parameter passed in the URL.

CSS-snippet:
/*---- General CSS ----*/
@font-face {
	font-family: Gravur; src: url('/resource/Gravur_CondensedLight');
}

@font-face {
	font-family: Gravur; font-weight: bold; src: url('/resource/Gravur_CondensedBold');
}

/*---- Page CSS ----*/
html
{
	font-family: Gravur;
	font-size: 16px;
}

/* And more irrelevant down below */

My Controller-snippet:
public class PartyRSVPController
{
	public String cmid {get;set;}
	public Boolean rendered {get; set;}
	public Boolean renderedError {get; set;}
	public Boolean success {get; set;}
	public CampaignMember aMember {get; set;}
	public Integer numGuests {get; set;}
	public Campaign aCampaign {get; set;}
	public String campaignImagePath {get; set;}
	public StaticResource res;
	
	public PartyRSVPController(){
		aMember = new CampaignMember();
		aCampaign = new Campaign();
		rendered = true;
		success = false;
		res = new StaticResource();
	}
	
	public void init() {
		cmid = System.currentPageReference().getParameters().get('cmid');
		if ( cmid == null || cmid.equals('') ) {
			rendered = false;
		}
		
		try {
			aMember = queryCampaignMember(cmid);
			aCampaign = queryCampaign(aMember.CampaignId);
		} catch(system.QueryException qe) {
			rendered = false;
		}
		
		campaignImagePath = '/servlet/servlet.FileDownload?file=' + aCampaign.CampaignImageId__c;
		renderedError = !rendered;
	}
}

My page-snippet
<apex:page showHeader="false" sidebar="false" showChat="false" controller="PartyRSVPController"  standardStylesheets="false" docType="html-5.0" title="Party RSVP" action="{!init}">
<apex:pageMessages />
	<apex:stylesheet value="{!$Resource.PartyRSVP}" />
	<apex:form styleClass="form">
		<apex:pageBlock >
			<section id="page_header">
				<apex:image value="{!$Resource.ArchitonicLogo}" alt="Architonic Logo"/>
			</section>
		</apex:pageBlock>
		<apex:pageBlock rendered="{!rendered}">
			<section id="page_content">
				<aside>
					<!-- apex:image url="{!URLFOR($Action.Attachment.Download, aCampaign.CampaignImageId__c)}" alt="Campaign Image" styleClass="image" /-->
					<apex:image value="{!campaignImagePath}" alt="Campaign Image" styleClass="image" />
				</aside>
				<article>
					Dear {!Salutation}<br/>
					<br/>
					We are delighted to have you at our event <b>{!aCampaign.CampaignNamePublic__c}</b>! Please let us know whether you bring any guests and how many you bring:<br/>
					<div>
						<apex:input value="{!numGuests}" type="number" styleClass="inputField" label="Guests"/>
						<apex:commandButton id="SubmitButton" title="RSVP" value="RSVP" action="{!submit}"/>
					</div>
				</article>
			</section>
		</apex:pageBlock>
	</apex:form>
</apex:page>

Generally, the stylesheet loadsbecause it displays my tags correctly, but the font doesn't load. As you can see I use font face for using fonts for both bold and normal display. I do suspect that the simple "src:url( '/resource/Gravur_CondensedLight')" is the source of the problem. I do however not know how to deal with it.

This is what it looks like:
User-added image

And this is how it is supposed to look like:
User-added image

Note that now we see a different font. As stated before, the image is saved as attachment on a sObject and it is (so far) referenced with the "/servlet/servlet.FileDownload?file=" syntax by passing the image id.

While I know that I can save the image as static resource and then display it accordingly, it is not an option due to the workflow requiring the users to be able to define the image dynamically without having access to static resources.

Does anybody have experience with this? Any hints towards a solution? Thanks ahead.

Best
Roger
Dear community

We are looking for a solution to prevent standard Lead-Account-mapping during conversion. As is, when converting and merging a Lead into an existing Account, an empty Account billing address would be set by the Lead address. While this behaviour is not so bad for completely new Accounts, in a merge it is devestating for us.

It often happens, that active clients still apply on our website, e.g. for an additional product or so. Those clients already have an Account. Usually, we only use the shipping address and only fill in the billing address if it happens to be different. That means, in most cases the billing address is empty. When a Lead is now converted and merged to such an Account and the Lead address differs from the Account's shipping address, we might not realise it until we already sent out the invoice to the wrong place. That is why we need to prevent that standard mapping.

I have looked into solutions with Apex but I am still clueless. According to this entry on stackexchange, at least the custom field mapping is executed already before the before triggers. I guess that standard field mappings also apply then. That leaves me literally no option to prevent the change. Because in the before trigger I would not even see that the billing address is being updated.

Does anybody have an input for this?
Hi, guys.

I have a strange problem here, which really pisses me off :(

So it is very simple, just typing in execute anonymous:
Account acc;
acc.getCloneSourceId();

Gives me an error:
"Compile error at line 2 column 1
Method does not exist or incorrect signature: [Account].getCloneSourceId()"

According to this (https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_methods_system_sobject.htm)article, this method should work with any SObject, but it doesn't work with any object...
Please, tell me what am I missing.



Thanks!
  • October 16, 2015
  • Like
  • 0
Hello all, 
long time reader, first time poster.
I have a trigger that runs before update, gets some related users and sends them an email and then updates a field for a 'Sent
 email.
Good - When the records are manually changed (testing functionality), everything works great.
Bad - Test class produces the following error:
System.DmlException: Update failed. First exception on row 0 with id a0Ee0000006KoJZEA0; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, EventSessionTrigger: execution of BeforeUpdate
caused by: System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_ID_FIELD, WhatId is not available for sending emails to UserIds.: []


There is documentation here (https://www.salesforce.com/us/developer/docs/apexcode/Content/apex_classes_email_outbound_single.htm#apex_Messaging_SingleEmailMessage_setWhatId) that says why the test error is coming up, BUT the question is why does it work in practice and not in the test?
The overall goal, would be to have it work in both. 
 
//Test class
@isTest
private class EventSessions_Test
{
  @testSetup static void testSetup()
  {
    Profile p =
      [SELECT Id FROM Profile
       WHERE Name = 'Customer Service Managers'];
    User u1 = new User(
      Alias = 'standt', Email = 'dhuckins@argyleforum.com',
      EmailEncodingKey = 'UTF-8', LastName = 'Testing',
      LanguageLocaleKey = 'en_US',
      LocaleSidKey = 'en_US', ProfileId = p.Id,
      TimeZoneSidKey = 'America/Los_Angeles',
      UserName = 'argyletestuser1@bar.com'
    );
    insert u1;
    EmailTemplate template = new EmailTemplate(
      Name = 'Test template',
      Subject = 'test subject',
      DeveloperName = 'Test_template_for_attendance_alerts',
      TemplateType = 'text',
      FolderId = '00l40000001pLA5AAM',
      IsActive = true
    );
    insert template;
  }
  @isTest static void test_method_one()
  {
    EmailTemplate template =
      [SELECT Id
       FROM EmailTemplate
       WHERE DeveloperName = 'Test_template_for_attendance_alerts'
                             LIMIT 1];
    User u1 =
      [SELECT Id
       FROM User
       WHERE Email = 'dhuckins@argyleforum.com'
                     LIMIT 1];
    Account account = new Account(
      Name = 'Test Account',
      Primary_Membership__c = 'CIO'
    );
    insert account;
    Contact contact = new Contact(
      FirstName = 'Test',
      LastName = 'Contact',
      AccountId = account.Id,
      Email = 'email@email.com'
    );
    insert contact;
    Events__c event = new Events__c(
      Name = 'Test Event ',
      Related_Membership__c = 'CIO',
      Event_Date__c = Date.today(),
      Programming__c = u1.id,
      Client_Name__c = account.Id,
      Is_there_a_dinner_with_this_meeting__c = 'No',
      Meeting_Type__c = 'Full Day'
    );
    insert event;
    Event_Session__c session = new Event_Session__c(
      Name = 'Test SEssion',
      Related_Event__c = event.Id,
      Email_Alert_Padding__c = 10,
      Email_Alert_Status__c = 'Scheduled',
      Start_Time__c = Datetime.now(),
      Email_Alert_Template__c = template.Id
    );
    insert session;
    Client_Project_Management__c module = new Client_Project_Management__c(
      Name = 'Test module',
      Account__c = account.Id
    );
    insert module;
    Campaign_Attribute__c attribute = new Campaign_Attribute__c(
      Project__c = event.Id,
      Staff_responsible_for_client_at_event__c = u1.Id, // send an email to this one
      Client_Project_Name__c = module.Id
    );
    insert attribute;
    Argyle_Alerts_Entry__c entry = new Argyle_Alerts_Entry__c(
      Account__c = account.Id,
      Interested_Account__c = account.Id,
      Contact__c = contact.Id,
      Related_Project__c = event.Id
    );
    insert entry;
    Attendee_Project_Junction__c attendee = new Attendee_Project_Junction__c(
      Attendance_Status__c = 'Attended',
      Attendee_Name__c = contact.Id,
      Attendee_Type__c = 'Other/Neutral',
      Project__c = event.Id
    );
    insert attendee;
    session.Email_Alert_Status__c = 'Sending';
    Test.startTest();
    update session; // line of error
    Test.stopTest();
    Event_Session__c check =
      [SELECT Id, Email_Alert_Status__c
       FROM Event_Session__c
       WHERE Id = :session.Id];
    System.assertEquals('Sent', check.Email_Alert_Status__c);
  }
}
 
//trigger handler
public class EventSessions extends fflib_SObjectDomain
{
  public EventSessions(List<Event_Session__c> records)
  {
    super(records);
  }

  public override void onBeforeUpdate(Map<Id, sObject> existingRecords)
  {
    EventSessionService.emailAlertsRouter(
      (List<Event_Session__c>)records,
      (Map<Id, Event_Session__c>)existingRecords
    );
  }

  public class Constructor implements fflib_SObjectDomain.IConstructable
  {
    public fflib_SObjectDomain construct(List<SObject> sObjectList)
    {
      return new EventSessions(sObjectList);
    }
  }
}
 
//service class
public class EventSessionService
{
  public static void emailAlertsRouter(
    List<Event_Session__c> sessions,
    Map<Id, Event_Session__c> oldSessions)
  {
    List<Event_Session__c> emailTriggers = new List<Event_Session__c>();
    for (Event_Session__c es : sessions)
    {
      if (es.Email_Alert_Status__c == 'Sending' &&
          oldSessions.get(es.Id).Email_Alert_Status__c == 'Scheduled')
      {
        emailTriggers.add(es);
      }
    }
    prepareAttendanceAlerts(emailTriggers);
  }

  public static fflib_SObjectUnitOfWork newInstance()
  {
    return new fflib_SObjectUnitOfWork(
             new Schema.SObjectType[]
             {
               Event_Session__c.SObjectType
             });
  }

  public static void prepareAttendanceAlerts(List<Event_Session__c> emailTriggers)
  {
    //we need a map of the event session and
    //the ids of users responsible for client
    //at that event
    fflib_SObjectUnitOfWork uow = newInstance();
    Set<Id> eventIds = new Set<Id>();
    for (Event_Session__c session : emailTriggers)
    {
      eventIds.add(session.Related_Event__c);
    }
    Set<Id> userIds = new Set<Id>();
    //Messaging.SingleEmailMessage[] emails = new Messaging.SingleEmailMessage[] {};
    for (Campaign_Attribute__c ca :
         [SELECT Id, Client_Project_Name__r.Account__c,
          Staff_responsible_for_client_at_event__c
          FROM Campaign_Attribute__c
          WHERE Project__c IN :eventIds])
    {
      userIds.add(ca.Staff_responsible_for_client_at_event__c);
    }
    for (Event_Session__c es : emailTriggers)
    {
      for (Id target : userIds)
      {
        Messaging.SingleEmailMessage email = new Messaging.SingleEmailMessage();
        email.setTemplateId(es.Email_Alert_Template__c);
        email.setSaveAsActivity(false);
        email.setTargetObjectId(target);
        email.setWhatId(es.Id);
        uow.registerEmail(email);
      }
      es.Email_Alert_Status__c = 'Sent';
      //uow.registerDirty(es);  //moved to onBeforeUpdate
    }
    uow.commitWork();
  }
}
 
Class	EventSessions_Test
Method Name	test_method_one
Pass/Fail	Fail
Error Message	System.DmlException: Update failed. First exception on row 0 with id a0Ee0000006KoJoEAK; first error: CANNOT_INSERT_UPDATE_ACTIVATE_ENTITY, EventSessionTrigger: execution of BeforeUpdate
caused by: System.EmailException: SendEmail failed. First exception on row 0; first error: INVALID_ID_FIELD, WhatId is not available for sending emails to UserIds.: []

Class.fflib_SObjectUnitOfWork.SendEmailWork.doWork: line 308, column 1
Class.fflib_SObjectUnitOfWork.commitWork: line 244, column 1
Class.EventSessionService.prepareAttendanceAlerts: line 78, column 1
Class.EventSessionService.emailAlertsRouter: line 31, column 1
Class.EventSessions.onBeforeUpdate: line 25, column 1
Class.fflib_SObjectDomain.handleBeforeUpdate: line 155, column 1
Class.fflib_SObjectDomain.triggerHandler: line 311, column 1
Class.fflib_SObjectDomain.triggerHandler: line 271, column 1
Trigger.EventSessionTrigger: line 18, column 1: []
Stack Trace	Class.EventSessions_Test.test_method_one: line 98, column 1



 
Hey there

Because I want my test class to work without assuming that certain data is present in the deploy-org, I need to create some partner community users in my test class. As I haven't found any guides on how to do that and because I've failed the last time I tried I'd like to ask the developer community for council.

I think the preconditions to create a partner community user in a test class are:
  • Having an account enabled as partner
    • How to enable an account as partner via apex?
  • Having a contact attached to said account
  • Having a partner community profile
    • Necessary, because we use a custom partner community Profile
    • How to create a representation of our real partner community profile?

Thanks for your advice
Roger

I need to add users with ceratin profile to be automatically  added to a public group . Any hints plz

  • May 31, 2012
  • Like
  • 0
Hi experts,
I found a Salesforce object named OpportunityContactRole by using the IDE (Eclipse) and I want to add a new trigger for this object but I got the following Error message when try to save the new created trigger:
    Save error: SObject type does not allow triggers: OpportunityContactRole
I really need to add the special processing to this object before updating, do any experts have any idea how to fix this issue?
Best regards!
Boi Hue


Message Edited by boihue on 12-09-2008 11:02 AM

Message Edited by boihue on 12-09-2008 11:02 AM
  • December 09, 2008
  • Like
  • 0
I have a trigger set up on an object to trigger after an update.  If I do the update manually it works just fine.  However, if I do a mass update using the Data Loader the trigger doesn't get invoked.
 
Is there a way to force the trigger while using the Apex Data Loader?
  • November 18, 2008
  • Like
  • 0
Database.DMLOptions dm = new Database.DMLOptions();
dm.EmailHeader.triggerUserEmail = true;

Task t = new Task(Subject='Test Task', ActivityDate=date.today(), OwnerId='00590000000wND2');
database.insert(new Task[]{t}, dm);

Email Notifications are enabeld for user in My Setitngs:

Email notifications enabled for user setting

Global Settings for Allow User to control Notifications also Enabled:

User-added image


Based on these settings the Email Notification should have received by the User. And the OwnerId passed in the code is NOT the Logged In User, so the code should ahve triggerred email notifications. Please correct me if I'm wrong, but it looks like something is broken here. The code sends out Email Notifications ONLY when I've disabled "Enable User Control Over Task Assignment Notifications" globally for all users.

 
Hi, 

I'm using the Force.com IDE version 28.0 and since last Thursday  (19th of June 2014) I started getting Save warnings whenever I try to save/compile any change to classes/pages/triggers and in the package.xml.

Save warning:  package.xml /DEV Environment/src line 0 Force.com save problem
Save warning: Associate metadata or source file failed to save.  File only saved locally, not to Salesforce. CountryProfileTester.cls-meta.xml /DEV Environment/src/classes line 1 Force.com save problem
The classes/files are still being saved to the server but I get save AND Deploy Warnings.
I've tried cleaning the registry and changing the workspace and nothing. I've also tried through the Developer Console.

Is there any workaround available?

Many thanks in advance,
Ines