Skip to content

Conversation

@turip
Copy link
Member

@turip turip commented Jan 12, 2026

Overview

Adding the new tables for detailed lines.

A new table billing_invoice_write_schema_levels is added that can be used to toggle which schema version the adapter writes.

Each invoice will have one schema version to signify which tables the invoice is using.

This will allow us to do non-distruptive upgrades where needed.

Summary by CodeRabbit

  • New Features
    • Added detailed invoice line tracking with support for itemized discounts and rounding adjustments.
    • Introduced invoice schema versioning to manage billing system changes.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 12, 2026

📝 Walkthrough

Walkthrough

This PR introduces detailed invoice line tracking to the billing system. It adds three new database tables (detailed lines, their amount discounts, and a schema version tracker), updates the BillingInvoice entity with a schema_level field, and establishes relationships between invoices and detailed line items.

Changes

Cohort / File(s) Summary
Ent Schema Entity Definitions
openmeter/ent/schema/billing.go
Introduces two new entities: BillingStandardInvoiceDetailedLine (with fields for service periods, quantities, pricing, and category) and BillingStandardInvoiceDetailedLineAmountDiscount (for tracking discounts on detailed lines). Adds BillingInvoiceWriteSchemaLevel to track schema versions. Updates BillingInvoice and BillingInvoiceLine with new edges to detailed lines. Adds schema_level field to BillingInvoice. Minor comment rename in BillingInvoiceLineDiscount.
Forward Migration
tools/migrate/migrations/20260112160815_detailed-lines.up.sql
Creates billing_invoice_write_schema_levels table with seed data. Adds schema_level column (default 1) to billing_invoices. Creates billing_standard_invoice_detailed_lines table with comprehensive fields (service periods, quantities, amounts, category, payment terms, etc.) and foreign keys to both billing_invoices and billing_invoice_lines with cascade delete. Creates billing_standard_invoice_detailed_line_amount_discounts table with discount-specific fields and FK to detailed lines. Establishes multiple indexes including conditional unique constraints on child_unique_reference_id.
Reverse Migration
tools/migrate/migrations/20260112160815_detailed-lines.down.sql
Drops all tables and indexes created in forward migration (billing_standard_invoice_detailed_lines, billing_standard_invoice_detailed_line_amount_discounts, billing_invoice_write_schema_levels) and reverts the schema_level column addition on billing_invoices.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested labels

release-note/feature, area/billing

Suggested reviewers

  • chrisgacsal
  • hekike
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title clearly and concisely summarizes the main change: introducing a new detailed line schema for billing invoices.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/detailed-lines-data-schema

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.

@turip turip added the release-note/misc Miscellaneous changes label Jan 12, 2026
Adding the new tables for detailed lines.

A new table billing_invoice_write_schema_levels is added that can be
used to toggle which schema version the adapter writes.

Each invoice will have one schema version to signify which tables
the invoice is using.

This will allow us to do non-distruptive upgrades where needed.
@turip turip force-pushed the feat/detailed-lines-data-schema branch from 071ea70 to 803bfc0 Compare January 12, 2026 16:16
@turip turip marked this pull request as ready for review January 12, 2026 16:19
@turip turip requested a review from a team as a code owner January 12, 2026 16:19
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
openmeter/ent/schema/billing.go (1)

1346-1368: Redundant unique index on primary key.

The id field will be the primary key, which already enforces uniqueness. The explicit unique index in Indexes() is redundant and creates unnecessary overhead.

♻️ Suggested fix
 func (BillingInvoiceWriteSchemaLevel) Indexes() []ent.Index {
-	return []ent.Index{
-		index.Fields("id").Unique(),
-	}
+	return nil
 }
tools/migrate/migrations/20260112160815_detailed-lines.up.sql (1)

1-10: Redundant unique index on primary key column.

Line 10 creates a unique index on id, but id is already the primary key (line 5). PostgreSQL automatically creates a unique index for primary keys, so this explicit index is redundant.

If this migration is auto-generated by ent, consider removing the redundant index definition from the schema (as noted in the billing.go review).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between dd7b67f and 803bfc0.

⛔ Files ignored due to path filters (46)
  • go.sum is excluded by !**/*.sum, !**/*.sum
  • openmeter/ent/db/billinginvoice.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoice/billinginvoice.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoice/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoice_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoice_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoice_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoiceline.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoiceline/billinginvoiceline.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoiceline/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoiceline_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoiceline_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoiceline_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoicewriteschemalevel.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoicewriteschemalevel/billinginvoicewriteschemalevel.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoicewriteschemalevel/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoicewriteschemalevel_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoicewriteschemalevel_delete.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoicewriteschemalevel_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billinginvoicewriteschemalevel_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedline.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedline/billingstandardinvoicedetailedline.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedline/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedline_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedline_delete.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedline_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedline_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedlineamountdiscount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedlineamountdiscount/billingstandardinvoicedetailedlineamountdiscount.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedlineamountdiscount/where.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedlineamountdiscount_create.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedlineamountdiscount_delete.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedlineamountdiscount_query.go is excluded by !**/ent/db/**
  • openmeter/ent/db/billingstandardinvoicedetailedlineamountdiscount_update.go is excluded by !**/ent/db/**
  • openmeter/ent/db/client.go is excluded by !**/ent/db/**
  • openmeter/ent/db/ent.go is excluded by !**/ent/db/**
  • openmeter/ent/db/expose.go is excluded by !**/ent/db/**
  • openmeter/ent/db/hook/hook.go is excluded by !**/ent/db/**
  • openmeter/ent/db/migrate/schema.go is excluded by !**/ent/db/**
  • openmeter/ent/db/mutation.go is excluded by !**/ent/db/**
  • openmeter/ent/db/paginate.go is excluded by !**/ent/db/**
  • openmeter/ent/db/predicate/predicate.go is excluded by !**/ent/db/**
  • openmeter/ent/db/runtime.go is excluded by !**/ent/db/**
  • openmeter/ent/db/setorclear.go is excluded by !**/ent/db/**
  • openmeter/ent/db/tx.go is excluded by !**/ent/db/**
  • tools/migrate/migrations/atlas.sum is excluded by !**/*.sum, !**/*.sum
📒 Files selected for processing (3)
  • openmeter/ent/schema/billing.go
  • tools/migrate/migrations/20260112160815_detailed-lines.down.sql
  • tools/migrate/migrations/20260112160815_detailed-lines.up.sql
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

⚙️ CodeRabbit configuration file

**/*.go: In general when reviewing the Golang code make readability and maintainability a priority, even potentially suggest restructuring the code to improve them.

Performance should be a priority in critical code paths. Anything related to event ingestion, message processing, database operations (regardless of database) should be vetted for potential performance bottlenecks.

Files:

  • openmeter/ent/schema/billing.go
🧬 Code graph analysis (1)
openmeter/ent/schema/billing.go (4)
pkg/framework/entutils/mixins.go (3)
  • AnnotationsMixin (157-159)
  • AnnotationsMixin (162-170)
  • AnnotationsMixin (172-181)
openmeter/billing/invoicedetailedline.go (1)
  • FlatFeeCategoryRegular (21-21)
openmeter/productcatalog/price.go (1)
  • PaymentTermType (24-24)
openmeter/billing/discount.go (1)
  • DiscountReason (147-152)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: Quickstart
  • GitHub Check: E2E
  • GitHub Check: Code Generators
  • GitHub Check: Analyze (go)
🔇 Additional comments (11)
openmeter/ent/schema/billing.go (8)

477-478: LGTM!

Clean edge addition with cascade delete annotation. The naming detailed_lines_v2 is clear about versioning while the existing detailed_lines edge remains for backward compatibility.


701-704: TODO comment noted.

Good practice to document the planned rename. The comment correctly indicates this should be renamed alongside the DB table for consistency.


873-878: Comment doesn't match field definition.

The comment says "Quantity is optional as for usage-based billing..." but the field is defined as required (no Optional() or Nillable()). This differs from BillingInvoiceLine where quantity is optional.

If detailed lines are only created after calculation (when quantity is known), then the field should be required and the comment should be updated. Otherwise, if the field should actually be optional, the definition needs to change.


910-920: Good use of StorageKey for the partial index.

The explicit storage key billingstdinvdetailedline_ns_parent_child_id keeps the index name manageable. The partial index with the deleted_at IS NULL filter is a nice pattern for soft-delete scenarios.


923-937: LGTM!

The edges are well-structured: both billing_invoice and billing_invoice_line edges are required (makes sense for detailed child lines), and the amount_discounts edge properly cascades deletes.


940-1000: LGTM!

Nicely mirrors the existing BillingInvoiceLineDiscount pattern. Good reuse of BillingInvoiceLineDiscountBase mixin and consistent index structure with the explicit storage key for the partial unique index.


1166-1168: LGTM!

Simple and clean addition. The default of 1 aligns with the seeded value in the migration.


1205-1206: LGTM!

The edge to BillingStandardInvoiceDetailedLine with cascade delete is properly defined, allowing direct traversal from invoice to detailed lines.

tools/migrate/migrations/20260112160815_detailed-lines.up.sql (2)

14-61: Well-structured table with appropriate constraints and indexes.

The billing_standard_invoice_detailed_lines table looks solid:

  • FKs properly cascade on delete
  • GIN index on annotations for JSON queries
  • Partial unique index for child_unique_reference_id with soft-delete awareness

One observation: the id column has both a PRIMARY KEY constraint (line 44) and a unique index (lines 50-51). The unique index is redundant here too, but since this is likely auto-generated by ent, it's a minor nit.


62-87: LGTM!

The discounts table follows the established pattern with appropriate FK to the parent detailed lines table and consistent indexing strategy.

tools/migrate/migrations/20260112160815_detailed-lines.down.sql (1)

1-32: LGTM!

The down migration correctly reverses all changes in the proper order:

  1. Child table indexes and table dropped first
  2. Parent table indexes and table dropped second
  3. Column modification reverted
  4. Config table dropped last

The explicit DROP INDEX statements before DROP TABLE are technically redundant (dropping a table auto-drops its indexes), but they don't cause issues and the migration will work correctly.

@turip turip enabled auto-merge (squash) January 12, 2026 16:25
@turip turip merged commit 5768aa7 into main Jan 12, 2026
28 of 29 checks passed
@turip turip deleted the feat/detailed-lines-data-schema branch January 12, 2026 17:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release-note/misc Miscellaneous changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants