Last modified by Mihail Opinca on 2026/08/16 09:12

Show last authors
1 = Техническая документация — calc-ref =
2
3 == 1. Назначение ==
4
5 Учёт импорта товаров, содержащих хладагенты, расчёт массы хладагента и CO2-эквивалента, контроль годовых квот и формирование отчётности для внутреннего использования.
6
7 == 2. Архитектура ==
8
9 Монолит: один backend-процесс отдаёт и API, и собранный фронтенд.
10
11 {{code}}
12 Браузер
13 | HTTPS
14 v
15 nginx (calc-ref.online) -- proxy_pass --> gunicorn (127.0.0.1:5002)
16 |
17 Flask app (app.py)
18 +- /api/* -> JSON API
19 +- /* -> dist/ (Vue SPA, статика)
20 |
21 python-oracledb (Thick Mode)
22 |
23 Oracle DB (una.md:4024, схема DIPPFRUCT)
24 {{/code}}
25
26 * **Frontend**: Vue 3 (##<script setup>##), Vue Router, без Vuex/Pinia. Сборка — Vite.
27 * **Backend**: Flask 3, сессии на cookie, Oracle через ##python-oracledb## в **Thick Mode** (БД — Oracle 11g, для Thin Mode слишком старая).
28 * **БД**: Oracle, схема ##DIPPFRUCT##, таблицы с префиксом ##CR_*##.
29 * **Прод-инфраструктура**: systemd (gunicorn) + nginx (reverse proxy + TLS через Let's Encrypt/certbot).
30
31 == 3. Структура репозитория ==
32
33 {{code}}
34 app.py, config.py - точка входа Flask, конфиг из .env
35 controllers/auth_controller.py - аутентификация (БД + fallback из .env)
36 models/ - доступ к данным, один файл на сущность
37 services/ - зарезервировано, пока пусто
38 migrate.py - вспомогательный скрипт миграций/данных
39 sql/ - DDL и миграции схемы (01...08)
40 src/ - Vue-приложение
41 dist/ - собранный фронтенд, генерируется npm run build
42 deploy/calc-ref.service - systemd-юнит (gunicorn)
43 deploy/push.sh - скрипт деплоя изменений на прод
44 docs/ - документация (этот раздел)
45 {{/code}}
46
47 == 4. Backend: модели ==
48
49 |=Файл|=Отвечает за
50 |models/database.py|Инициализация Oracle Instant Client (Thick Mode), пул соединений
51 |models/user_model.py|Проверка логина/пароля (CR_USERS)
52 |models/refrigerant_model.py|Справочник хладагентов: код, GWP, REQUIRES_QUOTA
53 |models/product_model.py|Справочник товаров: связь с хладагентом, кг/ед., код ТНВЭД
54 |models/import_doc_model.py|Документы импорта и строки: CRUD, расчёт масс/CO2, проверка квот, отчёт
55 |models/quota_model.py|Годовые квоты по хладагентам (CR_QUOTAS)
56 |models/tax_rate_model.py|Ставки эконалога (CR_TAX_RATES), вводятся администратором вручную
57
58 Расчёт в документах импорта: ##TOTAL_KG = QUANTITY ? REFRIGERANT_KG##, ##CO2_KG = TOTAL_KG ? GWP##. Считается на backend при сохранении.
59
60 == 5. API ==
61
62 Аутентификация — сессионная cookie. ##@require_auth## — нужен вход, ##@require_admin## — нужна роль admin.
63
64 |=Метод|=Путь|=Доступ|=Назначение
65 |POST|/api/auth/login|публичный|Вход по логину/паролю
66 |POST|/api/auth/logout|-|Выход
67 |GET|/api/auth/me|-|Текущий пользователь
68 |GET|/api/refrigerants|auth|Список хладагентов
69 |POST|/api/refrigerants|admin|Новый хладагент
70 |PUT|/api/refrigerants/<id>|admin|Правка хладагента
71 |POST|/api/refrigerants/<id>/toggle|admin|Вкл/выкл активность
72 |GET|/api/products|auth|Список товаров
73 |POST|/api/products|auth|Новый товар
74 |PUT|/api/products/<id>|auth|Правка товара
75 |POST|/api/products/<id>/toggle|auth|Вкл/выкл активность
76 |GET|/api/import-docs|auth|Список документов импорта
77 |GET|/api/import-docs/next-number|auth|Следующий свободный номер документа
78 |POST|/api/import-docs|auth|Создать документ (черновик)
79 |GET|/api/import-docs/<id>|auth|Документ + строки
80 |PUT|/api/import-docs/<id>|auth|Обновить документ и строки
81 |DELETE|/api/import-docs/<id>|auth|Удалить документ
82 |POST|/api/import-docs/<id>/status|auth|Смена статуса, проверка квот при POSTED
83 |GET|/api/quotas|auth|Квоты по году/хладагенту
84 |GET|/api/quotas/years|auth|Список годов с квотами
85 |POST|/api/quotas|auth|Новая квота
86 |PUT|/api/quotas/<id>|auth|Правка квоты
87 |DELETE|/api/quotas/<id>|auth|Удалить квоту
88 |GET|/api/report|auth|Отчёт по хладагентам с фильтрами
89 |GET|/api/report/excel|auth|Тот же отчёт, выгрузка в .xlsx
90 |GET|/*|публичный|Отдача SPA и статики фронтенда
91
92 == 6. База данных ==
93
94 |=Таблица|=Ключевые поля|=Смысл
95 |CR_USERS|USERNAME, PASSWORD_HASH, ROLE|Сотрудники с доступом
96 |CR_REFRIGERANTS|CODE, GWP, REQUIRES_QUOTA|Справочник хладагентов
97 |CR_TAX_RATES|REFRIGERANT_ID, RATE, VALID_FROM/TO|Ставки эконалога
98 |CR_PRODUCTS|HAS_REFRIGERANT, REFRIGERANT_ID, REFRIGERANT_KG, TNVED_CODE|Справочник товаров
99 |CR_IMPORT_DOCS|DOC_DATE, DOC_NUMBER, SUPPLIER (NOT NULL), STATUS|Заголовки документов импорта
100 |CR_IMPORT_LINES|DOC_ID, PRODUCT_ID, ORIGIN_COUNTRY, QUANTITY, TOTAL_KG, CO2_KG|Строки документов
101 |CR_QUOTAS|YEAR, REFRIGERANT_ID, QUOTA_KG|Годовые квоты на ввоз
102
103 {{warning}}
104 CR_IMPORT_DOCS.SUPPLIER обязателен на уровне БД. Пустая строка трактуется Oracle как NULL и вызывает ORA-01400 — поэтому валидация стоит и на фронтенде, и на backend.
105 {{/warning}}
106
107 == 7. Аутентификация и роли ==
108
109 Сессия хранится на сервере (Flask session cookie). Сперва проверка по Oracle (CR_USERS), при недоступности БД — fallback на пользователей из .env. Роли: **admin** (полный доступ) и **operator** (без справочника хладагентов).
110
111 == 8. Деплой ==
112
113 1. Backend: gunicorn под systemd, 2 воркера ? 4 потока, порт 127.0.0.1:5002.
114 2. Oracle Instant Client (Thick Mode) в /opt/oracle/instantclient_19_19, путь зарегистрирован в ldconfig.
115 3. nginx терминирует TLS (Let's Encrypt/certbot, автопродление), проксирует на gunicorn.
116 4. Frontend собирается локально (##base: '/'##) и копируется вместе с backend-кодом.
117 5. Обновление — скрипт ##deploy/push.sh##.