< Summary

Information
Class: Services.BitrixStockServices.WarehouseStockBitrixViewService
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/BitrixStockServices/WarehouseStockBitrixViewService.cs
Line coverage
75%
Covered lines: 129
Uncovered lines: 42
Coverable lines: 171
Total lines: 244
Line coverage: 75.4%
Branch coverage
42%
Covered branches: 37
Total branches: 88
Branch coverage: 42%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor(...)100%11100%
GetActiveStoresAsync()100%210%
GetRemainingStockAsync()53.33%663065.88%
ResolveSelectedStores(...)33.33%14640%
BuildStores(...)100%11100%
ApplySorting(...)35.41%6604835.71%
get_ProductId()100%11100%
get_Article()100%11100%
get_PreviewText()100%11100%
get_Supplier()100%11100%
get_TimestampX()100%11100%
get_M1Qty()100%11100%
get_M2Qty()100%11100%
get_M3Qty()100%11100%
get_B1Qty()100%11100%
get_B2Qty()100%11100%
get_Price()100%11100%
get_UnitCost()100%11100%
get_C0()100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/BitrixStockServices/WarehouseStockBitrixViewService.cs

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using OzonDomains.Models.BitrixModels;
 3using OzonOrdersWeb.Areas.Studio2.ViewModels.Bitrix;
 4using OzonRepositories.Context;
 5
 6namespace Services.BitrixStockServices;
 7
 8public interface IWarehouseStockBitrixViewService
 9{
 10    Task<List<string>> GetActiveStoresAsync();
 11    Task<PagedResult<RemainingStockBitrix>> GetRemainingStockAsync(RemainingStockFilter filter);
 12}
 13
 14public class WarehouseStockBitrixViewService : IWarehouseStockBitrixViewService
 15{
 116    private static readonly IReadOnlyList<(string Code, int Id)> StoreMap = new List<(string Code, int Id)>
 117    {
 118        ("B1", 1),
 119        ("B2", 2),
 120        ("M1", 5),
 121        ("M2", 3),
 122        ("M3", 4)
 123    };
 24
 25    private readonly ApplicationDbContext _context;
 26
 127    public WarehouseStockBitrixViewService(ApplicationDbContext context)
 28    {
 129        _context = context;
 130    }
 31
 32    public Task<List<string>> GetActiveStoresAsync()
 33    {
 034        return Task.FromResult(StoreMap.Select(x => x.Code).ToList());
 35    }
 36
 37    public async Task<PagedResult<RemainingStockBitrix>> GetRemainingStockAsync(RemainingStockFilter filter)
 38    {
 139        var baseQuery = _context.WarehouseStockSnapshots.AsNoTracking().AsQueryable();
 40
 141        if (!string.IsNullOrWhiteSpace(filter.Article))
 42        {
 043            var article = filter.Article.Trim();
 044            baseQuery = baseQuery.Where(x => x.Article != null && x.Article.Contains(article));
 45        }
 46
 147        if (!string.IsNullOrWhiteSpace(filter.PreviewText))
 48        {
 049            var preview = filter.PreviewText.Trim();
 050            baseQuery = baseQuery.Where(x => x.ProductName != null && x.ProductName.Contains(preview));
 51        }
 52
 153        if (!string.IsNullOrWhiteSpace(filter.Supplier))
 54        {
 055            var supplier = filter.Supplier.Trim();
 056            baseQuery = baseQuery.Where(x => x.Manufacturer != null && x.Manufacturer.Contains(supplier));
 57        }
 58
 159        if (filter.DateFrom.HasValue)
 060            baseQuery = baseQuery.Where(x => x.SyncedAtUtc >= filter.DateFrom.Value);
 161        if (filter.DateTo.HasValue)
 062            baseQuery = baseQuery.Where(x => x.SyncedAtUtc < filter.DateTo.Value.Date.AddDays(1));
 63
 164        var groupedQuery = baseQuery
 165            .Where(x => x.Article != null && x.Article != string.Empty)
 166            .GroupBy(x => x.Article!)
 167            .Select(g => new SnapshotAggregateRow
 168            {
 169                Article = g.Key,
 170                ProductId = g.Max(x => x.SourceRowNumber),
 171                PreviewText = g.Max(x => x.ProductName),
 172                Supplier = g.Max(x => x.Manufacturer),
 173                TimestampX = g.Max(x => x.SyncedAtUtc),
 174                M1Qty = g.Sum(x => x.M1Qty ?? 0m),
 175                M2Qty = g.Sum(x => x.M2Qty ?? 0m),
 176                M3Qty = g.Sum(x => x.M3Qty ?? 0m),
 177                B1Qty = g.Sum(x => x.B1Qty ?? 0m),
 178                B2Qty = g.Sum(x => x.B2Qty ?? 0m),
 179                Price = g.Average(x => x.AccountingPriceUnit ?? 0m),
 180                UnitCost = g.Average(x => x.UnitCost ?? 0m),
 181                C0 = g.Max(x => x.C0)
 182            });
 83
 184        if (filter.ProductId.HasValue)
 085            groupedQuery = groupedQuery.Where(x => x.ProductId == filter.ProductId.Value);
 86
 187        if (filter.QuantityFrom.HasValue)
 088            groupedQuery = groupedQuery.Where(x => x.M1Qty + x.M2Qty + x.M3Qty + x.B1Qty + x.B2Qty >= filter.QuantityFro
 189        if (filter.QuantityTo.HasValue)
 090            groupedQuery = groupedQuery.Where(x => x.M1Qty + x.M2Qty + x.M3Qty + x.B1Qty + x.B2Qty <= filter.QuantityTo.
 91
 192        if (filter.PriceValueFrom.HasValue)
 093            groupedQuery = groupedQuery.Where(x => x.Price >= filter.PriceValueFrom.Value);
 194        if (filter.PriceValueTo.HasValue)
 095            groupedQuery = groupedQuery.Where(x => x.Price <= filter.PriceValueTo.Value);
 96
 197        if (filter.RegistrationPriceFrom.HasValue)
 098            groupedQuery = groupedQuery.Where(x => x.UnitCost >= filter.RegistrationPriceFrom.Value);
 199        if (filter.RegistrationPriceTo.HasValue)
 0100            groupedQuery = groupedQuery.Where(x => x.UnitCost <= filter.RegistrationPriceTo.Value);
 101
 1102        var selectedStores = ResolveSelectedStores(filter);
 1103        var hasM1 = selectedStores.Contains("M1", StringComparer.OrdinalIgnoreCase);
 1104        var hasM2 = selectedStores.Contains("M2", StringComparer.OrdinalIgnoreCase);
 1105        var hasM3 = selectedStores.Contains("M3", StringComparer.OrdinalIgnoreCase);
 1106        var hasB1 = selectedStores.Contains("B1", StringComparer.OrdinalIgnoreCase);
 1107        var hasB2 = selectedStores.Contains("B2", StringComparer.OrdinalIgnoreCase);
 1108        if (filter.AmountFrom.HasValue)
 109        {
 0110            var amountFrom = (decimal)filter.AmountFrom.Value;
 0111            groupedQuery = groupedQuery.Where(x =>
 0112                (hasM1 && x.M1Qty >= amountFrom) ||
 0113                (hasM2 && x.M2Qty >= amountFrom) ||
 0114                (hasM3 && x.M3Qty >= amountFrom) ||
 0115                (hasB1 && x.B1Qty >= amountFrom) ||
 0116                (hasB2 && x.B2Qty >= amountFrom));
 117        }
 1118        if (filter.AmountTo.HasValue)
 119        {
 0120            var amountTo = (decimal)filter.AmountTo.Value;
 0121            groupedQuery = groupedQuery.Where(x =>
 0122                (hasM1 && x.M1Qty <= amountTo) ||
 0123                (hasM2 && x.M2Qty <= amountTo) ||
 0124                (hasM3 && x.M3Qty <= amountTo) ||
 0125                (hasB1 && x.B1Qty <= amountTo) ||
 0126                (hasB2 && x.B2Qty <= amountTo));
 127        }
 128
 1129        groupedQuery = ApplySorting(groupedQuery, filter.SortColumn, filter.SortDirection);
 130
 1131        var totalCount = await groupedQuery.CountAsync();
 1132        if (!filter.DisablePagination)
 1133            groupedQuery = groupedQuery.Skip((filter.Page - 1) * filter.PageSize).Take(filter.PageSize);
 134
 1135        var rows = await groupedQuery.ToListAsync();
 1136        var items = rows.Select(x =>
 1137        {
 1138            var quantity = (double)(x.M1Qty + x.M2Qty + x.M3Qty + x.B1Qty + x.B2Qty);
 1139            return new RemainingStockBitrix
 1140            {
 1141                ProductId = x.ProductId,
 1142                Article = x.Article,
 1143                PreviewText = x.PreviewText,
 1144                Active = "Y",
 1145                Available = quantity > 0 ? "Y" : "N",
 1146                Supplier = x.Supplier,
 1147                TimestampX = x.TimestampX,
 1148                Quantity = quantity,
 1149                Price = (double)x.Price,
 1150                C0 = x.C0,
 1151                PriceHistory = new OzonRepositories.Data.Bitrix.PriceHistory
 1152                {
 1153                    BitrixId = x.ProductId,
 1154                    Article = x.Article ?? string.Empty,
 1155                    CreateDateTime = x.TimestampX,
 1156                    PriceValue = x.UnitCost,
 1157                    RegistrationPrice = x.UnitCost,
 1158                    ValueSource = "WarehouseStockSnapshot",
 1159                    Author = "WarehouseStock"
 1160                },
 1161                Stores = BuildStores(x, selectedStores)
 1162            };
 1163        }).ToList();
 164
 1165        return new PagedResult<RemainingStockBitrix>
 1166        {
 1167            Items = items,
 1168            TotalCount = totalCount,
 1169            Page = filter.Page,
 1170            PageSize = filter.PageSize
 1171        };
 1172    }
 173
 174    private static List<string> ResolveSelectedStores(RemainingStockFilter filter)
 175    {
 1176        if (filter.SelectedStoreTitles is { Count: > 0 })
 3177            return filter.SelectedStoreTitles.Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()).ToList();
 178
 0179        if (!string.IsNullOrWhiteSpace(filter.StoreTitle))
 0180            return StoreMap.Where(x => x.Code.Contains(filter.StoreTitle.Trim(), StringComparison.OrdinalIgnoreCase)).Se
 181
 0182        return StoreMap.Select(x => x.Code).ToList();
 183    }
 184
 185    private static List<StockByStore> BuildStores(SnapshotAggregateRow row, IReadOnlyCollection<string> selectedStores)
 186    {
 1187        var values = new Dictionary<string, double>(StringComparer.OrdinalIgnoreCase)
 1188        {
 1189            ["M1"] = (double)row.M1Qty,
 1190            ["M2"] = (double)row.M2Qty,
 1191            ["M3"] = (double)row.M3Qty,
 1192            ["B1"] = (double)row.B1Qty,
 1193            ["B2"] = (double)row.B2Qty
 1194        };
 195
 1196        return StoreMap
 5197            .Where(s => selectedStores.Contains(s.Code, StringComparer.OrdinalIgnoreCase))
 1198            .Select(s => new StockByStore
 1199            {
 1200                StoreId = s.Id,
 1201                Title = s.Code,
 1202                Amount = values.GetValueOrDefault(s.Code, 0d)
 1203            })
 1204            .ToList();
 205    }
 206
 207    private static IQueryable<SnapshotAggregateRow> ApplySorting(
 208        IQueryable<SnapshotAggregateRow> items,
 209        string? sortColumn,
 210        string? sortDirection)
 211    {
 1212        var desc = string.Equals(sortDirection, "desc", StringComparison.OrdinalIgnoreCase);
 1213        return (sortColumn ?? string.Empty).ToLowerInvariant() switch
 1214        {
 0215            "article" => desc ? items.OrderByDescending(x => x.Article) : items.OrderBy(x => x.Article),
 0216            "previewtext" => desc ? items.OrderByDescending(x => x.PreviewText) : items.OrderBy(x => x.PreviewText),
 0217            "supplier" => desc ? items.OrderByDescending(x => x.Supplier) : items.OrderBy(x => x.Supplier),
 0218            "quantity" => desc
 0219                ? items.OrderByDescending(x => x.M1Qty + x.M2Qty + x.M3Qty + x.B1Qty + x.B2Qty)
 0220                : items.OrderBy(x => x.M1Qty + x.M2Qty + x.M3Qty + x.B1Qty + x.B2Qty),
 0221            "price" => desc ? items.OrderByDescending(x => x.Price) : items.OrderBy(x => x.Price),
 0222            "registrationprice" => desc ? items.OrderByDescending(x => x.UnitCost) : items.OrderBy(x => x.UnitCost),
 0223            "timestampx" => desc ? items.OrderByDescending(x => x.TimestampX) : items.OrderBy(x => x.TimestampX),
 1224            _ => desc ? items.OrderByDescending(x => x.ProductId) : items.OrderBy(x => x.ProductId)
 1225        };
 226    }
 227
 228    private sealed class SnapshotAggregateRow
 229    {
 3230        public int ProductId { get; set; }
 3231        public string? Article { get; set; }
 2232        public string? PreviewText { get; set; }
 2233        public string? Supplier { get; set; }
 3234        public DateTime TimestampX { get; set; }
 3235        public decimal M1Qty { get; set; }
 3236        public decimal M2Qty { get; set; }
 3237        public decimal M3Qty { get; set; }
 3238        public decimal B1Qty { get; set; }
 3239        public decimal B2Qty { get; set; }
 2240        public decimal Price { get; set; }
 3241        public decimal UnitCost { get; set; }
 2242        public decimal? C0 { get; set; }
 243    }
 244}