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 @@ -12,7 +12,6 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
Expand Down Expand Up @@ -83,8 +82,7 @@ private int getStartOfBusinessYear(YearMonth startOfQuarter, int quarter) {
return startOfQuarter.minusMonths((quarter - 1) * 3L).getYear();
}

private void generateQuarter(LocalDate start, String label, String schema) {
TenantContext.setCurrentTenant(schema);
private Quarter generateQuarter(LocalDate start, String label) {

YearMonth yearMonth = YearMonth.from(start);
Quarter quarter = Quarter.Builder
Expand All @@ -94,14 +92,7 @@ private void generateQuarter(LocalDate start, String label, String schema) {
.withEndDate(yearMonth.plusMonths(2).atEndOfMonth())
.build();
validator.validateOnGeneration(quarter);
quarterPersistenceService.save(quarter);
}

private boolean isInLastMonthOfQuarter(int currentQuarter, int nextQuarter) {
// If the quarter 4 months in the future and the current are exactly 2 apart,
// we are in the final month of the current quarter. This works for all 4 cases:
// 1 -> 3 | 2 -> 4 | 3 -> 1 | 4 -> 2
return Math.abs(nextQuarter - currentQuarter) == 2;
return quarterPersistenceService.save(quarter);
}

public YearMonth getCurrentYearMonth() {
Expand All @@ -119,27 +110,42 @@ Map<Integer, Integer> generateQuarters() {
return quarters;
}

@Scheduled(cron = "0 59 23 L * ?") // Cron expression for 23:59:00 on the last day of every month
public void scheduledGenerationQuarters() {
Map<Integer, Integer> quarters = generateQuarters();
YearMonth currentYearMonth = getCurrentYearMonth();
YearMonth nextQuarterYearMonth = currentYearMonth.plusMonths(4);
private Quarter getOrCreateCurrentQuarter() {
Quarter current = getCurrentQuarter();
if (current == null) {
current = createQuarter(getCurrentYearMonth());
}
return current;
}

int currentQuarter = quarters.get(currentYearMonth.getMonthValue());
int nextQuarter = quarters.get(nextQuarterYearMonth.getMonthValue());
private Quarter createQuarter(YearMonth creationDate) {
String label = createQuarterLabel(creationDate, generateQuarters().get(creationDate.getMonthValue()));
return generateQuarter(creationDate.atDay(1), label);
}

private boolean isFirstMonthOfQuarter(Quarter currentQuarter) {
YearMonth firstMonth = YearMonth.from(currentQuarter.getStartDate());
return getCurrentYearMonth().equals(firstMonth);
}

@Scheduled(cron = "0 1 0 1 * ?") // Runs at 00:01 on the 1st of each month
public void scheduledGenerationQuarters() {
logger.warn("Start scheduled generation of quarters");
String initialTenant = TenantContext.getCurrentTenant();

Set<String> tenantSchemas = this.tenantConfigProvider.getAllTenantIds();
// If we are in the last month of a quarter, generate the next quarter
if (isInLastMonthOfQuarter(currentQuarter, nextQuarter)) {
for (String schema : tenantSchemas) {
logger.info("Generated quarters on last day of month for tenant {}", schema);
String label = createQuarterLabel(nextQuarterYearMonth, nextQuarter);
generateQuarter(nextQuarterYearMonth.atDay(1), label, schema);
for (String schema : tenantConfigProvider.getAllTenantIds()) {
logger.warn("Start generating quarters on first day of month for tenant {}", schema);
TenantContext.setCurrentTenant(schema);

Quarter currentQuarter = getOrCreateCurrentQuarter();
if (isFirstMonthOfQuarter(currentQuarter)) {
YearMonth nextQuarter = YearMonth.from(currentQuarter.getEndDate()).plusMonths(1);
createQuarter(nextQuarter);
}
logger.warn("Successfully generated quarters on first day of month for tenant {}", schema);
}

TenantContext.setCurrentTenant(initialTenant);
logger.warn("End scheduled generation of quarters");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -107,35 +107,64 @@ void shouldGetBacklogQuarter() {
assertNull(quarterList.get(0).getEndDate());
}

@ParameterizedTest(name = "Should not generate a new quarter on scheduledGenerationQuarters() when it is not the last month of the quarter such as {0}")
@ValueSource(ints = { 1, 2, 4, 5, 7, 8, 10, 11 })
@ParameterizedTest(name = "Should not generate a new quarter on scheduledGenerationQuarters() when it is not the first month of the quarter such as {0}")
@ValueSource(ints = { 2, 3, 5, 6, 8, 9, 11, 12 })
void shouldNotGenerateQuarterIfNotLastMonthOfQuarter(int month) {
ReflectionTestUtils.setField(quarterBusinessService, "quarterStart", 7);

Mockito.when(quarterBusinessService.getCurrentYearMonth()).thenReturn(YearMonth.of(2030, month));
quarterBusinessService.scheduledGenerationQuarters();
verify(quarterPersistenceService, never()).save(any());
}

@ParameterizedTest(name = "Should generate new quarter on scheduledGenerationQuarters() when it is the last month of the quarter such as {0}")
@ValueSource(ints = { 3, 6, 9, 12 })
void shouldGenerateQuarterIfLastMonthOfQuarter(int month) {
@ParameterizedTest(name = "Should generate both new quarter if the current one does not exist on scheduledGenerationQuarters() when it is the first month of the quarter such as {0}")
@ValueSource(ints = { 1, 4, 7, 10 })
void shouldGenerateBothQuartersIfLastMonthOfQuarterAndCurrentQuarterDoesNotExist(int month) {
ReflectionTestUtils.setField(quarterBusinessService, "quarterStart", 7);
Mockito.doReturn(Set.of(TestHelper.SCHEMA_PITC)).when(tenantConfigProvider).getAllTenantIds();

Mockito.when(quarterBusinessService.getCurrentYearMonth()).thenReturn(YearMonth.of(2030, month));

LocalDate currentQuarterStart = LocalDate.of(2030, month, 1);
LocalDate currentQuarterEnd = currentQuarterStart.plusMonths(3).minusDays(1);

Quarter currentQuarter = new Quarter();
currentQuarter.setStartDate(currentQuarterStart);
currentQuarter.setEndDate(currentQuarterEnd);

Mockito.when(quarterPersistenceService.save(currentQuarter)).thenReturn(currentQuarter);
quarterBusinessService.scheduledGenerationQuarters();
verify(quarterPersistenceService, times(2)).save(any());
}

@ParameterizedTest(name = "Should generate new quarter on scheduledGenerationQuarters() when it is the first month of the quarter such as {0}")
@ValueSource(ints = { 1, 4, 7, 10 })
void shouldGenerateQuarterIfFirstMonthOfQuarter(int month) {
ReflectionTestUtils.setField(quarterBusinessService, "quarterStart", 7);
Mockito.doReturn(Set.of(TestHelper.SCHEMA_PITC)).when(tenantConfigProvider).getAllTenantIds();

Mockito.when(quarterBusinessService.getCurrentYearMonth()).thenReturn(YearMonth.of(2030, month));

LocalDate currentQuarterStart = LocalDate.of(2030, month, 1);
LocalDate currentQuarterEnd = currentQuarterStart.plusMonths(3).minusDays(1);

Quarter currentQuarter = new Quarter();
currentQuarter.setStartDate(currentQuarterStart);
currentQuarter.setEndDate(currentQuarterEnd);

Mockito.when(quarterBusinessService.getCurrentQuarter()).thenReturn(currentQuarter);
quarterBusinessService.scheduledGenerationQuarters();

verify(quarterPersistenceService, times(1)).save(any());
}

private static Stream<Arguments> generateQuarterParams() {
return Stream
.of(Arguments.of(7, "GJ xx/yy-Qzz", YearMonth.of(2030, 3), "GJ 30/31-Q1"),
Arguments.of(7, "GJ xx/yy-Qzz", YearMonth.of(2030, 9), "GJ 30/31-Q3"),
Arguments.of(5, "GJ xx/yy-Qzz", YearMonth.of(2030, 4), "GJ 30/31-Q2"),
Arguments.of(1, "GJ xx-Qzz", YearMonth.of(2030, 9), "GJ 31-Q1"),
Arguments.of(1, "GJ xxxx-Qzz", YearMonth.of(2030, 6), "GJ 2030-Q4"),
Arguments.of(2, "xx-yy-xxxx-yyyy-Qzz", YearMonth.of(2030, 1), "30-31-2030-2031-Q2"));
.of(Arguments.of(7, "GJ xx/yy-Qzz", YearMonth.of(2030, 4), "GJ 30/31-Q1"),
Arguments.of(7, "GJ xx/yy-Qzz", YearMonth.of(2030, 10), "GJ 30/31-Q3"),
Arguments.of(5, "GJ xx/yy-Qzz", YearMonth.of(2030, 5), "GJ 30/31-Q2"),
Arguments.of(1, "GJ xx-Qzz", YearMonth.of(2030, 10), "GJ 31-Q1"),
Arguments.of(1, "GJ xxxx-Qzz", YearMonth.of(2030, 7), "GJ 2030-Q4"),
Arguments.of(2, "xx-yy-xxxx-yyyy-Qzz", YearMonth.of(2030, 2), "30-31-2030-2031-Q2"));
}

@ParameterizedTest(name = "Should generate quarters correctly on scheduledGenerationQuarters() with quarter start {0}, format {1}, current month of year {2} and label {3}")
Expand All @@ -147,10 +176,10 @@ void shouldGenerateCorrectQuarter(int quarterStart, String quarterFormat, YearMo
ReflectionTestUtils.setField(quarterBusinessService, "quarterStart", quarterStart);
ReflectionTestUtils.setField(quarterBusinessService, "quarterFormat", quarterFormat);

int monthsToNextQuarterStart = 4;
int monthsToNextQuarterStart = 3;
LocalDate expectedStart = currentYearMonth.plusMonths(monthsToNextQuarterStart).atDay(1);

int monthsToNextQuarterEnd = 6;
int monthsToNextQuarterEnd = 5;
LocalDate expectedEnd = currentYearMonth.plusMonths(monthsToNextQuarterEnd).atEndOfMonth();

Quarter expectedQuarter = Quarter.Builder
Expand All @@ -161,7 +190,15 @@ void shouldGenerateCorrectQuarter(int quarterStart, String quarterFormat, YearMo
.withEndDate(expectedEnd)
.build();

LocalDate currentQuarterStart = LocalDate.of(currentYearMonth.getYear(), currentYearMonth.getMonth(), 1);
LocalDate currentQuarterEnd = currentQuarterStart.plusMonths(3).minusDays(1);

Quarter currentQuarter = new Quarter();
currentQuarter.setStartDate(currentQuarterStart);
currentQuarter.setEndDate(currentQuarterEnd);

Mockito.when(quarterBusinessService.getCurrentYearMonth()).thenReturn(currentYearMonth);
Mockito.when(quarterPersistenceService.getCurrentQuarter()).thenReturn(currentQuarter);

quarterBusinessService.scheduledGenerationQuarters();

Expand Down Expand Up @@ -207,7 +244,6 @@ void shouldGetQuartersBasedOnStart(int start, int month, int quarter) {
@DisplayName("Should return null on scheduledGenerationQuarters() when no quarters need to be generated")
@Test
void shouldReturnNullWhenNoQuarterGenerationNeeded() {
Mockito.when(quarterBusinessService.getCurrentYearMonth()).thenReturn(YearMonth.of(2030, 4));
quarterBusinessService.scheduledGenerationQuarters();
verify(quarterPersistenceService, times(0)).save(any());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -138,15 +138,30 @@ void getModelNameShouldReturnQuarter() {
assertEquals(QUARTER, quarterPersistenceService.getModelName());
}

@ParameterizedTest(name = "Should generate quarter with Cron-Job when current month is the last month of the current quarter (Month: {0}, Quarter: {1})")
@CsvSource(value = { "1,1,0", "2,1,0", "3,1,1", "4,1,0", "5,1,0", "6,2,1", "7,1,0", "8,1,0", "9,3,1", "10,3,0",
"11,1,0", "12,4,1" })
void shouldGenerateQuarterWithCronJob(int month, int quarterIndex, int amountOfInvocations) {
@ParameterizedTest(name = "Should generate quarter with Cron-Job when current month is the first month of the current quarter (Month: {0}, Quarter: {1})")
@CsvSource(value = { "1,4,1,1", "2,4,0,1", "3,4,0,1", "4,1,1,4", "5,1,0,4", "6,1,0,4", "7,2,1,7", "8,2,0,7",
"9,2,0,7", "10,3,1,10", "11,3,0,10", "12,3,0,10" })
void shouldGenerateQuarterWithCronJob(int month, int quarterIndex, int amountOfInvocations,
int currentQuarterStart) {
int startQuarter = 7;
ReflectionTestUtils.setField(quarterBusinessService, "quarterStart", startQuarter);
int nextYear = Year.now().atMonth(startQuarter).plusMonths(month + 12 - 1).getYear();
int nextYearShort = nextYear % 1000;
String expectedLabel = "GJ " + nextYearShort + "/" + (nextYearShort + 1) + "-Q" + quarterIndex;
String expectedLabel;
if (quarterIndex == 4) {
expectedLabel = "GJ " + (nextYearShort - 1) + "/" + nextYearShort + "-Q" + quarterIndex;
} else {
expectedLabel = "GJ " + nextYearShort + "/" + (nextYearShort + 1) + "-Q" + quarterIndex;
}

LocalDate start = LocalDate.of(nextYear, currentQuarterStart, 1);
LocalDate end = start.plusMonths(3).minusDays(1);

Quarter currentQuarter = new Quarter();
currentQuarter.setStartDate(start);
currentQuarter.setEndDate(end);

Mockito.when(quarterBusinessService.getCurrentQuarter()).thenReturn(currentQuarter);

Mockito.doReturn(YearMonth.of(nextYear, month)).when(quarterBusinessService).getCurrentYearMonth();
Mockito.doReturn(Set.of(TestHelper.SCHEMA_PITC)).when(tenantConfigProvider).getAllTenantIds();
Expand Down
Loading