< Summary

Information
Class: Services.BitrixStockServices.BitrixStockSyncCleanupLineWriter
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/BitrixStockServices/BitrixStockSyncCleanupLineWriter.cs
Line coverage
0%
Covered lines: 0
Uncovered lines: 140
Coverable lines: 140
Total lines: 210
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 80
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
UpsertCleanupWarehouseAsync()0%2352480%
BuildCleanupNote(...)0%110100%
LoadSiblingTemplateAsync()0%420200%
AddParam(...)0%620%
get_ProductId()100%210%
get_OfferId()100%210%
get_OzonProductId()100%210%
get_OzonClientName()100%210%
get_OneCPrice()100%210%
get_PrevOneCPrice()100%210%
get_OzonWarehouseName()100%210%

File(s)

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

#LineLine coverage
 1using Microsoft.EntityFrameworkCore;
 2using OzonRepositories.Context;
 3
 4namespace Services.BitrixStockServices;
 5
 6/// <summary>Запись обнулений этапа 3 как отдельных строк отчёта (артикул + клиент + склад).</summary>
 7public static class BitrixStockSyncCleanupLineWriter
 8{
 9    public static async Task UpsertCleanupWarehouseAsync(
 10        ApplicationDbContext db,
 11        string reportId,
 12        int clientId,
 13        string? article,
 14        long warehouseId,
 15        string? warehouseName,
 16        bool success,
 17        string? note,
 18        int qtyBefore,
 19        string? offerId,
 20        long? productId,
 21        CancellationToken cancellationToken)
 22    {
 023        reportId = reportId.Trim();
 024        article = (article ?? string.Empty).Trim();
 025        var status = success ? BitrixStockSyncCleanupStatuses.Success : BitrixStockSyncCleanupStatuses.Failed;
 026        var isProtectedMark = note?.Contains("Сохранён", StringComparison.OrdinalIgnoreCase) == true;
 27
 028        if (isProtectedMark)
 29        {
 030            await db.Database.ExecuteSqlRawAsync(
 031                @"UPDATE BitrixStockSyncReportLines
 032SET OzonCleanupStatus = {4}, OzonCleanupAtUtc = UTC_TIMESTAMP(6), OzonCleanupError = {5}
 033WHERE ReportId = {0} AND OzonClientId = {1} AND Article = {2} AND OzonWarehouseId = {3}",
 034                EfSqlRawParameterGuard.Materialize(reportId, clientId, article, warehouseId, status, note),
 035                cancellationToken);
 036            return;
 37        }
 38
 039        var cleanupNote = BuildCleanupNote(note, qtyBefore, warehouseName, success);
 040        var updated = await db.Database.ExecuteSqlRawAsync(
 041            @"UPDATE BitrixStockSyncReportLines
 042SET OzonCleanupStatus = {4}, OzonCleanupAtUtc = UTC_TIMESTAMP(6), OzonCleanupError = {5},
 043    OzonQtyAfter = {6}, OzonQty = {7}, Delta = {8},
 044    Action = {9}, ActionLabel = {10}, ActionIcon = {11}, RowClass = {12},
 045    MappingDescription = {13}, Note = {14}
 046WHERE ReportId = {0} AND OzonClientId = {1} AND Article = {2} AND OzonWarehouseId = {3}
 047  AND Action = {9}",
 048            EfSqlRawParameterGuard.Materialize(
 049                reportId, clientId, article, warehouseId, status, cleanupNote,
 050                success ? 0 : (object?)null,
 051                qtyBefore,
 052                success ? -qtyBefore : 0,
 053                BitrixStockSyncReportActions.CleanupExtra,
 054                CleanupExtraLabels.ActionLabel,
 055                CleanupExtraLabels.ActionIcon,
 056                CleanupExtraLabels.RowClass,
 057                CleanupExtraLabels.MappingDescription,
 058                cleanupNote),
 059            cancellationToken);
 60
 061        if (updated > 0)
 062            return;
 63
 064        var template = await LoadSiblingTemplateAsync(db, reportId, clientId, article, cancellationToken);
 065        var whName = string.IsNullOrWhiteSpace(warehouseName)
 066            ? (template?.OzonWarehouseName ?? $"#{warehouseId}")
 067            : warehouseName.Trim();
 068        var resolvedOfferId = !string.IsNullOrWhiteSpace(offerId) ? offerId.Trim() : template?.OfferId;
 069        var resolvedProductId = productId is > 0 ? productId : template?.OzonProductId;
 070        var clientName = template?.OzonClientName ?? $"ID {clientId}";
 71
 072        await db.Database.ExecuteSqlRawAsync(
 073            @"INSERT INTO BitrixStockSyncReportLines
 074(ReportId, ProductId, Article, OfferId, OneCStore, OzonWarehouseName, OzonWarehouseId, MappingDescription,
 075 OneCQty, OzonQty, Delta, Action, ActionLabel, ActionIcon, RowClass, Note, OzonProductId,
 076 OzonApplyStatus, OzonApplyError, OzonQtyAfter,
 077 OzonCleanupStatus, OzonCleanupAtUtc, OzonCleanupError,
 078 IsArchived, IsAutoArchived, OzonClientId, OzonClientName, OneCPrice, PrevOneCPrice, CreatedAtUtc)
 079VALUES
 080({0}, {1}, {2}, {3}, {4}, {5}, {6}, {7},
 081 0, {8}, {9}, {10}, {11}, {12}, {13}, {14}, {15},
 082 {16}, {17}, {18},
 083 {19}, UTC_TIMESTAMP(6), {20},
 084 0, 0, {21}, {22}, {23}, {24}, UTC_TIMESTAMP(6))",
 085            EfSqlRawParameterGuard.Materialize(
 086                reportId,
 087                template?.ProductId ?? 0,
 088                article,
 089                resolvedOfferId,
 090                CleanupExtraLabels.OneCStore,
 091                whName,
 092                warehouseId,
 093                CleanupExtraLabels.MappingDescription,
 094                qtyBefore,
 095                success ? -qtyBefore : 0,
 096                BitrixStockSyncReportActions.CleanupExtra,
 097                CleanupExtraLabels.ActionLabel,
 098                CleanupExtraLabels.ActionIcon,
 099                CleanupExtraLabels.RowClass,
 0100                cleanupNote,
 0101                resolvedProductId,
 0102                success ? BitrixStockSyncReportApplyStatuses.Success : null,
 0103                success ? CleanupExtraLabels.ApplyNote : null,
 0104                success ? 0 : null,
 0105                status,
 0106                cleanupNote,
 0107                clientId,
 0108                clientName,
 0109                template?.OneCPrice,
 0110                template?.PrevOneCPrice),
 0111            cancellationToken);
 112
 0113        await db.Database.ExecuteSqlRawAsync(
 0114            @"UPDATE BitrixStockSyncReports
 0115SET WarehouseLinesTotal = COALESCE(WarehouseLinesTotal, 0) + 1
 0116WHERE ReportId = {0}",
 0117            EfSqlRawParameterGuard.Materialize(reportId),
 0118            cancellationToken);
 0119    }
 120
 121    private static string BuildCleanupNote(string? note, int qtyBefore, string? warehouseName, bool success)
 122    {
 0123        var wh = string.IsNullOrWhiteSpace(warehouseName) ? "склад" : warehouseName.Trim();
 0124        if (!string.IsNullOrWhiteSpace(note))
 125        {
 0126            var trimmed = note.Trim();
 0127            if (trimmed.Contains("⓪₃", StringComparison.Ordinal))
 0128                return trimmed;
 0129            return success
 0130                ? $"⓪₃ {trimmed} — {wh} {qtyBefore} → 0"
 0131                : $"✗ Этап 3: {trimmed} — {wh}";
 132        }
 133
 0134        return success
 0135            ? $"⓪₃ Этап 3: {wh} {qtyBefore} → 0 (лишний FBS-склад)"
 0136            : $"✗ Этап 3: не удалось обнулить {wh}";
 137    }
 138
 139    private static async Task<CleanupSiblingTemplate?> LoadSiblingTemplateAsync(
 140        ApplicationDbContext db,
 141        string reportId,
 142        int clientId,
 143        string? article,
 144        CancellationToken cancellationToken)
 145    {
 0146        await db.Database.OpenConnectionAsync(cancellationToken);
 147        try
 148        {
 0149            var conn = db.Database.GetDbConnection();
 0150            await using var cmd = conn.CreateCommand();
 0151            cmd.CommandText = @"
 0152SELECT ProductId, OfferId, OzonProductId, OzonClientName, OneCPrice, PrevOneCPrice, OzonWarehouseName
 0153FROM BitrixStockSyncReportLines
 0154WHERE ReportId = @reportId AND OzonClientId = @clientId AND Article = @article
 0155ORDER BY COALESCE(OzonQtyAfter, OzonQty) DESC, Id
 0156LIMIT 1";
 0157            AddParam(cmd, "@reportId", reportId);
 0158            AddParam(cmd, "@clientId", clientId);
 0159            AddParam(cmd, "@article", article);
 160
 0161            await using var reader = await cmd.ExecuteReaderAsync(cancellationToken);
 0162            if (!await reader.ReadAsync(cancellationToken))
 0163                return null;
 164
 0165            return new CleanupSiblingTemplate
 0166            {
 0167                ProductId = reader.GetInt32(0),
 0168                OfferId = reader.IsDBNull(1) ? null : reader.GetString(1),
 0169                OzonProductId = reader.IsDBNull(2) ? null : reader.GetInt64(2),
 0170                OzonClientName = reader.IsDBNull(3) ? string.Empty : reader.GetString(3),
 0171                OneCPrice = reader.IsDBNull(4) ? null : Convert.ToDecimal(reader.GetValue(4)),
 0172                PrevOneCPrice = reader.IsDBNull(5) ? null : Convert.ToDecimal(reader.GetValue(5)),
 0173                OzonWarehouseName = reader.IsDBNull(6) ? null : reader.GetString(6)
 0174            };
 0175        }
 176        finally
 177        {
 0178            await db.Database.CloseConnectionAsync();
 179        }
 0180    }
 181
 182    private static void AddParam(System.Data.Common.DbCommand cmd, string name, object? value)
 183    {
 0184        var p = cmd.CreateParameter();
 0185        p.ParameterName = name;
 0186        p.Value = value ?? DBNull.Value;
 0187        cmd.Parameters.Add(p);
 0188    }
 189
 190    private sealed class CleanupSiblingTemplate
 191    {
 0192        public int ProductId { get; set; }
 0193        public string? OfferId { get; set; }
 0194        public long? OzonProductId { get; set; }
 0195        public string OzonClientName { get; set; } = string.Empty;
 0196        public decimal? OneCPrice { get; set; }
 0197        public decimal? PrevOneCPrice { get; set; }
 0198        public string? OzonWarehouseName { get; set; }
 199    }
 200}
 201
 202internal static class CleanupExtraLabels
 203{
 204    public const string ActionLabel = "Этап 3: лишний склад";
 205    public const string ActionIcon = "⓪₃";
 206    public const string RowClass = "sync-row-cleanup-extra";
 207    public const string OneCStore = "—";
 208    public const string MappingDescription = "Лишний FBS-склад вне защищённых (этап 3)";
 209    public const string ApplyNote = "Этап 3 — не участвовал в накате этапа 2";
 210}