Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Comments

v1.0.39#807

Merged
nkmishra1997 merged 17 commits intomainfrom
release/02-dev
Dec 2, 2025
Merged

v1.0.39#807
nkmishra1997 merged 17 commits intomainfrom
release/02-dev

Conversation

@hiteshshimpi-55
Copy link
Contributor

@hiteshshimpi-55 hiteshshimpi-55 commented Dec 2, 2025

Important

Enhance customer and subscription models to support parent-child relationships and invoicing flexibility, with updated filters and tests.

  • Behavior:
    • Added ParentCustomerID to Customer model in customer.go and related files to support parent-child relationships.
    • Introduced InvoiceBilling type in subscription.go to determine invoice recipient (parent or self).
    • Updated ProcessInvoicePaymentWithWallets to use invoicing customer's wallet.
  • Filters:
    • Added ParentCustomerIDs filter to CustomerFilter in customer.go.
    • Added InvoicingCustomerIDs filter to SubscriptionFilter in subscription.go.
  • Tests:
    • Added tests in wallet_payment_test.go to verify payment processing with invoicing customer wallets.
    • Updated inmemory_customer_store.go to handle parent customer filtering.

This description was created by Ellipsis for 3cd454c. You can customize this summary. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • Added support for separate invoicing customer designation for subscriptions
    • Introduced parent-child customer hierarchy capability
    • Added invoice billing preference control (bill subscription customer or parent directly)
    • Introduced commitment true-up feature for usage charges
  • Documentation

    • Added comprehensive documentation detailing invoicing customer functionality, domain impacts, and testing scenarios

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

hiteshshimpi-55 and others added 16 commits December 2, 2025 17:30
… ID in customer creation and update processes
…ce validation logic in customer creation and update requests
…esponse and enhance validation for expand fields
…g customer logic and enhance validation for subscription creation
… and update related logic for improved clarity and consistency
@coderabbitai
Copy link

coderabbitai bot commented Dec 2, 2025

Walkthrough

Introduces the Invoicing Customer ID feature enabling subscriptions to reference a separate invoicing customer for billing operations. Adds customer parent relationships, commitment true-up support, modifies database schema with new fields and relationships, removes the price-unit edge, and updates billing, invoice, and payment processing to use the invoicing customer context.

Changes

Cohort / File(s) Summary
Documentation
docs/invoicing-customer-id.md
New documentation detailing domain changes, billing/invoicing/payment flow impacts, test scenarios, migration notes, and backward compatibility considerations.
Customer Entity & Schema
ent/schema/customer.go, ent/customer.go, ent/customer/customer.go, ent/customer/where.go, ent/customer_create.go, ent/customer_update.go, internal/domain/customer/model.go, internal/repository/ent/customer.go, internal/types/customer.go, internal/testutil/inmemory_customer_store.go
Adds parent_customer_id field as nullable string; extends creation, update, and query builders with setter/getter/predicate methods; adds ParentCustomerIDs filter support; adds ParentCustomer expansion config.
Price Entity & Edge Removal
ent/schema/price.go, ent/price.go, ent/price/price.go, ent/price/where.go, ent/price_create.go, ent/price_query.go, ent/price_update.go, internal/domain/price/model.go
Changes price_unit_id from string to nullable string; removes price_unit_edge edge and all edge-traversal builders (QueryPriceUnitEdge, HasPriceUnitEdge, etc.); replaces edge-based mutations with direct field handling.
PriceUnit Entity & Edge Removal
ent/schema/priceunit.go, ent/priceunit.go, ent/priceunit/priceunit.go, ent/priceunit/where.go, ent/priceunit_create.go, ent/priceunit_query.go, ent/priceunit_update.go
Removes prices edge from PriceUnit; deletes edge traversal methods (QueryPrices, WithPrices, HasPrices, etc.); removes AddPrices/RemovePrices edge management methods.
Subscription Entity & Schema
ent/schema/subscription.go, ent/subscription.go, ent/subscription/subscription.go, ent/subscription/where.go, ent/subscription_create.go, ent/subscription_query.go, ent/subscription_update.go, internal/domain/subscription/model.go, internal/repository/ent/subscription.go
Adds enable_true_up (bool, default false) and invoicing_customer_id fields; adds invoicing_customer M2O edge to Customer; extends creation, update, query builders with edge and field handlers; adds GetInvoicingCustomerID() helper method.
Entity Mutations & Runtime
ent/mutation.go, ent/runtime.go
Extends mutation interfaces with new field/edge accessors for ParentCustomerID, InvoicingCustomerID, EnableTrueUp, and related PriceUnitID changes; adds default value support for EnableTrueUp.
Database Schema Migration
ent/migrate/schema.go
Adds parent_customer_id, price_unit_id, enable_true_up, and invoicing_customer_id columns; creates invoicing_customer foreign key (subscriptions → customers, SetNull on delete); adjusts column indices for price indexes.
Client Edge Traversal
ent/client.go
Removes price-unit edge traversal builders; adds QueryInvoicingCustomer() on SubscriptionClient.
API DTOs
internal/api/dto/customer.go, internal/api/dto/subscription.go
Adds ParentCustomerID/ParentCustomerExternalID to customer requests/responses; adds InvoicingCustomerID, InvoiceBilling, EnableTrueUp to subscription requests with mutual-exclusion validation.
Type Definitions
internal/types/customer.go, internal/types/subscription.go, internal/types/expand.go
Adds ParentCustomerIDs filter field; introduces InvoiceBilling enum (invoice_to_parent, invoice_to_self) with validation; adds ExpandParentCustomer constant; adds InvoicingCustomerIDs filter field.
Service Layer—Billing & Invoice
internal/service/billing.go, internal/service/invoice.go, internal/service/invoice_test.go
Billing: adds commitment true-up line item creation, uses invoicing customer ID for invoice creation. Invoice: updates RecalculateInvoice to use invoicing customer ID from new request. Tests: adds two test cases for invoicing customer ID scenarios.
Service Layer—Customer
internal/service/customer.go
Adds parent customer resolution, validation, and hierarchy enforcement; extends GetCustomer(s) with nested parent expansion; adds GetUpcomingCreditGrantApplications; validates parent assignment constraints (no active subscriptions, no self-parenting, no nesting).
Service Layer—Subscription
internal/service/subscription.go, internal/service/subscription_test.go
Adds InvoiceBilling logic (invoice_to_parent sets invoicing customer to parent); propagates EnableTrueUp flag; adds four new end-to-end tests for invoicing customer ID scenarios (period processing, auto-cancellation, recalculation, billing updates).
Service Layer—Payment Processing
internal/service/subscription_payment_processor.go, internal/service/wallet_payment_test.go
Updates payment processor to use invoicing customer ID for wallet/Stripe operations; expands payment metadata with invoicing and subscription customer IDs; updates getPaymentMethodID signature to accept invoicing customer ID; adds two new wallet payment tests.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant SubscriptionService
    participant CustomerService
    participant BillingService
    participant InvoiceService
    participant PaymentProcessor
    participant Wallet as Wallet Service

    Client->>SubscriptionService: CreateSubscription(InvoiceBilling=invoice_to_parent, ParentID)
    activate SubscriptionService
    SubscriptionService->>CustomerService: GetCustomer(ParentID)
    activate CustomerService
    CustomerService-->>SubscriptionService: Parent Customer
    deactivate CustomerService
    SubscriptionService->>SubscriptionService: Set InvoicingCustomerID=ParentID
    SubscriptionService-->>Client: Subscription created
    deactivate SubscriptionService

    Client->>BillingService: CreateInvoiceRequestForCharges(Subscription)
    activate BillingService
    BillingService->>SubscriptionService: GetInvoicingCustomerID()
    SubscriptionService-->>BillingService: InvoicingCustomerID
    BillingService->>InvoiceService: CreateInvoice(CustomerID=InvoicingCustomerID)
    deactivate BillingService

    Client->>PaymentProcessor: ProcessSubscriptionPayment(Subscription)
    activate PaymentProcessor
    PaymentProcessor->>SubscriptionService: GetInvoicingCustomerID()
    SubscriptionService-->>PaymentProcessor: InvoicingCustomerID
    PaymentProcessor->>Wallet: GetWallet(InvoicingCustomerID)
    activate Wallet
    Wallet-->>PaymentProcessor: Wallet funds
    deactivate Wallet
    PaymentProcessor->>PaymentProcessor: Apply wallet to invoice
    PaymentProcessor-->>Client: Payment processed
    deactivate PaymentProcessor
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Areas requiring extra attention:

  • Database schema and entity generation (ent/migrate/schema.go, ent/subscription.go, ent/subscription_query.go): Verify foreign key relationships, index references after column additions, and edge loading logic for the new invoicing_customer edge.
  • Payment processor invoicing context (internal/service/subscription_payment_processor.go): Carefully review the wallet and Stripe operation routing through invoicing customer ID; verify metadata recording of both invoicing and subscription customer IDs.
  • Customer parent hierarchy validation (internal/service/customer.go): Validate business logic constraints (no active subscriptions, no self-parenting, no circular nesting) and ensure parent expansion doesn't introduce N+1 queries.
  • Billing true-up logic (internal/service/billing.go): Verify commitment true-up line item creation, calculation of remaining commitment, and proper inclusion in usage charges and preview flows.
  • Price entity edge removal (ent/price*.go, ent/priceunit*.go): Ensure complete removal of edge traversal and no orphaned references; verify PriceUnitID nullable-pointer handling consistency.

