From 2a7d674204d1185a1c31fd20b6e725e7b5579dad Mon Sep 17 00:00:00 2001 From: William Brawner Date: Wed, 2 Feb 2022 21:21:20 -0700 Subject: [PATCH] Fix monthly recurring transactions not being created when the year changes The previous implementation only checked that at least one month had passed between the last run date and now by subtracting the ordinal values. If it were February (month 2) and the previous run date were in January (month 1), then 2 - 1 would equal 1, so it would have been one month since the previous run and we would create the recurring transaction. If it were December however (month 12), the following month would be January (month 1), and 1 - 12 would be -11, so it would appear that we had already created a transaction 11 months in the future. The new implementation also takes the year into account to avoid this situation. --- .../RecurringTransactionProcessingJob.kt | 4 +- .../RecurringTransactionProcessingJobTest.kt | 61 +++++++++++++------ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/app/src/main/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJob.kt b/app/src/main/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJob.kt index 50d69d4..b740014 100644 --- a/app/src/main/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJob.kt +++ b/app/src/main/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJob.kt @@ -40,7 +40,8 @@ class RecurringTransactionProcessingJob( is Frequency.Monthly -> { it.lastRun?.let { last -> val zonedLastRun = last.atZone(ZoneId.of("UTC")) - if (zonedNow.monthValue - zonedLastRun.monthValue < it.frequency.count) + val monthsPassed = ((zonedNow.year * 12) + zonedNow.monthValue) - ((zonedLastRun.year * 12) + zonedLastRun.monthValue) + if (monthsPassed < it.frequency.count) return@forEach } val frequency = (it.frequency as Frequency.Monthly).dayOfMonth @@ -63,7 +64,6 @@ class RecurringTransactionProcessingJob( return@forEach } } - } } is Frequency.Yearly -> { diff --git a/app/src/test/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJobTest.kt b/app/src/test/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJobTest.kt index a11faa1..14936b3 100644 --- a/app/src/test/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJobTest.kt +++ b/app/src/test/kotlin/com/wbrawner/twigs/server/RecurringTransactionProcessingJobTest.kt @@ -231,19 +231,19 @@ class RecurringTransactionProcessingJobTest { fun `monthly transactions are created every last friday`() = runBlockingTest { val start = Instant.parse("1970-01-01T00:00:00Z") recurringTransactionRepository.save( - RecurringTransaction( - title = "Monthly transaction", - amount = 123, - frequency = Frequency.Monthly( - 1, - DayOfMonth.positionalDayOfWeek(Position.LAST, DayOfWeek.FRIDAY), - Time(9, 0, 0) - ), - expense = true, - start = start, - createdBy = "tester", - budgetId = "budgetId" - ) + RecurringTransaction( + title = "Monthly transaction", + amount = 123, + frequency = Frequency.Monthly( + 1, + DayOfMonth.positionalDayOfWeek(Position.LAST, DayOfWeek.FRIDAY), + Time(9, 0, 0) + ), + expense = true, + start = start, + createdBy = "tester", + budgetId = "budgetId" + ) ) loopFor(start, 120) val createdTransactions = transactionRepository.findAll() @@ -254,15 +254,40 @@ class RecurringTransactionProcessingJobTest { assertEquals("1970-04-24T09:00:00Z", createdTransactions[3].date.toString()) } + @Test + fun `monthly transactions are created in the new year`() = runBlockingTest { + val start = Instant.parse("1971-01-01T00:00:00Z") + recurringTransactionRepository.save( + RecurringTransaction( + title = "Monthly transaction", + amount = 123, + frequency = Frequency.Monthly( + 1, + DayOfMonth.day(1), + Time(9, 0, 0) + ), + expense = true, + start = start, + createdBy = "tester", + budgetId = "budgetId", + lastRun = Instant.parse("1970-12-01T09:00:00Z") + ) + ) + loopFor(start, 1) + val createdTransactions = transactionRepository.findAll() + assertEquals(1, createdTransactions.size) + assertEquals("1971-01-01T09:00:00Z", createdTransactions[0].date.toString()) + } + @Test fun `yearly transactions are created every march 31st`() = runBlockingTest { val start = Instant.parse("1970-01-01T00:00:00Z") recurringTransactionRepository.save( - RecurringTransaction( - title = "Yearly transaction", - amount = 123, - frequency = Frequency.Yearly(1, MonthDay.of(3, 31), Time(9, 0, 0)), - expense = true, + RecurringTransaction( + title = "Yearly transaction", + amount = 123, + frequency = Frequency.Yearly(1, MonthDay.of(3, 31), Time(9, 0, 0)), + expense = true, start = start, createdBy = "tester", budgetId = "budgetId"