chore: flat-fee run support#4350
Conversation
📝 WalkthroughWalkthroughThis PR refactors flat-fee charge realizations into a run-scoped model (CurrentRun + PriorRuns), adds ProvisionCurrentRun and FetchCurrentRunDetailedLines adapter methods, changes invoiced-usage to use input structs, updates DB schema/mapping, and cascades changes through services, ledger, and tests. ChangesFlat-Fee Realization Run Domain Refactor
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
openmeter/billing/charges/service/lineage_test.go (1)
131-132: ⚡ Quick winAvoid order-coupled assertions on
CreditRealizations.These asserts depend on
[0]/[1]staying stable. Small ordering shifts can make this flaky even when behavior is correct. Prefer asserting by realization identity/properties (e.g., build expected-by-origin/amount map first).🤖 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 `@openmeter/billing/charges/service/lineage_test.go` around lines 131 - 132, The two assertions in the test are order-dependent because they index CreditRealizations by [0] and [1]; instead, build a lookup (map) keyed by each realization's ID or by a stable key like origin+amount from charge.Realizations.CurrentRun.CreditRealizations, then call s.assertInitialLineage(...) for each expected realization by retrieving the matching realization from that map and passing its ID and expected properties; update the assertions that reference CreditRealizations[0]/[1] to use the lookup so tests no longer rely on ordering.
🤖 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/adapter/mapper.go`:
- Around line 68-74: The mapping loop can leave realizations.CurrentRun nil when
entity.CurrentRealizationRunID is set but no dbRun matched; after the loop in
the mapper function that builds the realizations (the code referencing
entity.CurrentRealizationRunID, dbRun.ID, realizations.CurrentRun,
realizations.PriorRuns), add a fail-fast check: if
entity.CurrentRealizationRunID != nil && realizations.CurrentRun == nil then
return an explicit error (or propagate one) indicating the current realization
run id was not found; update the mapper's signature to return an error if
necessary and ensure callers handle that error instead of proceeding with a nil
CurrentRun.
In `@openmeter/ledger/customerbalance/calculation.go`:
- Around line 41-45: When computing realized credits in the block referencing
charge.Realizations.CurrentRun and CreditRealizations.Sum(), skip credits from
runs that have been voided (mirror the usage-based logic). Update the
early-return logic so that after checking CurrentRun != nil you also check the
run's void flag or method (e.g., CurrentRun.Voided == true or
CurrentRun.IsVoided()) and return alpacadecimal.Zero for voided runs; otherwise
return charge.Realizations.CurrentRun.CreditRealizations.Sum().
---
Nitpick comments:
In `@openmeter/billing/charges/service/lineage_test.go`:
- Around line 131-132: The two assertions in the test are order-dependent
because they index CreditRealizations by [0] and [1]; instead, build a lookup
(map) keyed by each realization's ID or by a stable key like origin+amount from
charge.Realizations.CurrentRun.CreditRealizations, then call
s.assertInitialLineage(...) for each expected realization by retrieving the
matching realization from that map and passing its ID and expected properties;
update the assertions that reference CreditRealizations[0]/[1] to use the lookup
so tests no longer rely on ordering.
🪄 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: 99637358-9a68-4b1f-bc68-f3ec5425833f
⛔ Files ignored due to path filters (37)
openmeter/ent/db/billinginvoice.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoice/billinginvoice.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoice/where.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoice_create.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoice_query.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoice_update.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoiceline.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoiceline/billinginvoiceline.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoiceline/where.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoiceline_create.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoiceline_query.gois excluded by!**/ent/db/**openmeter/ent/db/billinginvoiceline_update.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeerun.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeerun/chargeflatfeerun.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeerun/where.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeerun_create.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeerun_query.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeerun_update.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeeruninvoicedusage.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeeruninvoicedusage/chargeflatfeeruninvoicedusage.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeeruninvoicedusage/where.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeeruninvoicedusage_create.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeeruninvoicedusage_query.gois excluded by!**/ent/db/**openmeter/ent/db/chargeflatfeeruninvoicedusage_update.gois excluded by!**/ent/db/**openmeter/ent/db/chargeusagebasedruninvoicedusage.gois excluded by!**/ent/db/**openmeter/ent/db/chargeusagebasedruninvoicedusage/chargeusagebasedruninvoicedusage.gois excluded by!**/ent/db/**openmeter/ent/db/chargeusagebasedruninvoicedusage/where.gois excluded by!**/ent/db/**openmeter/ent/db/chargeusagebasedruninvoicedusage_create.gois excluded by!**/ent/db/**openmeter/ent/db/chargeusagebasedruninvoicedusage_query.gois excluded by!**/ent/db/**openmeter/ent/db/chargeusagebasedruninvoicedusage_update.gois excluded by!**/ent/db/**openmeter/ent/db/client.gois excluded by!**/ent/db/**openmeter/ent/db/entmixinaccessor.gois excluded by!**/ent/db/**openmeter/ent/db/migrate/schema.gois excluded by!**/ent/db/**openmeter/ent/db/mutation.gois excluded by!**/ent/db/**openmeter/ent/db/runtime.gois excluded by!**/ent/db/**openmeter/ent/db/setorclear.gois excluded by!**/ent/db/**tools/migrate/migrations/atlas.sumis excluded by!**/*.sum,!**/*.sum
📒 Files selected for processing (30)
openmeter/billing/charges/flatfee/adapter.goopenmeter/billing/charges/flatfee/adapter/charge.goopenmeter/billing/charges/flatfee/adapter/detailedline.goopenmeter/billing/charges/flatfee/adapter/detailedline_test.goopenmeter/billing/charges/flatfee/adapter/mapper.goopenmeter/billing/charges/flatfee/adapter/realizationrun.goopenmeter/billing/charges/flatfee/adapter/usage.goopenmeter/billing/charges/flatfee/charge.goopenmeter/billing/charges/flatfee/realizationrun.goopenmeter/billing/charges/flatfee/service/create.goopenmeter/billing/charges/flatfee/service/creditsonly.goopenmeter/billing/charges/flatfee/service/invoice.goopenmeter/billing/charges/flatfee/service/payment.goopenmeter/billing/charges/flatfee/service/realizations/service.goopenmeter/billing/charges/models/invoicedusage/mixin.goopenmeter/billing/charges/models/invoicedusage/stdinvoice.goopenmeter/billing/charges/service/invoicable_test.goopenmeter/billing/charges/service/lineage_test.goopenmeter/billing/charges/usagebased/service/creditheninvoice_test.goopenmeter/billing/charges/usagebased/service/run/invoice.goopenmeter/billing/charges/usagebased/service/run/payment_test.goopenmeter/ent/schema/billing.goopenmeter/ent/schema/chargesflatfee.goopenmeter/ledger/chargeadapter/flatfee.goopenmeter/ledger/chargeadapter/flatfee_test.goopenmeter/ledger/chargeadapter/usagebased_test.goopenmeter/ledger/customerbalance/calculation.gotest/credits/sanity_test.gotools/migrate/migrations/20260512114051_flatfee_run_domain_fields.down.sqltools/migrate/migrations/20260512114051_flatfee_run_domain_fields.up.sql
💤 Files with no reviewable changes (5)
- openmeter/ledger/chargeadapter/usagebased_test.go
- openmeter/billing/charges/models/invoicedusage/mixin.go
- openmeter/billing/charges/usagebased/service/run/invoice.go
- openmeter/billing/charges/usagebased/service/run/payment_test.go
- openmeter/billing/charges/usagebased/service/creditheninvoice_test.go
There was a problem hiding this comment.
🧹 Nitpick comments (1)
openmeter/ledger/customerbalance/service_test.go (1)
206-231: ⚡ Quick winNice coverage—please add the nil
CurrentRuncase too.This test covers the voided-run path well, but the paired behavior (
CurrentRun == nil=> zero realized credits) should get its own regression test in the same block.As per coding guidelines, "
**/*_test.go: Make sure the tests are comprehensive and cover the changes."🤖 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 `@openmeter/ledger/customerbalance/service_test.go` around lines 206 - 231, Add a paired test that asserts realized credits are zero when the flatfee charge has Realizations with CurrentRun == nil: create a new test (e.g., TestImpactRealizedCreditsSkipsNilCurrentRun) that constructs the same input via NewImpact/charges.NewCharge with flatfee.Realizations where CurrentRun is nil and a positive overall balance (alpacadecimal.NewFromInt(50)), assert no error from NewImpact and require.True that impact.RealizedCredits().Equal(alpacadecimal.Zero); reference NewImpact, charges.NewCharge, flatfee.Realizations and the CurrentRun field when locating where to add this test.
🤖 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.
Nitpick comments:
In `@openmeter/ledger/customerbalance/service_test.go`:
- Around line 206-231: Add a paired test that asserts realized credits are zero
when the flatfee charge has Realizations with CurrentRun == nil: create a new
test (e.g., TestImpactRealizedCreditsSkipsNilCurrentRun) that constructs the
same input via NewImpact/charges.NewCharge with flatfee.Realizations where
CurrentRun is nil and a positive overall balance (alpacadecimal.NewFromInt(50)),
assert no error from NewImpact and require.True that
impact.RealizedCredits().Equal(alpacadecimal.Zero); reference NewImpact,
charges.NewCharge, flatfee.Realizations and the CurrentRun field when locating
where to add this test.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 8236f01a-d83d-43e9-a857-a773ca268156
📒 Files selected for processing (4)
openmeter/billing/charges/flatfee/adapter/mapper.goopenmeter/billing/charges/flatfee/realizationrun.goopenmeter/ledger/customerbalance/calculation.goopenmeter/ledger/customerbalance/service_test.go
🚧 Files skipped from review as they are similar to previous changes (3)
- openmeter/ledger/customerbalance/calculation.go
- openmeter/billing/charges/flatfee/adapter/mapper.go
- openmeter/billing/charges/flatfee/realizationrun.go
Summary
Adds domain-level run support for flat-fee charge realizations.
Flat-fee realizations are now grouped under a current run, with prior runs available for future prorating workflows. Credit allocations, invoice usage, detailed lines, and payment state are represented through the current run instead of directly on the flat-fee charge realization state.
Behavior Changes
Migration
Existing flat-fee run data is migrated into the new run shape without backfilling invoice line references, since flat-fee credit-then-invoice was not active for existing production data.
Summary by CodeRabbit
Refactor
Data model / Migrations
Tests