-
-
Notifications
You must be signed in to change notification settings - Fork 508
Preserve historical usage limits across plan changes #2409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b1be04e
d0c8272
52a50b8
a7c3ed0
8c869fa
9b7c2de
4df6dcb
a249dda
66079e6
456383c
431e4e3
77f1dd5
e2d9181
88b9f8d
541a387
6e9af55
8c48fa5
e9fe951
afd3733
e2bc280
27ae6db
8d4f6a2
0dd16f3
761e47a
25578cb
67e24df
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,6 +2,7 @@ | |
| using Exceptionless.Core.Models; | ||
| using Exceptionless.Core.Models.Billing; | ||
| using Exceptionless.Core.Repositories; | ||
| using Exceptionless.DateTimeExtensions; | ||
| using Foundatio.Lock; | ||
|
|
||
| namespace Exceptionless.Core.Billing; | ||
|
|
@@ -106,16 +107,23 @@ public async Task<ChangePlanResult> CanDownGradeAsync(Organization organization, | |
|
|
||
| public void ApplyBillingPlan(Organization organization, BillingPlan plan, User? user = null, bool updateBillingPrice = true) | ||
| { | ||
| var utcNow = _timeProvider.GetUtcNow().UtcDateTime; | ||
| CaptureOutgoingUsageLimit(organization, plan, utcNow); | ||
|
|
||
| organization.PlanId = plan.Id; | ||
| organization.PlanName = plan.Name; | ||
| organization.PlanDescription = plan.Description; | ||
| organization.BillingChangeDate = _timeProvider.GetUtcNow().UtcDateTime; | ||
| organization.BillingChangeDate = utcNow; | ||
|
|
||
| if (updateBillingPrice) | ||
| { | ||
| organization.BillingPrice = plan.Price; | ||
| } | ||
|
|
||
| if (user is not null) | ||
| { | ||
| organization.BillingChangedByUserId = user.Id; | ||
| } | ||
|
|
||
| organization.MaxUsers = plan.MaxUsers; | ||
| organization.MaxProjects = plan.MaxProjects; | ||
|
|
@@ -126,6 +134,29 @@ public void ApplyBillingPlan(Organization organization, BillingPlan plan, User? | |
| organization.GetCurrentUsage(_timeProvider).Limit = organization.GetMaxEventsPerMonthWithBonus(_timeProvider); | ||
| } | ||
|
|
||
| private void CaptureOutgoingUsageLimit(Organization organization, BillingPlan plan, DateTime utcNow) | ||
| { | ||
| if (String.IsNullOrEmpty(organization.PlanId) | ||
| || String.Equals(organization.PlanId, plan.Id, StringComparison.OrdinalIgnoreCase) | ||
| || organization.MaxEventsPerMonth == 0) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var previousMonthUtc = utcNow.StartOfMonth().AddMonths(-1); | ||
| var organizationCreatedMonthUtc = organization.CreatedUtc.ToUniversalTime().StartOfMonth(); | ||
| if (previousMonthUtc < organizationCreatedMonthUtc) | ||
| { | ||
| return; | ||
| } | ||
|
|
||
| var previousUsage = organization.Usage.GetOrAddMonthlyUsage(previousMonthUtc, organization.MaxEventsPerMonth); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When a plan changes just after a month boundary while a five-minute event bucket from the preceding month is still pending, this creates the correct outgoing-plan anchor, but AGENTS.md reference: AGENTS.md:L72-L75 Useful? React with 👍 / 👎. |
||
| if (previousUsage.Limit == 0) | ||
| { | ||
| previousUsage.Limit = organization.MaxEventsPerMonth; | ||
| } | ||
| } | ||
|
|
||
| public void ApplyBonus(Organization organization, int bonusEvents, DateTime? expires = null) | ||
| { | ||
| organization.BonusEventsPerMonth = bonusEvents; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When
update-organization-plansreapplies a configured plan whoseMaxEventsPerMonthchanged but whose plan ID stayed the same, this guard treats the operation as unchanged and skipsCaptureOutgoingUsageLimit. The maintenance path then updates the organization to the new limit and writes only the current-month bucket, soViewOrganization.EnsureUsagecan backfill sparse prior months with the new limit instead of anchoring the old one; compare the old and new event limits as well as the plan ID before returning.AGENTS.md reference: AGENTS.md:L74-L74
Useful? React with 👍 / 👎.