< Summary

Information
Class: Services.AiAssistant.AiArticleActionSuggestor
Assembly: Services
File(s): /home/runner/work/Studio2Prod/Studio2Prod/AppRepository/AiAssistant/AiArticleActionSuggestor.cs
Line coverage
100%
Covered lines: 45
Uncovered lines: 0
Coverable lines: 45
Total lines: 77
Line coverage: 100%
Branch coverage
90%
Covered branches: 18
Total branches: 20
Branch coverage: 90%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
Suggest()90%1010100%
ResolveArticle(...)90%1010100%

File(s)

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

#LineLine coverage
 1using Services.ApiServices.OpenRouterApi.Models;
 2
 3namespace Services.AiAssistant;
 4
 5/// <summary>
 6/// Чипы для контекста «артикул / товар».
 7/// Синие (read) — запрос данных по артикулу.
 8/// Серые (nav)  — переход к разделу системы.
 9///
 10/// ПРАВИЛО: добавляй сюда новые read-действия для артикула.
 11/// Каждый чип должен открывать данные, а не инициировать изменение.
 12/// </summary>
 13public sealed class AiArticleActionSuggestor : IAiActionSuggestor
 14{
 115    private static readonly HashSet<string> ArticleTools = new(StringComparer.Ordinal)
 116    {
 117        "ozon_live_product", "bitrix_stock_article", "order_product",
 118        "order_detail", "order_history"
 119    };
 20
 21    public IEnumerable<AiActionLinkDto> Suggest(AiSuggestionContext ctx)
 22    {
 1723        var article = ResolveArticle(ctx);
 1724        if (string.IsNullOrWhiteSpace(article))
 325            yield break;
 26
 1427        if (!ArticleTools.Contains(ctx.ToolName ?? "") &&
 1728            ctx.Orders.All(o => string.IsNullOrWhiteSpace(o.Article)))
 129            yield break;
 30
 1331        var art = Uri.EscapeDataString(article);
 32
 33        // ── Read (синие) ──────────────────────────────────────────────────────
 1334        if (ctx.ToolName != "ozon_live_product")
 35        {
 1136            yield return new AiActionLinkDto
 1137            {
 1138                Label       = "Актуально на Ozon (API)",
 1139                Url         = "#",
 1140                Kind        = "ozon_live",
 1141                ChatMessage = $"Актуальные остатки и цена на Ozon для артикула {article}"
 1142            };
 43        }
 44
 1345        yield return new AiActionLinkDto
 1346        {
 1347            Label = "Остатки WarehouseStock",
 1348            Url   = $"/Studio2/WarehouseStock?article={art}",
 1349            Kind  = "warehouse"
 1350        };
 51
 1352        yield return new AiActionLinkDto
 1353        {
 1354            Label = "Движения 1С",
 1355            Url   = $"/Studio2/WarehouseStock/movement-map?article={art}",
 1356            Kind  = "movement"
 1357        };
 58
 59        // ── Nav (серые) ───────────────────────────────────────────────────────
 1360        yield return new AiActionLinkDto
 1361        {
 1362            Label = "Синхронизация Ozon и 1С",
 1363            Url   = $"/Studio2/BitrixStock?article={art}",
 1364            Kind  = "sync",
 1365            IsNav = true
 1366        };
 1367    }
 68
 69    private static string? ResolveArticle(AiSuggestionContext ctx)
 70    {
 1771        if (!string.IsNullOrWhiteSpace(ctx.Article))
 1272            return ctx.Article.Trim();
 573        if (!string.IsNullOrWhiteSpace(ctx.ConversationContext?.Article))
 174            return ctx.ConversationContext.Article.Trim();
 775        return ctx.Orders.FirstOrDefault(o => !string.IsNullOrWhiteSpace(o.Article))?.Article?.Trim();
 76    }
 77}