| | | 1 | | using OzonDomains.Models; |
| | | 2 | | |
| | | 3 | | namespace Services.BitrixStockServices; |
| | | 4 | | |
| | | 5 | | /// <summary>Подписи исключений в строках отчёта BitrixStock.</summary> |
| | | 6 | | internal static class BitrixStockExceptionReportAnnotator |
| | | 7 | | { |
| | | 8 | | public const string NotePrefix = "Исключения — "; |
| | | 9 | | |
| | | 10 | | public static bool HasStockSkip( |
| | | 11 | | string article, |
| | | 12 | | HashSet<string> skipStockArticles, |
| | | 13 | | BitrixStockExceptionStockRule? stockRule) |
| | | 14 | | { |
| | 10 | 15 | | if (string.IsNullOrWhiteSpace(article)) return false; |
| | 4 | 16 | | return skipStockArticles.Contains(article) |
| | 4 | 17 | | || stockRule?.StockMode == BitrixStockStockMode.Skip; |
| | | 18 | | } |
| | | 19 | | |
| | | 20 | | /// <summary>Все исключения артикула для кабинета (остатки + цены).</summary> |
| | | 21 | | public static string FormatFullNote( |
| | | 22 | | string article, |
| | | 23 | | int clientId, |
| | | 24 | | HashSet<string> skipStockArticles, |
| | | 25 | | HashSet<string> skipPriceArticles, |
| | | 26 | | Dictionary<(string article, int clientId), BitrixStockExceptionClientPrice> priceMap, |
| | | 27 | | BitrixStockExceptionStockRule? stockRule) |
| | | 28 | | { |
| | 13 | 29 | | var parts = CollectParts(article, clientId, skipStockArticles, skipPriceArticles, priceMap, stockRule, |
| | 13 | 30 | | includeStock: true, includePrice: true); |
| | 13 | 31 | | return parts.Count == 0 ? string.Empty : NotePrefix + string.Join("; ", parts); |
| | | 32 | | } |
| | | 33 | | |
| | | 34 | | /// <summary>Только ценовые исключения (для строк с обычным накатом остатков).</summary> |
| | | 35 | | public static string FormatPriceOnlyNote( |
| | | 36 | | string article, |
| | | 37 | | int clientId, |
| | | 38 | | HashSet<string> skipPriceArticles, |
| | | 39 | | Dictionary<(string article, int clientId), BitrixStockExceptionClientPrice> priceMap) |
| | | 40 | | { |
| | 5 | 41 | | var parts = CollectParts(article, clientId, null, skipPriceArticles, priceMap, null, |
| | 5 | 42 | | includeStock: false, includePrice: true); |
| | 5 | 43 | | return parts.Count == 0 ? string.Empty : NotePrefix + string.Join("; ", parts); |
| | | 44 | | } |
| | | 45 | | |
| | | 46 | | public static void AppendToNote(BitrixStockSyncReportWarehouseLineDto line, string exceptionNote) |
| | | 47 | | { |
| | 14 | 48 | | if (string.IsNullOrWhiteSpace(exceptionNote)) return; |
| | 6 | 49 | | line.Note = string.IsNullOrWhiteSpace(line.Note) |
| | 6 | 50 | | ? exceptionNote |
| | 6 | 51 | | : $"{line.Note} | {exceptionNote}"; |
| | 6 | 52 | | } |
| | | 53 | | |
| | | 54 | | public static void ApplyPriceNotesToLines( |
| | | 55 | | IEnumerable<BitrixStockSyncReportWarehouseLineDto> lines, |
| | | 56 | | int clientId, |
| | | 57 | | HashSet<string> skipPriceArticles, |
| | | 58 | | Dictionary<(string article, int clientId), BitrixStockExceptionClientPrice> priceMap) |
| | | 59 | | { |
| | 22 | 60 | | foreach (var line in lines) |
| | | 61 | | { |
| | 6 | 62 | | if (string.IsNullOrWhiteSpace(line.Article)) continue; |
| | 3 | 63 | | var priceNote = FormatPriceOnlyNote(line.Article, clientId, skipPriceArticles, priceMap); |
| | 3 | 64 | | AppendToNote(line, priceNote); |
| | | 65 | | } |
| | 5 | 66 | | } |
| | | 67 | | |
| | | 68 | | private static List<string> CollectParts( |
| | | 69 | | string article, |
| | | 70 | | int clientId, |
| | | 71 | | HashSet<string>? skipStockArticles, |
| | | 72 | | HashSet<string> skipPriceArticles, |
| | | 73 | | Dictionary<(string article, int clientId), BitrixStockExceptionClientPrice> priceMap, |
| | | 74 | | BitrixStockExceptionStockRule? stockRule, |
| | | 75 | | bool includeStock, |
| | | 76 | | bool includePrice) |
| | | 77 | | { |
| | 18 | 78 | | var parts = new List<string>(); |
| | | 79 | | |
| | 18 | 80 | | if (includeStock && skipStockArticles != null) |
| | | 81 | | { |
| | 13 | 82 | | if (skipStockArticles.Contains(article)) |
| | | 83 | | { |
| | 3 | 84 | | parts.Add("остатки: глобальный справочник, пропуск (все кабинеты)"); |
| | | 85 | | } |
| | 10 | 86 | | else if (stockRule?.StockMode == BitrixStockStockMode.Skip) |
| | | 87 | | { |
| | 3 | 88 | | var scope = stockRule.OzonClientId == null ? "все кабинеты" : $"кабинет #{clientId}"; |
| | 3 | 89 | | var comment = string.IsNullOrWhiteSpace(stockRule.Comment) ? "" : $" — {stockRule.Comment}"; |
| | 3 | 90 | | parts.Add($"остатки: правило, пропуск ({scope}){comment}"); |
| | | 91 | | } |
| | | 92 | | } |
| | | 93 | | |
| | 18 | 94 | | if (!includePrice) return parts; |
| | | 95 | | |
| | 18 | 96 | | if (skipPriceArticles.Contains(article)) |
| | 4 | 97 | | parts.Add("цены: глобальный справочник, пропуск (все кабинеты)"); |
| | | 98 | | |
| | 18 | 99 | | if (priceMap.TryGetValue((article.ToLowerInvariant(), clientId), out var priceRule)) |
| | | 100 | | { |
| | 7 | 101 | | var comment = string.IsNullOrWhiteSpace(priceRule.Comment) ? "" : $" — {priceRule.Comment}"; |
| | 7 | 102 | | switch (priceRule.PriceMode) |
| | | 103 | | { |
| | | 104 | | case BitrixStockPriceMode.Skip: |
| | 4 | 105 | | parts.Add($"цены: правило кабинета #{clientId}, пропуск{comment}"); |
| | 4 | 106 | | break; |
| | 3 | 107 | | case BitrixStockPriceMode.Fixed when priceRule.FixedPrice.HasValue: |
| | | 108 | | { |
| | 2 | 109 | | var old = priceRule.FixedOldPrice.HasValue |
| | 2 | 110 | | ? $", старая {priceRule.FixedOldPrice.Value:N0} ₽" |
| | 2 | 111 | | : ""; |
| | 2 | 112 | | parts.Add($"цены: правило кабинета #{clientId}, фикс. {priceRule.FixedPrice.Value:N0} ₽{old}{comment |
| | | 113 | | break; |
| | | 114 | | } |
| | | 115 | | } |
| | | 116 | | } |
| | | 117 | | |
| | 18 | 118 | | return parts; |
| | | 119 | | } |
| | | 120 | | } |