Skip to content

Commit 7cddb66

Browse files
authored
fix: child unique reference handling (#4385)
1 parent 5b32f03 commit 7cddb66

13 files changed

Lines changed: 2241 additions & 814 deletions

File tree

.agents/skills/test/SKILL.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,12 @@ For running a specific test directly:
3838
POSTGRES_HOST=127.0.0.1 go test -tags=dynamic ./openmeter/<domain>/...
3939
```
4040

41+
When a Postgres-backed test fails, the suite output often includes a `testdbconf:` URL for the per-test database. Use that URL with `psql` to inspect the failed test state before rerunning, because the database is still useful for RCA. Quote the connection string if it contains query parameters, for example:
42+
43+
```bash
44+
psql 'postgres://pgtdbuser:pgtdbpass@127.0.0.1:5432/testdb_tpl_...?sslmode=disable'
45+
```
46+
4147
## Key Test Utilities
4248

4349
From `openmeter/testutils/`:

openmeter/billing/charges/charge.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,31 @@ func (c Charge) GetChargeID() (meta.ChargeID, error) {
137137
return meta.ChargeID{}, fmt.Errorf("invalid charge type: %s", c.t)
138138
}
139139

140+
func (c Charge) GetUniqueReferenceID() (*string, error) {
141+
switch c.t {
142+
case meta.ChargeTypeFlatFee:
143+
if c.flatFee == nil {
144+
return nil, fmt.Errorf("flat fee charge is nil")
145+
}
146+
147+
return c.flatFee.Intent.UniqueReferenceID, nil
148+
case meta.ChargeTypeCreditPurchase:
149+
if c.creditPurchase == nil {
150+
return nil, fmt.Errorf("credit purchase charge is nil")
151+
}
152+
153+
return c.creditPurchase.Intent.UniqueReferenceID, nil
154+
case meta.ChargeTypeUsageBased:
155+
if c.usageBased == nil {
156+
return nil, fmt.Errorf("usage based charge is nil")
157+
}
158+
159+
return c.usageBased.Intent.UniqueReferenceID, nil
160+
}
161+
162+
return nil, fmt.Errorf("invalid charge type: %s", c.t)
163+
}
164+
140165
func (c Charge) GetCustomerID() (customer.CustomerID, error) {
141166
switch c.t {
142167
case meta.ChargeTypeFlatFee:

openmeter/billing/charges/flatfee/service/create.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,10 +191,9 @@ func buildFlatFeeGatheringLine(input buildFlatFeeGatheringLineInput) (billing.Ga
191191

192192
TaxConfig: lineIntent.TaxConfig.ToTaxConfig(),
193193

194-
Engine: billing.LineEngineTypeChargeFlatFee,
195-
ChargeID: lo.ToPtr(flatFee.ID),
196-
ChildUniqueReferenceID: lineIntent.UniqueReferenceID,
197-
Subscription: subscription,
194+
Engine: billing.LineEngineTypeChargeFlatFee,
195+
ChargeID: lo.ToPtr(flatFee.ID),
196+
Subscription: subscription,
198197
},
199198
}
200199

openmeter/billing/charges/usagebased/service/create.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -127,10 +127,9 @@ func gatheringLineFromUsageBasedChargeForPeriod(charge usagebased.Charge, servic
127127

128128
TaxConfig: intent.TaxConfig.ToTaxConfig(),
129129

130-
ChargeID: lo.ToPtr(charge.ID),
131-
Engine: billing.LineEngineTypeChargeUsageBased,
132-
ChildUniqueReferenceID: intent.UniqueReferenceID,
133-
Subscription: subscription,
130+
ChargeID: lo.ToPtr(charge.ID),
131+
Engine: billing.LineEngineTypeChargeUsageBased,
132+
Subscription: subscription,
134133
},
135134
}
136135

openmeter/billing/httpdriver/invoice.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -317,7 +317,7 @@ func (h *handler) ProgressInvoice(action ProgressAction) ProgressInvoiceHandler
317317
case InvoiceProgressActionAdvance:
318318
invoice, err = h.service.AdvanceInvoice(ctx, request.Invoice)
319319
case InvoiceProgressActionSnapshotQuantities:
320-
invoice, err = h.service.SnapshotQuantities(ctx, request.Invoice)
320+
invoice, err = h.service.ForceCollectInvoice(ctx, request.Invoice)
321321
default:
322322
return ProgressInvoiceResponse{}, fmt.Errorf("invalid action: %s", action)
323323
}

openmeter/billing/service.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,8 @@ type InvoiceService interface {
8888
// - the invoice is in a state that cannot be advanced (e.g. waiting for draft period to expire)
8989
// - the invoice is advanced to the final state
9090
AdvanceInvoice(ctx context.Context, input AdvanceInvoiceInput) (StandardInvoice, error)
91-
SnapshotQuantities(ctx context.Context, input SnapshotQuantitiesInput) (StandardInvoice, error)
91+
// ForceCollectInvoice bypasses the invoice collection period and moves the invoice into collection immediately.
92+
ForceCollectInvoice(ctx context.Context, input ForceCollectInvoiceInput) (StandardInvoice, error)
9293
ApproveInvoice(ctx context.Context, input ApproveInvoiceInput) (StandardInvoice, error)
9394
PaymentAuthorized(ctx context.Context, input PaymentAuthorizedInput) (StandardInvoice, error)
9495
RetryInvoice(ctx context.Context, input RetryInvoiceInput) (StandardInvoice, error)

openmeter/billing/service/invoice.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -514,8 +514,9 @@ func (s *Service) PaymentAuthorized(ctx context.Context, input billing.PaymentAu
514514
return s.executeTriggerOnInvoice(ctx, input, billing.TriggerAuthorized)
515515
}
516516

517-
func (s *Service) SnapshotQuantities(ctx context.Context, input billing.SnapshotQuantitiesInput) (billing.StandardInvoice, error) {
518-
return s.executeTriggerOnInvoice(ctx, input, billing.TriggerSnapshotQuantities)
517+
// ForceCollectInvoice bypasses the invoice collection period and moves the invoice into collection immediately.
518+
func (s *Service) ForceCollectInvoice(ctx context.Context, input billing.ForceCollectInvoiceInput) (billing.StandardInvoice, error) {
519+
return s.executeTriggerOnInvoice(ctx, input, billing.TriggerForceCollect)
519520
}
520521

521522
func (s *Service) RetryInvoice(ctx context.Context, input billing.RetryInvoiceInput) (billing.StandardInvoice, error) {

openmeter/billing/service/stdinvoicestate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ func allocateStateMachine() *InvoiceStateMachine {
101101
).
102102
Permit(billing.TriggerDelete, billing.StandardInvoiceStatusDeleteInProgress).
103103
Permit(billing.TriggerUpdated, billing.StandardInvoiceStatusDraftUpdating).
104-
Permit(billing.TriggerSnapshotQuantities, billing.StandardInvoiceStatusDraftCollecting)
104+
Permit(billing.TriggerForceCollect, billing.StandardInvoiceStatusDraftCollecting)
105105

106106
stateMachine.Configure(billing.StandardInvoiceStatusDraftCollecting).
107107
Permit(billing.TriggerNext, billing.StandardInvoiceStatusDraftValidating).
@@ -409,7 +409,7 @@ func (m *InvoiceStateMachine) StatusDetails(ctx context.Context) (billing.Standa
409409
outErr = errors.Join(outErr, err)
410410
}
411411

412-
if availableActions.SnapshotQuantities, err = m.calculateAvailableActionDetails(ctx, billing.TriggerSnapshotQuantities); err != nil {
412+
if availableActions.SnapshotQuantities, err = m.calculateAvailableActionDetails(ctx, billing.TriggerForceCollect); err != nil {
413413
outErr = errors.Join(outErr, err)
414414
}
415415

openmeter/billing/stdinvoice.go

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -752,11 +752,11 @@ type AssociatedLineCountsAdapterResponse struct {
752752
}
753753

754754
type (
755-
AdvanceInvoiceInput = InvoiceID
756-
ApproveInvoiceInput = InvoiceID
757-
PaymentAuthorizedInput = InvoiceID
758-
RetryInvoiceInput = InvoiceID
759-
SnapshotQuantitiesInput = InvoiceID
755+
AdvanceInvoiceInput = InvoiceID
756+
ApproveInvoiceInput = InvoiceID
757+
ForceCollectInvoiceInput = InvoiceID
758+
PaymentAuthorizedInput = InvoiceID
759+
RetryInvoiceInput = InvoiceID
760760
)
761761

762762
type UpdateStandardInvoiceAdapterInput = StandardInvoice

openmeter/billing/stdinvoicestate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ var (
2121
// TriggerUpdated is used to trigger a change in the invoice (we are using this to calculate the immutable states
2222
// and trigger re-validation)
2323
TriggerUpdated InvoiceTrigger = "trigger_updated"
24-
// TriggerSnapshotQuantities is used to snapshot the quantities for usage based line items
25-
TriggerSnapshotQuantities InvoiceTrigger = "trigger_snapshot_quantities"
24+
// TriggerForceCollect skips the collection wait and forces an invoice into collection.
25+
TriggerForceCollect InvoiceTrigger = "trigger_force_collect"
2626
// triggerDelete is used to delete the invoice
2727
TriggerDelete InvoiceTrigger = "trigger_delete"
2828

0 commit comments

Comments
 (0)