File size: 5,525 Bytes
16cdcb7 | 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 | package service
import (
"context"
"fmt"
"github.com/openmeterio/openmeter/openmeter/taxcode"
"github.com/openmeterio/openmeter/pkg/framework/transaction"
"github.com/openmeterio/openmeter/pkg/models"
"github.com/openmeterio/openmeter/pkg/pagination"
)
func (s *Service) CreateTaxCode(ctx context.Context, input taxcode.CreateTaxCodeInput) (taxcode.TaxCode, error) {
if err := input.Validate(); err != nil {
return taxcode.TaxCode{}, err
}
return transaction.Run(ctx, s.adapter, func(ctx context.Context) (taxcode.TaxCode, error) {
return s.adapter.CreateTaxCode(ctx, input)
})
}
func (s *Service) UpdateTaxCode(ctx context.Context, input taxcode.UpdateTaxCodeInput) (taxcode.TaxCode, error) {
if err := input.Validate(); err != nil {
return taxcode.TaxCode{}, err
}
return transaction.Run(ctx, s.adapter, func(ctx context.Context) (taxcode.TaxCode, error) {
existing, err := s.adapter.GetTaxCode(ctx, taxcode.GetTaxCodeInput{NamespacedID: input.NamespacedID})
if err != nil {
return taxcode.TaxCode{}, err
}
if existing.IsManagedBySystem() && !input.AllowAnnotations {
return taxcode.TaxCode{}, models.NewGenericConflictError(taxcode.ErrTaxCodeManagedBySystem)
}
return s.adapter.UpdateTaxCode(ctx, input)
})
}
func (s *Service) ListTaxCodes(ctx context.Context, input taxcode.ListTaxCodesInput) (pagination.Result[taxcode.TaxCode], error) {
if err := input.Validate(); err != nil {
return pagination.Result[taxcode.TaxCode]{}, err
}
return transaction.Run(ctx, s.adapter, func(ctx context.Context) (pagination.Result[taxcode.TaxCode], error) {
return s.adapter.ListTaxCodes(ctx, input)
})
}
func (s *Service) GetTaxCode(ctx context.Context, input taxcode.GetTaxCodeInput) (taxcode.TaxCode, error) {
if err := input.Validate(); err != nil {
return taxcode.TaxCode{}, err
}
return transaction.Run(ctx, s.adapter, func(ctx context.Context) (taxcode.TaxCode, error) {
return s.adapter.GetTaxCode(ctx, input)
})
}
func (s *Service) GetTaxCodeByKey(ctx context.Context, input taxcode.GetTaxCodeByKeyInput) (taxcode.TaxCode, error) {
if err := input.Validate(); err != nil {
return taxcode.TaxCode{}, err
}
return transaction.Run(ctx, s.adapter, func(ctx context.Context) (taxcode.TaxCode, error) {
return s.adapter.GetTaxCodeByKey(ctx, input)
})
}
func (s *Service) GetTaxCodeByAppMapping(ctx context.Context, input taxcode.GetTaxCodeByAppMappingInput) (taxcode.TaxCode, error) {
if err := input.Validate(); err != nil {
return taxcode.TaxCode{}, err
}
return transaction.Run(ctx, s.adapter, func(ctx context.Context) (taxcode.TaxCode, error) {
return s.adapter.GetTaxCodeByAppMapping(ctx, input)
})
}
// GetOrCreateByAppMapping looks up a TaxCode by its app mapping. If none exists,
// it creates one with a key derived from the app-specific code.
func (s *Service) GetOrCreateByAppMapping(ctx context.Context, input taxcode.GetOrCreateByAppMappingInput) (taxcode.TaxCode, error) {
if err := input.Validate(); err != nil {
return taxcode.TaxCode{}, err
}
return transaction.Run(ctx, s.adapter, func(ctx context.Context) (taxcode.TaxCode, error) {
// Try to find an existing TaxCode with this app mapping.
tc, err := s.adapter.GetTaxCodeByAppMapping(ctx, taxcode.GetTaxCodeByAppMappingInput(input))
if err != nil && !taxcode.IsTaxCodeNotFoundError(err) {
return taxcode.TaxCode{}, err
}
if err == nil { // If taxcode is returned let's just return it to the caller
return tc, nil
}
// Not found — create a new TaxCode.
key := fmt.Sprintf("%s_%s", input.AppType, input.TaxCode)
tc, err = s.adapter.CreateTaxCode(ctx, taxcode.CreateTaxCodeInput{
Namespace: input.Namespace,
Key: key,
Name: input.TaxCode,
AppMappings: taxcode.TaxCodeAppMappings{
{AppType: input.AppType, TaxCode: input.TaxCode},
},
})
if err != nil {
// Another request may have created it concurrently.
if models.IsGenericConflictError(err) {
tc, retryErr := s.adapter.GetTaxCodeByAppMapping(ctx, taxcode.GetTaxCodeByAppMappingInput(input))
if retryErr != nil {
if taxcode.IsTaxCodeNotFoundError(retryErr) {
// The key derived from this Stripe code exists but its app mapping was changed
// after auto-creation (orphaned key). Avoid poisoning the pg tx.
return taxcode.TaxCode{}, fmt.Errorf("resolving orphaned tax code key for %q: %w", input.TaxCode, taxcode.ErrTaxCodeOrphanedKey)
}
return taxcode.TaxCode{}, retryErr
}
return tc, nil
}
return taxcode.TaxCode{}, err
}
return tc, nil
})
}
func (s *Service) DeleteTaxCode(ctx context.Context, input taxcode.DeleteTaxCodeInput) error {
if err := input.Validate(); err != nil {
return err
}
return transaction.RunWithNoValue(ctx, s.adapter, func(ctx context.Context) error {
existing, err := s.adapter.GetTaxCode(ctx, taxcode.GetTaxCodeInput{NamespacedID: input.NamespacedID})
if err != nil {
return err
}
if existing.IsManagedBySystem() && !input.AllowAnnotations {
return models.NewGenericConflictError(taxcode.ErrTaxCodeManagedBySystem)
}
defaults, err := s.adapter.GetOrganizationDefaultTaxCodes(ctx, taxcode.GetOrganizationDefaultTaxCodesInput{Namespace: input.NamespacedID.Namespace})
if err != nil {
return err
}
if defaults.CreditGrantTaxCodeID == existing.ID || defaults.InvoicingTaxCodeID == existing.ID {
return models.NewGenericConflictError(taxcode.ErrTaxCodeIsOrganizationDefault)
}
return s.adapter.DeleteTaxCode(ctx, input)
})
}
|