Possibly related PRs

Suggested reviewers

  • nkmishra1997

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Title check ⚠️ Warning The PR title 'v1.0.39' does not follow the required naming convention format of {{type_of_change}}({{module}}): {{message}}. Update the title to follow the format: feat(invoices): add invoicing customer ID and true-up support or similar descriptive title with appropriate type (feat/fix/chore) and module.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 90.77% which is sufficient. The required threshold is 80.00%.
✨ 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 release/02-dev

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.

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

Important

Looks good to me! 👍

Reviewed everything up to 3cd454c in 4 minutes and 50 seconds. Click for details.
  • Reviewed 5587 lines of code in 52 files
  • Skipped 0 files when reviewing.
  • Skipped posting 3 draft comments. View those below.
  • Modify your settings and rules to customize what types of comments Ellipsis leaves. And don't forget to react with 👍 or 👎 to teach Ellipsis.
1. internal/testutil/inmemory_customer_store.go:31
  • Draft comment:
    The deep copy performed in copyCustomer does not copy the new ParentCustomerID field. This will cause customer hierarchy data to be lost in tests.
  • Reason this comment was not posted:
    Comment was not on a location in the diff, so it can't be submitted as a review comment.
2. ent/subscription/where.go:2443
  • Draft comment:
    Typo in comment: "applies the HasEdge predicate on the "invoicing_customer" edge with a given conditions (other predicates)". Consider changing "with a given conditions" to "with given conditions" or "with a given condition".
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% This is auto-generated code (line 1 explicitly says "Code generated by ent, DO NOT EDIT"). The comment is pointing out a grammatical error in documentation. However, several rules apply here: 1) This is generated code that shouldn't be manually edited, 2) The comment doesn't suggest a code change that would fix a bug or improve functionality - it's purely about grammar in a comment, 3) The same grammatical error appears throughout the file in other similar functions (HasLineItemsWith, HasPausesWith, etc.), suggesting this is a pattern in the code generator itself. Since this is generated code, the fix would need to be made in the code generator, not in this file. The PR author cannot fix this directly in this file without violating the "DO NOT EDIT" directive. While the grammatical error is real, I might be missing that some teams do manually edit generated code despite the warning. Also, even if it's generated code, pointing out the error could be valuable for fixing the generator. However, the rules say not to comment unless there's clearly a code change required, and this is just a grammar fix in a comment. The rules are clear: "Do NOT comment unless there is clearly a code change required" and "Do NOT make comments that are obvious or unimportant." A grammatical error in a comment is not a required code change - it doesn't affect functionality. Additionally, this is generated code that explicitly says "DO NOT EDIT", so the PR author shouldn't be making manual changes to it anyway. The comment is not actionable in this context. This comment should be deleted. It's pointing out a minor grammatical error in auto-generated code that explicitly says "DO NOT EDIT". The comment is not actionable for the PR author and doesn't represent a functional issue. Grammar fixes in comments of generated code are not important enough to warrant a review comment.
3. internal/api/dto/subscription.go:189
  • Draft comment:
    Minor typographical suggestion: Consider hyphenating 'True-Up Fee' in the comment on line 189 for clarity (i.e. 'Enable Commitment True-Up Fee').
  • Reason this comment was not posted:
    Decided after close inspection that this draft comment was likely wrong and/or not actionable: usefulness confidence = 10% vs. threshold = 50% This comment is about a very minor typographical/stylistic issue in a code comment. According to the rules, I should "Do NOT make comments that are obvious or unimportant." A suggestion to hyphenate "True Up" is extremely minor and doesn't affect code functionality, readability in any meaningful way, or correctness. While "true-up" is sometimes hyphenated in financial contexts, both "true up" and "true-up" are acceptable. This is the kind of nitpicky comment that doesn't provide real value and would likely be annoying to a developer. The comment is about a change (the new field was added), but it's not a meaningful or important comment about that change. Could this be a domain-specific term where the hyphenation matters for consistency with other documentation or industry standards? Perhaps in financial/billing contexts, "true-up" has a specific meaning that should be consistently formatted. Even if "true-up" is more standard in financial contexts, this is an extremely minor stylistic point that doesn't warrant a PR comment. The meaning is completely clear either way, and this level of nitpicking is not helpful. The rules explicitly state not to make comments that are "obvious or unimportant," and this clearly falls into that category. This comment should be deleted. It's a trivial typographical suggestion that doesn't meaningfully improve the code or documentation. It falls squarely under "obvious or unimportant" comments that should not be made.

