< Summary

Information
Class: AspNetCoreGeneratedDocument.Areas_Studio2_Views_MatchFileData_Index
Assembly: OzonOrdersWeb
File(s): /home/runner/work/Studio2Prod/Studio2Prod/OzonOrdersWeb/Areas/Studio2/Views/MatchFileData/Index.cshtml
Line coverage
0%
Covered lines: 0
Uncovered lines: 14
Coverable lines: 14
Total lines: 532
Line coverage: 0%
Branch coverage
0%
Covered branches: 0
Total branches: 24
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%702260%
<ExecuteAsync()100%210%

File(s)

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

#LineLine coverage
 1@using OzonOrdersWeb.ViewModels.MatchFileDataViewModel
 2
 3@model MatchFileDataViewModel
 4
 5@{
 06    ViewData["Title"] = "Сопоставление файлов";
 07    Layout = "~/Views/Shared/_Layout.cshtml";
 08    string errorResult = ViewData["ErrorResult"] as string ?? "";
 9}
 10
 11<style>
 12    .card {
 13        border: none;
 14        box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075);
 15        border-radius: 0.5rem;
 16        margin-bottom: 1.5rem;
 17    }
 18
 19    .card-header {
 20        background-color: #f8f9fa;
 21        border-bottom: 1px solid #e9ecef;
 22        padding: 1.25rem 1.5rem;
 23    }
 24
 25    .card-body {
 26        padding: 1.5rem;
 27    }
 28
 29    .form-group {
 30        margin-bottom: 1rem;
 31    }
 32
 33    .form-label {
 34        font-weight: 600;
 35        color: #495057;
 36        margin-bottom: 0.25rem;
 37        display: block;
 38        font-size: 0.9rem;
 39    }
 40
 41    .form-control, .form-select {
 42        border: 1px solid #e9ecef;
 43        border-radius: 0.25rem;
 44        padding: 0.5rem 0.75rem;
 45        font-size: 0.9rem;
 46        transition: border-color 0.15s ease-in-out;
 47    }
 48
 49    .form-control:focus, .form-select:focus {
 50        border-color: #007bff;
 51        box-shadow: 0 0 0 0.2rem rgba(0, 123, 255, 0.25);
 52    }
 53
 54    .btn-primary {
 55        background-color: #007bff;
 56        border-color: #007bff;
 57        padding: 0.5rem 1rem;
 58        font-size: 0.9rem;
 59    }
 60
 61    .btn-secondary {
 62        background-color: #6c757d;
 63        border-color: #6c757d;
 64        padding: 0.5rem 1rem;
 65        font-size: 0.9rem;
 66    }
 67
 68    .btn-link {
 69        color: #007bff;
 70        font-size: 0.8rem;
 71        text-decoration: none;
 72        background: transparent;
 73        padding: 0.25rem 0.5rem;
 74        border: none;
 75    }
 76
 77    .btn-link:hover {
 78        color: #0056b3;
 79        text-decoration: underline;
 80        background-color: #f8f9fa;
 81        border-radius: 0.2rem;
 82    }
 83
 84    .text-muted-small {
 85        font-size: 0.8rem;
 86        color: #6c757d;
 87    }
 88
 89    .section-title {
 90        color: #495057;
 91        font-size: 1rem;
 92        margin-bottom: 1rem;
 93        padding-bottom: 0.5rem;
 94        border-bottom: 1px solid #e9ecef;
 95    }
 96
 97    .file-section {
 98        background-color: #fafbfc;
 99        border: 1px solid #e9ecef;
 100        border-radius: 0.5rem;
 101        padding: 1.5rem;
 102        margin-bottom: 1.5rem;
 103    }
 104
 105    .form-grid {
 106        display: grid;
 107        grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
 108        gap: 1rem;
 109        margin-bottom: 1rem;
 110    }
 111
 112    .matching-row {
 113        border: 1px solid #e9ecef;
 114        border-radius: 0.25rem;
 115        padding: 1rem;
 116        margin-bottom: 1rem;
 117        background-color: #fff;
 118    }
 119
 120    .dropdown-menu {
 121        max-width: 400px;
 122        box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
 123    }
 124
 125    .list-group-item {
 126        border: none;
 127        border-bottom: 1px solid #e9ecef;
 128        padding: 0.75rem 1rem;
 129    }
 130
 131    .list-group-item:last-child {
 132        border-bottom: none;
 133    }
 134
 135    .nav-header {
 136        background-color: #f8f9fa;
 137        border-bottom: 1px solid #e9ecef;
 138        padding: 1rem 1.5rem;
 139    }
 140
 141    .progress {
 142        margin: 20px 0;
 143    }
 144
 145    /* Модальные окна */
 146    .modal-content {
 147        border: none;
 148        border-radius: 0.5rem;
 149        box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15);
 150    }
 151
 152    .modal-header {
 153        background-color: #f8f9fa;
 154        border-bottom: 1px solid #e9ecef;
 155        padding: 1.25rem 1.5rem;
 156    }
 157
 158    .modal-footer {
 159        border-top: 1px solid #e9ecef;
 160        padding: 1rem 1.5rem;
 161    }
 162</style>
 163
 164<!-- Модальное окно сохранения сопоставлений -->
 165<div class="modal fade" id="saveMappingModal" tabindex="-1" role="dialog" aria-labelledby="saveMappingModalLabel"
 166     aria-hidden="true">
 167    <div class="modal-dialog" role="document">
 168        <div class="modal-content">
 169            <div class="modal-header">
 170                <h5 class="modal-title" id="saveMappingModalLabel">Сохранить состояние сопоставлений</h5>
 171                <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
 172            </div>
 173            <div class="modal-body">
 174                <p class="mb-3">Введите название для сохранения (необязательно):</p>
 175                <input type="text" id="saveMappingName" class="form-control" placeholder="Название сопоставления">
 176            </div>
 177            <div class="modal-footer">
 178                <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Отмена</button>
 179                <button type="button" class="btn btn-primary" onclick="submitFormWithSave()">
 180                    <i class="fas fa-save me-1"></i>Сохранить и загрузить
 181                </button>
 182                <button type="button" class="btn btn-outline-primary" onclick="submitFormWithoutSave()">
 183                    Продолжить без сохранения
 184                </button>
 185            </div>
 186        </div>
 187    </div>
 188</div>
 189
 190<div class="container">
 191    <!-- Навигационная панель -->
 192    <div class="nav-header">
 193        <div class="dropdown">
 194            <button class="btn btn-outline-secondary dropdown-toggle" type="button" id="savedMappingsDropdown"
 195                    data-bs-toggle="dropdown" aria-expanded="false">
 196                <i class="fas fa-folder-open me-2"></i>Загруженные сопоставления
 197            </button>
 198            <ul class="dropdown-menu" aria-labelledby="savedMappingsDropdown"
 199                style="max-height: 400px; overflow-y: auto;">
 0200                @foreach (var matchedResult in Model.MatchedResults)
 201                {
 202                    <li>
 203                        <div class="dropdown-item">
 204                            <div class="d-flex w-100 justify-content-between">
 0205                                <h6 class="mb-1">@matchedResult.MainFileName / @matchedResult.ScondaryFileName</h6>
 0206                                <small class="text-muted">@matchedResult.СreationDate.ToString("dd.MM.yyyy")</small>
 207                            </div>
 208                            <div class="d-flex justify-content-between mt-2">
 209                                <a href="/MatchFileData/GetMatchResult?id=@matchedResult.Id"
 210                                   class="btn btn-sm btn-outline-primary">
 211                                    <i class="fas fa-eye me-1"></i>Просмотр
 212                                </a>
 213                                <a href="/MatchFileData/ExtendMatchedData?id=@matchedResult.Id"
 214                                   class="btn btn-sm btn-outline-secondary">
 215                                    <i class="fas fa-plus me-1"></i>Дополнить
 216                                </a>
 217                                <a href="/MatchFileData/DeleteMatchResult?id=@matchedResult.Id"
 218                                   class="btn btn-sm btn-outline-danger"
 219                                   data-st2-confirm="Вы уверены, что хотите удалить это сопоставление?"
 220                                   data-st2-confirm-variant="danger"
 221                                   data-st2-confirm-text="Удалить">
 222                                    <i class="fas fa-trash me-1"></i>Удалить
 223                                </a>
 224                            </div>
 225                        </div>
 226                    </li>
 227                    <li>
 228                        <hr class="dropdown-divider">
 229                    </li>
 230                }
 231            </ul>
 232        </div>
 233    </div>
 234
 235    <div class="row">
 236        <div class="col-12">
 237            <!-- Основная карточка -->
 238            <div class="card">
 239                <div class="card-header">
 240                    <h2 style="margin: 0; font-size: 1.5rem;">Загрузка файлов для сопоставления</h2>
 241                </div>
 242                <div class="card-body">
 0243                    @if (errorResult != null)
 244                    {
 245                        <div class="alert alert-danger alert-dismissible fade show mb-4" role="alert">
 0246                            <i class="fas fa-exclamation-circle me-2"></i>@Html.Raw(errorResult)
 247                            <button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
 248                        </div>
 249                    }
 250
 251                    <!-- Сохраненные сопоставления -->
 252                    <div class="form-group mb-4">
 253                        <label for="savedMappings" class="form-label">Выберите сохраненное сопоставление</label>
 254                        <select id="savedMappings" class="form-select custom-select-init" onchange="loadSavedMapping(thi
 255                            <option value="">-- Выберите сохраненное сопоставление --</option>
 0256                            @foreach (var mapping in Model.SavedMatchingColumns)
 257                            {
 0258                                <option value="@mapping.Id">@mapping.Name</option>
 259                            }
 260                        </select>
 261                        <div class="text-muted-small mt-1">
 262                            <i class="fas fa-info-circle me-1"></i>Загрузите настройки из ранее сохраненного
 263                            сопоставления
 264                        </div>
 265                    </div>
 266
 267                    <!-- Форма загрузки файлов -->
 0268                    @using (Html.BeginForm("MatchFiles", "MatchFileData", FormMethod.Post, new { enctype = "multipart/fo
 269                    {
 270                        <div class="form-grid">
 271                            <!-- Файл 1: Заказы -->
 272                            <div class="file-section">
 273                                <h5 class="section-title">
 274                                    <i class="fas fa-file-alt me-2"></i>Файл заказов
 275                                </h5>
 276                                <div class="form-group">
 277                                    <label for="fileInput1" class="form-label">Файл заказов</label>
 278                                    <input type="file" class="form-control" id="fileInput1" name="file1"
 279                                           accept=".xlsx,.csv" required>
 280                                    <div class="invalid-feedback">
 281                                        Пожалуйста, выберите файл заказов.
 282                                    </div>
 283                                </div>
 284                                <div id="startInputs1" style="display: none;">
 285                                    <div class="row">
 286                                        <div class="col-md-6">
 287                                            <label for="startRowInput1" class="form-label">Начальная строка</label>
 288                                            <input type="number" class="form-control" id="startRowInput1"
 289                                                   name="startRow1" value="1" min="1">
 290                                        </div>
 291                                        <div class="col-md-6">
 292                                            <label for="startColumnInput1" class="form-label">Начальный столбец</label>
 293                                            <input type="number" class="form-control" id="startColumnInput1"
 294                                                   name="startColumn1" value="1" min="1">
 295                                        </div>
 296                                    </div>
 297                                </div>
 298                            </div>
 299
 300                            <!-- Файл 2: Платежи -->
 301                            <div class="file-section">
 302                                <h5 class="section-title">
 303                                    <i class="fas fa-file-invoice me-2"></i>Файл платежей
 304                                </h5>
 305                                <div class="form-group">
 306                                    <label for="fileInput2" class="form-label">Файл платежей</label>
 307                                    <input type="file" class="form-control" id="fileInput2" name="file2"
 308                                           accept=".xlsx,.csv" required>
 309                                    <div class="invalid-feedback">
 310                                        Пожалуйста, выберите файл платежей.
 311                                    </div>
 312                                </div>
 313                                <div id="startInputs2" style="display: none;">
 314                                    <div class="row">
 315                                        <div class="col-md-6">
 316                                            <label for="startRowInput2" class="form-label">Начальная строка</label>
 317                                            <input type="number" class="form-control" id="startRowInput2"
 318                                                   name="startRow2" value="1" min="1">
 319                                        </div>
 320                                        <div class="col-md-6">
 321                                            <label for="startColumnInput2" class="form-label">Начальный столбец</label>
 322                                            <input type="number" class="form-control" id="startColumnInput2"
 323                                                   name="startColumn2" value="1" min="1">
 324                                        </div>
 325                                    </div>
 326                                </div>
 327                            </div>
 328                        </div>
 329
 330                        <!-- Поля сопоставления -->
 331                        <div class="form-group">
 332                            <div class="d-flex justify-content-between align-items-center mb-3">
 333                                <h5 class="section-title mb-0">
 334                                    <i class="fas fa-link me-2"></i>Поля для сопоставления
 335                                </h5>
 336                                <button type="button" id="addMatchingFields" class="btn btn-link">
 337                                    <i class="fas fa-plus me-1"></i>Добавить сопоставление
 338                                </button>
 339                            </div>
 340
 341                            <div id="matchingFieldsContainer">
 342                                <div class="matching-row">
 343                                    <div class="row">
 344                                        <div class="col-md-6">
 345                                            <label class="form-label">Столбец из файла заказов</label>
 346                                            <input type="text" class="form-control" name="matchingColumns[0].Item1"
 347                                                   required placeholder="Название столбца">
 348                                        </div>
 349                                        <div class="col-md-6">
 350                                            <label class="form-label">Столбец из файла платежей</label>
 351                                            <input type="text" class="form-control" name="matchingColumns[0].Item2"
 352                                                   required placeholder="Название столбца">
 353                                        </div>
 354                                    </div>
 355                                </div>
 356                            </div>
 357                        </div>
 358
 359                        <input type="hidden" id="matchName" name="matchName">
 360
 361                        <!-- Кнопка отправки -->
 362                        <div class="form-group mt-4">
 363                            <button type="button" class="btn btn-primary" id="submitButton" disabled
 364                                    onclick="showSaveModal()">
 365                                <i class="fas fa-play me-1"></i>Загрузить и сопоставить
 366                            </button>
 367                        </div>
 0368                    }
 369
 0370                    @if (!string.IsNullOrEmpty(ViewBag.Message))
 371                    {
 372                        <div class="alert alert-info mt-4">
 0373                            <i class="fas fa-info-circle me-2"></i>@ViewBag.Message
 374                        </div>
 375                    }
 376                </div>
 377            </div>
 378        </div>
 379    </div>
 380</div>
 381
 382<script>
 383    let matchCount = 1;
 384
 385    // Показать модальное окно сохранения
 386    function showSaveModal() {
 387        $('#saveMappingModal').modal('show');
 388    }
 389
 390    // Отправить форму с сохранением
 391    function submitFormWithSave() {
 392        const matchName = document.getElementById('saveMappingName').value;
 393        document.getElementById('matchName').value = matchName.trim();
 394        $('#saveMappingModal').modal('hide');
 395        document.getElementById('uploadForm').submit();
 396    }
 397
 398    // Отправить форму без сохранения
 399    function submitFormWithoutSave() {
 400        $('#saveMappingModal').modal('hide');
 401        document.getElementById('uploadForm').submit();
 402    }
 403
 404    // Обновить состояние кнопки отправки
 405    function updateSubmitButtonState() {
 406        const file1 = document.getElementById('fileInput1').files.length > 0;
 407        const file2 = document.getElementById('fileInput2').files.length > 0;
 408        document.getElementById('submitButton').disabled = !(file1 && file2);
 409    }
 410
 411    // Обработчики файлов
 412    document.getElementById('fileInput1').addEventListener('change', function () {
 413        toggleStartInputs('startInputs1', this.files[0]);
 414        updateSubmitButtonState();
 415    });
 416
 417    document.getElementById('fileInput2').addEventListener('change', function () {
 418        toggleStartInputs('startInputs2', this.files[0]);
 419        updateSubmitButtonState();
 420    });
 421
 422    function toggleStartInputs(inputsId, file) {
 423        const inputs = document.getElementById(inputsId);
 424        if (file) {
 425            const extension = file.name.split('.').pop().toLowerCase();
 426            inputs.style.display = (extension === 'csv' || extension === 'xlsx') ? 'block' : 'none';
 427        } else {
 428            inputs.style.display = 'none';
 429        }
 430    }
 431
 432    // Добавление полей сопоставления
 433    document.getElementById('addMatchingFields').addEventListener('click', function () {
 434        const container = document.getElementById('matchingFieldsContainer');
 435        const newRow = document.createElement('div');
 436        newRow.className = 'matching-row';
 437        newRow.id = `matchRow${matchCount}`;
 438        newRow.innerHTML = `
 439            <div class="row">
 440                <div class="col-md-6">
 441                    <label class="form-label">Столбец из файла заказов</label>
 442                    <input type="text" class="form-control" name="matchingColumns[${matchCount}].Item1" required placeho
 443                </div>
 444                <div class="col-md-6">
 445                    <label class="form-label">Столбец из файла платежей</label>
 446                    <input type="text" class="form-control" name="matchingColumns[${matchCount}].Item2" required placeho
 447                </div>
 448            </div>
 449            <div class="text-end mt-2">
 450                <button type="button" class="btn btn-link btn-sm text-danger" onclick="removeMatchingField(${matchCount}
 451                    <i class="fas fa-times me-1"></i>Удалить
 452                </button>
 453            </div>
 454        `;
 455        container.appendChild(newRow);
 456        matchCount++;
 457    });
 458
 459    // Удаление поля сопоставления
 460    function removeMatchingField(index) {
 461        const row = document.getElementById(`matchRow${index}`);
 462        if (row) row.remove();
 463    }
 464
 465    // Загрузка сохраненного сопоставления
 466    function loadSavedMapping(mappingId) {
 467        if (!mappingId) return;
 468
 469        fetch(`/MatchFileData/GetMappingById?id=${mappingId}`)
 470            .then(response => response.json())
 471            .then(data => populateMatchingFields(data))
 472            .catch(error => console.error('Ошибка:', error));
 473    }
 474
 475    function populateMatchingFields(data) {
 476        const container = document.getElementById('matchingFieldsContainer');
 477        container.innerHTML = '';
 478
 479        // Установка начальных позиций
 480        document.getElementById('startRowInput1').value = data.startRow1 || 1;
 481        document.getElementById('startColumnInput1').value = data.startColumn1 || 1;
 482        document.getElementById('startRowInput2').value = data.startRow2 || 1;
 483        document.getElementById('startColumnInput2').value = data.startColumn2 || 1;
 484
 485        // Показать поля начальных позиций
 486        document.getElementById('startInputs1').style.display = 'block';
 487        document.getElementById('startInputs2').style.display = 'block';
 488
 489        // Заполнение полей сопоставления
 490        data.matchingColumns.forEach((column, index) => {
 491            const row = document.createElement('div');
 492            row.className = 'matching-row';
 493            row.id = `matchRow${index}`;
 494            row.innerHTML = `
 495                <div class="row">
 496                    <div class="col-md-6">
 497                        <label class="form-label">Столбец из файла заказов</label>
 498                        <input type="text" class="form-control" name="matchingColumns[${index}].Item1" required
 499                               value="${column.item1}" placeholder="Название столбца">
 500                    </div>
 501                    <div class="col-md-6">
 502                        <label class="form-label">Столбец из файла платежей</label>
 503                        <input type="text" class="form-control" name="matchingColumns[${index}].Item2" required
 504                               value="${column.item2}" placeholder="Название столбца">
 505                    </div>
 506                </div>
 507                ${index > 0 ? `
 508                <div class="text-end mt-2">
 509                    <button type="button" class="btn btn-link btn-sm text-danger" onclick="removeMatchingField(${index})
 510                        <i class="fas fa-times me-1"></i>Удалить
 511                    </button>
 512                </div>
 513                ` : ''}
 514            `;
 515            container.appendChild(row);
 516        });
 517
 518        matchCount = data.matchingColumns.length;
 519    }
 520
 521    // Валидация формы
 522    document.addEventListener('DOMContentLoaded', function () {
 523        const form = document.getElementById('uploadForm');
 524        form.addEventListener('submit', function (event) {
 525            if (!form.checkValidity()) {
 526                event.preventDefault();
 527                event.stopPropagation();
 528            }
 529            form.classList.add('was-validated');
 530        }, false);
 531    });
 532</script>

Methods/Properties

ExecuteAsync()
<ExecuteAsync()