From 8d604f715a8d0221ff0f564b4fd8bb1a0a3f8407 Mon Sep 17 00:00:00 2001 From: Conor Macpherson Date: Thu, 5 Jan 2023 15:31:58 -0500 Subject: [PATCH] Change logic for due dates. Previosuly they were 15 days after the first days of the quarters last month. Now, due dates are 15 days after the last day of the quarters last month. --- utils/license.go | 33 ----------- utils/license_test.go | 112 ------------------------------------- utils/true_up.go | 85 ++++++++++++++++++++++++++++ utils/true_up_test.go | 125 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 210 insertions(+), 145 deletions(-) create mode 100644 utils/true_up.go create mode 100644 utils/true_up_test.go diff --git a/utils/license.go b/utils/license.go index eb49b3e165..0c6b7dbe04 100644 --- a/utils/license.go +++ b/utils/license.go @@ -16,7 +16,6 @@ import ( "os" "path/filepath" "strconv" - "time" "github.com/mattermost/mattermost-server/v6/model" "github.com/mattermost/mattermost-server/v6/shared/mlog" @@ -35,11 +34,6 @@ hwIDAQAB var LicenseValidator LicenseValidatorIface -const trueUpReviewDueDay = 15 -const businessQuarterStep = 3 -const day = time.Hour * 24 -const week = day * 7 - func init() { if LicenseValidator == nil { LicenseValidator = &LicenseValidatorImpl{} @@ -230,30 +224,3 @@ func GetSanitizedClientLicense(l map[string]string) map[string]string { return sanitizedLicense } - -func GetNextTrueUpReviewDueDate(now time.Time) time.Time { - quaterEndMonths := []time.Month{time.March, time.June, time.September, time.December} - - var nextQuarterEndMonth time.Month = time.March - for _, month := range quaterEndMonths { - if now.Month() <= month && now.Day() <= trueUpReviewDueDay { - nextQuarterEndMonth = month - break - } else if now.Month() <= month && now.Day() > trueUpReviewDueDay { - nextQuarterEndMonth = month + businessQuarterStep - break - } - } - - return time.Date(now.Year(), nextQuarterEndMonth, trueUpReviewDueDay, 0, 0, 0, 0, now.Location()) -} - -func IsTrueUpReviewDueDateWithinTheNextTwoWeeks(now time.Time, dueDate time.Time) bool { - dueDateWindow := dueDate.Add(-(week * 2)) - - if now.Before(dueDateWindow) || now.After(dueDate) { - return false - } - - return true -} diff --git a/utils/license_test.go b/utils/license_test.go index 57c57a6a3c..8c57685444 100644 --- a/utils/license_test.go +++ b/utils/license_test.go @@ -8,7 +8,6 @@ import ( "encoding/base64" "os" "testing" - "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -92,114 +91,3 @@ func TestGetLicenseFileFromDisk(t *testing.T) { assert.False(t, success, "should have been an invalid file") }) } - -func TestGetNextTrueUpReviewDueDate(t *testing.T) { - t.Run("Due date always falls on the 15th", func(t *testing.T) { - // Before the 15th - now := time.Date(2022, 12, 14, 0, 0, 0, 0, time.Local) - due := GetNextTrueUpReviewDueDate(now) - assert.Equal(t, due.Day(), trueUpReviewDueDay) - - // On the 15th - now = time.Date(2022, 12, 15, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, due.Day(), trueUpReviewDueDay) - - // After the 15th - now = time.Date(2022, 12, 16, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, due.Day(), trueUpReviewDueDay) - }) - - t.Run("Due date will always be in next quarter if the current date is past the 15th", func(t *testing.T) { - now := time.Date(2022, time.March, 16, 0, 0, 0, 0, time.Local) - due := GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.June, due.Month()) - - now = time.Date(2022, time.June, 16, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.September, due.Month()) - - now = time.Date(2022, time.September, 16, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.December, due.Month()) - - now = time.Date(2022, time.December, 16, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.March, due.Month()) - }) - - t.Run("Due date will always be in the current quarter if the current date is before or on the 15th", func(t *testing.T) { - now := time.Date(2022, time.March, 15, 0, 0, 0, 0, time.Local) - due := GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.March, due.Month()) - - now = time.Date(2022, time.June, 15, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.June, due.Month()) - - now = time.Date(2022, time.September, 14, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.September, due.Month()) - - now = time.Date(2022, time.December, 14, 0, 0, 0, 0, time.Local) - due = GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.December, due.Month()) - }) - - t.Run("Due date will be in the next year if the next quarter is not within the current year", func(t *testing.T) { - now := time.Date(2022, time.December, 18, 0, 0, 0, 0, time.Local) - due := GetNextTrueUpReviewDueDate(now) - assert.Equal(t, time.March, due.Month()) - assert.Equal(t, 2023, due.Year()) - }) -} - -func TestIsTrueUpReviewDueDateWithinTheNextTwoWeeks(t *testing.T) { - t.Run("Ensure a date within two weeks before the due date returns true", func(t *testing.T) { - // 1 Day before the due date - now := time.Date(2022, time.December, 14, 0, 0, 0, 0, time.Local) - // Due date is December 15th, 2022 - due := GetNextTrueUpReviewDueDate(now) - - res := IsTrueUpReviewDueDateWithinTheNextTwoWeeks(now, due) - assert.True(t, res) - }) - - t.Run("Ensure a date that is more than two weeks before the due date returns false", func(t *testing.T) { - // 15 Days before the due date - now := time.Date(2022, time.November, 30, 0, 0, 0, 0, time.Local) - // Due date is December 15th, 2022 - due := GetNextTrueUpReviewDueDate(now) - - res := IsTrueUpReviewDueDateWithinTheNextTwoWeeks(now, due) - assert.False(t, res) - }) - - t.Run("Ensure a date that past the due date returns false", func(t *testing.T) { - now := time.Date(2022, time.December, 16, 0, 0, 0, 0, time.Local) - - // Due date is December 15th, 2022 - dueNow := time.Date(2022, time.December, 15, 0, 0, 0, 0, time.Local) - due := GetNextTrueUpReviewDueDate(dueNow) - - res := IsTrueUpReviewDueDateWithinTheNextTwoWeeks(now, due) - assert.False(t, res) - }) - - t.Run("Ensure a date that is on the due date returns true", func(t *testing.T) { - now := time.Date(2022, time.December, 15, 0, 0, 0, 0, time.Local) - due := GetNextTrueUpReviewDueDate(now) - - res := IsTrueUpReviewDueDateWithinTheNextTwoWeeks(now, due) - assert.True(t, res) - }) - - t.Run("Ensure a date that is on the first day of the due date window returns true", func(t *testing.T) { - now := time.Date(2022, time.December, 1, 0, 0, 0, 0, time.Local) - due := GetNextTrueUpReviewDueDate(now) - - res := IsTrueUpReviewDueDateWithinTheNextTwoWeeks(now, due) - assert.True(t, res) - }) -} diff --git a/utils/true_up.go b/utils/true_up.go new file mode 100644 index 0000000000..22f19a7fc8 --- /dev/null +++ b/utils/true_up.go @@ -0,0 +1,85 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package utils + +import ( + "fmt" + "time" +) + +const trueUpReviewDueDay = 15 +const day = time.Hour * 24 +const week = day * 7 + +type DueDateWindow struct { + Start time.Time + End time.Time +} + +func GetNextTrueUpReviewDueDate(now time.Time) time.Time { + nowYear := now.Year() + nowMonth := now.Month() + nowDay := now.Day() + finalQuarterYear := nowYear + if nowMonth >= time.October && nowMonth <= time.December { + finalQuarterYear = nowYear + 1 + } + trueUpSubmissionWindows := []DueDateWindow{ + { + Start: time.Date(now.Year(), time.January, 16, 0, 0, 0, 0, now.Location()), + End: time.Date(now.Year(), time.April, 15, 0, 0, 0, 0, now.Location()), + }, + { + Start: time.Date(now.Year(), time.April, 16, 0, 0, 0, 0, now.Location()), + End: time.Date(now.Year(), time.July, 15, 0, 0, 0, 0, now.Location()), + }, + { + Start: time.Date(now.Year(), time.July, 16, 0, 0, 0, 0, now.Location()), + End: time.Date(now.Year(), time.October, 15, 0, 0, 0, 0, now.Location()), + }, + { + Start: time.Date(now.Year(), time.October, 16, 0, 0, 0, 0, now.Location()), + End: time.Date(finalQuarterYear, time.January, 15, 0, 0, 0, 0, now.Location()), + }, + } + + for _, window := range trueUpSubmissionWindows { + withinWindow := false + // Our due dates "wrap" around, so we'll need to check the months different. Since January = 1 and December = 12, the checks + // for the current month being greater or equal to the start month and less than or equal to the end month will not work. + if window.End.Month() == time.January { + withinWindow = (nowMonth != time.January && nowMonth >= window.Start.Month()) || nowMonth == window.End.Month() + } else { + withinWindow = nowMonth >= window.Start.Month() && nowMonth <= window.End.Month() + fmt.Printf("now month: %s, window start month: %s, window end month: %s\n", now.Format("Jan"), window.Start.Format("Jan"), window.End.Format("Jan")) + } + + // Only check the days if the current month is equal to the start or end months. + // The dates of the middle month(s) don't matter so much. + isFirstMonth := nowMonth == window.Start.Month() + if isFirstMonth { + withinWindow = withinWindow && nowDay >= window.Start.Day() + } + isFinalMonth := nowMonth == window.End.Month() + if isFinalMonth { + withinWindow = withinWindow && nowDay <= window.End.Day() + } + + if withinWindow { + return window.End + } + } + + return trueUpSubmissionWindows[0].End +} + +func IsTrueUpReviewDueDateWithinTheNext30Days(now time.Time, dueDate time.Time) bool { + dueDateWindow := dueDate.Add(-day * 30) + + if now.Before(dueDateWindow) || now.After(dueDate) { + return false + } + + return true +} diff --git a/utils/true_up_test.go b/utils/true_up_test.go new file mode 100644 index 0000000000..76517ac728 --- /dev/null +++ b/utils/true_up_test.go @@ -0,0 +1,125 @@ +// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. +// See LICENSE.txt for license information. + +package utils + +import ( + "fmt" + "testing" + "time" + + "github.com/stretchr/testify/assert" +) + +func TestGetNextTrueUpReviewDueDate(t *testing.T) { + t.Run("Due date always falls on the 15th", func(t *testing.T) { + // Before the 15th + now := time.Date(2022, 12, 14, 0, 0, 0, 0, time.Local) + due := GetNextTrueUpReviewDueDate(now) + assert.Equal(t, due.Day(), trueUpReviewDueDay) + + // On the 15th + now = time.Date(2022, 12, 15, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, due.Day(), trueUpReviewDueDay) + + // After the 15th + now = time.Date(2022, 12, 16, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, due.Day(), trueUpReviewDueDay) + }) + + t.Run("Due date will always be in next quarter if the current date is past the 15th", func(t *testing.T) { + now := time.Date(2022, time.March, 16, 0, 0, 0, 0, time.Local) + due := GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.April, due.Month()) + + now = time.Date(2022, time.June, 16, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.July, due.Month()) + + now = time.Date(2022, time.September, 16, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.October, due.Month()) + + now = time.Date(2022, time.December, 16, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.January, due.Month()) + }) + + t.Run("Due date will always be in the current quarter if the current date is before or on the 15th", func(t *testing.T) { + now := time.Date(2022, time.April, 15, 0, 0, 0, 0, time.Local) + due := GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.April, due.Month()) + + now = time.Date(2022, time.July, 15, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.July, due.Month()) + + now = time.Date(2022, time.October, 14, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.October, due.Month()) + + now = time.Date(2022, time.January, 14, 0, 0, 0, 0, time.Local) + due = GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.January, due.Month()) + }) + + t.Run("Due date will be in the next year if the next quarter is not within the current year", func(t *testing.T) { + now := time.Date(2022, time.October, 21, 0, 0, 0, 0, time.Local) + due := GetNextTrueUpReviewDueDate(now) + assert.Equal(t, time.January, due.Month()) + assert.Equal(t, 2023, due.Year()) + }) +} + +func TestIsTrueUpReviewDueDateWithinTheNext15Days(t *testing.T) { + t.Run("Ensure a date within 30 days before the due date returns true", func(t *testing.T) { + // 1 Day before the due date + now := time.Date(2022, time.March, 16, 0, 0, 0, 0, time.Local) + // Due date is December 15th, 2022 + due := GetNextTrueUpReviewDueDate(now) + + res := IsTrueUpReviewDueDateWithinTheNext30Days(now, due) + assert.True(t, res) + }) + + t.Run("Ensure a date that is more than two weeks before the due date returns false", func(t *testing.T) { + // 15 Days before the due date + now := time.Date(2022, time.October, 16, 0, 0, 0, 0, time.Local) + // Due date is December 15th, 2022 + due := GetNextTrueUpReviewDueDate(now) + + res := IsTrueUpReviewDueDateWithinTheNext30Days(now, due) + assert.False(t, res) + }) + + t.Run("Ensure a date that is past the due date returns false", func(t *testing.T) { + now := time.Date(2022, time.April, 15, 0, 0, 0, 0, time.Local) + + // Due date is April 16th, 2022 + dueNow := time.Date(2022, time.April, 16, 0, 0, 0, 0, time.Local) + due := GetNextTrueUpReviewDueDate(dueNow) + + res := IsTrueUpReviewDueDateWithinTheNext30Days(now, due) + assert.False(t, res) + }) + + t.Run("Ensure a date that is on the due date returns true", func(t *testing.T) { + now := time.Date(2022, time.January, 15, 0, 0, 0, 0, time.Local) + due := GetNextTrueUpReviewDueDate(now) + fmt.Printf("\n\ndue date: %s\n\n", due.Format("2006-Jan-02")) + fmt.Printf("\n\nnow: %s\n\n", now.Format("2006-Jan-02")) + + res := IsTrueUpReviewDueDateWithinTheNext30Days(now, due) + assert.True(t, res) + }) + + t.Run("Ensure a date that is on the first day of the due date window returns true", func(t *testing.T) { + now := time.Date(2022, time.December, 16, 0, 0, 0, 0, time.Local) + due := GetNextTrueUpReviewDueDate(now) + + res := IsTrueUpReviewDueDateWithinTheNext30Days(now, due) + assert.True(t, res) + }) +}