Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4023,4 +4023,33 @@ private class MockDatabaseTest {

Assert.areEqual(12, opportunities.size(), 'Incorrect number of opportunities');
}

@isTest
static void ensureLastFiscalYearWorksInWhereClause() {

List<Opportunity> oppList = new List<Opportunity>();
Date startOfYear = Gmt.startOfThisFiscalYear();
for(Integer i = 0; i < 13; i++) {

oppList.add(new Opportunity(
Name = 'Opp BACKWARDS' + i,
CloseDate = startOfYear.addMonths(-i)
));


oppList.add(new Opportunity(
Name = 'Opp FORWARDS' + i,
CloseDate = startOfYear.addMonths(i)
));
}


MockDatabase.doInsert(oppList, true);

Test.startTest();
List<Opportunity> opportunities = MockDatabase.query('SELECT Id FROM Opportunity WHERE CloseDate = LAST_FISCAL_YEAR');
Test.stopTest();

Assert.areEqual(12, opportunities.size(), 'Incorrect number of opportunities');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ public with sharing class DateLiteralComparableFactory {
Token.NEXT_FISCAL_YEAR_LITERAL => NextFiscalYearComparable.class,
Token.THIS_YEAR_LITERAL => ThisYearComparable.class,
Token.THIS_FISCAL_YEAR_LITERAL => ThisFiscalYearComparable.class,
Token.LAST_N_FISCAL_YEARS_LITERAL => LastNFiscalYearsComparable.class
Token.LAST_N_FISCAL_YEARS_LITERAL => LastNFiscalYearsComparable.class,
Token.LAST_FISCAL_YEAR_LITERAL => LastFiscalYearComparable.class
};

@TestVisible
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/**
* @description Comparable for the LAST_FISCAL_YEAR date literal
* @author Zackary Frazier
* @since 1/10/2025
*/
public with sharing class LastFiscalYearComparable extends DateLiteralComparable {
/**
* @description Returns true if the fieldValue is less than the start of the last fiscal year
* @param fieldValue `Datetime`
* @return `Boolean`
*/
public override Boolean isLessThan(Datetime fieldValue) {
Date startOfThisFiscalYear = Gmt.startOfThisFiscalYear();
Date startOfLastFiscalYear = startOfThisFiscalYear.addYears(-1);
return fieldValue < startOfLastFiscalYear;
}

/**
* @description Returns true if the fieldValue is greater than the start of the last fiscal year
* @param fieldValue `Datetime`
* @return `Boolean`
*/
public override Boolean isGreaterThan(Datetime fieldValue) {
Date startOfThisFiscalYear = Gmt.startOfThisFiscalYear();
return fieldValue >= startOfThisFiscalYear;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@isTest
private class LastFiscalYearComparableTest {

@isTest
static void ensureIsLessThanWorks() {
DateLiteralComparable comparable = new LastFiscalYearComparable();
Date startOfThisFiscalYear = Gmt.startOfThisFiscalYear();
Date startOfLastFiscalYear = startOfThisFiscalYear.addYears(-1);
Date startOfFiscalYearBeforeThat = startOfLastFiscalYear.addYears(-1);
Assert.isTrue(comparable.isLessThan(startOfFiscalYearBeforeThat), 'Should be less than');
Assert.isFalse(comparable.isLessThan(startOfLastFiscalYear), 'Should not be less than');
Assert.isFalse(comparable.isLessThan(startOfThisFiscalYear), 'Should not be less than');
}

@isTest
static void ensureIsGreaterThan() {
DateLiteralComparable comparable = new LastFiscalYearComparable();
Date startOfThisFiscalYear = Gmt.startOfThisFiscalYear();
Date startOfNextFiscalYear = startOfThisFiscalYear.addYears(1);
Date startOfLastFiscalYear = startOfThisFiscalYear.addYears(-1);
Date startOfFiscalYearBeforeThat = startOfLastFiscalYear.addYears(-1);
Assert.isFalse(comparable.isGreaterThan(startOfFiscalYearBeforeThat), 'Should not be greater than');
Assert.isFalse(comparable.isGreaterThan(startOfLastFiscalYear), 'Should not be greater than');
Assert.isTrue(comparable.isGreaterThan(startOfThisFiscalYear), 'Should not be greater than');
Assert.isTrue(comparable.isGreaterThan(startOfNextFiscalYear), 'Should be greater than');
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -483,4 +483,17 @@ private class ParserTest {
}
Test.stopTest();
}

@isTest
static void ensureLastFiscalYearCanBeParsed() {
Parser p = new Parser();
String soqlQuery = 'SELECT Id FROM Opportunity WHERE CreatedDate = LAST_FISCAL_YEAR';
Test.startTest();
try {
p.parse(soqlQuery);
} catch(Exception e) {
Assert.fail('Expected no exception but got ' + e.getMessage() + ' for ' + soqlQuery);
}
Test.stopTest();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,12 @@ private class HavingParserTest {
@isTest
static void ensureTodayCanByParsedByHavingParser() {
HavingParser parser = new HavingParser();
Exception ex;
Intermediary intermediary;
Test.startTest();
try {
intermediary = parser.parse('HAVING CreatedDate < TODAY');
} catch(Exception e) {
ex = e;
}
intermediary = parser.parse('HAVING CreatedDate < TODAY');
Test.stopTest();

Assert.isNull(ex, 'Should be able to parse date literals');
Assert.isNotNull(intermediary, 'Should be able to parse date literals');

}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@
*/
@SuppressWarnings('PMD.CognitiveComplexity')
public abstract class SubParser {

protected String tkn;

/**
* @description Sets the token for the parser
* @param token `String`
*/
public void setToken(String token) {
this.tkn = token;
}

/**
* @description Parses a subquery and returns an `Intermediary` object.
* @param query `String`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public inherited sharing class ValueParser extends SubParser {
SubParser primitiveParser = PrimitiveParserFactory.getPrimitiveParser(query);
return primitiveParser.parse(query);
} catch(Exception e) {
throw new ValidationException('No valid token found for: ' + query);
throw new ParsingException('Error parsing value: ' + query + ' Failed with exception: ' + e.getMessage());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,32 +6,35 @@
public with sharing class PrimitiveParserFactory {
@TestVisible
static Map<String, Type> parserTypeByToken = new Map<String, Type> {
Token.TOMORROW_LITERAL => DateLiteralParser.class,
Token.TODAY_LITERAL => DateLiteralParser.class,
Token.YESTERDAY_LITERAL => DateLiteralParser.class,
Token.LAST_WEEK_LITERAL => DateLiteralParser.class,
Token.THIS_WEEK_LITERAL => DateLiteralParser.class,
Token.NEXT_WEEK_LITERAL => DateLiteralParser.class,
Token.LAST_MONTH_LITERAL => DateLiteralParser.class,
Token.THIS_MONTH_LITERAL => DateLiteralParser.class,
Token.NEXT_MONTH_LITERAL => DateLiteralParser.class,
Token.LAST_90_DAYS_LITERAL => DateLiteralParser.class,
Token.NEXT_90_DAYS_LITERAL => DateLiteralParser.class,
Token.LAST_N_DAYS_LITERAL => DateLiteralParser.class,
Token.NEXT_N_DAYS_LITERAL => DateLiteralParser.class,
Token.N_DAYS_AGO_LITERAL => DateLiteralParser.class,
Token.NEXT_N_WEEKS_LITERAL => DateLiteralParser.class,
Token.LAST_N_WEEKS_LITERAL => DateLiteralParser.class,
Token.N_MONTHS_AGO_LITERAL => DateLiteralParser.class,
Token.THIS_QUARTER_LITERAL => DateLiteralParser.class,
Token.LAST_QUARTER_LITERAL => DateLiteralParser.class,
Token.NEXT_QUARTER_LITERAL => DateLiteralParser.class,
Token.NEXT_N_QUARTERS_LITERAL => DateLiteralParser.class,
Token.THIS_FISCAL_YEAR_LITERAL => DateLiteralParser.class,
Token.LAST_N_FISCAL_YEARS_LITERAL => DateLiteralParser.class,
Token.NEXT_FISCAL_YEAR_LITERAL => DateLiteralParser.class,
Token.THIS_YEAR_LITERAL => DateLiteralParser.class,
Token.LAST_FISCAL_YEAR_LITERAL => DateLiteralParser.class,


Token.SINGLE_QUOTE => StringParser.class,
Token.TOMORROW_LITERAL => TomorrowParser.class,
Token.TODAY_LITERAL => TodayParser.class,
Token.YESTERDAY_LITERAL => YesterdayParser.class,
Token.LAST_WEEK_LITERAL => LastWeekParser.class,
Token.THIS_WEEK_LITERAL => ThisWeekParser.class,
Token.NEXT_WEEK_LITERAL => NextWeekParser.class,
Token.LAST_MONTH_LITERAL => LastMonthParser.class,
Token.THIS_MONTH_LITERAL => ThisMonthParser.class,
Token.NEXT_MONTH_LITERAL => NextMonthParser.class,
Token.LAST_90_DAYS_LITERAL => Last90DaysParser.class,
Token.NEXT_90_DAYS_LITERAL => Next90DaysParser.class,
Token.LAST_N_DAYS_LITERAL => LastNDaysParser.class,
Token.NEXT_N_DAYS_LITERAL => NextNDaysParser.class,
Token.N_DAYS_AGO_LITERAL => NDaysAgoParser.class,
Token.NEXT_N_WEEKS_LITERAL => NextNWeeksParser.class,
Token.LAST_N_WEEKS_LITERAL => LastNWeeksParser.class,
Token.N_MONTHS_AGO_LITERAL => NMonthsAgoParser.class,
Token.THIS_QUARTER_LITERAL => ThisQuarterParser.class,
Token.LAST_QUARTER_LITERAL => LastQuarterParser.class,
Token.NEXT_QUARTER_LITERAL => NextQuarterParser.class,
Token.NEXT_N_QUARTERS_LITERAL => NextNQuartersParser.class,
Token.THIS_FISCAL_YEAR_LITERAL => ThisFiscalYearParser.class,
Token.LAST_N_FISCAL_YEARS_LITERAL => LastNFiscalYearsParser.class,
Token.NEXT_FISCAL_YEAR_LITERAL => NextFiscalYearParser.class,
Token.THIS_YEAR_LITERAL => ThisYearParser.class,
Token.XTRUE => BooleanParser.class,
Token.XFALSE => BooleanParser.class,
Token.XNULL => NullParser.class,
Expand All @@ -58,6 +61,7 @@ public with sharing class PrimitiveParserFactory {
if(ParserUtils.isNext(query, token)) {
Type parserType = parserTypeByToken.get(token);
SubParser primitiveParser = (SubParser) parserType.newInstance();
primitiveParser.setToken(token);
return primitiveParser;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,25 @@
* @author Zackary Frazier
* @since 11/29/2024
*/
public abstract class DateLiteralParser extends SubParser {
public class DateLiteralParser extends SubParser {

// symbol that denotes this is a variable value, e.g. LAST_N_DAYS:5, LAST_N_WEEKS:3
static final String VARIABLE_SINGNIFIER = 'n_';

public override Intermediary parse(String query) {
if(this.tkn.contains(VARIABLE_SINGNIFIER)) {
return parseN(query, this.tkn);
}
return parse(query, this.tkn);
}

/**
* @description Parse a generic date literal
* @param query `String`
* @param token `String`
* @return `Intermediary`
*/
public Intermediary parse(String query, String token) {
private Intermediary parse(String query, String token) {
query = consume(query, token);
return new Intermediary(
new NodeBuilder()
Expand All @@ -22,13 +33,13 @@ public abstract class DateLiteralParser extends SubParser {
}

/**
* @description For the N_DAYS dates literals, parse the query and return the token
* @description For the variable date literals (N_DAYS_AGO:5, etc.), parse the query and return the token
* @param query `String`
* @param nDaysTkn `String`
* @return `Intermediary`
* @exception ValidationException
*/
public Intermediary parseNDays(String query, String nDaysTkn) {
private Intermediary parseN(String query, String nDaysTkn) {
String tkn = nDaysTkn;
String subquery = consume(query, tkn);
subquery = skipWhitespace(subquery);
Expand Down

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

This file was deleted.

Loading
Loading