File size: 5,389 Bytes
429334c | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | package flatfee
import (
"context"
"errors"
"fmt"
"time"
"github.com/alpacahq/alpacadecimal"
"github.com/openmeterio/openmeter/openmeter/billing/charges/lineage"
"github.com/openmeterio/openmeter/openmeter/billing/charges/models/creditrealization"
"github.com/openmeterio/openmeter/openmeter/billing/charges/models/ledgertransaction"
"github.com/openmeterio/openmeter/openmeter/billing/models/totals"
"github.com/openmeterio/openmeter/pkg/currencyx"
"github.com/openmeterio/openmeter/pkg/models"
"github.com/openmeterio/openmeter/pkg/timeutil"
)
type OnAllocateCreditsInput struct {
Charge Charge `json:"charge"`
ServicePeriod timeutil.ClosedPeriod `json:"servicePeriod"`
BookedAt time.Time `json:"bookedAt"`
// PreTaxAmountToAllocate is the pre-tax amount to allocate from credits.
// The input charge's settlement mode governs whether this may create a negative balance.
PreTaxAmountToAllocate alpacadecimal.Decimal `json:"preTaxAmountToAllocate"`
}
func (i OnAllocateCreditsInput) Validate() error {
var errs []error
if err := i.Charge.Validate(); err != nil {
errs = append(errs, fmt.Errorf("charge: %w", err))
}
if err := i.ServicePeriod.Validate(); err != nil {
errs = append(errs, fmt.Errorf("service period: %w", err))
}
if i.BookedAt.IsZero() {
errs = append(errs, fmt.Errorf("booked at is required"))
}
if i.PreTaxAmountToAllocate.IsNegative() {
errs = append(errs, fmt.Errorf("pre tax amount to allocate cannot be negative"))
}
return models.NewNillableGenericValidationError(errors.Join(errs...))
}
type OnInvoiceUsageAccruedInput struct {
Charge Charge `json:"charge"`
ServicePeriod timeutil.ClosedPeriod `json:"servicePeriod"`
BookedAt time.Time `json:"bookedAt"`
Totals totals.Totals `json:"totals"`
}
func (i OnInvoiceUsageAccruedInput) Validate() error {
var errs []error
if err := i.Charge.Validate(); err != nil {
errs = append(errs, fmt.Errorf("charge: %w", err))
}
if err := i.ServicePeriod.Validate(); err != nil {
errs = append(errs, fmt.Errorf("service period: %w", err))
}
if i.BookedAt.IsZero() {
errs = append(errs, fmt.Errorf("booked at is required"))
}
if err := i.Totals.Validate(); err != nil {
errs = append(errs, fmt.Errorf("totals: %w", err))
}
return models.NewNillableGenericValidationError(errors.Join(errs...))
}
type CorrectCreditAllocationsInput struct {
Charge Charge `json:"charge"`
BookedAt time.Time `json:"bookedAt"`
Corrections creditrealization.CorrectionRequest `json:"corrections"`
LineageSegmentsByRealization lineage.ActiveSegmentsByRealizationID `json:"-"`
}
func (i CorrectCreditAllocationsInput) Validate() error {
var errs []error
if err := i.Charge.Validate(); err != nil {
errs = append(errs, fmt.Errorf("charge: %w", err))
}
if i.BookedAt.IsZero() {
errs = append(errs, fmt.Errorf("booked at is required"))
}
return models.NewNillableGenericValidationError(errors.Join(errs...))
}
func (i CorrectCreditAllocationsInput) ValidateWith(currencyCalculator currencyx.Currency) error {
var errs []error
if err := i.Validate(); err != nil {
return err
}
if err := i.Corrections.ValidateWith(currencyCalculator); err != nil {
errs = append(errs, fmt.Errorf("corrections: %w", err))
}
return models.NewNillableGenericValidationError(errors.Join(errs...))
}
type PaymentEventInput struct {
Charge Charge `json:"charge"`
EventAt time.Time `json:"eventAt"`
Amount alpacadecimal.Decimal `json:"amount"`
}
func (i PaymentEventInput) Validate() error {
var errs []error
if err := i.Charge.Validate(); err != nil {
errs = append(errs, fmt.Errorf("charge: %w", err))
}
if i.EventAt.IsZero() {
errs = append(errs, fmt.Errorf("event at is required"))
}
if i.Amount.IsNegative() {
errs = append(errs, fmt.Errorf("amount cannot be negative"))
}
return models.NewNillableGenericValidationError(errors.Join(errs...))
}
type (
OnPaymentAuthorizedInput = PaymentEventInput
OnPaymentSettledInput = PaymentEventInput
)
type Handler interface {
// OnAllocateCredits is called when a flat fee allocates credits.
OnAllocateCredits(ctx context.Context, input OnAllocateCreditsInput) (creditrealization.CreateAllocationInputs, error)
// OnFlatFeeStandardInvoiceUsageAccrued is called when the remaining usage is sent to the customer on a standard invoice.
OnInvoiceUsageAccrued(ctx context.Context, input OnInvoiceUsageAccruedInput) (ledgertransaction.GroupReference, error)
// OnCorrectCreditAllocations is called when a credit allocation needs to be corrected.
OnCorrectCreditAllocations(ctx context.Context, input CorrectCreditAllocationsInput) (creditrealization.CreateCorrectionInputs, error)
// OnFlatFeePaymentAuthorized is called when a flat fee payment is authorized.
OnPaymentAuthorized(ctx context.Context, input OnPaymentAuthorizedInput) (ledgertransaction.GroupReference, error)
// OnFlatFeePaymentSettled is called when a flat fee payment is settled.
OnPaymentSettled(ctx context.Context, input OnPaymentSettledInput) (ledgertransaction.GroupReference, error)
// OnFlatFeePaymentUncollectible is called when a flat fee payment is uncollectible
OnPaymentUncollectible(ctx context.Context, charge Charge) (ledgertransaction.GroupReference, error)
}
|