feat: fix prorating credit corrections#4363
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (18)
✅ Files skipped from review due to trivial changes (2)
🚧 Files skipped from review as they are similar to previous changes (13)
📝 WalkthroughWalkthroughAdds a CreditNotes feature flag and threads it through flat-fee service/state machines; implements credit reconciliation and standard-line-to-intent reconciliation; changes immutable-proration patching and zero-amount behavior to avoid creating gathering lines; and adds tests, helpers, and docs. ChangesCredit-Notes Support and Immutable Proration Reconciliation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
03d97fe to
c1176de
Compare
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
test/billing/collection_test.go (1)
69-69: ⚡ Quick winUse test-scoped context here.
Line 69 should use
s.T().Context()so cancellation/lifecycle stays tied to the test harness.As per coding guidelines: "In tests, prefer `t.Context()` when a `testing.T` or `testing.TB` is available instead of using `context.Background()` to keep cancellation and lifecycle tied to the test harness."Suggested fix
- ctx := context.Background() + ctx := s.T().Context()🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/billing/collection_test.go` at line 69, Replace the test-scoped background context with the test harness context: instead of creating ctx := context.Background() in the test (the ctx variable in collection_test.go), call s.T().Context() to derive the context tied to the testing.T lifecycle so cancellations/timeouts propagate correctly from the test runner.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openmeter/billing/charges/flatfee/service/service.go`:
- Around line 95-97: The setter SetCreditNotesSupportedByLineUpdater currently
writes s.creditNotesSupported concurrently with reads in AdvanceCharge,
TriggerPatch, and newStateMachineForStandardLine, risking a data race; either
restrict this setter to startup/tests (documenting that it must not be called at
runtime) or make the field safe for concurrent access by replacing the bool with
a concurrency-safe primitive (e.g., use sync/atomic with an atomic.Bool or
protect access with a sync.RWMutex on the service struct) and update all
reads/writes (in AdvanceCharge, TriggerPatch, newStateMachineForStandardLine,
and SetCreditNotesSupportedByLineUpdater) to use the chosen synchronization
method.
In `@openmeter/billing/worker/subscriptionsync/service/base_test.go`:
- Around line 534-535: The test currently asserts charge.Intent.FeatureKey
equals itemKey but feature keys can differ due to custom mappings; update the
assertion to compare charge.Intent.FeatureKey against the expected feature key
from the rate card/mapping used to build the charge (e.g., derive
expectedFeatureKey from the rate card or productcatalog mapping used in the test
setup) instead of itemKey—replace the itemKey reference in the assertion for
charge.Intent.FeatureKey with that expectedFeatureKey (keep the other
assertions, e.g., productcatalog.NewPriceFrom(...) for price, unchanged).
---
Nitpick comments:
In `@test/billing/collection_test.go`:
- Line 69: Replace the test-scoped background context with the test harness
context: instead of creating ctx := context.Background() in the test (the ctx
variable in collection_test.go), call s.T().Context() to derive the context tied
to the testing.T lifecycle so cancellations/timeouts propagate correctly from
the test runner.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 782a300b-251c-4b6a-9f28-3165c4ee4c4a
📒 Files selected for processing (15)
openmeter/billing/charges/features.goopenmeter/billing/charges/flatfee/service/create.goopenmeter/billing/charges/flatfee/service/creditheninvoice.goopenmeter/billing/charges/flatfee/service/lineengine.goopenmeter/billing/charges/flatfee/service/realizations/correct.goopenmeter/billing/charges/flatfee/service/realizations/credittheninvoice.goopenmeter/billing/charges/flatfee/service/service.goopenmeter/billing/charges/flatfee/service/statemachine.goopenmeter/billing/charges/flatfee/service/triggers.goopenmeter/billing/charges/service/base_test.goopenmeter/billing/charges/service/invoicable_test.goopenmeter/billing/worker/subscriptionsync/service/base_test.goopenmeter/billing/worker/subscriptionsync/service/sync_credittheninvoice_test.goopenmeter/billing/worker/subscriptionsync/service/sync_test.gotest/billing/collection_test.go
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@openmeter/billing/charges/flatfee/service/creditheninvoice.go`:
- Around line 299-333: The current short-circuit in the s.CreditNotesSupported
== false branch exits on any immutable amount change which drops billable work
for extensions; change the logic to only short-circuit for the already-invoiced
overlap: compute the overlap between currentRun (line/invoice period) and the
incoming change and compare the prorationed amounts for that overlap rather than
the whole charge (use currentRun.LineID/currentRun.InvoiceID and billing.LineID
semantics to identify the invoiced period); if the overlap's
AmountAfterProration differs, emit the delete patch via
s.AddInvoicePatch(invoiceupdater.NewDeleteLinePatch(...)) and set
s.Charge.State.AmountAfterProration only for the overlap handling, but do not
return early when there is additional non-invoiced tail—allow creation of
replacement billable work for the newly added tail (i.e., only short-circuit
when the overlap would otherwise be corrected, not when the overall new amount
includes extra tail).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: b185ede0-c310-4bf4-b563-3f71ac84c583
📒 Files selected for processing (3)
openmeter/billing/charges/flatfee/service/creditheninvoice.goopenmeter/billing/worker/subscriptionsync/service/base_test.gotest/credits/credit_then_invoice_test.go
🚧 Files skipped from review as they are similar to previous changes (1)
- openmeter/billing/worker/subscriptionsync/service/base_test.go
f0b63db to
90be488
Compare
Overview
Extends credit-then-invoice subscription sync coverage and the flat-fee CTI behavior needed by those scenarios.
Code Changes
sync_credittheninvoice_test.goas the CTI subscription-sync suite, with converted scenarios for happy path provisioning, prorating, gathering/draft/issued invoice flows, aligned invoicing/cancellation, one-time fees, rate-card tax propagation, instant billing, usage-based updates, and ledger balance expectations.TestDiscountSynchronizationskipped for the known flat-fee CTI discount allocation issue.SuiteBase, including exact charge count matching, expected charge status, settlement mode, periods, invoice-at where applicable, tax config, and subscription linkage.CreditNotesSupportedByLineUpdaterdefault feature flag for flat-fee CTI immutable-proration behavior. With the default unsupported path, immutable prorated lines record the unsupported-credit-note warning without creating replacement billable work for the already-invoiced period.TestUncollectableCollectionfrom subscription sync tests intotest/billing/collection_test.go.Notes for Reviewer
TestRateCardTaxSyncFlatFeeandTestRateCardTaxSyncUsageBasedcases.TestDiscountSynchronizationremains skipped because flat-fee CTI currently allocates credits before percentage discounts are applied, which causes the discounted line to fail with credits-not-consumed validation.Validation
go test -count=1 -tags=dynamic ./openmeter/billing/worker/subscriptionsync/service -run '^TestCreditThenInvoiceScenarios$'make lint-go-fastSummary by CodeRabbit
New Features
Bug Fixes
Improvements
Tests
Documentation