< Summary

Information
Class: Services.AiAssistant.TransactionSummary
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/AiAssistant/OrderRussianFieldFormatter.cs
Line coverage
100%
Covered lines: 5
Uncovered lines: 0
Coverable lines: 5
Total lines: 219
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_Id()100%11100%
get_TypeName()100%11100%
get_CreatedAt()100%11100%
get_CreatedBy()100%11100%
get_Comment()100%11100%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/AppRepository/AiAssistant/OrderRussianFieldFormatter.cs

#LineLine coverage
 1using System.Globalization;
 2using System.Text;
 3using OzonDomains.Models;
 4
 5namespace Services.AiAssistant;
 6
 7/// <summary>
 8/// Форматирует заказ для LLM: все ключевые поля с русскими наименованиями.
 9/// </summary>
 10public static class OrderRussianFieldFormatter
 11{
 12    private static readonly CultureInfo Ru = CultureInfo.GetCultureInfo("ru-RU");
 13
 14    public static string FormatFieldGlossary() =>
 15        """
 16        Справочник полей заказа (имя в БД → русское название):
 17        • Id — ID заказа в системе
 18        • ShipmentNumber — Номер отправления Ozon
 19        • Key — Внутренний ключ
 20        • TrackingNumber — Трек-номер
 21        • ProcessingDate — Дата принятия в обработку
 22        • ShippingDate — Дедлайн отгрузки
 23        • AppStatus — Статус в отчёте (внутренний)
 24        • ClientStatus / Status — Статус Ozon (маркетплейс)
 25        • LastStatusChangeDate — Дата последнего изменения статуса
 26        • ShipmentAmount — Сумма отправления
 27        • ProductName — Наименование товара
 28        • Article — Артикул
 29        • ProductKey — Ключ товара (кратность в [N])
 30        • Manufacturer — Производитель
 31        • Price — Цена продажи
 32        • CurrentPriceWithDiscount — Цена сайта, база плановой прибыли
 33        • Quantity — Количество
 34        • TrueQuantity — Истинное количество (с кратностью)
 35        • ShipmentWarehouse — Склад отгрузки
 36        • Supplier — Поставщик
 37        • OrderNumberToSupplier — Номер заказа у поставщика
 38        • PurchasePrice — Цена закупки
 39        • OriginalPurchasePrice — Цена закупки до конвертации
 40        • CostPrice — Себестоимость
 41        • MinProfit / MaxProfit — Мин./макс. прибыль
 42        • TargetShipmentProfit135 — Целевая прибыль 13,5% от цены сайта (fallback: Price)
 43        • MinOzonCommission / MaxOzonCommission — Комиссия Ozon
 44        • MinDiscount / MaxDiscount — Скидка %
 45        • DeliveryCity — Город доставки
 46        • Delivery — Тип доставки
 47        • OzonClient — Аккаунт Ozon
 48        • Сurrency — Валюта
 49        • NewCategory — Категория товара
 50        • Comment — Комментарий менеджера
 51        • IsVerified — Проверен
 52        • IsAccepted — Принят
 53        • IsReturnable — Подлежит возврату
 54        • IsReturned — Заказ возвращён/отменён в Ozon
 55        • FromFile — Загружен из Excel
 56        • UpdatedBy — Кто обновил запись
 57        • OrderArchive — запись об архивации (заказ скрыт из главной таблицы, см. /Studio2/OrderArchive)
 58        • TransactionId — привязка к транзакции (операция смены статуса)
 59        """;
 60
 61    public static string FormatOrderDetail(Order order, IReadOnlyList<TransactionSummary> transactions)
 62    {
 63        var sb = new StringBuilder();
 64        sb.AppendLine($"=== Заказ ID {order.Id} ===");
 65        AppendArchiveBlock(sb, order);
 66        AppendKeyPartiesBlock(sb, order);
 67        AppendOrderQuantityBlock(sb, order);
 68
 69        Line(sb, "Номер отправления", order.ShipmentNumber);
 70        Line(sb, "Внутренний ключ", order.Key);
 71        Line(sb, "Трек-номер", order.TrackingNumber);
 72        DateLine(sb, "Принят в обработку", order.ProcessingDate);
 73        DateLine(sb, "Дедлайн отгрузки", order.ShippingDate);
 74        DateLine(sb, "Последнее изменение статуса", order.LastStatusChangeDate);
 75
 76        Line(sb, "Статус в отчёте", order.AppStatus?.Name);
 77        Line(sb, "Статус Ozon", order.ClientStatus?.Name);
 78        Line(sb, "Подлежит возврату", order.IsReturnable == true ? "да" : "нет");
 79        Line(sb, "Возврат/отмена Ozon", order.IsReturned ? "да" : "нет");
 80
 81        AppendTransactionsBlock(sb, transactions);
 82
 83        Line(sb, "Наименование", order.ProductName);
 84        Line(sb, "Артикул", order.Article);
 85        Line(sb, "Ключ товара", order.ProductKey);
 86        Line(sb, "Производитель", order.Manufacturer?.Name);
 87        Line(sb, "Категория", order.NewCategory);
 88
 89        MoneyLine(sb, "Цена продажи", order.Price);
 90        MoneyLine(sb, "Цена сайта", order.ProductInfo?.CurrentPriceWithDiscount);
 91        Line(sb, "Количество", order.Quantity?.ToString(Ru));
 92        Line(sb, "Истинное количество", order.TrueQuantity?.ToString(Ru));
 93        MoneyLine(sb, "Сумма отправления", order.ShipmentAmount);
 94
 95        MoneyLine(sb, "Цена закупки", order.PurchasePrice);
 96        MoneyLine(sb, "Цена закупки (до конвертации)", order.OriginalPurchasePrice);
 97        MoneyLine(sb, "Себестоимость", order.CostPrice);
 98        MoneyLine(sb, "Мин. прибыль", order.MinProfit);
 99        MoneyLine(sb, "Макс. прибыль", order.MaxProfit);
 100        MoneyLine(sb, "Целевая прибыль 13,5%", order.TargetShipmentProfit135);
 101        MoneyLine(sb, "Мин. комиссия Ozon", order.MinOzonCommission);
 102        MoneyLine(sb, "Макс. комиссия Ozon", order.MaxOzonCommission);
 103        Line(sb, "Мин. скидка %", order.MinDiscount?.ToString("N1", Ru));
 104        Line(sb, "Макс. скидка %", order.MaxDiscount?.ToString("N1", Ru));
 105
 106        Line(sb, "Номер заказа у поставщика", order.OrderNumberToSupplier);
 107        Line(sb, "Валюта", order.Сurrency?.Name);
 108        Line(sb, "Город доставки", order.DeliveryCity);
 109        Line(sb, "Тип доставки", order.Delivery?.Name);
 110
 111        Line(sb, "Проверен", order.IsVerified ? "да" : "нет");
 112        Line(sb, "Принят", order.IsAccepted ? "да" : "нет");
 113        Line(sb, "Из Excel", order.FromFile == true ? "да" : "нет");
 114        Line(sb, "Комментарий", order.Comment);
 115        Line(sb, "Обновил", order.UpdatedBy);
 116
 117        if (order.ShippingDate.HasValue && order.AppStatus is { IsFinal: false })
 118        {
 119            var overdue = order.ShippingDate.Value < DateTime.UtcNow;
 120            Line(sb, "Просрочен", overdue ? "да" : "нет");
 121        }
 122
 123        return sb.ToString().TrimEnd();
 124    }
 125
 126    public static void AppendKeyPartiesBlock(StringBuilder sb, Order order)
 127    {
 128        sb.AppendLine("--- Участники заказа (ключевое) ---");
 129        var supplier = string.IsNullOrWhiteSpace(order.Supplier?.Name) ? "не назначен" : order.Supplier!.Name;
 130        var warehouse = string.IsNullOrWhiteSpace(order.ShipmentWarehouse?.Name)
 131            ? "не указан"
 132            : order.ShipmentWarehouse!.Name;
 133        var client = string.IsNullOrWhiteSpace(order.OzonClient?.Name) ? "не указан" : order.OzonClient!.Name;
 134
 135        sb.AppendLine($"► Поставщик: «{supplier}»");
 136        sb.AppendLine($"► Склад отгрузки: «{warehouse}»");
 137        sb.AppendLine($"► Ozon-клиент: «{client}»");
 138        sb.AppendLine();
 139    }
 140
 141    public static void AppendOrderQuantityBlock(StringBuilder sb, Order order)
 142    {
 143        var qty = order.Quantity ?? 1;
 144        var trueQty = order.TrueQuantity ?? qty;
 145
 146        sb.AppendLine("--- Количество в заказе ---");
 147        sb.AppendLine($"► В заказе: {qty.ToString(Ru)} шт.");
 148        if (trueQty != qty)
 149            sb.AppendLine($"► Истинное количество (с кратностью): {trueQty.ToString(Ru)} шт.");
 150        sb.AppendLine();
 151    }
 152
 153    public static void AppendTransactionsBlock(StringBuilder sb, IReadOnlyList<TransactionSummary> transactions)
 154    {
 155        sb.AppendLine("--- Транзакции по заказу ---");
 156        if (transactions.Count == 0)
 157        {
 158            sb.AppendLine("Заказ не участвовал в транзакциях (или записи не найдены).");
 159            sb.AppendLine();
 160            return;
 161        }
 162
 163        sb.AppendLine($"Всего транзакций: {transactions.Count} (от старых к новым):");
 164        var i = 1;
 165        foreach (var t in transactions.OrderBy(x => x.CreatedAt ?? DateTime.MinValue))
 166        {
 167            var when = AiMskDateTime.Format(t.CreatedAt);
 168            var who = string.IsNullOrWhiteSpace(t.CreatedBy) ? "не указан" : t.CreatedBy!.Trim();
 169            sb.AppendLine($"  {i}) {when} — «{t.TypeName}» (транзакция #{t.Id}) — провёл: {who}");
 170            if (!string.IsNullOrWhiteSpace(t.Comment))
 171                sb.AppendLine($"     Комментарий: {t.Comment.Trim()}");
 172            i++;
 173        }
 174
 175        sb.AppendLine();
 176    }
 177
 178    public static void AppendArchiveBlock(StringBuilder sb, Order order)
 179    {
 180        if (order.OrderArchive == null) return;
 181
 182        var a = order.OrderArchive;
 183        sb.AppendLine("--- АРХИВ (OrderArchive) ---");
 184        sb.AppendLine("Заказ в архиве: не отображается в главной таблице без фильтра «Включая архив».");
 185        sb.AppendLine($"Дата архивации: {a.ArchivedAt:dd.MM.yyyy HH:mm}");
 186        Line(sb, "Кто архивировал", a.ArchivedBy);
 187        Line(sb, "Причина", a.ArchiveReason);
 188        Line(sb, "Бывший статус", a.PreviousStatus);
 189        sb.AppendLine("Восстановление: /Studio2/OrderArchive");
 190        sb.AppendLine();
 191    }
 192
 193    private static void Line(StringBuilder sb, string label, string? value)
 194    {
 195        if (string.IsNullOrWhiteSpace(value)) return;
 196        sb.AppendLine($"{label}: {value}");
 197    }
 198
 199    private static void DateLine(StringBuilder sb, string label, DateTime? value)
 200    {
 201        if (!value.HasValue) return;
 202        sb.AppendLine($"{label}: {value.Value:dd.MM.yyyy HH:mm} UTC");
 203    }
 204
 205    private static void MoneyLine(StringBuilder sb, string label, decimal? value)
 206    {
 207        if (!value.HasValue) return;
 208        sb.AppendLine($"{label}: {value.Value:N2} ₽");
 209    }
 210}
 211
 212public sealed class TransactionSummary
 213{
 10214    public int Id { get; init; }
 15215    public string TypeName { get; init; } = string.Empty;
 15216    public DateTime? CreatedAt { get; init; }
 14217    public string? CreatedBy { get; init; }
 12218    public string? Comment { get; init; }
 219}