Workflow ID: wflow_A4JTybl2huRipZv5

You can customize Ellipsis by changing your verbosity settings, reacting with 👍 or 👎, replying to comments, or adding code review rules.

Copy link

@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: 1

🧹 Nitpick comments (15)
ent/schema/customer.go (1)

82-87: ParentCustomerID column is defined appropriately; consider indexing or a self-edge if lookups grow

The parent_customer_id field definition (varchar(50), nillable + optional) is consistent with how you’re modeling it elsewhere in ent. If you expect frequent queries like “get all children of parent X”, it may be worth adding:

  • An index on parent_customer_id (plus tenant/environment if needed), and/or
  • A self-referential ent edge for more convenient traversal and stronger FK semantics.

These are optional design improvements and can be deferred until usage patterns justify them.

docs/invoicing-customer-id.md (2)

52-66: Clarify how RecalculateTaxesOnInvoice derives its customer ID

Here you explain that RecalculateTaxesOnInvoice() “uses invoice's existing CustomerID directly,” while later in the impact analysis you summarize it as “uses invoicing customer ID.” That’s logically true only because invoice.CustomerID is set to the invoicing customer at creation.

To avoid confusion for future readers, consider tightening the wording to explicitly say that tax recalculation uses invoice.CustomerID (which for new flows is the invoicing customer), and does not re-fetch the subscription. This keeps behavior crystal‑clear if flows around CustomerID ever change.


318-324: Double‑check “No database migration needed” claim

This section states that no DB migration is needed because the field already exists in the schema. Given this PR also wires invoicing_customer_id into ent schema/migrate and introduces a new edge, please confirm that:

  • The column and FK truly pre‑exist in all supported environments, and
  • No new DDL is being introduced in this or a related migration.

If any migration is required (even a lightweight one), it’s worth updating this note so ops/dev teams aren’t surprised.

ent/schema/subscription.go (1)

179-182: Edge definition correctly binds the invoicing customer relationship.

The invoicing_customer edge properly uses Unique() for M2O cardinality and Field("invoicing_customer_id") to bind to the foreign key column.

Consider adding an index on invoicing_customer_id in the Indexes() function if you anticipate frequent queries filtering by this field (e.g., "find all subscriptions for a given invoicing customer"). This would improve query performance for such lookups.

// Example addition to Indexes():
index.Fields("tenant_id", "environment_id", "invoicing_customer_id", "status").
    Annotations(entsql.IndexWhere("invoicing_customer_id IS NOT NULL AND status = 'published'")),
internal/repository/ent/customer.go (1)

76-76: Consider adding validation for ParentCustomerID relationships.

While the implementation correctly uses SetNillableParentCustomerID, there's no validation to prevent potential issues such as:

  • Circular references (customer A → B → A)
  • Referencing a non-existent parent customer
  • Setting a customer as its own parent

Consider adding validation in the service layer or as a database constraint. Would you like me to help generate validation logic for these scenarios?

internal/service/subscription.go (1)

293-294: EnableTrueUp propagation into the domain model is straightforward

Copying req.EnableTrueUp onto sub.EnableTrueUp before persistence cleanly exposes the new flag to downstream billing logic. Just ensure this behavior matches any ent‑level DefaultEnableTrueUp so there’s no divergence between API and schema defaults.

internal/api/dto/subscription.go (1)

189-190: EnableTrueUp flag is added without unexpected API behavior

Introducing enable_true_up as a plain bool (no omitempty) is safe and backward compatible; existing clients that omit it will default to false. If the intended default is ever non‑false, you’ll want to mirror that here in Validate() or via a documented API default.

internal/service/billing.go (1)

544-591: Significant code duplication in true-up logic.

The commitment true-up logic at lines 544-591 is nearly identical to lines 991-1037 in CalculateUsageChargesForPreview. This violates DRY principles and creates a maintenance burden where future changes need to be applied in both places.

Consider extracting the true-up logic into a shared helper method:

