feat: словарь управлений ФСТЭК + переменная {{fstek_recipient_address}}

- dictionaries/fstek_offices.json: 7 управлений ФСТЭК по федеральным округам
  с адресами (из официальных реестров) и списками городов/регионов:
  ЦФО (Москва, Севастопольский пр-т 56/40), СЗФО (СПб, Исаакиевская пл. 11),
  ЮФО+СКФО (Ростов-на-Дону, ул. Ярослава Галана 1Е/25), ПФО (Н.Новгород,
  пр-т Гагарина 60 к11), УрФО (Екатеринбург, ул. Гагарина 28Б), СФО
  (Новосибирск, Красный пр-т 41), ДФО (Хабаровск, ул. Ленина 37)
- variables.py: _find_fstek_office(address) — определяет управление по городу/
  региону из адреса организации (с нормализацией сокращений обл./респ./г.о.)
- новые переменные: {{fstek_recipient_address}} (адрес управления для отправки),
  {{fstek_district}} (наименование управления)
- логика: адрес организации → управление ФСТЭК → адрес подставляется в документ
This commit is contained in:
2026-08-04 19:45:33 +04:00
parent 730551aeba
commit c4a8de57d1
2 changed files with 333 additions and 0 deletions
+50
View File
@@ -64,6 +64,8 @@ VARIABLE_DEFS = [
# === Комиссия ===
('{{commission_list}}', 'A', 'Список комиссии', 'commission_list'),
('{{fstek_district}}', 'A', 'Наименование управления ФСТЭК России (по адресу организации)', 'fstek_district'),
('{{fstek_recipient_address}}', 'A', 'Адрес управления ФСТЭК России для направления сведений', 'fstek_recipient_address'),
# === Специальные переменные ===
('{{date_year}}', 'A', 'Текущий год', 'date:year'),
@@ -370,6 +372,44 @@ def _build_loop_data(company: Company, is_list: List) -> Dict[str, List[Dict]]:
# РЕЗОЛВЕР ЗНАЧЕНИЙ
# ============================================================
def _find_fstek_office(address_text: str):
"""Определяет управление ФСТЭК России по адресу организации.
Ищет город/регион из адреса в словаре fstek_offices.json.
Возвращает dict {'name': ..., 'address': ...} или None.
"""
import json as _json
import os as _os
if not address_text:
return None
dict_path = _os.path.join(_os.path.dirname(__file__), 'dictionaries', 'fstek_offices.json')
try:
with open(dict_path, encoding='utf-8') as _f:
offices = _json.load(_f)
except Exception:
return None
hay = address_text.lower()
# Нормализация сокращений: обл.→область, респ.→республика, г.о.→городской округ
hay = hay.replace('обл.', 'область').replace('обл,', 'область')
hay = hay.replace('респ.', 'республика').replace('респ,', 'республика')
hay = hay.replace('г.о.', 'городской округ').replace('г.о,', 'городской округ')
hay = hay.replace('ао', 'автономный округ')
best = None
best_len = 0
for off in offices:
# Ищем по городам
for city in off.get('cities', []):
if city in hay and len(city) > best_len:
best = off
best_len = len(city)
# Ищем по регионам (область/край/республика)
for region in off.get('regions', []):
if region in hay and len(region) > best_len:
best = off
best_len = len(region)
return best
def _resolve(source: str, ctx: Dict) -> str:
"""Преобразует ключ-источник в значение, подставляя из контекста."""
company = ctx.get('company')
@@ -417,6 +457,16 @@ def _resolve(source: str, ctx: Dict) -> str:
if source in commission_data:
return str(commission_data[source])
# Управление ФСТЭК (адресат отправки сведений о категорировании)
if source in ('fstek_recipient_address', 'fstek_district', 'fstek_office_name'):
addr = getattr(company, 'address', '') or ''
off = _find_fstek_office(addr)
if off:
if source == 'fstek_recipient_address':
return off.get('address', '') or ''
return off.get('name', '') or ''
return ''
# Даты
if source.startswith('date:'):
fmt = source.split(':', 1)[1]