235 lines
12 KiB
HTML
235 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="ru">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Сверка выручки водителей</title>
|
|
<link rel="stylesheet" href="styles.css">
|
|
<!-- Библиотека XLSX для чтения/записи Excel-файлов -->
|
|
<script src="https://cdn.jsdelivr.net/npm/xlsx@0.19.2/dist/xlsx.full.min.js"></script>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<header class="hero">
|
|
<div class="hero-badge">🔒 Работает офлайн — файлы не отправляются на сервер</div>
|
|
<h1>Сверка выручки водителей</h1>
|
|
<p class="hero-subtitle">Загрузите ведомости приёма наличных и выгрузку транзакций — получите сводную таблицу, расхождения и готовый Excel-отчёт</p>
|
|
</header>
|
|
|
|
<!-- Статус-бар -->
|
|
<div class="status-bar" id="statusBar" style="display:none"></div>
|
|
|
|
<!-- Файлы -->
|
|
<section class="card">
|
|
<div class="card-head">
|
|
<div class="step-badge">1</div>
|
|
<h2>Файлы</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="dropzone" id="dropzone">
|
|
<div class="dropzone-icon">
|
|
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="#6366f1" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/>
|
|
<polyline points="17 8 12 3 7 8"/>
|
|
<line x1="12" y1="3" x2="12" y2="15"/>
|
|
</svg>
|
|
</div>
|
|
<div class="dropzone-title">Перетащите файлы сюда или нажмите для выбора</div>
|
|
<div class="dropzone-hint">
|
|
Любое число ведомостей (<strong>.xlsx</strong>) + выгрузка <strong>TRANSACTIONS_EXT</strong>.
|
|
Тип определяется автоматически.
|
|
</div>
|
|
</div>
|
|
<input type="file" id="fileInput" multiple accept=".xlsx,.xls" style="display:none">
|
|
<div class="file-list" id="fileList"></div>
|
|
<div class="actions">
|
|
<button class="btn btn-primary" id="btnReconcile" disabled>✓ Провести сверку</button>
|
|
<button class="btn btn-outline" id="btnClear" disabled>✕ Очистить</button>
|
|
<button class="btn btn-export" id="btnExportCsv" disabled>⬇ Экспорт CSV</button>
|
|
<button class="btn btn-theme" id="btnToggleTheme">🌙 Тёмная тема</button>
|
|
</div>
|
|
<div class="progress-bar" id="progressBar">
|
|
<div class="progress-fill" id="progressFill"></div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Результат -->
|
|
<section class="card" id="resultCard">
|
|
<div class="card-head">
|
|
<div class="step-badge">2</div>
|
|
<h2>Результат</h2>
|
|
</div>
|
|
<div class="card-body">
|
|
<div id="emptyState" class="empty-state">
|
|
<div class="empty-state-icon">📋</div>
|
|
<p>Загрузите файлы и нажмите «Провести сверку»</p>
|
|
</div>
|
|
<div id="resultActions" style="display:none;margin-bottom:1.5rem;">
|
|
<button class="btn btn-success" id="btnDownload">📥 Скачать Excel-отчёт</button>
|
|
</div>
|
|
<div id="tabsContainer" style="display:none;">
|
|
<div class="tabs-wrapper">
|
|
<button class="tab-btn active" data-tab="discrepancies">
|
|
Расхождения <span class="count" id="countDisc">0</span>
|
|
</button>
|
|
<button class="tab-btn" data-tab="all">
|
|
Все водители <span class="count" id="countAll">0</span>
|
|
</button>
|
|
<button class="tab-btn" data-tab="cashiers">По кассирам</button>
|
|
<button class="tab-btn" data-tab="routes">По маршрутам</button>
|
|
<button class="tab-btn" data-tab="conclusions">Выводы</button>
|
|
</div>
|
|
|
|
<!-- Вкладка: Расхождения -->
|
|
<div class="tab-panel active" id="panel-discrepancies">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th data-key="driver">Водитель</th>
|
|
<th data-key="car">Гос. номер</th>
|
|
<th data-key="given">Сдано (₽)</th>
|
|
<th data-key="collected">Собрано (₽)</th>
|
|
<th data-key="diff">Расхождение</th>
|
|
<th>Статус</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tableDiscrepancies"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Вкладка: Все водители -->
|
|
<div class="tab-panel" id="panel-all">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th data-key="date">Дата</th>
|
|
<th data-key="driver">Водитель</th>
|
|
<th data-key="route">Маршрут</th>
|
|
<th data-key="section">Тип</th>
|
|
<th data-key="given">Сдано (₽)</th>
|
|
<th data-key="collected">Собрано (₽)</th>
|
|
<th data-key="txCount">Транзакций</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tableAll"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Вкладка: По кассирам -->
|
|
<div class="tab-panel" id="panel-cashiers">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th data-key="cashier">Кассир</th>
|
|
<th>Ведомостей</th>
|
|
<th data-key="given">Сдано (₽)</th>
|
|
<th data-key="collected">Собрано (₽)</th>
|
|
<th data-key="diff">Расхождение</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tableCashiers"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Вкладка: По маршрутам -->
|
|
<div class="tab-panel" id="panel-routes">
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th data-key="route">Маршрут</th>
|
|
<th>Тип</th>
|
|
<th>Водителей</th>
|
|
<th data-key="sum">Сумма (₽)</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="tableRoutes"></tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Вкладка: Выводы -->
|
|
<div class="tab-panel" id="panel-conclusions">
|
|
<div id="conclusionsContent"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Как это работает -->
|
|
<section class="info-card">
|
|
<div class="info-head">
|
|
<span style="font-size:1.4rem">💡</span>
|
|
<h2>Как это работает</h2>
|
|
</div>
|
|
<div class="info-grid">
|
|
<div class="info-block">
|
|
<div class="info-block-icon blue">📄</div>
|
|
<div>
|
|
<div class="info-block-title">Определение типа файла</div>
|
|
<div class="info-block-text">
|
|
Ведомость распознаётся по заголовку «ВЕДОМОСТЬ ПРИЕМА НАЛИЧНЫХ СРЕДСТВ»;
|
|
из неё берутся номер документа, дата, ФИО кассира, разделы «Город» / «Пригород»
|
|
и строки водителей. Выгрузка распознаётся по столбцам CONDUCTOR, DATE и TARIF_SUM.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="info-block">
|
|
<div class="info-block-icon green">📅</div>
|
|
<div>
|
|
<div class="info-block-title">Период</div>
|
|
<div class="info-block-text">
|
|
Из транзакций берутся только даты, указанные в загруженных ведомостях.
|
|
Остальные даты в сверку не входят.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="info-block">
|
|
<div class="info-block-icon amber">🔍</div>
|
|
<div>
|
|
<div class="info-block-title">Ключ сопоставления</div>
|
|
<div class="info-block-text">
|
|
ФИО водителя + гос. номер ТС. Номера нормализуются (латинские A, B, C, E, H, K, M, O, P, T, X, Y
|
|
заменяются на кириллические), ФИО — верхний регистр, Ё→Е.
|
|
Опечатки в ФИО сводятся нечётким сравнением при схожести ≥ 88%.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="info-block">
|
|
<div class="info-block-icon purple">💰</div>
|
|
<div>
|
|
<div class="info-block-title">Суммы</div>
|
|
<div class="info-block-text">
|
|
«Сдано» — графа «Итого на сумму» ведомости.
|
|
«Собрано» — TARIF_SUM ÷ 100 (копейки → рубли) по всем транзакциям водителя за выбранные даты.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="info-block">
|
|
<div class="info-block-icon rose">🔒</div>
|
|
<div>
|
|
<div class="info-block-title">Приватность</div>
|
|
<div class="info-block-text">
|
|
Файлы никуда не отправляются — весь расчёт выполняется в браузере на вашем компьютере.
|
|
Приложение работает полностью офлайн.
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<div class="footer">
|
|
Сверка выручки водителей — все вычисления выполняются локально в вашем браузере
|
|
</div>
|
|
</div>
|
|
|
|
<script type="module" src="main.js"></script>
|
|
</body>
|
|
</html> |