]> gitweb.erp-flowers.ru Git - erp24_rep/yii-erp24/.git/commitdiff
Мониторинг чеков по выгрузкам из 1С
authorVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Wed, 22 Oct 2025 15:06:28 +0000 (18:06 +0300)
committerVladimir Fomichev <vladimir.fomichev@erp-flowers.ru>
Wed, 22 Oct 2025 15:06:28 +0000 (18:06 +0300)
erp24/controllers/DashboardController.php
erp24/views/dashboard/check-mismatch.php [new file with mode: 0644]

index 58c6c2ccfd44a73df7fcc7193126dd1211658bed..bb880b04ebb6b048bce10d20a89ac3912087e472 100755 (executable)
@@ -20,6 +20,7 @@ class DashboardController extends \yii\web\Controller
             'commercial-up-vote' => \yii_app\actions\dashboard\CommercialUpVoteAction::class,
             'commercial-down-vote' => \yii_app\actions\dashboard\CommercialDownVoteAction::class,
             'validate' => \yii_app\actions\dashboard\ValidateAction::class,
+            'check-mismatch' => \yii_app\actions\dashboard\CheckMismatchAction::class,
         ];
     }
 }
diff --git a/erp24/views/dashboard/check-mismatch.php b/erp24/views/dashboard/check-mismatch.php
new file mode 100644 (file)
index 0000000..13fb5e4
--- /dev/null
@@ -0,0 +1,343 @@
+<?php
+
+use yii\helpers\Html;
+use yii\widgets\ActiveForm;
+
+/* @var $this yii\web\View */
+/* @var $uploadedData array */
+/* @var $mismatchedChecks array */
+/* @var $availableDates array */
+/* @var $storeGuidNames array */
+
+$this->title = 'Проверка несовпадения дат чеков';
+?>
+
+<div class="check-mismatch-container" style="max-width: 1400px; margin: 0 auto; padding: 20px;">
+    <h1><?= Html::encode($this->title) ?></h1>
+    
+    <!-- Форма загрузки файла -->
+    <div class="upload-section" style="background-color: #f9f9f9; padding: 20px; border-radius: 5px; margin-bottom: 20px;">
+        <h2>Загрузить JSON файл</h2>
+        <?php $form = ActiveForm::begin(['options' => ['enctype' => 'multipart/form-data', 'method' => 'post']]); ?>
+            <div class="form-group">
+                <label for="jsonFile">Выберите JSON файл:</label>
+                <input type="file" id="jsonFile" name="jsonFile" accept=".json" class="form-control" required>
+            </div>
+            <div class="form-group">
+                <?= Html::submitButton('Загрузить и анализировать', ['class' => 'btn btn-primary']) ?>
+            </div>
+        <?php ActiveForm::end(); ?>
+    </div>
+
+    <!-- Фильтры (отображаются только если файл был загружен) -->
+    <?php if ($uploadedData && count($availableDates) > 0): ?>
+        <div class="filters-section" style="background-color: #f5f5f5; padding: 15px; border-radius: 5px; margin-bottom: 20px;">
+            <h3>Фильтры</h3>
+            <form method="get" id="filters-form" style="display: flex; gap: 15px; flex-wrap: wrap; align-items: center;">
+                <!-- Фильтр по дате -->
+                <div style="flex: 1; min-width: 200px;">
+                    <label for="dateFilter" style="display: block; margin-bottom: 5px;">Дата:</label>
+                    <select id="dateFilter" name="date" class="form-control" onchange="applyFilters()">
+                        <option value="">Все даты</option>
+                        <?php foreach ($availableDates as $date): ?>
+                            <option value="<?= Html::encode($date) ?>">
+                                <?= Html::encode($date) ?>
+                            </option>
+                        <?php endforeach; ?>
+                    </select>
+                </div>
+
+                <!-- Фильтр по магазину (динамически заполняется из данных) -->
+                <div style="flex: 1; min-width: 200px;">
+                    <label for="storeFilter" style="display: block; margin-bottom: 5px;">Магазин:</label>
+                    <select id="storeFilter" name="store" class="form-control" onchange="applyFilters()">
+                        <option value="">Все магазины</option>
+                        <?php
+                        $stores = [];
+                        foreach ($mismatchedChecks as $check) {
+                            if (!empty($check['store_id']) && !isset($stores[$check['store_id']])) {
+                                $stores[$check['store_id']] = $check['store_id'];
+                            }
+                        }
+                        foreach ($stores as $storeId): ?>
+                            <option value="<?= Html::encode($storeId) ?>">
+                                <?= Html::encode($storeGuidNames[$storeId] ?? $storeId) ?>
+                            </option>
+                        <?php endforeach; ?>
+                    </select>
+                </div>
+
+                <!-- Кнопка очистки фильтров -->
+                <div style="padding-top: 20px;">
+                    <button type="button" class="btn btn-secondary" onclick="clearFilters()">Очистить фильтры</button>
+                </div>
+            </form>
+        </div>
+
+        <!-- Информация о результатах -->
+        <div class="results-info" style="background-color: #e8f4f8; padding: 15px; border-radius: 5px; margin-bottom: 20px; border-left: 4px solid #0275d8;">
+            <strong>Всего несовпадений:</strong> <?= count($mismatchedChecks) ?><br>
+            <strong>Всего чеков в файле:</strong> <?= count($uploadedData['checks']) ?><br>
+            <strong>Чеков с order_id:</strong> <?= count(array_filter($uploadedData['checks'], function($c) { return !empty($c['order_id']); })) ?>
+        </div>
+
+        <!-- Таблица результатов -->
+        <?php if (count($mismatchedChecks) > 0): ?>
+            <div class="results-table" style="overflow-x: auto;">
+                <h3>Несовпадающие чеки</h3>
+                <table class="table table-striped table-hover" style="background-color: white;">
+                    <thead style="background-color: #f8f9fa;">
+                        <tr>
+                            <th>Магазин</th>
+                            <th>ID чека</th>
+                            <th>Order ID</th>
+                            <th>Дата чека (JSON)</th>
+                            <th>Дата чека (Create Checks)</th>
+                            <th>Дата продажи (Sales)</th>
+                            <th>Тип</th>
+                            <th>Статус</th>
+                            <th>Номер чека</th>
+                        </tr>
+                    </thead>
+                    <tbody>
+                        <?php foreach ($mismatchedChecks as $check): ?>
+                            <tr style="border-left: 3px solid #dc3545;">
+                                <td>
+                                    <strong><?= Html::encode($storeGuidNames[$check['store_id']] ?? $check['store_id']) ?></strong><br>
+                                    <code style="font-size: 10px;"><?= Html::encode($check['store_id']) ?></code>
+                                </td>
+                                <td><code style="font-size: 11px;"><?= Html::encode(substr($check['check_id'], 0, 20) . '...') ?></code></td>
+                                <td><strong><?= Html::encode($check['order_id']) ?></strong></td>
+                                <td>
+                                    <span style="background-color: #fff3cd; padding: 2px 6px; border-radius: 3px;">
+                                        <?= Html::encode($check['check_date']) ?>
+                                    </span>
+                                </td>
+                                <td>
+                                    <span style="background-color: #f8d7da; padding: 2px 6px; border-radius: 3px;">
+                                        <?= Html::encode($check['create_check_date']) ?>
+                                    </span>
+                                </td>
+                                <td>
+                                    <span style="background-color: #d4edda; padding: 2px 6px; border-radius: 3px;">
+                                        <?= Html::encode($check['sales_date']) ?>
+                                    </span>
+                                </td>
+                                <td><?= Html::encode($check['check_type']) ?></td>
+                                <td><?= Html::encode($check['check_status']) ?></td>
+                                <td><?= Html::encode($check['check_number']) ?></td>
+                            </tr>
+                        <?php endforeach; ?>
+                    </tbody>
+                </table>
+            </div>
+        <?php else: ?>
+            <div class="alert alert-success" role="alert">
+                ✓ Несовпадающих чеков не найдено. Все даты корректны!
+            </div>
+        <?php endif; ?>
+
+    <?php elseif ($uploadedData): ?>
+        <div class="alert alert-info" role="alert">
+            ℹ️ Файл загружен, но чеков с order_id не найдено или на нет несовпадений.
+        </div>
+    <?php endif; ?>
+</div>
+
+<style>
+    .check-mismatch-container {
+        font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
+    }
+
+    .upload-section, .filters-section {
+        box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
+    }
+
+    .table {
+        width: 100%;
+        border-collapse: collapse;
+    }
+
+    .table thead th {
+        font-weight: 600;
+        text-align: left;
+        padding: 12px;
+        border-bottom: 2px solid #dee2e6;
+    }
+
+    .table tbody td {
+        padding: 10px 12px;
+        border-bottom: 1px solid #dee2e6;
+    }
+
+    .table tbody tr:hover {
+        background-color: #f9f9f9;
+    }
+
+    .btn {
+        padding: 8px 16px;
+        border-radius: 4px;
+        border: none;
+        cursor: pointer;
+        font-size: 14px;
+        font-weight: 500;
+    }
+
+    .btn-primary {
+        background-color: #0275d8;
+        color: white;
+    }
+
+    .btn-primary:hover {
+        background-color: #025aa5;
+    }
+
+    .btn-secondary {
+        background-color: #6c757d;
+        color: white;
+    }
+
+    .btn-secondary:hover {
+        background-color: #5a6268;
+    }
+
+    .form-control {
+        width: 100%;
+        padding: 8px 12px;
+        border: 1px solid #ced4da;
+        border-radius: 4px;
+        font-size: 14px;
+    }
+
+    .form-control:focus {
+        outline: none;
+        border-color: #0275d8;
+        box-shadow: 0 0 0 3px rgba(2, 117, 216, 0.25);
+    }
+
+    .form-group {
+        margin-bottom: 15px;
+    }
+
+    code {
+        background-color: #f5f5f5;
+        padding: 2px 6px;
+        border-radius: 3px;
+        font-family: 'Courier New', monospace;
+    }
+
+    .alert {
+        padding: 15px;
+        border-radius: 4px;
+        margin-bottom: 20px;
+    }
+
+    .alert-success {
+        background-color: #d4edda;
+        color: #155724;
+        border: 1px solid #c3e6cb;
+    }
+
+    .alert-info {
+        background-color: #d1ecf1;
+        color: #0c5460;
+        border: 1px solid #bee5eb;
+    }
+</style>
+
+<script>
+// Сохранение оригинальных данных чеков в памяти
+const allMismatchedChecks = <?= json_encode($mismatchedChecks, JSON_UNESCAPED_UNICODE) ?>;
+const storeGuidNames = <?= json_encode($storeGuidNames, JSON_UNESCAPED_UNICODE) ?>;
+
+function applyFilters() {
+    const dateFilter = document.getElementById('dateFilter').value;
+    const storeFilter = document.getElementById('storeFilter').value;
+    
+    // Фильтруем данные
+    let filteredChecks = allMismatchedChecks;
+    
+    if (dateFilter) {
+        filteredChecks = filteredChecks.filter(check => check.sales_date.startsWith(dateFilter));
+    }
+    
+    if (storeFilter) {
+        filteredChecks = filteredChecks.filter(check => check.store_id === storeFilter);
+    }
+    
+    // Обновляем таблицу
+    updateTable(filteredChecks);
+    
+    // Обновляем информацию о результатах
+    document.querySelector('.results-info').innerHTML = `
+        <strong>Всего несовпадений:</strong> ${filteredChecks.length}<br>
+        <strong>Всего чеков в файле:</strong> ${allMismatchedChecks.length}<br>
+        <strong>Чеков с order_id:</strong> ${allMismatchedChecks.length}
+    `;
+}
+
+function updateTable(checks) {
+    const tbody = document.querySelector('.table tbody');
+    
+    if (!tbody) return;
+    
+    if (checks.length === 0) {
+        tbody.innerHTML = `
+            <tr>
+                <td colspan="9" style="text-align: center; padding: 30px; color: #999;">
+                    По выбранным фильтрам результатов не найдено
+                </td>
+            </tr>
+        `;
+        return;
+    }
+    
+    tbody.innerHTML = checks.map(check => `
+        <tr style="border-left: 3px solid #dc3545;">
+            <td>
+                <strong>${escapeHtml(storeGuidNames[check.store_id] || check.store_id)}</strong><br>
+                <code style="font-size: 10px;">${escapeHtml(check.store_id)}</code>
+            </td>
+            <td><code style="font-size: 11px;">${escapeHtml(check.check_id.substring(0, 20) + '...')}</code></td>
+            <td><strong>${escapeHtml(check.order_id)}</strong></td>
+            <td>
+                <span style="background-color: #fff3cd; padding: 2px 6px; border-radius: 3px;">
+                    ${escapeHtml(check.check_date)}
+                </span>
+            </td>
+            <td>
+                <span style="background-color: #f8d7da; padding: 2px 6px; border-radius: 3px;">
+                    ${escapeHtml(check.create_check_date)}
+                </span>
+            </td>
+            <td>
+                <span style="background-color: #d4edda; padding: 2px 6px; border-radius: 3px;">
+                    ${escapeHtml(check.sales_date)}
+                </span>
+            </td>
+            <td>${escapeHtml(check.check_type)}</td>
+            <td>${escapeHtml(check.check_status)}</td>
+            <td>${escapeHtml(check.check_number)}</td>
+        </tr>
+    `).join('');
+}
+
+function clearFilters() {
+    document.getElementById('dateFilter').value = '';
+    document.getElementById('storeFilter').value = '';
+    updateTable(allMismatchedChecks);
+    
+    // Обновляем информацию о результатах
+    document.querySelector('.results-info').innerHTML = `
+        <strong>Всего несовпадений:</strong> ${allMismatchedChecks.length}<br>
+        <strong>Всего чеков в файле:</strong> ${allMismatchedChecks.length}<br>
+        <strong>Чеков с order_id:</strong> ${allMismatchedChecks.length}
+    `;
+}
+
+function escapeHtml(text) {
+    const div = document.createElement('div');
+    div.textContent = text;
+    return div.innerHTML;
+}
+</script>