@@ -173,6 +173,38 @@ func (t *Tax) AppliesTo(country, productType string) bool {
173173 return applies
174174}
175175
176+ func calculateAmountsForSingleItem (settings * Settings , lineLogger logrus.FieldLogger , jwtClaims map [string ]interface {}, params PriceParameters , item Item , multiplier uint64 ) ItemPrice {
177+ itemPrice := ItemPrice {Quantity : item .GetQuantity ()}
178+
179+ singlePrice := item .PriceInLowestUnit () * multiplier
180+ _ , itemPrice .Subtotal = calculateTaxes (singlePrice , item , params , settings )
181+
182+ // apply discount to original price
183+ coupon := params .Coupon
184+ if coupon != nil && coupon .ValidForType (item .ProductType ()) && coupon .ValidForProduct (item .ProductSku ()) {
185+ itemPrice .Discount = calculateDiscount (singlePrice , coupon .PercentageDiscount (), coupon .FixedDiscount (params .Currency )* multiplier )
186+ }
187+ if settings != nil && settings .MemberDiscounts != nil {
188+ for _ , discount := range settings .MemberDiscounts {
189+
190+ if jwtClaims != nil && claims .HasClaims (jwtClaims , discount .Claims ) && discount .ValidForType (item .ProductType ()) && discount .ValidForProduct (item .ProductSku ()) {
191+ lineLogger = lineLogger .WithField ("discount" , discount .Claims )
192+ itemPrice .Discount += calculateDiscount (singlePrice , discount .Percentage , discount .FixedDiscount (params .Currency )* multiplier )
193+ }
194+ }
195+ }
196+
197+ discountedPrice := uint64 (0 )
198+ if itemPrice .Discount < singlePrice {
199+ discountedPrice = singlePrice - itemPrice .Discount
200+ }
201+
202+ itemPrice .Taxes , itemPrice .NetTotal = calculateTaxes (discountedPrice , item , params , settings )
203+ itemPrice .Total = int64 (itemPrice .NetTotal + itemPrice .Taxes )
204+
205+ return itemPrice
206+ }
207+
176208// CalculatePrice will calculate the final total price. It takes into account
177209// currency, country, coupons, and discounts.
178210func CalculatePrice (settings * Settings , jwtClaims map [string ]interface {}, params PriceParameters , log logrus.FieldLogger ) Price {
@@ -193,34 +225,7 @@ func CalculatePrice(settings *Settings, jwtClaims map[string]interface{}, params
193225 "product_sku" : item .ProductSku (),
194226 })
195227
196- itemPrice := ItemPrice {Quantity : item .GetQuantity ()}
197-
198- singlePrice := item .PriceInLowestUnit ()
199- _ , itemPrice .Subtotal = calculateTaxes (singlePrice , item , params , settings )
200-
201- // apply discount to original price
202- coupon := params .Coupon
203- if coupon != nil && coupon .ValidForType (item .ProductType ()) && coupon .ValidForProduct (item .ProductSku ()) {
204- itemPrice .Discount = calculateDiscount (singlePrice , coupon .PercentageDiscount (), coupon .FixedDiscount (params .Currency ))
205- }
206- if settings != nil && settings .MemberDiscounts != nil {
207- for _ , discount := range settings .MemberDiscounts {
208-
209- if jwtClaims != nil && claims .HasClaims (jwtClaims , discount .Claims ) && discount .ValidForType (item .ProductType ()) && discount .ValidForProduct (item .ProductSku ()) {
210- lineLogger = lineLogger .WithField ("discount" , discount .Claims )
211- itemPrice .Discount += calculateDiscount (singlePrice , discount .Percentage , discount .FixedDiscount (params .Currency ))
212- }
213- }
214- }
215-
216- discountedPrice := uint64 (0 )
217- if itemPrice .Discount < singlePrice {
218- discountedPrice = singlePrice - itemPrice .Discount
219- }
220-
221- itemPrice .Taxes , itemPrice .NetTotal = calculateTaxes (discountedPrice , item , params , settings )
222-
223- itemPrice .Total = int64 (itemPrice .NetTotal + itemPrice .Taxes )
228+ itemPrice := calculateAmountsForSingleItem (settings , lineLogger , jwtClaims , params , item , 1 )
224229
225230 lineLogger .WithFields (
226231 logrus.Fields {
@@ -233,11 +238,13 @@ func CalculatePrice(settings *Settings, jwtClaims map[string]interface{}, params
233238
234239 price .Items = append (price .Items , itemPrice )
235240
236- price .Subtotal += (itemPrice .Subtotal * itemPrice .Quantity )
237- price .Discount += (itemPrice .Discount * itemPrice .Quantity )
238- price .NetTotal += (itemPrice .NetTotal * itemPrice .Quantity )
239- price .Taxes += (itemPrice .Taxes * itemPrice .Quantity )
240- price .Total += (itemPrice .Total * int64 (itemPrice .Quantity ))
241+ // avoid issues with rounding when multiplying by quantity before taxation
242+ itemPriceMultiple := calculateAmountsForSingleItem (settings , lineLogger , jwtClaims , params , item , item .GetQuantity ())
243+ price .Subtotal += itemPriceMultiple .Subtotal
244+ price .Discount += itemPriceMultiple .Discount
245+ price .NetTotal += itemPriceMultiple .NetTotal
246+ price .Taxes += itemPriceMultiple .Taxes
247+ price .Total += itemPriceMultiple .Total
241248 }
242249
243250 price .Total = int64 (price .NetTotal + price .Taxes )
0 commit comments