< Summary

Class:SVETA.Api.Controllers.ShowcaseController
Assembly:SVETA.Api
File(s):/opt/dev/sveta_api_build/SVETA.Api/Controllers/ShowcaseController.cs
Covered lines:0
Uncovered lines:228
Coverable lines:228
Total lines:418
Line coverage:0% (0 of 228)
Covered branches:0
Total branches:90
Branch coverage:0% (0 of 90)

Metrics

MethodLine coverage Branch coverage
.ctor(...)0%100%
CheckDepartment()0%0%
GetGoods()0%0%
GetGood()0%0%
GetCategories()0%0%
BuildCategoryTree(...)0%0%
GetTopDiscountGoods()0%0%
DownloadPrices()0%0%

File(s)

/opt/dev/sveta_api_build/SVETA.Api/Controllers/ShowcaseController.cs

#LineLine coverage
 1using System;
 2using WinSolutions.Sveta.Common.Extensions;
 3using System.Collections.Concurrent;
 4using System.Collections.Generic;
 5using System.Collections.Immutable;
 6using System.Diagnostics;
 7using System.Linq;
 8using System.Threading.Tasks;
 9using Microsoft.AspNetCore.Http;
 10using Microsoft.AspNetCore.Mvc;
 11using Microsoft.AspNetCore.Authorization;
 12using Microsoft.EntityFrameworkCore;
 13using WinSolutions.Sveta.Server.Services.Interfaces;
 14using Microsoft.Extensions.Logging;
 15using Swashbuckle.AspNetCore.Annotations;
 16using Microsoft.Extensions.Options;
 17using SVETA.Api.Data.Domain;
 18using WinSolutions.Sveta.Server.Data.DataModel.Entities;
 19using SVETA.Api.Data.DTO.Showcase;
 20using SVETA.Api.Data.DTO;
 21using SVETA.Api.Data.DTO.Goods;
 22using SVETA.Api.Services.Interfaces;
 23using WinSolutions.Sveta.Common;
 24using WinSolutions.Sveta.Server.Data.DataModel.Extensions;
 25using WinSolutions.Sveta.Server.Data.DataModel.Kinds;
 26using System.IO;
 27
 28namespace SVETA.Api.Controllers
 29{
 30    /// <summary>
 31    /// Витрина
 32    /// </summary>
 33    [Route("api/v1/Showcase")]
 34    [ApiController]
 35    //[Authorize]
 36    public class ShowcaseController : SvetaController
 37    {
 38        const string _routeUrl = "api/v1/Showcase";
 39        readonly IShowcaseWorker _worker;
 40        readonly ILogger<ShowcaseController> _logger;
 41        private readonly ICategoryService _categoryService;
 42        private readonly IGoodService _goodService;
 43        private readonly IDepartmentService _departmentService;
 44        private readonly IAuthenticationService _authService;
 45        private readonly CommonSettings _commonSettings;
 46        private readonly AuthorizationSettings _authorizationSettings;
 47        IDiskStorageService _diskStorage;
 48
 49        public ShowcaseController(IShowcaseWorker worker, ILogger<ShowcaseController> logger, ICategoryService categoryS
 50            IGoodService goodService, IDepartmentService departmentService, IAuthenticationService authService, IDiskSto
 51            IOptions<CommonSettings> options,
 052            IOptions<AuthorizationSettings> authOptions) : base(logger)
 053        {
 054            _worker = worker;
 055            _logger = logger;
 056            _categoryService = categoryService;
 057            _goodService = goodService;
 058            _departmentService = departmentService;
 059            _authService = authService;
 060            _commonSettings = options.Value;
 061            _authorizationSettings = authOptions.Value;
 062            _diskStorage = diskStorage;
 063        }
 64
 65        async Task<Department> CheckDepartment(long departmentId)
 066        {
 067            var dep = _authService.IsUserPlatform()
 068                ? await _departmentService.GetDepartment(departmentId)
 069                : await _departmentService.GetDepartment(departmentId, _authService.ContragentId);
 070            if (dep == null)
 071            {
 072                throw new KeyNotFoundException("Не найден магазин");
 73            }
 074            if (dep.Cluster == null)
 075            {
 076                throw new ArgumentException("Не найден кластер");
 77            }
 078            if (dep.Cluster.Warehouse == null)
 079            {
 080                throw new ArgumentException("Не найден склад-владелец");
 81            }
 082            return dep;
 083        }
 84
 85        /// <summary>
 86        /// Возвращает товары для витрины
 87        /// </summary>
 88        /// <param name="categoryId">Id категории (0 для всех категорий)</param>
 89        /// <param name="departmentId">Id магазина, для которого будут возвращены товары с конкретными ценами под этот м
 90        /// <param name="showNA">возвращать ли товары, которых нет на остатке</param>
 91        /// <param name="page">пейджинг: номер страницы (любое значение ниже нуля изменится на 1)</param>
 92        /// <param name="limit">пейджинг: размер страницы (любое значение ниже нуля изменится на 10)</param>
 93        /// <param name="filter">фильтр по значимым полям (имя товара и штрихкод)</param>
 94        /// <param name="sort">сортировка по полям name,name|desc, brandName,brandName|desc, price,price|desc discount,d
 95        /// <param name="sidebarFilterJson">sidebar фильтр { "brands": [ "6" ], "country": [ "174" ], "price": { "price_
 96        /// <remarks>author: oboligatov</remarks>
 97        [HttpGet("FromCategory/{categoryId}/ForDepartment/{departmentId}")]
 98        [SwaggerResponse(200, "Успешно", typeof(BaseResponseDTO<ShowcaseListGoodDTO>))]
 99        [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))]
 100        public async Task<IActionResult> GetGoods(long categoryId, long departmentId, bool showNA = false, int page = 1,
 101            string sidebarFilterJson = null)
 0102        {
 0103            await CheckDepartment(departmentId);
 0104            filter = filter.NormalizeName();
 0105            page = page < 1 ? 1 : page;
 0106            limit = limit < 1 ? 10 : limit;
 0107            var goods = _worker.GetShowcaseGoods(categoryId > 0 ? categoryId : (long?)null, departmentId, showNA, page -
 0108            var totalCount = count;
 0109            var totalFiltredCount = filteredCount;
 110
 0111            var response = new BaseResponseDTO<ShowcaseListGoodDTO>(_routeUrl, page, limit, totalFiltredCount, totalCoun
 0112            {
 0113                Data = goods.Select(x => new ShowcaseListGoodDTO(x)).ToList()
 0114            };
 0115            return Ok(response);
 0116        }
 117
 118        /// <summary>
 119        /// Возвращает товар для витрины
 120        /// </summary>
 121        /// <param name="goodId">Id товара</param>
 122        /// <param name="departmentId">Id магазина, для которого будут возвращены товары с конкретными ценами под этот м
 123        /// <returns></returns>
 124        [HttpGet("Good/{goodId}/ForDepartment/{departmentId}")]
 125        [SwaggerResponse(200, "Успешно", typeof(ShowcaseGoodDTO))]
 126        [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))]
 127        //[Authorize]
 128        public async Task<IActionResult> GetGood(long goodId, long departmentId)
 0129        {
 0130            await CheckDepartment(departmentId);
 131
 0132            var good = await _worker.GetShowcaseGood(goodId, departmentId);
 0133            return Ok(good);
 0134        }
 135
 136        /// <summary>
 137        /// Получить категории по фильтру
 138        /// </summary>
 139        /// <param name="parentId">Идентифиатор родительской категории (0 - все категории)</param>
 140        /// <param name="departmentId">Id магазина, для которого будут возвращены товары с конкретными ценами под этот м
 141        [HttpGet("Categories/{parentId}/ForDepartment/{departmentId}")]
 142        [SwaggerResponse(200, "Успешно", typeof(List<CategoryResponseDTO>))]
 143        [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))]
 144        [Obsolete]
 145        //[AllowAnonymous]
 146        public async Task<IActionResult> GetCategories(long parentId, long departmentId)
 0147        {
 0148            var dep = await CheckDepartment(departmentId);
 0149            var result = new List<CategoryResponseDTO>();
 150
 0151            if((await _worker.GetContractRatio(dep)) == 0 || dep.Cluster.RatioForCalculations == 0)
 0152            {
 0153                return Ok(result);
 154            }
 155
 0156            var categories = await _categoryService.GetCategories(1, int.MaxValue, null, parentId == 0 ? -1 : parentId);
 157
 0158            var goodCategories = (await _worker.GetShowcaseGoodsBaseQuery(parentId == 0 ? (long?)null : parentId, depart
 0159                .Select(x => x.Category.Id)
 0160                .Distinct()
 0161                .ToList();
 162
 0163            var cats = new List<Category>();
 0164            foreach (var cat in categories)
 0165            {
 0166                var tree = _goodService.ExpandCategoryTree(new List<long>() { cat.Id });
 0167                tree.Add(cat.Id);
 168
 0169                if(tree.Intersect(goodCategories).Any())
 0170                {
 0171                    cats.Add(cat);
 0172                }
 0173            }
 174
 0175            result.AddRange(cats.Select(x => new CategoryResponseDTO(x)
 0176            {
 0177                Expandable = cats.Any(c => c.Parent != null && c.Parent.Id == x.Id)
 0178            }));
 179
 0180            return Ok(result);
 0181        }
 182
 183        /// <summary>
 184        /// Получить дерево категорий по фильтру
 185        /// </summary>
 186        /// <param name="parentId">Идентифиатор родительской категории (0 - все категории)</param>
 187        /// <param name="departmentId">Id магазина, для которого будут возвращены товары с конкретными ценами под этот м
 188        [HttpGet("CategoriesTree/{parentId}/ForDepartment/{departmentId}")]
 189        [SwaggerResponse(200, "Успешно", typeof(List<CategoryResponseDTO>))]
 190        [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))]
 191        //[AllowAnonymous]
 192        public async Task<IActionResult> GetCategoriesTree(long parentId, long departmentId)
 193        {
 194            var dep = await CheckDepartment(departmentId);
 195            var result = new List<CategoryResponseDTO>();
 196
 197            if ((await _worker.GetContractRatio(dep)) == 0 || dep.Cluster.RatioForCalculations == 0)
 198            {
 199                return Ok(result);
 200            }
 201
 202            var categories = await _categoryService.GetCategories(1, int.MaxValue, null, parentId == 0 ? -1 : parentId);
 203
 204            var goodCategories = (await _worker.GetShowcaseGoodsBaseQuery(parentId == 0 ? (long?)null : parentId, depart
 205                .Select(x => x.Category.Id)
 206                .Distinct()
 207                .ToList();
 208
 209            var cats = new List<Category>();
 210            foreach (var cat in categories)
 211            {
 212                var tree = _goodService.ExpandCategoryTree(new List<long>() { cat.Id });
 213                tree.Add(cat.Id);
 214
 215                if (tree.Intersect(goodCategories).Any())
 216                {
 217                    cats.Add(cat);
 218                }
 219            }
 220
 0221            result.AddRange(cats.Select(x => new CategoryResponseDTO(x)
 0222            {
 0223                Expandable = cats.Any(c => c.Parent != null && c.Parent.Id == x.Id)
 0224            }));
 225
 226            var result2 = new List<CategoryResponseDTO>();
 0227            result2.AddRange(result.Where(x => x.ParentId == 0));
 228            foreach (var cat in result2)
 229            {
 230                BuildCategoryTree(cat, result);
 231            }
 232            return Ok(result2);
 233        }
 234
 235        void BuildCategoryTree(CategoryResponseDTO parent, List<CategoryResponseDTO> flatList)
 0236        {
 0237            var children = flatList.Where(x => x.ParentCode == parent.Code);
 0238            parent.Children.AddRange(children);
 239
 0240            foreach(var child in children)
 0241            {
 0242                BuildCategoryTree(child, flatList);
 0243            }
 0244        }
 245
 246        /// <summary>
 247        /// Возвращаем топ 6 самых высоких скидок
 248        /// </summary>
 249        /// <returns></returns>
 250        [SwaggerResponse(200, "Успешно", typeof(TopGoodDiscountDto))]
 251        [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))]
 252        [HttpGet("TopDiscount")]
 253        public async Task<IActionResult> GetTopDiscountGoods()
 0254        {
 0255            _logger.LogInformation("Start return top discount goods");
 0256            Department dep =
 0257                (await _departmentService.GetDepartmentsByUserAndKind(_authService.UserId, DepartmentKind.Shop, default)
 0258                )?.First()
 0259                ?? throw new ArgumentException("Не найден магазин для расчета");
 0260            if (dep.Cluster == null || dep.Cluster.WarehouseId == 0)
 0261                throw new ArgumentException("Не найден склад для расчета");
 0262            var category = await _categoryService.GetCategories(0, 1, default);
 0263            int count = 0;
 0264            int filteredCount = 0;
 265            List<ShowcaseGoodDTO> goods;
 266            List<TopGoodDiscount> topGoods;
 0267            int goodsCount = await _goodService.GetGoodsCount(default, null, true);
 0268            goods = _worker.GetShowcaseGoods( (long?)null, dep.Id, false, 0, goodsCount, default, "discount|desc", null,
 0269            if (goods != null && goods.Count > 0)
 0270            {
 0271                topGoods = goods.Select(good => new TopGoodDiscount
 0272                {
 0273                    CurrentPrice = good.Price,
 0274                    OldPrice = good.OldPrice ?? 0,
 0275                    Name = good.Name,
 0276                    Img = good.Photos.FirstOrDefault() ?? new PhotoDTO(),
 0277                    Category = good.Category.Name,
 0278                    Discount = good.Discount ?? 0,
 0279                    Url = _commonSettings.BaseFrontUrl + $"/showcase/catalog?product={good.Id}&department=-1",
 0280                    LabelColor = good.LabelColor
 0281                }).ToList();
 0282            }
 283            else
 0284            {
 0285                goods = _worker.GetShowcaseGoods( (long?)null, dep.Id, false, 0, goodsCount, default, "discount|desc", n
 0286                var top = goods
 0287                    .OrderByDescending(good => good.Discount)
 0288                    .GroupBy(d => d.Category.Id);
 289
 0290                if (top.Count() >= 6)
 0291                {
 0292                    topGoods = top
 0293                        .Select(d => d.First())
 0294                        .Select(good => new TopGoodDiscount
 0295                        {
 0296                            CurrentPrice = good.Price,
 0297                            OldPrice = good.OldPrice ?? 0,
 0298                            Name = good.Name,
 0299                            Img = good.Photos.FirstOrDefault() ?? new PhotoDTO(),
 0300                            Category = good.Category.Name,
 0301                            Discount = good.Discount ?? 0,
 0302                            Url = _commonSettings.BaseFrontUrl + $"/showcase/catalog?product={good.Id}&department=-1",
 0303                            LabelColor = good.LabelColor
 0304                        })
 0305                        .Take(6)
 0306                        .ToList();
 0307                }
 308                else
 0309                {
 0310                    int countGroup = top.Count();
 0311                    topGoods = new List<TopGoodDiscount>();
 0312                    for (int i = 0; i < 6; i++)
 0313                    {
 0314                        for (int j = 0; j < countGroup; j++)
 0315                        {
 0316                            topGoods.Append(new TopGoodDiscount
 0317                            {
 0318                                Name = top.ElementAt(j).ElementAt(i).Name,
 0319                                Category = top.ElementAt(j).ElementAt(i).Category.Name,
 0320                                Img = top.ElementAt(j).ElementAt(i).Photos.FirstOrDefault() ?? new PhotoDTO(),
 0321                                CurrentPrice = top.ElementAt(j).ElementAt(i).Price,
 0322                                OldPrice = top.ElementAt(j).ElementAt(i).OldPrice ?? 0,
 0323                                Discount = top.ElementAt(j).ElementAt(i).Discount ?? 0,
 0324                                Url = _commonSettings.BaseFrontUrl+ $"/showcase/catalog?product={top.ElementAt(j).Elemen
 0325                                LabelColor = top.ElementAt(j).ElementAt(i).LabelColor
 0326                            });
 0327                        }
 0328                    }
 0329                }
 0330            }
 331
 0332            TopGoodDiscountDto result = new TopGoodDiscountDto
 0333            {
 0334                DiscountGoods = topGoods,
 0335                StsServer = _authorizationSettings.IdentityUrl,
 0336                BaseFronUrl = _commonSettings.BaseFrontUrl
 0337            };
 0338            _logger.LogInformation("Stop return top discount goods");
 0339            return Ok(result);
 0340        }
 341
 342        /// <summary>
 343        /// Выгрузка номенклатуры в excel
 344        /// </summary>
 345        /// <param name="activeOnly">выгружать только активные товары (иначе все)</param>
 346        /// <returns></returns>
 347        [HttpGet("DownloadPrices")]
 348        [SwaggerResponse(200, "Успешно")]
 349        [SwaggerResponse(500, "Ошибка на стороне сервера", typeof(ErrorDTO))]
 350        [Authorize(Roles = Role.SystemAdmin + "," + Role.SystemOperator)]
 351        public async Task<IActionResult> DownloadPrices(string departmentFilter = null)
 0352        {
 0353            if(string.IsNullOrWhiteSpace(departmentFilter))
 0354            {
 0355                throw new ArgumentException("Не задан фильтр по магазинам");
 356            }
 0357            departmentFilter = departmentFilter.NormalizeName().ToLower();
 358
 0359            var rows = new List<string[]>();
 0360            rows.Add(new string[]
 0361            {
 0362                "Дата выгрузки",
 0363                "Магазин-Получатель",
 0364                "ИД Товара",
 0365                "ШК товара",
 0366                "Артикул Товара",
 0367                "Название товара",
 0368                "Регулярная Цена товара",
 0369                "Акционная цена товара",
 0370                "Скидка",
 0371                "Количество на складе",
 0372                "Ярлыки товара",
 0373                "Наличие картинки",
 0374                "Название картинки"
 0375            });
 376
 0377            var downloadDate = DateTime.Today.ToString();
 0378            var departments = (await _departmentService.GetAllDepartments())
 0379                .Where(x => x.Name.ToLower().Contains(departmentFilter) && x.Cluster != null && !x.Cluster.IsDeleted)
 0380                .ToList();
 0381            foreach(var dep in departments)
 0382            {
 0383                var goods = _worker.GetShowcaseGoods((long?)null, dep.Id, true, 0, int.MaxValue, null, null, null, out i
 0384                goods.ForEach(x =>
 0385                {
 0386                    string pictureFileName = "";
 0387                    if (x.Photos.Any())
 0388                    {
 0389                        pictureFileName = x.Photos.First().FullSizeUrl;
 0390                        Uri uri = new Uri(pictureFileName);
 0391                        pictureFileName = Path.GetFileName(uri.LocalPath);
 0392                    }
 0393
 0394                    rows.Add(new string[]
 0395                    {
 0396                        downloadDate,
 0397                        dep.Name,
 0398                        x.UniqueCode,
 0399                        x.MainBarcode?.Code,
 0400                        x.VendorCode,
 0401                        x.Name,
 0402                        x.OldPrice?.ToString(),
 0403                        x.Price.ToString(),
 0404                        x.Discount?.ToString(),
 0405                        x.RestQuantity.ToString(),
 0406                        string.Join(',', x.Labels.Select(x => x.Name).ToArray()),
 0407                        _diskStorage.PictureExists(pictureFileName) ? "1" : "0",
 0408                        pictureFileName
 0409                    });
 0410                });
 0411            }
 412
 0413            var stream = CsvUtil.ToExcelStream(rows);
 0414            _diskStorage.SaveDownload("prices.xlsx", stream, out string fileName);
 0415            return File(stream, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", Path.GetFileName(fi
 0416        }
 417    }
 418}