diff --git a/VirtoCommerce.CatalogModule.Web.Core/Model/SearchCriteria.cs b/VirtoCommerce.CatalogModule.Web.Core/Model/SearchCriteria.cs index acf55ce5b..200f6b8f3 100644 --- a/VirtoCommerce.CatalogModule.Web.Core/Model/SearchCriteria.cs +++ b/VirtoCommerce.CatalogModule.Web.Core/Model/SearchCriteria.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using VirtoCommerce.Domain.Catalog.Model; namespace VirtoCommerce.CatalogModule.Web.Model @@ -73,7 +69,7 @@ public SearchCriteria() public DateTime? IndexDate { get; set; } public string PricelistId { get; set; } - + public string[] PricelistIds { get; set; } /// @@ -123,6 +119,8 @@ public SearchCriteria() public string[] VendorIds { get; set; } public DateTime? StartDateFrom { get; set; } - + + public string[] ObjectIds { get; set; } + } } diff --git a/VirtoCommerce.CatalogModule.Web/Controllers/Api/CatalogModuleListEntryController.cs b/VirtoCommerce.CatalogModule.Web/Controllers/Api/CatalogModuleListEntryController.cs index 1e4a9a7f8..497d99424 100644 --- a/VirtoCommerce.CatalogModule.Web/Controllers/Api/CatalogModuleListEntryController.cs +++ b/VirtoCommerce.CatalogModule.Web/Controllers/Api/CatalogModuleListEntryController.cs @@ -6,13 +6,14 @@ using System.Web.Http.Description; using VirtoCommerce.CatalogModule.Web.Converters; using VirtoCommerce.CatalogModule.Web.Security; +using VirtoCommerce.CatalogModule.Web.Services; using VirtoCommerce.Domain.Catalog.Services; +using VirtoCommerce.Domain.Search; using VirtoCommerce.Platform.Core.Assets; using VirtoCommerce.Platform.Core.Common; using VirtoCommerce.Platform.Core.Security; using coreModel = VirtoCommerce.Domain.Catalog.Model; using webModel = VirtoCommerce.CatalogModule.Web.Model; -using VirtoCommerce.CatalogModule.Web.Services; namespace VirtoCommerce.CatalogModule.Web.Controllers.Api { @@ -27,6 +28,8 @@ public class CatalogModuleListEntryController : CatalogBaseController private readonly ListEntryMover _categoryMover; private readonly ListEntryMover _productMover; + private const int DeleteBatchSize = 50; + public CatalogModuleListEntryController( ICatalogSearchService searchService, ICategoryService categoryService, @@ -59,19 +62,8 @@ public CatalogModuleListEntryController( [ResponseType(typeof(webModel.ListEntrySearchResult))] public IHttpActionResult ListItemsSearch(webModel.SearchCriteria criteria) { - var coreModelCriteria = criteria.ToCoreModel(); - ApplyRestrictionsForCurrentUser(coreModelCriteria); - - coreModelCriteria.WithHidden = true; - - // need search in children categories if user specify keyword - if (!string.IsNullOrEmpty(coreModelCriteria.Keyword)) - { - coreModelCriteria.SearchInChildren = true; - coreModelCriteria.SearchInVariations = true; - } - var result = _listEntrySearchService.Search(coreModelCriteria); + var result = SearchListEntries(criteria); return Ok(result); } @@ -217,6 +209,100 @@ public IHttpActionResult Move(webModel.MoveInfo moveInfo) return Ok(); } + /// + /// Bulk delete by the search criteria. + /// + /// + [HttpPost] + [Route("delete")] + [ResponseType(typeof(void))] + public IHttpActionResult Delete(webModel.SearchCriteria searchCriteria) + { + var idsToDelete = searchCriteria.ObjectIds?.ToList() ?? new List(); + var productIds = new List(); + var categoryIds = new List(); + + if (idsToDelete.IsNullOrEmpty()) + { + idsToDelete = GetIdsToDelete(searchCriteria); + } + + if (!idsToDelete.IsNullOrEmpty()) + { + idsToDelete.ProcessWithPaging(DeleteBatchSize, (ids, currentItem, totalCount) => + { + var commonIds = ids.ToArray(); + var searchProductResult = _itemService.GetByIds(commonIds, coreModel.ItemResponseGroup.None); + CheckCurrentUserHasPermissionForObjects(CatalogPredefinedPermissions.Delete, searchProductResult); + productIds.AddRange(searchProductResult.Select(x => x.Id)); + + var searchCategoryResult = _categoryService.GetByIds(commonIds.ToArray(), coreModel.CategoryResponseGroup.None); + CheckCurrentUserHasPermissionForObjects(CatalogPredefinedPermissions.Delete, searchCategoryResult); + categoryIds.AddRange(searchCategoryResult.Select(x => x.Id)); + }); + + productIds.ProcessWithPaging(DeleteBatchSize, (ids, currentItem, totalCount) => + { + _itemService.Delete(ids.ToArray()); + }); + + categoryIds.ProcessWithPaging(DeleteBatchSize, (ids, currentItem, totalCount) => + { + _categoryService.Delete(ids.ToArray()); + }); + } + + return StatusCode(HttpStatusCode.NoContent); + } + + + private webModel.ListEntrySearchResult SearchListEntries(webModel.SearchCriteria criteria) + { + var coreModelCriteria = criteria.ToCoreModel(); + ApplyRestrictionsForCurrentUser(coreModelCriteria); + + coreModelCriteria.WithHidden = true; + + // need search in children categories if user specify keyword + if (!string.IsNullOrEmpty(coreModelCriteria.Keyword)) + { + coreModelCriteria.SearchInChildren = true; + coreModelCriteria.SearchInVariations = true; + } + + return _listEntrySearchService.Search(coreModelCriteria); + + } + private List GetIdsToDelete(webModel.SearchCriteria searchCriteria) + { + // Any pagination for deleting should be managed at back-end. + searchCriteria.Take = DeleteBatchSize; + searchCriteria.Skip = 0; + + var itemIds = new List(); + bool hasItems; + + do + { + var searchResult = SearchListEntries(searchCriteria); + var listEntriesIds = searchResult.ListEntries + .Where(x => x.Type.EqualsInvariant(KnownDocumentTypes.Product) || x.Type.EqualsInvariant(KnownDocumentTypes.Category)) + .Select(x => x.Id) + .ToArray(); + + hasItems = !listEntriesIds.IsNullOrEmpty(); + + if (hasItems) + { + itemIds.AddRange(listEntriesIds); + searchCriteria.Skip += searchCriteria.Take; + } + } + while (hasItems); + + return itemIds; + } + private void InnerUpdateLinks(webModel.ListEntryLink[] links, Action action) { var changedObjects = new List(); diff --git a/VirtoCommerce.CatalogModule.Web/Scripts/resources/items.js b/VirtoCommerce.CatalogModule.Web/Scripts/resources/items.js index cb0b7fdf9..d54928bb8 100644 --- a/VirtoCommerce.CatalogModule.Web/Scripts/resources/items.js +++ b/VirtoCommerce.CatalogModule.Web/Scripts/resources/items.js @@ -1,4 +1,4 @@ -angular.module('virtoCommerce.catalogModule') +angular.module('virtoCommerce.catalogModule') .factory('virtoCommerce.catalogModule.items', ['$resource', function ($resource) { return $resource('api/catalog/products/:id', null, { remove: { method: 'DELETE', url: 'api/catalog/products' }, @@ -9,4 +9,4 @@ update: { method: 'POST' }, plenty: { method: 'POST', url: 'api/catalog/products/plenty', isArray: true } }); -}]); \ No newline at end of file +}]); diff --git a/VirtoCommerce.CatalogModule.Web/Scripts/resources/listEntries.js b/VirtoCommerce.CatalogModule.Web/Scripts/resources/listEntries.js index 8fd5df32a..5e3b6322f 100644 --- a/VirtoCommerce.CatalogModule.Web/Scripts/resources/listEntries.js +++ b/VirtoCommerce.CatalogModule.Web/Scripts/resources/listEntries.js @@ -6,7 +6,8 @@ angular.module('virtoCommerce.catalogModule') createlinks: { method: 'POST', url: 'api/catalog/listentrylinks' }, bulkcreatelinks: { method: 'POST', url: 'api/catalog/listentrylinks/bulkcreate' }, deletelinks: { method: 'POST', url: 'api/catalog/listentrylinks/delete' }, - move: { method: 'POST', url: 'api/catalog/listentries/move' } + move: { method: 'POST', url: 'api/catalog/listentries/move' }, + delete: { method: 'POST', url: 'api/catalog/listentries/delete' } }); }]);