< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_JobLogs_Index
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/JobLogs/Index.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 120
Coverable lines: 120
Total lines: 375
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 338
Branch coverage: 0%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
ExecuteAsync()0%355321880%
TabUrl()100%210%
HeroSubtitle()0%1640400%
HeroHelp()0%1190340%
TabHint()0%1190340%
<ExecuteAsync()0%1806420%

File(s)

/home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/JobLogs/Index.cshtml

#LineLine coverage
 1@model OzonOrdersWeb.Areas.Studio2.ViewModels.JobLogs.JobLogsPageViewModel
 2@{
 03    ViewData["Title"] = "Журнал задач";
 04    var isTransactions = Model.ActiveTab == "transactions";
 05    var isOnec = Model.ActiveTab == "onec";
 06    var isTimeTracking = Model.ActiveTab == "timetracking";
 07    var isWebhook = Model.ActiveTab == "webhook";
 08    var isEmail = Model.ActiveTab == "email";
 09    var isFileUpload = Model.ActiveTab == "fileupload";
 010    var isStockSync = Model.ActiveTab == "stocksync";
 011    var isOrderSearch = Model.ActiveTab == "ordersearch";
 012    var isServer = Model.ActiveTab == "server";
 013    var isAlerts = Model.ActiveTab == "alerts";
 014    string TabUrl(string tab) => $"/Studio2/JobLogs?tab={tab}";
 15
 016    string HeroSubtitle(string tab) => tab switch
 017    {
 018        "transactions" => "Запуски авто-транзакций Ozon: отгрузка комиссионеру, возврат покупателя, отгрузка клиенту.",
 019        "onec" => "Проведения документов в 1С: что, кем и с каким результатом проведено.",
 020        "timetracking" => "Сколько времени занимает проведение транзакций у каждого пользователя.",
 021        "webhook" => "Входящие push-уведомления Ozon и результат их обработки.",
 022        "email" => "Письма с вложениями: что сохранено в Dropbox и что импортировано в заказы.",
 023        "fileupload" => "История разбора файлов заказов из Dropbox.",
 024        "stocksync" => "Обновления snapshot остатков 1С из Google Sheets и Excel.",
 025        "ordersearch" => "Диагностика Elasticsearch: индексация заказов, скорость поиска и расхождения (drift).",
 026        "server" => "Состояние приложения, ресурсов сервера и базы данных MariaDB.",
 027        "alerts" => "Активные и восстановленные системные инциденты.",
 028        _ => "Фоновые транзакции, интеграции и диагностика — в одном месте."
 029    };
 30
 031    string HeroHelp(string tab) => tab switch
 032    {
 033        "transactions" => "Строка = один запуск расписания или ручной старт. Провалитесь в запуск, чтобы увидеть отобран
 034        "onec" => "Строка = один вызов 1С по заказу; источник — таблица OrderHistoryOneCPostings.",
 035        "timetracking" => "Сводка и графики считаются по всем строкам под фильтром, а не по текущей странице таблицы.",
 036        "webhook" => "Ozon шлёт уведомление на каждое изменение отправления — здесь видно, приняли ли мы его и что сдела
 037        "email" => "Правила маршрутизации решают, чьё письмо принимать и в какую папку класть вложение.",
 038        "fileupload" => "Строка = один файл, который забрал фоновый импорт; в «Результате» — сколько заказов создано или
 039        "stocksync" => "Snapshot WarehouseStock — источник для сравнения с остатками Ozon; здесь видно, когда он обновля
 040        "ordersearch" => "Журнал живёт в памяти процесса и обнуляется при рестарте; счётчики индекса и checkpoint читают
 041        "server" => "Показатели собираются при открытии вкладки. MariaDB проверяется запросами только на чтение; никаких
 042        _ => ""
 043    };
 44
 045    string TabHint(string tab) => tab switch
 046    {
 047        "transactions" => "Транзакции → Запуски: что запускалось по расписанию и вручную.",
 048        "onec" => "Транзакции → Проведения 1С: результат каждого вызова 1С.",
 049        "timetracking" => "Транзакции → Трекинг времени: длительность работы пользователей в формах проведения.",
 050        "webhook" => "Интеграции → Webhook Ozon: входящий поток уведомлений маркетплейса.",
 051        "email" => "Интеграции → Email → Dropbox: приём вложений из почты.",
 052        "fileupload" => "Интеграции → Файлы Dropbox: разбор файлов заказов.",
 053        "stocksync" => "Интеграции → Синхронизация остатков: обновление snapshot 1С.",
 054        "ordersearch" => "Диагностика → Поиск ES: состояние индекса заказов в Elasticsearch.",
 055        "server" => "Диагностика → Сервер и БД: ресурсы приложения, доступность и нагрузка MariaDB.",
 056        _ => ""
 057    };
 58}
 59
 060@await Html.PartialAsync("_BsxGridShell")
 61<link rel="stylesheet" href="~/css/Studio2/JobLogs/job-logs-shared.css" asp-append-version="true" />
 62<link rel="stylesheet" href="~/css/Studio2/shared/quick-links-chip.css" asp-append-version="true" />
 063@if (isTimeTracking)
 64{
 65    <link rel="stylesheet" href="~/css/Studio2/TransactionTimeTracking/transaction-time-tracking-report.css" asp-append-
 66}
 067@if (isOrderSearch)
 68{
 69    <link rel="stylesheet" href="~/css/Studio2/JobLogs/job-logs-order-search.css" asp-append-version="true" />
 70}
 071@if (isServer)
 72{
 73    <link rel="stylesheet" href="~/css/Studio2/JobLogs/job-logs-server-diagnostics.css" asp-append-version="true" />
 74}
 75
 76<div class="container-fluid mt-3 job-logs-page bsx-surface">
 77    <div class="job-logs-hero app-section-hero app-section-hero--import">
 78        <div class="d-flex flex-wrap justify-content-between align-items-start gap-3">
 79            <div>
 80                <h1><i class="fas fa-clipboard-list text-primary me-2"></i>Журнал задач</h1>
 081                <div class="subtitle">@HeroSubtitle(Model.ActiveTab)</div>
 082                @if (!string.IsNullOrEmpty(HeroHelp(Model.ActiveTab)))
 83                {
 084                    <div class="subtitle-help">@HeroHelp(Model.ActiveTab)</div>
 85                }
 86                <div class="job-logs-quick-links app-section-hero__quick-links">
 087                    @switch (Model.ActiveTab)
 88                    {
 89                        case "transactions":
 90                            <a asp-area="Studio2" asp-controller="TransactionJobs" asp-action="Index"><i class="fas fa-r
 91                            <a asp-area="Studio2" asp-controller="Job" asp-action="Index"><i class="fas fa-cogs"></i>Фон
 92                            <a href="/hangfire" target="_blank" rel="noopener"><i class="fas fa-external-link-alt"></i>H
 093                            break;
 94                        case "onec":
 95                            <a href="@TabUrl("transactions")"><i class="fas fa-truck-loading"></i>Запуски</a>
 96                            <a asp-area="Studio2" asp-controller="Orders" asp-action="Index"><i class="fas fa-table"></i
 097                            break;
 98                        case "timetracking":
 99                            <a href="@TabUrl("transactions")"><i class="fas fa-truck-loading"></i>Запуски</a>
 100                            <a href="@TabUrl("onec")"><i class="fas fa-file-invoice"></i>Проведения 1С</a>
 0101                            break;
 102                        case "webhook":
 103                            <a asp-area="Studio2" asp-controller="OzonClients" asp-action="Index"><i class="fas fa-store
 104                            <a asp-area="Studio2" asp-controller="HangfireQueue" asp-action="Index"><i class="fas fa-lis
 105                            <a asp-area="Studio2" asp-controller="Orders" asp-action="Index"><i class="fas fa-table"></i
 0106                            break;
 107                        case "email":
 108                            <a asp-area="Studio2" asp-controller="EmailRoutingRules" asp-action="Index"><i class="fas fa
 109                            <a href="@TabUrl("fileupload")"><i class="fas fa-folder-open"></i>Файлы Dropbox</a>
 0110                            break;
 111                        case "fileupload":
 112                            <a href="@TabUrl("email")"><i class="fas fa-envelope"></i>Email → Dropbox</a>
 113                            <a asp-area="Studio2" asp-controller="Orders" asp-action="Upload"><i class="fas fa-file-impo
 114                            <a asp-area="Studio2" asp-controller="Job" asp-action="Index"><i class="fas fa-cogs"></i>Фон
 0115                            break;
 116                        case "stocksync":
 117                            <a asp-area="Studio2" asp-controller="WarehouseStock" asp-action="Index"><i class="fas fa-wa
 118                            <a asp-area="Studio2" asp-controller="BitrixStock" asp-action="Index" asp-route-tab="sync"><
 119                            <a asp-area="Studio2" asp-controller="Job" asp-action="Index"><i class="fas fa-cogs"></i>Фон
 0120                            break;
 121                        case "ordersearch":
 122                            <a asp-area="Studio2" asp-controller="Orders" asp-action="Index"><i class="fas fa-table"></i
 123                            <a asp-area="Studio2" asp-controller="HangfireQueue" asp-action="Index"><i class="fas fa-lis
 0124                            break;
 125                        case "server":
 126                            <a asp-area="Studio2" asp-controller="HangfireQueue" asp-action="Index"><i class="fas fa-lis
 127                            <a asp-area="Studio2" asp-controller="Job" asp-action="Index"><i class="fas fa-cogs"></i>Фон
 128                            break;
 129                    }
 130                </div>
 131            </div>
 132        </div>
 133    </div>
 134
 135    @* Инвариант: группа «Транзакции» никогда не пустеет — «Трекинг времени» доступен и не-админу.
 136       При переносе вкладок между группами это надо перепроверить, иначе останется заголовок без пилюль. *@
 137    <nav class="job-logs-tabgroups" aria-label="Разделы журнала задач">
 138        <div class="job-logs-tabgroup" data-group="tx">
 139            <span class="job-logs-tabgroup-title">Транзакции</span>
 140            <ul class="nav job-logs-tabs" role="tablist">
 0141                @if (Model.ShowTransactionsTab)
 142                {
 143                    <li class="nav-item" role="presentation">
 144                        <a class="nav-link d-inline-flex align-items-center @(isTransactions ? "active" : "")" data-tab=
 145                           title="Запуски авто-транзакций Ozon по расписанию и вручную">
 146                            <i class="fas fa-truck-loading me-1"></i>Запуски
 147                            <span class="job-logs-tab-badge badge rounded-pill" data-job-badge="transactions"></span>
 148                        </a>
 149                    </li>
 150                    <li class="nav-item" role="presentation">
 151                        <a class="nav-link d-inline-flex align-items-center @(isOnec ? "active" : "")" data-tab="onec" h
 152                           title="Результат проведения документов в 1С">
 153                            <i class="fas fa-file-invoice me-1"></i>Проведения 1С
 154                        </a>
 155                    </li>
 156                }
 157                <li class="nav-item" role="presentation">
 158                    <a class="nav-link d-inline-flex align-items-center @(isTimeTracking ? "active" : "")" data-tab="tim
 159                       title="Длительность работы пользователей в формах проведения">
 160                        <i class="fas fa-stopwatch me-1"></i>Трекинг времени
 161                    </a>
 162                </li>
 0163                @if (Model.ShowTransactionsTab)
 164                {
 165                    <li class="nav-item" role="presentation">
 166                        <a class="nav-link d-inline-flex align-items-center @(isAlerts ? "active" : "")" data-tab="alert
 167                            <i class="fas fa-triangle-exclamation me-1"></i>Системные алерты
 168                        </a>
 169                    </li>
 170                }
 171            </ul>
 172        </div>
 173
 174        <div class="job-logs-tabgroup" data-group="int">
 175            <span class="job-logs-tabgroup-title">Интеграции</span>
 176            <ul class="nav job-logs-tabs" role="tablist">
 177                <li class="nav-item" role="presentation">
 178                    <a class="nav-link d-inline-flex align-items-center @(isWebhook ? "active" : "")" data-tab="webhook"
 179                       title="Входящие push-уведомления Ozon">
 180                        <i class="fas fa-bell me-1"></i>Webhook Ozon
 181                        <span class="job-logs-tab-badge badge rounded-pill" data-job-badge="webhook"></span>
 182                    </a>
 183                </li>
 184                <li class="nav-item" role="presentation">
 185                    <a class="nav-link d-inline-flex align-items-center @(isEmail ? "active" : "")" data-tab="email" hre
 186                       title="Приём вложений из почты в Dropbox">
 187                        <i class="fas fa-envelope me-1"></i>Email → Dropbox
 188                        <span class="job-logs-tab-badge badge rounded-pill" data-job-badge="email"></span>
 189                    </a>
 190                </li>
 191                <li class="nav-item" role="presentation">
 192                    <a class="nav-link d-inline-flex align-items-center @(isFileUpload ? "active" : "")" data-tab="fileu
 193                       title="Разбор файлов заказов из Dropbox">
 194                        <i class="fas fa-folder-open me-1"></i>Файлы Dropbox
 195                        <span class="job-logs-tab-badge badge rounded-pill" data-job-badge="fileupload"></span>
 196                    </a>
 197                </li>
 198                <li class="nav-item" role="presentation">
 199                    <a class="nav-link d-inline-flex align-items-center @(isStockSync ? "active" : "")" data-tab="stocks
 200                       title="Обновление snapshot остатков 1С">
 201                        <i class="fas fa-warehouse me-1"></i>Синхронизация остатков
 202                        <span class="job-logs-tab-badge badge rounded-pill" data-job-badge="stocksync"></span>
 203                    </a>
 204                </li>
 205            </ul>
 206        </div>
 207
 208        <div class="job-logs-tabgroup" data-group="diag">
 209            <span class="job-logs-tabgroup-title">Диагностика</span>
 210            <ul class="nav job-logs-tabs" role="tablist">
 211                <li class="nav-item" role="presentation">
 212                    <a class="nav-link d-inline-flex align-items-center @(isOrderSearch ? "active" : "")" data-tab="orde
 213                       title="Состояние индекса заказов в Elasticsearch">
 214                        <i class="fas fa-search me-1"></i>Поиск ES
 215                    </a>
 216                </li>
 217                <li class="nav-item" role="presentation">
 218                    <a class="nav-link d-inline-flex align-items-center @(isServer ? "active" : "")" data-tab="server" h
 219                       title="Ресурсы приложения и состояние MariaDB">
 220                        <i class="fas fa-server me-1"></i>Сервер и БД
 221                    </a>
 222                </li>
 223            </ul>
 224        </div>
 225    </nav>
 226
 0227    <div class="job-logs-tab-hint">@TabHint(Model.ActiveTab)</div>
 228
 229    <div class="tab-content">
 0230        @if (Model.ShowTransactionsTab)
 231        {
 0232            var domains = Model.ActiveTab switch { "transactions" => new[] { "transactions", "hangfire" }, "onec" => new
 0233            var healthAlerts = Model.CurrentSystemAlerts.Where(x => domains.Contains(x.Domain)).ToList();
 0234            if (healthAlerts.Count > 0)
 235            {
 0236                <section class="job-logs-panel card mb-3"><div class="card-header"><strong><i class="fas fa-heart-pulse 
 237            }
 238        }
 0239        @if (isTransactions && Model.Transactions != null)
 240        {
 0241            @await Html.PartialAsync("_TransactionExecutionsTab", Model.Transactions)
 242        }
 0243        else if (isOnec && Model.OneC != null)
 244        {
 0245            @await Html.PartialAsync("_TransactionOneCPostingsSection", Model.OneC)
 246        }
 0247        else if (isTimeTracking && Model.TimeTracking != null)
 248        {
 0249            @await Html.PartialAsync("_TransactionTimeTrackingTab", Model.TimeTracking)
 250        }
 0251        else if (isWebhook && Model.Webhook != null)
 252        {
 0253            @await Html.PartialAsync("_WebhookLogTab", Model.Webhook)
 254        }
 0255        else if (isEmail && Model.EmailRouting != null)
 256        {
 0257            @await Html.PartialAsync("_EmailRoutingTab", Model.EmailRouting)
 258        }
 0259        else if (isFileUpload && Model.FileUpload != null)
 260        {
 0261            @await Html.PartialAsync("_FileUploadTab", Model.FileUpload)
 262        }
 0263        else if (isStockSync && Model.StockSync != null)
 264        {
 0265            @await Html.PartialAsync("_WarehouseStockSyncTab", Model.StockSync)
 266        }
 0267        else if (isOrderSearch && Model.OrderSearch != null)
 268        {
 0269            @await Html.PartialAsync("_OrderSearchTab", Model.OrderSearch)
 270        }
 0271        else if (isServer && Model.ServerDiagnostics != null)
 272        {
 0273            @await Html.PartialAsync("_ServerDiagnosticsTab", Model.ServerDiagnostics)
 274        }
 0275        else if (isAlerts && Model.ShowTransactionsTab)
 276        {
 277            <section class="job-logs-panel card mb-3" id="nomenclatureAlertsLinks">
 278                <div class="card-header"><strong><i class="fas fa-tags me-1"></i>Номенклатура 1С</strong></div>
 279                <div class="card-body d-flex flex-wrap align-items-center gap-2">
 280                    <span class="small bsx-muted me-1">Для конкретного заказа нажмите «Номенклатура» в строке алерта.</s
 281                    <a class="btn btn-sm btn-outline-warning" href="/Studio2/OneCNomenclature/Report/Duplicates" target=
 282                        <i class="fas fa-copy me-1"></i>Дубли <span class="badge text-bg-secondary ms-1" id="nomenclatur
 283                    </a>
 284                    <a class="btn btn-sm btn-outline-warning" href="/Studio2/OneCNomenclature/Report/BrandPairs" target=
 285                        <i class="fas fa-not-equal me-1"></i>Несовпадения брендов <span class="badge text-bg-secondary m
 286                    </a>
 287                </div>
 288            </section>
 0289            @await Html.PartialAsync("_JobLogsGridPanel", new OzonOrdersWeb.Areas.Studio2.ViewModels.JobLogs.JobLogsGrid
 0290                "Системные алерты", "fa-triangle-exclamation", "Контур, проблема, детали…",
 0291                "Фильтры, сортировка и раскладка колонок сохраняются отдельно для администратора."))
 292        }
 293    </div>
 294</div>
 295
 296@section Scripts {
 297    <script>
 298        window.jobLogsConfig = {
 0299            activeTab: '@Model.ActiveTab',
 0300            highlightExecutionId: @(Model.HighlightExecutionId.HasValue ? Model.HighlightExecutionId.Value.ToString() : 
 0301            webhookDetailsUrl: '@Url.Action("Details", "OzonWebhookLog", new { area = "Studio2" })'
 302        };
 303
 304        function jobLogsChannelFromData(data, tab) {
 305            if (!data) return null;
 306            switch (tab) {
 307                case 'transactions': return data.transactions;
 308                case 'onec': return data.transactions;
 309                case 'email': return data.emailRouting;
 310                case 'fileupload': return data.fileUpload;
 311                case 'stocksync': return data.stockSync;
 312                case 'webhook': return data.webhook;
 313                default: return null;
 314            }
 315        }
 316
 317        window.onJobLogsBadgesUpdated = function (data) {
 318            const tab = window.jobLogsConfig.activeTab;
 319            const channel = jobLogsChannelFromData(data, tab);
 320            if (channel?.latestAtUtc && typeof window.markJobLogsTabSeen === 'function') {
 321                window.markJobLogsTabSeen(tab, channel.latestAtUtc);
 322            }
 323        };
 324
 325        document.addEventListener('DOMContentLoaded', function () {
 326            window.initCustomSelects?.('.job-logs-page .form-select');
 327            if (window.jobLogsBadgeSnapshot && typeof window.onJobLogsBadgesUpdated === 'function') {
 328                window.onJobLogsBadgesUpdated(window.jobLogsBadgeSnapshot);
 329            } else if (typeof window.updateJobLogsBadges === 'function') {
 330                window.updateJobLogsBadges();
 331            }
 332        });
 333    </script>
 334    @{
 0335        var gridTabs = new[] { "transactions", "onec", "webhook", "email", "fileupload", "stocksync", "alerts" };
 0336        var jlQuery = Context.Request.Query;
 0337        var jlParams = new Dictionary<string, string?>();
 0338        if (Model.ActiveTab == "transactions" && !string.IsNullOrWhiteSpace(jlQuery["orderNumber"])) jlParams["orderNumb
 0339        if (Model.ActiveTab == "onec")
 340        {
 0341            var execId = !string.IsNullOrWhiteSpace(jlQuery["onecExecutionId"]) ? jlQuery["onecExecutionId"].ToString() 
 0342            if (!string.IsNullOrWhiteSpace(execId)) jlParams["executionId"] = execId;
 343        }
 0344        var sellerNames = Model.Webhook?.Clients?
 0345            .Where(c => !string.IsNullOrWhiteSpace(c.DecryptClientId))
 0346            .GroupBy(c => c.DecryptClientId!)
 0347            .ToDictionary(g => g.Key, g => g.First().Name) ?? new Dictionary<string, string>();
 348    }
 349    <script>
 350        window.jobLogsGridConfig = {
 0351            tab: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(gridTabs.Contains(Model.ActiveTab) ? Model.ActiveTa
 0352            gridUrl: @Html.Raw(System.Text.Json.JsonSerializer.Serialize($"/Studio2/JobLogs/grid/{Model.ActiveTab}")),
 0353            facetsUrl: @Html.Raw(System.Text.Json.JsonSerializer.Serialize($"/Studio2/JobLogs/grid/{Model.ActiveTab}/fac
 354            onecStatisticsUrl: '/Studio2/JobLogs/onec/statistics',
 355            onecExcelUrl: '/Studio2/JobLogs/onec/export/excel',
 0356            params: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(jlParams)),
 0357            highlightId: @(Model.HighlightExecutionId.HasValue ? Model.HighlightExecutionId.Value.ToString() : "null"),
 0358            sellerNames: @Html.Raw(System.Text.Json.JsonSerializer.Serialize(sellerNames))
 359        };
 360    </script>
 0361    @if (isOnec)
 362    {
 363        <script src="~/lib/ag-charts/ag-charts-community.min.js" asp-append-version="true"></script>
 364        <script src="~/js/Studio2/ProductAnalysis/pa-charts.js" asp-append-version="true"></script>
 365        <script src="~/js/Studio2/JobLogs/job-logs-onec-statistics.js" asp-append-version="true"></script>
 366    }
 367    <script src="~/js/Studio2/JobLogs/job-logs-grids.js" asp-append-version="true"></script>
 368    <script src="~/js/Studio2/JobLogs/job-logs-webhook.js" asp-append-version="true"></script>
 0369    @if (isTimeTracking)
 370    {
 371        <script src="~/js/Studio2/Grid/bsx-directory.js" asp-append-version="true"></script>
 372        <script src="~/lib/ag-charts/ag-charts-community.min.js" asp-append-version="true"></script>
 373        <script src="~/js/Studio2/TransactionTimeTracking/transaction-time-tracking-report.js" asp-append-version="true"
 374    }
 375}