/// <returns>A task that represents the asynchronous operation</returns> protected virtual async Task WriteOrderItemsToXmlAsync(XmlWriter writer, ICollection <OrderItem> orderItems) { await writer.WriteStartElementAsync("Items"); foreach (var orderItem in orderItems) { var product = await _productService.GetProductByIdAsync(orderItem.ProductId); var order = await _orderService.GetOrderByIdAsync(orderItem.OrderId); //is shippable if (!product.IsShipEnabled) { continue; } await writer.WriteStartElementAsync("Item"); var sku = product.Sku; if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { var attributesXml = orderItem.AttributesXml; if (!string.IsNullOrEmpty(attributesXml) && product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes) { var combination = await _productAttributeParser.FindProductAttributeCombinationAsync(product, attributesXml); if (combination != null && !string.IsNullOrEmpty(combination.Sku)) { sku = combination.Sku; } } } await writer.WriteElementStringAsync("SKU", string.IsNullOrEmpty(sku)?product.Id.ToString() : sku); await writer.WriteElementStringAsync("Name", product.Name); await writer.WriteElementStringAsync("Quantity", orderItem.Quantity.ToString()); await writer.WriteElementStringAsync("UnitPrice", (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax ? orderItem.UnitPriceInclTax : orderItem.UnitPriceExclTax).ToString(CultureInfo.InvariantCulture)); await writer.WriteEndElementAsync(); await writer.FlushAsync(); } await writer.WriteEndElementAsync(); await writer.FlushAsync(); }
/// <summary> /// Handle shopping cart changed event /// </summary> /// <param name="cartItem">Shopping cart item</param> /// <returns>A task that represents the asynchronous operation</returns> public async Task HandleShoppingCartChangedEventAsync(ShoppingCartItem cartItem) { //whether marketing automation is enabled if (!_sendinblueSettings.UseMarketingAutomation) { return; } var customer = await _customerService.GetCustomerByIdAsync(cartItem.CustomerId); try { //create API client var client = CreateMarketingAutomationClient(); //first, try to identify current customer await client.IdentifyAsync(new Identify(customer.Email)); //get shopping cart GUID var shoppingCartGuid = await _genericAttributeService .GetAttributeAsync <Guid?>(customer, SendinblueDefaults.ShoppingCartGuidAttribute); //create track event object var trackEvent = new TrackEvent(customer.Email, string.Empty); //get current customer's shopping cart var store = await _storeContext.GetCurrentStoreAsync(); var cart = await _shoppingCartService .GetShoppingCartAsync(customer, ShoppingCartType.ShoppingCart, store.Id); if (cart.Any()) { //get URL helper var urlHelper = _urlHelperFactory.GetUrlHelper(_actionContextAccessor.ActionContext); //get shopping cart amounts var(_, cartDiscount, _, cartSubtotal, _) = await _orderTotalCalculationService.GetShoppingCartSubTotalAsync(cart, await _workContext.GetTaxDisplayTypeAsync() == TaxDisplayType.IncludingTax); var cartTax = await _orderTotalCalculationService.GetTaxTotalAsync(cart, false); var cartShipping = await _orderTotalCalculationService.GetShoppingCartShippingTotalAsync(cart); var(cartTotal, _, _, _, _, _) = await _orderTotalCalculationService.GetShoppingCartTotalAsync(cart, false, false); //get products data by shopping cart items var itemsData = await cart.Where(item => item.ProductId != 0).SelectAwait(async item => { var product = await _productService.GetProductByIdAsync(item.ProductId); //try to get product attribute combination var combination = await _productAttributeParser.FindProductAttributeCombinationAsync(product, item.AttributesXml); //get default product picture var picture = await _pictureService.GetProductPictureAsync(product, item.AttributesXml); //get product SEO slug name var seName = await _urlRecordService.GetSeNameAsync(product); //create product data return(new { id = product.Id, name = product.Name, variant_id = combination?.Id ?? product.Id, variant_name = combination?.Sku ?? product.Name, sku = combination?.Sku ?? product.Sku, category = await(await _categoryService.GetProductCategoriesByProductIdAsync(item.ProductId)).AggregateAwaitAsync(",", async(all, pc) => { var res = (await _categoryService.GetCategoryByIdAsync(pc.CategoryId)).Name; res = all == "," ? res : all + ", " + res; return res; }), url = urlHelper.RouteUrl("Product", new { SeName = seName }, _webHelper.GetCurrentRequestProtocol()), image = (await _pictureService.GetPictureUrlAsync(picture)).Url, quantity = item.Quantity, price = (await _shoppingCartService.GetSubTotalAsync(item, true)).subTotal }); }).ToArrayAsync(); //prepare cart data var cartData = new { affiliation = store.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.GetCurrentRequestProtocol()), currency = (await _currencyService.GetCurrencyByIdAsync(_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 ??= 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 ??= Guid.NewGuid(); trackEvent.EventName = SendinblueDefaults.CartDeletedEventName; trackEvent.EventData = new { id = $"cart:{shoppingCartGuid}" }; } //track event await client.TrackEventAsync(trackEvent); //update GUID for the current customer's shopping cart await _genericAttributeService.SaveAttributeAsync(customer, SendinblueDefaults.ShoppingCartGuidAttribute, shoppingCartGuid); } catch (Exception exception) { //log full error await _logger.ErrorAsync($"Sendinblue Marketing Automation error: {exception.Message}.", exception, customer); } }
public async override Task <(decimal unitPrice, decimal discountAmount, List <Discount> appliedDiscounts)> GetUnitPriceAsync( Product product, Customer customer, ShoppingCartType shoppingCartType, int quantity, string attributesXml, decimal customerEnteredPrice, DateTime?rentalStartDate, DateTime?rentalEndDate, bool includeDiscounts) { if (product == null) { throw new ArgumentNullException(nameof(product)); } if (customer == null) { throw new ArgumentNullException(nameof(customer)); } var discountAmount = decimal.Zero; var appliedDiscounts = new List <Discount>(); decimal finalPrice; var combination = await _productAttributeParser.FindProductAttributeCombinationAsync(product, attributesXml); if (combination?.OverriddenPrice.HasValue ?? false) { (_, finalPrice, discountAmount, appliedDiscounts) = await _priceCalculationService.GetFinalPriceAsync(product, customer, combination.OverriddenPrice.Value, decimal.Zero, includeDiscounts, quantity, product.IsRental?rentalStartDate : null, product.IsRental?rentalEndDate : null); } else { // custom // summarize price of all attributes except warranties (warranties processed separately) decimal warrantyPrice = decimal.Zero; ProductAttributeMapping warrantyPam = await _attributeUtilities.GetWarrantyAttributeMappingAsync(attributesXml); //summarize price of all attributes var attributesTotalPrice = decimal.Zero; var attributeValues = await _productAttributeParser.ParseProductAttributeValuesAsync(attributesXml); // custom - considers warranties if (attributeValues != null) { foreach (var attributeValue in attributeValues) { if (warrantyPam != null && attributeValue.ProductAttributeMappingId == warrantyPam.Id) { warrantyPrice = await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync( product, attributeValue, customer, product.CustomerEntersPrice?(decimal?)customerEnteredPrice : null ); } else { attributesTotalPrice += await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync( product, attributeValue, customer, product.CustomerEntersPrice?(decimal?)customerEnteredPrice : null ); } } } //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 = (await GetShoppingCartAsync(customer, shoppingCartType: shoppingCartType, productId: product.Id)) .Sum(x => x.Quantity); if (qty == 0) { qty = quantity; } } else { qty = quantity; } (_, finalPrice, discountAmount, appliedDiscounts) = await _priceCalculationService.GetFinalPriceAsync(product, customer, attributesTotalPrice, includeDiscounts, qty, product.IsRental?rentalStartDate : null, product.IsRental?rentalEndDate : null); finalPrice += warrantyPrice; } } //rounding if (_shoppingCartSettings.RoundPricesDuringCalculation) { finalPrice = await _priceCalculationService.RoundPriceAsync(finalPrice); } return(finalPrice, discountAmount, appliedDiscounts); }