-
Notifications
You must be signed in to change notification settings - Fork 163
feat: charges example handlers for flat fee/credit purchase #3900
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
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,119 @@ | ||||||||||||||
| package charges | ||||||||||||||
|
|
||||||||||||||
| import ( | ||||||||||||||
| "errors" | ||||||||||||||
| "fmt" | ||||||||||||||
|
|
||||||||||||||
| "github.com/alpacahq/alpacadecimal" | ||||||||||||||
|
|
||||||||||||||
| "github.com/openmeterio/openmeter/pkg/models" | ||||||||||||||
| "github.com/openmeterio/openmeter/pkg/timeutil" | ||||||||||||||
| ) | ||||||||||||||
|
|
||||||||||||||
| type CreditRealizationCreateInput struct { | ||||||||||||||
| Annotations models.Annotations `json:"annotations"` | ||||||||||||||
| ServicePeriod timeutil.ClosedPeriod `json:"servicePeriod"` | ||||||||||||||
|
|
||||||||||||||
| // TODO: let's add ledger transaction id(s) here | ||||||||||||||
|
|
||||||||||||||
| Amount alpacadecimal.Decimal `json:"amount"` | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| func (i CreditRealizationCreateInput) Validate() error { | ||||||||||||||
| var errs []error | ||||||||||||||
|
|
||||||||||||||
| if err := i.ServicePeriod.Validate(); err != nil { | ||||||||||||||
| errs = append(errs, fmt.Errorf("service period: %w", err)) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if i.Amount.IsNegative() { | ||||||||||||||
| errs = append(errs, fmt.Errorf("amount must be positive")) | ||||||||||||||
| } | ||||||||||||||
|
Comment on lines
+29
to
+31
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Error message says "must be positive" but zero is allowed. The check Proposed fix if i.Amount.IsNegative() {
- errs = append(errs, fmt.Errorf("amount must be positive"))
+ errs = append(errs, fmt.Errorf("amount cannot be negative"))
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||
|
|
||||||||||||||
| return errors.Join(errs...) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| type CreditRealization struct { | ||||||||||||||
| models.NamespacedID | ||||||||||||||
| models.ManagedModel | ||||||||||||||
| CreditRealizationCreateInput | ||||||||||||||
|
|
||||||||||||||
| // AllocatedToStandardInvoiceRealizationID is the standard invoice realization ID that the credit was allocated to. | ||||||||||||||
| // If nil, the credit is not allocated to any invoice line (e.g. line is still in gathering, | ||||||||||||||
| // credit_only mode without invoicing, etc.) | ||||||||||||||
| AllocatedToStandardInvoiceRealizationID *string `json:"allocatedToStandardInvoiceRealizationID"` | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| func (r CreditRealization) Validate() error { | ||||||||||||||
| var errs []error | ||||||||||||||
|
|
||||||||||||||
| if err := r.CreditRealizationCreateInput.Validate(); err != nil { | ||||||||||||||
| errs = append(errs, fmt.Errorf("credit realization input: %w", err)) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| if r.AllocatedToStandardInvoiceRealizationID != nil && *r.AllocatedToStandardInvoiceRealizationID == "" { | ||||||||||||||
| errs = append(errs, fmt.Errorf("allocated to standard invoice realization ID must be set")) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| return errors.Join(errs...) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| type CreditRealizations []CreditRealization | ||||||||||||||
|
|
||||||||||||||
| func (r CreditRealizations) Validate() error { | ||||||||||||||
| var errs []error | ||||||||||||||
|
|
||||||||||||||
| for idx, realization := range r { | ||||||||||||||
| if err := realization.Validate(); err != nil { | ||||||||||||||
| errs = append(errs, fmt.Errorf("credit realization[%d]: %w", idx, err)) | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| return errors.Join(errs...) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| func (r CreditRealizations) RealizedUnsettledAmount() alpacadecimal.Decimal { | ||||||||||||||
| sum := alpacadecimal.Zero | ||||||||||||||
| for _, realization := range r { | ||||||||||||||
| sum = sum.Add(realization.Amount) | ||||||||||||||
| } | ||||||||||||||
| return sum | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| func (r CreditRealizations) LastRealizedPeriod() *timeutil.ClosedPeriod { | ||||||||||||||
| // TODO: we might want to filter for period realizations only | ||||||||||||||
| if len(r) == 0 { | ||||||||||||||
| return nil | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| lastRealizedPeriod := r[0].ServicePeriod | ||||||||||||||
| for _, realization := range r { | ||||||||||||||
| if realization.ServicePeriod.To.After(lastRealizedPeriod.To) { | ||||||||||||||
| lastRealizedPeriod = realization.ServicePeriod | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| return &lastRealizedPeriod | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| type ReversedCreditRealization struct { | ||||||||||||||
| RealizationID models.NamespacedID `json:"realizationID"` | ||||||||||||||
|
|
||||||||||||||
| TransactionGroupReferences []LedgerTransactionGroupReference `json:"transactionGroupReference"` | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| func (r ReversedCreditRealization) Validate() error { | ||||||||||||||
| var errs []error | ||||||||||||||
|
|
||||||||||||||
| if err := r.RealizationID.Validate(); err != nil { | ||||||||||||||
| errs = append(errs, fmt.Errorf("realization ID: %w", err)) | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| for idx, transactionGroupReference := range r.TransactionGroupReferences { | ||||||||||||||
| if err := transactionGroupReference.Validate(); err != nil { | ||||||||||||||
| errs = append(errs, fmt.Errorf("transaction group reference[%d]: %w", idx, err)) | ||||||||||||||
| } | ||||||||||||||
| } | ||||||||||||||
|
|
||||||||||||||
| return errors.Join(errs...) | ||||||||||||||
| } | ||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package charges | ||
|
|
||
| import ( | ||
| "errors" | ||
| "fmt" | ||
|
|
||
| "github.com/openmeterio/openmeter/pkg/models" | ||
| ) | ||
|
|
||
| // LedgerTransactionGroupReference is a reference to a ledger transaction group. | ||
| // It is used to track payment settlement transactions. | ||
| type LedgerTransactionGroupReference struct { | ||
| TransactionGroupID models.NamespacedID `json:"transactionGroupID"` | ||
| } | ||
|
|
||
| func (r LedgerTransactionGroupReference) Validate() error { | ||
| var errs []error | ||
|
|
||
| if err := r.TransactionGroupID.Validate(); err != nil { | ||
| errs = append(errs, fmt.Errorf("transaction group ID: %w", err)) | ||
| } | ||
|
|
||
| return errors.Join(errs...) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
JSON tag
"totalAmount"doesn't match the field namePreTaxTotalAmount.The Go field name communicates "pre-tax" semantics, but the JSON tag just says
"totalAmount". This mismatch could confuse consumers of the serialized form — they'd seetotalAmountand might not realize it's specifically the pre-tax amount. Consider aligning them, e.g.,json:"preTaxTotalAmount".Proposed fix
type OnFlatFeeAssignedToInvoiceInput struct { Charge FlatFeeCharge `json:"charge"` - PreTaxTotalAmount alpacadecimal.Decimal `json:"totalAmount"` + PreTaxTotalAmount alpacadecimal.Decimal `json:"preTaxTotalAmount"` }📝 Committable suggestion
🤖 Prompt for AI Agents