+func (s *billingService) addCommitmentTrueUpIfNeeded(
+	ctx context.Context,
+	sub *subscription.Subscription,
+	usage *dto.GetUsageBySubscriptionResponse,
+	periodStart, periodEnd time.Time,
+	usageCharges *[]dto.CreateInvoiceLineItemRequest,
+	totalUsageCost *decimal.Decimal,
+) {
+	commitmentAmount := lo.FromPtr(sub.CommitmentAmount)
+	overageFactor := lo.FromPtr(sub.OverageFactor)
+	hasCommitment := commitmentAmount.GreaterThan(decimal.Zero) && overageFactor.GreaterThan(decimal.NewFromInt(1))
+
+	if !hasCommitment || usage.HasOverage || !sub.EnableTrueUp {
+		return
+	}
+
+	remainingCommitment := s.calculateRemainingCommitment(usage, commitmentAmount)
+	if !remainingCommitment.GreaterThan(decimal.Zero) {
+		return
+	}
+
+	planDisplayName := ""
+	for _, item := range sub.LineItems {
+		if item.PlanDisplayName != "" {
+			planDisplayName = item.PlanDisplayName
+			break
+		}
+	}
+
+	precision := types.GetCurrencyPrecision(sub.Currency)
+	roundedRemainingCommitment := remainingCommitment.Round(precision)
+	commitmentUtilized := commitmentAmount.Sub(roundedRemainingCommitment)
+
+	trueUpLineItem := dto.CreateInvoiceLineItemRequest{
+		EntityID:        lo.ToPtr(sub.PlanID),
+		EntityType:      lo.ToPtr(string(types.SubscriptionLineItemEntityTypePlan)),
+		PriceType:       lo.ToPtr(string(types.PRICE_TYPE_FIXED)),
+		PlanDisplayName: lo.ToPtr(planDisplayName),
+		DisplayName:     lo.ToPtr(fmt.Sprintf("%s True Up", planDisplayName)),
+		Amount:          roundedRemainingCommitment,
+		Quantity:        decimal.NewFromInt(1),
+		PeriodStart:     &periodStart,
+		PeriodEnd:       &periodEnd,
+		PriceID:         lo.ToPtr(types.GenerateUUIDWithPrefix(types.UUID_PREFIX_PRICE)),
+		Metadata: types.Metadata{
+			"is_commitment_trueup": "true",
+			"description":          "Remaining commitment amount for billing period",
+			"commitment_amount":    commitmentAmount.String(),
+			"commitment_utilized":  commitmentUtilized.String(),
+		},
+	}
+
+	*usageCharges = append(*usageCharges, trueUpLineItem)
+	*totalUsageCost = totalUsageCost.Add(roundedRemainingCommitment)
+}

Also applies to: 991-1037

internal/service/invoice_test.go (2)

1691-1702: Test assertions inside nil check may silently pass on failures.

Both tests wrap assertions in if got != nil { ... } blocks, but this means if CreateSubscriptionInvoice returns nil for an unexpected reason, the test will pass silently without verifying the core functionality. The tests call s.NoError(err) before the nil check, but a zero-amount invoice scenario (which returns nil) would skip all customer ID assertions.

Consider making the assertion more explicit about expectations:

 s.NoError(err)
-if got != nil {
+if got == nil {
+    s.T().Log("Invoice was nil (likely zero-amount), consider adding line items to subscription for full test coverage")
+    return
+}
 s.NotEmpty(got.ID)
 // Invoice should have invoicing customer ID, not subscription customer ID
 s.Equal(invoicingCustomer.ID, got.CustomerID, "Invoice should use invoicing customer ID")
 // ... remaining assertions
-}

Also applies to: 1751-1761


1662-1662: Test subscription created without line items may result in zero-amount invoice.

The test creates a subscription without line items ([]*subscription.SubscriptionLineItem{}), which likely results in a zero-amount invoice that returns nil. This means the core assertion about invoicingCustomer.ID == got.CustomerID may never actually execute.

Consider adding line items to ensure the invoice is created:

-s.NoError(s.GetStores().SubscriptionRepo.CreateWithLineItems(s.GetContext(), subscriptionWithInvoicing, []*subscription.SubscriptionLineItem{}))
+lineItems := []*subscription.SubscriptionLineItem{
+    {
+        ID:              types.GenerateUUIDWithPrefix(types.UUID_PREFIX_SUBSCRIPTION_LINE_ITEM),
+        SubscriptionID:  subscriptionWithInvoicing.ID,
+        CustomerID:      subscriptionWithInvoicing.CustomerID,
+        EntityID:        s.testData.plan.ID,
+        EntityType:      types.SubscriptionLineItemEntityTypePlan,
+        PriceID:         s.testData.prices.apiCalls.ID,
+        PriceType:       s.testData.prices.apiCalls.Type,
+        InvoiceCadence:  types.InvoiceCadenceAdvance, // Ensures charges at period start
+        DisplayName:     "API Calls",
+        Quantity:        decimal.NewFromInt(1),
+        Currency:        subscriptionWithInvoicing.Currency,
+        BillingPeriod:   subscriptionWithInvoicing.BillingPeriod,
+        BaseModel:       types.GetDefaultBaseModel(s.GetContext()),
+    },
+}
+s.NoError(s.GetStores().SubscriptionRepo.CreateWithLineItems(s.GetContext(), subscriptionWithInvoicing, lineItems))
internal/service/subscription_test.go (1)

4610-4921: Invoicing‑customer tests are directionally good; consider making invoice creation deterministic

The new tests around invoicing customer ID (period processing, auto‑cancellation, billing‑period cron) correctly:

  • Create a distinct invoicing customer.
  • Attach InvoicingCustomerID on subscriptions.
  • Assert invoices and cancellations respect that ID where invoices exist.

Right now several assertions are guarded by if len(invoices) > 0 and effectively become no‑ops when no invoice is produced, so regressions in invoice generation would still let tests pass. If you want stronger guarantees, consider:

  • Adding minimal line items/usage so a subscription invoice is always created in these tests; and/or
  • Asserting that at least one invoice exists for these scenarios.

This is optional but would turn these into hard correctness tests rather than best‑effort checks.

internal/service/customer.go (4)

35-58: Parent‑customer assignment rules are coherent; a couple of edge behaviors to be aware of

