@@ -14,6 +14,7 @@ type Price struct {
14
14
15
15
Subtotal uint64
16
16
Discount uint64
17
+ NetTotal uint64
17
18
Taxes uint64
18
19
Total int64
19
20
}
@@ -24,6 +25,7 @@ type ItemPrice struct {
24
25
25
26
Subtotal uint64
26
27
Discount uint64
28
+ NetTotal uint64
27
29
Taxes uint64
28
30
Total int64
29
31
}
@@ -175,7 +177,6 @@ func (t *Tax) AppliesTo(country, productType string) bool {
175
177
// currency, country, coupons, and discounts.
176
178
func CalculatePrice (settings * Settings , jwtClaims map [string ]interface {}, params PriceParameters , log logrus.FieldLogger ) Price {
177
179
price := Price {}
178
- includeTaxes := settings != nil && settings .PricesIncludeTaxes
179
180
180
181
priceLogger := log .WithField ("action" , "calculate_price" )
181
182
if am , ok := jwtClaims ["app_metadata" ]; ok {
@@ -193,67 +194,39 @@ func CalculatePrice(settings *Settings, jwtClaims map[string]interface{}, params
193
194
})
194
195
195
196
itemPrice := ItemPrice {Quantity : item .GetQuantity ()}
196
- itemPrice .Subtotal = item .PriceInLowestUnit ()
197
-
198
- taxAmounts := []taxAmount {}
199
- if item .FixedVAT () != 0 {
200
- taxAmounts = append (taxAmounts , taxAmount {price : itemPrice .Subtotal , percentage : item .FixedVAT ()})
201
- } else if settings != nil && item .TaxableItems () != nil && len (item .TaxableItems ()) > 0 {
202
- for _ , item := range item .TaxableItems () {
203
- amount := taxAmount {price : item .PriceInLowestUnit ()}
204
- for _ , t := range settings .Taxes {
205
- if t .AppliesTo (params .Country , item .ProductType ()) {
206
- amount .percentage = t .Percentage
207
- break
208
- }
209
- }
210
- taxAmounts = append (taxAmounts , amount )
211
- }
212
- } else if settings != nil {
213
- for _ , t := range settings .Taxes {
214
- if t .AppliesTo (params .Country , item .ProductType ()) {
215
- taxAmounts = append (taxAmounts , taxAmount {price : itemPrice .Subtotal , percentage : t .Percentage })
216
- break
217
- }
218
- }
219
- }
220
197
221
- if len (taxAmounts ) != 0 {
222
- if includeTaxes {
223
- itemPrice .Subtotal = 0
224
- }
225
- for _ , tax := range taxAmounts {
226
- if includeTaxes {
227
- tax .price = rint (float64 (tax .price ) / (100 + float64 (tax .percentage )) * 100 )
228
- itemPrice .Subtotal += tax .price
229
- }
230
- itemPrice .Taxes += rint (float64 (tax .price ) * float64 (tax .percentage ) / 100 )
231
- }
232
- }
198
+ singlePrice := item .PriceInLowestUnit ()
199
+ _ , itemPrice .Subtotal = calculateTaxes (singlePrice , item , params , settings )
233
200
201
+ // apply discount to original price
234
202
coupon := params .Coupon
235
203
if coupon != nil && coupon .ValidForType (item .ProductType ()) && coupon .ValidForProduct (item .ProductSku ()) {
236
- itemPrice .Discount = calculateDiscount (itemPrice . Subtotal , itemPrice . Taxes , coupon .PercentageDiscount (), coupon .FixedDiscount (params .Currency ), includeTaxes )
204
+ itemPrice .Discount = calculateDiscount (singlePrice , coupon .PercentageDiscount (), coupon .FixedDiscount (params .Currency ))
237
205
}
238
206
if settings != nil && settings .MemberDiscounts != nil {
239
207
for _ , discount := range settings .MemberDiscounts {
240
208
241
209
if jwtClaims != nil && claims .HasClaims (jwtClaims , discount .Claims ) && discount .ValidForType (item .ProductType ()) && discount .ValidForProduct (item .ProductSku ()) {
242
210
lineLogger = lineLogger .WithField ("discount" , discount .Claims )
243
- itemPrice .Discount += calculateDiscount (itemPrice . Subtotal , itemPrice . Taxes , discount .Percentage , discount .FixedDiscount (params .Currency ), includeTaxes )
211
+ itemPrice .Discount += calculateDiscount (singlePrice , discount .Percentage , discount .FixedDiscount (params .Currency ))
244
212
}
245
213
}
246
214
}
247
215
248
- itemPrice . Total = int64 ( itemPrice . Subtotal + itemPrice . Taxes ) - int64 ( itemPrice . Discount )
249
- if itemPrice .Total < 0 {
250
- itemPrice . Total = 0
216
+ discountedPrice := uint64 ( 0 )
217
+ if itemPrice .Discount < singlePrice {
218
+ discountedPrice = singlePrice - itemPrice . Discount
251
219
}
252
220
221
+ itemPrice .Taxes , itemPrice .NetTotal = calculateTaxes (discountedPrice , item , params , settings )
222
+
223
+ itemPrice .Total = int64 (itemPrice .NetTotal + itemPrice .Taxes )
224
+
253
225
lineLogger .WithFields (
254
226
logrus.Fields {
255
227
"item_price" : itemPrice .Total ,
256
228
"item_discount" : itemPrice .Discount ,
229
+ "item_nettotal" : itemPrice .NetTotal ,
257
230
"item_quantity" : itemPrice .Quantity ,
258
231
"item_taxes" : itemPrice .Taxes ,
259
232
}).Info ("calculated item price" )
@@ -262,28 +235,24 @@ func CalculatePrice(settings *Settings, jwtClaims map[string]interface{}, params
262
235
263
236
price .Subtotal += (itemPrice .Subtotal * itemPrice .Quantity )
264
237
price .Discount += (itemPrice .Discount * itemPrice .Quantity )
238
+ price .NetTotal += (itemPrice .NetTotal * itemPrice .Quantity )
265
239
price .Taxes += (itemPrice .Taxes * itemPrice .Quantity )
266
240
price .Total += (itemPrice .Total * int64 (itemPrice .Quantity ))
267
241
}
268
242
269
- price .Total = int64 (price .Subtotal + price .Taxes ) - int64 (price .Discount )
270
- if price .Total < 0 {
271
- price .Total = 0
272
- }
243
+ price .Total = int64 (price .NetTotal + price .Taxes )
273
244
priceLogger .WithFields (
274
245
logrus.Fields {
275
246
"total_price" : price .Total ,
276
247
"total_discount" : price .Discount ,
248
+ "total_net" : price .NetTotal ,
277
249
"total_taxes" : price .Taxes ,
278
250
}).Info ("calculated total price" )
279
251
280
252
return price
281
253
}
282
254
283
- func calculateDiscount (amountToDiscount , taxes , percentage , fixed uint64 , includeTaxes bool ) uint64 {
284
- if includeTaxes {
285
- amountToDiscount += taxes
286
- }
255
+ func calculateDiscount (amountToDiscount , percentage , fixed uint64 ) uint64 {
287
256
var discount uint64
288
257
if percentage > 0 {
289
258
discount = rint (float64 (amountToDiscount ) * float64 (percentage ) / 100 )
@@ -296,6 +265,54 @@ func calculateDiscount(amountToDiscount, taxes, percentage, fixed uint64, includ
296
265
return discount
297
266
}
298
267
268
+ func calculateTaxes (amountToTax uint64 , item Item , params PriceParameters , settings * Settings ) (taxes uint64 , subtotal uint64 ) {
269
+ includeTaxes := settings != nil && settings .PricesIncludeTaxes
270
+ originalPrice := item .PriceInLowestUnit ()
271
+
272
+ taxAmounts := []taxAmount {}
273
+ if item .FixedVAT () != 0 {
274
+ taxAmounts = append (taxAmounts , taxAmount {price : amountToTax , percentage : item .FixedVAT ()})
275
+ } else if settings != nil && item .TaxableItems () != nil && len (item .TaxableItems ()) > 0 {
276
+ for _ , item := range item .TaxableItems () {
277
+ // because a discount may have been applied we need to determine the real price of this sub-item
278
+ priceShare := float64 (item .PriceInLowestUnit ()) / float64 (originalPrice )
279
+ itemPrice := rint (float64 (amountToTax ) * priceShare )
280
+ amount := taxAmount {price : itemPrice }
281
+ for _ , t := range settings .Taxes {
282
+ if t .AppliesTo (params .Country , item .ProductType ()) {
283
+ amount .percentage = t .Percentage
284
+ break
285
+ }
286
+ }
287
+ taxAmounts = append (taxAmounts , amount )
288
+ }
289
+ } else if settings != nil {
290
+ for _ , t := range settings .Taxes {
291
+ if t .AppliesTo (params .Country , item .ProductType ()) {
292
+ taxAmounts = append (taxAmounts , taxAmount {price : amountToTax , percentage : t .Percentage })
293
+ break
294
+ }
295
+ }
296
+ }
297
+
298
+ taxes = 0
299
+ if len (taxAmounts ) == 0 {
300
+ subtotal = amountToTax
301
+ return
302
+ }
303
+
304
+ subtotal = 0
305
+ for _ , tax := range taxAmounts {
306
+ if includeTaxes {
307
+ tax .price = rint (float64 (tax .price ) / (100 + float64 (tax .percentage )) * 100 )
308
+ }
309
+ subtotal += tax .price
310
+ taxes += rint (float64 (tax .price ) * float64 (tax .percentage ) / 100 )
311
+ }
312
+
313
+ return
314
+ }
315
+
299
316
// Nopes - no `round` method in go
300
317
// See https://github.com/golang/go/blob/master/src/math/floor.go#L58
301
318
0 commit comments