< Summary

Information
Class: Services.BitrixStockServices.BitrixStockSyncCleanupFbsWarehouse
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/BitrixStockServices/BitrixStockSyncCleanupPlanner.cs
Line coverage
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 132
Line coverage: 100%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
get_WarehouseId()100%11100%
get_WarehouseName()100%11100%
get_Present()100%11100%
get_ProductId()100%11100%
get_Sku()100%11100%

File(s)

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

#LineLine coverage
 1namespace Services.BitrixStockServices;
 2
 3/// <summary>Отбор складов для этапа 3: обнуление лишних FBS-складов Ozon.</summary>
 4public static class BitrixStockSyncCleanupPlanner
 5{
 6    public static IReadOnlySet<long> BuildProtectedWarehouseIds(
 7        IEnumerable<BitrixStockSyncCleanupReportLine> reportLines)
 8    {
 9        return reportLines
 10            .Where(l => l.EffectiveQty > 0 && l.OzonWarehouseId > 0)
 11            .Select(l => l.OzonWarehouseId)
 12            .ToHashSet();
 13    }
 14
 15    /// <summary>
 16    /// В отчёте есть остаток Ozon, не привязанный к складу (FbsNoWarehouse: склад не определён).
 17    /// Защищённые склады тогда неизвестны, и обнулять «лишние» нельзя — иначе очистка обнулит рабочие склады.
 18    /// </summary>
 19    public static bool HasUnresolvedWarehouseLines(IEnumerable<BitrixStockSyncCleanupReportLine> reportLines) =>
 20        reportLines.Any(l => l.EffectiveQty > 0 && l.OzonWarehouseId <= 0);
 21
 22    public static IReadOnlyList<BitrixStockSyncCleanupWarehouseTarget> SelectWarehousesToZero(
 23        IReadOnlySet<long> protectedWarehouseIds,
 24        IEnumerable<BitrixStockSyncCleanupFbsWarehouse> fbsWarehouses)
 25    {
 26        return fbsWarehouses
 27            .Where(w => w.Present > 0 && w.WarehouseId > 0 && !protectedWarehouseIds.Contains(w.WarehouseId))
 28            .OrderBy(w => w.WarehouseName, StringComparer.OrdinalIgnoreCase)
 29            .Select(w => new BitrixStockSyncCleanupWarehouseTarget
 30            {
 31                WarehouseId = w.WarehouseId,
 32                WarehouseName = w.WarehouseName,
 33                Present = w.Present,
 34                ProductId = w.ProductId,
 35                Sku = w.Sku
 36            })
 37            .ToList();
 38    }
 39
 40    public static bool IsCleanupCandidate(IReadOnlyList<BitrixStockSyncCleanupWarehouseTarget> toZero) =>
 41        toZero.Count > 0;
 42
 43    public static bool IsGroupPending(
 44        IReadOnlyList<BitrixStockSyncCleanupWarehouseTarget> toZero,
 45        IReadOnlyDictionary<long, string?> cleanupStatusByWarehouseId)
 46    {
 47        return toZero.Any(w =>
 48            !cleanupStatusByWarehouseId.TryGetValue(w.WarehouseId, out var status) ||
 49            !string.Equals(status, BitrixStockSyncCleanupStatuses.Success, StringComparison.OrdinalIgnoreCase));
 50    }
 51
 52    /// <summary>Парсинг Ozon warehouse id из строки (JS передаёт id &gt; 2^53-1 без потери точности).</summary>
 53    public static bool TryParseWarehouseId(string? raw, out long warehouseId)
 54    {
 55        warehouseId = 0;
 56        return long.TryParse(raw?.Trim(), out warehouseId) && warehouseId > 0;
 57    }
 58
 59    /// <summary>
 60    /// Максимальное число групп отчёта, сканируемых за один вызов превью (пагинация/защита от таймаута HTTP).
 61    /// </summary>
 62    public const int PreviewMaxGroupsPerRequest = 40;
 63
 64    /// <summary>
 65    /// Ограничивает выборку групп для этапа 3: в режиме dryRun (превью) сканируется не более
 66    /// <see cref="PreviewMaxGroupsPerRequest"/> групп за один HTTP-запрос, предотвращая перегрузку Ozon API
 67    /// и таймаут HTTP-запроса браузера/прокси.
 68    /// </summary>
 69    public static IEnumerable<T> SliceGroupsToScan<T>(
 70        IEnumerable<T> groups,
 71        int startOffset,
 72        bool dryRun,
 73        int maxGroupsPerRequest = PreviewMaxGroupsPerRequest)
 74    {
 75        var offset = Math.Max(0, startOffset);
 76        var query = groups.Skip(offset);
 77        if (dryRun && maxGroupsPerRequest > 0)
 78        {
 79            query = query.Take(maxGroupsPerRequest);
 80        }
 81        return query;
 82    }
 83
 84    /// <summary>
 85    /// Лимит артикулов для превью (пагинация). Полная очистка без фильтра articles — без лимита.
 86    /// </summary>
 87    public static int? ResolvePreviewMaxProducts(bool dryRun, int? maxProducts, bool hasArticleFilter, int previewDefaul
 88    {
 89        if (dryRun)
 90            return Math.Max(1, maxProducts ?? previewDefault);
 91        return hasArticleFilter ? maxProducts : null;
 92    }
 93}
 94
 95public static class BitrixStockSyncCleanupStatuses
 96{
 97    public const string Success = "Success";
 98    public const string Failed = "Failed";
 99    public const string Skipped = "Skipped";
 100}
 101
 102/// <summary>Строка отчёта, участвующая в планировании cleanup.</summary>
 103public sealed class BitrixStockSyncCleanupReportLine
 104{
 105    public long LineId { get; init; }
 106    public int OzonClientId { get; init; }
 107    public string Article { get; init; } = string.Empty;
 108    public string? OfferId { get; init; }
 109    public long? OzonProductId { get; init; }
 110    public long OzonWarehouseId { get; init; }
 111    public string OzonWarehouseName { get; init; } = string.Empty;
 112    public int EffectiveQty { get; init; }
 113    public string? OzonCleanupStatus { get; init; }
 114}
 115
 116public sealed class BitrixStockSyncCleanupFbsWarehouse
 117{
 43118    public long WarehouseId { get; init; }
 42119    public string WarehouseName { get; init; } = string.Empty;
 30120    public int Present { get; init; }
 11121    public long ProductId { get; init; }
 15122    public long Sku { get; init; }
 123}
 124
 125public sealed class BitrixStockSyncCleanupWarehouseTarget
 126{
 127    public long WarehouseId { get; init; }
 128    public string WarehouseName { get; init; } = string.Empty;
 129    public int Present { get; init; }
 130    public long ProductId { get; init; }
 131    public long Sku { get; init; }
 132}