flamingo.me/flamingo-commerce/v3@v3.11.0/cart/domain/decorator/cartDecorator.go (about) 1 package decorator 2 3 import ( 4 "context" 5 "sort" 6 7 "go.opencensus.io/trace" 8 9 cartDomain "flamingo.me/flamingo-commerce/v3/cart/domain/cart" 10 11 "flamingo.me/flamingo/v3/framework/flamingo" 12 13 priceDomain "flamingo.me/flamingo-commerce/v3/price/domain" 14 "flamingo.me/flamingo-commerce/v3/product/domain" 15 ) 16 17 type ( 18 // DecoratedCartFactory - Factory to be injected: If you need to create a new Decorator then get the factory injected and use the factory 19 DecoratedCartFactory struct { 20 productService domain.ProductService 21 logger flamingo.Logger 22 } 23 24 // DecoratedCart Decorates Access To a Cart 25 DecoratedCart struct { 26 Cart cartDomain.Cart 27 DecoratedDeliveries []DecoratedDelivery 28 Ctx context.Context `json:"-"` 29 Logger flamingo.Logger `json:"-"` 30 } 31 32 // DecoratedDelivery Decorates a CartItem with its Product 33 DecoratedDelivery struct { 34 Delivery cartDomain.Delivery 35 DecoratedItems []DecoratedCartItem 36 logger flamingo.Logger 37 } 38 39 // DecoratedCartItem Decorates a CartItem with its Product 40 DecoratedCartItem struct { 41 Item cartDomain.Item 42 Product domain.BasicProduct 43 logger flamingo.Logger 44 } 45 46 // GroupedDecoratedCartItem - value object used for grouping (generated on the fly) 47 GroupedDecoratedCartItem struct { 48 DecoratedItems []DecoratedCartItem 49 Group string 50 } 51 52 cartCtxKey struct{} 53 deliveryCtxKey struct{} 54 ) 55 56 // Inject dependencies 57 func (df *DecoratedCartFactory) Inject( 58 productService domain.ProductService, 59 logger flamingo.Logger, 60 ) { 61 df.productService = productService 62 df.logger = logger 63 } 64 65 // Create Factory method to get Decorated Cart 66 func (df *DecoratedCartFactory) Create(ctx context.Context, cart cartDomain.Cart) *DecoratedCart { 67 ctx, span := trace.StartSpan(ctx, "cart/DecoratedCartFactory/Create") 68 defer span.End() 69 70 decoratedCart := DecoratedCart{Cart: cart, Logger: df.logger} 71 72 contextWithCart := context.WithValue(ctx, cartCtxKey{}, cart) 73 74 for _, d := range cart.Deliveries { 75 contextWithDeliveryCode := ContextWithDeliveryCode(contextWithCart, d.DeliveryInfo.Code) 76 77 decoratedCart.DecoratedDeliveries = append(decoratedCart.DecoratedDeliveries, DecoratedDelivery{ 78 Delivery: d, 79 DecoratedItems: df.CreateDecorateCartItems(contextWithDeliveryCode, d.Cartitems), 80 logger: df.logger, 81 }) 82 } 83 84 decoratedCart.Ctx = ctx 85 86 return &decoratedCart 87 } 88 89 // CreateDecorateCartItems Factory method to get Decorated Cart 90 func (df *DecoratedCartFactory) CreateDecorateCartItems(ctx context.Context, items []cartDomain.Item) []DecoratedCartItem { 91 ctx, span := trace.StartSpan(ctx, "cart/DecoratedCartFactory/CreateDecorateCartItems") 92 defer span.End() 93 94 var decoratedItems []DecoratedCartItem 95 96 for _, cartItem := range items { 97 decoratedItem := df.decorateCartItem(ctx, cartItem) 98 decoratedItems = append(decoratedItems, decoratedItem) 99 } 100 101 return decoratedItems 102 } 103 104 // decorateCartItem factory method 105 func (df *DecoratedCartFactory) decorateCartItem(ctx context.Context, cartItem cartDomain.Item) DecoratedCartItem { 106 ctx, span := trace.StartSpan(ctx, "cart/DecoratedCartFactory/decorateCartItem") 107 defer span.End() 108 109 decoratedItem := DecoratedCartItem{Item: cartItem, logger: df.logger} 110 111 product, err := df.productService.Get(ctx, cartItem.MarketplaceCode) 112 if err != nil { 113 df.logger.WithContext(ctx).Debug("cart.decorator - no product for item: ", err) 114 115 if product == nil { 116 // To avoid errors if consumers want to access the product data 117 product = domain.SimpleProduct{ 118 BasicProductData: domain.BasicProductData{ 119 Title: cartItem.ProductName + "[outdated]", 120 }, 121 } 122 123 decoratedItem.Product = product 124 } 125 126 return decoratedItem 127 } 128 129 if product.Type() == domain.TypeConfigurable { 130 if configurable, ok := product.(domain.ConfigurableProduct); ok { 131 configurableWithVariant, err := configurable.GetConfigurableWithActiveVariant(cartItem.VariantMarketPlaceCode) 132 if err != nil { 133 product = domain.SimpleProduct{ 134 BasicProductData: domain.BasicProductData{ 135 Title: cartItem.ProductName + "[outdated]", 136 }, 137 } 138 } else { 139 product = configurableWithVariant 140 } 141 } 142 } 143 144 if product.Type() == domain.TypeBundle { 145 if bundle, ok := product.(domain.BundleProduct); ok { 146 bundleWithActiveChoices, err := bundle.GetBundleProductWithActiveChoices(cartItem.BundleConfig) 147 if err != nil { 148 product = domain.SimpleProduct{ 149 BasicProductData: domain.BasicProductData{ 150 Title: cartItem.ProductName + "[outdated]", 151 }, 152 } 153 } else { 154 product = bundleWithActiveChoices 155 } 156 } 157 } 158 159 decoratedItem.Product = product 160 161 return decoratedItem 162 } 163 164 // IsConfigurable - checks if current CartItem is a Configurable Product 165 func (dci DecoratedCartItem) IsConfigurable() bool { 166 if dci.Product == nil { 167 return false 168 } 169 return dci.Product.Type() == domain.TypeConfigurableWithActiveVariant 170 } 171 172 // GetVariant getter 173 func (dci DecoratedCartItem) GetVariant() (*domain.Variant, error) { 174 return dci.Product.(domain.ConfigurableProductWithActiveVariant).Variant(dci.Item.VariantMarketPlaceCode) 175 } 176 177 // GetDisplayTitle getter 178 func (dci DecoratedCartItem) GetDisplayTitle() string { 179 if dci.IsConfigurable() { 180 variant, e := dci.GetVariant() 181 if e != nil { 182 return "Error Getting Variant" 183 } 184 return variant.Title 185 } 186 return dci.Product.BaseData().Title 187 } 188 189 // GetDisplayMarketplaceCode getter 190 func (dci DecoratedCartItem) GetDisplayMarketplaceCode() string { 191 if dci.IsConfigurable() { 192 variant, e := dci.GetVariant() 193 if e != nil { 194 return "Error Getting Variant" 195 } 196 return variant.MarketPlaceCode 197 } 198 return dci.Product.BaseData().MarketPlaceCode 199 } 200 201 // GetVariantsVariationAttributes getter 202 func (dci DecoratedCartItem) GetVariantsVariationAttributes() domain.Attributes { 203 attributes := domain.Attributes{} 204 if dci.IsConfigurable() { 205 variant, _ := dci.GetVariant() 206 207 for _, attributeName := range dci.Product.(domain.ConfigurableProductWithActiveVariant).VariantVariationAttributes { 208 attributes[attributeName] = variant.BaseData().Attributes[attributeName] 209 } 210 } 211 return attributes 212 } 213 214 // GetVariantsVariationAttributeCodes getter 215 func (dci DecoratedCartItem) GetVariantsVariationAttributeCodes() []string { 216 if dci.Product.Type() == domain.TypeConfigurableWithActiveVariant { 217 return dci.Product.(domain.ConfigurableProductWithActiveVariant).VariantVariationAttributes 218 } 219 return nil 220 } 221 222 // GetChargesToPay getter 223 func (dci DecoratedCartItem) GetChargesToPay(wishedToPaySum *domain.WishedToPay) priceDomain.Charges { 224 priceToPayForItem := dci.Item.RowPriceGrossWithDiscount 225 return dci.Product.SaleableData().GetLoyaltyChargeSplit(&priceToPayForItem, wishedToPaySum, dci.Item.Qty) 226 } 227 228 // GetGroupedBy legacy function 229 // deprecated: only here to support the old structure of accesing DecoratedItems in the Decorated Cart 230 // Use instead: 231 // 232 // or iterate over DecoratedCart.DecoratedDelivery 233 func (dc DecoratedCart) GetGroupedBy(group string, sortGroup bool, params ...string) []*GroupedDecoratedCartItem { 234 235 if dc.Logger != nil { 236 dc.Logger.Warn("DEPRECATED: DecoratedCart.GetGroupedBy()") 237 } 238 if len(dc.DecoratedDeliveries) != 1 { 239 return nil 240 } 241 return dc.DecoratedDeliveries[0].GetGroupedBy(group, sortGroup, params...) 242 } 243 244 // GetAllDecoratedItems getter 245 func (dc DecoratedCart) GetAllDecoratedItems() []DecoratedCartItem { 246 var allItems []DecoratedCartItem 247 for _, dd := range dc.DecoratedDeliveries { 248 allItems = append(allItems, dd.DecoratedItems...) 249 } 250 return allItems 251 } 252 253 // GetDecoratedDeliveryByCode getter 254 func (dc DecoratedCart) GetDecoratedDeliveryByCode(deliveryCode string) (*DecoratedDelivery, bool) { 255 for _, dd := range dc.DecoratedDeliveries { 256 if dd.Delivery.DeliveryInfo.Code == deliveryCode { 257 return &dd, true 258 } 259 260 } 261 return nil, false 262 } 263 264 // GetDecoratedDeliveryByCodeWithoutBool - used inside a template, therefor we need the method with a single return param 265 func (dc DecoratedCart) GetDecoratedDeliveryByCodeWithoutBool(deliveryCode string) *DecoratedDelivery { 266 decoratedDelivery, _ := dc.GetDecoratedDeliveryByCode(deliveryCode) 267 return decoratedDelivery 268 } 269 270 // GetGroupedBy getter 271 func (dc DecoratedDelivery) GetGroupedBy(group string, sortGroup bool, params ...string) []*GroupedDecoratedCartItem { 272 groupedItemsCollection := make(map[string]*GroupedDecoratedCartItem) 273 var groupedItemsCollectionKeys []string 274 275 var groupKey string 276 for _, item := range dc.DecoratedItems { 277 switch group { 278 case "retailer_code": 279 groupKey = item.Product.BaseData().RetailerCode 280 default: 281 groupKey = "default" 282 } 283 if _, ok := groupedItemsCollection[groupKey]; !ok { 284 groupedItemsCollection[groupKey] = &GroupedDecoratedCartItem{ 285 Group: groupKey, 286 } 287 groupedItemsCollectionKeys = append(groupedItemsCollectionKeys, groupKey) 288 } 289 290 if groupedItemsEntry, ok := groupedItemsCollection[groupKey]; ok { 291 groupedItemsEntry.DecoratedItems = append(groupedItemsEntry.DecoratedItems, item) 292 } 293 } 294 295 // sort before return 296 if sortGroup { 297 direction := "" 298 if len(params) > 0 { 299 direction = params[0] 300 } 301 302 if direction == "DESC" { 303 sort.Sort(sort.Reverse(sort.StringSlice(groupedItemsCollectionKeys))) 304 } else { 305 sort.Strings(groupedItemsCollectionKeys) 306 } 307 } 308 309 var groupedItemsCollectionSorted []*GroupedDecoratedCartItem 310 for _, keyName := range groupedItemsCollectionKeys { 311 if groupedItemsEntry, ok := groupedItemsCollection[keyName]; ok { 312 groupedItemsCollectionSorted = append(groupedItemsCollectionSorted, groupedItemsEntry) 313 } 314 } 315 return groupedItemsCollectionSorted 316 } 317 318 // GetDecoratedCartItemByID getter 319 func (dc DecoratedDelivery) GetDecoratedCartItemByID(ID string) *DecoratedCartItem { 320 for _, decoratedItem := range dc.DecoratedItems { 321 if decoratedItem.Item.ID == ID { 322 return &decoratedItem 323 } 324 } 325 return nil 326 } 327 328 func CartFromDecoratedCartFactoryContext(ctx context.Context) *cartDomain.Cart { 329 ctx, span := trace.StartSpan(ctx, "cart/CartFromDecoratedCartFactoryContext") 330 defer span.End() 331 332 if cart, ok := ctx.Value(cartCtxKey{}).(cartDomain.Cart); ok { 333 return &cart 334 } 335 336 return nil 337 } 338 339 func DeliveryCodeFromDecoratedCartFactoryContext(ctx context.Context) string { 340 ctx, span := trace.StartSpan(ctx, "cart/DeliveryCodeFromDecoratedCartFactoryContext") 341 defer span.End() 342 343 if deliveryCode, ok := ctx.Value(deliveryCtxKey{}).(string); ok { 344 return deliveryCode 345 } 346 347 return "" 348 } 349 350 func ContextWithDeliveryCode(ctx context.Context, deliveryCode string) context.Context { 351 if _, ok := ctx.Value(deliveryCtxKey{}).(string); !ok { 352 return context.WithValue(ctx, deliveryCtxKey{}, deliveryCode) 353 } 354 355 return ctx 356 }