forked from i-love-flamingo/flamingo-commerce
/
cartDecorator.go
332 lines (277 loc) · 9.65 KB
/
cartDecorator.go
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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
package decorator
import (
"context"
"sort"
"go.opencensus.io/trace"
cartDomain "flamingo.me/flamingo-commerce/v3/cart/domain/cart"
"flamingo.me/flamingo/v3/framework/flamingo"
priceDomain "flamingo.me/flamingo-commerce/v3/price/domain"
"flamingo.me/flamingo-commerce/v3/product/domain"
)
type (
// DecoratedCartFactory - Factory to be injected: If you need to create a new Decorator then get the factory injected and use the factory
DecoratedCartFactory struct {
productService domain.ProductService
logger flamingo.Logger
}
// DecoratedCart Decorates Access To a Cart
DecoratedCart struct {
Cart cartDomain.Cart
DecoratedDeliveries []DecoratedDelivery
Ctx context.Context `json:"-"`
Logger flamingo.Logger `json:"-"`
}
// DecoratedDelivery Decorates a CartItem with its Product
DecoratedDelivery struct {
Delivery cartDomain.Delivery
DecoratedItems []DecoratedCartItem
logger flamingo.Logger
}
// DecoratedCartItem Decorates a CartItem with its Product
DecoratedCartItem struct {
Item cartDomain.Item
Product domain.BasicProduct
logger flamingo.Logger
}
// GroupedDecoratedCartItem - value object used for grouping (generated on the fly)
GroupedDecoratedCartItem struct {
DecoratedItems []DecoratedCartItem
Group string
}
ctxKey struct{}
)
// Inject dependencies
func (df *DecoratedCartFactory) Inject(
productService domain.ProductService,
logger flamingo.Logger,
) {
df.productService = productService
df.logger = logger
}
// Create Factory method to get Decorated Cart
func (df *DecoratedCartFactory) Create(ctx context.Context, cart cartDomain.Cart) *DecoratedCart {
ctx, span := trace.StartSpan(ctx, "cart/DecoratedCartFactory/Create")
defer span.End()
decoratedCart := DecoratedCart{Cart: cart, Logger: df.logger}
for _, d := range cart.Deliveries {
decoratedCart.DecoratedDeliveries = append(decoratedCart.DecoratedDeliveries, DecoratedDelivery{
Delivery: d,
DecoratedItems: df.CreateDecorateCartItems(context.WithValue(ctx, ctxKey{}, cart), d.Cartitems),
logger: df.logger,
})
}
decoratedCart.Ctx = ctx
return &decoratedCart
}
// CreateDecorateCartItems Factory method to get Decorated Cart
func (df *DecoratedCartFactory) CreateDecorateCartItems(ctx context.Context, items []cartDomain.Item) []DecoratedCartItem {
ctx, span := trace.StartSpan(ctx, "cart/DecoratedCartFactory/CreateDecorateCartItems")
defer span.End()
var decoratedItems []DecoratedCartItem
for _, cartItem := range items {
decoratedItem := df.decorateCartItem(ctx, cartItem)
decoratedItems = append(decoratedItems, decoratedItem)
}
return decoratedItems
}
// decorateCartItem factory method
func (df *DecoratedCartFactory) decorateCartItem(ctx context.Context, cartItem cartDomain.Item) DecoratedCartItem {
ctx, span := trace.StartSpan(ctx, "cart/DecoratedCartFactory/decorateCartItem")
defer span.End()
decoratedItem := DecoratedCartItem{Item: cartItem, logger: df.logger}
product, err := df.productService.Get(ctx, cartItem.MarketplaceCode)
if err != nil {
df.logger.WithContext(ctx).Debug("cart.decorator - no product for item: ", err)
if product == nil {
// To avoid errors if consumers want to access the product data
product = domain.SimpleProduct{
BasicProductData: domain.BasicProductData{
Title: cartItem.ProductName + "[outdated]",
},
}
decoratedItem.Product = product
}
return decoratedItem
}
if product.Type() == domain.TypeConfigurable {
if configurable, ok := product.(domain.ConfigurableProduct); ok {
configurableWithVariant, err := configurable.GetConfigurableWithActiveVariant(cartItem.VariantMarketPlaceCode)
if err != nil {
product = domain.SimpleProduct{
BasicProductData: domain.BasicProductData{
Title: cartItem.ProductName + "[outdated]",
},
}
} else {
product = configurableWithVariant
}
}
}
if product.Type() == domain.TypeBundle {
if bundle, ok := product.(domain.BundleProduct); ok {
bundleWithActiveChoices, err := bundle.GetBundleProductWithActiveChoices(cartItem.BundleConfig)
if err != nil {
product = domain.SimpleProduct{
BasicProductData: domain.BasicProductData{
Title: cartItem.ProductName + "[outdated]",
},
}
} else {
product = bundleWithActiveChoices
}
}
}
decoratedItem.Product = product
return decoratedItem
}
// IsConfigurable - checks if current CartItem is a Configurable Product
func (dci DecoratedCartItem) IsConfigurable() bool {
if dci.Product == nil {
return false
}
return dci.Product.Type() == domain.TypeConfigurableWithActiveVariant
}
// GetVariant getter
func (dci DecoratedCartItem) GetVariant() (*domain.Variant, error) {
return dci.Product.(domain.ConfigurableProductWithActiveVariant).Variant(dci.Item.VariantMarketPlaceCode)
}
// GetDisplayTitle getter
func (dci DecoratedCartItem) GetDisplayTitle() string {
if dci.IsConfigurable() {
variant, e := dci.GetVariant()
if e != nil {
return "Error Getting Variant"
}
return variant.Title
}
return dci.Product.BaseData().Title
}
// GetDisplayMarketplaceCode getter
func (dci DecoratedCartItem) GetDisplayMarketplaceCode() string {
if dci.IsConfigurable() {
variant, e := dci.GetVariant()
if e != nil {
return "Error Getting Variant"
}
return variant.MarketPlaceCode
}
return dci.Product.BaseData().MarketPlaceCode
}
// GetVariantsVariationAttributes getter
func (dci DecoratedCartItem) GetVariantsVariationAttributes() domain.Attributes {
attributes := domain.Attributes{}
if dci.IsConfigurable() {
variant, _ := dci.GetVariant()
for _, attributeName := range dci.Product.(domain.ConfigurableProductWithActiveVariant).VariantVariationAttributes {
attributes[attributeName] = variant.BaseData().Attributes[attributeName]
}
}
return attributes
}
// GetVariantsVariationAttributeCodes getter
func (dci DecoratedCartItem) GetVariantsVariationAttributeCodes() []string {
if dci.Product.Type() == domain.TypeConfigurableWithActiveVariant {
return dci.Product.(domain.ConfigurableProductWithActiveVariant).VariantVariationAttributes
}
return nil
}
// GetChargesToPay getter
func (dci DecoratedCartItem) GetChargesToPay(wishedToPaySum *domain.WishedToPay) priceDomain.Charges {
priceToPayForItem := dci.Item.RowPriceGrossWithDiscount
return dci.Product.SaleableData().GetLoyaltyChargeSplit(&priceToPayForItem, wishedToPaySum, dci.Item.Qty)
}
// GetGroupedBy legacy function
// deprecated: only here to support the old structure of accesing DecoratedItems in the Decorated Cart
// Use instead:
//
// or iterate over DecoratedCart.DecoratedDelivery
func (dc DecoratedCart) GetGroupedBy(group string, sortGroup bool, params ...string) []*GroupedDecoratedCartItem {
if dc.Logger != nil {
dc.Logger.Warn("DEPRECATED: DecoratedCart.GetGroupedBy()")
}
if len(dc.DecoratedDeliveries) != 1 {
return nil
}
return dc.DecoratedDeliveries[0].GetGroupedBy(group, sortGroup, params...)
}
// GetAllDecoratedItems getter
func (dc DecoratedCart) GetAllDecoratedItems() []DecoratedCartItem {
var allItems []DecoratedCartItem
for _, dd := range dc.DecoratedDeliveries {
allItems = append(allItems, dd.DecoratedItems...)
}
return allItems
}
// GetDecoratedDeliveryByCode getter
func (dc DecoratedCart) GetDecoratedDeliveryByCode(deliveryCode string) (*DecoratedDelivery, bool) {
for _, dd := range dc.DecoratedDeliveries {
if dd.Delivery.DeliveryInfo.Code == deliveryCode {
return &dd, true
}
}
return nil, false
}
// GetDecoratedDeliveryByCodeWithoutBool - used inside a template, therefor we need the method with a single return param
func (dc DecoratedCart) GetDecoratedDeliveryByCodeWithoutBool(deliveryCode string) *DecoratedDelivery {
decoratedDelivery, _ := dc.GetDecoratedDeliveryByCode(deliveryCode)
return decoratedDelivery
}
// GetGroupedBy getter
func (dc DecoratedDelivery) GetGroupedBy(group string, sortGroup bool, params ...string) []*GroupedDecoratedCartItem {
groupedItemsCollection := make(map[string]*GroupedDecoratedCartItem)
var groupedItemsCollectionKeys []string
var groupKey string
for _, item := range dc.DecoratedItems {
switch group {
case "retailer_code":
groupKey = item.Product.BaseData().RetailerCode
default:
groupKey = "default"
}
if _, ok := groupedItemsCollection[groupKey]; !ok {
groupedItemsCollection[groupKey] = &GroupedDecoratedCartItem{
Group: groupKey,
}
groupedItemsCollectionKeys = append(groupedItemsCollectionKeys, groupKey)
}
if groupedItemsEntry, ok := groupedItemsCollection[groupKey]; ok {
groupedItemsEntry.DecoratedItems = append(groupedItemsEntry.DecoratedItems, item)
}
}
// sort before return
if sortGroup {
direction := ""
if len(params) > 0 {
direction = params[0]
}
if direction == "DESC" {
sort.Sort(sort.Reverse(sort.StringSlice(groupedItemsCollectionKeys)))
} else {
sort.Strings(groupedItemsCollectionKeys)
}
}
var groupedItemsCollectionSorted []*GroupedDecoratedCartItem
for _, keyName := range groupedItemsCollectionKeys {
if groupedItemsEntry, ok := groupedItemsCollection[keyName]; ok {
groupedItemsCollectionSorted = append(groupedItemsCollectionSorted, groupedItemsEntry)
}
}
return groupedItemsCollectionSorted
}
// GetDecoratedCartItemByID getter
func (dc DecoratedDelivery) GetDecoratedCartItemByID(ID string) *DecoratedCartItem {
for _, decoratedItem := range dc.DecoratedItems {
if decoratedItem.Item.ID == ID {
return &decoratedItem
}
}
return nil
}
func CartFromDecoratedCartFactoryContext(ctx context.Context) *cartDomain.Cart {
ctx, span := trace.StartSpan(ctx, "cart/CartFromDecoratedCartFactoryContext")
defer span.End()
if cart, ok := ctx.Value(ctxKey{}).(cartDomain.Cart); ok {
return &cart
}
return nil
}