The combination of:

  • CreateCustomer checks that a chosen parent (by ID or external ID) is not itself a child.
  • UpdateCustomer using validateParentCustomerAssignment only when ParentCustomerID actually changes.
  • validateParentCustomerAssignment enforcing:
    • No hierarchy changes when the customer has non‑cancelled subscriptions.
    • No self‑parenting.
    • Parent must be top‑level (no parent of its own).
    • A customer that already has children cannot become a child.

gives you a clean “two‑level” hierarchy (root + direct children) and prevents re‑parenting when active subscriptions or existing children would make it ambiguous. That all looks logically sound.

Two behavioral notes (not necessarily bugs):

  1. Detaching a parent uses empty string semantics
    Because you only enter this block when req.ParentCustomerID != nil, callers must pass "" (not nil) to clear the parent. If ParentCustomerID is omitted (nil), no hierarchy change occurs. Make sure the API docs / clients are aligned with this contract.

  2. No concurrent‑safety at the DB level
    These validations happen via separate reads (SubRepo.List, CustomerRepo.Get, CustomerRepo.List) before the final update. Concurrent updates could, in theory, slip between the checks and the update. If stronger guarantees are required later, this logic might want to live in a transaction and/or be backed by DB‑level constraints.

Overall the implementation is fine; consider documenting the “empty string clears parent” behavior externally.

Also applies to: 342-361, 579-633


194-217: GetCustomer parent expansion is correct but recursively calls itself

Wrapping the repo result into dto.CustomerResponse and, when ParentCustomerID is set, calling GetCustomer again for the parent produces the expected expanded structure (ParentCustomer filled).

Given your hierarchy rules disallow nested parents and parent‑customers from having parents, recursion depth is effectively bounded to 1, so this is safe. If you ever relax those rules, you may want to introduce a max‑depth or cycle‑detection guard to avoid pathological recursion.


226-302: Bulk parent expansion in GetCustomers is efficient; minor nit on attach path

Using filter.GetExpand().Validate(CustomerExpandConfig), collecting unique ParentCustomerIDs, fetching them with a single List call, and then attaching from a lookup map is a good pattern that avoids N+1 queries.

One tiny optimization you could consider is to skip the “attach parent customers” loop entirely when parentCustomersByID is nil/empty (currently you always iterate response and just never hit the map key). It’s negligible, so only worth changing if you touch this code again.


660-707: GetUpcomingCreditGrantApplications flow looks reasonable; verify ListByCustomerID semantics

The new method correctly:

  • Verifies the customer exists.
  • Uses SubscriptionService.ListByCustomerID to gather all subscriptions.
  • Returns an empty, well‑formed response when there are none.
  • Delegates to SubscriptionService.GetUpcomingCreditGrantApplications with the collected IDs.

Two small points:

  • This constructs a new SubscriptionService per call; that’s fine for now, but if this endpoint becomes hot you might consider injecting a SubscriptionService into customerService instead of recreating it.
  • The code assumes ListByCustomerID returns a flat slice of subscriptions, not a paginated wrapper. If that method’s contract ever changes, this code would silently break, so it’s worth double‑checking that assumption.

Functionally this looks good.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c952dc7 and 12b23d8.

