Skip to content

feat(feature): unit config#4001

Draft
hekike wants to merge 2 commits intomainfrom
feat/unit-config
Draft

feat(feature): unit config#4001
hekike wants to merge 2 commits intomainfrom
feat/unit-config

Conversation

@hekike
Copy link
Copy Markdown
Contributor

@hekike hekike commented Mar 24, 2026

Summary

Adds UnitConfig — a unit conversion layer that transforms raw metered quantities into billing-ready units before pricing. This replaces the need for separate PackagePrice and DynamicPrice types by combining a generic conversion operation with standard pricing (Unit, Tiered, etc.).

What it does

UnitConfig sits between the meter and the pricer:

Meter (raw units) → UnitConfig (convert + round) → Price (bill converted units)

Core fields:

  • operation: divide or multiply
  • conversion_factor: positive non-zero decimal
  • rounding: ceiling, floor, half_up, or none (default)
  • precision: decimal places for rounding (default 0)
  • display_unit: human-readable label (e.g., "GB", "M tokens")

Examples:

  • Bill per million tokens: divide by 1,000,000 + ceiling rounding + UnitPrice → package-style billing
  • Cost + 20% margin: multiply by 1.2 + none rounding + UnitPrice → dynamic pricing
  • Meter bytes, bill GB: divide by 1e9 + ceiling + displayUnit: "GB"

Changes

API layer:

  • TypeSpec definitions for UnitConfig, UnitConfigOperation, UnitConfigRoundingMode, InvoiceUsageQuantityDetail
  • unitConfig field on FeatureUnitConfig (features), RateCardUsageBased (rate cards), and invoice lines
  • Generated OpenAPI spec, Go/JS/Python SDK clients

Domain layer (openmeter/productcatalog):

  • UnitConfig type with Convert() (precise, for entitlements) and ConvertAndRound() (for invoicing)
  • Comprehensive validation (operation, factor > 0, rounding mode, precision)
  • HTTP mapping (API ↔ domain conversion)

Billing integration:

  • UnitConfigConversion mutator: applies conversion before pricing
  • UnitConfigRounding mutator: applies rounding for invoiced quantities
  • UsageQuantityDetail on invoice lines: full audit trail (raw → converted → invoiced)
  • UnitConfig snapshotted on invoice lines for historical accuracy

Database:

  • unit_config JSONB column added to: features, plan_rate_cards, addon_rate_cards, subscription_items, billing_invoice_usage_based_line_configs
  • Ent schema + migration

Tests:

  • Unit tests for conversion, rounding modes, precision, validation
  • Billing integration tests: divide+ceil, multiply, tiered pricing, floor rounding, nil (no-op)

Live validation against running server

Validated end-to-end on localhost:8888 with the sandbox billing app.

Setup: Created customer → subscribed to plan_ppm (pay-per-million tokens) → ingested 1,500,001 raw tokens → triggered invoicing.

Plan rate card config:

{
  "unitConfig": {
    "operation": "divide",
    "conversion_factor": "1000000",
    "display_unit": "M tokens",
    "rounding": "ceiling"
  },
  "price": { "amount": "1.5", "type": "unit" }
}

Invoice line result:

{
  "quantity": "2",
  "totals": { "total": "3" },
  "usageQuantityDetail": {
    "rawQuantity": "1500001",
    "convertedQuantity": "1.500001",
    "invoicedQuantity": "2",
    "appliedUnitConfig": {
      "operation": "divide",
      "conversion_factor": "1000000",
      "display_unit": "M tokens",
      "rounding": "ceiling"
    }
  }
}

Validation:

Step Expected Actual Status
Raw meter value 1,500,001 rawQuantity: 1500001
Conversion (÷1M) 1.500001 convertedQuantity: 1.500001
Rounding (ceiling) 2 invoicedQuantity: 2
Pricing (2 × $1.50) $3.00 total: 3
UnitConfig snapshot preserved appliedUnitConfig present

Test plan

  • Unit tests: openmeter/productcatalog/unitconfig_test.go — conversion, rounding, validation
  • Billing rating tests: rate/unit_test.go, rate/tieredgraduated_test.go — UnitConfig with pricing
  • Integration tests: test/billing/unitconfig_test.go — full gathering → invoice pipeline
  • Manual validation: live server e2e with sandbox billing app

hekike and others added 2 commits March 24, 2026 13:13
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@hekike hekike requested review from tothandras and turip March 24, 2026 20:39
@hekike hekike added the release-note/feature Release note: Exciting New Features label Mar 24, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 24, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5b091b33-5af6-4573-8a42-75ba4650e51a

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/unit-config

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/feature Release note: Exciting New Features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant