function readOnly(count){ }
Starting November 20, the site will be set to read-only. On December 4, 2023,
forum discussions will move to the Trailblazer Community.
+ Start a Discussion
Roger WickiRoger Wicki 

recalculate formulas NoDataFoundException

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
LBKLBK
May be these test data are not created in the ORG, because I don't see any INSERT or UPSERT statements.

Can you try to run an INSERT on the newExpenses list before calling the recalculateformulas method?
Roger WickiRoger Wicki
Hi LBK

That is true, but in general, for testing methods, records don't need to be inserted. And formulas work on records – no documentation states these records need to be inserted to or be present in the database. That is the whole idea: not inserting the data because it makes testing much slower and not isolated (due to causing other triggers to fire).