⛔ Files ignored due to path filters (1)
  • go.sum is excluded by !**/*.sum
📒 Files selected for processing (51)
  • docs/invoicing-customer-id.md (1 hunks)
  • ent/client.go (1 hunks)
  • ent/customer.go (4 hunks)
  • ent/customer/customer.go (3 hunks)
  • ent/customer/where.go (2 hunks)
  • ent/customer_create.go (2 hunks)
  • ent/customer_update.go (4 hunks)
  • ent/migrate/schema.go (6 hunks)
  • ent/mutation.go (38 hunks)
  • ent/price.go (4 hunks)
  • ent/price/price.go (0 hunks)
  • ent/price/where.go (0 hunks)
  • ent/price_create.go (1 hunks)
  • ent/price_query.go (3 hunks)
  • ent/price_update.go (2 hunks)
  • ent/priceunit.go (1 hunks)
  • ent/priceunit/priceunit.go (0 hunks)
  • ent/priceunit/where.go (0 hunks)
  • ent/priceunit_create.go (0 hunks)
  • ent/priceunit_query.go (1 hunks)
  • ent/priceunit_update.go (0 hunks)
  • ent/runtime.go (1 hunks)
  • ent/schema/customer.go (1 hunks)
  • ent/schema/price.go (2 hunks)
  • ent/schema/priceunit.go (1 hunks)
  • ent/schema/subscription.go (2 hunks)
  • ent/subscription.go (8 hunks)
  • ent/subscription/subscription.go (8 hunks)
  • ent/subscription/where.go (3 hunks)
  • ent/subscription_create.go (7 hunks)
  • ent/subscription_query.go (9 hunks)
  • ent/subscription_update.go (11 hunks)
  • internal/api/dto/customer.go (4 hunks)
  • internal/api/dto/subscription.go (4 hunks)
  • internal/domain/customer/model.go (2 hunks)
  • internal/domain/price/model.go (1 hunks)
  • internal/domain/subscription/model.go (2 hunks)
  • internal/repository/ent/customer.go (3 hunks)
  • internal/repository/ent/subscription.go (4 hunks)
  • internal/service/billing.go (3 hunks)
  • internal/service/customer.go (7 hunks)
  • internal/service/invoice.go (3 hunks)
  • internal/service/invoice_test.go (1 hunks)
  • internal/service/subscription.go (2 hunks)
  • internal/service/subscription_payment_processor.go (9 hunks)
  • internal/service/subscription_test.go (3 hunks)
  • internal/service/wallet_payment_test.go (1 hunks)
  • internal/testutil/inmemory_customer_store.go (1 hunks)
  • internal/types/customer.go (1 hunks)
  • internal/types/expand.go (2 hunks)
  • internal/types/subscription.go (2 hunks)
💤 Files with no reviewable changes (6)
  • ent/priceunit_create.go
  • ent/priceunit/priceunit.go
  • ent/price/where.go
  • ent/price/price.go
  • ent/priceunit/where.go
  • ent/priceunit_update.go
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-18T10:22:48.348Z
Learnt from: omkar273
Repo: flexprice/flexprice PR: 652
File: internal/service/billing.go:1454-1477
Timestamp: 2025-10-18T10:22:48.348Z
Learning: In `internal/service/billing.go`, the `GetCustomerEntitlements` function passes `subscriptions[0].ID` to `AggregateEntitlements` as a fallback subscription ID. This is acceptable because customers typically have a single subscription, and the subscription ID parameter is only used as fallback metadata in `EntitlementSource` objects, not for business logic or filtering.

Applied to files:

  • internal/types/subscription.go
  • internal/service/subscription_test.go
  • ent/subscription_query.go
  • internal/service/billing.go
  • internal/service/customer.go
  • ent/subscription.go
  • internal/service/subscription.go
  • internal/service/subscription_payment_processor.go
  • internal/api/dto/subscription.go
  • ent/mutation.go
  • ent/subscription_update.go
  • internal/repository/ent/subscription.go
  • ent/subscription_create.go
  • ent/subscription/where.go
  • internal/domain/subscription/model.go
  • ent/migrate/schema.go
📚 Learning: 2025-10-17T21:53:42.505Z
Learnt from: omkar273
Repo: flexprice/flexprice PR: 652
File: internal/service/subscription.go:3495-3534
Timestamp: 2025-10-17T21:53:42.505Z
Learning: In `internal/service/subscription.go`, when adding multiple-type addons to a subscription, existing active line items are intentionally reused rather than creating new line items for each addon association. The addon occurrences are tracked via separate `AddonAssociation` records, but billing happens through the shared `SubscriptionLineItem`. This is the expected behavior and should not be flagged as an issue.

Applied to files:

  • internal/types/subscription.go
  • internal/service/subscription_test.go
  • internal/service/billing.go
  • ent/subscription.go
  • internal/service/subscription.go
  • internal/api/dto/subscription.go
  • ent/mutation.go
  • internal/domain/subscription/model.go
🧬 Code graph analysis (35)
internal/types/subscription.go (3)
internal/domain/entityintegrationmapping/model.go (1)
  • Validate (84-141)
internal/errors/builder.go (1)
  • NewError (17-19)
internal/errors/errors.go (1)
  • ErrValidation (16-16)
ent/customer/customer.go (2)
ent/subscription/subscription.go (1)
  • OrderOption (330-330)
ent/plan/plan.go (1)
  • OrderOption (101-101)
internal/service/wallet_payment_test.go (11)
ent/customer/where.go (1)
  • ExternalID (103-105)
internal/types/wallet.go (2)
  • WalletTypePrePaid (25-25)
  • WalletStatusActive (15-15)
ent/wallet/where.go (2)
  • Balance (124-126)
  • CreditBalance (129-131)
internal/domain/wallet/transaction.go (1)
  • Transaction (12-32)
internal/types/uuid.go (2)
  • GenerateUUIDWithPrefix (19-24)
  • UUID_PREFIX_WALLET_TRANSACTION (83-83)
ent/wallettransaction/where.go (3)
  • WalletID (104-106)
  • CreditAmount (119-121)
  • CreditsAvailable (159-161)
internal/types/invoice.go (2)
  • InvoiceTypeSubscription (91-91)
  • InvoiceStatusFinalized (126-126)
internal/types/payment.go (3)
  • PaymentStatusPending (13-13)
  • PaymentFilter (109-121)
  • PaymentDestinationTypeInvoice (86-86)
ent/invoice/where.go (4)
  • AmountDue (135-137)
  • AmountPaid (140-142)
  • AmountRemaining (145-147)
  • DueDate (185-187)
internal/service/wallet_payment.go (2)
  • WalletPaymentOptions (28-35)
  • PromotionalFirstStrategy (20-20)
ent/payment/where.go (2)
  • DestinationID (115-117)
  • DestinationType (110-112)
internal/testutil/inmemory_customer_store.go (1)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
internal/service/invoice_test.go (3)
internal/domain/subscription/model.go (1)
  • Subscription (13-128)
ent/schema/subscription.go (5)
  • Subscription (17-19)
  • Subscription (22-27)
  • Subscription (30-166)
  • Subscription (169-184)
  • Subscription (187-197)
internal/types/invoice.go (3)
  • InvoiceFlowManual (59-59)
  • InvoiceTypeSubscription (91-91)
  • InvoiceType (87-87)
ent/priceunit.go (1)
ent/priceunit/where.go (1)
  • Precision (129-131)
ent/customer_create.go (2)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
ent/customer/customer.go (1)
  • FieldParentCustomerID (51-51)
ent/schema/subscription.go (1)
ent/schema/customer.go (5)
  • Customer (14-16)
  • Customer (19-25)
  • Customer (28-89)
  • Customer (92-94)
  • Customer (97-109)
ent/price_query.go (2)
ent/ent.go (2)
  • QueryContext (67-67)
  • Interceptor (70-70)
ent/schema/price.go (5)
  • Price (15-17)
  • Price (20-25)
  • Price (28-214)
  • Price (217-219)
  • Price (222-231)
internal/domain/customer/model.go (1)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
ent/customer_update.go (2)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
ent/customer/customer.go (1)
  • FieldParentCustomerID (51-51)
ent/subscription_query.go (5)
internal/repository/ent/subscription.go (1)
  • SubscriptionQuery (451-451)
ent/ent.go (1)
  • Query (66-66)
ent/schema/subscription.go (5)
  • Subscription (17-19)
  • Subscription (22-27)
  • Subscription (30-166)
  • Subscription (169-184)
  • Subscription (187-197)
ent/subscription.go (2)
  • Subscription (19-105)
  • Subscription (194-215)
ent/subscription/where.go (2)
  • InvoicingCustomerID (245-247)
  • ID (15-17)
internal/api/dto/customer.go (5)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
internal/errors/builder.go (1)
  • NewError (17-19)
internal/errors/errors.go (1)
  • ErrValidation (16-16)
internal/types/context.go (1)
  • GetEnvironmentID (54-59)
internal/validator/validator.go (1)
  • ValidateRequest (35-52)
ent/customer.go (2)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
ent/customer/customer.go (1)
  • FieldParentCustomerID (51-51)
ent/priceunit_query.go (3)
ent/priceunit.go (2)
  • PriceUnit (17-48)
  • PriceUnit (51-68)
ent/schema/priceunit.go (6)
  • PriceUnit (14-16)
  • PriceUnit (19-23)
  • PriceUnit (26-31)
  • PriceUnit (34-72)
  • PriceUnit (75-77)
  • PriceUnit (80-87)
ent/predicate/predicate.go (1)
  • PriceUnit (94-94)
internal/types/customer.go (2)
internal/types/search_filter.go (2)
  • FilterCondition (55-60)
  • SortCondition (138-141)
ent/customer/where.go (2)
  • ExternalID (103-105)
  • Email (113-115)
ent/client.go (4)
internal/domain/subscription/model.go (1)
  • Subscription (13-128)
ent/subscription.go (2)
  • Subscription (19-105)
  • Subscription (194-215)
ent/ent.go (1)
  • Query (66-66)
ent/subscription/subscription.go (3)
  • Table (112-112)
  • InvoicingCustomerTable (156-156)
  • InvoicingCustomerColumn (161-161)
ent/price.go (3)
ent/price/where.go (2)
  • PriceUnitID (123-125)
  • GroupID (228-230)
ent/subscriptionlineitem/where.go (1)
  • PriceUnitID (150-152)
ent/invoicelineitem/where.go (1)
  • PriceUnitID (155-157)
ent/runtime.go (1)
ent/subscription/subscription.go (1)
  • DefaultEnableTrueUp (272-272)
internal/service/billing.go (5)
ent/subscription/where.go (5)
  • CommitmentAmount (215-217)
  • OverageFactor (220-222)
  • Currency (125-127)
  • ID (15-17)
  • CustomerID (110-112)
internal/types/currency.go (1)
  • GetCurrencyPrecision (68-73)
internal/api/dto/invoice.go (2)
  • CreateInvoiceLineItemRequest (441-490)
  • CreateInvoiceRequest (33-118)
internal/types/subscription.go (1)
  • SubscriptionLineItemEntityTypePlan (47-47)
internal/types/uuid.go (2)
  • GenerateUUIDWithPrefix (19-24)
  • UUID_PREFIX_PRICE (73-73)
internal/service/customer.go (7)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
internal/errors/builder.go (1)
  • NewError (17-19)
internal/api/dto/customer.go (1)
  • CustomerResponse (116-119)
internal/types/expand.go (2)
  • CustomerExpandConfig (138-143)
  • ExpandParentCustomer (38-38)
internal/types/customer.go (2)
  • NewNoLimitCustomerFilter (33-37)
  • NewCustomerFilter (26-30)
internal/types/subscription.go (2)
  • NewSubscriptionFilter (243-247)
  • SubscriptionStatusCancelled (59-59)
ent/subscription/where.go (1)
  • SubscriptionStatusNotIn (905-907)
ent/price_update.go (2)
ent/price/where.go (1)
  • PriceUnitID (123-125)
ent/price/price.go (1)
  • FieldPriceUnitID (39-39)
ent/subscription.go (4)
ent/subscription/where.go (2)
  • EnableTrueUp (240-242)
  • InvoicingCustomerID (245-247)
ent/ent.go (2)
  • NotFoundError (283-285)
  • NotLoadedError (329-331)
ent/customer/customer.go (1)
  • Label (13-13)
ent/subscription/subscription.go (3)
  • Label (16-16)
  • FieldEnableTrueUp (94-94)
  • FieldInvoicingCustomerID (96-96)
internal/service/subscription.go (4)
internal/types/subscription.go (2)
  • InvoiceBilling (11-11)
  • InvoiceBillingInvoiceToParent (15-15)
internal/errors/builder.go (1)
  • NewError (17-19)
ent/subscription/where.go (3)
  • CustomerID (110-112)
  • InvoicingCustomerID (245-247)
  • EnableTrueUp (240-242)
internal/errors/errors.go (1)
  • ErrValidation (16-16)
internal/api/dto/subscription.go (2)
ent/subscription/where.go (2)
  • InvoicingCustomerID (245-247)
  • EnableTrueUp (240-242)
internal/types/subscription.go (2)
  • InvoiceBilling (11-11)
  • InvoiceBillingInvoiceToSelf (18-18)
ent/mutation.go (6)
ent/customer/where.go (1)
  • ParentCustomerID (148-150)
ent/customer/customer.go (1)
  • FieldParentCustomerID (51-51)
ent/price/where.go (1)
  • PriceUnitID (123-125)
ent/subscriptionlineitem/where.go (1)
  • PriceUnitID (150-152)
ent/subscription/where.go (2)
  • EnableTrueUp (240-242)
  • InvoicingCustomerID (245-247)
ent/subscription/subscription.go (3)
  • FieldInvoicingCustomerID (96-96)
  • FieldEnableTrueUp (94-94)
  • EdgeInvoicingCustomer (110-110)
ent/subscription_update.go (2)
ent/subscription/where.go (1)
  • EnableTrueUp (240-242)
ent/subscription/subscription.go (4)
  • FieldEnableTrueUp (94-94)
  • Table (112-112)
  • InvoicingCustomerTable (156-156)
  • InvoicingCustomerColumn (161-161)
internal/repository/ent/subscription.go (2)
ent/subscription/where.go (3)
  • EnableTrueUp (240-242)
  • InvoicingCustomerID (245-247)
  • InvoicingCustomerIDIn (2230-2232)
ent/subscription/subscription.go (1)
  • FieldInvoicingCustomerID (96-96)
ent/subscription_create.go (1)
ent/subscription/subscription.go (6)
  • FieldEnableTrueUp (94-94)
  • Table (112-112)
  • InvoicingCustomerTable (156-156)
  • Columns (165-206)
  • InvoicingCustomerColumn (161-161)
  • FieldID (18-18)
ent/subscription/where.go (1)
ent/subscription/subscription.go (5)
  • FieldEnableTrueUp (94-94)
  • FieldInvoicingCustomerID (96-96)
  • Table (112-112)
  • InvoicingCustomerTable (156-156)
  • InvoicingCustomerColumn (161-161)
ent/customer/where.go (3)
ent/customer.go (2)
  • Customer (17-58)
  • Customer (61-76)
ent/schema/customer.go (5)
  • Customer (14-16)
  • Customer (19-25)
  • Customer (28-89)
  • Customer (92-94)
  • Customer (97-109)
internal/domain/customer/model.go (1)
  • Customer (10-51)
ent/subscription/subscription.go (3)
ent/customer/customer.go (3)
  • OrderOption (109-109)
  • Table (53-53)
  • FieldID (15-15)
ent/subscriptionphase/subscriptionphase.go (3)
  • OrderOption (98-98)
  • Table (42-42)
  • FieldID (16-16)
ent/plan/plan.go (3)
  • OrderOption (101-101)
  • Table (44-44)
  • FieldID (16-16)
internal/domain/subscription/model.go (4)
ent/subscription/where.go (5)
  • EnableTrueUp (240-242)
  • InvoicingCustomerID (245-247)
  • CustomerID (110-112)
  • CustomerTimezone (230-232)
  • ProrationBehavior (235-237)
ent/schema/subscription.go (5)
  • Subscription (17-19)
  • Subscription (22-27)
  • Subscription (30-166)
  • Subscription (169-184)
  • Subscription (187-197)
ent/subscription.go (2)
  • Subscription (19-105)
  • Subscription (194-215)
internal/types/proration.go (1)
  • ProrationBehavior (29-29)
ent/migrate/schema.go (2)
ent/customer/customer.go (1)
  • Columns (57-77)
ent/subscription/subscription.go (1)
  • Columns (165-206)
internal/repository/ent/customer.go (1)
ent/customer/where.go (2)
  • ParentCustomerID (148-150)
  • ParentCustomerIDIn (1263-1265)
⏰ 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). (1)
  • GitHub Check: Run Tests and Lint

Comment on lines +5398 to +5412
// QueryInvoicingCustomer queries the invoicing_customer edge of a Subscription.
func (c *SubscriptionClient) QueryInvoicingCustomer(s *Subscription) *CustomerQuery {
query := (&CustomerClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := s.ID
step := sqlgraph.NewStep(
sqlgraph.From(subscription.Table, subscription.FieldID, id),
sqlgraph.To(customer.Table, customer.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, subscription.InvoicingCustomerTable, subscription.InvoicingCustomerColumn),
)
fromV = sqlgraph.Neighbors(s.driver.Dialect(), step)
return fromV, nil
}
return query
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, check the file size to understand scope
wc -l ent/client.go

Repository: flexprice/flexprice

Length of output: 81


🏁 Script executed:

# Read the target lines 5398-5412
sed -n '5398,5412p' ent/client.go

Repository: flexprice/flexprice

Length of output: 719


🏁 Script executed:

# Search for other M2O edge definitions in the file to compare patterns
rg "sqlgraph\.Edge\(sqlgraph\.M2O" ent/client.go -A 1 -B 1

Repository: flexprice/flexprice

Length of output: 2770


Fix sqlgraph.Edge inverse flag for QueryInvoicingCustomer

QueryInvoicingCustomer is the only M2O edge helper in this file using sqlgraph.Edge(sqlgraph.M2O, false, ...). Every other M2O edge (CouponApplication, CouponAssociation, CreditGrant, CreditNoteLineItem, InvoiceLineItem, PaymentAttempt, SubscriptionLineItem, SubscriptionPause, SubscriptionPhase) uses true for the inverse flag, which is the correct behavior for child→parent traversal in ent-generated code.

This inconsistency will produce incorrect edge traversal and join logic for the invoicing_customer relationship.

Change to:

-           sqlgraph.Edge(sqlgraph.M2O, false, subscription.InvoicingCustomerTable, subscription.InvoicingCustomerColumn),
+           sqlgraph.Edge(sqlgraph.M2O, true, subscription.InvoicingCustomerTable, subscription.InvoicingCustomerColumn),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// QueryInvoicingCustomer queries the invoicing_customer edge of a Subscription.
func (c *SubscriptionClient) QueryInvoicingCustomer(s *Subscription) *CustomerQuery {
query := (&CustomerClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := s.ID
step := sqlgraph.NewStep(
sqlgraph.From(subscription.Table, subscription.FieldID, id),
sqlgraph.To(customer.Table, customer.FieldID),
sqlgraph.Edge(sqlgraph.M2O, false, subscription.InvoicingCustomerTable, subscription.InvoicingCustomerColumn),
)
fromV = sqlgraph.Neighbors(s.driver.Dialect(), step)
return fromV, nil
}
return query
}
// QueryInvoicingCustomer queries the invoicing_customer edge of a Subscription.
func (c *SubscriptionClient) QueryInvoicingCustomer(s *Subscription) *CustomerQuery {
query := (&CustomerClient{config: c.config}).Query()
query.path = func(context.Context) (fromV *sql.Selector, _ error) {
id := s.ID
step := sqlgraph.NewStep(
sqlgraph.From(subscription.Table, subscription.FieldID, id),
sqlgraph.To(customer.Table, customer.FieldID),
sqlgraph.Edge(sqlgraph.M2O, true, subscription.InvoicingCustomerTable, subscription.InvoicingCustomerColumn),
)
fromV = sqlgraph.Neighbors(s.driver.Dialect(), step)
return fromV, nil
}
return query
}
🤖 Prompt for AI Agents
In ent/client.go around lines 5398 to 5412, the QueryInvoicingCustomer M2O edge
is using sqlgraph.Edge(sqlgraph.M2O, false, ...) which is inconsistent with
other M2O child→parent edges and causes incorrect traversal; change the inverse
flag from false to true in the sqlgraph.Edge call for the invoicing_customer
relationship so the edge is treated as child→parent during join/traversal.

@hiteshshimpi-55 hiteshshimpi-55 changed the title temp PR v1.0.39 Dec 2, 2025
@nkmishra1997 nkmishra1997 merged commit 70056d3 into main Dec 2, 2025
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Dec 3, 2025
8 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants