Skip to content

Conversation

@turip
Copy link
Member

@turip turip commented Aug 17, 2025

Overview

LineChildren type was used for two reasons:

  • Collection of invoice lines (with the option of being expanded, thus using mo.Option)
  • Collection of detailed lines (expand does not make sense here, if a line is fetched all detailed lines are fetched)

Historically this reuse made sense as we were having the progressively billed lines represented as Lines, but now the progressive billing part is captured in a SplitLineGroup, thus we don't need this amount of generic coding.

This patch makes sure that we have separate types as preparation for the introduction of specific DetailedLine types where the collection would need to either contain Line or DetailedLine types.

Motivation

Why should I care? You should not, but this #3219 would require to split the Lines/DetailedLines before so that the adapter change is reviewable (e.g not a rewrite, it just contains the new mapping rules)

The split cannot happen until the same type is used for both invoice level Lines and the forhtcoming DetailedLines (as in it's current state the code would allow to have children of detailed lines, which is a logic error, but allowed by the DB schema).

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 17, 2025

📝 Walkthrough

Walkthrough

The PR replaces the LineChildren wrapper with a new InvoiceLines type and converts Line.Children from an optional wrapper to a plain slice across the billing domain. Constructors and method usages are updated accordingly. Supporting adapters, services, mappers, and tests are adjusted to iterate over slices directly (no OrEmpty/MustGet), and lo.Map is used where applicable.

Changes

Cohort / File(s) Summary
Core type refactor
openmeter/billing/invoice.go, openmeter/billing/invoiceline.go
Introduces InvoiceLines (mo.Option-backed) for Invoice.Lines; converts Line.Children to []*Line; adds/updates methods for InvoiceLines; removes LineChildren wrapper behaviors; updates validations and helpers to slice-based logic.
API usage/update to InvoiceLines
openmeter/billing/adapter/invoice.go, openmeter/billing/httpdriver/invoice.go, openmeter/billing/httpdriver/invoiceline.go, openmeter/billing/service/invoice.go, openmeter/notification/internal/rule.go
Replaces NewLineChildren/LineChildren with NewInvoiceLines/InvoiceLines; updates handler method signature to return billing.InvoiceLines.
Children slice semantics in adapters/mappers
openmeter/billing/adapter/invoicelinediff.go, openmeter/billing/adapter/invoicelinediff_test.go, openmeter/billing/adapter/invoicelinemapper.go, openmeter/billing/adapter/invoicelines.go
Removes OrEmpty/IsPresent usage; iterates over slices directly; replaces Append with append; adjusts delete/creation paths; improves error wrapping.
Service calc and line service updates
openmeter/billing/service/lineservice/service.go, openmeter/billing/service/invoicecalc/gatheringrealtime.go
Switches to NewInvoiceLines; iterates over line.Children directly; uses lo.Map for child transformations; removes presence guards.
Tests updated for slice access and constructors
openmeter/billing/invoice_test.go, openmeter/billing/worker/subscription/sync_test.go, test/billing/*
Replaces MustGet/OrEmpty with direct slice access and len checks; replaces children Map with lo.Map; uses NewInvoiceLines in place of NewLineChildren.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch reafactor/disambiguate-line-children

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@turip turip added release-note/ignore Ignore this change when generating release notes area/billing labels Aug 17, 2025
@turip turip force-pushed the reafactor/disambiguate-line-children branch from 09c5016 to 9b4d970 Compare August 17, 2025 16:09
LineChildren type was used for two reasons:
- Collection of invoice lines (with the option of being expanded)
- Collection of detailed lines (expand does not make sense)

This patch makes sure that we have seperate types as perparation for
the introduction of specific detailed line types.
@turip turip force-pushed the reafactor/disambiguate-line-children branch from 9b4d970 to daa356d Compare August 17, 2025 16:13
@turip turip marked this pull request as ready for review August 17, 2025 16:13
@turip turip requested a review from a team as a code owner August 17, 2025 16:13
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: 2

🧹 Nitpick comments (6)
openmeter/billing/invoice.go (1)

523-614: Optional: expose IsEmpty() to clarify “present vs. empty” semantics

Given NewInvoiceLines may wrap nil with Some(nil), having a small helper improves readability and avoids subtle presence checks across the codebase.

Proposed addition (outside this hunk, in the same type):

func (c InvoiceLines) IsEmpty() bool {
	s, ok := c.Get()
	return !ok || len(s) == 0
}
openmeter/billing/worker/subscription/sync_test.go (1)

3496-3502: Repeat mapping: OK, consider extracting helper to reduce duplication

You perform the same child ConfigID clearing in multiple places. A small helper could simplify tests and reduce churn if the shape changes again.

Example:

func clearChildrenFlatFeeConfigIDs(line *billing.Line) *billing.Line {
	line.Children = lo.Map(line.Children, func(child *billing.Line, _ int) *billing.Line {
		if child.FlatFee != nil {
			child.FlatFee.ConfigID = ""
		}
		return child
	})
	return line
}
openmeter/billing/service/invoicecalc/gatheringrealtime.go (1)

17-34: Using lo.Map over children aligns with slice-based refactor; consider clock usage for determinism

  • The switch from the wrapper’s Map to lo.Map over the slice is correct and type-safe with Children as a plain slice.
  • Optional: replace time.Now() with the project clock to maintain determinism in tests and consistency with the rest of the codebase.

Apply this diff to use the shared clock and normalize to UTC:

@@
-import (
-	"time"
-
-	"github.com/oklog/ulid/v2"
-	"github.com/samber/lo"
-
-	"github.com/openmeterio/openmeter/openmeter/billing"
-)
+import (
+	"time"
+
+	"github.com/oklog/ulid/v2"
+	"github.com/samber/lo"
+	"github.com/openmeterio/openmeter/pkg/clock"
+
+	"github.com/openmeterio/openmeter/openmeter/billing"
+)
@@
-			if child.CreatedAt.IsZero() {
-				child.CreatedAt = time.Now()
-			}
+			if child.CreatedAt.IsZero() {
+				child.CreatedAt = clock.Now().In(time.UTC)
+			}
@@
-			if child.UpdatedAt.IsZero() {
-				child.UpdatedAt = time.Now()
-			}
+			if child.UpdatedAt.IsZero() {
+				child.UpdatedAt = clock.Now().In(time.UTC)
+			}
openmeter/billing/service/lineservice/service.go (2)

158-171: Avoid variable shadowing: rename loop variable

The inner loop variable named line shadows the function parameter line, which harms readability and can lead to bugs on future edits. Rename the inner binding to child.

Apply this diff:

-    for _, line := range line.Children {
-        if line.DeletedAt != nil {
+    for _, child := range line.Children {
+        if child.DeletedAt != nil {
             continue
         }
 
-        lineSvc, err := s.FromEntity(line)
+        lineSvc, err := s.FromEntity(child)
         if err != nil {
-            return fmt.Errorf("creating line service: %w", err)
+            return fmt.Errorf("creating line service: %w", err)
         }
 
         if err := lineSvc.UpdateTotals(); err != nil {
-            return fmt.Errorf("updating totals for line[%s]: %w", line.ID, err)
+            return fmt.Errorf("updating totals for line[%s]: %w", child.ID, err)
         }
     }

173-179: Fix typo in comment

Minor: “syncorinzed” -> “synchronized”.

Apply this diff:

-// The usageBasedLine will never be syncorinzed directly to stripe or other apps, only the detailed lines.
+// The usageBasedLine will never be synchronized directly to Stripe or other apps, only the detailed lines.
openmeter/billing/httpdriver/invoiceline.go (1)

186-202: Consider nil safety for direct slice access.

The function now operates on a direct slice instead of an optional wrapper. While the implementation is correct, consider adding a nil check at the beginning for defensive programming:

 func mapDetailedLinesToAPI(children billing.LineChildren) (*[]api.InvoiceDetailedLine, error) {
+	if children == nil {
+		return &[]api.InvoiceDetailedLine{}, nil
+	}
 	out := make([]api.InvoiceDetailedLine, 0, len(children))

This would make the function more robust against potential nil inputs.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled
  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 0805745 and daa356d.

📒 Files selected for processing (20)
  • openmeter/billing/adapter/invoice.go (3 hunks)
  • openmeter/billing/adapter/invoicelinediff.go (4 hunks)
  • openmeter/billing/adapter/invoicelinediff_test.go (1 hunks)
  • openmeter/billing/adapter/invoicelinemapper.go (1 hunks)
  • openmeter/billing/adapter/invoicelines.go (1 hunks)
  • openmeter/billing/httpdriver/invoice.go (1 hunks)
  • openmeter/billing/httpdriver/invoiceline.go (7 hunks)
  • openmeter/billing/invoice.go (6 hunks)
  • openmeter/billing/invoice_test.go (1 hunks)
  • openmeter/billing/invoiceline.go (7 hunks)
  • openmeter/billing/service/invoice.go (4 hunks)
  • openmeter/billing/service/invoicecalc/gatheringrealtime.go (2 hunks)
  • openmeter/billing/service/lineservice/service.go (2 hunks)
  • openmeter/billing/worker/subscription/sync_test.go (4 hunks)
  • openmeter/notification/internal/rule.go (1 hunks)
  • test/billing/adapter_test.go (10 hunks)
  • test/billing/discount_test.go (4 hunks)
  • test/billing/invoice_test.go (8 hunks)
  • test/billing/tax_test.go (1 hunks)
  • test/billing/ubpflatfee_test.go (2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (13)
openmeter/notification/internal/rule.go (2)
openmeter/billing/invoice.go (1)
  • NewInvoiceLines (527-534)
openmeter/billing/invoiceline.go (1)
  • Line (305-319)
openmeter/billing/httpdriver/invoice.go (2)
openmeter/billing/service/lineservice/service.go (1)
  • Lines (223-223)
openmeter/billing/invoice.go (1)
  • NewInvoiceLines (527-534)
openmeter/billing/adapter/invoicelines.go (1)
openmeter/billing/service/lineservice/service.go (1)
  • Lines (223-223)
openmeter/billing/service/lineservice/service.go (3)
openmeter/billing/invoice.go (1)
  • NewInvoiceLines (527-534)
openmeter/billing/invoiceline.go (1)
  • Line (305-319)
openmeter/billing/totals.go (1)
  • Totals (9-26)
openmeter/billing/service/invoicecalc/gatheringrealtime.go (1)
openmeter/billing/invoiceline.go (1)
  • Line (305-319)
openmeter/billing/service/invoice.go (1)
openmeter/billing/invoice.go (2)
  • NewInvoiceLines (527-534)
  • InvoiceLines (523-525)
test/billing/adapter_test.go (3)
openmeter/ent/db/billinginvoiceline/where.go (3)
  • ID (17-19)
  • ChildUniqueReferenceID (178-180)
  • Amount (108-110)
openmeter/billing/invoiceline.go (1)
  • Line (305-319)
openmeter/billing/invoicelinediscount.go (1)
  • LineDiscounts (352-355)
openmeter/billing/worker/subscription/sync_test.go (2)
openmeter/billing/invoiceline.go (1)
  • Line (305-319)
openmeter/billing/invoice.go (1)
  • NewInvoiceLines (527-534)
openmeter/billing/httpdriver/invoiceline.go (3)
openmeter/billing/invoiceline.go (2)
  • LineChildren (710-710)
  • InvoiceLineTypeFee (32-32)
openmeter/billing/invoice.go (3)
  • Invoice (331-342)
  • InvoiceLines (523-525)
  • NewInvoiceLines (527-534)
openmeter/ent/db/ent.go (1)
  • ValidationError (259-262)
openmeter/billing/adapter/invoice.go (1)
openmeter/billing/invoice.go (2)
  • InvoiceLines (523-525)
  • NewInvoiceLines (527-534)
openmeter/billing/invoiceline.go (1)
pkg/slicesx/map.go (1)
  • Map (8-21)
test/billing/invoice_test.go (4)
openmeter/billing/service/lineservice/service.go (2)
  • Lines (223-223)
  • Line (206-221)
openmeter/billing/invoice.go (1)
  • NewInvoiceLines (527-534)
openmeter/billing/invoiceline.go (2)
  • Line (305-319)
  • NewLineChildren (712-719)
openmeter/billing/totals.go (1)
  • Totals (9-26)
openmeter/billing/invoice.go (3)
openmeter/billing/invoiceline.go (3)
  • InvoiceLineTypeUsageBased (34-34)
  • Line (305-319)
  • InvoiceLineStatusValid (48-48)
pkg/models/validator.go (1)
  • Validate (16-26)
openmeter/billing/validationissue.go (1)
  • ValidationWithFieldPrefix (131-140)
⏰ 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). (7)
  • GitHub Check: Artifacts / Container image
  • GitHub Check: Lint
  • GitHub Check: Migration Checks
  • GitHub Check: Code Generators
  • GitHub Check: Test
  • GitHub Check: Build
  • GitHub Check: Analyze (go)
🔇 Additional comments (59)
openmeter/billing/invoice.go (6)

335-335: Good API rename: Lines → InvoiceLines

Switching the top-level lines collection to the new InvoiceLines wrapper aligns with the stated PR objective and improves semantic clarity at the invoice level.


413-416: Direct iteration over child slice looks correct and nil-safe

Iterating over line.Children directly (plain slice) removes wrapper ceremony and remains safe even when nil. This matches the refactor goal and keeps FlattenLinesByID straightforward.


480-481: Re-wrapping after in-place sort is fine; double-check presence semantics

Re-wrapping the in-place sorted slice via NewInvoiceLines(lines) is fine. One nuance: NewInvoiceLines coerces empty to nil and returns mo.Some(nil). That means SortLines will still treat “empty but present” as present. If any call sites rely on “absent means not expanded,” ensure they don't accidentally consider “present but empty (nil slice)” as expanded.

Would you like a helper like InvoiceLines.IsEmpty() to make this intent explicit across call sites?


515-517: Recursive child sorting for usage-based lines LGTM

Recursively calling sortLines on usage-based children keeps ordering consistent across hierarchies. The nil child slice path is safe.


1051-1052: SimulateInvoiceInput: Lines type change is consistent

Switching to InvoiceLines in the simulation input matches the new model and keeps API symmetry with Invoice.Lines.


1083-1086: Validation via length-only check is clearer

Requiring lines by checking len(i.Lines.OrEmpty()) == 0 reads better than optional presence checks, especially with NewInvoiceLines coercion.

openmeter/billing/invoice_test.go (1)

72-75: Test change to direct slice access is correct

Accessing children directly as a slice matches the refactor. Given this test expects two children on lines[1], this is a safe and readable change.

openmeter/notification/internal/rule.go (1)

187-187: Constructor rename to NewInvoiceLines aligns with new API

The switch from NewLineChildren to NewInvoiceLines for invoice-level collections is correct and consistent with the new type.

openmeter/billing/worker/subscription/sync_test.go (2)

3488-3492: Swapping to lo.Map over direct slice is the right move

This mirrors the model change (children -> []*Line) and keeps mutation localized. Good use of a transformation rather than manual loops.


4131-4132: Direct child assertions are clearer and safe (length asserted above)

Using direct indexing after s.Len(line.Children, 1) is concise and robust.

openmeter/billing/httpdriver/invoice.go (1)

386-387: SimulateInvoice: NewInvoiceLines usage is correct

Constructing the request with billing.NewInvoiceLines(lines) matches the new public API. Downstream MapInvoiceToAPI already handles sorting and mapping correctly.

test/billing/tax_test.go (1)

263-268: Dropping OrEmpty() for direct child slice access is correct

Directly using the slice aligns with the refactor. The preceding length assertion makes the nil vs empty distinction safe here.

openmeter/billing/adapter/invoicelinediff_test.go (1)

464-467: Iterating over direct children slice is safe and clearer

Ranging a nil slice yields zero iterations, so this is behaviorally equivalent without the wrapper and simpler to read.

openmeter/billing/adapter/invoicelines.go (3)

159-160: Flattening children via direct slice is correct (nil-safe)

Returning input.Lines[idx].Children works fine—lo.FlatMap gracefully handles nil returns.


165-167: Parent linkage restoration with direct slice looks good

Re-linking ParentLineID by iterating line.Children keeps semantics intact post-refactor and remains nil-safe.


159-160: No leftover wrapper calls found
Ran the provided ripgrep searches for .Children.(OrEmpty|MustGet) and NewLineChildren()—no matches were returned. It looks like all old wrapper usages have been removed across the repo.

test/billing/ubpflatfee_test.go (2)

113-115: Direct child indexing in tests: LGTM

Switching from MustGet() to length assertion + direct indexing matches the new slice semantics and keeps the test robust.


194-196: Repeat of the pattern: LGTM

Same here—asserting length then indexing is concise and safe.

openmeter/billing/service/invoicecalc/gatheringrealtime.go (1)

7-7: New lo import is justified

Used by the new lo.Map on children; all good.

openmeter/billing/adapter/invoicelinemapper.go (2)

73-77: Switch to slice append for Children is correct and nil-safe

Using append on a slice is the right replacement for the previous wrapper’s Append; it handles nil slices and avoids allocation when capacity allows. No functional issues spotted here.


73-79: Confirm downstream expectations for nil vs empty Children

With the wrapper removed, parents with no children will have Children == nil (and thus omitted in JSON due to omitempty). Verify that API consumers and serializers rely on this behavior and don’t require an explicit empty array.

If helpful, I can scan the codebase for any logic that assumes non-nil Children and propose follow-ups.

openmeter/billing/adapter/invoicelinediff.go (3)

182-194: Creation flow for new children reads well

Iterating over workItem.Children directly is consistent with the new slice semantics; range over nil is a no-op, so this path remains safe.


258-261: Good: delete children on parent deletion

Executing deleteLineChildren unconditionally in the deletion branch is the correct behavior and avoids stale descendants.


364-372: Direct iteration over DBState.Children is consistent

Iterating the saved DB state’s children is correct for deletion. No issues spotted.

test/billing/discount_test.go (4)

160-163: LGTM: direct access to Children in tests

Replacing OrEmpty() with direct indexing is correct under the new slice semantics.


364-365: LGTM: zero-children assertion

Using require.Len(invoiceLine.Children, 0) aligns with nil-safe slice semantics.


406-408: LGTM: detailed line access

Accessing the first child directly reflects the updated representation. No issues.


438-440: LGTM: detailed line access

Consistent with other changes; test intent remains the same.

openmeter/billing/service/lineservice/service.go (2)

150-151: Correct switch to NewInvoiceLines for association

Building Invoice.Lines via NewInvoiceLines with existing + new lineEntities is correct and preserves the optional presence semantics for top-level invoice lines.


182-190: LGTM: totals aggregation over Children

Mapping over line.Children to sum Totals while skipping deleted lines is consistent and clear.

openmeter/billing/adapter/invoice.go (5)

556-556: Initialize updatedLines as InvoiceLines

Good adaptation to the new API.


570-571: Wrap upsert results via NewInvoiceLines

Correct construction and preserves nil-for-empty semantics for tests/equality.


578-581: Filter out deleted lines while preserving option semantics

This keeps response payloads aligned with expand flags. Looks good.


764-765: mapInvoiceFromDB: NewInvoiceLines assignment is correct

Consistent with the public API change; no issues spotted.


555-582: Verify removal of legacy children logic

The grep results show that references to the old LineChildren/NewLineChildren type and calls to Children.OrEmpty() still exist throughout both production code and tests. If this migration’s goal is to consolidate around the new top-level InvoiceLines API, please review and clean up any remaining legacy constructs.

Key locations to inspect:

  • Domain model (children type definitions and constructors):
    openmeter/billing/invoiceline.go:710–714 (type LineChildren []*Line, func NewLineChildren(...))
    openmeter/billing/invoiceline.go:386–394 (defaulting to NewLineChildren(nil))
  • Adapter logic:
    openmeter/billing/adapter/invoice.go:555–582 (snippet under review still filters on updatedLines.OrEmpty())
  • Core services & HTTP drivers:
    • Multiple occurrences in openmeter/billing/service/... and openmeter/billing/httpdriver/...
  • Tests:
    test/billing/adapter_test.go
    test/billing/invoice_test.go
    openmeter/billing/invoice_test.go
    openmeter/billing/adapter/invoicelinediff_test.go
    • plus others under worker/subscription/sync_test.go

Please confirm that each of these is either intentionally retained (e.g., for backward-compatibility or event-marshalling) or remove/replace them with the new InvoiceLines API to avoid surprises post-migration.

test/billing/invoice_test.go (5)

317-317: LGTM!

The line correctly updates the Lines field to use the new billing.NewInvoiceLines constructor, which aligns with the PR's objective of replacing LineChildren with InvoiceLines at the top-level invoice representation.


388-390: New test assertions correctly verify invoice field presence.

The test correctly validates that Lines is not present when no expansion is requested, and that the workflow and app fields are populated as expected. This properly tests the new InvoiceLines wrapper behavior.

Also applies to: 420-423


1664-1664: Direct child line access is now correctly implemented.

The code correctly accesses child lines directly via line.Children[index] instead of the previous wrapper methods. This aligns with the refactor where Line.Children is now a plain slice type (LineChildren []*Line).

Also applies to: 2424-2426, 2460-2462, 2519-2521


3030-3031: Child line iteration and access patterns are properly updated.

The direct slice iteration over line.Children and accessing children via index is correct. The validation logic for detailed lines properly handles the new slice-based LineChildren type.

Also applies to: 3040-3056, 3517-3522


3916-3924: Line sorting implementation correctly handles the new structure.

The sorting logic properly manipulates line.Children as a direct slice and correctly reconstructs it using billing.NewLineChildren. The use of billing.NewInvoiceLines for the top-level invoice lines is also appropriate.

openmeter/billing/httpdriver/invoiceline.go (1)

694-771: LGTM! The function correctly returns the new InvoiceLines type.

The migration from LineChildren to InvoiceLines is properly implemented throughout the function, including all error paths and the successful return using billing.NewInvoiceLines(out).

openmeter/billing/service/invoice.go (2)

138-139: Consistent migration to InvoiceLines type.

The service correctly uses billing.NewInvoiceLines for line wrapping and billing.InvoiceLines{} for empty initialization. The filtering logic for deleted lines is properly maintained with the new type.

Also applies to: 193-194, 196-205


924-924: SimulateInvoice correctly uses NewInvoiceLines.

Both the UpdateInvoice and SimulateInvoice functions properly construct invoice lines using billing.NewInvoiceLines, maintaining consistency with the new type system.

Also applies to: 1107-1120

test/billing/adapter_test.go (4)

143-143: Direct slice append is the correct approach.

The change from out.Children.Append(line) to out.Children = append(out.Children, line) correctly handles the new plain slice type.


228-235: Test assertions properly updated for direct slice access.

All test assertions have been correctly updated to use direct slice access (lines[x].Children) instead of the previous wrapper methods. The element matching and count validations remain functionally equivalent.

Also applies to: 238-248, 250-256, 271-277, 301-309, 311-323


327-332: Detailed line handling correctly uses direct slice manipulation.

The test correctly handles detailed lines as a direct slice, including sorting and validation operations. The use of strings.Compare for sorting by ChildUniqueReferenceID is appropriate.

Also applies to: 370-378


449-450: Discount access patterns correctly updated.

All discount-related test assertions have been properly updated to access discounts through the direct slice (lines[0].Children[0].Discounts) instead of through wrapper methods.

Also applies to: 495-496, 506-511, 519-520, 589-590

openmeter/billing/invoiceline.go (12)

370-373: LGTM! Clean migration to lo.Map for child transformation.

The use of lo.Map for transforming children is cleaner than the previous wrapper-based Map method.


385-395: Proper handling of empty children slices.

The direct length check len(out.Children) == 0 and subsequent initialization with NewLineChildren(nil) correctly ensures consistent nil/empty slice behavior.


429-434: Child cloning correctly uses lo.Map.

The cloning logic properly uses lo.Map to transform children, maintaining the parent-child relationship correctly.


467-488: Validation correctly iterates over direct slice.

The validation logic properly checks len(i.Children) > 0 and iterates directly over the slice with proper error reporting including the index.


566-571: DisassociateChildren simplified correctly.

The function now simply assigns an empty LineChildren{} to both the line and its DBState, which is cleaner than the previous implementation.


710-719: NewLineChildren correctly returns plain slice.

The constructor properly handles nil normalization for empty slices, which helps with test equality checks as noted in the comment.


721-725: Validation uses lo.Map appropriately.

The validation method correctly uses lo.Map with proper error prefixing for each child line.


727-731: GetByID correctly operates on value receiver.

The method properly uses lo.FindOrElse on the slice value, which is appropriate for the new plain slice type.


733-744: RemoveByID correctly mutates slice in place.

The method properly uses lo.Filter to remove the matching element and reassigns the filtered result to the receiver.


746-750: GetByChildUniqueReferenceID uses value receiver correctly.

The method properly operates on a value receiver and uses lo.FindOrElse for the lookup.


754-796: ChildrenWithIDReuse correctly handles the new LineChildren type.

The function properly operates on the direct slice (c.Children) and correctly preserves IDs and timestamps when reusing existing children.


895-897: Pending line validation correctly checks direct slice length.

The validation properly uses len(line.Children) > 0 instead of the previous wrapper-based check.

@turip turip enabled auto-merge (squash) August 17, 2025 16:37
@turip turip merged commit 8df6026 into main Aug 18, 2025
26 checks passed
@turip turip deleted the reafactor/disambiguate-line-children branch August 18, 2025 08:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/billing release-note/ignore Ignore this change when generating release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants