Exemple #1
0
        private async Task ProcessBundleItems(DbContextScope scope, Product product, Product clone, IEnumerable <Language> languages)
        {
            var localizedKeySelectors = new List <Expression <Func <ProductBundleItem, string> > >
            {
                x => x.Name,
                x => x.ShortDescription
            };

            var bundledItems = await _db.ProductBundleItem
                               .AsNoTracking()
                               .Include(x => x.AttributeFilters)
                               .ApplyBundledProductsFilter(new[] { product.Id }, true)
                               .ToListAsync();

            if (!bundledItems.Any())
            {
                return;
            }

            var itemMap = new Dictionary <int, ProductBundleItem>();

            foreach (var bundledItem in bundledItems)
            {
                var newBundleItem = bundledItem.Clone();
                newBundleItem.BundleProductId = clone.Id;
                itemMap[bundledItem.Id]       = newBundleItem;
            }

            _db.ProductBundleItem.AddRange(itemMap.Select(x => x.Value).Reverse());
            await scope.CommitAsync();

            foreach (var bundledItem in bundledItems)
            {
                if (!itemMap.TryGetValue(bundledItem.Id, out var newBundleItem))
                {
                    continue;
                }

                foreach (var itemFilter in bundledItem.AttributeFilters)
                {
                    var newItemFilter = itemFilter.Clone();
                    newItemFilter.BundleItemId = newBundleItem.Id;

                    _db.ProductBundleItemAttributeFilter.Add(newItemFilter);
                }

                await ProcessLocalizations(bundledItem, newBundleItem, localizedKeySelectors, languages);
            }

            await scope.CommitAsync();
        }
        private async Task MoveMedia()
        {
            if (_config.StoreMediaInDB)
            {
                return;
            }

            // All pictures have initially been stored in the DB. Move the binaries to disk as configured.
            var fileSystemStorageProvider = EngineContext.Current.ResolveService <Func <IMediaStorageProvider> >().Invoke();

            using (var scope = new DbContextScope(_db, autoDetectChanges: true))
            {
                var mediaFiles = await _db.MediaFiles
                                 .Include(x => x.MediaStorage)
                                 .Where(x => x.MediaStorageId != null)
                                 .ToListAsync();

                foreach (var mediaFile in mediaFiles)
                {
                    if (mediaFile.MediaStorage?.Data?.LongLength > 0)
                    {
                        await fileSystemStorageProvider.SaveAsync(mediaFile, MediaStorageItem.FromStream(mediaFile.MediaStorage.Data.ToStream()));

                        mediaFile.MediaStorageId = null;
                        mediaFile.MediaStorage   = null;
                    }
                }

                await scope.CommitAsync();
            }
        }
Exemple #3
0
        public async Task <MediaFileInfo> ReplaceFileAsync(MediaFile file, Stream inStream, string newFileName)
        {
            Guard.NotNull(file, nameof(file));
            Guard.NotNull(inStream, nameof(inStream));
            Guard.NotEmpty(newFileName, nameof(newFileName));

            var fileInfo = ConvertMediaFile(file);
            var pathData = CreatePathData(fileInfo.Path);

            pathData.FileName = newFileName;

            var storageItem = ProcessFile(ref file, pathData, inStream, false, DuplicateFileHandling.Overwrite, MimeValidationType.MediaTypeMustMatch);

            using (var scope = new DbContextScope(_fileRepo.Context, autoCommit: false))
            {
                try
                {
                    await _storageProvider.SaveAsync(file, storageItem);

                    await scope.CommitAsync();
                }
                catch (Exception ex)
                {
                    Logger.Error(ex);
                }
            }

            return(fileInfo);
        }
Exemple #4
0
        protected virtual async Task CheckOrderStatusAsync(Order order)
        {
            Guard.NotNull(order, nameof(order));

            using var scope = new DbContextScope(_db, deferCommit: true);

            if (order.PaymentStatus == PaymentStatus.Paid && !order.PaidDateUtc.HasValue)
            {
                order.PaidDateUtc = DateTime.UtcNow;
            }

            if (order.OrderStatus == OrderStatus.Pending &&
                (order.PaymentStatus == PaymentStatus.Authorized || order.PaymentStatus == PaymentStatus.Paid))
            {
                await SetOrderStatusAsync(order, OrderStatus.Processing, false);
            }

            if (order.OrderStatus == OrderStatus.Pending &&
                (order.ShippingStatus == ShippingStatus.PartiallyShipped || order.ShippingStatus == ShippingStatus.Shipped || order.ShippingStatus == ShippingStatus.Delivered))
            {
                await SetOrderStatusAsync(order, OrderStatus.Processing, false);
            }

            if (order.OrderStatus != OrderStatus.Cancelled &&
                order.OrderStatus != OrderStatus.Complete &&
                order.PaymentStatus == PaymentStatus.Paid &&
                (order.ShippingStatus == ShippingStatus.ShippingNotRequired || order.ShippingStatus == ShippingStatus.Delivered))
            {
                await SetOrderStatusAsync(order, OrderStatus.Complete, true);
            }

            await scope.CommitAsync();
        }
        protected virtual async Task <int> ProcessCustomerRolesAsync(ImportExecuteContext context, DbContextScope scope, IEnumerable <ImportRow <Customer> > batch)
        {
            var cargo = await GetCargoData(context);

            if (!cargo.AllowManagingCustomerRoles)
            {
                return(0);
            }

            foreach (var row in batch)
            {
                var customer = row.Entity;
                var importRoleSystemNames = row.GetDataValue <List <string> >("CustomerRoleSystemNames");

                var assignedRoles = customer.CustomerRoleMappings
                                    .Where(x => !x.IsSystemMapping)
                                    .Select(x => x.CustomerRole)
                                    .ToDictionarySafe(x => x.SystemName, StringComparer.OrdinalIgnoreCase);

                // Roles to remove.
                foreach (var customerRole in assignedRoles)
                {
                    var systemName = customerRole.Key;
                    if (!systemName.EqualsNoCase(SystemCustomerRoleNames.Administrators) &&
                        !systemName.EqualsNoCase(SystemCustomerRoleNames.SuperAdministrators) &&
                        !importRoleSystemNames.Contains(systemName))
                    {
                        var mappings = customer.CustomerRoleMappings.Where(x => !x.IsSystemMapping && x.CustomerRoleId == customerRole.Value.Id);
                        _db.CustomerRoleMappings.RemoveRange(mappings);
                    }
                }

                // Roles to add.
                foreach (var systemName in importRoleSystemNames)
                {
                    if (systemName.EqualsNoCase(SystemCustomerRoleNames.Administrators) ||
                        systemName.EqualsNoCase(SystemCustomerRoleNames.SuperAdministrators))
                    {
                        context.Result.AddInfo("Security. Ignored administrator role.", row.RowInfo, "CustomerRoleSystemNames");
                    }
                    else if (!assignedRoles.ContainsKey(systemName))
                    {
                        // Add role mapping but never insert roles.
                        // Be careful not to insert mappings several times!
                        if (cargo.CustomerRoleIds.TryGetValue(systemName, out var roleId))
                        {
                            _db.CustomerRoleMappings.Add(new CustomerRoleMapping
                            {
                                CustomerId     = customer.Id,
                                CustomerRoleId = roleId
                            });
                        }
                    }
                }
            }

            var num = await scope.CommitAsync(context.CancelToken);

            return(num);
        }
Exemple #6
0
        public async Task <MediaFileInfo> SaveFileAsync(string path, Stream stream, bool isTransient = true, DuplicateFileHandling dupeFileHandling = DuplicateFileHandling.ThrowError)
        {
            var pathData = CreatePathData(path);

            var file = await _fileRepo.Table.FirstOrDefaultAsync(x => x.Name == pathData.FileName && x.FolderId == pathData.Folder.Id);

            var isNewFile   = file == null;
            var storageItem = ProcessFile(ref file, pathData, stream, isTransient, dupeFileHandling);

            using (var scope = new DbContextScope(_fileRepo.Context, autoCommit: false))
            {
                if (file.Id == 0)
                {
                    _fileRepo.Insert(file);
                    await scope.CommitAsync();
                }

                try
                {
                    await _storageProvider.SaveAsync(file, storageItem);

                    await scope.CommitAsync();
                }
                catch (Exception ex)
                {
                    if (isNewFile)
                    {
                        // New file's metadata should be removed on storage save failure immediately
                        DeleteFile(file, true, true);
                        await scope.CommitAsync();
                    }

                    Logger.Error(ex);
                }
            }

            return(ConvertMediaFile(file, pathData.Folder));
        }
        protected virtual async Task <int> ProcessAddressesAsync(ImportExecuteContext context, DbContextScope scope, IEnumerable <ImportRow <Customer> > batch)
        {
            var cargo = await GetCargoData(context);

            foreach (var row in batch)
            {
                ImportAddress("BillingAddress.", row, context, cargo);
                ImportAddress("ShippingAddress.", row, context, cargo);
            }

            var num = await scope.CommitAsync(context.CancelToken);

            return(num);
        }
        private async Task <MediaFolderInfo> InternalCopyFolder(
            DbContextScope scope,
            TreeNode <MediaFolderNode> sourceNode,
            string destPath,
            DuplicateEntryHandling dupeEntryHandling,
            IList <DuplicateFileInfo> dupeFiles,
            CancellationToken cancelToken = default)
        {
            // Get dest node
            var destNode = _folderService.GetNodeByPath(destPath);

            // Dupe handling
            if (destNode != null && dupeEntryHandling == DuplicateEntryHandling.ThrowError)
            {
                throw _exceptionFactory.DuplicateFolder(sourceNode.Value.Path, destNode.Value);
            }

            var doDupeCheck = destNode != null;

            // Create dest folder
            if (destNode == null)
            {
                destNode = await CreateFolderAsync(destPath);
            }

            // INFO: we gonna change file name during the files loop later.
            var destPathData = new MediaPathData(destNode, "placeholder.txt");

            // Get all source files in one go
            var files = await _searcher.SearchFiles(
                new MediaSearchQuery { FolderId = sourceNode.Value.Id },
                MediaLoadFlags.AsNoTracking | MediaLoadFlags.WithTags).LoadAsync();

            IDictionary <string, MediaFile> destFiles = null;
            HashSet <string> destNames = null;

            if (doDupeCheck)
            {
                // Get all files in destination folder for faster dupe selection
                destFiles = (await _searcher
                             .SearchFiles(new MediaSearchQuery {
                    FolderId = destNode.Value.Id
                }, MediaLoadFlags.None).LoadAsync())
                            .ToDictionarySafe(x => x.Name);

                // Make a HashSet from all file names in the destination folder for faster unique file name lookups
                destNames = new HashSet <string>(destFiles.Keys, StringComparer.CurrentCultureIgnoreCase);
            }

            // Holds source and copy together, 'cause we perform a two-pass copy (file first, then data)
            var tuples = new List <(MediaFile, MediaFile)>(500);

            // Copy files batched
            foreach (var batch in files.Slice(500))
            {
                if (cancelToken.IsCancellationRequested)
                {
                    break;
                }

                foreach (var file in batch)
                {
                    if (cancelToken.IsCancellationRequested)
                    {
                        break;
                    }

                    destPathData.FileName = file.Name;

                    // >>> Do copy
                    var copyResult = await InternalCopyFile(
                        file,
                        destPathData,
                        false /* copyData */,
                        dupeEntryHandling,
                        () => Task.FromResult(destFiles?.Get(file.Name)),
                        p => UniqueFileNameChecker(p));

                    if (copyResult.Copy != null)
                    {
                        if (copyResult.IsDupe)
                        {
                            dupeFiles.Add(new DuplicateFileInfo
                            {
                                SourceFile      = ConvertMediaFile(file, sourceNode.Value),
                                DestinationFile = ConvertMediaFile(copyResult.Copy, destNode.Value),
                                UniquePath      = destPathData.FullPath
                            });
                        }
                        if (!copyResult.IsDupe || dupeEntryHandling != DuplicateEntryHandling.Skip)
                        {
                            // When dupe: add to processing queue only if file was NOT skipped
                            tuples.Add((file, copyResult.Copy));
                        }
                    }
                }

                if (!cancelToken.IsCancellationRequested)
                {
                    // Save batch to DB (1st pass)
                    await scope.CommitAsync(cancelToken);

                    // Now copy file data
                    foreach (var op in tuples)
                    {
                        await InternalCopyFileData(op.Item1, op.Item2);
                    }

                    // Save batch to DB (2nd pass)
                    await scope.CommitAsync(cancelToken);
                }

                _db.DetachEntities <MediaFolder>();
                _db.DetachEntities <MediaFile>();
                tuples.Clear();
            }

            // Copy folders
            foreach (var node in sourceNode.Children)
            {
                if (cancelToken.IsCancellationRequested)
                {
                    break;
                }

                destPath = destNode.Value.Path + "/" + node.Value.Name;
                await InternalCopyFolder(scope, node, destPath, dupeEntryHandling, dupeFiles, cancelToken);
            }

            return(new MediaFolderInfo(destNode));

            Task UniqueFileNameChecker(MediaPathData pathData)
            {
                if (destNames != null && _helper.CheckUniqueFileName(pathData.FileTitle, pathData.Extension, destNames, out var uniqueName))
                {
                    pathData.FileName = uniqueName;
                }

                return(Task.CompletedTask);
            }
        }
        protected virtual async Task <int> ProcessGenericAttributesAsync(ImportExecuteContext context, DbContextScope scope, IEnumerable <ImportRow <Customer> > batch)
        {
            // TODO: (mg) (core) (perf) (low) Prefetch all generic attributes for whole batch and work against batch (to be implemented after initial release).
            var cargo = await GetCargoData(context);

            foreach (var row in batch)
            {
                if (_taxSettings.EuVatEnabled)
                {
                    SetGenericAttribute <string>(SystemCustomerAttributeNames.VatNumber, row);
                }

                if (_customerSettings.StreetAddressEnabled)
                {
                    SetGenericAttribute <string>(SystemCustomerAttributeNames.StreetAddress, row);
                }

                if (_customerSettings.StreetAddress2Enabled)
                {
                    SetGenericAttribute <string>(SystemCustomerAttributeNames.StreetAddress2, row);
                }

                if (_customerSettings.CityEnabled)
                {
                    SetGenericAttribute <string>(SystemCustomerAttributeNames.City, row);
                }

                if (_customerSettings.ZipPostalCodeEnabled)
                {
                    SetGenericAttribute <string>(SystemCustomerAttributeNames.ZipPostalCode, row);
                }

                if (_customerSettings.CountryEnabled)
                {
                    SetGenericAttribute <int>(SystemCustomerAttributeNames.CountryId, row);
                }

                if (_customerSettings.CountryEnabled && _customerSettings.StateProvinceEnabled)
                {
                    SetGenericAttribute <int>(SystemCustomerAttributeNames.StateProvinceId, row);
                }

                if (_customerSettings.PhoneEnabled)
                {
                    SetGenericAttribute <string>(SystemCustomerAttributeNames.Phone, row);
                }

                if (_customerSettings.FaxEnabled)
                {
                    SetGenericAttribute <string>(SystemCustomerAttributeNames.Fax, row);
                }

                // TODO: (mg) (core) ForumSettings required in CustomerImporter.
                //if (_forumSettings.ForumsEnabled)
                //    SetGenericAttribute<int>(SystemCustomerAttributeNames.ForumPostCount, row);

                //if (_forumSettings.SignaturesEnabled)
                //    SetGenericAttribute<string>(SystemCustomerAttributeNames.Signature, row);

                var countryId = CountryCodeToId(row.GetDataValue <string>("CountryCode"), cargo);
                var stateId   = StateAbbreviationToId(countryId, row.GetDataValue <string>("StateAbbreviation"), cargo);

                if (countryId.HasValue)
                {
                    SetGenericAttribute(SystemCustomerAttributeNames.CountryId, countryId.Value, row);
                }

                if (stateId.HasValue)
                {
                    SetGenericAttribute(SystemCustomerAttributeNames.StateProvinceId, stateId.Value, row);
                }
            }

            var num = await scope.CommitAsync(context.CancelToken);

            return(num);
        }
        private async Task InternalDeleteFolder(
            DbContextScope scope,
            MediaFolder folder,
            TreeNode <MediaFolderNode> node,
            TreeNode <MediaFolderNode> root,
            FolderDeleteResult result,
            FileHandling strategy,
            CancellationToken cancelToken = default)
        {
            // (perf) We gonna check file tracks, so we should preload all tracks.
            await _db.LoadCollectionAsync(folder, (MediaFolder x) => x.Files, false, q => q.Include(f => f.Tracks));

            var files        = folder.Files.ToList();
            var lockedFiles  = new List <MediaFile>(files.Count);
            var trackedFiles = new List <MediaFile>(files.Count);

            // First delete files
            if (folder.Files.Any())
            {
                var albumId = strategy == FileHandling.MoveToRoot
                    ? _folderService.FindAlbum(folder.Id).Value.Id
                    : (int?)null;

                foreach (var batch in files.Slice(500))
                {
                    if (cancelToken.IsCancellationRequested)
                    {
                        break;
                    }

                    foreach (var file in batch)
                    {
                        if (cancelToken.IsCancellationRequested)
                        {
                            break;
                        }

                        if (strategy == FileHandling.Delete && file.Tracks.Any())
                        {
                            // Don't delete tracked files
                            trackedFiles.Add(file);
                            continue;
                        }

                        if (strategy == FileHandling.Delete)
                        {
                            try
                            {
                                result.DeletedFileNames.Add(file.Name);
                                await DeleteFileAsync(file, true);
                            }
                            catch (DeleteTrackedFileException)
                            {
                                trackedFiles.Add(file);
                            }
                            catch (IOException)
                            {
                                lockedFiles.Add(file);
                            }
                        }
                        else if (strategy == FileHandling.SoftDelete)
                        {
                            await DeleteFileAsync(file, false);

                            file.FolderId = null;
                            result.DeletedFileNames.Add(file.Name);
                        }
                        else if (strategy == FileHandling.MoveToRoot)
                        {
                            file.FolderId = albumId;
                            result.DeletedFileNames.Add(file.Name);
                        }
                    }

                    await scope.CommitAsync(cancelToken);
                }

                if (lockedFiles.Any())
                {
                    // Retry deletion of failed files due to locking.
                    // INFO: By default "LocalFileSystem" waits for 500ms until the lock is revoked or it throws.
                    foreach (var lockedFile in lockedFiles.ToArray())
                    {
                        if (cancelToken.IsCancellationRequested)
                        {
                            break;
                        }

                        try
                        {
                            await DeleteFileAsync(lockedFile, true);

                            lockedFiles.Remove(lockedFile);
                        }
                        catch { }
                    }

                    await scope.CommitAsync(cancelToken);
                }
            }

            if (!cancelToken.IsCancellationRequested && lockedFiles.Count > 0)
            {
                var fullPath = CombinePaths(root.Value.Path, lockedFiles[0].Name);
                throw new IOException(T("Admin.Media.Exception.InUse", fullPath));
            }

            if (!cancelToken.IsCancellationRequested && lockedFiles.Count == 0 && trackedFiles.Count == 0 && node.Children.All(x => result.DeletedFolderIds.Contains(x.Value.Id)))
            {
                // Don't delete folder if a containing file could not be deleted,
                // any tracked file was found or any of its child folders could not be deleted..
                _db.MediaFolders.Remove(folder);
                await scope.CommitAsync(cancelToken);

                result.DeletedFolderIds.Add(folder.Id);
            }

            result.LockedFileNames  = lockedFiles.Select(x => x.Name).ToList();
            result.TrackedFileNames = trackedFiles.Select(x => x.Name).ToList();
        }
        protected virtual async Task <int> ProcessCustomersAsync(ImportExecuteContext context, DbContextScope scope, IEnumerable <ImportRow <Customer> > batch)
        {
            var cargo = await GetCargoData(context);

            var currentCustomer = _services.WorkContext.CurrentCustomer;
            var customerQuery   = _db.Customers
                                  .Include(x => x.Addresses)
                                  .Include(x => x.CustomerRoleMappings)
                                  .ThenInclude(x => x.CustomerRole);

            foreach (var row in batch)
            {
                Customer customer = null;
                var      id       = row.GetDataValue <int>("Id");
                var      email    = row.GetDataValue <string>("Email");
                var      userName = row.GetDataValue <string>("Username");

                foreach (var keyName in context.KeyFieldNames)
                {
                    switch (keyName)
                    {
                    case "Id":
                        customer = await _db.Customers.FindByIdAsync(id, true, context.CancelToken);

                        break;

                    case "CustomerGuid":
                        var customerGuid = row.GetDataValue <string>("CustomerGuid");
                        if (customerGuid.HasValue())
                        {
                            var guid = new Guid(customerGuid);
                            customer = await customerQuery.FirstOrDefaultAsync(x => x.CustomerGuid == guid, context.CancelToken);
                        }
                        break;

                    case "Email":
                        if (email.HasValue())
                        {
                            customer = await customerQuery.FirstOrDefaultAsync(x => x.Email == email, context.CancelToken);
                        }
                        break;

                    case "Username":
                        if (userName.HasValue())
                        {
                            customer = await customerQuery.FirstOrDefaultAsync(x => x.Username == userName, context.CancelToken);
                        }
                        break;
                    }

                    if (customer != null)
                    {
                        break;
                    }
                }

                if (customer == null)
                {
                    if (context.UpdateOnly)
                    {
                        ++context.Result.SkippedRecords;
                        continue;
                    }

                    customer = new Customer
                    {
                        CustomerGuid = new Guid(),
                        AffiliateId  = 0,
                        Active       = true
                    };
                }
                else
                {
                    await _db.LoadCollectionAsync(customer, x => x.CustomerRoleMappings, false, q => q.Include(x => x.CustomerRole), context.CancelToken);
                }

                var affiliateId = row.GetDataValue <int>("AffiliateId");

                row.Initialize(customer, email ?? id.ToString());

                row.SetProperty(context.Result, (x) => x.CustomerGuid);
                row.SetProperty(context.Result, (x) => x.Username);
                row.SetProperty(context.Result, (x) => x.Email);
                row.SetProperty(context.Result, (x) => x.Salutation);
                row.SetProperty(context.Result, (x) => x.FullName);
                row.SetProperty(context.Result, (x) => x.FirstName);
                row.SetProperty(context.Result, (x) => x.LastName);

                if (_customerSettings.TitleEnabled)
                {
                    row.SetProperty(context.Result, (x) => x.Title);
                }

                if (_customerSettings.CompanyEnabled)
                {
                    row.SetProperty(context.Result, (x) => x.Company);
                }

                if (_customerSettings.DateOfBirthEnabled)
                {
                    row.SetProperty(context.Result, (x) => x.BirthDate);
                }

                if (_privacySettings.StoreLastIpAddress)
                {
                    row.SetProperty(context.Result, (x) => x.LastIpAddress);
                }

                if (email.HasValue() && currentCustomer.Email.EqualsNoCase(email))
                {
                    context.Result.AddInfo("Security. Ignored password of current customer (who started this import).", row.RowInfo, "Password");
                }
                else
                {
                    row.SetProperty(context.Result, (x) => x.Password);
                    row.SetProperty(context.Result, (x) => x.PasswordFormatId);
                    row.SetProperty(context.Result, (x) => x.PasswordSalt);
                }

                row.SetProperty(context.Result, (x) => x.AdminComment);
                row.SetProperty(context.Result, (x) => x.IsTaxExempt);
                row.SetProperty(context.Result, (x) => x.Active);

                row.SetProperty(context.Result, (x) => x.CreatedOnUtc, context.UtcNow);
                row.SetProperty(context.Result, (x) => x.LastActivityDateUtc, context.UtcNow);

                if (_taxSettings.EuVatEnabled)
                {
                    row.SetProperty(context.Result, (x) => x.VatNumberStatusId);
                }

                if (_dateTimeSettings.AllowCustomersToSetTimeZone)
                {
                    row.SetProperty(context.Result, (x) => x.TimeZoneId);
                }

                if (_customerSettings.GenderEnabled)
                {
                    row.SetProperty(context.Result, (x) => x.Gender);
                }

                if (affiliateId > 0 && cargo.AffiliateIds.Contains(affiliateId))
                {
                    customer.AffiliateId = affiliateId;
                }

                string customerNumber = null;

                if (_customerSettings.CustomerNumberMethod == CustomerNumberMethod.AutomaticallySet && row.IsTransient)
                {
                    customerNumber = row.Entity.Id.ToString();
                }
                else if (_customerSettings.CustomerNumberMethod == CustomerNumberMethod.Enabled && !row.IsTransient && row.HasDataValue("CustomerNumber"))
                {
                    customerNumber = row.GetDataValue <string>("CustomerNumber");
                }

                if (customerNumber.HasValue() || !cargo.CustomerNumbers.Contains(customerNumber))
                {
                    row.Entity.CustomerNumber = customerNumber;

                    if (!customerNumber.IsEmpty())
                    {
                        cargo.CustomerNumbers.Add(customerNumber);
                    }
                }

                if (row.IsTransient)
                {
                    _db.Customers.Add(customer);
                }
            }

            // Commit whole batch at once.
            var num = await scope.CommitAsync(context.CancelToken);

            return(num);
        }
Exemple #12
0
        public async Task Run(TaskExecutionContext ctx, CancellationToken cancelToken = default)
        {
            var count         = 0;
            var numDeleted    = 0;
            var numAdded      = 0;
            var numCategories = 0;
            var pageSize      = 500;
            var pageIndex     = -1;

            var categoryIds = ctx.Parameters.ContainsKey("CategoryIds")
                ? ctx.Parameters["CategoryIds"].ToIntArray()
                : null;

            // Hooks are enabled because search index needs to be updated.
            using (var scope = new DbContextScope(_db, autoDetectChanges: false, hooksEnabled: true, deferCommit: true))
            {
                // Delete existing system mappings.
                var deleteQuery = _db.ProductCategories.Where(x => x.IsSystemMapping);
                if (categoryIds != null)
                {
                    deleteQuery = deleteQuery.Where(x => categoryIds.Contains(x.CategoryId));
                }

                numDeleted = await deleteQuery.BatchDeleteAsync(cancelToken);

                // Insert new product category mappings.
                var categoryQuery = _db.Categories
                                    .Include(x => x.RuleSets)
                                    .AsNoTracking();

                if (categoryIds != null)
                {
                    categoryQuery = categoryQuery.Where(x => categoryIds.Contains(x.Id));
                }

                var categories = await categoryQuery
                                 .Where(x => x.Published && x.RuleSets.Any(y => y.IsActive))
                                 .ToListAsync(cancelToken);

                numCategories = categories.Count;

                foreach (var category in categories)
                {
                    var ruleSetProductIds = new HashSet <int>();

                    await ctx.SetProgressAsync(++count, categories.Count, $"Add product mappings for category \"{category.Name.NaIfEmpty()}\".");

                    // Execute active rule sets and collect product ids.
                    foreach (var ruleSet in category.RuleSets.Where(x => x.IsActive))
                    {
                        if (cancelToken.IsCancellationRequested)
                        {
                            return;
                        }

                        var expressionGroup = await _ruleService.CreateExpressionGroupAsync(ruleSet, (IRuleVisitor)_productRuleProvider);

                        if (expressionGroup is SearchFilterExpression expression)
                        {
                            pageIndex = -1;
                            while (true)
                            {
                                // Do not touch searchResult.Hits. We only need the product identifiers.
                                var searchResult = await _productRuleProvider.SearchAsync(new[] { expression }, ++pageIndex, pageSize);

                                ruleSetProductIds.AddRange(searchResult.HitsEntityIds);

                                if (pageIndex >= (searchResult.TotalHitsCount / pageSize))
                                {
                                    break;
                                }
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetProductIds.Any())
                    {
                        foreach (var chunk in ruleSetProductIds.Slice(500))
                        {
                            if (cancelToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var productId in chunk)
                            {
                                _db.ProductCategories.Add(new ProductCategory
                                {
                                    ProductId       = productId,
                                    CategoryId      = category.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync(cancelToken);
                        }

                        try
                        {
                            scope.DbContext.DetachEntities <ProductCategory>();
                        }
                        catch { }
                    }
                }
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} product mappings for {numCategories} categories.");
        }
        public async Task Run(TaskExecutionContext ctx, CancellationToken cancelToken = default)
        {
            //var count = 0;
            var numDeleted = 0;
            var numAdded   = 0;
            var rolesCount = 0;

            using (var scope = new DbContextScope(_db, autoDetectChanges: false, hooksEnabled: false, deferCommit: true))
            {
                // Delete existing system mappings.
                var deleteQuery = _db.CustomerRoleMappings.Where(x => x.IsSystemMapping);
                if (ctx.Parameters.ContainsKey("CustomerRoleIds"))
                {
                    var roleIds = ctx.Parameters["CustomerRoleIds"].ToIntArray();
                    deleteQuery = deleteQuery.Where(x => roleIds.Contains(x.CustomerRoleId));
                }

                numDeleted = await deleteQuery.BatchDeleteAsync(cancelToken);

                // Insert new customer role mappings.
                var roles = await _db.CustomerRoles
                            .Include(x => x.RuleSets)
                            .AsNoTracking()
                            .Where(x => x.Active && x.RuleSets.Any(y => y.IsActive))
                            .ToListAsync(cancelToken);

                rolesCount = roles.Count;

                foreach (var role in roles)
                {
                    var ruleSetCustomerIds = new HashSet <int>();

                    // TODO: (mg) (core) Complete TargetGroupEvaluatorTask (TaskExecutionContext required).
                    //ctx.SetProgress(++count, roles.Count, $"Add customer assignments for role \"{role.SystemName.NaIfEmpty()}\".");

                    // Execute active rule sets and collect customer ids.
                    foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive))
                    {
                        if (cancelToken.IsCancellationRequested)
                        {
                            return;
                        }

                        var expressionGroup = await _ruleService.CreateExpressionGroupAsync(ruleSet, _targetGroupService);

                        if (expressionGroup is FilterExpression expression)
                        {
                            var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500);
                            var resultPager  = new FastPager <Customer>(filterResult.SourceQuery, 500);

                            while ((await resultPager.ReadNextPageAsync(x => x.Id, x => x)).Out(out var customerIds))
                            {
                                ruleSetCustomerIds.AddRange(customerIds);
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetCustomerIds.Any())
                    {
                        foreach (var chunk in ruleSetCustomerIds.Slice(500))
                        {
                            if (cancelToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var customerId in chunk)
                            {
                                _db.CustomerRoleMappings.Add(new CustomerRoleMapping
                                {
                                    CustomerId      = customerId,
                                    CustomerRoleId  = role.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync(cancelToken);
                        }

                        try
                        {
                            scope.DbContext.DetachEntities <CustomerRoleMapping>();
                        }
                        catch { }
                    }
                }
            }

            if (numAdded > 0 || numDeleted > 0)
            {
                await _cache.RemoveByPatternAsync(AclService.ACL_SEGMENT_PATTERN);
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} customer assignments for {rolesCount} roles.");
        }
Exemple #14
0
        public virtual async Task <ProductSummaryModel> MapProductSummaryModelAsync(IPagedList <Product> products, ProductSummaryMappingSettings settings)
        {
            Guard.NotNull(products, nameof(products));

            if (settings == null)
            {
                settings = new ProductSummaryMappingSettings();
            }

            using (_services.Chronometer.Step("MapProductSummaryModel"))
            {
                var model = new ProductSummaryModel(products)
                {
                    ViewMode                          = settings.ViewMode,
                    GridColumnSpan                    = _catalogSettings.GridStyleListColumnSpan,
                    ShowSku                           = _catalogSettings.ShowProductSku,
                    ShowWeight                        = _catalogSettings.ShowWeight,
                    ShowDimensions                    = settings.MapDimensions,
                    ShowLegalInfo                     = settings.MapLegalInfo,
                    ShowDescription                   = settings.MapShortDescription,
                    ShowFullDescription               = settings.MapFullDescription,
                    ShowRatings                       = settings.MapReviews,
                    ShowPrice                         = settings.MapPrices,
                    ShowBasePrice                     = settings.MapPrices && _catalogSettings.ShowBasePriceInProductLists && settings.ViewMode != ProductSummaryViewMode.Mini,
                    ShowShippingSurcharge             = settings.MapPrices && settings.ViewMode != ProductSummaryViewMode.Mini,
                    ShowButtons                       = settings.ViewMode != ProductSummaryViewMode.Mini,
                    ShowBrand                         = settings.MapManufacturers,
                    ForceRedirectionAfterAddingToCart = settings.ForceRedirectionAfterAddingToCart,
                    CompareEnabled                    = _catalogSettings.CompareProductsEnabled,
                    WishlistEnabled                   = _services.Permissions.Authorize(Permissions.Cart.AccessWishlist),
                    BuyEnabled                        = !_catalogSettings.HideBuyButtonInLists,
                    ThumbSize                         = settings.ThumbnailSize,
                    ShowDiscountBadge                 = _catalogSettings.ShowDiscountSign,
                    ShowNewBadge                      = _catalogSettings.LabelAsNewForMaxDays.HasValue,
                    DeliveryTimesPresentation         = settings.DeliveryTimesPresentation,
                };

                if (products.Count == 0)
                {
                    // No products, stop here.
                    return(model);
                }

                using var scope = new DbContextScope(_db, retainConnection: true, deferCommit: true);

                // PERF!!
                var store       = _services.StoreContext.CurrentStore;
                var customer    = _services.WorkContext.CurrentCustomer;
                var currency    = _services.WorkContext.WorkingCurrency;
                var language    = _services.WorkContext.WorkingLanguage;
                var allowPrices = await _services.Permissions.AuthorizeAsync(Permissions.Catalog.DisplayPrice);

                var allowShoppingCart = await _services.Permissions.AuthorizeAsync(Permissions.Cart.AccessShoppingCart);

                var allowWishlist = await _services.Permissions.AuthorizeAsync(Permissions.Cart.AccessWishlist);

                var taxDisplayType       = _workContext.GetTaxDisplayTypeFor(customer, store.Id);
                var cachedBrandModels    = new Dictionary <int, BrandOverviewModel>();
                var prefetchTranslations = settings.PrefetchTranslations == true || (settings.PrefetchTranslations == null && _performanceSettings.AlwaysPrefetchTranslations);
                var prefetchSlugs        = settings.PrefetchUrlSlugs == true || (settings.PrefetchUrlSlugs == null && _performanceSettings.AlwaysPrefetchUrlSlugs);
                var allProductIds        = prefetchSlugs || prefetchTranslations?products.Select(x => x.Id).ToArray() : Array.Empty <int>();

                //var productIds = products.Select(x => x.Id).ToArray();

                string taxInfo   = T(taxDisplayType == TaxDisplayType.IncludingTax ? "Tax.InclVAT" : "Tax.ExclVAT");
                var    legalInfo = string.Empty;

                var res = new Dictionary <string, LocalizedString>(StringComparer.OrdinalIgnoreCase)
                {
                    { "Products.CallForPrice", T("Products.CallForPrice") },
                    { "Products.PriceRangeFrom", T("Products.PriceRangeFrom") },
                    { "Media.Product.ImageLinkTitleFormat", T("Media.Product.ImageLinkTitleFormat") },
                    { "Media.Product.ImageAlternateTextFormat", T("Media.Product.ImageAlternateTextFormat") },
                    { "Products.DimensionsValue", T("Products.DimensionsValue") },
                    { "Common.AdditionalShippingSurcharge", T("Common.AdditionalShippingSurcharge") }
                };

                if (settings.MapLegalInfo)
                {
                    var shippingInfoUrl = (await _urlHelper.TopicAsync("shippinginfo"));
                    legalInfo = shippingInfoUrl.HasValue()
                        ? T("Tax.LegalInfoShort", taxInfo, shippingInfoUrl)
                        : T("Tax.LegalInfoShort2", taxInfo);
                }

                if (prefetchSlugs)
                {
                    await _urlService.PrefetchUrlRecordsAsync(nameof(Product), new[] { language.Id, 0 }, allProductIds);
                }

                if (prefetchTranslations)
                {
                    // Prefetch all delivery time translations
                    await _localizedEntityService.PrefetchLocalizedPropertiesAsync(nameof(DeliveryTime), language.Id, null);
                }

                // Run in uncommitting scope, because pictures could be updated (IsNew property)
                var batchContext = _dataExporter.Value.CreateProductExportContext(products, customer, null, 1, false);

                if (settings.MapPrices)
                {
                    await batchContext.AppliedDiscounts.LoadAllAsync();

                    await batchContext.TierPrices.LoadAllAsync();
                }

                if (settings.MapAttributes || settings.MapColorAttributes)
                {
                    await batchContext.Attributes.LoadAllAsync();

                    if (prefetchTranslations)
                    {
                        // Prefetch all product attribute translations
                        await PrefetchTranslations(
                            nameof(ProductAttribute),
                            language.Id,
                            batchContext.Attributes.SelectMany(x => x.Value).Select(x => x.ProductAttribute));

                        // Prefetch all variant attribute value translations
                        await PrefetchTranslations(
                            nameof(ProductVariantAttributeValue),
                            language.Id,
                            batchContext.Attributes.SelectMany(x => x.Value).SelectMany(x => x.ProductVariantAttributeValues));
                    }
                }

                if (settings.MapManufacturers)
                {
                    await batchContext.ProductManufacturers.LoadAllAsync();
                }

                if (settings.MapSpecificationAttributes)
                {
                    await batchContext.SpecificationAttributes.LoadAllAsync();

                    if (prefetchTranslations)
                    {
                        // Prefetch all spec attribute option translations
                        await PrefetchTranslations(
                            nameof(SpecificationAttributeOption),
                            language.Id,
                            batchContext.SpecificationAttributes.SelectMany(x => x.Value).Select(x => x.SpecificationAttributeOption));

                        // Prefetch all spec attribute translations
                        await PrefetchTranslations(
                            nameof(SpecificationAttribute),
                            language.Id,
                            batchContext.SpecificationAttributes.SelectMany(x => x.Value).Select(x => x.SpecificationAttributeOption.SpecificationAttribute));
                    }
                }

                // If a size has been set in the view, we use it in priority
                int thumbSize = model.ThumbSize ?? _mediaSettings.ProductThumbPictureSize;

                var mapItemContext = new MapProductSummaryItemContext
                {
                    BatchContext      = batchContext,
                    CachedBrandModels = cachedBrandModels,
                    PrimaryCurrency   = store.PrimaryStoreCurrency,
                    StoreCurrency     = currency,
                    LegalInfo         = legalInfo,
                    Model             = model,
                    Resources         = res,
                    Settings          = settings,
                    Customer          = customer,
                    Store             = store,
                    AllowPrices       = allowPrices,
                    AllowShoppingCart = allowShoppingCart,
                    AllowWishlist     = allowWishlist,
                    TaxDisplayType    = taxDisplayType
                };

                if (settings.MapPictures)
                {
                    var fileIds = products
                                  .Select(x => x.MainPictureId ?? 0)
                                  .Where(x => x != 0)
                                  .Distinct()
                                  .ToArray();

                    mapItemContext.MediaFiles = (await _mediaService.GetFilesByIdsAsync(fileIds)).ToDictionarySafe(x => x.Id);
                }

                foreach (var product in products)
                {
                    await MapProductSummaryItem(product, mapItemContext);
                }

                _services.DisplayControl.AnnounceRange(products);

                await scope.CommitAsync();

                batchContext.Clear();

                // don't show stuff without data at all
                model.ShowDescription = model.ShowDescription && model.Items.Any(x => x.ShortDescription?.Value?.HasValue() == true);
                model.ShowBrand       = model.ShowBrand && model.Items.Any(x => x.Brand != null);

                return(model);
            }
        }
        public override async Task ExecuteAsync(TaskExecutionContext ctx)
        {
            var count      = 0;
            var clearCache = false;
            var roleQuery  = _customerRoleRepository.TableUntracked.Expand(x => x.RuleSets);

            if (ctx.Parameters.ContainsKey("CustomerRoleIds"))
            {
                var rolesIds = ctx.Parameters["CustomerRoleIds"].ToIntArray();
                roleQuery = roleQuery.Where(x => rolesIds.Contains(x.Id));
            }

            var roles = await roleQuery.ToListAsync();

            if (!roles.Any())
            {
                return;
            }

            using (var scope = new DbContextScope(ctx: _customerRoleMappingRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false))

                foreach (var role in roles)
                {
                    try
                    {
                        ctx.SetProgress(++count, roles.Count, $"Sync customer assignments for role {role.SystemName.NaIfEmpty()}.");

                        _customerRoleMappingRepository.Context.DetachEntities(x => x is CustomerRoleMapping);
                    }
                    catch { }

                    var ruleSetCustomerIds  = new HashSet <int>();
                    var existingCustomerIds = new HashSet <int>();
                    var numDeleted          = 0;
                    var numAdded            = 0;

                    // Execute active rule sets and collect customer ids.
                    // Delete old mappings if the role is inactive or has no assigned rule sets.
                    if (role.Active)
                    {
                        foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive))
                        {
                            if (ctx.CancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            var expression = _ruleFactory.CreateExpressionGroup(ruleSet, _targetGroupService) as FilterExpression;
                            if (expression != null)
                            {
                                var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500);
                                var resultPager  = new FastPager <Customer>(filterResult.SourceQuery, 500);

                                for (var i = 0; i < 9999999; ++i)
                                {
                                    var customerIds = await resultPager.ReadNextPageAsync(x => x.Id, x => x);

                                    if (!(customerIds?.Any() ?? false))
                                    {
                                        break;
                                    }

                                    ruleSetCustomerIds.AddRange(customerIds);
                                }
                            }
                        }
                    }

                    // Sync mappings.
                    var query = _customerRoleMappingRepository.Table.Where(x => x.CustomerRoleId == role.Id && x.IsSystemMapping);
                    var pager = new FastPager <CustomerRoleMapping>(query, 500);

                    // Mappings to delete.
                    for (var i = 0; i < 9999999; ++i)
                    {
                        if (ctx.CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        var mappings = await pager.ReadNextPageAsync <CustomerRoleMapping>();

                        if (!(mappings?.Any() ?? false))
                        {
                            break;
                        }

                        foreach (var mapping in mappings)
                        {
                            if (!role.Active || !ruleSetCustomerIds.Contains(mapping.CustomerId))
                            {
                                _customerRoleMappingRepository.Delete(mapping);

                                ++numDeleted;
                                clearCache = true;
                            }
                            else
                            {
                                existingCustomerIds.Add(mapping.CustomerId);
                            }
                        }

                        await scope.CommitAsync();
                    }

                    // Mappings to add.
                    if (role.Active)
                    {
                        var toAdd = ruleSetCustomerIds.Except(existingCustomerIds).ToList();
                        if (toAdd.Any())
                        {
                            foreach (var chunk in toAdd.Slice(500))
                            {
                                if (ctx.CancellationToken.IsCancellationRequested)
                                {
                                    return;
                                }

                                foreach (var customerId in chunk)
                                {
                                    _customerRoleMappingRepository.Insert(new CustomerRoleMapping
                                    {
                                        CustomerId      = customerId,
                                        CustomerRoleId  = role.Id,
                                        IsSystemMapping = true
                                    });

                                    ++numAdded;
                                    clearCache = true;
                                }

                                await scope.CommitAsync();
                            }
                        }
                    }

                    Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Customer assignments for {role.SystemName.NaIfEmpty()}: deleted {numDeleted}, added {numAdded}.");
                }

            if (clearCache)
            {
                _cacheManager.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN);
            }
        }
Exemple #16
0
        public override async Task ExecuteAsync(TaskExecutionContext ctx)
        {
            var count         = 0;
            var numDeleted    = 0;
            var numAdded      = 0;
            var pageSize      = 500;
            var categoryQuery = _categoryRepository.TableUntracked.Expand(x => x.RuleSets);

            if (ctx.Parameters.ContainsKey("CategoryIds"))
            {
                var categoryIds = ctx.Parameters["CategoryIds"].ToIntArray();
                categoryQuery = categoryQuery.Where(x => categoryIds.Contains(x.Id));

                numDeleted = _productCategoryRepository.Context.ExecuteSqlCommand(
                    "Delete From [dbo].[Product_Category_Mapping] Where [CategoryId] In ({0}) And [IsSystemMapping] = 1",
                    false,
                    null,
                    string.Join(",", categoryIds));
            }
            else
            {
                numDeleted = _productCategoryRepository.Context.ExecuteSqlCommand("Delete From [dbo].[Product_Category_Mapping] Where [IsSystemMapping] = 1");
            }

            var categories = await categoryQuery
                             .Where(x => x.Published && !x.Deleted && x.RuleSets.Any(y => y.IsActive))
                             .ToListAsync();

            using (var scope = new DbContextScope(ctx: _productCategoryRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false))
            {
                foreach (var category in categories)
                {
                    var ruleSetProductIds = new HashSet <int>();

                    ctx.SetProgress(++count, categories.Count, $"Add product mappings for category \"{category.Name.NaIfEmpty()}\".");

                    // Execute active rule sets and collect product ids.
                    foreach (var ruleSet in category.RuleSets.Where(x => x.IsActive))
                    {
                        if (ctx.CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        if (_ruleFactory.CreateExpressionGroup(ruleSet, _productRuleProvider) is SearchFilterExpression expression)
                        {
                            var pageIndex = -1;
                            while (true)
                            {
                                // Do not touch searchResult.Hits. We only need the product identifiers.
                                var searchResult = _productRuleProvider.Search(new SearchFilterExpression[] { expression }, ++pageIndex, pageSize);
                                ruleSetProductIds.AddRange(searchResult.HitsEntityIds);

                                if (pageIndex >= (searchResult.TotalHitsCount / pageSize))
                                {
                                    break;
                                }
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetProductIds.Any())
                    {
                        foreach (var chunk in ruleSetProductIds.Slice(500))
                        {
                            if (ctx.CancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var productId in chunk)
                            {
                                _productCategoryRepository.Insert(new ProductCategory
                                {
                                    ProductId       = productId,
                                    CategoryId      = category.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync();
                        }

                        try
                        {
                            _productCategoryRepository.Context.DetachEntities <ProductCategory>();
                        }
                        catch { }
                    }
                }
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} product mappings for {categories.Count} categories.");
        }
Exemple #17
0
        public virtual async Task <(int AffectedCategories, int AffectedProducts)> InheritStoresIntoChildrenAsync(
            int categoryId,
            bool touchProductsWithMultipleCategories = false,
            bool touchExistingAcls = false,
            bool categoriesOnly    = false)
        {
            var categoryEntityName = nameof(Category);
            var productEntityName  = nameof(Product);
            var affectedCategories = 0;
            var affectedProducts   = 0;

            var allStoreIds = await _db.Stores
                              .AsQueryable()
                              .Select(x => x.Id)
                              .ToListAsync();

            var referenceCategory = await _db.Categories.FindByIdAsync(categoryId);

            var referenceStoreMappingIds = await _storeMappingService.GetAuthorizedStoreIdsAsync(referenceCategory);

            using (var scope = new DbContextScope(_db, autoDetectChanges: false))
            {
                await ProcessCategory(scope, referenceCategory);
            }

            _cache.RemoveByPattern(StoreMappingService.STOREMAPPING_SEGMENT_PATTERN);

            return(affectedCategories, affectedProducts);

            async Task ProcessCategory(DbContextScope scope, Category c)
            {
                // Process sub-categories.
                var subCategories = await _db.Categories
                                    .AsQueryable()
                                    .Where(x => x.ParentCategoryId == c.Id)
                                    .OrderBy(x => x.DisplayOrder)
                                    .ToListAsync();

                foreach (var subCategory in subCategories)
                {
                    if (subCategory.LimitedToStores != referenceCategory.LimitedToStores)
                    {
                        subCategory.LimitedToStores = referenceCategory.LimitedToStores;
                    }

                    var storeMappings = await _db.StoreMappings
                                        .ApplyEntityFilter(subCategory)
                                        .ToListAsync();

                    var storeMappingsDic = storeMappings.ToDictionarySafe(x => x.StoreId);

                    foreach (var storeId in allStoreIds)
                    {
                        if (referenceStoreMappingIds.Contains(storeId))
                        {
                            if (!storeMappingsDic.ContainsKey(storeId))
                            {
                                _db.StoreMappings.Add(new StoreMapping {
                                    StoreId = storeId, EntityId = subCategory.Id, EntityName = categoryEntityName
                                });
                            }
                        }
                        else if (storeMappingsDic.TryGetValue(storeId, out var storeMappingToDelete))
                        {
                            _db.StoreMappings.Remove(storeMappingToDelete);
                        }
                    }
                }

                await scope.CommitAsync();

                affectedCategories += subCategories.Count;

                // Process products.
                var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id))
                {
                    c.Id
                };

                var searchQuery   = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray());
                var productsQuery = _catalogSearchService.PrepareQuery(searchQuery);
                var productsPager = new FastPager <Product>(productsQuery, 500);

                while ((await productsPager.ReadNextPageAsync <Product>()).Out(out var products))
                {
                    foreach (var product in products)
                    {
                        if (product.LimitedToStores != referenceCategory.LimitedToStores)
                        {
                            product.LimitedToStores = referenceCategory.LimitedToStores;
                        }

                        var storeMappings = await _db.StoreMappings
                                            .ApplyEntityFilter(product)
                                            .ToListAsync();

                        var storeMappingsDic = storeMappings.ToDictionarySafe(x => x.StoreId);

                        foreach (var storeId in allStoreIds)
                        {
                            if (referenceStoreMappingIds.Contains(storeId))
                            {
                                if (!storeMappingsDic.ContainsKey(storeId))
                                {
                                    _db.StoreMappings.Add(new StoreMapping {
                                        StoreId = storeId, EntityId = product.Id, EntityName = productEntityName
                                    });
                                }
                            }
                            else if (storeMappingsDic.TryGetValue(storeId, out var storeMappingToDelete))
                            {
                                _db.StoreMappings.Remove(storeMappingToDelete);
                            }
                        }
                    }

                    await scope.CommitAsync();

                    affectedProducts += products.Count;
                }

                await scope.CommitAsync();

                try
                {
                    scope.DbContext.DetachEntities(x => x is Product || x is Category || x is StoreMapping, false);
                }
                catch { }

                foreach (var subCategory in subCategories)
                {
                    await ProcessCategory(scope, subCategory);
                }
            }
        }
Exemple #18
0
        public virtual async Task <(int AffectedCategories, int AffectedProducts)> InheritAclIntoChildrenAsync(
            int categoryId,
            bool touchProductsWithMultipleCategories = false,
            bool touchExistingAcls = false,
            bool categoriesOnly    = false)
        {
            var categoryEntityName = nameof(Category);
            //var productEntityName = nameof(Product);
            var affectedCategories = 0;
            var affectedProducts   = 0;

            var allCustomerRolesIds = await _db.CustomerRoles
                                      .AsQueryable()
                                      .Select(x => x.Id)
                                      .ToListAsync();

            var referenceCategory = await _db.Categories.FindByIdAsync(categoryId);

            // TODO: (mg) (core) Complete ICategoryService.InheritAclIntoChildrenAsync (IAclService required).
            var referenceRoleIds = Array.Empty <int>();

            using (var scope = new DbContextScope(_db, autoDetectChanges: false))
            {
                await ProcessCategory(scope, referenceCategory);
            }

            // TODO: (mg) (core) Complete ICategoryService.InheritAclIntoChildrenAsync (IAclService required).
            //_cache.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN);

            return(affectedCategories, affectedProducts);

            async Task ProcessCategory(DbContextScope scope, Category c)
            {
                // Process sub-categories.
                var subCategories = await _db.Categories
                                    .AsQueryable()
                                    .Where(x => x.ParentCategoryId == c.Id)
                                    .OrderBy(x => x.DisplayOrder)
                                    .ToListAsync();

                foreach (var subCategory in subCategories)
                {
                    if (subCategory.SubjectToAcl != referenceCategory.SubjectToAcl)
                    {
                        subCategory.SubjectToAcl = referenceCategory.SubjectToAcl;
                    }

                    var aclRecords = await _db.AclRecords
                                     .ApplyEntityFilter(subCategory)
                                     .ToListAsync();

                    var aclRecordsDic = aclRecords.ToDictionarySafe(x => x.CustomerRoleId);

                    foreach (var roleId in allCustomerRolesIds)
                    {
                        if (referenceRoleIds.Contains(roleId))
                        {
                            if (!aclRecordsDic.ContainsKey(roleId))
                            {
                                await _db.AclRecords.AddAsync(new AclRecord { CustomerRoleId = roleId, EntityId = subCategory.Id, EntityName = categoryEntityName });
                            }
                        }
                        else if (aclRecordsDic.TryGetValue(roleId, out var aclRecordToDelete))
                        {
                            _db.AclRecords.Remove(aclRecordToDelete);
                        }
                    }
                }

                await scope.CommitAsync();

                affectedCategories += subCategories.Count;

                // Process products.
                var categoryIds = new HashSet <int>(subCategories.Select(x => x.Id));

                categoryIds.Add(c.Id);

                // TODO: (mg) (core) Complete ICategoryService.InheritAclIntoChildrenAsync (ICatalogSearchService required).
                //var searchQuery = new CatalogSearchQuery().WithCategoryIds(null, categoryIds.ToArray());
                //var productsQuery = _catalogSearchService.PrepareQuery(searchQuery);
                //var productsPager = new FastPager<Product>(productsQuery, 500);

                //while ((await productsPager.ReadNextPageAsync<Product>()).Out(out var products))
                //{
                //    foreach (var product in products)
                //    {
                //        if (product.SubjectToAcl != referenceCategory.SubjectToAcl)
                //        {
                //            product.SubjectToAcl = referenceCategory.SubjectToAcl;
                //        }

                //        var aclRecords = await _db.AclRecords
                //            .ApplyEntityFilter(product)
                //            .ToListAsync();

                //        var aclRecordsDic = aclRecords.ToDictionarySafe(x => x.CustomerRoleId);

                //        foreach (var roleId in allCustomerRolesIds)
                //        {
                //            if (referenceRoleIds.Contains(roleId))
                //            {
                //                if (!aclRecordsDic.ContainsKey(roleId))
                //                {
                //                    await _db.AclRecords.AddAsync(new AclRecord { CustomerRoleId = roleId, EntityId = product.Id, EntityName = productEntityName });
                //                }
                //            }
                //            else if (aclRecordsDic.TryGetValue(roleId, out var aclRecordToDelete))
                //            {
                //                _db.AclRecords.Remove(aclRecordToDelete);
                //            }
                //        }
                //    }

                //    await scope.CommitAsync();
                //    affectedProducts += products.Count;
                //}

                //await scope.CommitAsync();

                try
                {
                    scope.DbContext.DetachEntities(x => x is Product || x is Category || x is AclRecord, false);
                }
                catch { }

                foreach (var subCategory in subCategories)
                {
                    await ProcessCategory(scope, subCategory);
                }
            }
        }
        public override async Task ExecuteAsync(TaskExecutionContext ctx)
        {
            var count      = 0;
            var numDeleted = 0;
            var numAdded   = 0;
            var roleQuery  = _customerRoleRepository.TableUntracked.Expand(x => x.RuleSets);

            if (ctx.Parameters.ContainsKey("CustomerRoleIds"))
            {
                var roleIds = ctx.Parameters["CustomerRoleIds"].ToIntArray();
                roleQuery = roleQuery.Where(x => roleIds.Contains(x.Id));

                numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand(
                    "Delete From [dbo].[CustomerRoleMapping] Where [CustomerRoleId] In ({0}) And [IsSystemMapping] = 1",
                    false,
                    null,
                    string.Join(",", roleIds));
            }
            else
            {
                numDeleted = _customerRoleMappingRepository.Context.ExecuteSqlCommand("Delete From [dbo].[CustomerRoleMapping] Where [IsSystemMapping] = 1");
            }

            var roles = await roleQuery
                        .Where(x => x.Active && x.RuleSets.Any(y => y.IsActive))
                        .ToListAsync();

            using (var scope = new DbContextScope(ctx: _customerRoleMappingRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: false, autoCommit: false))
            {
                foreach (var role in roles)
                {
                    var ruleSetCustomerIds = new HashSet <int>();

                    ctx.SetProgress(++count, roles.Count, $"Add customer assignments for role \"{role.SystemName.NaIfEmpty()}\".");

                    // Execute active rule sets and collect customer ids.
                    foreach (var ruleSet in role.RuleSets.Where(x => x.IsActive))
                    {
                        if (ctx.CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        if (_ruleFactory.CreateExpressionGroup(ruleSet, _targetGroupService) is FilterExpression expression)
                        {
                            var filterResult = _targetGroupService.ProcessFilter(expression, 0, 500);
                            var resultPager  = new FastPager <Customer>(filterResult.SourceQuery, 500);

                            while (true)
                            {
                                var customerIds = await resultPager.ReadNextPageAsync(x => x.Id, x => x);

                                if (!(customerIds?.Any() ?? false))
                                {
                                    break;
                                }

                                ruleSetCustomerIds.AddRange(customerIds);
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetCustomerIds.Any())
                    {
                        foreach (var chunk in ruleSetCustomerIds.Slice(500))
                        {
                            if (ctx.CancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var customerId in chunk)
                            {
                                _customerRoleMappingRepository.Insert(new CustomerRoleMapping
                                {
                                    CustomerId      = customerId,
                                    CustomerRoleId  = role.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync();
                        }

                        try
                        {
                            _customerRoleMappingRepository.Context.DetachEntities <CustomerRoleMapping>();
                        }
                        catch { }
                    }
                }
            }

            if (numAdded > 0 || numDeleted > 0)
            {
                _cacheManager.RemoveByPattern(AclService.ACL_SEGMENT_PATTERN);
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} customer assignments for {roles.Count} roles.");
        }
Exemple #20
0
        public virtual async Task <Product> CloneProductAsync(
            Product product,
            string cloneName,
            bool isPublished,
            bool copyAssociatedProducts = true)
        {
            Guard.NotNull(product, nameof(product));
            Guard.NotEmpty(cloneName, nameof(cloneName));

            var localizedKeySelectors = new List <Expression <Func <Product, string> > >
            {
                x => x.Name,
                x => x.ShortDescription,
                x => x.FullDescription,
                x => x.MetaKeywords,
                x => x.MetaDescription,
                x => x.MetaTitle,
                x => x.BundleTitleText
            };

            var clone     = new Product();
            var utcNow    = DateTime.UtcNow;
            var languages = await _languageService.GetAllLanguagesAsync(true);

            int?sampleDownloadId = null;

            // Enable hooks for slugs cache invalidation.
            using (_chronometer.Step("Clone product " + product.Id))
                using (var scope = new DbContextScope(_db, autoDetectChanges: false, hooksEnabled: true, deferCommit: true, forceNoTracking: true))
                {
                    if (product.HasSampleDownload && product.SampleDownload != null)
                    {
                        var sampleDownloadClone = product.SampleDownload.Clone();
                        _db.Downloads.Add(sampleDownloadClone);

                        await scope.CommitAsync();

                        sampleDownloadId = sampleDownloadClone.Id;
                    }

                    var props = FastProperty.GetProperties(typeof(Product), PropertyCachingStrategy.EagerCached);

                    foreach (var prop in props.Values)
                    {
                        if (prop.IsComplexType)
                        {
                            continue;
                        }

                        if (!prop.IsPublicSettable)
                        {
                            continue;
                        }

                        prop.SetValue(clone, prop.GetValue(product));
                    }

                    clone.Id               = 0;
                    clone.Name             = cloneName;
                    clone.SampleDownloadId = sampleDownloadId;
                    clone.Published        = isPublished;
                    clone.CreatedOnUtc     = utcNow;
                    clone.UpdatedOnUtc     = utcNow;

                    // Category mappings.
                    clone.ProductCategories.AddRange(product.ProductCategories.Select(x => new ProductCategory
                    {
                        CategoryId        = x.CategoryId,
                        IsFeaturedProduct = x.IsFeaturedProduct,
                        DisplayOrder      = x.DisplayOrder
                    }));

                    // Manufacturer mappings.
                    clone.ProductManufacturers.AddRange(product.ProductManufacturers.Select(x => new ProductManufacturer
                    {
                        ManufacturerId    = x.ManufacturerId,
                        IsFeaturedProduct = x.IsFeaturedProduct,
                        DisplayOrder      = x.DisplayOrder
                    }));

                    // Media file mappings.
                    clone.ProductPictures.AddRange(product.ProductPictures.Select(x => new ProductMediaFile
                    {
                        MediaFileId  = x.MediaFileId,
                        DisplayOrder = x.DisplayOrder
                    }));
                    if (clone.MainPictureId == null)
                    {
                        clone.MainPictureId = product.ProductPictures.FirstOrDefault()?.MediaFileId;
                    }

                    // Product specification attributes.
                    clone.ProductSpecificationAttributes.AddRange(product.ProductSpecificationAttributes.Select(x => new ProductSpecificationAttribute
                    {
                        SpecificationAttributeOptionId = x.SpecificationAttributeOptionId,
                        AllowFiltering    = x.AllowFiltering,
                        ShowOnProductPage = x.ShowOnProductPage,
                        DisplayOrder      = x.DisplayOrder
                    }));

                    // Tier prices.
                    clone.TierPrices.AddRange(product.TierPrices.Select(x => new TierPrice
                    {
                        StoreId           = x.StoreId,
                        CustomerRoleId    = x.CustomerRoleId,
                        Quantity          = x.Quantity,
                        Price             = x.Price,
                        CalculationMethod = x.CalculationMethod
                    }));
                    clone.HasTierPrices = clone.TierPrices.Any();

                    // Discount mappings.
                    foreach (var discount in product.AppliedDiscounts)
                    {
                        clone.AppliedDiscounts.Add(discount);
                        clone.HasDiscountsApplied = true;
                    }

                    // Tags.
                    foreach (var tag in product.ProductTags)
                    {
                        clone.ProductTags.Add(tag);
                    }

                    // >>>>>>> Put clone to db (from here on we need the product clone's ID).
                    _db.Products.Add(clone);
                    await scope.CommitAsync();

                    // Store mappings.
                    var selectedStoreIds = await _storeMappingService.GetAuthorizedStoreIdsAsync(product);

                    selectedStoreIds.Each(id => _storeMappingService.AddStoreMapping(clone, id));

                    await ProcessPromotions(product, clone);

                    await ProcessSlugs(product, clone, languages);

                    await ProcessLocalizations(product, clone, localizedKeySelectors, languages);

                    await ProcessDownloads(product, clone);

                    // >>>>>>> Put to db.
                    await scope.CommitAsync();

                    await ProcessBundleItems(scope, product, clone, languages);

                    // Attributes and attribute combinations.
                    await ProcessAttributes(scope, product, clone, languages);

                    // Update computed properties.
                    clone.LowestAttributeCombinationPrice = await _db.ProductVariantAttributeCombinations
                                                            .ApplyLowestPriceFilter(clone.Id)
                                                            .Select(x => x.Price)
                                                            .FirstOrDefaultAsync();

                    // Associated products.
                    if (copyAssociatedProducts && product.ProductType != ProductType.BundledProduct)
                    {
                        await ProcessAssociatedProducts(product, clone, isPublished);
                    }

                    // >>>>>>> Our final commit.
                    await scope.CommitAsync();
                }

            return(clone);
        }
        protected virtual async Task <int> ProcessAvatarsAsync(ImportExecuteContext context, DbContextScope scope, IEnumerable <ImportRow <Customer> > batch)
        {
            foreach (var row in batch)
            {
                var urlOrPath = row.GetDataValue <string>("AvatarPictureUrl");
                if (urlOrPath.IsEmpty())
                {
                    continue;
                }

                var image = CreateDownloadItem(context, urlOrPath, 1);
                if (image == null)
                {
                    continue;
                }

                // Download avatar.
                if (image.Url.HasValue() && !image.Success)
                {
                    await context.DownloadManager.DownloadFilesAsync(new[] { image }, context.CancelToken);
                }

                if (FileDownloadSucceeded(image, context))
                {
                    using var stream = File.OpenRead(image.Path);

                    if (stream?.Length > 0)
                    {
                        var file = await _services.MediaService.GetFileByIdAsync(row.Entity.GenericAttributes.AvatarPictureId ?? 0, MediaLoadFlags.AsNoTracking);

                        if (file != null)
                        {
                            var isEqualData = await _services.MediaService.FindEqualFileAsync(stream, new[] { file.File }, true);

                            if (isEqualData.Success)
                            {
                                context.Result.AddInfo($"Found equal file for avatar '{image.FileName}'. Skipping file.", row.RowInfo, "AvatarPictureUrl");
                                continue;
                            }
                        }

                        // An avatar may not be assigned to several customers. A customer could otherwise delete the avatar of another.
                        // Overwriting is probably too dangerous here, because we could overwrite the avatar of another customer, so better rename.
                        var path           = _services.MediaService.CombinePaths(SystemAlbumProvider.Customers, image.FileName);
                        var saveFileResult = await _services.MediaService.SaveFileAsync(path, stream, false, DuplicateFileHandling.Rename);

                        if (saveFileResult.File.Id > 0)
                        {
                            SetGenericAttribute(SystemCustomerAttributeNames.AvatarPictureId, saveFileResult.File.Id, row);
                        }
                    }
                }
                else
                {
                    context.Result.AddInfo($"Download failed for avatar {image.Url}.", row.RowInfo, "AvatarPictureUrl");
                }
            }

            var num = await scope.CommitAsync(context.CancelToken);

            return(num);
        }
        public async Task ExecuteAsync(ImportExecuteContext context, CancellationToken cancelToken)
        {
            var currentStoreId = _services.StoreContext.CurrentStore.Id;
            var segmenter      = context.DataSegmenter;
            var batch          = segmenter.GetCurrentBatch <NewsletterSubscription>();

            using (var scope = new DbContextScope(_services.DbContext, autoDetectChanges: false, minHookImportance: HookImportance.Important, deferCommit: true))
            {
                await context.SetProgressAsync(segmenter.CurrentSegmentFirstRowIndex - 1, segmenter.TotalRows);

                foreach (var row in batch)
                {
                    try
                    {
                        NewsletterSubscription subscription = null;
                        var email   = row.GetDataValue <string>("Email");
                        var storeId = row.GetDataValue <int>("StoreId");

                        if (storeId == 0)
                        {
                            storeId = currentStoreId;
                        }

                        if (row.HasDataValue("Active") && row.TryGetDataValue("Active", out bool active))
                        {
                        }
                        else
                        {
                            active = true;  // Default.
                        }

                        if (email.IsEmpty())
                        {
                            context.Result.AddWarning("Skipped empty email address.", row.RowInfo, "Email");
                            continue;
                        }

                        if (email.Length > 255)
                        {
                            context.Result.AddWarning($"Skipped email address '{email}'. It exceeds the maximum allowed length of 255.", row.RowInfo, "Email");
                            continue;
                        }

                        if (!email.IsEmail())
                        {
                            context.Result.AddWarning($"Skipped invalid email address '{email}'.", row.RowInfo, "Email");
                            continue;
                        }

                        foreach (var keyName in context.KeyFieldNames)
                        {
                            switch (keyName)
                            {
                            case "Email":
                                subscription = await _services.DbContext.NewsletterSubscriptions
                                               .OrderBy(x => x.Id)
                                               .FirstOrDefaultAsync(x => x.Email == email && x.StoreId == storeId, cancelToken);

                                break;
                            }

                            if (subscription != null)
                            {
                                break;
                            }
                        }

                        if (subscription == null)
                        {
                            if (context.UpdateOnly)
                            {
                                ++context.Result.SkippedRecords;
                                continue;
                            }

                            subscription = new NewsletterSubscription
                            {
                                Active       = active,
                                CreatedOnUtc = context.UtcNow,
                                Email        = email,
                                NewsletterSubscriptionGuid = Guid.NewGuid(),
                                StoreId = storeId
                            };

                            _services.DbContext.NewsletterSubscriptions.Add(subscription);
                            context.Result.NewRecords++;
                        }
                        else
                        {
                            subscription.Active = active;
                            context.Result.ModifiedRecords++;
                        }
                    }
                    catch (Exception ex)
                    {
                        context.Result.AddError(ex.ToAllMessages(), row.RowInfo);
                    }
                }

                await scope.CommitAsync(cancelToken);
            }

            await _services.EventPublisher.PublishAsync(new ImportBatchExecutedEvent <NewsletterSubscription>(context, batch), cancelToken);
        }
Exemple #23
0
        public override async Task ExecuteAsync(TaskExecutionContext ctx)
        {
            var count         = 0;
            var numDeleted    = 0;
            var numAdded      = 0;
            var numCategories = 0;
            var pageSize      = 500;
            var pageIndex     = -1;

            var categoryIds = ctx.Parameters.ContainsKey("CategoryIds")
                ? ctx.Parameters["CategoryIds"].ToIntArray()
                : null;

            // Hooks are enabled because search index needs to be updated.
            using (var scope = new DbContextScope(ctx: _productCategoryRepository.Context, autoDetectChanges: false, validateOnSave: false, hooksEnabled: true, autoCommit: false))
            {
                // Delete existing system mappings.
                var deleteQuery = _productCategoryRepository.Table.Where(x => x.IsSystemMapping);
                if (categoryIds != null)
                {
                    deleteQuery = deleteQuery.Where(x => categoryIds.Contains(x.CategoryId));
                }

                var pager = new FastPager <ProductCategory>(deleteQuery, pageSize);

                while (pager.ReadNextPage(out var mappings))
                {
                    if (ctx.CancellationToken.IsCancellationRequested)
                    {
                        return;
                    }

                    if (mappings.Any())
                    {
                        _productCategoryRepository.DeleteRange(mappings);
                        numDeleted += await scope.CommitAsync();
                    }
                }

                try
                {
                    _productCategoryRepository.Context.DetachEntities <ProductCategory>();
                }
                catch { }

                // Insert new product category mappings.
                var categoryQuery = _categoryRepository.TableUntracked.Expand(x => x.RuleSets);
                if (categoryIds != null)
                {
                    categoryQuery = categoryQuery.Where(x => categoryIds.Contains(x.Id));
                }

                var categories = await categoryQuery
                                 .Where(x => x.Published && !x.Deleted && x.RuleSets.Any(y => y.IsActive))
                                 .ToListAsync();

                numCategories = categories.Count;

                foreach (var category in categories)
                {
                    var ruleSetProductIds = new HashSet <int>();

                    ctx.SetProgress(++count, categories.Count, $"Add product mappings for category \"{category.Name.NaIfEmpty()}\".");

                    // Execute active rule sets and collect product ids.
                    foreach (var ruleSet in category.RuleSets.Where(x => x.IsActive))
                    {
                        if (ctx.CancellationToken.IsCancellationRequested)
                        {
                            return;
                        }

                        if (_ruleFactory.CreateExpressionGroup(ruleSet, _productRuleProvider) is SearchFilterExpression expression)
                        {
                            pageIndex = -1;
                            while (true)
                            {
                                // Do not touch searchResult.Hits. We only need the product identifiers.
                                var searchResult = _productRuleProvider.Search(new SearchFilterExpression[] { expression }, ++pageIndex, pageSize);
                                ruleSetProductIds.AddRange(searchResult.HitsEntityIds);

                                if (pageIndex >= (searchResult.TotalHitsCount / pageSize))
                                {
                                    break;
                                }
                            }
                        }
                    }

                    // Add mappings.
                    if (ruleSetProductIds.Any())
                    {
                        foreach (var chunk in ruleSetProductIds.Slice(500))
                        {
                            if (ctx.CancellationToken.IsCancellationRequested)
                            {
                                return;
                            }

                            foreach (var productId in chunk)
                            {
                                _productCategoryRepository.Insert(new ProductCategory
                                {
                                    ProductId       = productId,
                                    CategoryId      = category.Id,
                                    IsSystemMapping = true
                                });

                                ++numAdded;
                            }

                            await scope.CommitAsync();
                        }

                        try
                        {
                            _productCategoryRepository.Context.DetachEntities <ProductCategory>();
                        }
                        catch { }
                    }
                }
            }

            Debug.WriteLineIf(numDeleted > 0 || numAdded > 0, $"Deleted {numDeleted} and added {numAdded} product mappings for {numCategories} categories.");
        }
Exemple #24
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();
        }