flamingo.me/flamingo-commerce/v3@v3.11.0/cart/interfaces/controller/cartcontroller.go (about) 1 package controller 2 3 import ( 4 "context" 5 "encoding/gob" 6 "strconv" 7 8 "go.opencensus.io/trace" 9 10 "flamingo.me/flamingo-commerce/v3/cart/domain/decorator" 11 "flamingo.me/flamingo-commerce/v3/cart/domain/validation" 12 13 "flamingo.me/flamingo-commerce/v3/cart/application" 14 productDomain "flamingo.me/flamingo-commerce/v3/product/domain" 15 "flamingo.me/flamingo/v3/framework/flamingo" 16 "flamingo.me/flamingo/v3/framework/web" 17 ) 18 19 type ( 20 // CartViewData is used for cart views/templates 21 CartViewData struct { 22 DecoratedCart decorator.DecoratedCart 23 CartValidationResult validation.Result 24 AddToCartProductsData []productDomain.BasicProductData 25 CartRestrictionError application.RestrictionError 26 } 27 28 // CartViewController for carts 29 CartViewController struct { 30 responder *web.Responder 31 32 applicationCartService *application.CartService 33 applicationCartReceiverService *application.CartReceiverService 34 router *web.Router 35 logger flamingo.Logger 36 37 showEmptyCartPageIfNoItems bool 38 adjustItemsToRestrictedQty bool 39 } 40 41 // CartViewActionData for rendering results 42 CartViewActionData struct { 43 AddToCartProductsData []productDomain.BasicProductData 44 } 45 ) 46 47 func init() { 48 gob.Register(CartViewActionData{}) 49 } 50 51 // Inject dependencies 52 func (cc *CartViewController) Inject( 53 responder *web.Responder, 54 55 applicationCartService *application.CartService, 56 applicationCartReceiverService *application.CartReceiverService, 57 router *web.Router, 58 logger flamingo.Logger, 59 config *struct { 60 ShowEmptyCartPageIfNoItems bool `inject:"config:commerce.cart.showEmptyCartPageIfNoItems,optional"` 61 AdjustItemsToRestrictedQty bool `inject:"config:commerce.cart.adjustItemsToRestrictedQty,optional"` 62 }, 63 ) { 64 cc.responder = responder 65 cc.applicationCartService = applicationCartService 66 cc.applicationCartReceiverService = applicationCartReceiverService 67 cc.router = router 68 cc.logger = logger.WithField(flamingo.LogKeyCategory, "cartcontroller").WithField(flamingo.LogKeyModule, "cart") 69 70 if config != nil { 71 cc.showEmptyCartPageIfNoItems = config.ShowEmptyCartPageIfNoItems 72 cc.adjustItemsToRestrictedQty = config.AdjustItemsToRestrictedQty 73 } 74 } 75 76 // ViewAction the DecoratedCart View ( / cart) 77 func (cc *CartViewController) ViewAction(ctx context.Context, r *web.Request) web.Result { 78 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/ViewAction") 79 defer span.End() 80 81 if cc.adjustItemsToRestrictedQty { 82 err := cc.adjustItemsToRestrictedQtyAndAddToSession(ctx, r) 83 if err != nil { 84 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.viewaction: Error %v", err) 85 } 86 } 87 88 decoratedCart, err := cc.applicationCartReceiverService.ViewDecoratedCart(ctx, r.Session()) 89 if err != nil { 90 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.viewaction: Error %v", err) 91 return cc.responder.Render("checkout/carterror", nil).SetNoCache() 92 } 93 94 if cc.showEmptyCartPageIfNoItems && decoratedCart.Cart.ItemCount() == 0 { 95 return cc.responder.Render("checkout/emptycart", nil).SetNoCache() 96 } 97 98 cartViewData := CartViewData{ 99 DecoratedCart: *decoratedCart, 100 CartValidationResult: cc.applicationCartService.ValidateCart(ctx, r.Session(), decoratedCart), 101 } 102 103 flashes := r.Session().Flashes("cart.view.data") 104 if len(flashes) > 0 { 105 if cartViewActionData, ok := flashes[0].(CartViewActionData); ok { 106 cartViewData.AddToCartProductsData = cartViewActionData.AddToCartProductsData 107 } 108 } 109 restrictionFlashes := r.Session().Flashes("cart.view.error.restriction") 110 if len(restrictionFlashes) > 0 { 111 if restrictionError, ok := restrictionFlashes[0].(application.RestrictionError); ok { 112 cartViewData.CartRestrictionError = restrictionError 113 } 114 } 115 116 return cc.responder.Render("checkout/cart", cartViewData).SetNoCache() 117 } 118 119 // AddAndViewAction the DecoratedCart View ( / cart) 120 func (cc *CartViewController) AddAndViewAction(ctx context.Context, r *web.Request) web.Result { 121 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/AddAndViewAction") 122 defer span.End() 123 124 variantMarketplaceCode := r.Params["variantMarketplaceCode"] 125 126 qty, ok := r.Params["qty"] 127 if !ok { 128 qty = "1" 129 } 130 131 qtyInt, _ := strconv.Atoi(qty) 132 deliveryCode := r.Params["deliveryCode"] 133 134 addRequest := cc.applicationCartService.BuildAddRequest(ctx, r.Params["marketplaceCode"], variantMarketplaceCode, qtyInt, nil) 135 136 product, err := cc.applicationCartService.AddProduct(ctx, r.Session(), deliveryCode, addRequest) 137 if notAllowedErr, ok := err.(*validation.AddToCartNotAllowed); ok { 138 if notAllowedErr.RedirectHandlerName != "" { 139 return cc.responder.RouteRedirect(notAllowedErr.RedirectHandlerName, notAllowedErr.RedirectParams) 140 } 141 } 142 if err != nil { 143 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.addandviewaction: Error %v", err) 144 return cc.responder.Render("checkout/carterror", nil) 145 } 146 147 r.Session().AddFlash(CartViewActionData{ 148 AddToCartProductsData: []productDomain.BasicProductData{product.BaseData()}, 149 }, "cart.view.data") 150 return cc.responder.RouteRedirect("cart.view", nil) 151 } 152 153 // UpdateQtyAndViewAction the DecoratedCart View ( / cart) 154 func (cc *CartViewController) UpdateQtyAndViewAction(ctx context.Context, r *web.Request) web.Result { 155 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/UpdateQtyAndViewAction") 156 defer span.End() 157 158 id, ok := r.Params["id"] 159 if !ok { 160 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.UpdateQtyAndViewAction: param id not found") 161 return cc.responder.RouteRedirect("cart.view", nil) 162 } 163 164 qty, ok := r.Params["qty"] 165 if !ok { 166 qty = "1" 167 } 168 169 qtyInt, err := strconv.Atoi(qty) 170 if err != nil { 171 qtyInt = 1 172 } 173 deliveryCode := r.Params["deliveryCode"] 174 175 err = cc.applicationCartService.UpdateItemQty(ctx, r.Session(), id, deliveryCode, qtyInt) 176 if err != nil { 177 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.UpdateAndViewAction: Error %v", err) 178 179 if e, ok := err.(*application.RestrictionError); ok { 180 r.Session().AddFlash(e, "cart.view.error.restriction") 181 } 182 } 183 184 return cc.responder.RouteRedirect("cart.view", nil) 185 } 186 187 // DeleteAndViewAction the DecoratedCart View ( / cart) 188 func (cc *CartViewController) DeleteAndViewAction(ctx context.Context, r *web.Request) web.Result { 189 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/DeleteAndViewAction") 190 defer span.End() 191 192 id, ok := r.Params["id"] 193 if !ok { 194 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.deleteaction: param id not found") 195 return cc.responder.RouteRedirect("cart.view", nil) 196 } 197 deliveryCode := r.Params["deliveryCode"] 198 err := cc.applicationCartService.DeleteItem(ctx, r.Session(), id, deliveryCode) 199 if err != nil { 200 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.deleteaction: Error %v", err) 201 } 202 203 return cc.responder.RouteRedirect("cart.view", nil) 204 } 205 206 // DeleteAllAndViewAction empties the cart and shows it 207 func (cc *CartViewController) DeleteAllAndViewAction(ctx context.Context, r *web.Request) web.Result { 208 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/DeleteAllAndViewAction") 209 defer span.End() 210 211 err := cc.applicationCartService.DeleteAllItems(ctx, r.Session()) 212 if err != nil { 213 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.deleteaction: Error %v", err) 214 } 215 216 return cc.responder.RouteRedirect("cart.view", nil) 217 } 218 219 // CleanAndViewAction empties the cart and shows it 220 func (cc *CartViewController) CleanAndViewAction(ctx context.Context, r *web.Request) web.Result { 221 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/CleanAndViewAction") 222 defer span.End() 223 224 err := cc.applicationCartService.Clean(ctx, r.Session()) 225 if err != nil { 226 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.deleteaction: Error %v", err) 227 } 228 229 return cc.responder.RouteRedirect("cart.view", nil) 230 } 231 232 // CleanDeliveryAndViewAction empties a single delivery and shows it 233 func (cc *CartViewController) CleanDeliveryAndViewAction(ctx context.Context, r *web.Request) web.Result { 234 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/CleanDeliveryAndViewAction") 235 defer span.End() 236 237 deliveryCode := r.Params["deliveryCode"] 238 _, err := cc.applicationCartService.DeleteDelivery(ctx, r.Session(), deliveryCode) 239 if err != nil { 240 cc.logger.WithContext(ctx).Warn("cart.cartcontroller.deleteaction: Error %v", err) 241 } 242 243 return cc.responder.RouteRedirect("cart.view", nil) 244 } 245 246 // adjustItemsToRestrictedQtyAndAddToSession checks the items of the cart against their qty restrictions and adds adjustments to the session 247 func (cc *CartViewController) adjustItemsToRestrictedQtyAndAddToSession(ctx context.Context, r *web.Request) error { 248 ctx, span := trace.StartSpan(ctx, "cart/CartViewController/adjustItemsToRestrictedQtyAndAddToSession") 249 defer span.End() 250 251 adjustments, err := cc.applicationCartService.AdjustItemsToRestrictedQty(ctx, r.Session()) 252 if err != nil { 253 return err 254 } 255 256 cc.addAdjustmentsToSession(adjustments, r) 257 258 return nil 259 } 260 261 func (cc *CartViewController) addAdjustmentsToSession(adjustments application.QtyAdjustmentResults, r *web.Request) { 262 var storedAdjustments application.QtyAdjustmentResults 263 var ok bool 264 265 if sessionStoredAdjustments, found := r.Session().Load("cart.view.quantity.adjustments"); found { 266 if storedAdjustments, ok = sessionStoredAdjustments.(application.QtyAdjustmentResults); !ok { 267 storedAdjustments = application.QtyAdjustmentResults{} 268 } 269 } else { 270 storedAdjustments = application.QtyAdjustmentResults{} 271 } 272 273 for _, adjustment := range adjustments { 274 if i, contains := cc.containsAdjustment(storedAdjustments, adjustment); contains { 275 storedAdjustments[i] = adjustment 276 } else { 277 storedAdjustments = append(storedAdjustments, adjustment) 278 } 279 } 280 281 r.Session().Store("cart.view.quantity.adjustments", storedAdjustments) 282 } 283 284 func (cc *CartViewController) containsAdjustment(adjustments application.QtyAdjustmentResults, adjustment application.QtyAdjustmentResult) (index int, contains bool) { 285 for i, a := range adjustments { 286 if a.OriginalItem.ID == adjustment.OriginalItem.ID && a.DeliveryCode == adjustment.DeliveryCode { 287 return i, true 288 } 289 } 290 291 return -1, false 292 }