Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: store status details in DB #2221

Merged
merged 2 commits into from
Feb 10, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions openmeter/billing/adapter/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ func (a *adapter) UpdateInvoice(ctx context.Context, in billing.UpdateInvoiceAda
SetMetadata(in.Metadata).
// Currency is immutable
SetStatus(in.Status).
SetOrClearStatusDetailsCache(lo.EmptyableToPtr(in.StatusDetails)).
// Type is immutable
SetNumber(in.Number).
SetOrClearDescription(in.Description).
Expand Down Expand Up @@ -668,6 +669,7 @@ func (a *adapter) mapInvoiceFromDB(ctx context.Context, invoice *db.BillingInvoi
Metadata: invoice.Metadata,
Currency: invoice.Currency,
Status: invoice.Status,
StatusDetails: invoice.StatusDetailsCache,
Type: invoice.Type,
Number: invoice.Number,
Description: invoice.Description,
Expand Down
4 changes: 2 additions & 2 deletions openmeter/billing/httpdriver/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func (h *handler) ListInvoices() ListInvoicesHandler {

IssuedAfter: input.IssuedAfter,
IssuedBefore: input.IssuedBefore,
Expand: mapInvoiceExpandToEntity(lo.FromPtrOr(input.Expand, nil)).SetGatheringTotals(true),
Expand: mapInvoiceExpandToEntity(lo.FromPtrOr(input.Expand, nil)).SetRecalculateGatheringInvoice(true),

Order: sortx.Order(lo.FromPtrOr(input.Order, api.InvoiceOrderByOrderingOrder(sortx.OrderDefault))),
OrderBy: lo.FromPtrOr(input.OrderBy, ""),
Expand Down Expand Up @@ -196,7 +196,7 @@ func (h *handler) GetInvoice() GetInvoiceHandler {
ID: params.InvoiceID,
Namespace: ns,
},
Expand: mapInvoiceExpandToEntity(params.Expand).SetDeletedLines(params.IncludeDeletedLines).SetGatheringTotals(true),
Expand: mapInvoiceExpandToEntity(params.Expand).SetDeletedLines(params.IncludeDeletedLines).SetRecalculateGatheringInvoice(true),
}, nil
},
func(ctx context.Context, request GetInvoiceRequest) (GetInvoiceResponse, error) {
Expand Down
10 changes: 5 additions & 5 deletions openmeter/billing/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -208,9 +208,9 @@ type InvoiceExpand struct {
DeletedLines bool
SplitLines bool

// GatheringTotals is used to calculate the totals of the invoice when gathering, this is temporary
// until we implement the full progressive billing stack.
GatheringTotals bool
// RecalculateGatheringInvoice is used to calculate the totals and status details of the invoice when gathering,
// this is temporary until we implement the full progressive billing stack, including gathering invoice recalculations.
RecalculateGatheringInvoice bool
}

var InvoiceExpandAll = InvoiceExpand{
Expand Down Expand Up @@ -241,8 +241,8 @@ func (e InvoiceExpand) SetSplitLines(v bool) InvoiceExpand {
return e
}

func (e InvoiceExpand) SetGatheringTotals(v bool) InvoiceExpand {
e.GatheringTotals = v
func (e InvoiceExpand) SetRecalculateGatheringInvoice(v bool) InvoiceExpand {
e.RecalculateGatheringInvoice = v
return e
}

Expand Down
25 changes: 19 additions & 6 deletions openmeter/billing/service/invoice.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ func (s *Service) ListInvoices(ctx context.Context, input billing.ListInvoicesIn
return billing.ListInvoicesResponse{}, fmt.Errorf("error resolving status details for invoice [%s]: %w", invoices.Items[i].ID, err)
}

if input.Expand.GatheringTotals {
if input.Expand.RecalculateGatheringInvoice {
invoices.Items[i], err = s.recalculateGatheringInvoice(ctx, recalculateGatheringInvoiceInput{
Invoice: invoices.Items[i],
Expand: input.Expand,
Expand Down Expand Up @@ -71,6 +71,18 @@ func (s *Service) resolveWorkflowApps(ctx context.Context, invoice billing.Invoi
}

func (s *Service) resolveStatusDetails(ctx context.Context, invoice billing.Invoice) (billing.Invoice, error) {
if invoice.Status == billing.InvoiceStatusGathering {
// Let's use the default and recalculateGatheringInvoice will fix the gaps
return invoice, nil
}

// If we are not in a time sensitive state and the status details is not empty, we can return the invoice as is, so we
// don't have to lock the invoice in the DB
if !lo.IsEmpty(invoice.StatusDetails) &&
invoice.Status != billing.InvoiceStatusDraftWaitingAutoApproval { // The status details depends on the current time, so we should recalculate
return invoice, nil
}

// let's resolve the statatus details (the invoice state machine has this side-effect after the callback)
resolvedInvoice, err := s.WithInvoiceStateMachine(ctx, invoice, func(ctx context.Context, sm *InvoiceStateMachine) error {
return nil
Expand Down Expand Up @@ -176,10 +188,11 @@ func (s *Service) recalculateGatheringInvoice(ctx context.Context, in recalculat
// TODO[later]: If this sugar is removed due to properly implemented progressive billing stack, we need to cache the when the invoice is first invoicable in the db
// so that we don't have to fetch all the lines to have proper status details.

if hasInvoicableLines.IsAbsent() {
invoice.StatusDetails.AvailableActions.Invoice = nil
} else {
invoice.StatusDetails.AvailableActions.Invoice = &billing.InvoiceAvailableActionInvoiceDetails{}
invoice.StatusDetails = billing.InvoiceStatusDetails{
Immutable: false,
AvailableActions: billing.InvoiceAvailableActions{
Invoice: lo.If(hasInvoicableLines.IsPresent(), &billing.InvoiceAvailableActionInvoiceDetails{}).Else(nil),
},
}

return invoice, nil
Expand All @@ -201,7 +214,7 @@ func (s *Service) GetInvoiceByID(ctx context.Context, input billing.GetInvoiceBy
return billing.Invoice{}, fmt.Errorf("error resolving status details for invoice [%s]: %w", invoice.ID, err)
}

if input.Expand.GatheringTotals {
if input.Expand.RecalculateGatheringInvoice {
invoice, err = s.recalculateGatheringInvoice(ctx, recalculateGatheringInvoiceInput{
Invoice: invoice,
Expand: input.Expand,
Expand Down
15 changes: 3 additions & 12 deletions openmeter/billing/service/invoicestate.go
Original file line number Diff line number Diff line change
Expand Up @@ -262,18 +262,9 @@ func (s *Service) WithInvoiceStateMachine(ctx context.Context, invoice billing.I
func (m *InvoiceStateMachine) StatusDetails(ctx context.Context) (billing.InvoiceStatusDetails, error) {
if m.Invoice.Status == billing.InvoiceStatusGathering {
// Gathering is a special state that is not part of the state machine, due to
// cross invoice operations
return billing.InvoiceStatusDetails{
Immutable: false,
// The invoicable state is calculated in the services recalculateGatheringInvoice for now, as the
// line data is available there. On the long run we need to cache this information.
//
// For now, as a safety measure we lie here, as the recalculation will be performed either ways
// and the CreateInvoice method will validate this once more.
AvailableActions: billing.InvoiceAvailableActions{
Invoice: &billing.InvoiceAvailableActionInvoiceDetails{},
},
}, nil
// cross invoice operations, for now the sugar around grathering invoices will handle
// the status details.
return billing.InvoiceStatusDetails{}, nil
}

var outErr, err error
Expand Down
15 changes: 14 additions & 1 deletion openmeter/ent/db/billinginvoice.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions openmeter/ent/db/billinginvoice/billinginvoice.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions openmeter/ent/db/billinginvoice/where.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

78 changes: 78 additions & 0 deletions openmeter/ent/db/billinginvoice_create.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading