SXA modules(talking about features mainly) follow the repository pattern. Each api controller depends on a repository to call commerce roles. For example, we have a module Sitecore.Commerce.XA.Feature.Cart which takes care of cart and checkout related operations. There is a CartController which talks to AddToCartRepository for adding a product to the cart.
This repository exposes three methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
AddToCartRenderingModel GetAddToCartModel(); BaseJsonResult AddLineItemsToCart( IStorefrontContext storefrontContext, IVisitorContext visitorContext, string catalogName, string productId, string variantId, Decimal quantity); BaseJsonResult AddBundleToCart( IStorefrontContext storefrontContext, IVisitorContext visitorContext, BundleSelectionInputModel bundle); |
While working on a project recently, we had to implement inventory checks for certain products while adding them to the cart. AddLineItmesToCart method doesn’t check for inventory so we decided to overwrite the functionality for this method.
In order to extend AddToCartRepository, we created a feature module in our project calling it MyProject.Commerce.XA.Feature.Cart. Since this module extends the Sitecore.Commerce.XA.Feature.Cart module, we added required references to our module. We only wanted to extend the functionality of AddToCartRepository so we created the ExtendedAddToCartRepository class which inherits from AddToCartRepository.
And rest was simple C# code 🙂
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 |
public class ExtendedAddToCartRepository : AddToCartRepository { ICartManager cartManager; ISiteContext siteContext; IStorefrontContext storefrontContext; IVisitorContext visitorContext; ISearchManager searchManager; IInventoryManager inventoryManager; public ExtendedAddToCartRepository(IModelProvider modelProvider, ICartManager cartManager, ISiteContext siteContext, IStorefrontContext storefrontContext, IVisitorContext visitorContext, ISearchManager searchManager, IInventoryManager inventoryManager) : base(modelProvider, cartManager, siteContext) { this.cartManager = cartManager; this.siteContext = siteContext; this.storefrontContext = storefrontContext; this.visitorContext = visitorContext; this.searchManager = searchManager; this.inventoryManager = inventoryManager; } public override AddToCartRenderingModel GetAddToCartModel() { var model = base.GetAddToCartModel(); if (string.IsNullOrEmpty(model.CatalogName)) { model.CatalogName = storefrontContext?.CurrentStorefront?.Catalog; } return model; } public override BaseJsonResult AddLineItemsToCart(IStorefrontContext storefrontContext, IVisitorContext visitorContext, string catalogName, string productId, string variantId, decimal quantity) { Assert.ArgumentNotNull(storefrontContext, "storefrontContext"); Assert.ArgumentNotNull(visitorContext, "visitorContext"); Assert.ArgumentNotNullOrEmpty(catalogName, "catalogName"); Assert.ArgumentNotNullOrEmpty(productId, "productId"); Assert.IsTrue(quantity > decimal.Zero, "quantity > 0"); var model = ModelProvider.GetModel<BaseJsonResult>(); var currentStorefront = storefrontContext.CurrentStorefront; var stockinformations = GetProductStockInformation(productId, variantId); if (!string.IsNullOrEmpty(stockinformations.ErrorMessage)) { model.Success = false; model.Errors.Add(stockinformations.ErrorMessage); return model; } if (stockinformations.Count < decimal.ToDouble(quantity)) { model.Success = false; model.Errors.Add($"Sorry, we only have ({stockinformations.Count}) in stock currently"); return model; } var currentCart = CartManager.GetCurrentCart(visitorContext, storefrontContext, false); if (currentCart.ServiceProviderResult.Success && currentCart.Result != null) { var list = new List<CartLineArgument> { new CartLineArgument { CatalogName = catalogName, ProductId = productId, VariantId = variantId, Quantity = quantity } }; var managerResponse = CartManager.AddLineItemsToCart(currentStorefront, visitorContext, currentCart.Result, list); if (!managerResponse.ServiceProviderResult.Success) { model.SetErrors(managerResponse.ServiceProviderResult); return model; } model.Success = true; return model; } var systemMessage = storefrontContext.GetSystemMessage("Cart Not Found Error", true); currentCart.ServiceProviderResult.SystemMessages.Add(new SystemMessage { Message = systemMessage }); model.SetErrors(currentCart.ServiceProviderResult); return model; } public (double Count, string ErrorMessage) GetProductStockInformation(string productId, string variantId) { var model = ModelProvider.GetModel<StockInfoListJsonResult>(); var currentStorefront = storefrontContext.CurrentStorefront; string catalog = currentStorefront.Catalog; Item product = searchManager.GetProduct(productId, catalog); Assert.IsNotNull(product, string.Format(CultureInfo.InvariantCulture, "Unable to locate the product with id: {0}", productId)); var list = new List<CommerceInventoryProduct>(); if (product.HasChildren) { foreach (Item child in product.Children) { list.Add(new CommerceInventoryProduct { ProductId = productId, CatalogName = catalog, VariantId = child.Name }); } } else { list.Add(new CommerceInventoryProduct { ProductId = productId, CatalogName = catalog }); } ManagerResponse<GetStockInformationResult, IEnumerable<StockInformation>> stockInformation = inventoryManager.GetStockInformation(currentStorefront, list, StockDetailsLevel.All); if (!stockInformation.ServiceProviderResult.Success) { return (0, $"Cannot get stock information for prodcut: {productId}|{variantId}"); } StockInfoJsonResult stockInfoResult = null; model.Initialize(stockInformation.Result); stockInfoResult = (string.IsNullOrEmpty(variantId)) ? model.StockInformationList. Where(x => x.ProductId == productId).FirstOrDefault() : model.StockInformationList.Where(x => x.VariantId == variantId).FirstOrDefault(); return (stockInfoResult == null ? 0 : stockInfoResult.Count, ""); } } |
InventoryManager was required to make the inventory checks.
And the last step to replace the dependency injection
1 2 3 4 5 6 7 |
public class ServicesConfigurator : IServicesConfigurator { public void Configure(IServiceCollection serviceCollection) { serviceCollection.AddTransient(typeof(IAddToCartRepository), typeof(ExtendedAddToCartRepository)); } } |
There you are!