private async Task ManageStockByAttributesInventory(Product product, Shipment shipment, ShipmentItem shipmentItem) { var combination = _productAttributeParser.FindProductAttributeCombination(product, shipmentItem.Attributes); if (combination == null) { return; } if (!product.UseMultipleWarehouses) { combination.ReservedQuantity -= shipmentItem.Quantity; combination.StockQuantity -= shipmentItem.Quantity; var builder = Builders <Product> .Filter; var filter = builder.Eq(x => x.Id, product.Id); filter &= builder.ElemMatch(x => x.ProductAttributeCombinations, y => y.Id == combination.Id); var update = Builders <Product> .Update .Set("ProductAttributeCombinations.$.StockQuantity", combination.StockQuantity) .Set("ProductAttributeCombinations.$.ReservedQuantity", combination.ReservedQuantity) .Set("ProductAttributeCombinations.$.WarehouseInventory", combination.WarehouseInventory) .CurrentDate("UpdatedOnUtc"); await _productRepository.Collection.UpdateManyAsync(filter, update); } else { var pwi = combination.WarehouseInventory.FirstOrDefault(pi => pi.WarehouseId == shipmentItem.WarehouseId); if (pwi == null) { return; } pwi.ReservedQuantity -= shipmentItem.Quantity; pwi.StockQuantity -= shipmentItem.Quantity; combination.StockQuantity = combination.WarehouseInventory.Sum(x => x.StockQuantity); combination.ReservedQuantity = combination.WarehouseInventory.Sum(x => x.ReservedQuantity); var builder = Builders <Product> .Filter; var filter = builder.Eq(x => x.Id, product.Id); filter &= builder.ElemMatch(x => x.ProductAttributeCombinations, y => y.Id == combination.Id); var update = Builders <Product> .Update .Set("ProductAttributeCombinations.$.StockQuantity", combination.StockQuantity) .Set("ProductAttributeCombinations.$.ReservedQuantity", combination.ReservedQuantity) .Set("ProductAttributeCombinations.$.WarehouseInventory", combination.WarehouseInventory) .CurrentDate("UpdatedOnUtc"); await _productRepository.Collection.UpdateManyAsync(filter, update); } product.StockQuantity = product.ProductAttributeCombinations.Sum(x => x.StockQuantity); product.ReservedQuantity = product.ProductAttributeCombinations.Sum(x => x.ReservedQuantity); await UpdateStockProduct(product); }
private async Task ManageStockByAttributesInventory(Product product, Shipment shipment, ShipmentItem shipmentItem) { var combination = _productAttributeParser.FindProductAttributeCombination(product, shipmentItem.Attributes); if (combination == null) { return; } if (!product.UseMultipleWarehouses) { combination.ReservedQuantity -= shipmentItem.Quantity; combination.StockQuantity -= shipmentItem.Quantity; if (combination.ReservedQuantity < 0) { combination.ReservedQuantity = 0; } await _productRepository.UpdateToSet(product.Id, x => x.ProductAttributeCombinations, z => z.Id, combination.Id, combination); await _productRepository.UpdateField(product.Id, x => x.UpdatedOnUtc, DateTime.UtcNow); } else { var pwi = combination.WarehouseInventory.FirstOrDefault(pi => pi.WarehouseId == shipmentItem.WarehouseId); if (pwi == null) { return; } pwi.ReservedQuantity -= shipmentItem.Quantity; pwi.StockQuantity -= shipmentItem.Quantity; if (pwi.ReservedQuantity < 0) { pwi.ReservedQuantity = 0; } combination.StockQuantity = combination.WarehouseInventory.Sum(x => x.StockQuantity); combination.ReservedQuantity = combination.WarehouseInventory.Sum(x => x.ReservedQuantity); await _productRepository.UpdateToSet(product.Id, x => x.ProductAttributeCombinations, z => z.Id, combination.Id, combination); await _productRepository.UpdateField(product.Id, x => x.UpdatedOnUtc, DateTime.UtcNow); } product.StockQuantity = product.ProductAttributeCombinations.Sum(x => x.StockQuantity); product.ReservedQuantity = product.ProductAttributeCombinations.Sum(x => x.ReservedQuantity); await UpdateStockProduct(product); }
/// <summary> /// Gets product code /// </summary> private static void GetProductCode(this Product product, string attributesXml, IProductAttributeParser productAttributeParser, out string productCode) { if (product == null) { throw new ArgumentNullException("product"); } productCode = null; if (!String.IsNullOrEmpty(attributesXml) && product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { //manage stock by attribute combinations if (productAttributeParser == null) { throw new ArgumentNullException("productAttributeParser"); } //let's find appropriate record var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { productCode = combination.ProductCode; } } if (String.IsNullOrEmpty(productCode)) { productCode = product.ProductCode; } }
private static string GeStockMessage(Product product, string attributesXml, ILocalizationService localizationService, IProductAttributeParser productAttributeParser, IDateRangeService dateRangeService, IProductAttributeService productAttributeService) { if (!product.DisplayStockAvailability) { return(string.Empty); } string stockMessage; var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { //combination exists var stockQuantity = combination.StockQuantity; if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity localizationService.GetResource("Products.Availability.InStock"); } else if (combination.AllowOutOfStockOrders) { stockMessage = localizationService.GetResource("Products.Availability.InStock"); } else { var productAvailabilityRange = dateRangeService.GetProductAvailabilityRangeById(product.ProductAvailabilityRangeId); stockMessage = productAvailabilityRange == null ? localizationService.GetResource("Products.Availability.OutOfStock") : string.Format(localizationService.GetResource("Products.Availability.AvailabilityRange"), productAvailabilityRange.GetLocalized(range => range.Name)); } } else { //no combination configured if (product.AllowAddingOnlyExistingAttributeCombinations) { var productAvailabilityRange = dateRangeService.GetProductAvailabilityRangeById(product.ProductAvailabilityRangeId); stockMessage = productAvailabilityRange == null ? localizationService.GetResource("Products.Availability.OutOfStock") : string.Format(localizationService.GetResource("Products.Availability.AvailabilityRange"), productAvailabilityRange.GetLocalized(range => range.Name)); } else { stockMessage = !productAttributeService.GetProductAttributeMappingsByProductId(product.Id) .Any(pam => pam.IsRequired) ? localizationService.GetResource("Products.Availability.InStock") : localizationService.GetResource("Products.Availability.SelectRequiredAttributes"); } } return(stockMessage); }
/// <summary> /// Get product picture (for shopping cart and order details pages) /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Atributes (in XML format)</param> /// <param name="pictureService">Picture service</param> /// <param name="productAttributeParser">Product attribute service</param> /// <returns>Picture</returns> public static Picture GetProductPicture(this Product product, string attributesXml, IPictureService pictureService, IProductAttributeParser productAttributeParser) { if (product == null) { throw new ArgumentNullException(nameof(product)); } if (pictureService == null) { throw new ArgumentNullException(nameof(pictureService)); } if (productAttributeParser == null) { throw new ArgumentNullException(nameof(productAttributeParser)); } //first, try to get product attribute combination picture var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); var combinationPicture = pictureService.GetPictureById(combination?.PictureId ?? 0); if (combinationPicture != null) { return(combinationPicture); } //then, let's see whether we have attribute values with pictures var attributePicture = productAttributeParser.ParseProductAttributeValues(attributesXml) .Select(attributeValue => pictureService.GetPictureById(attributeValue?.PictureId ?? 0)) .FirstOrDefault(picture => picture != null); if (attributePicture != null) { return(attributePicture); } //now let's load the default product picture var productPicture = pictureService.GetPicturesByProductId(product.Id, 1).FirstOrDefault(); if (productPicture != null) { return(productPicture); } //finally, let's check whether this product has some parent "grouped" product if (!product.VisibleIndividually && product.ParentGroupedProductId > 0) { var parentGroupedProductPicture = pictureService.GetPicturesByProductId(product.ParentGroupedProductId, 1).FirstOrDefault(); if (parentGroupedProductPicture != null) { return(parentGroupedProductPicture); } } return(null); }
/// <summary> /// Gets SKU, Manufacturer part number and GTIN /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Attributes in XML format</param> /// <param name="productAttributeParser">Product attribute service (used when attributes are specified)</param> /// <param name="sku">SKU</param> /// <param name="manufacturerPartNumber">Manufacturer part number</param> // nopCommerceTest1-START /// <param name="author">Author</param> // nopCommerceTest1-END /// <param name="gtin">GTIN</param> private static void GetSkuMpnGtin(this Product product, string attributesXml, IProductAttributeParser productAttributeParser, out string sku, out string manufacturerPartNumber, out string author, out string gtin) { if (product == null) { throw new ArgumentNullException(nameof(product)); } sku = null; manufacturerPartNumber = null; // nopCommerceTest1-START author = null; // nopCommerceTest1-END gtin = null; if (!string.IsNullOrEmpty(attributesXml) && product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { //manage stock by attribute combinations if (productAttributeParser == null) { throw new ArgumentNullException(nameof(productAttributeParser)); } //let's find appropriate record var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { sku = combination.Sku; manufacturerPartNumber = combination.ManufacturerPartNumber; // nopCommerceTest1-START author = combination.Author; // nopCommerceTest1-END gtin = combination.Gtin; } } if (string.IsNullOrEmpty(sku)) { sku = product.Sku; } if (string.IsNullOrEmpty(manufacturerPartNumber)) { manufacturerPartNumber = product.ManufacturerPartNumber; } // nopCommerceTest1-START if (String.IsNullOrEmpty(author)) { author = product.Author; } // nopCommerceTest1-END if (string.IsNullOrEmpty(gtin)) { gtin = product.Gtin; } }
/// <summary> /// Gets SKU, Manufacturer part number and GTIN /// </summary> /// <param name="product">Product</param> /// <param name="attributes">Attributes</param> /// <param name="productAttributeParser">Product attribute service (used when attributes are specified)</param> /// <param name="sku">SKU</param> /// <param name="manufacturerPartNumber">Manufacturer part number</param> /// <param name="gtin">GTIN</param> private static void GetSkuMpnGtin(this Product product, IList <CustomAttribute> attributes, IProductAttributeParser productAttributeParser, out string sku, out string manufacturerPartNumber, out string gtin) { if (product == null) { throw new ArgumentNullException("product"); } sku = null; manufacturerPartNumber = null; gtin = null; if (attributes != null && product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { //manage stock by attribute combinations if (productAttributeParser == null) { throw new ArgumentNullException("productAttributeParser"); } //let's find appropriate record var combination = productAttributeParser.FindProductAttributeCombination(product, attributes); if (combination != null) { sku = combination.Sku; manufacturerPartNumber = combination.ManufacturerPartNumber; gtin = combination.Gtin; } } if (string.IsNullOrEmpty(sku)) { sku = product.Sku; } if (string.IsNullOrEmpty(manufacturerPartNumber)) { manufacturerPartNumber = product.ManufacturerPartNumber; } if (string.IsNullOrEmpty(gtin)) { gtin = product.Gtin; } }
/// <summary> /// Gets SKU, Collection part number and GTIN /// </summary> /// <param name="product">Product</param> /// <param name="attributes">Attributes</param> /// <param name="productAttributeParser">Product attribute service</param> /// <param name="sku">SKU</param> /// <param name="Mpn">MPN</param> /// <param name="gtin">GTIN</param> private static void GetSkuMpnGtin(this Product product, IList <CustomAttribute> attributes, IProductAttributeParser productAttributeParser, out string sku, out string Mpn, out string gtin) { if (product == null) { throw new ArgumentNullException(nameof(product)); } sku = null; Mpn = null; gtin = null; if (attributes != null && product.ManageInventoryMethodId == ManageInventoryMethod.ManageStockByAttributes) { if (productAttributeParser == null) { throw new ArgumentNullException(nameof(productAttributeParser)); } var combination = productAttributeParser.FindProductAttributeCombination(product, attributes); if (combination != null) { sku = combination.Sku; Mpn = combination.Mpn; gtin = combination.Gtin; } } if (string.IsNullOrEmpty(sku)) { sku = product.Sku; } if (string.IsNullOrEmpty(Mpn)) { Mpn = product.Mpn; } if (string.IsNullOrEmpty(gtin)) { gtin = product.Gtin; } }
/// <summary> /// Create items from shopping cart /// </summary> /// <param name="shoppingCart">Shopping cart</param> /// <returns>Collection of PayPal items</returns> protected IEnumerable <Item> CreateItems(IEnumerable <ShoppingCartItem> shoppingCart) { return(shoppingCart.Select(shoppingCartItem => { if (shoppingCartItem.Product == null) { return null; } var item = new Item { //name name = shoppingCartItem.Product.Name }; //SKU if (!string.IsNullOrEmpty(shoppingCartItem.AttributesXml)) { var combination = _productAttributeParser.FindProductAttributeCombination(shoppingCartItem.Product, shoppingCartItem.AttributesXml); item.sku = combination != null && !string.IsNullOrEmpty(combination.Sku) ? combination.Sku : shoppingCartItem.Product.Sku; } else { item.sku = shoppingCartItem.Product.Sku; } //item price var unitPrice = _priceCalculationService.GetUnitPrice(shoppingCartItem); var price = _taxService.GetProductPrice(shoppingCartItem.Product, unitPrice, false, shoppingCartItem.Customer, out decimal _); item.price = price.ToString("N", new CultureInfo("en-US")); //quantity item.quantity = shoppingCartItem.Quantity.ToString(); return item; })); }
/// <summary> /// Gets the shopping cart unit price (one item) /// </summary> /// <param name="product">Product</param> /// <param name="customer">Customer</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="quantity">Quantity</param> /// <param name="attributesXml">Product atrributes (XML format)</param> /// <param name="customerEnteredPrice">Customer entered price (if specified)</param> /// <param name="rentalStartDate">Rental start date (null for not rental products)</param> /// <param name="rentalEndDate">Rental end date (null for not rental products)</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for price computation</param> /// <param name="discountAmount">Applied discount amount</param> /// <param name="appliedDiscount">Applied discount</param> /// <returns>Shopping cart unit price (one item)</returns> public virtual async Task <(decimal unitprice, decimal discountAmount, List <AppliedDiscount> appliedDiscounts)> GetUnitPrice(Product product, Customer customer, ShoppingCartType shoppingCartType, int quantity, string attributesXml, decimal customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts) { if (product == null) { throw new ArgumentNullException("product"); } if (customer == null) { throw new ArgumentNullException("customer"); } var discountAmount = decimal.Zero; var appliedDiscounts = new List <AppliedDiscount>(); decimal?finalPrice = null; if (shoppingCartType == ShoppingCartType.Auctions && product.ProductType == ProductType.Auction) { finalPrice = customerEnteredPrice; } if (!finalPrice.HasValue) { var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { if (combination.OverriddenPrice.HasValue) { finalPrice = combination.OverriddenPrice.Value; } if (combination.TierPrices.Any()) { var storeId = _storeContext.CurrentStore.Id; var actualTierPrices = combination.TierPrices.Where(x => string.IsNullOrEmpty(x.StoreId) || x.StoreId == storeId) .Where(x => string.IsNullOrEmpty(x.CustomerRoleId) || customer.CustomerRoles.Where(role => role.Active).Select(role => role.Id).Contains(x.CustomerRoleId)).ToList(); var tierPrice = actualTierPrices.LastOrDefault(price => quantity >= price.Quantity); if (tierPrice != null) { finalPrice = tierPrice.Price; } } } } if (!finalPrice.HasValue) { //summarize price of all attributes decimal attributesTotalPrice = decimal.Zero; var attributeValues = _productAttributeParser.ParseProductAttributeValues(product, attributesXml); if (attributeValues != null) { foreach (var attributeValue in attributeValues) { attributesTotalPrice += await GetProductAttributeValuePriceAdjustment(attributeValue); } } //get price of a product (with previously calculated price of all attributes) if (product.CustomerEntersPrice) { finalPrice = customerEnteredPrice; } else { int qty; if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems) { //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records //so let's find how many of the current products are in the cart qty = customer.ShoppingCartItems .Where(x => x.ProductId == product.Id) .Where(x => x.ShoppingCartType == shoppingCartType) .Sum(x => x.Quantity); if (qty == 0) { qty = quantity; } } else { qty = quantity; } var getfinalPrice = await GetFinalPrice(product, customer, attributesTotalPrice, includeDiscounts, qty, product.ProductType == ProductType.Reservation?rentalStartDate : null, product.ProductType == ProductType.Reservation?rentalEndDate : null); finalPrice = getfinalPrice.finalPrice; discountAmount = getfinalPrice.discountAmount; appliedDiscounts = getfinalPrice.appliedDiscounts; } } if (!finalPrice.HasValue) { finalPrice = 0; } //rounding if (_shoppingCartSettings.RoundPricesDuringCalculation) { var primaryCurrency = await _currencyService.GetPrimaryExchangeRateCurrency(); finalPrice = RoundingHelper.RoundPrice(finalPrice.Value, primaryCurrency); } return(finalPrice.Value, discountAmount, appliedDiscounts); }
public List <Dictionary <string, object> > GenerateProductStockFeed() { var productFeed = new List <Dictionary <string, object> >(); var products = _productService.SearchProducts(visibleIndividuallyOnly: true); foreach (var product in products) { var productsToProcess = new List <Product>(); switch (product.ProductType) { case ProductType.SimpleProduct: { //simple product doesn't have child products productsToProcess.Add(product); } break; case ProductType.GroupedProduct: { //grouped products could have several child products var associatedProducts = _productService.GetAssociatedProducts(product.Id); productsToProcess.AddRange(associatedProducts); } break; default: continue; } foreach (var productToProcess in productsToProcess) { var productInfo = new Dictionary <string, object> { { "id", productToProcess.Id.ToString() }, { "product_availability", product.AvailableEndDateTimeUtc != null ? product.AvailableEndDateTimeUtc.Value.ToString("yy-MM-dd hh:mm:ss") : null } }; decimal price; decimal priceWithDiscount; GetProductPrice(product, out price, out priceWithDiscount); productInfo.Add("price", price.ToString(new CultureInfo("en-US", false).NumberFormat)); productInfo.Add("promo", priceWithDiscount.ToString(new CultureInfo("en-US", false).NumberFormat)); productInfo.Add("promo_price_end_date", null); var inventory = new Dictionary <string, object>(); var attributes = new Dictionary <string, object>(); var allAttributesXml = _productAttributeParser.GenerateAllCombinations(product, true); foreach (var attributesXml in allAttributesXml) { var warnings = new List <string>(); warnings.AddRange(_shoppingCartService.GetShoppingCartItemAttributeWarnings(_workContext.CurrentCustomer, ShoppingCartType.ShoppingCart, product, 1, attributesXml, true)); if (warnings.Count != 0) { continue; } var inStock = true; var existingCombination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (existingCombination != null) { inStock = existingCombination.StockQuantity > 0; } var varCode = GetCombinationCode(attributesXml); if (!attributes.ContainsKey(varCode)) { attributes.Add(varCode, inStock); } } var variation = new Dictionary <string, object> { { "variation", attributes } }; if (attributes.Count > 0) { inventory.Add("variations", true); inventory.Add("stock", variation); } else { inventory.Add("variations", false); inventory.Add("stock", product.StockQuantity > 0); } productInfo.Add("inventory", inventory); productInfo.Add("user_groups", false); productFeed.Add(productInfo); } } return(productFeed); }
public virtual async Task <ShipmentModel> PrepareShipmentModel(Order order) { var model = new ShipmentModel { OrderId = order.Id, OrderNumber = order.OrderNumber }; //measures var baseWeight = await _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId); var baseWeightIn = baseWeight != null ? baseWeight.Name : ""; var baseDimension = await _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId); var baseDimensionIn = baseDimension != null ? baseDimension.Name : ""; var orderItems = order.OrderItems; //a vendor should have access only to his products if (_workContext.CurrentVendor != null && !await _groupService.IsStaff(_workContext.CurrentCustomer)) { orderItems = orderItems.Where(_workContext.HasAccessToOrderItem).ToList(); } foreach (var orderItem in orderItems) { var product = await _productService.GetProductByIdIncludeArch(orderItem.ProductId); //we can ship only shippable products if (!product.IsShipEnabled) { continue; } //quantities var qtyInThisShipment = 0; var maxQtyToAdd = orderItem.OpenQty; var qtyOrdered = orderItem.Quantity; var qtyInAllShipments = orderItem.ShipQty; //ensure that this product can be added to a shipment if (maxQtyToAdd <= 0) { continue; } var shipmentItemModel = new ShipmentModel.ShipmentItemModel { OrderItemId = orderItem.Id, ProductId = orderItem.ProductId, ProductName = product.Name, WarehouseId = orderItem.WarehouseId, Sku = product.FormatSku(orderItem.Attributes, _productAttributeParser), AttributeInfo = orderItem.AttributeDescription, ShipSeparately = product.ShipSeparately, ItemWeight = orderItem.ItemWeight.HasValue ? string.Format("{0:F2} [{1}]", orderItem.ItemWeight, baseWeightIn) : "", ItemDimensions = string.Format("{0:F2} x {1:F2} x {2:F2} [{3}]", product.Length, product.Width, product.Height, baseDimensionIn), QuantityOrdered = qtyOrdered, QuantityInThisShipment = qtyInThisShipment, QuantityInAllShipments = qtyInAllShipments, QuantityToAdd = maxQtyToAdd, }; if (product.ManageInventoryMethodId == ManageInventoryMethod.ManageStock) { if (product.UseMultipleWarehouses) { //multiple warehouses supported shipmentItemModel.AllowToChooseWarehouse = true; foreach (var pwi in product.ProductWarehouseInventory .OrderBy(w => w.WarehouseId).ToList()) { var warehouse = await _warehouseService.GetWarehouseById(pwi.WarehouseId); if (warehouse != null) { shipmentItemModel.AvailableWarehouses.Add(new ShipmentModel.ShipmentItemModel.WarehouseInfo { WarehouseId = warehouse.Id, WarehouseName = warehouse.Name, StockQuantity = pwi.StockQuantity, ReservedQuantity = pwi.ReservedQuantity, PlannedQuantity = await _shipmentService.GetQuantityInShipments(product, orderItem.Attributes, warehouse.Id, true, true) }); } } } else { //multiple warehouses are not supported var warehouse = await _warehouseService.GetWarehouseById(product.WarehouseId); if (warehouse != null) { shipmentItemModel.AvailableWarehouses.Add(new ShipmentModel.ShipmentItemModel.WarehouseInfo { WarehouseId = warehouse.Id, WarehouseName = warehouse.Name, StockQuantity = product.StockQuantity }); } } } if (product.ManageInventoryMethodId == ManageInventoryMethod.ManageStockByAttributes) { if (product.UseMultipleWarehouses) { //multiple warehouses supported shipmentItemModel.AllowToChooseWarehouse = true; var comb = _productAttributeParser.FindProductAttributeCombination(product, orderItem.Attributes); if (comb != null) { foreach (var pwi in comb.WarehouseInventory .OrderBy(w => w.WarehouseId).ToList()) { var warehouse = await _warehouseService.GetWarehouseById(pwi.WarehouseId); if (warehouse != null) { shipmentItemModel.AvailableWarehouses.Add(new ShipmentModel.ShipmentItemModel.WarehouseInfo { WarehouseId = warehouse.Id, WarehouseName = warehouse.Name, StockQuantity = pwi.StockQuantity, ReservedQuantity = pwi.ReservedQuantity, PlannedQuantity = await _shipmentService.GetQuantityInShipments(product, orderItem.Attributes, warehouse.Id, true, true) }); } } } } else { //multiple warehouses are not supported var warehouse = await _warehouseService.GetWarehouseById(product.WarehouseId); if (warehouse != null) { shipmentItemModel.AvailableWarehouses.Add(new ShipmentModel.ShipmentItemModel.WarehouseInfo { WarehouseId = warehouse.Id, WarehouseName = warehouse.Name, StockQuantity = product.StockQuantity }); } } } if (product.ManageInventoryMethodId == ManageInventoryMethod.ManageStockByBundleProducts) { if (!string.IsNullOrEmpty(orderItem.WarehouseId)) { var warehouse = await _warehouseService.GetWarehouseById(product.WarehouseId); if (warehouse != null) { shipmentItemModel.AvailableWarehouses.Add(new ShipmentModel.ShipmentItemModel.WarehouseInfo { WarehouseId = warehouse.Id, WarehouseName = warehouse.Name, StockQuantity = await GetStockQty(product, orderItem.WarehouseId), ReservedQuantity = await GetReservedQty(product, orderItem.WarehouseId), PlannedQuantity = await GetPlannedQty(product, orderItem.WarehouseId) }); } } else { shipmentItemModel.AllowToChooseWarehouse = false; if (shipmentItemModel.AllowToChooseWarehouse) { var warehouses = await _warehouseService.GetAllWarehouses(); foreach (var warehouse in warehouses) { shipmentItemModel.AvailableWarehouses.Add(new ShipmentModel.ShipmentItemModel.WarehouseInfo { WarehouseId = warehouse.Id, WarehouseName = warehouse.Name, StockQuantity = await GetStockQty(product, warehouse.Id), ReservedQuantity = await GetReservedQty(product, warehouse.Id), PlannedQuantity = await GetPlannedQty(product, warehouse.Id) }); } } } } model.Items.Add(shipmentItemModel); } return(model); }
/// <summary> /// Formats the stock availability/quantity message /// </summary> public static string FormatStockMessage(this Product product, string attributesXml, ILocalizationService localizationService, IProductAttributeParser productAttributeParser) { if (product == null) { throw new ArgumentNullException("product"); } if (localizationService == null) { throw new ArgumentNullException("localizationService"); } if (productAttributeParser == null) { throw new ArgumentNullException("productAttributeParser"); } string stockMessage = string.Empty; switch (product.ManageInventoryMethod) { case ManageInventoryMethod.ManageStock: { #region Manage stock if (!product.DisplayStockAvailability) { return(stockMessage); } var stockQuantity = product.GetTotalStockQuantity(); if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity localizationService.GetResource("Products.Availability.InStock"); } else { //out of stock stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); } #endregion } break; case ManageInventoryMethod.ManageStockByAttributes: { #region Manage stock by attributes if (!product.DisplayStockAvailability) { return(stockMessage); } var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { //combination exists var stockQuantity = combination.StockQuantity; if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity localizationService.GetResource("Products.Availability.InStock"); } else if (combination.AllowOutOfStockOrders) { stockMessage = localizationService.GetResource("Products.Availability.InStock"); } else { stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); } } else { //no combination configured if (product.AllowAddingOnlyExistingAttributeCombinations) { stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); } else { stockMessage = localizationService.GetResource("Products.Availability.InStock"); } } #endregion } break; case ManageInventoryMethod.DontManageStock: default: return(stockMessage); } return(stockMessage); }
public async Task <ProductDetailsAttributeChangeModel> Handle(GetProductDetailsAttributeChange request, CancellationToken cancellationToken) { var model = new ProductDetailsAttributeChangeModel(); string attributeXml = await _mediator.Send(new GetParseProductAttributes() { Product = request.Product, Form = request.Form }); string warehouseId = _shoppingCartSettings.AllowToSelectWarehouse ? request.Form["WarehouseId"].ToString() : request.Product.UseMultipleWarehouses ? request.Store.DefaultWarehouseId : (string.IsNullOrEmpty(request.Store.DefaultWarehouseId) ? request.Product.WarehouseId : request.Store.DefaultWarehouseId); //rental attributes DateTime?rentalStartDate = null; DateTime?rentalEndDate = null; if (request.Product.ProductType == ProductType.Reservation) { request.Product.ParseReservationDates(request.Form, out rentalStartDate, out rentalEndDate); } model.Sku = request.Product.FormatSku(attributeXml, _productAttributeParser); model.Mpn = request.Product.FormatMpn(attributeXml, _productAttributeParser); model.Gtin = request.Product.FormatGtin(attributeXml, _productAttributeParser); if (await _permissionService.Authorize(StandardPermissionProvider.DisplayPrices) && !request.Product.CustomerEntersPrice && request.Product.ProductType != ProductType.Auction) { //we do not calculate price of "customer enters price" option is enabled var unitprice = await _priceCalculationService.GetUnitPrice(request.Product, request.Customer, ShoppingCartType.ShoppingCart, 1, attributeXml, 0, rentalStartDate, rentalEndDate, true); decimal discountAmount = unitprice.discountAmount; List <AppliedDiscount> scDiscounts = unitprice.appliedDiscounts; decimal finalPrice = unitprice.unitprice; var productprice = await _taxService.GetProductPrice(request.Product, finalPrice); decimal finalPriceWithDiscountBase = productprice.productprice; decimal taxRate = productprice.taxRate; decimal finalPriceWithDiscount = await _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithDiscountBase, request.Currency); model.Price = _priceFormatter.FormatPrice(finalPriceWithDiscount); } //stock model.StockAvailability = request.Product.FormatStockMessage(warehouseId, attributeXml, _localizationService, _productAttributeParser); //back in stock subscription if ((request.Product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes || request.Product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) && request.Product.BackorderMode == BackorderMode.NoBackorders && request.Product.AllowBackInStockSubscriptions) { var combination = _productAttributeParser.FindProductAttributeCombination(request.Product, attributeXml); if (combination != null) { if (request.Product.GetTotalStockQuantityForCombination(combination, warehouseId: request.Store.DefaultWarehouseId) <= 0) { model.DisplayBackInStockSubscription = true; } } if (request.Product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) { model.DisplayBackInStockSubscription = request.Product.AllowBackInStockSubscriptions; attributeXml = ""; } var subscription = await _backInStockSubscriptionService .FindSubscription(request.Customer.Id, request.Product.Id, attributeXml, request.Store.Id, warehouseId); if (subscription != null) { model.ButtonTextBackInStockSubscription = _localizationService.GetResource("BackInStockSubscriptions.DeleteNotifyWhenAvailable"); } else { model.ButtonTextBackInStockSubscription = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"); } } //conditional attributes if (request.ValidateAttributeConditions) { var attributes = request.Product.ProductAttributeMappings; foreach (var attribute in attributes) { var conditionMet = _productAttributeParser.IsConditionMet(request.Product, attribute, attributeXml); if (conditionMet.HasValue) { if (conditionMet.Value) { model.EnabledAttributeMappingIds.Add(attribute.Id); } else { model.DisabledAttributeMappingids.Add(attribute.Id); } } } } //picture. used when we want to override a default product picture when some attribute is selected if (request.LoadPicture) { //first, try to get product attribute combination picture var pictureId = request.Product.ProductAttributeCombinations.Where(x => x.AttributesXml == attributeXml).FirstOrDefault()?.PictureId ?? ""; //then, let's see whether we have attribute values with pictures if (string.IsNullOrEmpty(pictureId)) { pictureId = _productAttributeParser.ParseProductAttributeValues(request.Product, attributeXml) .FirstOrDefault(attributeValue => !string.IsNullOrEmpty(attributeValue.PictureId))?.PictureId ?? ""; } if (!string.IsNullOrEmpty(pictureId)) { var pictureModel = new PictureModel { Id = pictureId, FullSizeImageUrl = await _pictureService.GetPictureUrl(pictureId), ImageUrl = await _pictureService.GetPictureUrl(pictureId, _mediaSettings.ProductDetailsPictureSize) }; model.PictureFullSizeUrl = pictureModel.FullSizeImageUrl; model.PictureDefaultSizeUrl = pictureModel.ImageUrl; } } return(model); }
public virtual async Task <IActionResult> SubscribePopup(string productId, IFormCollection form) { var product = await _productService.GetProductById(productId); if (product == null) { throw new ArgumentException("No product found with the specified id"); } var customer = _workContext.CurrentCustomer; if (!customer.IsRegistered()) { return(Json(new { subscribe = false, buttontext = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"), resource = _localizationService.GetResource("BackInStockSubscriptions.OnlyRegistered") })); } if ((product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) && product.BackorderMode == BackorderMode.NoBackorders && product.AllowBackInStockSubscriptions && product.GetTotalStockQuantity(warehouseId: _storeContext.CurrentStore.DefaultWarehouseId) <= 0) { var subscription = await _backInStockSubscriptionService .FindSubscription(customer.Id, product.Id, string.Empty, _storeContext.CurrentStore.Id, product.UseMultipleWarehouses?_storeContext.CurrentStore.DefaultWarehouseId : ""); if (subscription != null) { //subscription already exists //unsubscribe await _backInStockSubscriptionService.DeleteSubscription(subscription); return(Json(new { subscribe = false, buttontext = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"), resource = _localizationService.GetResource("BackInStockSubscriptions.Unsubscribed") })); } //subscription does not exist //subscribe if ((await _backInStockSubscriptionService .GetAllSubscriptionsByCustomerId(customer.Id, _storeContext.CurrentStore.Id, 0, 1)) .TotalCount >= _catalogSettings.MaximumBackInStockSubscriptions) { return(Json(new { subscribe = false, buttontext = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"), resource = string.Format(_localizationService.GetResource("BackInStockSubscriptions.MaxSubscriptions"), _catalogSettings.MaximumBackInStockSubscriptions) })); } subscription = new BackInStockSubscription { CustomerId = customer.Id, ProductId = product.Id, StoreId = _storeContext.CurrentStore.Id, WarehouseId = product.UseMultipleWarehouses ? _storeContext.CurrentStore.DefaultWarehouseId : "", CreatedOnUtc = DateTime.UtcNow }; await _backInStockSubscriptionService.InsertSubscription(subscription); return(Json(new { subscribe = true, buttontext = _localizationService.GetResource("BackInStockSubscriptions.DeleteNotifyWhenAvailable"), resource = _localizationService.GetResource("BackInStockSubscriptions.Subscribed") })); } if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes && product.BackorderMode == BackorderMode.NoBackorders && product.AllowBackInStockSubscriptions) { string attributeXml = await _shoppingCartViewModelService.ParseProductAttributes(product, form); var combination = _productAttributeParser.FindProductAttributeCombination(product, attributeXml); var subscription = await _backInStockSubscriptionService .FindSubscription(customer.Id, product.Id, attributeXml, _storeContext.CurrentStore.Id, product.UseMultipleWarehouses?_storeContext.CurrentStore.DefaultWarehouseId : ""); if (subscription != null) { //subscription already exists //unsubscribe await _backInStockSubscriptionService.DeleteSubscription(subscription); return(Json(new { subscribe = false, buttontext = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"), resource = _localizationService.GetResource("BackInStockSubscriptions.Unsubscribed") })); } //subscription does not exist //subscribe if ((await _backInStockSubscriptionService .GetAllSubscriptionsByCustomerId(customer.Id, _storeContext.CurrentStore.Id, 0, 1)) .TotalCount >= _catalogSettings.MaximumBackInStockSubscriptions) { return(Json(new { subscribe = false, buttontext = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"), resource = string.Format(_localizationService.GetResource("BackInStockSubscriptions.MaxSubscriptions"), _catalogSettings.MaximumBackInStockSubscriptions) })); } subscription = new BackInStockSubscription { CustomerId = customer.Id, ProductId = product.Id, AttributeXml = attributeXml, StoreId = _storeContext.CurrentStore.Id, WarehouseId = product.UseMultipleWarehouses ? _storeContext.CurrentStore.DefaultWarehouseId : "", CreatedOnUtc = DateTime.UtcNow }; await _backInStockSubscriptionService.InsertSubscription(subscription); return(Json(new { subscribe = true, buttontext = _localizationService.GetResource("BackInStockSubscriptions.DeleteNotifyWhenAvailable"), resource = _localizationService.GetResource("BackInStockSubscriptions.Subscribed") })); } return(Json(new { subscribe = false, buttontext = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"), resource = _localizationService.GetResource("BackInStockSubscriptions.NotAllowed") })); }
/// <summary> /// Handle shopping cart changed event /// </summary> /// <param name="cartItem">Shopping cart item</param> public void HandleShoppingCartChangedEvent(ShoppingCartItem cartItem) { //whether marketing automation is enabled if (!_sendInBlueSettings.UseMarketingAutomation) { return; } try { //create API client var client = CreateMarketingAutomationClient(); //first, try to identify current customer client.Identify(new Identify(cartItem.Customer.Email)); //get shopping cart GUID var shoppingCartGuid = _genericAttributeService.GetAttribute <Guid?>(cartItem.Customer, SendinBlueDefaults.ShoppingCartGuidAttribute); //create track event object var trackEvent = new TrackEvent(cartItem.Customer.Email, string.Empty); //get current customer's shopping cart var cart = cartItem.Customer.ShoppingCartItems .Where(item => item.ShoppingCartType == ShoppingCartType.ShoppingCart) .LimitPerStore(_storeContext.CurrentStore.Id).ToList(); if (cart.Any()) { //get URL helper var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); //get shopping cart amounts _orderTotalCalculationService.GetShoppingCartSubTotal(cart, _workContext.TaxDisplayType == TaxDisplayType.IncludingTax, out var cartDiscount, out _, out var cartSubtotal, out _); var cartTax = _orderTotalCalculationService.GetTaxTotal(cart, false); var cartShipping = _orderTotalCalculationService.GetShoppingCartShippingTotal(cart); var cartTotal = _orderTotalCalculationService.GetShoppingCartTotal(cart, false, false); //get products data by shopping cart items var itemsData = cart.Where(item => item.Product != null).Select(item => { //try to get product attribute combination var combination = _productAttributeParser.FindProductAttributeCombination(item.Product, item.AttributesXml); //get default product picture var picture = _pictureService.GetProductPicture(item.Product, item.AttributesXml); //get product SEO slug name var seName = _urlRecordService.GetSeName(item.Product); //create product data return(new { id = item.Product.Id, name = item.Product.Name, variant_id = combination?.Id ?? item.Product.Id, variant_name = combination?.Sku ?? item.Product.Name, sku = combination?.Sku ?? item.Product.Sku, category = item.Product.ProductCategories.Aggregate(",", (all, category) => { var res = category.Category.Name; res = all == "," ? res : all + ", " + res; return res; }), url = urlHelper.RouteUrl("Product", new { SeName = seName }, _webHelper.CurrentRequestProtocol), image = _pictureService.GetPictureUrl(picture), quantity = item.Quantity, price = _priceCalculationService.GetSubTotal(item) }); }).ToArray(); //prepare cart data var cartData = new { affiliation = _storeContext.CurrentStore.Name, subtotal = cartSubtotal, shipping = cartShipping ?? decimal.Zero, total_before_tax = cartSubtotal + cartShipping ?? decimal.Zero, tax = cartTax, discount = cartDiscount, revenue = cartTotal ?? decimal.Zero, url = urlHelper.RouteUrl("ShoppingCart", null, _webHelper.CurrentRequestProtocol), currency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId)?.CurrencyCode, //gift_wrapping = string.Empty, //currently we can't get this value items = itemsData }; //if there is a single item in the cart, so the cart is just created if (cart.Count == 1) { shoppingCartGuid = Guid.NewGuid(); } else { //otherwise cart is updated shoppingCartGuid = shoppingCartGuid ?? Guid.NewGuid(); } trackEvent.EventName = SendinBlueDefaults.CartUpdatedEventName; trackEvent.EventData = new { id = $"cart:{shoppingCartGuid}", data = cartData }; } else { //there are no items in the cart, so the cart is deleted shoppingCartGuid = shoppingCartGuid ?? Guid.NewGuid(); trackEvent.EventName = SendinBlueDefaults.CartDeletedEventName; trackEvent.EventData = new { id = $"cart:{shoppingCartGuid}" }; } //track event client.TrackEvent(trackEvent); //update GUID for the current customer's shopping cart _genericAttributeService.SaveAttribute(cartItem.Customer, SendinBlueDefaults.ShoppingCartGuidAttribute, shoppingCartGuid); } catch (Exception exception) { //log full error _logger.Error($"SendinBlue Marketing Automation error: {exception.Message}.", exception, cartItem.Customer); } }
/// <summary> /// Formats the stock availability/quantity message /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Selected product attributes in XML format (if specified)</param> /// <param name="localizationService">Localization service</param> /// <param name="productAttributeParser">Product attribute parser</param> /// <returns>The stock message</returns> public static string FormatStockMessage(this Product product, string attributesXml, ILocalizationService localizationService, IProductAttributeParser productAttributeParser) { if (product == null) throw new ArgumentNullException("product"); if (localizationService == null) throw new ArgumentNullException("localizationService"); if (productAttributeParser == null) throw new ArgumentNullException("productAttributeParser"); string stockMessage = string.Empty; switch (product.ManageInventoryMethod) { case ManageInventoryMethod.ManageStock: { #region Manage stock if (!product.DisplayStockAvailability) return stockMessage; var stockQuantity = product.GetTotalStockQuantity(); if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity localizationService.GetResource("Products.Availability.InStock"); } else { //out of stock switch (product.BackorderMode) { case BackorderMode.NoBackorders: stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); break; case BackorderMode.AllowQtyBelow0: stockMessage = localizationService.GetResource("Products.Availability.InStock"); break; case BackorderMode.AllowQtyBelow0AndNotifyCustomer: stockMessage = localizationService.GetResource("Products.Availability.Backordering"); break; default: break; } } #endregion } break; case ManageInventoryMethod.ManageStockByAttributes: { #region Manage stock by attributes if (!product.DisplayStockAvailability) return stockMessage; var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { //combination exists var stockQuantity = combination.StockQuantity; if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity localizationService.GetResource("Products.Availability.InStock"); } else if (combination.AllowOutOfStockOrders) { stockMessage = localizationService.GetResource("Products.Availability.InStock"); } else { stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); } } else { //no combination configured stockMessage = localizationService.GetResource("Products.Availability.InStock"); } #endregion } break; case ManageInventoryMethod.DontManageStock: default: return stockMessage; } return stockMessage; }
/// <summary> /// Adjust inventory /// </summary> /// <param name="product">Product</param> /// <param name="quantityToChange">Quantity to increase or descrease</param> /// <param name="attributesXml">Attributes in XML format</param> public virtual async Task AdjustInventory(Product product, int quantityToChange, string attributesXml = "", string warehouseId = "") { if (product == null) { throw new ArgumentNullException("product"); } if (quantityToChange == 0) { return; } if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) { var prevStockQuantity = product.GetTotalStockQuantity(warehouseId: warehouseId); //update stock quantity if (product.UseMultipleWarehouses) { //use multiple warehouses if (quantityToChange < 0) { await ReserveInventory(product, quantityToChange, warehouseId); } else { await UnblockReservedInventory(product, quantityToChange, warehouseId); } product.StockQuantity = product.ProductWarehouseInventory.Sum(x => x.StockQuantity); await UpdateStockProduct(product); } else { //do not use multiple warehouses //simple inventory management product.StockQuantity += quantityToChange; await UpdateStockProduct(product); } //check if minimum quantity is reached if (quantityToChange < 0 && product.MinStockQuantity >= product.GetTotalStockQuantity(warehouseId: "")) { switch (product.LowStockActivity) { case LowStockActivity.DisableBuyButton: product.DisableBuyButton = true; product.DisableWishlistButton = true; var filter = Builders <Product> .Filter.Eq("Id", product.Id); var update = Builders <Product> .Update .Set(x => x.DisableBuyButton, product.DisableBuyButton) .Set(x => x.DisableWishlistButton, product.DisableWishlistButton) .Set(x => x.LowStock, true) .CurrentDate("UpdatedOnUtc"); await _productRepository.Collection.UpdateOneAsync(filter, update); //cache await _cacheManager.RemoveAsync(string.Format(PRODUCTS_BY_ID_KEY, product.Id)); //event notification await _mediator.EntityUpdated(product); break; case LowStockActivity.Unpublish: product.Published = false; var filter2 = Builders <Product> .Filter.Eq("Id", product.Id); var update2 = Builders <Product> .Update .Set(x => x.Published, product.Published) .CurrentDate("UpdatedOnUtc"); await _productRepository.Collection.UpdateOneAsync(filter2, update2); //cache await _cacheManager.RemoveAsync(string.Format(PRODUCTS_BY_ID_KEY, product.Id)); if (product.ShowOnHomePage) { await _cacheManager.RemoveByPrefix(PRODUCTS_SHOWONHOMEPAGE); } //event notification await _mediator.EntityUpdated(product); break; default: break; } } //qty is increased. product is back in stock (minimum stock quantity is reached again)? if (_catalogSettings.PublishBackProductWhenCancellingOrders) { if (quantityToChange > 0 && prevStockQuantity <= product.MinStockQuantity && product.MinStockQuantity < product.GetTotalStockQuantity(warehouseId: "")) { switch (product.LowStockActivity) { case LowStockActivity.DisableBuyButton: var filter = Builders <Product> .Filter.Eq("Id", product.Id); var update = Builders <Product> .Update .Set(x => x.DisableBuyButton, product.DisableBuyButton) .Set(x => x.DisableWishlistButton, product.DisableWishlistButton) .Set(x => x.LowStock, true) .CurrentDate("UpdatedOnUtc"); await _productRepository.Collection.UpdateOneAsync(filter, update); //cache await _cacheManager.RemoveAsync(string.Format(PRODUCTS_BY_ID_KEY, product.Id)); break; case LowStockActivity.Unpublish: product.Published = false; var filter2 = Builders <Product> .Filter.Eq("Id", product.Id); var update2 = Builders <Product> .Update .Set(x => x.Published, product.Published) .CurrentDate("UpdatedOnUtc"); await _productRepository.Collection.UpdateOneAsync(filter2, update2); //cache await _cacheManager.RemoveAsync(string.Format(PRODUCTS_BY_ID_KEY, product.Id)); if (product.ShowOnHomePage) { await _cacheManager.RemoveByPrefix(PRODUCTS_SHOWONHOMEPAGE); } break; default: break; } } } //send email notification if (quantityToChange < 0 && product.GetTotalStockQuantity(warehouseId: warehouseId) < product.NotifyAdminForQuantityBelow) { await _mediator.Send(new SendQuantityBelowStoreOwnerNotificationCommand() { Product = product }); } } if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { combination.ProductId = product.Id; if (!product.UseMultipleWarehouses) { combination.StockQuantity += quantityToChange; await _productAttributeService.UpdateProductAttributeCombination(combination); } else { if (quantityToChange < 0) { await ReserveInventoryCombination(product, combination, quantityToChange, warehouseId); } else { await UnblockReservedInventoryCombination(product, combination, quantityToChange, warehouseId); } } product.StockQuantity += quantityToChange; await UpdateStockProduct(product); //send email notification if (quantityToChange < 0 && combination.StockQuantity < combination.NotifyAdminForQuantityBelow) { await _mediator.Send(new SendQuantityBelowStoreOwnerNotificationCommand() { Product = product, ProductAttributeCombination = combination }); } } } if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByBundleProducts) { foreach (var item in product.BundleProducts) { var p1 = await _productRepository.GetByIdAsync(item.ProductId); if (p1 != null && (p1.ManageInventoryMethod == ManageInventoryMethod.ManageStock || p1.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes)) { await AdjustInventory(p1, quantityToChange *item.Quantity, attributesXml, warehouseId); } } } //bundled products var attributeValues = _productAttributeParser.ParseProductAttributeValues(product, attributesXml); foreach (var attributeValue in attributeValues) { if (attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct) { //associated product (bundle) var associatedProduct = await _productRepository.GetByIdAsync(attributeValue.AssociatedProductId); if (associatedProduct != null) { await AdjustInventory(associatedProduct, quantityToChange *attributeValue.Quantity, warehouseId); } } } //event notification await _mediator.EntityUpdated(product); }
/// <summary> /// Get product picture (for shopping cart and order details pages) /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Atributes (in XML format)</param> /// <param name="pictureService">Picture service</param> /// <param name="productAttributeParser">Product attribute service</param> /// <returns>Picture</returns> public static async Task <Picture> GetProductPicture(this Product product, IList <CustomAttribute> attributes, IProductService productService, IPictureService pictureService, IProductAttributeParser productAttributeParser) { if (product == null) { throw new ArgumentNullException("product"); } if (pictureService == null) { throw new ArgumentNullException("pictureService"); } if (productAttributeParser == null) { throw new ArgumentNullException("productAttributeParser"); } Picture picture = null; //first, let's see whether we have some attribute values with custom pictures if (attributes != null && attributes.Any()) { var comb = productAttributeParser.FindProductAttributeCombination(product, attributes); if (comb != null) { if (!string.IsNullOrEmpty(comb.PictureId)) { var combPicture = await pictureService.GetPictureById(comb.PictureId); if (combPicture != null) { picture = combPicture; } } } if (picture == null) { var attributeValues = productAttributeParser.ParseProductAttributeValues(product, attributes); foreach (var attributeValue in attributeValues) { var attributePicture = await pictureService.GetPictureById(attributeValue.PictureId); if (attributePicture != null) { picture = attributePicture; break; } } } } //now let's load the default product picture if (picture == null) { var pp = product.ProductPictures.OrderBy(x => x.DisplayOrder).FirstOrDefault(); if (pp != null) { picture = await pictureService.GetPictureById(pp.PictureId); } } //let's check whether this product has some parent "grouped" product if (picture == null && !product.VisibleIndividually && !string.IsNullOrEmpty(product.ParentGroupedProductId)) { var parentProduct = await productService.GetProductById(product.ParentGroupedProductId); if (parentProduct != null) { if (parentProduct.ProductPictures.Any()) { picture = await pictureService.GetPictureById(parentProduct.ProductPictures.FirstOrDefault().PictureId); } } } return(picture); }
/// <summary> /// Gets the shopping cart unit price /// </summary> /// <param name="product">Product</param> /// <param name="customer">Customer</param> /// <param name="currency">The currency</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="quantity">Quantity</param> /// <param name="attributes">Product atrributes</param> /// <param name="customerEnteredPrice">Customer entered price</param> /// <param name="rentalStartDate">Rental start date</param> /// <param name="rentalEndDate">Rental end date</param> /// <param name="includeDiscounts">Include discounts or not for price</param> /// <returns>Shopping cart unit price</returns> public virtual async Task <(double unitprice, double discountAmount, List <ApplyDiscount> appliedDiscounts)> GetUnitPrice( Product product, Customer customer, Currency currency, ShoppingCartType shoppingCartType, int quantity, IList <CustomAttribute> attributes, double?customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts) { if (product == null) { throw new ArgumentNullException(nameof(product)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } double discountAmount = 0; var appliedDiscounts = new List <ApplyDiscount>(); double?finalPrice = null; if (customerEnteredPrice.HasValue) { finalPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(customerEnteredPrice.Value, currency); } if (!finalPrice.HasValue) { var combination = _productAttributeParser.FindProductAttributeCombination(product, attributes); if (combination != null) { if (combination.OverriddenPrice.HasValue) { finalPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(combination.OverriddenPrice.Value, currency); } if (combination.TierPrices.Any()) { var storeId = _workContext.CurrentStore.Id; var actualTierPrices = combination.TierPrices.Where(x => string.IsNullOrEmpty(x.StoreId) || x.StoreId == storeId) .Where(x => string.IsNullOrEmpty(x.CustomerGroupId) || customer.Groups.Contains(x.CustomerGroupId)).ToList(); var tierPrice = actualTierPrices.LastOrDefault(price => quantity >= price.Quantity); if (tierPrice != null) { finalPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(tierPrice.Price, currency); } } } } if (!finalPrice.HasValue) { //summarize price of all attributes double attributesTotalPrice = 0; if (attributes != null && attributes.Any()) { if (product.ProductTypeId != ProductType.BundledProduct) { var attributeValues = _productAttributeParser.ParseProductAttributeValues(product, attributes); if (attributeValues != null) { foreach (var attributeValue in attributeValues) { attributesTotalPrice += await GetProductAttributeValuePriceAdjustment(attributeValue); } } } else { foreach (var item in product.BundleProducts) { var p1 = await _productService.GetProductById(item.ProductId); if (p1 != null) { var attributeValues = _productAttributeParser.ParseProductAttributeValues(p1, attributes); if (attributeValues != null) { foreach (var attributeValue in attributeValues) { attributesTotalPrice += (item.Quantity * await GetProductAttributeValuePriceAdjustment(attributeValue)); } } } } } } if (product.EnteredPrice) { finalPrice = customerEnteredPrice; } else { int qty = 0; if (_shoppingCartSettings.GroupTierPrices) { qty = customer.ShoppingCartItems .Where(x => x.ProductId == product.Id) .Where(x => x.ShoppingCartTypeId == shoppingCartType) .Sum(x => x.Quantity); } if (qty == 0) { qty = quantity; } var getfinalPrice = await GetFinalPrice(product, customer, currency, attributesTotalPrice, includeDiscounts, qty, rentalStartDate, rentalEndDate); finalPrice = getfinalPrice.finalPrice; discountAmount = getfinalPrice.discountAmount; appliedDiscounts = getfinalPrice.appliedDiscounts; } } if (!finalPrice.HasValue) { finalPrice = 0; } //rounding if (_shoppingCartSettings.RoundPrices) { finalPrice = RoundingHelper.RoundPrice(finalPrice.Value, _workContext.WorkingCurrency); } return(finalPrice.Value, discountAmount, appliedDiscounts); }
/// <summary> /// Gets SKU, Manufacturer part number and GTIN /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Attributes in XML format</param> /// <param name="productAttributeParser">Product attribute service (used when attributes are specified)</param> /// <param name="sku">SKU</param> /// <param name="manufacturerPartNumber">Manufacturer part number</param> /// <param name="gtin">GTIN</param> private static void GetSkuMpnGtin(this Product product, string attributesXml, IProductAttributeParser productAttributeParser, out string sku, out string manufacturerPartNumber, out string gtin) { if (product == null) throw new ArgumentNullException("product"); sku = null; manufacturerPartNumber = null; gtin = null; if (!String.IsNullOrEmpty(attributesXml) && product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { //manage stock by attribute combinations if (productAttributeParser == null) throw new ArgumentNullException("productAttributeParser"); //let's find appropriate record var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { sku = combination.Sku; manufacturerPartNumber = combination.ManufacturerPartNumber; gtin = combination.Gtin; } } if (String.IsNullOrEmpty(sku)) sku = product.Sku; if (String.IsNullOrEmpty(manufacturerPartNumber)) manufacturerPartNumber = product.ManufacturerPartNumber; if (String.IsNullOrEmpty(gtin)) gtin = product.Gtin; }
/// <summary> /// Get product picture (for shopping cart and order details pages) /// </summary> /// <param name="product">Product</param> /// <param name="attributes">Atributes </param> /// <param name="pictureService">Picture service</param> /// <param name="productAttributeParser">Product attribute service</param> /// <returns>Picture</returns> public static async Task <Picture> GetProductPicture(this Product product, IList <CustomAttribute> attributes, IProductService productService, IPictureService pictureService, IProductAttributeParser productAttributeParser) { if (product == null) { throw new ArgumentNullException(nameof(product)); } if (pictureService == null) { throw new ArgumentNullException(nameof(pictureService)); } if (productAttributeParser == null) { throw new ArgumentNullException(nameof(productAttributeParser)); } Picture picture = null; if (attributes != null && attributes.Any()) { var comb = productAttributeParser.FindProductAttributeCombination(product, attributes); if (comb != null) { if (!string.IsNullOrEmpty(comb.PictureId)) { var combPicture = await pictureService.GetPictureById(comb.PictureId); if (combPicture != null) { picture = combPicture; } } } if (picture == null) { var attributeValues = productAttributeParser.ParseProductAttributeValues(product, attributes); foreach (var attributeValue in attributeValues) { var attributePicture = await pictureService.GetPictureById(attributeValue.PictureId); if (attributePicture != null) { picture = attributePicture; break; } } } } if (picture == null) { var pp = product.ProductPictures.OrderBy(x => x.DisplayOrder).FirstOrDefault(); if (pp != null) { picture = await pictureService.GetPictureById(pp.PictureId); } } if (picture == null && !product.VisibleIndividually && !string.IsNullOrEmpty(product.ParentGroupedProductId)) { var parentProduct = await productService.GetProductById(product.ParentGroupedProductId); if (parentProduct != null) { if (parentProduct.ProductPictures.Any()) { picture = await pictureService.GetPictureById(parentProduct.ProductPictures.FirstOrDefault().PictureId); } } } return(picture); }
/// <summary> /// Gets the shopping cart unit price (one item) /// </summary> /// <param name="product">Product</param> /// <param name="customer">Customer</param> /// <param name="shoppingCartType">Shopping cart type</param> /// <param name="quantity">Quantity</param> /// <param name="attributesXml">Product atrributes (XML format)</param> /// <param name="customerEnteredPrice">Customer entered price (if specified)</param> /// <param name="rentalStartDate">Rental start date (null for not rental products)</param> /// <param name="rentalEndDate">Rental end date (null for not rental products)</param> /// <param name="includeDiscounts">A value indicating whether include discounts or not for price computation</param> /// <param name="discountAmount">Applied discount amount</param> /// <param name="appliedDiscounts">Applied discounts</param> /// <returns>Shopping cart unit price (one item)</returns> public virtual decimal GetUnitPrice(Product product, Customer customer, ShoppingCartType shoppingCartType, int quantity, string attributesXml, decimal customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts, out decimal discountAmount, out List <DiscountForCaching> appliedDiscounts) { if (product == null) { throw new ArgumentNullException("product"); } if (customer == null) { throw new ArgumentNullException("customer"); } discountAmount = decimal.Zero; appliedDiscounts = new List <DiscountForCaching>(); decimal finalPrice; var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null && combination.OverriddenPrice.HasValue) { finalPrice = GetFinalPrice(product, customer, combination.OverriddenPrice.Value, decimal.Zero, includeDiscounts, quantity, product.IsRental ? rentalStartDate : null, product.IsRental ? rentalEndDate : null, out discountAmount, out appliedDiscounts); } else { //summarize price of all attributes decimal attributesTotalPrice = decimal.Zero; var attributeValues = _productAttributeParser.ParseProductAttributeValues(attributesXml); if (attributeValues != null) { foreach (var attributeValue in attributeValues) { attributesTotalPrice += GetProductAttributeValuePriceAdjustment(attributeValue); } } //get price of a product (with previously calculated price of all attributes) if (product.CustomerEntersPrice) { finalPrice = customerEnteredPrice; } else { int qty; if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems) { //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records //so let's find how many of the current products are in the cart qty = customer.ShoppingCartItems .Where(x => x.ProductId == product.Id) .Where(x => x.ShoppingCartType == shoppingCartType) .Sum(x => x.Quantity); if (qty == 0) { qty = quantity; } } else { qty = quantity; } finalPrice = GetFinalPrice(product, customer, attributesTotalPrice, includeDiscounts, qty, product.IsRental ? rentalStartDate : null, product.IsRental ? rentalEndDate : null, out discountAmount, out appliedDiscounts); } } //rounding if (_shoppingCartSettings.RoundPricesDuringCalculation) { finalPrice = RoundingHelper.RoundPrice(finalPrice); } return(finalPrice); }
public override void AdjustInventory(Product product, int quantityToChange, string attributesXml = "", string message = "") { Debug.WriteLine("*****In New Override Adjust Inventory*****"); if (product == null) { throw new ArgumentNullException(nameof(product)); } if (quantityToChange == 0) { return; } //WGR Get the MMS total stock var mms_stock_qty = GetMmsStockQty(product); if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) { //previous stock var prevStockQuantity = GetTotalStockQuantity(product); //update stock quantity if (product.UseMultipleWarehouses) { //use multiple warehouses if (quantityToChange < 0) { ReserveInventory(product, quantityToChange); } else { UnblockReservedInventory(product, quantityToChange); } } else { //do not use multiple warehouses //simple inventory management product.StockQuantity += quantityToChange; UpdateProduct(product); //quantity change history AddStockQuantityHistoryEntry(product, quantityToChange, product.StockQuantity, product.WarehouseId, message); } //qty is reduced. check if minimum stock quantity is reached if ((quantityToChange < 0) && (product.MinStockQuantity >= mms_stock_qty)) //WGR { //what should we do now? disable buy button, unpublish the product, or do nothing? check "Low stock activity" property switch (product.LowStockActivity) { case LowStockActivity.DisableBuyButton: product.DisableBuyButton = true; product.DisableWishlistButton = true; UpdateProduct(product); break; case LowStockActivity.Unpublish: product.Published = false; UpdateProduct(product); break; default: break; } } //qty is increased. product is back in stock (minimum stock quantity is reached again)? if (_catalogSettings.PublishBackProductWhenCancellingOrders) { if (quantityToChange > 0 && prevStockQuantity <= product.MinStockQuantity && product.MinStockQuantity < mms_stock_qty) { switch (product.LowStockActivity) { case LowStockActivity.DisableBuyButton: product.DisableBuyButton = false; product.DisableWishlistButton = false; UpdateProduct(product); break; case LowStockActivity.Unpublish: product.Published = true; UpdateProduct(product); break; default: break; } } } //send email notification if ((quantityToChange < 0) && (mms_stock_qty < product.NotifyAdminForQuantityBelow)) //WGR { var workflowMessageService = EngineContext.Current.Resolve <IWorkflowMessageService>(); workflowMessageService.SendQuantityBelowStoreOwnerNotification(product, _localizationSettings.DefaultAdminLanguageId); } } if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { combination.StockQuantity += quantityToChange; _productAttributeService.UpdateProductAttributeCombination(combination); //quantity change history AddStockQuantityHistoryEntry(product, quantityToChange, combination.StockQuantity, message: message, combinationId: combination.Id); //send email notification if (quantityToChange < 0 && combination.StockQuantity < combination.NotifyAdminForQuantityBelow) { var workflowMessageService = EngineContext.Current.Resolve <IWorkflowMessageService>(); workflowMessageService.SendQuantityBelowStoreOwnerNotification(combination, _localizationSettings.DefaultAdminLanguageId); } } } } // AdjustInventory
public virtual string FormatStockMessage(Product product, string warehouseId, IList <CustomAttribute> attributes) { if (product == null) { throw new ArgumentNullException(nameof(product)); } string stockMessage = string.Empty; switch (product.ManageInventoryMethodId) { case ManageInventoryMethod.ManageStock: { #region Manage stock if (!product.StockAvailability) { return(stockMessage); } var stockQuantity = GetTotalStockQuantity(product, warehouseId: warehouseId); if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? string.Format(_translationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : _translationService.GetResource("Products.Availability.InStock"); } else { switch (product.BackorderModeId) { case BackorderMode.NoBackorders: stockMessage = _translationService.GetResource("Products.Availability.OutOfStock"); break; case BackorderMode.AllowQtyBelowZero: stockMessage = _translationService.GetResource("Products.Availability.Backordering"); break; default: break; } } #endregion } break; case ManageInventoryMethod.ManageStockByAttributes: { #region Manage stock by attributes if (!product.StockAvailability) { return(stockMessage); } var combination = _productAttributeParser.FindProductAttributeCombination(product, attributes); if (combination != null) { //combination exists var stockQuantity = GetTotalStockQuantityForCombination(product, combination, warehouseId: warehouseId); if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(_translationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity _translationService.GetResource("Products.Availability.InStock"); } else { //out of stock switch (product.BackorderModeId) { case BackorderMode.NoBackorders: stockMessage = _translationService.GetResource("Products.Availability.Attributes.OutOfStock"); break; case BackorderMode.AllowQtyBelowZero: stockMessage = _translationService.GetResource("Products.Availability.Attributes.Backordering"); break; default: break; } if (!combination.AllowOutOfStockOrders) { stockMessage = _translationService.GetResource("Products.Availability.Attributes.OutOfStock"); } } } else { stockMessage = _translationService.GetResource("Products.Availability.AttributeCombinationsNotExists"); } #endregion } break; case ManageInventoryMethod.DontManageStock: case ManageInventoryMethod.ManageStockByBundleProducts: default: return(stockMessage); } return(stockMessage); }
public virtual async Task <IList <string> > GetStandardWarnings(Customer customer, Product product, ShoppingCartItem shoppingCartItem) { if (customer == null) { throw new ArgumentNullException(nameof(customer)); } if (product == null) { throw new ArgumentNullException(nameof(product)); } var warnings = new List <string>(); //published if (!product.Published) { warnings.Add(_translationService.GetResource("ShoppingCart.ProductUnpublished")); } //we can't add grouped product if (product.ProductTypeId == ProductType.GroupedProduct) { warnings.Add("You can't add grouped product"); } //ACL if (!_aclService.Authorize(product, customer)) { warnings.Add(_translationService.GetResource("ShoppingCart.ProductUnpublished")); } //Store acl if (!_aclService.Authorize(product, shoppingCartItem.StoreId)) { warnings.Add(_translationService.GetResource("ShoppingCart.ProductUnpublished")); } //disabled "add to cart" button if (shoppingCartItem.ShoppingCartTypeId == ShoppingCartType.ShoppingCart && product.DisableBuyButton) { warnings.Add(_translationService.GetResource("ShoppingCart.BuyingDisabled")); } //disabled "add to wishlist" button if (shoppingCartItem.ShoppingCartTypeId == ShoppingCartType.Wishlist && product.DisableWishlistButton) { warnings.Add(_translationService.GetResource("ShoppingCart.WishlistDisabled")); } //call for price if (shoppingCartItem.ShoppingCartTypeId == ShoppingCartType.ShoppingCart && product.CallForPrice) { warnings.Add(_translationService.GetResource("Products.CallForPrice")); } //customer entered price if (product.EnteredPrice) { var shoppingCartItemEnteredPrice = shoppingCartItem.EnteredPrice.HasValue ? shoppingCartItem.EnteredPrice.Value : 0; if (shoppingCartItemEnteredPrice < product.MinEnteredPrice || shoppingCartItemEnteredPrice > product.MaxEnteredPrice) { double minimumCustomerEnteredPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(product.MinEnteredPrice, _workContext.WorkingCurrency); double maximumCustomerEnteredPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(product.MaxEnteredPrice, _workContext.WorkingCurrency); warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.CustomerEnteredPrice.RangeError"), _priceFormatter.FormatPrice(minimumCustomerEnteredPrice, false), _priceFormatter.FormatPrice(maximumCustomerEnteredPrice, false))); } } //quantity validation var hasQtyWarnings = false; if (shoppingCartItem.Quantity < product.OrderMinimumQuantity) { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.MinimumQuantity"), product.OrderMinimumQuantity)); hasQtyWarnings = true; } if (shoppingCartItem.Quantity > product.OrderMaximumQuantity) { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.MaximumQuantity"), product.OrderMaximumQuantity)); hasQtyWarnings = true; } var allowedQuantities = product.ParseAllowedQuantities(); if (allowedQuantities.Length > 0 && !allowedQuantities.Contains(shoppingCartItem.Quantity)) { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.AllowedQuantities"), string.Join(", ", allowedQuantities))); } if (_shoppingCartSettings.AllowToSelectWarehouse && string.IsNullOrEmpty(shoppingCartItem.WarehouseId)) { warnings.Add(_translationService.GetResource("ShoppingCart.RequiredWarehouse")); } var warehouseId = !string.IsNullOrEmpty(shoppingCartItem.WarehouseId) ? shoppingCartItem.WarehouseId : _workContext.CurrentStore?.DefaultWarehouseId; if (!string.IsNullOrEmpty(warehouseId)) { var warehouse = await _warehouseService.GetWarehouseById(warehouseId); if (warehouse == null) { warnings.Add(_translationService.GetResource("ShoppingCart.WarehouseNotExists")); } } var validateOutOfStock = shoppingCartItem.ShoppingCartTypeId == ShoppingCartType.ShoppingCart || !_shoppingCartSettings.AllowOutOfStockItemsToBeAddedToWishlist; if (validateOutOfStock && !hasQtyWarnings) { switch (product.ManageInventoryMethodId) { case ManageInventoryMethod.DontManageStock: { //do nothing } break; case ManageInventoryMethod.ManageStock: { if (product.BackorderModeId == BackorderMode.NoBackorders) { var qty = shoppingCartItem.Quantity; qty += customer.ShoppingCartItems .Where(x => x.ShoppingCartTypeId == shoppingCartItem.ShoppingCartTypeId && x.WarehouseId == warehouseId && x.ProductId == shoppingCartItem.ProductId && x.StoreId == shoppingCartItem.StoreId && x.Id != shoppingCartItem.Id) .Sum(x => x.Quantity); var maximumQuantityCanBeAdded = _stockQuantityService.GetTotalStockQuantity(product, warehouseId: warehouseId); if (maximumQuantityCanBeAdded < qty) { if (maximumQuantityCanBeAdded <= 0) { warnings.Add(_translationService.GetResource("ShoppingCart.OutOfStock")); } else { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded)); } } } } break; case ManageInventoryMethod.ManageStockByBundleProducts: { foreach (var item in product.BundleProducts) { var _qty = shoppingCartItem.Quantity * item.Quantity; var p1 = await _productService.GetProductById(item.ProductId); if (p1 != null) { if (p1.BackorderModeId == BackorderMode.NoBackorders) { if (p1.ManageInventoryMethodId == ManageInventoryMethod.ManageStock) { int maximumQuantityCanBeAdded = _stockQuantityService.GetTotalStockQuantity(p1, warehouseId: warehouseId); if (maximumQuantityCanBeAdded < _qty) { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.OutOfStock.BundleProduct"), p1.Name)); } } if (p1.ManageInventoryMethodId == ManageInventoryMethod.ManageStockByAttributes) { var combination = _productAttributeParser.FindProductAttributeCombination(p1, shoppingCartItem.Attributes); if (combination != null) { //combination exists - check stock level var stockquantity = _stockQuantityService.GetTotalStockQuantityForCombination(p1, combination, warehouseId: warehouseId); if (!combination.AllowOutOfStockOrders && stockquantity < _qty) { if (stockquantity <= 0) { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.OutOfStock.BundleProduct"), p1.Name)); } else { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.QuantityExceedsStock.BundleProduct"), p1.Name, stockquantity)); } } } else { warnings.Add(_translationService.GetResource("ShoppingCart.Combination.NotExist")); } } } } } } break; case ManageInventoryMethod.ManageStockByAttributes: { var combination = _productAttributeParser.FindProductAttributeCombination(product, shoppingCartItem.Attributes); if (combination != null) { //combination exists - check stock level var stockquantity = _stockQuantityService.GetTotalStockQuantityForCombination(product, combination, warehouseId: warehouseId); if (!combination.AllowOutOfStockOrders && stockquantity < shoppingCartItem.Quantity) { int maximumQuantityCanBeAdded = stockquantity; if (maximumQuantityCanBeAdded <= 0) { warnings.Add(_translationService.GetResource("ShoppingCart.OutOfStock")); } else { warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.QuantityExceedsStock"), maximumQuantityCanBeAdded)); } } } else { warnings.Add(_translationService.GetResource("ShoppingCart.Combination.NotExist")); } } break; default: break; } } //availability dates bool availableStartDateError = false; if (product.AvailableStartDateTimeUtc.HasValue) { DateTime now = DateTime.UtcNow; DateTime availableStartDateTime = DateTime.SpecifyKind(product.AvailableStartDateTimeUtc.Value, DateTimeKind.Utc); if (availableStartDateTime.CompareTo(now) > 0) { warnings.Add(_translationService.GetResource("ShoppingCart.NotAvailable")); availableStartDateError = true; } } if (product.AvailableEndDateTimeUtc.HasValue && !availableStartDateError && shoppingCartItem.ShoppingCartTypeId == ShoppingCartType.ShoppingCart) { DateTime now = DateTime.UtcNow; DateTime availableEndDateTime = DateTime.SpecifyKind(product.AvailableEndDateTimeUtc.Value, DateTimeKind.Utc); if (availableEndDateTime.CompareTo(now) < 0) { warnings.Add(_translationService.GetResource("ShoppingCart.NotAvailable")); } } return(warnings); }
/// <summary> /// Formats the stock availability/quantity message /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Selected product attributes in XML format (if specified)</param> /// <param name="localizationService">Localization service</param> /// <param name="productAttributeParser">Product attribute parser</param> /// <returns>The stock message</returns> public static string FormatStockMessage(this Product product, string warehouseId, string attributesXml, ILocalizationService localizationService, IProductAttributeParser productAttributeParser) { if (product == null) { throw new ArgumentNullException("product"); } if (localizationService == null) { throw new ArgumentNullException("localizationService"); } if (productAttributeParser == null) { throw new ArgumentNullException("productAttributeParser"); } string stockMessage = string.Empty; switch (product.ManageInventoryMethod) { case ManageInventoryMethod.ManageStock: { #region Manage stock if (!product.DisplayStockAvailability) { return(stockMessage); } var stockQuantity = product.GetTotalStockQuantity(warehouseId: warehouseId); if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity localizationService.GetResource("Products.Availability.InStock"); } else { //out of stock switch (product.BackorderMode) { case BackorderMode.NoBackorders: stockMessage = localizationService.GetResource("Products.Availability.OutOfStock"); break; case BackorderMode.AllowQtyBelow0: stockMessage = localizationService.GetResource("Products.Availability.InStock"); break; case BackorderMode.AllowQtyBelow0AndNotifyCustomer: stockMessage = localizationService.GetResource("Products.Availability.Backordering"); break; default: break; } } #endregion } break; case ManageInventoryMethod.ManageStockByAttributes: { #region Manage stock by attributes if (!product.DisplayStockAvailability) { return(stockMessage); } var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { //combination exists var stockQuantity = product.GetTotalStockQuantityForCombination(combination, warehouseId: warehouseId); if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(localizationService.GetResource("Products.Availability.InStockWithQuantity"), stockQuantity) : //display "in stock" without stock quantity localizationService.GetResource("Products.Availability.InStock"); } else { //out of stock switch (product.BackorderMode) { case BackorderMode.NoBackorders: stockMessage = localizationService.GetResource("Products.Availability.Attributes.OutOfStock"); break; case BackorderMode.AllowQtyBelow0: stockMessage = localizationService.GetResource("Products.Availability.Attributes.InStock"); break; case BackorderMode.AllowQtyBelow0AndNotifyCustomer: stockMessage = localizationService.GetResource("Products.Availability.Attributes.Backordering"); break; default: break; } if (!combination.AllowOutOfStockOrders) { stockMessage = localizationService.GetResource("Products.Availability.Attributes.OutOfStock"); } } } else { //no combination configured stockMessage = localizationService.GetResource("Products.Availability.InStock"); if (product.AllowAddingOnlyExistingAttributeCombinations) { stockMessage = localizationService.GetResource("Products.Availability.AllowAddingOnlyExistingAttributeCombinations.Yes"); } else { stockMessage = localizationService.GetResource("Products.Availability.AllowAddingOnlyExistingAttributeCombinations.No"); } } #endregion } break; case ManageInventoryMethod.DontManageStock: case ManageInventoryMethod.ManageStockByBundleProducts: default: return(stockMessage); } return(stockMessage); }
/// <summary> /// Formats the stock availability/quantity message /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Selected product attributes in XML format (if specified)</param> /// <param name="localizationService">Localization service</param> /// <param name="productAttributeParser">Product attribute parser</param> /// <returns>The stock message</returns> public static string FormatStockMessage(this Product product , string attributesXml //LocalizationDomainService localizationService , IProductAttributeParser productAttributeParser) { if (product == null) { throw new ArgumentNullException("product"); } //if (localizationService == null) // throw new ArgumentNullException("localizationService"); if (productAttributeParser == null) { throw new ArgumentNullException("productAttributeParser"); } string stockMessage = string.Empty; switch (product.ManageInventoryMethod) { case ManageInventoryMethod.ManageStock: { #region Manage stock if (!product.DisplayStockAvailability) { return(stockMessage); } var stockQuantity = product.GetTotalStockQuantity(); if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(InfoMsg.Products_Availability_InStockWithQuantity, stockQuantity) : //display "in stock" without stock quantity InfoMsg.Products_Availability_InStock; } else { //out of stock switch (product.BackorderMode) { case BackorderMode.NoBackorders: stockMessage = InfoMsg.Products_Availability_OutOfStock; break; case BackorderMode.AllowQtyBelow0: stockMessage = InfoMsg.Products_Availability_InStock; break; case BackorderMode.AllowQtyBelow0AndNotifyCustomer: stockMessage = InfoMsg.Products_Availability_Backordering; break; default: break; } } #endregion } break; case ManageInventoryMethod.ManageStockByAttributes: { #region Manage stock by attributes if (!product.DisplayStockAvailability) { return(stockMessage); } var combination = productAttributeParser.FindProductAttributeCombination(product, attributesXml); if (combination != null) { //combination exists var stockQuantity = combination.StockQuantity; if (stockQuantity > 0) { stockMessage = product.DisplayStockQuantity ? //display "in stock" with stock quantity string.Format(InfoMsg.Products_Availability_InStockWithQuantity, stockQuantity) : //display "in stock" without stock quantity InfoMsg.Products_Availability_InStock; } else if (combination.AllowOutOfStockOrders) { stockMessage = InfoMsg.Products_Availability_InStock; } else { stockMessage = InfoMsg.Products_Availability_OutOfStock; } } else { //no combination configured stockMessage = InfoMsg.Products_Availability_InStock; } #endregion } break; case ManageInventoryMethod.DontManageStock: default: return(stockMessage); } return(stockMessage); }