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>());
        }
Exemplo n.º 2
0
        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));
        }
Exemplo n.º 3
0
        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>();
        }
Exemplo n.º 4
0
        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);
            }
        }
Exemplo n.º 5
0
        // 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());
        }
Exemplo n.º 6
0
        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());
        }
Exemplo n.º 8
0
        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();
        }