private async Task <IEnumerable <TierPrice> > LoadTierPrices(Product product, Customer customer, PriceCalculationContext context = null) { IEnumerable <TierPrice> result = null; if (product.HasTierPrices) { if (context == null) { await _db.LoadCollectionAsync(product, x => x.TierPrices); result = product.TierPrices .FilterByStore(_storeContext.CurrentStore.Id) .FilterForCustomer(customer) .OrderBy(x => x.Quantity) .ToList() .RemoveDuplicatedQuantities(); } else { var tierPrices = await context.TierPrices.GetOrLoadAsync(product.Id); result = tierPrices.RemoveDuplicatedQuantities(); } } return(result ?? Enumerable.Empty <TierPrice>()); }
protected async Task <IEnumerable <CustomerRole> > GetOrLoadRolesAsync(Customer user, bool activeOnly = true) { await _db.LoadCollectionAsync(user, x => x.CustomerRoleMappings, false, q => q.Include(n => n.CustomerRole)); return(user.CustomerRoleMappings .Select(x => x.CustomerRole) .Where(x => !activeOnly || x.Active)); }
private async Task InternalCopyFileData(MediaFile file, MediaFile copy) { await _storageProvider.SaveAsync(copy, MediaStorageItem.FromStream(await _storageProvider.OpenReadAsync(file))); await _imageCache.DeleteAsync(copy); // Tags. await _db.LoadCollectionAsync(file, (MediaFile x) => x.Tags); var existingTagsIds = copy.Tags.Select(x => x.Id).ToList(); foreach (var tag in file.Tags) { if (!existingTagsIds.Contains(tag.Id)) { copy.Tags.Add(tag); existingTagsIds.Add(tag.Id); } } // Localized values. var languages = _languageService.GetAllLanguages(true); foreach (var language in languages) { var title = file.GetLocalized(x => x.Title, language.Id, false, false).Value; if (title.HasValue()) { await _localizedEntityService.ApplyLocalizedValueAsync(copy, x => x.Title, title, language.Id); } var alt = file.GetLocalized(x => x.Alt, language.Id, false, false).Value; if (alt.HasValue()) { await _localizedEntityService.ApplyLocalizedValueAsync(copy, x => x.Alt, alt, language.Id); } } await _db.SaveChangesAsync(); _db.DetachEntities <MediaTag>(); }
public virtual async Task DeleteExportProfileAsync(ExportProfile profile, bool force = false) { Guard.NotNull(profile, nameof(profile)); if (!force && profile.IsSystemProfile) { throw new SmartException(T("Admin.DataExchange.Export.CannotDeleteSystemProfile")); } await _db.LoadCollectionAsync(profile, x => x.Deployments); await _db.LoadReferenceAsync(profile, x => x.Task); var folder = profile.GetExportDirectory(); var deployments = profile.Deployments.Where(x => !x.IsTransientRecord()).ToList(); if (profile.Deployments.Any()) { _db.ExportDeployments.RemoveRange(deployments); } if (profile.Task != null) { _db.TaskDescriptors.Remove(profile.Task); } _db.ExportProfiles.Remove(profile); await _db.SaveChangesAsync(); if (Directory.Exists(folder)) // TODO: (mg) (core) Use IDirectory.Exists { var fs = new LocalFileSystem(folder); // TODO: (mg) (core) find out how to correctly use LocalFileSystem.ClearDirectory. // RE: If profile.GetExportDirectory() returns IDirectory, just pass it to ClearDirectory(). IDirectory whatever = null; fs.ClearDirectory(whatever, true, TimeSpan.Zero); } }
// TODO: (ms) (core) Test this for bundle & grouped products. public virtual async Task <bool> AddToCartAsync(AddToCartContext ctx) { Guard.NotNull(ctx, nameof(ctx)); // This is called when customer adds a product to cart ctx.Customer ??= _workContext.CurrentCustomer; ctx.StoreId ??= _storeContext.CurrentStore.Id; ctx.Customer.ResetCheckoutData(ctx.StoreId.Value); // Checks whether attributes have been selected if (ctx.VariantQuery != null || ctx.RawAttributes.HasValue()) { if (!ctx.RawAttributes.HasValue()) { await _db.LoadCollectionAsync(ctx.Product, x => x.ProductVariantAttributes, false); var(Selection, Warnings) = await _productAttributeMaterializer.CreateAttributeSelectionAsync( ctx.VariantQuery, ctx.Product.ProductVariantAttributes, ctx.Product.Id, ctx.BundleItemId); ctx.RawAttributes = Selection.AttributesMap.Any() ? Selection.AsJson() : string.Empty; } // Check context for bundle item errors if (ctx.Product.ProductType == ProductType.BundledProduct && ctx.RawAttributes.HasValue()) { ctx.Warnings.Add(T("ShoppingCart.Bundle.NoAttributes")); if (ctx.BundleItem != null) { return(false); } } } if (!await _cartValidator.ValidateAccessPermissionsAsync(ctx.Customer, ctx.CartType, ctx.Warnings)) { return(false); } var cartItems = await GetCartItemsAsync(ctx.Customer, ctx.CartType, ctx.StoreId.Value); // Adds required products automatically if it is enabled if (ctx.AutomaticallyAddRequiredProducts) { var requiredProductIds = ctx.Product.ParseRequiredProductIds(); if (requiredProductIds.Any()) { var cartProductIds = cartItems.Select(x => x.Item.ProductId); var missingRequiredProductIds = requiredProductIds.Except(cartProductIds); var missingRequiredProducts = await _db.Products.GetManyAsync(missingRequiredProductIds, false); foreach (var product in missingRequiredProducts) { var item = new ShoppingCartItem { CustomerEnteredPrice = ctx.CustomerEnteredPrice.Amount, RawAttributes = ctx.AttributeSelection.AsJson(), ShoppingCartType = ctx.CartType, StoreId = ctx.StoreId.Value, Quantity = ctx.Quantity, Customer = ctx.Customer, Product = product, BundleItemId = ctx.BundleItem?.Id }; await AddItemToCartAsync(new AddToCartContext { Item = item, ChildItems = ctx.ChildItems, Customer = ctx.Customer }); } } } // Checks whether required products are still missing await _cartValidator.ValidateRequiredProductsAsync(ctx.Product, cartItems, ctx.Warnings); ShoppingCartItem existingCartItem = null; if (ctx.BundleItem == null) { existingCartItem = cartItems.FindItemInCart(ctx.CartType, ctx.Product, ctx.AttributeSelection, ctx.CustomerEnteredPrice)?.Item; } // Add item to cart (if no warnings accured) if (existingCartItem != null) { // Product is already in cart, find existing item var newQuantity = ctx.Quantity + existingCartItem.Quantity; if (!await _cartValidator.ValidateAddToCartItemAsync(ctx, existingCartItem, cartItems)) { return(false); } // Update cart item existingCartItem.Quantity = newQuantity; existingCartItem.UpdatedOnUtc = DateTime.UtcNow; existingCartItem.RawAttributes = ctx.AttributeSelection.AsJson(); await _db.SaveChangesAsync(); return(true); } else { if (!_cartValidator.ValidateItemsMaximumCartQuantity(ctx.CartType, cartItems.Count, ctx.Warnings)) { return(false); } // Product is not in cart yet, create new item var cartItem = new ShoppingCartItem { CustomerEnteredPrice = ctx.CustomerEnteredPrice.Amount, RawAttributes = ctx.RawAttributes, ShoppingCartType = ctx.CartType, StoreId = ctx.StoreId.Value, Quantity = ctx.Quantity, Customer = ctx.Customer, Product = ctx.Product, ProductId = ctx.Product.Id, ParentItemId = null, BundleItemId = ctx.BundleItem?.Id, BundleItem = ctx.BundleItem }; // TODO: (core) (ms) Fix bundle attributes of child items & customer selected price if (!await _cartValidator.ValidateAddToCartItemAsync(ctx, cartItem, cartItems)) { return(false); } // Checks whether the product is the parent item of a bundle, or just a simple product. if (ctx.BundleItem == null) { // Set cart item as item for simple & bundle products, only if its not set by the caller ctx.Item ??= cartItem; } else { // Add item as child of bundle ctx.ChildItems.Add(cartItem); } } _requestCache.RemoveByPattern(CartItemsPatternKey); // If ctx.Product is a bundle product and the setting to automatically add bundle products is true, try to add all corresponding BundleItems. if (ctx.AutomaticallyAddBundleProducts && ctx.Product.ProductType == ProductType.BundledProduct && ctx.BundleItem == null && ctx.Warnings.Count == 0) { var bundleItems = await _db.ProductBundleItem .ApplyBundledProductsFilter(new[] { ctx.Product.Id }, true) .Include(x => x.Product) .ToListAsync(); foreach (var bundleItem in bundleItems) { bundleItem.BundleProduct = ctx.Item.Product; var bundleItemContext = new AddToCartContext { StoreId = ctx.StoreId, Customer = ctx.Customer, CartType = ctx.CartType, BundleItem = bundleItem, ChildItems = ctx.ChildItems, Product = bundleItem.Product, Quantity = bundleItem.Quantity, VariantQuery = ctx.VariantQuery, RawAttributes = ctx.RawAttributes, CustomerEnteredPrice = ctx.CustomerEnteredPrice, AutomaticallyAddRequiredProducts = ctx.AutomaticallyAddRequiredProducts, }; if (!await AddToCartAsync(bundleItemContext)) { ctx.Warnings.AddRange(bundleItemContext.Warnings); } } } // Add item and its children (if active) to the cart, when it is either a simple product or // if it is the parent item of its bundle (bundleItem = null) and no warnings occurred. if (ctx.BundleItem == null && ctx.Warnings.Count == 0) { await AddItemToCartAsync(ctx); } return(!ctx.Warnings.Any()); }
public async Task <OrderDetailsModel> PrepareOrderDetailsModelAsync(Order order) { Guard.NotNull(order, nameof(order)); var settingFactory = _services.SettingFactory; var store = await _db.Stores.FindByIdAsync(order.StoreId, false) ?? _services.StoreContext.CurrentStore; var language = _services.WorkContext.WorkingLanguage; var orderSettings = await settingFactory.LoadSettingsAsync <OrderSettings>(store.Id); var catalogSettings = await settingFactory.LoadSettingsAsync <CatalogSettings>(store.Id); var taxSettings = await settingFactory.LoadSettingsAsync <TaxSettings>(store.Id); var pdfSettings = await settingFactory.LoadSettingsAsync <PdfSettings>(store.Id); var addressSettings = await settingFactory.LoadSettingsAsync <AddressSettings>(store.Id); var companyInfoSettings = await settingFactory.LoadSettingsAsync <CompanyInformationSettings>(store.Id); var shoppingCartSettings = await settingFactory.LoadSettingsAsync <ShoppingCartSettings>(store.Id); var mediaSettings = await settingFactory.LoadSettingsAsync <MediaSettings>(store.Id); var model = new OrderDetailsModel { Id = order.Id, StoreId = order.StoreId, CustomerLanguageId = order.CustomerLanguageId, CustomerComment = order.CustomerOrderComment, OrderNumber = order.GetOrderNumber(), CreatedOn = _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc), OrderStatus = await _services.Localization.GetLocalizedEnumAsync(order.OrderStatus), IsReOrderAllowed = orderSettings.IsReOrderAllowed, IsReturnRequestAllowed = _orderProcessingService.IsReturnRequestAllowed(order), DisplayPdfInvoice = pdfSettings.Enabled, RenderOrderNotes = pdfSettings.RenderOrderNotes, // Shipping info. ShippingStatus = await _services.Localization.GetLocalizedEnumAsync(order.ShippingStatus) }; // TODO: refactor modelling for multi-order processing. var companyCountry = await _db.Countries.FindByIdAsync(companyInfoSettings.CountryId, false); model.MerchantCompanyInfo = companyInfoSettings; model.MerchantCompanyCountryName = companyCountry?.GetLocalized(x => x.Name); if (order.ShippingStatus != ShippingStatus.ShippingNotRequired) { model.IsShippable = true; await MapperFactory.MapAsync(order.ShippingAddress, model.ShippingAddress); model.ShippingMethod = order.ShippingMethod; // Shipments (only already shipped). var shipments = order.Shipments.Where(x => x.ShippedDateUtc.HasValue).OrderBy(x => x.CreatedOnUtc).ToList(); foreach (var shipment in shipments) { var shipmentModel = new OrderDetailsModel.ShipmentBriefModel { Id = shipment.Id, TrackingNumber = shipment.TrackingNumber, }; if (shipment.ShippedDateUtc.HasValue) { shipmentModel.ShippedDate = _dateTimeHelper.ConvertToUserTime(shipment.ShippedDateUtc.Value, DateTimeKind.Utc); } if (shipment.DeliveryDateUtc.HasValue) { shipmentModel.DeliveryDate = _dateTimeHelper.ConvertToUserTime(shipment.DeliveryDateUtc.Value, DateTimeKind.Utc); } model.Shipments.Add(shipmentModel); } } await MapperFactory.MapAsync(order.BillingAddress, model.BillingAddress); model.VatNumber = order.VatNumber; // Payment method. var paymentMethod = await _paymentService.LoadPaymentMethodBySystemNameAsync(order.PaymentMethodSystemName); model.PaymentMethodSystemName = order.PaymentMethodSystemName; // TODO: (mh) (core) //model.PaymentMethod = paymentMethod != null ? _pluginMediator.GetLocalizedFriendlyName(paymentMethod.Metadata) : order.PaymentMethodSystemName; model.PaymentMethod = order.PaymentMethodSystemName; model.CanRePostProcessPayment = await _paymentService.CanRePostProcessPaymentAsync(order); // Purchase order number (we have to find a better to inject this information because it's related to a certain plugin). if (paymentMethod != null && paymentMethod.Metadata.SystemName.Equals("Smartstore.PurchaseOrderNumber", StringComparison.InvariantCultureIgnoreCase)) { model.DisplayPurchaseOrderNumber = true; model.PurchaseOrderNumber = order.PurchaseOrderNumber; } if (order.AllowStoringCreditCardNumber) { model.CardNumber = _encryptor.DecryptText(order.CardNumber); model.MaskedCreditCardNumber = _encryptor.DecryptText(order.MaskedCreditCardNumber); model.CardCvv2 = _encryptor.DecryptText(order.CardCvv2); model.CardExpirationMonth = _encryptor.DecryptText(order.CardExpirationMonth); model.CardExpirationYear = _encryptor.DecryptText(order.CardExpirationYear); } if (order.AllowStoringDirectDebit) { model.DirectDebitAccountHolder = _encryptor.DecryptText(order.DirectDebitAccountHolder); model.DirectDebitAccountNumber = _encryptor.DecryptText(order.DirectDebitAccountNumber); model.DirectDebitBankCode = _encryptor.DecryptText(order.DirectDebitBankCode); model.DirectDebitBankName = _encryptor.DecryptText(order.DirectDebitBankName); model.DirectDebitBIC = _encryptor.DecryptText(order.DirectDebitBIC); model.DirectDebitCountry = _encryptor.DecryptText(order.DirectDebitCountry); model.DirectDebitIban = _encryptor.DecryptText(order.DirectDebitIban); } // TODO: (mh) (core) Reimplement when pricing is ready. // Totals. var customerCurrency = await _db.Currencies .AsNoTracking() .Where(x => x.CurrencyCode == order.CustomerCurrencyCode) .FirstOrDefaultAsync(); switch (order.CustomerTaxDisplayType) { case TaxDisplayType.ExcludingTax: { // Order subtotal. var orderSubtotalExclTax = _currencyService.ConvertToExchangeRate(order.OrderSubtotalExclTax, order.CurrencyRate, customerCurrency); model.OrderSubtotal = orderSubtotalExclTax.ToString(); // Discount (applied to order subtotal). var orderSubTotalDiscountExclTax = _currencyService.ConvertToExchangeRate(order.OrderSubTotalDiscountExclTax, order.CurrencyRate, customerCurrency); if (orderSubTotalDiscountExclTax > 0) { model.OrderSubTotalDiscount = (orderSubTotalDiscountExclTax * -1).ToString(); } // Order shipping. var orderShippingExclTax = _currencyService.ConvertToExchangeRate(order.OrderShippingExclTax, order.CurrencyRate, customerCurrency); model.OrderShipping = orderShippingExclTax.ToString(); // Payment method additional fee. var paymentMethodAdditionalFeeExclTax = _currencyService.ConvertToExchangeRate(order.PaymentMethodAdditionalFeeExclTax, order.CurrencyRate, customerCurrency); if (paymentMethodAdditionalFeeExclTax != 0) { model.PaymentMethodAdditionalFee = paymentMethodAdditionalFeeExclTax.ToString(); } } break; case TaxDisplayType.IncludingTax: { // Order subtotal. var orderSubtotalInclTax = _currencyService.ConvertToExchangeRate(order.OrderSubtotalInclTax, order.CurrencyRate, customerCurrency); model.OrderSubtotal = orderSubtotalInclTax.ToString(); // Discount (applied to order subtotal). var orderSubTotalDiscountInclTax = _currencyService.ConvertToExchangeRate(order.OrderSubTotalDiscountInclTax, order.CurrencyRate, customerCurrency); if (orderSubTotalDiscountInclTax > 0) { model.OrderSubTotalDiscount = (orderSubTotalDiscountInclTax * -1).ToString(); } // Order shipping. var orderShippingInclTax = _currencyService.ConvertToExchangeRate(order.OrderShippingInclTax, order.CurrencyRate, customerCurrency); model.OrderShipping = orderShippingInclTax.ToString(); // Payment method additional fee. var paymentMethodAdditionalFeeInclTax = _currencyService.ConvertToExchangeRate(order.PaymentMethodAdditionalFeeInclTax, order.CurrencyRate, customerCurrency); if (paymentMethodAdditionalFeeInclTax != 0) { model.PaymentMethodAdditionalFee = paymentMethodAdditionalFeeInclTax.ToString(); } } break; } // Tax. var displayTax = true; var displayTaxRates = true; if (taxSettings.HideTaxInOrderSummary && order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax) { displayTax = false; displayTaxRates = false; } else { if (order.OrderTax == 0 && taxSettings.HideZeroTax) { displayTax = false; displayTaxRates = false; } else { displayTaxRates = taxSettings.DisplayTaxRates && order.TaxRatesDictionary.Count > 0; displayTax = !displayTaxRates; var orderTaxInCustomerCurrency = _currencyService.ConvertToExchangeRate(order.OrderTax, order.CurrencyRate, customerCurrency); model.Tax = orderTaxInCustomerCurrency.ToString(); foreach (var tr in order.TaxRatesDictionary) { var rate = _taxService.FormatTaxRate(tr.Key); var labelKey = _services.WorkContext.TaxDisplayType == TaxDisplayType.IncludingTax ? "ShoppingCart.Totals.TaxRateLineIncl" : "ShoppingCart.Totals.TaxRateLineExcl"; model.TaxRates.Add(new OrderDetailsModel.TaxRate { Rate = rate, Label = T(labelKey, rate), Value = _currencyService.ConvertToExchangeRate(tr.Value, order.CurrencyRate, customerCurrency).ToString() }); } } } model.DisplayTaxRates = displayTaxRates; model.DisplayTax = displayTax; // Discount (applied to order total). var orderDiscountInCustomerCurrency = _currencyService.ConvertToExchangeRate(order.OrderDiscount, order.CurrencyRate, customerCurrency); if (orderDiscountInCustomerCurrency > 0) { model.OrderTotalDiscount = (orderDiscountInCustomerCurrency * -1).ToString(); } // Gift cards. foreach (var gcuh in order.GiftCardUsageHistory) { var remainingAmountBase = _giftCardService.GetRemainingAmount(gcuh.GiftCard); var remainingAmount = _currencyService.ConvertToExchangeRate(remainingAmountBase.Amount, order.CurrencyRate, customerCurrency); var usedAmount = _currencyService.ConvertToExchangeRate(gcuh.UsedValue, order.CurrencyRate, customerCurrency); var gcModel = new OrderDetailsModel.GiftCard { CouponCode = gcuh.GiftCard.GiftCardCouponCode, Amount = (usedAmount * -1).ToString(), Remaining = remainingAmount.ToString() }; model.GiftCards.Add(gcModel); } // Reward points . if (order.RedeemedRewardPointsEntry != null) { var usedAmount = _currencyService.ConvertToExchangeRate(order.RedeemedRewardPointsEntry.UsedAmount, order.CurrencyRate, customerCurrency); model.RedeemedRewardPoints = -order.RedeemedRewardPointsEntry.Points; model.RedeemedRewardPointsAmount = (usedAmount * -1).ToString(); } // Credit balance. if (order.CreditBalance > 0) { var convertedCreditBalance = _currencyService.ConvertToExchangeRate(order.CreditBalance, order.CurrencyRate, customerCurrency); model.CreditBalance = (convertedCreditBalance * -1).ToString(); } // Total. (var orderTotal, var roundingAmount) = await _orderService.GetOrderTotalInCustomerCurrencyAsync(order, customerCurrency); model.OrderTotal = orderTotal.ToString(); if (roundingAmount != 0) { model.OrderTotalRounding = roundingAmount.ToString(); } // Checkout attributes. model.CheckoutAttributeInfo = HtmlUtils.ConvertPlainTextToTable(HtmlUtils.ConvertHtmlToPlainText(order.CheckoutAttributeDescription)); // Order notes. await _db.LoadCollectionAsync(order, x => x.OrderNotes); var orderNotes = order.OrderNotes .Where(x => x.DisplayToCustomer) .OrderByDescending(x => x.CreatedOnUtc) .ToList(); foreach (var orderNote in orderNotes) { var createdOn = _dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc); model.OrderNotes.Add(new OrderDetailsModel.OrderNote { Note = orderNote.FormatOrderNoteText(), CreatedOn = createdOn, FriendlyCreatedOn = orderNote.CreatedOnUtc.Humanize() }); } // Purchased products. model.ShowSku = catalogSettings.ShowProductSku; model.ShowProductImages = shoppingCartSettings.ShowProductImagesOnShoppingCart; model.ShowProductBundleImages = shoppingCartSettings.ShowProductBundleImagesOnShoppingCart; model.BundleThumbSize = mediaSettings.CartThumbBundleItemPictureSize; await _db.LoadCollectionAsync(order, x => x.OrderItems, false, q => q.Include(x => x.Product)); foreach (var orderItem in order.OrderItems) { var orderItemModel = await PrepareOrderItemModelAsync(order, orderItem, catalogSettings, shoppingCartSettings, mediaSettings, customerCurrency); model.Items.Add(orderItemModel); } return(model); }
public virtual async Task <bool> ValidateProductAttributesAsync(ShoppingCartItem cartItem, IEnumerable <OrganizedShoppingCartItem> cartItems, IList <string> warnings) { Guard.NotNull(cartItem, nameof(cartItem)); // Check if the product is a bundle. Since bundles have no attributes, the customer has nothing to select if (cartItem.Product.ProductType == ProductType.BundledProduct || cartItem.BundleItem?.BundleProduct != null && !cartItem.BundleItem.BundleProduct.BundlePerItemPricing) { if (cartItem.RawAttributes.HasValue()) { warnings.Add(T("ShoppingCart.Bundle.NoAttributes")); } return(true); } // Get selected product variant attributes and check for product errors var selectedAttributes = await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(cartItem.AttributeSelection); foreach (var attribute in selectedAttributes) { if (attribute.Product == null || attribute.Product.Id != cartItem.Product.Id) { warnings.Add(T("ShoppingCart.AttributeError")); return(false); } } await _db.LoadCollectionAsync(cartItem.Product, x => x.ProductVariantAttributes, false, q => q.Include(x => x.ProductAttribute)); var currentWarnings = new List <string>(); // Get existing product variant attributes foreach (var existingAttribute in cartItem.Product.ProductVariantAttributes) { if (!existingAttribute.IsRequired) { continue; } var found = false; // Selected product attributes foreach (var selectedAttribute in selectedAttributes) { if (selectedAttribute.Id == existingAttribute.Id) { var values = cartItem.AttributeSelection.GetAttributeValues(selectedAttribute.Id) .Select(x => x.ToString()) .ToList(); found = values.Find(x => x.HasValue()).HasValue(); if (found) { break; } } } // If attribute is filtered out by bundle item, it cannot be selected by the customer if (!found && (cartItem.BundleItem?.FilterAttributes ?? false) && !cartItem.BundleItem.AttributeFilters.Any(x => x.AttributeId == existingAttribute.ProductAttributeId)) { found = true; } if (!found) { currentWarnings.Add(T( "ShoppingCart.SelectAttribute", existingAttribute.TextPrompt.IsEmpty() ? existingAttribute.ProductAttribute.GetLocalized(x => x.Name) : existingAttribute.GetLocalized(x => x.TextPrompt) )); } } if (currentWarnings.Any()) { warnings.AddRange(currentWarnings); return(false); } // Checks whether there is an active selected attribute combination if (cartItem.AttributeSelection.AttributesMap.Any()) { var combination = await _productAttributeMaterializer.FindAttributeCombinationAsync(cartItem.Product.Id, cartItem.AttributeSelection); if (combination != null && !combination.IsActive) { currentWarnings.Add(T("ShoppingCart.NotAvailable")); } } var attributeValues = await _productAttributeMaterializer.MaterializeProductVariantAttributeValuesAsync(cartItem.AttributeSelection); var linkedProductIds = attributeValues .Where(x => x.ValueType == ProductVariantAttributeValueType.ProductLinkage) .Select(x => x.LinkedProductId) .Distinct(); // Get products linked to attributes var linkedProducts = await _db.Products.GetManyAsync(linkedProductIds); // Filter products which could not be loaded var notFoundProductIds = linkedProductIds.Except(linkedProducts.Select(x => x.Id)); foreach (var productId in notFoundProductIds) { currentWarnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", productId)); } // Validate each linkedProduct, create shopping cart item from linkedProduct and run validation foreach (var linkedProductId in linkedProductIds) { var linkedProduct = linkedProducts.FirstOrDefault(x => x.Id == linkedProductId); var linkedAttributeValue = attributeValues.FirstOrDefault(x => x.LinkedProductId == linkedProductId); if (linkedProduct == null || linkedAttributeValue == null) { currentWarnings.Add(T("ShoppingCart.ProductLinkageProductNotLoading", linkedProductId)); continue; } var item = new ShoppingCartItem { ProductId = linkedProduct.Id, Product = linkedProduct, ShoppingCartType = cartItem.ShoppingCartType, Customer = cartItem.Customer, StoreId = cartItem.StoreId, Quantity = cartItem.Quantity * linkedAttributeValue.Quantity }; var ctx = new AddToCartContext { Product = linkedProduct, Customer = cartItem.Customer, CartType = cartItem.ShoppingCartType, StoreId = cartItem.StoreId, Quantity = cartItem.Quantity * linkedAttributeValue.Quantity }; // Get product linkage warnings await ValidateAddToCartItemAsync(ctx, item, cartItems); foreach (var linkageWarning in ctx.Warnings) { currentWarnings.Add( T("ShoppingCart.ProductLinkageAttributeWarning", linkedAttributeValue.ProductVariantAttribute.ProductAttribute.GetLocalized(x => x.Name), linkedAttributeValue.GetLocalized(x => x.Name), linkageWarning) ); } } warnings.AddRange(currentWarnings); return(!currentWarnings.Any()); }
private async Task ProcessAttributes(DbContextScope scope, Product product, Product clone, IEnumerable <Language> languages) { var localizedKeySelectors = new List <Expression <Func <ProductVariantAttributeValue, string> > > { x => x.Name, x => x.Alias }; await _db.LoadCollectionAsync(product, x => x.ProductVariantAttributes); await _db.LoadCollectionAsync(product, x => x.ProductVariantAttributeCombinations); // Former attribute id > clone. var attributeMap = new Dictionary <int, ProductVariantAttribute>(); // Former attribute value id > clone. var valueMap = new Dictionary <int, ProductVariantAttributeValue>(); var newCombinations = new List <ProductVariantAttributeCombination>(); // Product attributes. foreach (var attribute in product.ProductVariantAttributes) { // Save associated value (used for combinations copying). attributeMap[attribute.Id] = new ProductVariantAttribute { ProductId = clone.Id, ProductAttributeId = attribute.ProductAttributeId, TextPrompt = attribute.TextPrompt, IsRequired = attribute.IsRequired, AttributeControlTypeId = attribute.AttributeControlTypeId, DisplayOrder = attribute.DisplayOrder }; } // Reverse tracking order to have the clones in the same order in the database as the originals. _db.ProductVariantAttributes.AddRange(attributeMap.Select(x => x.Value).Reverse()); // >>>>>> Commit attributes. await scope.CommitAsync(); // Product variant attribute values. foreach (var attribute in product.ProductVariantAttributes) { var attributeClone = attributeMap[attribute.Id]; foreach (var value in attribute.ProductVariantAttributeValues) { // Save associated value (used for combinations copying). valueMap.Add(value.Id, new ProductVariantAttributeValue { ProductVariantAttributeId = attributeClone.Id, Name = value.Name, Color = value.Color, PriceAdjustment = value.PriceAdjustment, WeightAdjustment = value.WeightAdjustment, IsPreSelected = value.IsPreSelected, DisplayOrder = value.DisplayOrder, ValueTypeId = value.ValueTypeId, LinkedProductId = value.LinkedProductId, Quantity = value.Quantity, MediaFileId = value.MediaFileId }); } } // Reverse tracking order to have the clones in the same order in the database as the originals. _db.ProductVariantAttributeValues.AddRange(valueMap.Select(x => x.Value).Reverse()); // >>>>>> Commit attribute values. await scope.CommitAsync(); // Attribute value localization. var allValues = product.ProductVariantAttributes .Reverse() .SelectMany(x => x.ProductVariantAttributeValues.Reverse()) .ToArray(); foreach (var value in allValues) { if (valueMap.TryGetValue(value.Id, out var newValue)) { await ProcessLocalizations(value, newValue, localizedKeySelectors, languages); } } // >>>>>> Commit localized values. await scope.CommitAsync(); // Attribute combinations. foreach (var combination in product.ProductVariantAttributeCombinations) { var oldAttributesMap = combination.AttributeSelection.AttributesMap; var oldAttributes = await _productAttributeMaterializer.MaterializeProductVariantAttributesAsync(combination.AttributeSelection); var newSelection = new ProductVariantAttributeSelection(null); foreach (var oldAttribute in oldAttributes) { if (attributeMap.TryGetValue(oldAttribute.Id, out var newAttribute)) { var item = oldAttributesMap.FirstOrDefault(x => x.Key == oldAttribute.Id); if (item.Key != 0) { foreach (var value in item.Value) { if (newAttribute.IsListTypeAttribute()) { var oldValueId = value.ToString().EmptyNull().ToInt(); if (valueMap.TryGetValue(oldValueId, out var newValue)) { newSelection.AddAttributeValue(newAttribute.Id, newValue.Id); } } else { newSelection.AddAttributeValue(newAttribute.Id, value); } } } } } newCombinations.Add(new ProductVariantAttributeCombination { ProductId = clone.Id, RawAttributes = newSelection.AsJson(), StockQuantity = combination.StockQuantity, AllowOutOfStockOrders = combination.AllowOutOfStockOrders, Sku = combination.Sku, Gtin = combination.Gtin, ManufacturerPartNumber = combination.ManufacturerPartNumber, Price = combination.Price, AssignedMediaFileIds = combination.AssignedMediaFileIds, Length = combination.Length, Width = combination.Width, Height = combination.Height, BasePriceAmount = combination.BasePriceAmount, BasePriceBaseAmount = combination.BasePriceBaseAmount, DeliveryTimeId = combination.DeliveryTimeId, QuantityUnitId = combination.QuantityUnitId, IsActive = combination.IsActive //IsDefaultCombination = combination.IsDefaultCombination }); } // Reverse tracking order to have the clones in the same order in the database as the originals. _db.ProductVariantAttributeCombinations.AddRange(newCombinations.AsEnumerable().Reverse()); // >>>>>> Commit combinations. await scope.CommitAsync(); }