< Summary

Information
Class: Services.AiAssistant.OrderRussianFieldFormatter
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/AiAssistant/OrderRussianFieldFormatter.cs
Line coverage
100%
Covered lines: 150
Uncovered lines: 0
Coverable lines: 150
Total lines: 219
Line coverage: 100%
Branch coverage
100%
Covered branches: 70
Total branches: 70
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
FormatFieldGlossary()100%11100%
FormatOrderDetail(...)100%3838100%
AppendKeyPartiesBlock(...)100%1212100%
AppendOrderQuantityBlock(...)100%22100%
AppendTransactionsBlock(...)100%1010100%
AppendArchiveBlock(...)100%22100%
Line(...)100%22100%
DateLine(...)100%22100%
MoneyLine(...)100%22100%

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{
 112    private static readonly CultureInfo Ru = CultureInfo.GetCultureInfo("ru-RU");
 13
 14    public static string FormatFieldGlossary() =>
 1715        """
 1716        Справочник полей заказа (имя в БД → русское название):
 1717        • Id — ID заказа в системе
 1718        • ShipmentNumber — Номер отправления Ozon
 1719        • Key — Внутренний ключ
 1720        • TrackingNumber — Трек-номер
 1721        • ProcessingDate — Дата принятия в обработку
 1722        • ShippingDate — Дедлайн отгрузки
 1723        • AppStatus — Статус в отчёте (внутренний)
 1724        • ClientStatus / Status — Статус Ozon (маркетплейс)
 1725        • LastStatusChangeDate — Дата последнего изменения статуса
 1726        • ShipmentAmount — Сумма отправления
 1727        • ProductName — Наименование товара
 1728        • Article — Артикул
 1729        • ProductKey — Ключ товара (кратность в [N])
 1730        • Manufacturer — Производитель
 1731        • Price — Цена продажи
 1732        • CurrentPriceWithDiscount — Цена сайта, база плановой прибыли
 1733        • Quantity — Количество
 1734        • TrueQuantity — Истинное количество (с кратностью)
 1735        • ShipmentWarehouse — Склад отгрузки
 1736        • Supplier — Поставщик
 1737        • OrderNumberToSupplier — Номер заказа у поставщика
 1738        • PurchasePrice — Цена закупки
 1739        • OriginalPurchasePrice — Цена закупки до конвертации
 1740        • CostPrice — Себестоимость
 1741        • MinProfit / MaxProfit — Мин./макс. прибыль
 1742        • TargetShipmentProfit135 — Целевая прибыль 13,5% от цены сайта (fallback: Price)
 1743        • MinOzonCommission / MaxOzonCommission — Комиссия Ozon
 1744        • MinDiscount / MaxDiscount — Скидка %
 1745        • DeliveryCity — Город доставки
 1746        • Delivery — Тип доставки
 1747        • OzonClient — Аккаунт Ozon
 1748        • Сurrency — Валюта
 1749        • NewCategory — Категория товара
 1750        • Comment — Комментарий менеджера
 1751        • IsVerified — Проверен
 1752        • IsAccepted — Принят
 1753        • IsReturnable — Подлежит возврату
 1754        • IsReturned — Заказ возвращён/отменён в Ozon
 1755        • FromFile — Загружен из Excel
 1756        • UpdatedBy — Кто обновил запись
 1757        • OrderArchive — запись об архивации (заказ скрыт из главной таблицы, см. /Studio2/OrderArchive)
 1758        • TransactionId — привязка к транзакции (операция смены статуса)
 1759        """;
 60
 61    public static string FormatOrderDetail(Order order, IReadOnlyList<TransactionSummary> transactions)
 62    {
 863        var sb = new StringBuilder();
 864        sb.AppendLine($"=== Заказ ID {order.Id} ===");
 865        AppendArchiveBlock(sb, order);
 866        AppendKeyPartiesBlock(sb, order);
 867        AppendOrderQuantityBlock(sb, order);
 68
 869        Line(sb, "Номер отправления", order.ShipmentNumber);
 870        Line(sb, "Внутренний ключ", order.Key);
 871        Line(sb, "Трек-номер", order.TrackingNumber);
 872        DateLine(sb, "Принят в обработку", order.ProcessingDate);
 873        DateLine(sb, "Дедлайн отгрузки", order.ShippingDate);
 874        DateLine(sb, "Последнее изменение статуса", order.LastStatusChangeDate);
 75
 876        Line(sb, "Статус в отчёте", order.AppStatus?.Name);
 877        Line(sb, "Статус Ozon", order.ClientStatus?.Name);
 878        Line(sb, "Подлежит возврату", order.IsReturnable == true ? "да" : "нет");
 879        Line(sb, "Возврат/отмена Ozon", order.IsReturned ? "да" : "нет");
 80
 881        AppendTransactionsBlock(sb, transactions);
 82
 883        Line(sb, "Наименование", order.ProductName);
 884        Line(sb, "Артикул", order.Article);
 885        Line(sb, "Ключ товара", order.ProductKey);
 886        Line(sb, "Производитель", order.Manufacturer?.Name);
 887        Line(sb, "Категория", order.NewCategory);
 88
 889        MoneyLine(sb, "Цена продажи", order.Price);
 890        MoneyLine(sb, "Цена сайта", order.ProductInfo?.CurrentPriceWithDiscount);
 891        Line(sb, "Количество", order.Quantity?.ToString(Ru));
 892        Line(sb, "Истинное количество", order.TrueQuantity?.ToString(Ru));
 893        MoneyLine(sb, "Сумма отправления", order.ShipmentAmount);
 94
 895        MoneyLine(sb, "Цена закупки", order.PurchasePrice);
 896        MoneyLine(sb, "Цена закупки (до конвертации)", order.OriginalPurchasePrice);
 897        MoneyLine(sb, "Себестоимость", order.CostPrice);
 898        MoneyLine(sb, "Мин. прибыль", order.MinProfit);
 899        MoneyLine(sb, "Макс. прибыль", order.MaxProfit);
 8100        MoneyLine(sb, "Целевая прибыль 13,5%", order.TargetShipmentProfit135);
 8101        MoneyLine(sb, "Мин. комиссия Ozon", order.MinOzonCommission);
 8102        MoneyLine(sb, "Макс. комиссия Ozon", order.MaxOzonCommission);
 8103        Line(sb, "Мин. скидка %", order.MinDiscount?.ToString("N1", Ru));
 8104        Line(sb, "Макс. скидка %", order.MaxDiscount?.ToString("N1", Ru));
 105
 8106        Line(sb, "Номер заказа у поставщика", order.OrderNumberToSupplier);
 8107        Line(sb, "Валюта", order.Сurrency?.Name);
 8108        Line(sb, "Город доставки", order.DeliveryCity);
 8109        Line(sb, "Тип доставки", order.Delivery?.Name);
 110
 8111        Line(sb, "Проверен", order.IsVerified ? "да" : "нет");
 8112        Line(sb, "Принят", order.IsAccepted ? "да" : "нет");
 8113        Line(sb, "Из Excel", order.FromFile == true ? "да" : "нет");
 8114        Line(sb, "Комментарий", order.Comment);
 8115        Line(sb, "Обновил", order.UpdatedBy);
 116
 8117        if (order.ShippingDate.HasValue && order.AppStatus is { IsFinal: false })
 118        {
 3119            var overdue = order.ShippingDate.Value < DateTime.UtcNow;
 3120            Line(sb, "Просрочен", overdue ? "да" : "нет");
 121        }
 122
 8123        return sb.ToString().TrimEnd();
 124    }
 125
 126    public static void AppendKeyPartiesBlock(StringBuilder sb, Order order)
 127    {
 38128        sb.AppendLine("--- Участники заказа (ключевое) ---");
 38129        var supplier = string.IsNullOrWhiteSpace(order.Supplier?.Name) ? "не назначен" : order.Supplier!.Name;
 38130        var warehouse = string.IsNullOrWhiteSpace(order.ShipmentWarehouse?.Name)
 38131            ? "не указан"
 38132            : order.ShipmentWarehouse!.Name;
 38133        var client = string.IsNullOrWhiteSpace(order.OzonClient?.Name) ? "не указан" : order.OzonClient!.Name;
 134
 38135        sb.AppendLine($"► Поставщик: «{supplier}»");
 38136        sb.AppendLine($"► Склад отгрузки: «{warehouse}»");
 38137        sb.AppendLine($"► Ozon-клиент: «{client}»");
 38138        sb.AppendLine();
 38139    }
 140
 141    public static void AppendOrderQuantityBlock(StringBuilder sb, Order order)
 142    {
 34143        var qty = order.Quantity ?? 1;
 34144        var trueQty = order.TrueQuantity ?? qty;
 145
 34146        sb.AppendLine("--- Количество в заказе ---");
 34147        sb.AppendLine($"► В заказе: {qty.ToString(Ru)} шт.");
 34148        if (trueQty != qty)
 3149            sb.AppendLine($"► Истинное количество (с кратностью): {trueQty.ToString(Ru)} шт.");
 34150        sb.AppendLine();
 34151    }
 152
 153    public static void AppendTransactionsBlock(StringBuilder sb, IReadOnlyList<TransactionSummary> transactions)
 154    {
 10155        sb.AppendLine("--- Транзакции по заказу ---");
 10156        if (transactions.Count == 0)
 157        {
 8158            sb.AppendLine("Заказ не участвовал в транзакциях (или записи не найдены).");
 8159            sb.AppendLine();
 8160            return;
 161        }
 162
 2163        sb.AppendLine($"Всего транзакций: {transactions.Count} (от старых к новым):");
 2164        var i = 1;
 19165        foreach (var t in transactions.OrderBy(x => x.CreatedAt ?? DateTime.MinValue))
 166        {
 5167            var when = AiMskDateTime.Format(t.CreatedAt);
 5168            var who = string.IsNullOrWhiteSpace(t.CreatedBy) ? "не указан" : t.CreatedBy!.Trim();
 5169            sb.AppendLine($"  {i}) {when} — «{t.TypeName}» (транзакция #{t.Id}) — провёл: {who}");
 5170            if (!string.IsNullOrWhiteSpace(t.Comment))
 2171                sb.AppendLine($"     Комментарий: {t.Comment.Trim()}");
 5172            i++;
 173        }
 174
 2175        sb.AppendLine();
 2176    }
 177
 178    public static void AppendArchiveBlock(StringBuilder sb, Order order)
 179    {
 18180        if (order.OrderArchive == null) return;
 181
 2182        var a = order.OrderArchive;
 2183        sb.AppendLine("--- АРХИВ (OrderArchive) ---");
 2184        sb.AppendLine("Заказ в архиве: не отображается в главной таблице без фильтра «Включая архив».");
 2185        sb.AppendLine($"Дата архивации: {a.ArchivedAt:dd.MM.yyyy HH:mm}");
 2186        Line(sb, "Кто архивировал", a.ArchivedBy);
 2187        Line(sb, "Причина", a.ArchiveReason);
 2188        Line(sb, "Бывший статус", a.PreviousStatus);
 2189        sb.AppendLine("Восстановление: /Studio2/OrderArchive");
 2190        sb.AppendLine();
 2191    }
 192
 193    private static void Line(StringBuilder sb, string label, string? value)
 194    {
 342195        if (string.IsNullOrWhiteSpace(value)) return;
 76196        sb.AppendLine($"{label}: {value}");
 76197    }
 198
 199    private static void DateLine(StringBuilder sb, string label, DateTime? value)
 200    {
 41201        if (!value.HasValue) return;
 7202        sb.AppendLine($"{label}: {value.Value:dd.MM.yyyy HH:mm} UTC");
 7203    }
 204
 205    private static void MoneyLine(StringBuilder sb, string label, decimal? value)
 206    {
 165207        if (!value.HasValue) return;
 11208        sb.AppendLine($"{label}: {value.Value:N2} ₽");
 11209    }
 210}
 211
 212public sealed class TransactionSummary
 213{
 214    public int Id { get; init; }
 215    public string TypeName { get; init; } = string.Empty;
 216    public DateTime? CreatedAt { get; init; }
 217    public string? CreatedBy { get; init; }
 218    public string? Comment { get; init; }
 219}