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) + }) +}