Initial commit
This commit is contained in:
56
templates/base.html
Normal file
56
templates/base.html
Normal file
@@ -0,0 +1,56 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}Контроль ЭДО{% endblock %}</title>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<div class="container header-inner">
|
||||
<a href="{{ url_for('main.documents_1c') }}" class="logo-wrap">
|
||||
<div class="logo-icon">📋</div>
|
||||
<div class="logo-text">
|
||||
<h1>Контроль ЭДО</h1>
|
||||
<span>Отслеживание документов</span>
|
||||
</div>
|
||||
</a>
|
||||
<nav class="tabs">
|
||||
<a href="{{ url_for('main.documents_1c') }}"
|
||||
class="tab {% if request.endpoint == 'main.documents_1c' %}active{% endif %}">
|
||||
<span class="tab-icon">📄</span> 1С
|
||||
</a>
|
||||
<a href="{{ url_for('main.documents_sbis') }}"
|
||||
class="tab {% if request.endpoint == 'main.documents_sbis' %}active{% endif %}">
|
||||
<span class="tab-icon">📥</span> СБИС
|
||||
</a>
|
||||
<a href="{{ url_for('main.contractors') }}"
|
||||
class="tab {% if request.endpoint == 'main.contractors' %}active{% endif %}">
|
||||
<span class="tab-icon">👥</span> Контрагенты
|
||||
</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="container">
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% if messages %}
|
||||
<div class="messages">
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ category }}">
|
||||
{% if category == 'success' %}✓{% else %}⚠{% endif %}
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endwith %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
89
templates/contractors.html
Normal file
89
templates/contractors.html
Normal file
@@ -0,0 +1,89 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Контрагенты — Контроль ЭДО{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h2>Контрагенты</h2>
|
||||
<p>Список для фильтрации импорта — название должно совпадать с Excel</p>
|
||||
</div>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-header">
|
||||
<div class="panel-icon panel-icon-purple">+</div>
|
||||
<div class="panel-title">
|
||||
<h3>Добавить контрагентов</h3>
|
||||
<p>Один контрагент — одна строка. Можно вставить список целиком.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="post" class="bulk-form">
|
||||
<input type="hidden" name="action" value="bulk_add">
|
||||
<textarea name="names" rows="6" placeholder="ООО «Ромашка» ИП Иванов АО «Строй»" class="bulk-textarea"></textarea>
|
||||
<div class="bulk-actions">
|
||||
<button type="submit" class="btn btn-primary">Добавить список</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<div class="panel-divider"></div>
|
||||
|
||||
<form method="post" class="add-form">
|
||||
<input type="hidden" name="action" value="add">
|
||||
<input type="text" name="name" placeholder="Или одно название…">
|
||||
<button type="submit" class="btn btn-ghost">Добавить одного</button>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<div class="section-label">
|
||||
Контрагенты
|
||||
<span class="count-badge">{{ contractors|length }}</span>
|
||||
</div>
|
||||
|
||||
{% if contractors %}
|
||||
<div class="contractor-grid">
|
||||
{% for contractor_id, name, active in contractors %}
|
||||
<div class="contractor-card {% if not active %}contractor-inactive{% endif %}">
|
||||
<div class="contractor-info">
|
||||
<div class="contractor-avatar">{{ name[:2] }}</div>
|
||||
<div class="contractor-meta">
|
||||
<span class="contractor-name">{{ name }}</span>
|
||||
{% if active %}
|
||||
<span class="status status-ok">Активен</span>
|
||||
{% else %}
|
||||
<span class="status status-neutral">Неактивен</span>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="contractor-actions">
|
||||
{% if active %}
|
||||
<form method="post" class="inline-form">
|
||||
<input type="hidden" name="action" value="deactivate">
|
||||
<input type="hidden" name="contractor_id" value="{{ contractor_id }}">
|
||||
<button type="submit" class="btn btn-sm">Деактивировать</button>
|
||||
</form>
|
||||
{% else %}
|
||||
<form method="post" class="inline-form">
|
||||
<input type="hidden" name="action" value="activate">
|
||||
<input type="hidden" name="contractor_id" value="{{ contractor_id }}">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Активировать</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
<form method="post" class="inline-form" onsubmit="return confirm('Удалить контрагента «{{ name }}»?');">
|
||||
<input type="hidden" name="action" value="delete">
|
||||
<input type="hidden" name="contractor_id" value="{{ contractor_id }}">
|
||||
<button type="submit" class="btn btn-danger btn-sm">Удалить</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">👥</div>
|
||||
<p>Список пуст</p>
|
||||
<p class="empty-hint">Добавьте контрагентов перед импортом документов</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
160
templates/documents_1c.html
Normal file
160
templates/documents_1c.html
Normal file
@@ -0,0 +1,160 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Документы 1С — Контроль ЭДО{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h2>Документы 1С</h2>
|
||||
<p>Загрузка выгрузки из 1С и контроль актуальных статусов ЭДО</p>
|
||||
</div>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-header">
|
||||
<div class="panel-icon panel-icon-blue">⬆</div>
|
||||
<div class="panel-title">
|
||||
<h3>Загрузка Excel из 1С</h3>
|
||||
<p>Импортируются только контрагенты из списка. Повторная загрузка обновит статус существующего документа.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-zone">
|
||||
<form method="post" enctype="multipart/form-data" class="upload-form">
|
||||
<input type="hidden" name="action" value="upload">
|
||||
<div class="file-input-wrap">
|
||||
<input type="file" name="file" accept=".xlsx,.xls" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Импортировать</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<form method="get" class="filters">
|
||||
<label>
|
||||
Контрагент
|
||||
<select name="contractor">
|
||||
<option value="">Все</option>
|
||||
{% for name in contractors %}
|
||||
<option value="{{ name }}" {% if selected_contractor == name %}selected{% endif %}>
|
||||
{{ name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Статус
|
||||
<select name="status">
|
||||
<option value="">Все</option>
|
||||
{% for st in statuses %}
|
||||
<option value="{{ st }}" {% if selected_status == st %}selected{% endif %}>
|
||||
{{ st }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Поиск
|
||||
<input type="text" name="search" value="{{ search }}" placeholder="Номер документа">
|
||||
</label>
|
||||
<button type="submit" class="btn btn-ghost">Применить</button>
|
||||
</form>
|
||||
|
||||
<div class="section-label">
|
||||
Список документов
|
||||
<span class="count-badge">{{ rows|length }}</span>
|
||||
</div>
|
||||
|
||||
{% if rows %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-note"></th>
|
||||
<th>Контрагент</th>
|
||||
<th>Тип</th>
|
||||
<th>Дата</th>
|
||||
<th>Номер</th>
|
||||
<th>Сумма</th>
|
||||
<th>Статус</th>
|
||||
<th>Обновлён</th>
|
||||
<th>Заметка</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows %}
|
||||
{% set has_comment = row.comment and row.comment.strip() %}
|
||||
<tr class="{% if has_comment %}has-note{% endif %}">
|
||||
<td class="col-note">
|
||||
{% if has_comment %}
|
||||
<span class="note-indicator" title="Есть заметка">💬</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ row.contractor or '—' }}</td>
|
||||
<td>{{ row.doc_type }}</td>
|
||||
<td class="num-cell">
|
||||
{% if row.doc_date %}
|
||||
{% if row.doc_date is string %}
|
||||
{{ row.doc_date.replace('-', '.').split(' ')[0].split('-')[::-1]|join('.') }}
|
||||
{% else %}
|
||||
{{ row.doc_date.strftime('%d.%m.%Y') }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><span class="doc-number">{{ row.doc_number }}</span></td>
|
||||
<td class="num-cell">{{ "%.2f"|format(row.amount) if row.amount is not none else '—' }}</td>
|
||||
<td>
|
||||
{% set st = (row.state or '')|lower %}
|
||||
{% if 'заверш' in st %}
|
||||
<span class="status status-ok">{{ row.state }}</span>
|
||||
{% elif 'подпис' in st %}
|
||||
<span class="status status-warn">{{ row.state }}</span>
|
||||
{% elif 'ошиб' in st %}
|
||||
<span class="status status-error">{{ row.state }}</span>
|
||||
{% else %}
|
||||
<span class="status status-neutral">{{ row.state or '—' }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="num-cell">
|
||||
{% if row.updated_at %}
|
||||
{% if row.updated_at is string %}
|
||||
{{ row.updated_at.replace('-', '.').split(' ')[0].split('-')[::-1]|join('.') }}
|
||||
{% else %}
|
||||
{{ row.updated_at.strftime('%d.%m.%Y') }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="col-comment">
|
||||
<details class="comment-details" {% if has_comment %}open{% endif %}>
|
||||
<summary class="comment-summary">
|
||||
{% if has_comment %}Изменить{% else %}Добавить{% endif %}
|
||||
</summary>
|
||||
<form method="post" class="comment-form">
|
||||
<input type="hidden" name="action" value="comment">
|
||||
<input type="hidden" name="doc_id" value="{{ row.id }}">
|
||||
<input type="hidden" name="contractor" value="{{ selected_contractor }}">
|
||||
<input type="hidden" name="status" value="{{ selected_status }}">
|
||||
<input type="hidden" name="search" value="{{ search }}">
|
||||
<textarea name="comment" rows="2" placeholder="Комментарий или заметка…">{{ row.comment or '' }}</textarea>
|
||||
<div class="comment-actions">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📭</div>
|
||||
<p>Документов пока нет</p>
|
||||
<p class="empty-hint">Добавьте контрагентов и загрузите Excel из 1С</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
186
templates/documents_sbis.html
Normal file
186
templates/documents_sbis.html
Normal file
@@ -0,0 +1,186 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Документы СБИС — Контроль ЭДО{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header">
|
||||
<h2>Документы СБИС</h2>
|
||||
<p>Загрузка выгрузки из СБИС и отслеживание статусов документов</p>
|
||||
</div>
|
||||
|
||||
<section class="panel">
|
||||
<div class="panel-header">
|
||||
<div class="panel-icon panel-icon-green">⬆</div>
|
||||
<div class="panel-title">
|
||||
<h3>Загрузка Excel из СБИС</h3>
|
||||
<p>Импортируются только контрагенты из списка. Повторная загрузка обновит статус существующего документа.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-zone">
|
||||
<form method="post" enctype="multipart/form-data" class="upload-form">
|
||||
<input type="hidden" name="action" value="upload">
|
||||
<div class="file-input-wrap">
|
||||
<input type="file" name="file" accept=".xlsx,.xls" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">Импортировать</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section class="panel">
|
||||
<form method="get" class="filters">
|
||||
<label>
|
||||
Контрагент
|
||||
<select name="contractor">
|
||||
<option value="">Все</option>
|
||||
{% for name in contractors %}
|
||||
<option value="{{ name }}" {% if selected_contractor == name %}selected{% endif %}>
|
||||
{{ name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Статус
|
||||
<select name="status">
|
||||
<option value="">Все</option>
|
||||
{% for st in statuses %}
|
||||
<option value="{{ st }}" {% if selected_status == st %}selected{% endif %}>
|
||||
{{ st }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</label>
|
||||
<label>
|
||||
Поиск
|
||||
<input type="text" name="search" value="{{ search }}" placeholder="Номер документа">
|
||||
</label>
|
||||
<button type="submit" class="btn btn-ghost">Применить</button>
|
||||
</form>
|
||||
|
||||
<div class="section-label">
|
||||
Список документов СБИС
|
||||
<span class="count-badge">{{ rows|length }}</span>
|
||||
</div>
|
||||
|
||||
{% if rows %}
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="col-note"></th>
|
||||
<th>Дата</th>
|
||||
<th>Номер</th>
|
||||
<th>Контрагент</th>
|
||||
<th>Тип</th>
|
||||
<th>Сумма</th>
|
||||
<th>Статус</th>
|
||||
<th>Ответственный</th>
|
||||
<th>Обновлён</th>
|
||||
<th>Заметка</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in rows %}
|
||||
{% set has_comment = row.comment and row.comment.strip() %}
|
||||
<tr class="{% if has_comment %}has-note{% endif %}">
|
||||
<td class="col-note">
|
||||
{% if has_comment %}
|
||||
<span class="note-indicator" title="Есть заметка">💬</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="num-cell">
|
||||
{% if row.doc_date %}
|
||||
{% if row.doc_date is string %}
|
||||
{% set date_parts = row.doc_date.split(' ')[0].split('-') %}
|
||||
{{ date_parts[2] }}.{{ date_parts[1] }}.{{ date_parts[0] }}
|
||||
{% else %}
|
||||
{{ row.doc_date.strftime('%d.%m.%Y') }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td><span class="doc-number">{{ row.doc_number }}</span></td>
|
||||
<td>{{ row.contractor }}</td>
|
||||
<td>{{ row.doc_type or '—' }}</td>
|
||||
<td class="num-cell">{{ "%.2f"|format(row.amount) if row.amount is not none else '—' }}</td>
|
||||
<td>
|
||||
{% set st = (row.state or '')|lower %}
|
||||
{% if 'заверш' in st or 'успеш' in st %}
|
||||
<span class="status status-ok">{{ row.state }}</span>
|
||||
{% elif 'подпис' in st or 'обработ' in st %}
|
||||
<span class="status status-warn">{{ row.state }}</span>
|
||||
{% elif 'удал' in st or 'ошиб' in st %}
|
||||
<span class="status status-error">{{ row.state }}</span>
|
||||
{% else %}
|
||||
<span class="status status-neutral">{{ row.state or '—' }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>{{ row.responsible or '—' }}</td>
|
||||
<td class="num-cell">
|
||||
{% if row.updated_at %}
|
||||
{% if row.updated_at is string %}
|
||||
{% set date_parts = row.updated_at.split(' ')[0].split('-') %}
|
||||
{{ date_parts[2] }}.{{ date_parts[1] }}.{{ date_parts[0] }}
|
||||
{% else %}
|
||||
{{ row.updated_at.strftime('%d.%m.%Y') }}
|
||||
{% endif %}
|
||||
{% else %}
|
||||
—
|
||||
{% endif %}
|
||||
</td>
|
||||
<td class="col-comment">
|
||||
<details class="comment-details" {% if has_comment %}open{% endif %}>
|
||||
<summary class="comment-summary">
|
||||
{% if has_comment %}Изменить{% else %}Добавить{% endif %}
|
||||
</summary>
|
||||
<form method="post" class="comment-form">
|
||||
<input type="hidden" name="action" value="comment">
|
||||
<input type="hidden" name="doc_id" value="{{ row.id }}">
|
||||
<input type="hidden" name="contractor" value="{{ selected_contractor }}">
|
||||
<input type="hidden" name="status" value="{{ selected_status }}">
|
||||
<input type="hidden" name="search" value="{{ search }}">
|
||||
<textarea name="comment" rows="2" placeholder="Комментарий или заметка…">{{ row.comment or '' }}</textarea>
|
||||
<div class="comment-actions">
|
||||
<button type="submit" class="btn btn-primary btn-sm">Сохранить</button>
|
||||
</div>
|
||||
</form>
|
||||
</details>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div> <!-- После таблицы -->
|
||||
<!-- Пагинация -->
|
||||
{% if total_pages > 1 %}
|
||||
<div class="pagination" style="margin-top: 20px; display: flex; justify-content: space-between; align-items: center; padding: 15px 0;">
|
||||
<div>
|
||||
Показано записей: {{ rows|length }} из {{ total }}
|
||||
</div>
|
||||
<div style="display: flex; gap: 10px;">
|
||||
{% if page > 1 %}
|
||||
<a href="{{ url_for('main.documents_sbis', page=page-1, contractor=selected_contractor, status=selected_status, search=search) }}" class="btn btn-ghost">← Предыдущая</a>
|
||||
{% endif %}
|
||||
|
||||
<span style="padding: 8px 16px; background: #f0f0f0; border-radius: 6px;">
|
||||
Страница {{ page }} из {{ total_pages }}
|
||||
</span>
|
||||
|
||||
{% if page < total_pages %}
|
||||
<a href="{{ url_for('main.documents_sbis', page=page+1, contractor=selected_contractor, status=selected_status, search=search) }}" class="btn btn-ghost">Следующая →</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% else %}
|
||||
<div class="empty-state">
|
||||
<div class="empty-icon">📭</div>
|
||||
<p>Документов СБИС пока нет</p>
|
||||
<p class="empty-hint">Добавьте контрагентов и загрузите Excel из СБИС</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user