Feature: voucher cumulation (#14)

* enable optional cumulation of multiple regular vouchers (overruling best option rule) //  fix bug where vouchers could falsely be cumulated when pricerule is identical // add unit tests

* fix pre-filtering of vouchers

* minor cleanup

* refactor: remove access to global test data from promo cumulation tests // add test for employee disscount

* feat: ExcludeAlreadyDiscountedItemsForVoucher does no longer apply to discounts with TypePromotionCustomer

* update unit tests for promo cumulation

* update unit tests for promo cumulation

* update pricerule cumulation tests

* pricerules: reimplement test for BuyXPayY

* add more tests for pricerules
This commit is contained in:
florianschlegel 2020-01-27 12:20:15 +01:00 committed by Frederik Löffert
parent dc4811e002
commit 18e788cf8d
8 changed files with 1139 additions and 533 deletions

View File

@ -57,6 +57,9 @@ func previouslyAppliedExclusionInPlace(rule *PriceRule, orderDiscountsForPositio
previouslyAppliedExclusion := false
if rule.Type == TypePromotionCustomer || rule.Type == TypePromotionProduct || rule.Type == TypeVoucher {
if rule.Type == TypeVoucher && rule.CumulateWithOtherVouchers {
return false
}
if calculationParameters.bestOptionCustomeProductRulePerItem != nil {
if bestRuleID, ok := calculationParameters.bestOptionCustomeProductRulePerItem[itemID]; ok {
if rule.ID == bestRuleID {

View File

@ -243,18 +243,52 @@ func ApplyDiscounts(articleCollection *ArticleCollection, existingDiscounts Orde
orderDiscounts = calculateRule(orderDiscounts, priceRulePair, calculationParameters)
}
// prepare step 2: filter vouchers
// Make sure only one voucher per promo is set, unless CumulateWithOtherVouchers is true (bonus vouchers are not filtered)
// (There is a bug in the best option calculation which applies multiple vouchers to the same item when voucher is from the same promo. This should fix it)
voucherCodesTmp := []string{}
priceruleIDs := map[string]bool{}
for _, voucherCode := range voucherCodes {
voucherVo, voucherPriceRule, err := GetVoucherAndPriceRule(voucherCode, customProvider)
if voucherVo == nil {
//log.Println("voucher not found for code: " + voucherCode + " in " + "priceRule.ApplyDiscounts")
continue
}
if err != nil {
log.Println("skipping voucher "+voucherCode, err)
continue
}
// bonus vouchers are not being filtered
if voucherPriceRule.Type == TypeBonusVoucher {
voucherCodesTmp = append(voucherCodesTmp, voucherCode)
continue
}
// just to be safe
if voucherPriceRule.Type != TypeVoucher {
continue
}
if priceruleIDs[voucherPriceRule.ID] && !voucherPriceRule.CumulateWithOtherVouchers {
continue // we already have a voucher for that promo
}
voucherCodesTmp = append(voucherCodesTmp, voucherCode)
priceruleIDs[voucherPriceRule.ID] = true
}
// replace vouchers with filtered ones
voucherCodes = voucherCodesTmp
// ----------------------------------------------------------------------------------------------------------
// vouchers: step 2
// find the vouchers and voucher rules
// find applicable pricerules of type TypeVoucher for
bonusVoucherCodes := []string{}
// step 2: vouchers where best option per items applies (CumulateWithOtherVouchers == false)
if len(voucherCodes) > 0 {
var ruleVoucherPairsStep2 []RuleVoucherPair
for _, voucherCode := range voucherCodes {
if len(voucherCode) > 0 {
voucherVo, voucherPriceRule, err := GetVoucherAndPriceRule(voucherCode, customProvider)
if voucherVo == nil {
log.Println("voucher not found for code: " + voucherCode + " in " + "priceRule.ApplyDiscounts")
continue
//log.Println("voucher not found for code: " + voucherCode + " in " + "priceRule.ApplyDiscounts")
}
if err != nil {
log.Println("skipping voucher "+voucherCode, err)
@ -262,9 +296,11 @@ func ApplyDiscounts(articleCollection *ArticleCollection, existingDiscounts Orde
}
if voucherPriceRule.Type != TypeVoucher {
log.Println("skipping voucher "+voucherCode+" with type Voucher", err)
bonusVoucherCodes = append(bonusVoucherCodes, voucherCode)
continue
continue // bonus vouchers are handled later in separate step
}
if voucherPriceRule.CumulateWithOtherVouchers {
continue // voucher will be applied in next step
}
//check if rule is valid
@ -312,6 +348,69 @@ func ApplyDiscounts(articleCollection *ArticleCollection, existingDiscounts Orde
}
}
// ----------------------------------------------------------------------------------------------------------
// step 2.1: vouchers which are applied on top of other vouchers (CumulateWithOtherVouchers == true)
if len(voucherCodes) > 0 {
var ruleVoucherPairsStep21 []RuleVoucherPair
for _, voucherCode := range voucherCodes {
if len(voucherCode) > 0 {
voucherVo, voucherPriceRule, err := GetVoucherAndPriceRule(voucherCode, customProvider)
if voucherVo == nil {
//log.Println("voucher not found for code: " + voucherCode + " in " + "priceRule.ApplyDiscounts")
continue
}
if err != nil {
log.Println("skipping voucher "+voucherCode, err)
continue
}
if voucherPriceRule.Type != TypeVoucher {
continue // bonus vouchers are handled later in separate step
}
if !voucherPriceRule.CumulateWithOtherVouchers {
continue
}
//check if rule is valid
if time.Now().Before(voucherPriceRule.ValidFrom) || time.Now().After(voucherPriceRule.ValidTo) {
log.Println("skipping vocucher" + voucherCode + " VlidFrom/ValidTo mismatch")
continue
}
//filter out the vouchers that can not be applied due to a mismatch with checkoutAttributes
if len(voucherPriceRule.CheckoutAttributes) > 0 {
match := false
for _, checkoutAttribute := range checkoutAttributes {
if contains(checkoutAttribute, voucherPriceRule.CheckoutAttributes) {
match = true
break
}
}
if !match {
continue
}
}
if !voucherVo.TimeRedeemed.IsZero() {
if Verbose {
log.Println("voucher " + voucherCode + " already redeemed ... skipping")
}
continue
}
pair := RuleVoucherPair{
Rule: voucherPriceRule,
Voucher: voucherVo,
}
ruleVoucherPairsStep21 = append(ruleVoucherPairsStep21, pair)
}
}
//apply them
sort.Sort(ByPriority(ruleVoucherPairsStep21))
for _, priceRulePair := range ruleVoucherPairsStep21 {
orderDiscounts = calculateRule(orderDiscounts, priceRulePair, calculationParameters)
}
}
// ----------------------------------------------------------------------------------------------------------
// shipping - step 3
// shipping costs handling
@ -335,61 +434,57 @@ func ApplyDiscounts(articleCollection *ArticleCollection, existingDiscounts Orde
}
// ----------------------------------------------------------------------------------------------------------
// ----------------------------------------------------------------------------------------------------------
// vouchers: step 5
// bonus vouchers: step 5
// find the vouchers and voucher rules
// find applicable pricerules of type TypeVoucher for
if len(bonusVoucherCodes) > 0 {
var ruleVoucherPairsStep5 []RuleVoucherPair
for _, voucherCode := range bonusVoucherCodes {
if len(voucherCode) > 0 {
voucherVo, voucherPriceRule, err := GetVoucherAndPriceRule(voucherCode, customProvider)
if voucherVo == nil {
log.Println("voucher not found for code: " + voucherCode + " in " + "priceRule.ApplyDiscounts")
}
if err != nil {
log.Println("skipping voucher "+voucherCode, err)
continue
}
if voucherPriceRule.Type != TypeBonusVoucher {
log.Println("skipping voucher "+voucherCode+" with type BonusVoucher", err)
continue
}
//check if rule is valid
if time.Now().Before(voucherPriceRule.ValidFrom) || time.Now().After(voucherPriceRule.ValidTo) {
log.Println("skipping vocucher" + voucherCode + " VlidFrom/ValidTo mismatch")
continue
}
if !voucherVo.TimeRedeemed.IsZero() {
if Verbose {
log.Println("voucher " + voucherCode + " already redeemed ... skipping")
}
continue
}
pair := RuleVoucherPair{
Rule: voucherPriceRule,
Voucher: voucherVo,
}
ruleVoucherPairsStep5 = append(ruleVoucherPairsStep5, pair)
var ruleVoucherPairsStep5 []RuleVoucherPair
for _, voucherCode := range voucherCodes {
if len(voucherCode) > 0 {
voucherVo, voucherPriceRule, err := GetVoucherAndPriceRule(voucherCode, customProvider)
if voucherVo == nil {
//log.Println("voucher not found for code: " + voucherCode + " in " + "priceRule.ApplyDiscounts")
continue
}
if err != nil {
log.Println("skipping voucher "+voucherCode, err)
continue
}
if voucherPriceRule.Type != TypeBonusVoucher {
continue
}
//check if rule is valid
if time.Now().Before(voucherPriceRule.ValidFrom) || time.Now().After(voucherPriceRule.ValidTo) {
log.Println("skipping vocucher" + voucherCode + " VlidFrom/ValidTo mismatch")
continue
}
if !voucherVo.TimeRedeemed.IsZero() {
if Verbose {
log.Println("voucher " + voucherCode + " already redeemed ... skipping")
}
continue
}
pair := RuleVoucherPair{
Rule: voucherPriceRule,
Voucher: voucherVo,
}
ruleVoucherPairsStep5 = append(ruleVoucherPairsStep5, pair)
}
//apply them
}
//apply them
//no products should be blacklisted
calculationParameters.blacklistedItemIDs = []string{}
//no products should be blacklisted
calculationParameters.blacklistedItemIDs = []string{}
// the bonus voucher should be applicable on shipping costs as well
calculationParameters.shippingGroupIDs = []string{}
// the bonus voucher should be applicable on shipping costs as well
calculationParameters.shippingGroupIDs = []string{}
for _, priceRulePair := range ruleVoucherPairsStep5 {
orderDiscounts = calculateRule(orderDiscounts, priceRulePair, calculationParameters)
}
for _, priceRulePair := range ruleVoucherPairsStep5 {
orderDiscounts = calculateRule(orderDiscounts, priceRulePair, calculationParameters)
}
timeTrack(nowAll, "All rules together")
@ -720,9 +815,17 @@ func validatePriceRule(priceRule PriceRule, checkedPosition *Article, calculatio
if !checkedPosition.AllowCrossPriceCalculation {
return false, ValidationVoucherNotApplicableToAlreadyDiscountedItemsBySap
}
// Check if there is already any webshop promo discount
discounts, ok := orderDiscounts[checkedPosition.ID]
if ok && len(discounts.AppliedDiscounts) > 0 {
// filter all employee discounts, these are an exception for flag ExcludeAlreadyDiscountedItemsForVoucher
filteredDiscounts := filterDiscounts(discounts.AppliedDiscounts, func(d DiscountApplied) bool {
if !d.IsTypePromotionCustomer {
return true
}
return false
})
if ok && len(filteredDiscounts) > 0 {
return false, ValidationVoucherNotApplicableToAlreadyDiscountedItemsByWebshop
}
}
@ -1002,3 +1105,13 @@ func getShippingGroupIDs() (itemIDs []string, err error) {
itemIDs = RemoveDuplicates(itemIDs)
return
}
func filterDiscounts(discounts []DiscountApplied, f func(DiscountApplied) bool) []DiscountApplied {
filtered := []DiscountApplied{}
for _, discount := range discounts {
if f(discount) {
filtered = append(filtered, discount)
}
}
return filtered
}

View File

@ -60,14 +60,8 @@ func LoadGroup(ID string, customProvider PriceRuleCustomProvider) (*Group, error
return GetGroupByID(ID, customProvider)
}
// RemoveAllProductIds - clear all product IDs
func (group *Group) RemoveAllProductIds() bool {
group.ItemIDs = []string{}
return true
}
// AddGroupItemIDsAndPersist - appends removes duplicates and persists
func (group *Group) AddGroupItemIDsAndPersist(itemIDs []string) bool {
func (group *Group) AddGroupItemIDsAndPersist(itemIDs []string) error {
group.AddGroupItemIDs(itemIDs)
//addtoset
@ -76,17 +70,17 @@ func (group *Group) AddGroupItemIDsAndPersist(itemIDs []string) bool {
_, err := collection.Upsert(bson.M{"id": group.ID}, group)
if err != nil {
return false
return err
}
return true
return nil
}
// AddGroupItemIDs - appends removes duplicates and persists
func (group *Group) AddGroupItemIDs(itemIDs []string) bool {
func (group *Group) AddGroupItemIDs(itemIDs []string) {
var ids = append(group.ItemIDs, itemIDs...)
group.ItemIDs = RemoveDuplicates(ids)
return true
return
}
// GroupAlreadyExistsInDB checks if a Group with given ID already exists in the database

View File

@ -129,11 +129,16 @@ type PriceRule struct {
Custom interface{} `bson:",omitempty"` //make it extensible if needed (included, excluded group IDs)
ExcludeAlreadyDiscountedItemsForVoucher bool
// if true, voucher is not applied when item already has a discount (SAP or webshop pricerule)
// exception: employee discount is always granted when available
ExcludeAlreadyDiscountedItemsForVoucher bool // flag for vouchers only.
ExcludesEmployeesForVoucher bool // Note: exclusion of employees must actually be configured by setting IncludedCustomerGroupIDS/ExcludedCustomerGroupIDS.
// Note: exclusion of employees must actually be configured by setting IncludedCustomerGroupIDS/ExcludedCustomerGroupIDS.
// This flag is used for external validation purposes and only provides the information that this promo is supposed to exclude employees.
// It has no effect on the promo calculation itself!
ExcludesEmployeesForVoucher bool // flag for vouchers only
CumulateWithOtherVouchers bool // flag for vouchers only. If true, voucher will be applied on top of other vouchers (best option rule skipped)
}
//Type the type of the price rule

View File

@ -0,0 +1,654 @@
package pricerule
import (
"testing"
"github.com/foomo/shop/utils"
"github.com/stretchr/testify/assert"
)
func TestCumulationTwoVouchers_OnePerSku(t *testing.T) {
// Cart with 2 Items
// Expected result: one of the vouchers is applied to each item
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku1", 10.0, []string{helper.GroupIDSingleSku1}, false, false)
voucherCode2 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku2", 10.0, []string{helper.GroupIDSingleSku2}, false, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 10% on 30 CHF
assert.Equal(t, 3.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForSameSku(t *testing.T) {
// Cart with 2 Items
// Both voucher are only valid for one of the items
// Expected result: One voucher is being applied, the other one is dismissed
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku1", 10.0, []string{helper.GroupIDSingleSku1}, false, false)
voucherCode2 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku2", 10.0, []string{helper.GroupIDSingleSku1}, false, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// only one voucher should be applied => 1 CHF
assert.Equal(t, 1.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForBothSkus(t *testing.T) {
// Cart with 2 Items
// Both voucher are valid for both of the items
// Expected result: The better voucher is applied to both items
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-1", 5.0, []string{helper.GroupIDTwoSkus}, false, false)
voucherCode2 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-2", 10.0, []string{helper.GroupIDTwoSkus}, false, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 10% on 30 CHF
assert.Equal(t, 3.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForSameSku_AdditonalCrossPrice(t *testing.T) {
// Cart with 2 Items
// Both voucher are only valid for one of the items
// Expected result: - both items discounted by product promo
// - One voucher is being applied, the other one is dismissed
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "crossprice1", 5.0, false, []string{helper.GroupIDTwoSkus})
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku1", 5.0, []string{helper.GroupIDSingleSku1}, false, false)
voucherCode2 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku2", 10.0, []string{helper.GroupIDSingleSku1}, false, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// helper.Sku1: 5 + 0.5, helper.Sku2: 5 => 10.5
assert.Equal(t, 10.5, summary.TotalDiscountApplicable)
}
func TestCumulationProductPromo(t *testing.T) {
// Cart with 2 Items
// Expected result: - only better cross price should be applied
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "crossprice1", 2.0, false, []string{helper.GroupIDTwoSkus})
helper.setMockPriceRuleCrossPrice(t, "crossprice2", 5.0, false, []string{helper.GroupIDTwoSkus})
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{}, []string{}, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5+5 (5 on each product) = 10 CHF
assert.Equal(t, 10.0, summary.TotalDiscountApplicable)
}
func TestCumulationForExcludeVoucherOnCrossPriceWebhop(t *testing.T) {
// Cart with 2 Items
// Expected result: voucher is only applied to sku2. helper.Sku1 is skipped due to existing webshop cross-price
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "crossprice1", 5.0, false, []string{helper.GroupIDSingleSku1})
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-1", 10.0, []string{helper.GroupIDTwoSkus}, true, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// helper.Sku1: 5, helper.Sku2: 2
assert.Equal(t, 7.0, summary.TotalDiscountApplicable)
}
func TestCumulationForExcludeVoucherOnCrossPriceSAP(t *testing.T) {
// Cart with 2 Items
// Expected result: voucher is only applied to sku2. helper.Sku1 is skipped due to existing SAP cross-price
// (indicated by AllowCrossPriceCalculation == false)
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
for i, article := range articleCollection.Articles {
if i == 0 {
article.AllowCrossPriceCalculation = false // this is false if there already is a SAP crossprice
}
}
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-1", 10.0, []string{helper.GroupIDTwoSkus}, true, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// helper.Sku2: 2 CHF
assert.Equal(t, 2.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForBothSkus_BothApplied(t *testing.T) {
// Cart with 2 Items (CHF 20 and CHF 10)
// Both voucher are valid for both of the items
// Expected result: Both voucher are applied because on voucher has set CumulateWithOtherVouchers = true
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1, _ := helper.setMockPriceRuleAndVoucherAbsoluteCHF20(t, true)
voucherCode2 := helper.setMockPriceRuleAndVoucher10Percent(t, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 20 CHF (absolute) + 3 (10%) = 23
assert.Equal(t, 23.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForBothSkus_SamePromo_BothApplied(t *testing.T) {
// Cart with 2 Items (CHF 20 and CHF 10)
// Both voucher are valid for both of the items
// Vouchers are of same promo
// CumulateWithOtherVouchers = true
// Expected result: Both voucher are applied because CumulateWithOtherVouchers = true
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1, voucherCode2 := helper.setMockPriceRuleAndVoucherAbsoluteCHF20(t, true)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 20 CHF (absolute) + 10 (20 CHF but only 10 applicable) = 30
assert.Equal(t, 30.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForBothSkus_SamePromoOnlyOneApplied(t *testing.T) {
// Cart with 2 Items (CHF 20 and CHF 10)
// Both voucher are valid for both of the items
// Vouchers are of same promo
// CumulateWithOtherVouchers = false
// Expected result: Only one voucher is applied because CumulateWithOtherVouchers = true
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1, voucherCode2 := helper.setMockPriceRuleAndVoucherAbsoluteCHF20(t, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 20 CHF (absolute) 0 = 20
assert.Equal(t, 20.0, summary.TotalDiscountApplicable)
}
func TestCumulationBonusVoucher(t *testing.T) {
// Cart with 2 Items (CHF 20 and CHF 10)
// Both bonus vouchers should be applied
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1, voucherCode2 := helper.setMockBonusVoucherPriceRule(t)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 10 +10 = 20 CHF
assert.Equal(t, 20.0, summary.TotalDiscountApplicable)
}
func TestCumulationExcludeEmployeesFromVoucher(t *testing.T) {
// Cart with 2 Items
// Expected result: only regular customer gets discount
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku1", 10.0, []string{helper.GroupIDTwoSkus}, false, true)
// Get Discounts for regular customer
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 10% on 30 CHF
assert.Equal(t, 3.0, summary.TotalDiscountApplicable)
// Change customer type to employee => no discount
articleCollection.CustomerType = helper.CustomerGroupEmployee
discounts, summary, errApply = ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// No discount for employee
assert.Equal(t, 0.0, summary.TotalDiscountApplicable)
}
func TestCumulationEmployeeDiscount(t *testing.T) {
// Cart with 2 Items
// Expected result: only employee customer gets discount
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
helper.setMockEmployeeDiscount10Percent(t, "employee-discount", []string{helper.GroupIDTwoSkus})
// no employee discuonts for regular customer
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
assert.Equal(t, 0.0, summary.TotalDiscountApplicable)
// Change customer type to eomplyee => discount applied
articleCollection.CustomerType = helper.CustomerGroupEmployee
discounts, summary, errApply = ApplyDiscounts(articleCollection, nil, []string{}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// No discount for non-regular customer
assert.Equal(t, 3.0, summary.TotalDiscountApplicable)
}
func TestCumulationCrossPriceAndEmployeeDiscount(t *testing.T) {
// Cart with 2 Items
// Expected result: regular customer gets crossprice, employee gets additional employee discount
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "cross-price", 5.0, false, []string{helper.GroupIDTwoSkus})
helper.setMockEmployeeDiscount10Percent(t, "employee-discount", []string{helper.GroupIDTwoSkus})
// no employee discounts for regular customer
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5+5 CHF Crossprices, no employee discount
assert.Equal(t, 10.0, summary.TotalDiscountApplicable)
// Change customer type to eomplyee => discount applied
articleCollection.CustomerType = helper.CustomerGroupEmployee
discounts, summary, errApply = ApplyDiscounts(articleCollection, nil, []string{}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5+5 CHF Crossprices + 2 CHF employee discount
assert.Equal(t, 12.0, summary.TotalDiscountApplicable)
}
func TestCumulationEmployeeDiscountAndVoucherIncluceDiscountedItems(t *testing.T) {
// - Cart with 2 Items
// - Cross-price on Sku1
// - Employee discount on both items
// - 10% Voucher
// Expected result: Employee discount is granted regardless of flag ExcludeAlreadyDiscountedItemsForVoucher
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "cross-price", 5.0, false, []string{helper.GroupIDSingleSku1})
helper.setMockEmployeeDiscount10Percent(t, "employee-discount", []string{helper.GroupIDTwoSkus})
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku1", 10.0, []string{helper.GroupIDTwoSkus}, false, false)
// no employee discounts for regular customer
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5 CHF crossprice for Sku1 + 2.5 CHF voucher discount
assert.Equal(t, 7.5, summary.TotalDiscountApplicable)
// Change customer type to employee => additional employee discount
articleCollection.CustomerType = helper.CustomerGroupEmployee
discounts, summary, errApply = ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5 CHF crossprice for Sku1 + 2.5 CHF employee discount + 2.25 CHF voucher discount
assert.Equal(t, 9.75, summary.TotalDiscountApplicable)
}
func TestCumulationEmployeeDiscountAndVoucherExcludeDiscountedItems(t *testing.T) {
// - Cart with 2 Items
// - Cross-price on Sku1
// - Employee discount on both items
// - 10% Voucher
// Expected result: Employee discount is granted regardless of flag ExcludeAlreadyDiscountedItemsForVoucher
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "cross-price", 5.0, false, []string{helper.GroupIDSingleSku1})
helper.setMockEmployeeDiscount10Percent(t, "employee-discount", []string{helper.GroupIDTwoSkus})
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku1", 10.0, []string{helper.GroupIDTwoSkus}, true, false)
// no employee discounts for regular customer
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5 CHF crossprice for Sku1 + 2 CHF voucher discount
assert.Equal(t, 7.0, summary.TotalDiscountApplicable)
// Change customer type to employee => additional employee discount
articleCollection.CustomerType = helper.CustomerGroupEmployee
discounts, summary, errApply = ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5 CHF crossprice for Sku1 + 0.5+2.0 employee discount + 1.8 CHF voucher discount (for Sku2)
assert.Equal(t, 9.3, summary.TotalDiscountApplicable)
}
func TestCumulationEmployeeDiscountAndVoucherExcludeDiscountedItemsSAPCrossPrice(t *testing.T) {
// Same as TestCumulationEmployeeDiscountAndVoucherExcludeDiscountedItems but with SAP cross-price instead of webshop cross-price
// - Cart with 2 Items
// - Cross-price on Sku1
// - Employee discount on both items
// - 10% Voucher
// Expected result: Employee discount is granted regardless of flag ExcludeAlreadyDiscountedItemsForVoucher
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
// Simulate SAP cross price
articleCollection.Articles[0].Price = 5.0
articleCollection.Articles[0].AllowCrossPriceCalculation = false
helper.setMockEmployeeDiscount10Percent(t, "employee-discount", []string{helper.GroupIDTwoSkus})
voucherCode1 := helper.setMockPriceRuleAndVoucherXPercent(t, "voucher-sku1", 10.0, []string{helper.GroupIDTwoSkus}, true, false)
// no employee discounts for regular customer
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 2 CHF voucher discount (Sku2)
assert.Equal(t, 2.0, summary.TotalDiscountApplicable)
// Change customer type to employee => additional employee discount
articleCollection.CustomerType = helper.CustomerGroupEmployee
discounts, summary, errApply = ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.NoError(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 0.5+2.0 employee discount + 1.8 CHF voucher discount (for Sku2)
assert.Equal(t, 4.3, summary.TotalDiscountApplicable)
}
func TestCrossPriceAndBuyXGetY(t *testing.T) {
// Expected: Crossprice and BuyXPayY are both applied
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
allowCrossPriceCalculation := true
// add 3rd article required for BuyXPayY Promo
articleCollection.Articles = append(articleCollection.Articles, &Article{
ID: helper.Sku3,
Price: 50.0,
CrossPrice: 50.0,
Quantity: 1,
AllowCrossPriceCalculation: allowCrossPriceCalculation,
})
// adjuts prices
articleCollection.Articles[0].Price = 100.0
articleCollection.Articles[1].Price = 50.0
helper.setMockPriceRuleCrossPrice(t, "crossprice1", 10.0, false, []string{helper.GroupIDThreeSkus})
helper.setMockPriceRuleBuy3Pay2(t, "buyXPayY", []string{helper.GroupIDThreeSkus})
tests := []struct {
qty1 float64
qty2 float64
qty3 float64
expectedDiscountOrderService float64
expectedDiscountCatalogue float64
}{
{1.0, 1.0, 1.0, 70.0, 30.0}, // 10+10+10+10+40=70
{2.0, 1.0, 1.0, 80.0, 40.0},
{2.0, 2.0, 2.0, 140.0, 60.0}, // 2 items free
{2.0, 1.0, 0.0, 70.0, 30.0},
{0.0, 1.0, 1.0, 20.0, 20.0}, // only cross prices
}
for i, tt := range tests {
// Order -------------------------------------------------------------------------------
articleCollection.Articles[0].Quantity = tt.qty1
articleCollection.Articles[1].Quantity = tt.qty2
articleCollection.Articles[2].Quantity = tt.qty3
// Calculation for orderservice
_, summary, err := ApplyDiscounts(articleCollection, nil, []string{""}, []string{}, 0.05, nil)
if err != nil {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectedDiscountOrderService, summary.TotalDiscount, "case orderservice", i)
// Calculation for catalogue
discountsCatalogue, _, err := ApplyDiscountsOnCatalog(articleCollection, nil, 0.05, nil)
if err != nil {
assert.NoError(t, err)
}
// Note: In catalalogue calculation summary is always empty, therefore we have to get the data directly from the discounts
assert.Equal(t, tt.expectedDiscountCatalogue, helper.accumulateDiscountsOfItems(discountsCatalogue), "case catalogue", i)
}
}
func TestCrossPriceAndEmployeeDiscount(t *testing.T) {
// Expected: Crossprice and BuyXPayY are both applied
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
articleCollection.CustomerType = helper.CustomerGroupEmployee
includedProdcuts := []string{helper.GroupIDTwoSkus}
helper.setMockEmployeeDiscount10Percent(t, "employee-discount", includedProdcuts)
tests := []struct {
customerType string
amountCrossPrice float64
isCrosspricePercent bool
expectedDiscountOrderService float64
expectedDiscountCatalogue float64
}{
{helper.CustomerGroupRegular, 5.0, false, 10.0, 10.0},
{helper.CustomerGroupRegular, 10.0, true, 3.0, 3.0},
{helper.CustomerGroupEmployee, 5.0, false, 12.0, 10.0},
{helper.CustomerGroupEmployee, 10.0, true, 5.7, 3.0}, //@todo actual caculated result is 5.699999999999999 => fix rounding errors
}
for i, tt := range tests {
articleCollection.CustomerType = tt.customerType
helper.setMockPriceRuleCrossPrice(t, "crossprice1", tt.amountCrossPrice, tt.isCrosspricePercent, includedProdcuts)
// Calculation for orderservice
_, summary, err := ApplyDiscounts(articleCollection, nil, []string{""}, []string{}, 0.05, nil)
if err != nil {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectedDiscountOrderService, utils.Round(summary.TotalDiscount, 2), "case orderservice", i)
// Calculation for catalogue
discountsCatalogue, _, err := ApplyDiscountsOnCatalog(articleCollection, nil, 0.05, nil)
if err != nil {
assert.NoError(t, err)
}
// Note: In catalalogue calculation summary is always empty, therefore we have to get the data directly from the discounts
assert.Equal(t, tt.expectedDiscountCatalogue, helper.accumulateDiscountsOfItems(discountsCatalogue), "case catalogue", i)
}
}
func TestQuantityThreshold(t *testing.T) {
// Expected: Discount is applied if qty of one item is at least equal to threshold
// Note: if threshold is met for ONE item, discount will be applied to ALL products eligible for this promo
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
tests := []struct {
includedProducts []string
amount float64
threshold float64
qty1 float64
qty2 float64
expectedDiscount float64
}{
{[]string{helper.GroupIDTwoSkus}, 1.0, 3.0, 1.0, 1.0, 0.0},
{[]string{helper.GroupIDTwoSkus}, 1.0, 2.0, 1.0, 1.0, 2.0},
{[]string{helper.GroupIDTwoSkus}, 1.0, 2.0, 2.0, 1.0, 3.0},
{[]string{helper.GroupIDTwoSkus}, 1.0, 2.0, 2.0, 0.0, 2.0},
{[]string{helper.GroupIDTwoSkus}, 1.0, 2.0, 2.0, 2.0, 4.0},
{[]string{helper.GroupIDSingleSku1}, 1.0, 2.0, 2.0, 2.0, 2.0},
{[]string{helper.GroupIDSingleSku1}, 1.0, 2.0, 1.0, 1.0, 0.0},
}
for i, tt := range tests {
articleCollection.Articles[0].Quantity = tt.qty1
articleCollection.Articles[1].Quantity = tt.qty2
helper.setMockPriceRuleQtyThreshold(t, "qty-threshold", tt.amount, tt.threshold, tt.includedProducts)
_, summary, err := ApplyDiscounts(articleCollection, nil, []string{""}, []string{}, 0.05, nil)
if err != nil {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectedDiscount, summary.TotalDiscount, "case ", i)
}
}
func TestThresholdAmount(t *testing.T) {
// Expected: Discount is applied if threshold amount is met by all eligible items
helper := newTesthelper()
defer helper.cleanupTestData(t)
helper.cleanupAndRecreateTestData(t)
articleCollection := helper.getMockArticleCollection()
tests := []struct {
includedProducts []string
thresholdAmount float64
qty1 float64
qty2 float64
expectedDiscount float64
}{
{[]string{helper.GroupIDTwoSkus}, 30.0, 1.0, 1.0, 10.0},
{[]string{helper.GroupIDTwoSkus}, 40.0, 1.0, 1.0, 0.0},
{[]string{helper.GroupIDTwoSkus}, 30.0, 0.0, 2.0, 10.0},
{[]string{helper.GroupIDTwoSkus}, 30.0, 1.0, 1.0, 10.0},
{[]string{helper.GroupIDTwoSkus}, 40.0, 1.0, 1.0, 0.0},
{[]string{helper.GroupIDTwoSkus}, 30.0, 0.0, 2.0, 10.0},
}
for i, tt := range tests {
articleCollection.Articles[0].Quantity = tt.qty1
articleCollection.Articles[1].Quantity = tt.qty2
helper.setMockPriceRuleThresholdAmount(t, "qty-threshold", 5.0, tt.thresholdAmount, tt.includedProducts)
_, summary, err := ApplyDiscounts(articleCollection, nil, []string{""}, []string{}, 0.05, nil)
if err != nil {
assert.NoError(t, err)
}
assert.Equal(t, tt.expectedDiscount, summary.TotalDiscount, "case ", i)
}
}

View File

@ -64,89 +64,12 @@ const (
CustomerID1 = "CustomerID1"
CustomerID2 = "CustomerID2"
CustomerGroupID1 = "CustomerGroupID1 - super customer"
CustomerGroupID2 = "CustomerGroupID2 - employee"
CustomerGroupRegular = "regular-customer"
CustomerGroupEmployee = "employee"
)
var productsInGroups map[string][]string
func Init(t *testing.T) {
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSingleSku1] = []string{Sku1}
productsInGroups[GroupIDSingleSku2] = []string{Sku2}
productsInGroups[GroupIDTwoSkus] = []string{Sku1, Sku2}
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
productsInGroups[GroupIDNormal] = []string{ProductID4, ProductID5, ProductID4SKU1, ProductID4SKU2, ProductID5SKU1, ProductID5SKU2}
productsInGroups[GroupIDShirts] = []string{ProductID3, ProductID4, ProductID5, ProductID3SKU1, ProductID4SKU1, ProductID5SKU1, ProductID3SKU2, ProductID4SKU2, ProductID5SKU2}
RemoveAllGroups()
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
createMockPriceRules(t)
checkPriceRulesExists(t)
createMockVouchers(t)
checkVouchersExists(t)
}
func TestBuyXPayY(t *testing.T) {
//remove all and add again
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
productsInGroups[GroupIDNormal] = []string{ProductID4, ProductID5, ProductID4SKU1, ProductID4SKU2, ProductID5SKU1, ProductID5SKU2}
productsInGroups[GroupIDShirts] = []string{ProductID3, ProductID4, ProductID5, ProductID3SKU1, ProductID4SKU1, ProductID5SKU1, ProductID3SKU2, ProductID4SKU2, ProductID5SKU2}
RemoveAllGroups()
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
orderVo := &ArticleCollection{}
orderVo.CustomerID = CustomerID1
orderVo.CustomerType = CustomerID1
// Position with 2 qnt
positionVo := &Article{}
positionVo.ID = ProductID1
positionVo.Price = 10
positionVo.Quantity = 3
orderVo.Articles = append(orderVo.Articles, positionVo)
priceRule := NewPriceRule(PriceRuleIDSaleVoucher)
priceRule.Name = map[string]string{
"de": PriceRuleIDSaleVoucher,
"fr": PriceRuleIDSaleVoucher,
"it": PriceRuleIDSaleVoucher,
}
priceRule.Type = TypePromotionProduct
priceRule.Description = priceRule.Name
priceRule.Action = ActionBuyXPayY
priceRule.Amount = 20.0
priceRule.X = 3
priceRule.Y = 2
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
err := priceRule.Upsert()
if err != nil {
panic(err)
}
discountsVo, summary, err := ApplyDiscounts(orderVo, nil, []string{}, []string{}, 0.05, nil)
utils.PrintJSON(discountsVo)
utils.PrintJSON(summary)
utils.PrintJSON(err)
}
func TestQntThreshold(t *testing.T) {
//remove all and add again
productsInGroups = make(map[string][]string)
@ -158,7 +81,7 @@ func TestQntThreshold(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
@ -198,7 +121,8 @@ func TestQntThreshold(t *testing.T) {
}
func testBonuVoucher(t *testing.T) {
func TestBonuVoucher(t *testing.T) {
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
productsInGroups[GroupIDNormal] = []string{ProductID4, ProductID5, ProductID4SKU1, ProductID4SKU2, ProductID5SKU1, ProductID5SKU2}
@ -284,7 +208,8 @@ func testBonuVoucher(t *testing.T) {
}
// Test groups creation
func testGetApplicableVouchers(t *testing.T) {
func TestGetApplicableVouchers(t *testing.T) {
//remove all and add again
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
@ -295,7 +220,7 @@ func testGetApplicableVouchers(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
orderVo, err := createMockOrder(t)
@ -316,7 +241,7 @@ func testGetApplicableVouchers(t *testing.T) {
priceRule.Amount = 20.0
priceRule.Priority = 800
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
err = priceRule.Upsert()
if err != nil {
panic(err)
@ -370,7 +295,7 @@ func testGetApplicableVouchers(t *testing.T) {
}
func testShipping1(t *testing.T) {
func TestShipping1(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
@ -514,7 +439,8 @@ func testShipping1(t *testing.T) {
}
func testBlacklist(t *testing.T) {
func TestBlacklist(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
RemoveAllVouchers()
@ -602,7 +528,8 @@ func testBlacklist(t *testing.T) {
}
func testDiscountDistribution(t *testing.T) {
func TestDiscountDistribution(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
RemoveAllVouchers()
@ -725,7 +652,8 @@ func testDiscountDistribution(t *testing.T) {
}
func testBestOption(t *testing.T) {
func TestBestOption(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
RemoveAllVouchers()
@ -856,7 +784,8 @@ func testBestOption(t *testing.T) {
}
func testDiscountFoItemSets(t *testing.T) {
func TestDiscountFoItemSets(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
RemoveAllVouchers()
@ -930,7 +859,8 @@ func testDiscountFoItemSets(t *testing.T) {
}
func testVoucherRuleWithCheckoutAttributes(t *testing.T) {
func TestVoucherRuleWithCheckoutAttributes(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
RemoveAllVouchers()
@ -1003,7 +933,7 @@ func testVoucherRuleWithCheckoutAttributes(t *testing.T) {
}
func testShipping(t *testing.T) {
func TestShipping(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
@ -1078,8 +1008,7 @@ func testShipping(t *testing.T) {
}
func testCache(t *testing.T) {
Init(t)
func TestCache(t *testing.T) {
RemoveAllGroups()
RemoveAllPriceRules()
@ -1145,10 +1074,8 @@ func testCache(t *testing.T) {
}
// Test groups creation
func testScaled(t *testing.T) {
//Init
func TestScaled(t *testing.T) {
//Init
RemoveAllGroups()
RemoveAllPriceRules()
@ -1220,80 +1147,8 @@ func testScaled(t *testing.T) {
}
// Test groups creation
func TestBuyXGetY(t *testing.T) {
//Init
RemoveAllGroups()
RemoveAllPriceRules()
func TestExclude(t *testing.T) {
//create group --------------------------------------------------------------------
groupID := "discounted"
group := new(Group)
group.Type = ProductGroup
group.ID = groupID
group.Name = groupID
group.AddGroupItemIDs([]string{ProductID1SKU1, ProductID1SKU2, ProductID2SKU1})
group.Upsert()
//create pricerule --------------------------------------------------------------------
priceRule := NewPriceRule(PriceRuleIDSale)
priceRule.Name = map[string]string{
"de": PriceRuleIDSale,
"fr": PriceRuleIDSale,
"it": PriceRuleIDSale,
}
priceRule.Type = TypePromotionOrder
priceRule.Description = priceRule.Name
priceRule.Action = ActionBuyXPayY
priceRule.X = 3
priceRule.Y = 1
priceRule.WhichXYFree = XYMostExpensiveFree
priceRule.WhichXYList = []string{ProductID2SKU1, ProductID1SKU1, ProductID1SKU2}
priceRule.MaxUses = 10
priceRule.MaxUsesPerCustomer = 10
priceRule.IncludedProductGroupIDS = []string{"discounted"}
priceRule.IncludedCustomerGroupIDS = []string{}
err := priceRule.Upsert()
if err != nil {
panic(err)
}
// Order -------------------------------------------------------------------------------
orderVo := &ArticleCollection{}
orderVo.CustomerID = CustomerID1
positionVo := &Article{}
positionVo.ID = ProductID1SKU1
positionVo.Price = 100
positionVo.Quantity = 2
orderVo.Articles = append(orderVo.Articles, positionVo)
positionVo = &Article{}
positionVo.ID = ProductID1SKU2
positionVo.Price = 300
positionVo.Quantity = float64(2)
orderVo.Articles = append(orderVo.Articles, positionVo)
positionVo = &Article{}
positionVo.ID = ProductID2SKU1
positionVo.Price = 500
positionVo.Quantity = float64(2)
orderVo.Articles = append(orderVo.Articles, positionVo)
// Order -------------------------------------------------------------------------------
discountsVo, summary, err := ApplyDiscounts(orderVo, nil, []string{""}, []string{}, 0.05, nil)
// defer removeOrder(orderVo)
if err != nil {
panic(err)
}
fmt.Println("discounts for buy x get y")
utils.PrintJSON(discountsVo)
utils.PrintJSON(*summary)
}
// Test groups creation
func testExclude(t *testing.T) {
//remove all and add again
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
@ -1304,7 +1159,7 @@ func testExclude(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
orderVo, err := createMockOrder(t)
@ -1351,7 +1206,7 @@ func testExclude(t *testing.T) {
}
// Test discounts for customers (employee)
func testCustomerDiscounts(t *testing.T) {
func TestCustomerDiscounts(t *testing.T) {
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
@ -1362,7 +1217,7 @@ func testCustomerDiscounts(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
orderVo, err := createMockOrder(t)
@ -1425,7 +1280,7 @@ func testCustomerDiscounts(t *testing.T) {
priceRule.Amount = 10.0
priceRule.Priority = 90
priceRule.IncludedProductGroupIDS = []string{GroupIDShirts}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
priceRule.MinOrderAmount = 0
priceRule.MinOrderAmountApplicableItemsOnly = true
err = priceRule.Upsert()
@ -1446,7 +1301,7 @@ func testCustomerDiscounts(t *testing.T) {
priceRule.Amount = 10.0
priceRule.Priority = 90
priceRule.IncludedProductGroupIDS = []string{GroupIDShirts}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
priceRule.MinOrderAmount = 0
priceRule.MinOrderAmountApplicableItemsOnly = true
err = priceRule.Upsert()
@ -1496,7 +1351,8 @@ func testCustomerDiscounts(t *testing.T) {
}
func testApplicableInCatalogFlag(t *testing.T) {
func TestApplicableInCatalogFlag(t *testing.T) {
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
productsInGroups[GroupIDNormal] = []string{ProductID4, ProductID5, ProductID4SKU1, ProductID4SKU2, ProductID5SKU1, ProductID5SKU2}
@ -1506,7 +1362,7 @@ func testApplicableInCatalogFlag(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
orderVo, err := createMockOrder(t)
@ -1542,7 +1398,8 @@ func testApplicableInCatalogFlag(t *testing.T) {
}
func testAbsoluteVoucher(t *testing.T) {
func TestAbsoluteVoucher(t *testing.T) {
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
productsInGroups[GroupIDNormal] = []string{ProductID4, ProductID5, ProductID4SKU1, ProductID4SKU2, ProductID5SKU1, ProductID5SKU2}
@ -1552,7 +1409,7 @@ func testAbsoluteVoucher(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
@ -1645,7 +1502,8 @@ func testAbsoluteVoucher(t *testing.T) {
}
// Test groups creation
func testMaxOrder(t *testing.T) {
func TestMaxOrder(t *testing.T) {
//remove all and add again
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
@ -1656,7 +1514,7 @@ func testMaxOrder(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
orderVo, err := createMockOrder(t)
@ -1679,7 +1537,7 @@ func testMaxOrder(t *testing.T) {
priceRule.Amount = 10.0
priceRule.Priority = 90
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
priceRule.MinOrderAmount = 0
priceRule.MinOrderAmountApplicableItemsOnly = true
err = priceRule.Upsert()
@ -1743,7 +1601,8 @@ func testMaxOrder(t *testing.T) {
}
// Test groups creation
func testTwoStepWorkflow(t *testing.T) {
func TestTwoStepWorkflow(t *testing.T) {
//remove all and add again
productsInGroups = make(map[string][]string)
productsInGroups[GroupIDSale] = []string{ProductID1, ProductID2, ProductID1SKU1, ProductID1SKU2, ProductID2SKU1, ProductID2SKU2}
@ -1754,7 +1613,7 @@ func testTwoStepWorkflow(t *testing.T) {
RemoveAllPriceRules()
RemoveAllVouchers()
checkGroupsNotExists(t)
createMockCustomerGroups(t)
createMockProductGroups(t)
checkGroupsExists(t)
orderVo, err := createMockOrder(t)
@ -1777,7 +1636,7 @@ func testTwoStepWorkflow(t *testing.T) {
priceRule.Amount = 10.0
priceRule.Priority = 90
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
err = priceRule.Upsert()
if err != nil {
panic(err)
@ -1795,7 +1654,7 @@ func testTwoStepWorkflow(t *testing.T) {
priceRule.Amount = 10.0
priceRule.Priority = 100
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
err = priceRule.Upsert()
if err != nil {
panic(err)
@ -1814,7 +1673,7 @@ func testTwoStepWorkflow(t *testing.T) {
priceRule.Amount = 20.0
priceRule.Priority = 800
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
err = priceRule.Upsert()
if err != nil {
panic(err)
@ -1878,9 +1737,8 @@ func testTwoStepWorkflow(t *testing.T) {
}
// Test groups creation
func testPricerulesWorkflow(t *testing.T) {
func TestPricerulesWorkflow(t *testing.T) {
//remove all and add again
Init(t)
orderVo, err := createMockOrder(t)
if err != nil {
@ -1901,9 +1759,7 @@ func testPricerulesWorkflow(t *testing.T) {
}
// Test checkout functionality
func testCheckoutWorkflow(t *testing.T) {
//remove all and add again
Init(t)
func TestCheckoutWorkflow(t *testing.T) {
orderVo, err := createMockOrder(t)
if err != nil {
@ -1959,31 +1815,13 @@ func createMockProductGroups(t *testing.T) {
group.Type = ProductGroup
group.ID = groupID
group.Name = groupID
group.AddGroupItemIDs(productsInGroups[groupID])
err := group.Upsert()
err := group.AddGroupItemIDsAndPersist(productsInGroups[groupID])
if err != nil {
t.Fatal("Could not upsert product group " + groupID)
t.Fatal(err, "Could not upsert product group "+groupID)
}
}
}
func createMockCustomerGroups(t *testing.T) {
for _, groupID := range []string{CustomerGroupID1, CustomerGroupID2} {
group := new(Group)
group.Type = CustomerGroup
group.ID = groupID
group.Name = groupID
group.AddGroupItemIDs([]string{CustomerID1})
err := group.Upsert()
if err != nil {
log.Println(err)
t.Fatal("Could not upsert customer group " + groupID)
}
group.AddGroupItemIDsAndPersist([]string{CustomerID2})
}
}
// PRICERULES ---------------------------------------------
func createMockPriceRules(t *testing.T) {
@ -2008,7 +1846,7 @@ func createMockPriceRules(t *testing.T) {
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupRegular}
err := priceRule.Upsert()
if err != nil {
@ -2033,7 +1871,7 @@ func createMockPriceRules(t *testing.T) {
priceRule.IncludedProductGroupIDS = []string{GroupIDSale}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID2}
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupEmployee}
err = priceRule.Upsert()
if err != nil {

View File

@ -0,0 +1,250 @@
package pricerule
import (
"strconv"
"testing"
"github.com/stretchr/testify/assert"
)
type cumulationTestHelper struct {
CustomerGroupRegular string
CustomerGroupEmployee string
Sku1 string
Sku2 string
Sku3 string
GroupIDSingleSku1 string
GroupIDSingleSku2 string
GroupIDTwoSkus string
GroupIDThreeSkus string
}
func newTesthelper() cumulationTestHelper {
return cumulationTestHelper{
CustomerGroupRegular: "customer-regular",
CustomerGroupEmployee: "customer-employee",
Sku1: "sku1",
Sku2: "sku2",
GroupIDSingleSku1: "group-with-sku1",
GroupIDSingleSku2: "group-with-sku2",
GroupIDTwoSkus: "group-with-two-skus",
GroupIDThreeSkus: "group-with-three-skus",
}
}
func (helper cumulationTestHelper) cleanupTestData(t *testing.T) {
assert.NoError(t, RemoveAllGroups())
assert.NoError(t, RemoveAllPriceRules())
assert.NoError(t, RemoveAllVouchers())
ClearCache() // reset cache for catalogue calculations
}
func (helper cumulationTestHelper) cleanupAndRecreateTestData(t *testing.T) {
helper.cleanupTestData(t)
productsInGroups := make(map[string][]string)
productsInGroups[helper.GroupIDSingleSku1] = []string{helper.Sku1}
productsInGroups[helper.GroupIDSingleSku2] = []string{helper.Sku2}
productsInGroups[helper.GroupIDTwoSkus] = []string{helper.Sku1, helper.Sku2}
productsInGroups[helper.GroupIDThreeSkus] = []string{helper.Sku1, helper.Sku2, helper.Sku3}
helper.createMockCustomerGroups(t, []string{helper.CustomerGroupRegular, helper.CustomerGroupEmployee})
helper.createMockProductGroups(t, productsInGroups)
}
func (helper cumulationTestHelper) createMockProductGroups(t *testing.T, productGroups map[string][]string) {
for groupID, items := range productGroups {
group := new(Group)
group.Type = ProductGroup
group.ID = groupID
group.Name = groupID
assert.NoError(t, group.AddGroupItemIDsAndPersist(items), "Could not create product groups")
}
}
func (helper cumulationTestHelper) createMockCustomerGroups(t *testing.T, customerGroups []string) {
for _, groupID := range customerGroups {
group := new(Group)
group.Type = CustomerGroup
group.ID = groupID
group.Name = groupID
assert.NoError(t, group.AddGroupItemIDsAndPersist([]string{groupID}), "Could not create customer groups")
}
}
// createVouchers creates n voucher codes for given Pricerule
func (helper cumulationTestHelper) createMockVouchers(t *testing.T, priceRule *PriceRule, n int) []string {
vouchers := make([]string, n)
for i, _ := range vouchers {
voucherCode := "voucher-" + priceRule.ID + "-" + strconv.Itoa(i)
voucher := NewVoucher(voucherCode, voucherCode, priceRule, "")
assert.NoError(t, voucher.Upsert())
vouchers[i] = voucherCode
}
return vouchers
}
func (helper cumulationTestHelper) getMockArticleCollection() *ArticleCollection {
return &ArticleCollection{
Articles: []*Article{
{
ID: helper.Sku1,
Price: 10.0,
Quantity: 1,
AllowCrossPriceCalculation: true,
},
{
ID: helper.Sku2,
Price: 20.0,
Quantity: 1,
AllowCrossPriceCalculation: true,
},
},
CustomerType: helper.CustomerGroupRegular,
}
}
func (helper cumulationTestHelper) setMockEmployeeDiscount10Percent(t *testing.T, name string, includedProductGroupIDS []string) {
// Create pricerule
priceRule := NewPriceRule("PriceRule-EmployeeDiscount-" + name)
priceRule.Type = TypePromotionCustomer
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByPercent
priceRule.Amount = 10
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
priceRule.IncludedCustomerGroupIDS = []string{helper.CustomerGroupEmployee}
assert.NoError(t, priceRule.Upsert())
return
}
func (helper cumulationTestHelper) setMockPriceRuleAndVoucherXPercent(t *testing.T, name string, amount float64, includedProductGroupIDS []string, excludeAlreadyDiscountedItems bool, excludeEmployees bool) string {
// Create pricerule
priceRule := NewPriceRule("PriceRule-" + name)
priceRule.Type = TypeVoucher
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByPercent
priceRule.Amount = amount
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
if excludeEmployees {
priceRule.IncludedCustomerGroupIDS = []string{helper.CustomerGroupRegular}
}
priceRule.ExcludeAlreadyDiscountedItemsForVoucher = excludeAlreadyDiscountedItems
assert.Nil(t, priceRule.Upsert())
// Create voucher
return helper.createMockVouchers(t, priceRule, 1)[0]
}
func (helper cumulationTestHelper) setMockPriceRuleCrossPrice(t *testing.T, name string, amount float64, isPercent bool, includedProductGroupIDS []string) {
// Create pricerule
priceRule := NewPriceRule("PriceRulePromotionProduct-" + name)
priceRule.Type = TypePromotionProduct
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByAbsolute
if isPercent {
priceRule.Action = ActionItemByPercent
}
priceRule.Amount = amount
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
assert.NoError(t, priceRule.Upsert())
}
func (helper cumulationTestHelper) setMockPriceRuleBuy3Pay2(t *testing.T, name string, includedProductGroupIDS []string) {
// Create pricerule
priceRule := NewPriceRule("PriceRuleBuy3Pay2-" + name)
priceRule.Type = TypePromotionOrder
priceRule.Description = priceRule.Name
priceRule.Action = ActionBuyXPayY
priceRule.X = 3
priceRule.Y = 2
priceRule.WhichXYFree = XYCheapestFree
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
assert.NoError(t, priceRule.Upsert())
}
func (helper cumulationTestHelper) setMockPriceRuleAndVoucherAbsoluteCHF20(t *testing.T, cumulate bool) (string, string) {
// Create pricerule
priceRule := NewPriceRule("PriceRule-" + "CartAbsoluteCHF20")
priceRule.Type = TypeVoucher
priceRule.Description = priceRule.Name
priceRule.Action = ActionCartByAbsolute
priceRule.Amount = 20.0
priceRule.IncludedProductGroupIDS = []string{helper.GroupIDTwoSkus}
priceRule.IncludedCustomerGroupIDS = []string{helper.CustomerGroupRegular}
priceRule.ExcludeAlreadyDiscountedItemsForVoucher = false
priceRule.CumulateWithOtherVouchers = cumulate
assert.NoError(t, priceRule.Upsert())
// Create vouchers
vouchers := helper.createMockVouchers(t, priceRule, 2)
return vouchers[0], vouchers[1]
}
func (helper cumulationTestHelper) setMockPriceRuleAndVoucher10Percent(t *testing.T, cumulate bool) string {
// Create pricerule
priceRule := NewPriceRule("PriceRule-" + "Cart10Percent")
priceRule.Type = TypeVoucher
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByPercent
priceRule.Amount = 10.0
priceRule.IncludedProductGroupIDS = []string{helper.GroupIDTwoSkus}
priceRule.IncludedCustomerGroupIDS = []string{helper.CustomerGroupRegular}
priceRule.ExcludeAlreadyDiscountedItemsForVoucher = false
priceRule.CumulateWithOtherVouchers = cumulate
assert.NoError(t, priceRule.Upsert())
// Create voucher
return helper.createMockVouchers(t, priceRule, 1)[0]
}
func (helper cumulationTestHelper) setMockPriceRuleQtyThreshold(t *testing.T, name string, amount float64, threshold float64, includedProductGroupIDS []string) {
// Create pricerule
priceRule := NewPriceRule("PriceRulePromotionQtyThreshold-" + name)
priceRule.Type = TypePromotionOrder // TypePromotionProduct would also work here
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByAbsolute
priceRule.QtyThreshold = threshold
priceRule.Amount = amount
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
assert.NoError(t, priceRule.Upsert())
}
func (helper cumulationTestHelper) setMockPriceRuleThresholdAmount(t *testing.T, name string, amount float64, thresholdAmount float64, includedProductGroupIDS []string) {
// Create pricerule
priceRule := NewPriceRule("PriceRulePromotionProductThresholdAmount-" + name)
priceRule.Type = TypePromotionOrder // TypePromotionProduct would also work here
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByAbsolute
priceRule.Amount = amount
priceRule.MinOrderAmount = thresholdAmount
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
assert.NoError(t, priceRule.Upsert())
}
func (helper cumulationTestHelper) setMockBonusVoucherPriceRule(t *testing.T) (string, string) {
// Create pricerule
priceRule := NewPriceRule("PriceRule-" + "Bonus10CHF")
priceRule.Description = priceRule.Name
priceRule.Type = TypeBonusVoucher
priceRule.Action = ActionCartByAbsolute
priceRule.Amount = 10.0
priceRule.CalculateDiscountedOrderAmount = true
priceRule.Priority = 999
assert.NoError(t, priceRule.Upsert())
// Create vouchers
vouchers := helper.createMockVouchers(t, priceRule, 2)
return vouchers[0], vouchers[1]
}
func (helper cumulationTestHelper) accumulateDiscountsOfItems(discounts OrderDiscounts) float64 {
sum := 0.0
for _, d := range discounts {
sum += d.TotalDiscountAmountApplicable
}
return sum
}

View File

@ -1,251 +0,0 @@
package pricerule
import (
"testing"
"github.com/foomo/shop/utils"
"github.com/stretchr/testify/assert"
)
type cumulationTestHelper struct{}
func (helper cumulationTestHelper) getMockArticleCollection() *ArticleCollection {
return &ArticleCollection{
Articles: []*Article{
{
ID: Sku1,
Price: 10.0,
Quantity: 1,
AllowCrossPriceCalculation: true,
},
{
ID: Sku2,
Price: 20.0,
Quantity: 1,
AllowCrossPriceCalculation: true,
},
},
CustomerType: CustomerID1,
}
}
func (helper cumulationTestHelper) setMockPriceRuleAndVoucher(t *testing.T, name string, amount float64, includedProductGroupIDS []string, excludeAlreadyDiscountedItems bool) string {
// PRICERULE
priceRule := NewPriceRule("PriceRule-" + name)
priceRule.Type = TypeVoucher
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByPercent
priceRule.Amount = amount
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
priceRule.IncludedCustomerGroupIDS = []string{CustomerGroupID1}
priceRule.ExcludeAlreadyDiscountedItemsForVoucher = excludeAlreadyDiscountedItems
assert.Nil(t, priceRule.Upsert())
voucherCode := "voucherCode-" + priceRule.ID
voucher := NewVoucher(voucherCode, voucherCode, priceRule, "")
assert.Nil(t, voucher.Upsert())
return voucherCode
}
func (helper cumulationTestHelper) setMockPriceRuleCrossPrice(t *testing.T, name string, amount float64, includedProductGroupIDS []string) {
// PRICERULE 0
priceRule := NewPriceRule("PriceRulePromotionProduct-" + name)
priceRule.Type = TypePromotionProduct
priceRule.Description = priceRule.Name
priceRule.Action = ActionItemByAbsolute
priceRule.Amount = amount
priceRule.IncludedProductGroupIDS = includedProductGroupIDS
assert.Nil(t, priceRule.Upsert())
}
func TestCumulationTwoVouchers_OnePerSku(t *testing.T) {
// Cart with 2 Items
// Expected result: one of the vouchers is applied to each item
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucher(t, "voucher-sku1", 10.0, []string{GroupIDSingleSku1}, false)
voucherCode2 := helper.setMockPriceRuleAndVoucher(t, "voucher-sku2", 10.0, []string{GroupIDSingleSku2}, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.Nil(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 10% on 30 CHF
assert.Equal(t, 3.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForSameSku(t *testing.T) {
// Cart with 2 Items
// Both voucher are only valid for one of the items
// Expected result: One voucher is being applied, the other one is dismissed
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucher(t, "voucher-sku1", 10.0, []string{GroupIDSingleSku1}, false)
voucherCode2 := helper.setMockPriceRuleAndVoucher(t, "voucher-sku2", 10.0, []string{GroupIDSingleSku1}, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.Nil(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// only one voucher should be applied => 1 CHF
assert.Equal(t, 1.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForBothSkus(t *testing.T) {
// Cart with 2 Items
// Both voucher are valid for both of the items
// Expected result: The better voucher is applied to both items
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucher(t, "voucher-1", 5.0, []string{GroupIDTwoSkus}, false)
voucherCode2 := helper.setMockPriceRuleAndVoucher(t, "voucher-2", 10.0, []string{GroupIDTwoSkus}, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.Nil(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 10% on 30 CHF
assert.Equal(t, 3.0, summary.TotalDiscountApplicable)
}
func TestCumulationTwoVouchers_BothForSameSku_AdditonalCrossPrice(t *testing.T) {
// Cart with 2 Items
// Both voucher are only valid for one of the items
// Expected result: - both items discounted by product promo
// - One voucher is being applied, the other one is dismissed
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "crossprice1", 5.0, []string{GroupIDTwoSkus})
voucherCode1 := helper.setMockPriceRuleAndVoucher(t, "voucher-sku1", 5.0, []string{GroupIDSingleSku1}, false)
voucherCode2 := helper.setMockPriceRuleAndVoucher(t, "voucher-sku2", 10.0, []string{GroupIDSingleSku1}, false)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1, voucherCode2}, nil, 0.05, nil)
assert.Nil(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// Sku1: 5 + 0.5, Sku2: 5 => 10.5
assert.Equal(t, 10.5, summary.TotalDiscountApplicable)
}
func TestCumulationProductPromo(t *testing.T) {
// Cart with 2 Items
// Expected result: - only better cross price should be applied
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "crossprice1", 2.0, []string{GroupIDTwoSkus})
helper.setMockPriceRuleCrossPrice(t, "crossprice2", 5.0, []string{GroupIDTwoSkus})
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{}, []string{}, 0.05, nil)
assert.Nil(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// 5+5 (5 on each product) = 10 CHF
assert.Equal(t, 10.0, summary.TotalDiscountApplicable)
}
func TestCumulationForExcludeVoucherOnCrossPriceWebhop(t *testing.T) {
// Cart with 2 Items
// Expected result: voucher is only applied to sku2. Sku1 is skipped due to existing webshop cross-price
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
helper.setMockPriceRuleCrossPrice(t, "crossprice1", 5.0, []string{GroupIDSingleSku1})
voucherCode1 := helper.setMockPriceRuleAndVoucher(t, "voucher-1", 10.0, []string{GroupIDTwoSkus}, true)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.Nil(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// Sku1: 5, Sku2: 2
assert.Equal(t, 7.0, summary.TotalDiscountApplicable)
}
func TestCumulationForExcludeVoucherOnCrossPriceSAP(t *testing.T) {
// Cart with 2 Items
// Expected result: voucher is only applied to sku2. Sku1 is skipped due to existing SAP cross-price
// (indicated by AllowCrossPriceCalculation == false)
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
for i, article := range articleCollection.Articles {
if i == 0 {
article.AllowCrossPriceCalculation = false // this is false if there already is a SAP crossprice
}
}
voucherCode1 := helper.setMockPriceRuleAndVoucher(t, "voucher-1", 10.0, []string{GroupIDTwoSkus}, true)
discounts, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.Nil(t, errApply)
utils.PrintJSON(discounts)
utils.PrintJSON(summary)
// Sku2: 2 CHF
assert.Equal(t, 2.0, summary.TotalDiscountApplicable)
}
func TestCumulationExcludeStaff(t *testing.T) {
// Cart with 2 Items
// Expected result: only regular customer gets discount
Init(t)
helper := cumulationTestHelper{}
articleCollection := helper.getMockArticleCollection()
voucherCode1 := helper.setMockPriceRuleAndVoucher(t, "voucher-sku1", 10.0, []string{GroupIDTwoSkus}, false)
// Get Discuonts for regular customer
_, summary, errApply := ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.Nil(t, errApply)
// 10% on 30 CHF
assert.Equal(t, 3.0, summary.TotalDiscountApplicable)
// Change customer type => no discount
articleCollection.CustomerType = "employee"
_, summary, errApply = ApplyDiscounts(articleCollection, nil, []string{voucherCode1}, nil, 0.05, nil)
assert.Nil(t, errApply)
// No discount for non-regulat customer
assert.Equal(t, 0.0, summary.TotalDiscountApplicable)
}