| package billingservice |
|
|
| import ( |
| "context" |
| "fmt" |
|
|
| "github.com/openmeterio/openmeter/openmeter/billing" |
| "github.com/openmeterio/openmeter/pkg/slicesx" |
| ) |
|
|
| var _ billing.StandardInvoiceService = (*Service)(nil) |
|
|
| func (s *Service) UpdateStandardInvoice(ctx context.Context, input billing.UpdateStandardInvoiceInput) (billing.StandardInvoice, error) { |
| if err := input.Validate(); err != nil { |
| return billing.StandardInvoice{}, billing.ValidationError{ |
| Err: err, |
| } |
| } |
|
|
| return s.executeTriggerOnInvoice( |
| ctx, |
| input.Invoice, |
| billing.TriggerUpdated, |
| ExecuteTriggerWithIncludeDeletedLines(input.IncludeDeletedLines), |
| ExecuteTriggerWithAllowInStates(billing.StandardInvoiceStatusDraftUpdating), |
| ExecuteTriggerWithEditCallback(func(ctx context.Context, sm *InvoiceStateMachine) error { |
| originalInvoice, err := sm.Invoice.Clone() |
| if err != nil { |
| return fmt.Errorf("cloning invoice before edit: %w", err) |
| } |
|
|
| if err := input.EditFn(&sm.Invoice); err != nil { |
| return fmt.Errorf("editing invoice: %w", err) |
| } |
|
|
| lineDiff, err := s.diffMutableInvoiceLines(ctx, originalInvoice, sm.Invoice, input.ChangeSource) |
| if err != nil { |
| return billing.ValidationError{ |
| Err: fmt.Errorf("collecting mutable invoice line changes: %w", err), |
| } |
| } |
|
|
| switch input.ChangeSource { |
| case billing.ChangeSourceAPIRequest: |
| invoiceWithLineEngineChanges, err := s.applyAPIInvoiceLineEdits(ctx, applyAPIInvoiceLineEditsInput{ |
| EditedInvoice: sm.Invoice, |
| LineDiff: lineDiff, |
| }) |
| if err != nil { |
| return fmt.Errorf("applying API standard invoice line edits: %w", err) |
| } |
|
|
| standardInvoice, err := invoiceWithLineEngineChanges.AsInvoice().AsStandardInvoice() |
| if err != nil { |
| return fmt.Errorf("converting edited invoice to standard invoice: %w", err) |
| } |
| sm.Invoice = standardInvoice |
|
|
| case billing.ChangeSourceSystem: |
| |
| |
| |
| |
| if err := s.dispatchSystemStandardLineDeletions(ctx, sm.Invoice, lineDiff.Deleted); err != nil { |
| return fmt.Errorf("dispatching system standard line deletions: %w", err) |
| } |
|
|
| default: |
| return fmt.Errorf("unsupported change source: %s", input.ChangeSource) |
| } |
|
|
| if err := sm.Invoice.Validate(); err != nil { |
| return billing.ValidationError{ |
| Err: err, |
| } |
| } |
|
|
| return nil |
| }), |
| ) |
| } |
|
|
| func (s *Service) GetStandardInvoiceById(ctx context.Context, input billing.GetStandardInvoiceByIdInput) (billing.StandardInvoice, error) { |
| if err := input.Validate(); err != nil { |
| return billing.StandardInvoice{}, billing.ValidationError{ |
| Err: err, |
| } |
| } |
|
|
| invoiceType, err := s.adapter.GetInvoiceType(ctx, input.Invoice) |
| if err != nil { |
| return billing.StandardInvoice{}, fmt.Errorf("getting invoice type: %w", err) |
| } |
|
|
| if invoiceType != billing.InvoiceTypeStandard { |
| return billing.StandardInvoice{}, billing.ValidationError{ |
| Err: fmt.Errorf("invoice[%s] is not a standard invoice, cannot be fetched via the standard invoice service", input.Invoice.ID), |
| } |
| } |
|
|
| invoice, err := s.adapter.GetStandardInvoiceById(ctx, input) |
| if err != nil { |
| return billing.StandardInvoice{}, err |
| } |
|
|
| invoice, err = s.resolveWorkflowApps(ctx, invoice) |
| if err != nil { |
| return billing.StandardInvoice{}, fmt.Errorf("error resolving workflow apps for invoice [%s]: %w", input.Invoice.ID, err) |
| } |
|
|
| invoice, err = s.resolveStatusDetails(ctx, invoice) |
| if err != nil { |
| return billing.StandardInvoice{}, fmt.Errorf("error resolving status details for invoice [%s]: %w", input.Invoice.ID, err) |
| } |
|
|
| return invoice, nil |
| } |
|
|
| func (s *Service) ListStandardInvoices(ctx context.Context, input billing.ListStandardInvoicesInput) (billing.ListStandardInvoicesResponse, error) { |
| if err := input.Validate(); err != nil { |
| return billing.ListStandardInvoicesResponse{}, billing.ValidationError{ |
| Err: err, |
| } |
| } |
|
|
| adapterInput := billing.ListInvoicesAdapterInput{ |
| Page: input.Page, |
| Namespaces: input.Namespaces, |
| IDs: input.IDs, |
| Statuses: input.Statuses, |
| ExtendedStatuses: input.ExtendedStatuses, |
| HasAvailableAction: input.HasAvailableAction, |
|
|
| ExternalIDs: input.ExternalIDs, |
| DraftUntilLTE: input.DraftUntilLTE, |
| CollectionAtLTE: input.CollectionAtLTE, |
| IncludeDeleted: input.IncludeDeleted, |
|
|
| Expand: billing.InvoiceExpands{}. |
| SetOrUnsetIf(input.Expand.Has(billing.StandardInvoiceExpandLines), billing.InvoiceExpandLines). |
| SetOrUnsetIf(input.Expand.Has(billing.StandardInvoiceExpandDeletedLines), billing.InvoiceExpandDeletedLines), |
| OnlyStandard: true, |
| } |
|
|
| resp, err := s.adapter.ListInvoices(ctx, adapterInput) |
| if err != nil { |
| return billing.ListStandardInvoicesResponse{}, fmt.Errorf("listing invoices: %w", err) |
| } |
|
|
| stdInvoices, err := slicesx.MapWithErr(resp.Items, func(item billing.Invoice) (billing.StandardInvoice, error) { |
| return item.AsStandardInvoice() |
| }) |
| if err != nil { |
| return billing.ListStandardInvoicesResponse{}, fmt.Errorf("mapping invoices to standard invoices: %w", err) |
| } |
|
|
| return billing.ListStandardInvoicesResponse{ |
| Items: stdInvoices, |
| Page: resp.Page, |
| TotalCount: resp.TotalCount, |
| }, nil |
| } |
|
|
| func (s *Service) ListStandardInvoicesPendingAdvancement(ctx context.Context, input billing.ListStandardInvoicesPendingAdvancementInput) ([]billing.StandardInvoice, error) { |
| if err := input.Validate(); err != nil { |
| return nil, billing.ValidationError{Err: err} |
| } |
|
|
| invoices, err := s.adapter.ListStandardInvoicesPendingAdvancement(ctx, input) |
| if err != nil { |
| return nil, fmt.Errorf("listing standard invoices pending advancement: %w", err) |
| } |
|
|
| return invoices, nil |
| } |
|
|