@@ -21,6 +21,7 @@ import (
2121 "github.com/openmeterio/openmeter/pkg/clock"
2222 "github.com/openmeterio/openmeter/pkg/currencyx"
2323 "github.com/openmeterio/openmeter/pkg/framework/transaction"
24+ "github.com/openmeterio/openmeter/pkg/models"
2425 "github.com/openmeterio/openmeter/pkg/ref"
2526 "github.com/openmeterio/openmeter/pkg/slicesx"
2627)
@@ -43,29 +44,9 @@ func (s *service) applyDefaultTaxCodes(ctx context.Context, namespace string, in
4344 })
4445
4546 return slicesx .MapWithErr (intents , func (intent charges.ChargeIntent ) (charges.ChargeIntent , error ) {
46- var taxCodeID string
47-
48- switch intent .Type () {
49- case meta .ChargeTypeFlatFee :
50- flatFee , err := intent .AsFlatFeeIntent ()
51- if err != nil {
52- return charges.ChargeIntent {}, err
53- }
54- taxCodeID = flatFee .TaxConfig .TaxCodeID
55- case meta .ChargeTypeCreditPurchase :
56- creditPurchase , err := intent .AsCreditPurchaseIntent ()
57- if err != nil {
58- return charges.ChargeIntent {}, err
59- }
60- taxCodeID = creditPurchase .TaxConfig .TaxCodeID
61- case meta .ChargeTypeUsageBased :
62- usageBased , err := intent .AsUsageBasedIntent ()
63- if err != nil {
64- return charges.ChargeIntent {}, err
65- }
66- taxCodeID = usageBased .TaxConfig .TaxCodeID
67- default :
68- return charges.ChargeIntent {}, fmt .Errorf ("unsupported charge type: %s" , intent .Type ())
47+ taxCodeID , err := intent .TaxCodeID ()
48+ if err != nil {
49+ return charges.ChargeIntent {}, err
6950 }
7051
7152 if taxCodeID != "" {
@@ -87,6 +68,44 @@ func (s *service) applyDefaultTaxCodes(ctx context.Context, namespace string, in
8768 })
8869}
8970
71+ // validateTaxCodesExist verifies every distinct non-empty tax code referenced by the intents
72+ // exists.
73+ func (s * service ) validateTaxCodesExist (ctx context.Context , namespace string , intents charges.ChargeIntents ) error {
74+ seen := make (map [string ]struct {}, len (intents ))
75+
76+ for _ , intent := range intents {
77+ taxCodeID , err := intent .TaxCodeID ()
78+ if err != nil {
79+ return err
80+ }
81+
82+ if taxCodeID == "" {
83+ continue
84+ }
85+
86+ if _ , ok := seen [taxCodeID ]; ok {
87+ continue
88+ }
89+ seen [taxCodeID ] = struct {}{}
90+
91+ _ , err = s .taxCodeService .GetTaxCode (ctx , taxcode.GetTaxCodeInput {
92+ NamespacedID : models.NamespacedID {Namespace : namespace , ID : taxCodeID },
93+ })
94+ if err != nil {
95+ if taxcode .IsTaxCodeNotFoundError (err ) {
96+ return models .NewGenericValidationError (
97+ models .NewValidationError ("tax_code_not_found" , fmt .Sprintf ("referenced tax code %q does not exist" , taxCodeID )).
98+ WithPathString ("tax_config" , "tax_code" ),
99+ )
100+ }
101+
102+ return err
103+ }
104+ }
105+
106+ return nil
107+ }
108+
90109func (s * service ) Create (ctx context.Context , input charges.CreateInput ) (charges.Charges , error ) {
91110 result , err := s .create (ctx , input )
92111 if err != nil {
@@ -127,6 +146,10 @@ func (s *service) create(ctx context.Context, input charges.CreateInput) (*charg
127146 return nil , err
128147 }
129148
149+ if err := s .validateTaxCodesExist (ctx , input .Namespace , input .Intents ); err != nil {
150+ return nil , err
151+ }
152+
130153 result , err := transaction .Run (ctx , s .adapter , func (ctx context.Context ) (* chargesWithInvoiceNowActions , error ) {
131154 intentsByType , err := input .Intents .ByType ()
132155 if err != nil {
0 commit comments