Compare commits

..
10 Commits
Author SHA1 Message Date
prog1764 de522360b8 feat: ребрендинг под dokogenMU (основа из dokogen187) 2026-08-03 23:45:16 +04:00
prog1764 da4946cb59 feat: ребрендинг под 187-ФЗ (КИИ)
- Все упоминания 152-ФЗ заменены на 187-ФЗ (заголовок окна, справка,
  импорт, переменные, docstring'и, spec)
- import_152fz -> import_187fz, direction '152fz' -> '187fz'
- autosave_152 -> autosave_187
- Опросный_лист_152_ФЗ.xlsx -> Опросный_лист_187_ФЗ.xlsx
- Промпт ИИ: 'юрист по защите персональных данных (152-ФЗ)' -> 187-ФЗ
2026-08-03 23:25:18 +04:00
prog1764 ac122451b3 fix: _set_app_icon разрывал __init__ (NameError: root is not defined) 2026-08-03 22:29:54 +04:00
prog1764 eee1842421 feat: иконка на окне программы (заголовок/панель задач) + в ресурсах exe 2026-08-03 22:05:44 +04:00
kneon223557 bb987894b2 Обновить dokogen/api_settings.py 2026-08-03 22:03:38 +04:00
kneon223557 19b74fc72a Обновить dokogen/dictionaries/pd_items.json 2026-08-03 22:02:28 +04:00
kneon223557 5a407f2568 Обновить dokogen/dictionaries/pd_categories.json 2026-08-03 22:01:52 +04:00
kneon223557 faefa786c5 Обновить dokogen/dictionaries/pd_actions.json 2026-08-03 22:01:20 +04:00
prog1764 4893899d5d feat: иконка — 64/128px из нового оригинала (512x512) 2026-08-03 21:59:26 +04:00
prog1764 73edfdf533 feat: иконка — 48px из нового оригинала (512) 2026-08-03 21:58:12 +04:00
16 changed files with 132 additions and 90 deletions
+3 -1
View File
@@ -11,8 +11,10 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules
datas = [ datas = [
# Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json) # Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json)
('dokogen/dictionaries', 'dokogen/dictionaries'), ('dokogen/dictionaries', 'dokogen/dictionaries'),
# Пустой опросник 152-ФЗ (для «Справка → Пустой опросник») # Пустой опросник MU (для «Справка → Пустой опросник»)
('dokogen/data', 'dokogen/data'), ('dokogen/data', 'dokogen/data'),
# Иконка приложения (для окна программы в exe)
('dokogen/icon.ico', 'dokogen'),
] ]
# ---------- Словари pymorphy3 (обязательно!) ---------- # ---------- Словари pymorphy3 (обязательно!) ----------
+1 -1
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""DokoGen — Пакет генератора документов 152-ФЗ""" """DokoGen — Пакет генератора документов MU"""
from .models import Company, CommissionMember, InformationSystem, PaperDocument from .models import Company, CommissionMember, InformationSystem, PaperDocument
from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials
+2 -2
View File
@@ -14,8 +14,8 @@ import sys
import json import json
DEFAULT_SETTINGS = { DEFAULT_SETTINGS = {
'dadata_token': '', 'dadata_token': '431aa11706bf83f571cef0319259289aa750bcb1',
'ai_api_key': '', 'ai_api_key': 'sk-3c7e6673f245412697d0e14b6096435f',
'ai_model': 'deepseek-chat', 'ai_model': 'deepseek-chat',
'ai_base_url': 'https://api.deepseek.com', 'ai_base_url': 'https://api.deepseek.com',
} }
+16 -21
View File
@@ -1,22 +1,17 @@
[ [
"Сбор персональных данных", "сбор",
"Запись персональных данных", "систематизация",
"Систематизация персональных данных", "хранение",
"Накопление персональных данных", "извлечение",
"Хранение персональных данных", "передача (предоставление доступа)",
"Уточнение (обновление, изменение) персональных данных", "блокирование",
"Извлечение персональных данных", "уничтожение",
"Использование персональных данных", "запись",
"Передача (распространение, предоставление, доступ) персональных данных", "накопление",
"Обезличивание персональных данных", "уточнение (обновление, изменение)",
"Блокирование персональных данных", "использование",
"Удаление персональных данных", "обезличивание",
"Уничтожение персональных данных", "удаление",
"Трансграничная передача персональных данных", "распространение",
"Автоматизированная обработка персональных данных", "иные действия"
"Неавтоматизированная обработка персональных данных", ]
"Смешанная обработка персональных данных",
"Формирование и ведение баз данных",
"Передача данных третьим лицам",
"Внутренняя передача данных между подразделениями"
]
+15 -7
View File
@@ -1,8 +1,16 @@
[ [
"работники оператора", "Сотрудникам организации",
"клиенты", "Соискателям",
"контрагенты", "Родственникам сотрудников",
"родственники работников", "Уволенным сотрудникам",
"соискатели", "Контрагентам",
"иные лица" "Представителям контрагентов",
] "Клиентам",
"Посетителям сайта",
"Выгодоприобретателям по договорам",
"Субъект персональных данных, предоставивший согласие на трансграничную передачу персональных данных",
"Учащимя",
"Студентам",
"Законным представителям",
"Иным категориям субъектов персональных данных, персональные данные которых обрабатываются"
]
+39 -23
View File
@@ -1,28 +1,44 @@
[ [
"Фамилия, имя, отчество", "Фамилия, Имя, Отчество",
"Год рождения",
"Месяц рождения",
"Дата рождения", "Дата рождения",
"Место рождения", "Место рождения",
"Паспортные данные",
"Адрес регистрации",
"Адрес проживания",
"ИНН",
"СНИЛС",
"Номер телефона",
"Адрес электронной почты",
"Образование",
"Профессия",
"Сведения о доходах",
"Семейное положение", "Семейное положение",
"Состав семьи", "Социальное положение",
"Состояние здоровья", "Имущественное положение",
"Национальность", "Сведения о доходах",
"Гражданство", "Пол",
"Сведения о судимости", "Адрес электронной почты",
"Фотография", "Адрес места жительства",
"Биометрические данные", "Адрес регистрации",
"Рабочий e-mail", "Номер телефона",
"Страховой номер индивидуального лицевого счета (СНИЛС)",
"Идентификационный номер налогоплательщика (ИНН);",
"Сведения о гражданстве",
"Данные документа, удостоверяющего личность",
"Данные водительского удостоверения",
"Данные документа, удостоверяющего личность за пределами Российской Федерации",
"Данные документа, содержащиеся в свидетельстве о рождении",
"Реквизиты банковских карт",
"Номер расчетного счета",
"Номер лицевого счета",
"Профессия",
"Должность", "Должность",
"Табельный номер", "Сведения о трудовой деятельности (в том числе стаж работы, данные о трудовой занятости на текущее время с указанием наименования и расчетного счета организации)",
"Сведения о воинском учете", "Сведения об отношении к воинской обязанности, о воинском учете",
"Сведения о командировках" "Иные персональные данные",
] "Сведения, собираемые посредством метрических программ (например, Яндекс.Метрика)",
"Сведения об образовании",
"Фото-видео изображение лица",
"Данные голоса человека",
"Сведения о состоянии здоровья",
"Национальная принадлежность",
"Политические взгляды",
"Религиозные или философские убеждения",
"Сведения о судимости",
"Данные изображения лица, полученные с помощью фото- видео устройств, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
"Данные голоса человека, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
"Изображение папиллярных узоров пальцев рук",
"Иные биометрические персональные данные"
]
+8 -8
View File
@@ -5,7 +5,7 @@
рядом с программой (при сборке .exe — рядом с exe): рядом с программой (при сборке .exe — рядом с exe):
- Инструкция_пользователя.docx - Инструкция_пользователя.docx
- Инструкция_администратора.docx - Инструкция_администратора.docx
- Опросный_лист_152_ФЗ.xlsx (пустой шаблон) - Опросный_лист_MU.xlsx (пустой шаблон)
""" """
import os import os
@@ -45,7 +45,7 @@ def variables_reference_path():
def survey_path(): def survey_path():
return os.path.join(docs_dir(), 'Опросный_лист_152_ФЗ.xlsx') return os.path.join(docs_dir(), 'Опросный_лист_MU.xlsx')
def _h(doc, text, size=14): def _h(doc, text, size=14):
@@ -83,7 +83,7 @@ def _generate_variables_reference(path):
_p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст ' _p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст '
'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.') 'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.')
groups = {'A': 'Общие переменные', 'B': '152-ФЗ (персональные данные)'} groups = {'A': 'Общие переменные', 'B': 'MU (персональные данные)'}
by_group = {'A': [], 'B': []} by_group = {'A': [], 'B': []}
for var, group, desc, _ in VARIABLE_DEFS: for var, group, desc, _ in VARIABLE_DEFS:
by_group.setdefault(group, []).append((var, desc)) by_group.setdefault(group, []).append((var, desc))
@@ -108,11 +108,11 @@ def _generate_variables_reference(path):
def _generate_user_manual(path): def _generate_user_manual(path):
"""Инструкция пользователя: программа, циклы, переменные, проверки.""" """Инструкция пользователя: программа, циклы, переменные, проверки."""
doc = docx.Document() doc = docx.Document()
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ') _h(doc, 'ДоКоГеНеРаТоР — MU')
_h(doc, 'Инструкция пользователя', 13) _h(doc, 'Инструкция пользователя', 13)
_h(doc, '1. Что это за программа', 12) _h(doc, '1. Что это за программа', 12)
_p(doc, 'Программа автоматически заполняет шаблоны документов по 152-ФЗ ' _p(doc, 'Программа автоматически заполняет шаблоны документов по MU '
'(акты обследования, политики, инструкции, приказы и т.д.) данными ' '(акты обследования, политики, инструкции, приказы и т.д.) данными '
'об организации, информационных системах, комиссии и сотрудниках. ' 'об организации, информационных системах, комиссии и сотрудниках. '
'Данные можно ввести вручную на вкладках или импортировать из ' 'Данные можно ввести вручную на вкладках или импортировать из '
@@ -173,7 +173,7 @@ def _generate_user_manual(path):
def _generate_admin_manual(path): def _generate_admin_manual(path):
"""Инструкция администратора: словари, API-ключи, лимиты.""" """Инструкция администратора: словари, API-ключи, лимиты."""
doc = docx.Document() doc = docx.Document()
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ') _h(doc, 'ДоКоГеНеРаТоР — MU')
_h(doc, 'Инструкция администратора', 13) _h(doc, 'Инструкция администратора', 13)
_h(doc, '1. Файлы настроек рядом с программой', 12) _h(doc, '1. Файлы настроек рядом с программой', 12)
@@ -240,8 +240,8 @@ def _ensure_survey():
return target return target
# Ищем шаблон опросника: рядом с программой или встроенный # Ищем шаблон опросника: рядом с программой или встроенный
candidates = [ candidates = [
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_152_ФЗ.xlsx'), os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_MU.xlsx'),
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_152_ФЗ.xlsx'), os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_MU.xlsx'),
] ]
for c in candidates: for c in candidates:
if os.path.exists(c): if os.path.exists(c):
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 63 KiB

After

Width:  |  Height:  |  Size: 63 KiB

+9 -9
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""DokoGen — Модуль импорта из Excel (опросный лист 152-ФЗ)""" """DokoGen — Модуль импорта из Excel (опросный лист MU)"""
import re import re
import traceback import traceback
@@ -122,8 +122,8 @@ def clean_value(val):
return text return text
def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict: def import_mufz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
"""Импорт данных из опросного листа Excel (152-ФЗ). """Импорт данных из опросного листа Excel (MU).
settings_path — путь к JSON-файлу настроек импорта (что и откуда брать). settings_path — путь к JSON-файлу настроек импорта (что и откуда брать).
Если None — файл import_settings.json рядом с программой; если его нет — Если None — файл import_settings.json рядом с программой; если его нет —
@@ -154,9 +154,9 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
return data return data
try: try:
_import_152_common(wb, data, settings, log_fn) _import_mu_common(wb, data, settings, log_fn)
_import_152_is_list(wb, data, settings, log_fn) _import_mu_is_list(wb, data, settings, log_fn)
_import_152_employees(wb, data, settings, log_fn) _import_mu_employees(wb, data, settings, log_fn)
except Exception as e: except Exception as e:
log_fn(f"❌ Ошибка при импорте: {e}") log_fn(f"❌ Ошибка при импорте: {e}")
log_fn(traceback.format_exc()) log_fn(traceback.format_exc())
@@ -179,7 +179,7 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
return data return data
def _import_152_common(wb, data, settings, log_fn): def _import_mu_common(wb, data, settings, log_fn):
"""Импорт общих сведений по словарю настроек. """Импорт общих сведений по словарю настроек.
Для каждого поля: сначала пробуем конкретную ячейку (cell), Для каждого поля: сначала пробуем конкретную ячейку (cell),
@@ -247,7 +247,7 @@ def _import_152_common(wb, data, settings, log_fn):
break break
def _import_152_is_list(wb, data, settings, log_fn): def _import_mu_is_list(wb, data, settings, log_fn):
"""Импорт информационных систем из остальных листов.""" """Импорт информационных систем из остальных листов."""
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники']) exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
is_sheets = [] is_sheets = []
@@ -447,7 +447,7 @@ def _import_152_is_list(wb, data, settings, log_fn):
log_fn(f"\n✅ Всего импортировано ИС: {len(is_list)}") log_fn(f"\n✅ Всего импортировано ИС: {len(is_list)}")
def _import_152_employees(wb, data, settings, log_fn): def _import_mu_employees(wb, data, settings, log_fn):
"""Импорт комиссии и сотрудников из листа «Сотрудники».""" """Импорт комиссии и сотрудников из листа «Сотрудники»."""
sheet_name = None sheet_name = None
sheet_kw = settings.get('employees_sheet_keyword', 'сотруд') sheet_kw = settings.get('employees_sheet_keyword', 'сотруд')
+2 -2
View File
@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""DokoGen — Модели данных (только 152-ФЗ, только нужные поля)""" """DokoGen — Модели данных (только MU, только нужные поля)"""
from dataclasses import dataclass, field, asdict from dataclasses import dataclass, field, asdict
from typing import List, Dict, Any from typing import List, Dict, Any
@@ -49,7 +49,7 @@ class PaperDocument:
@dataclass @dataclass
class Company: class Company:
"""Организация — основной объект (152-ФЗ)""" """Организация — основной объект (MU)"""
# Полное название # Полное название
full_name: str = "" full_name: str = ""
company_name_1: str = "" # именительный company_name_1: str = "" # именительный
+29 -8
View File
@@ -31,9 +31,10 @@ class DokoGenApp:
def __init__(self, root: tk.Tk): def __init__(self, root: tk.Tk):
self.root = root self.root = root
root.title("ДоКоГеНеРаТоР — 152-ФЗ (Персональные данные)") root.title("ДоКоГеНеРаТоР — MU")
root.geometry("1200x800") root.geometry("1200x800")
root.minsize(900, 600) root.minsize(900, 600)
self._set_app_icon()
self.model = Company() self.model = Company()
self.current_file: Optional[str] = None self.current_file: Optional[str] = None
@@ -43,7 +44,7 @@ class DokoGenApp:
self._last_template_dir: str = "" self._last_template_dir: str = ""
self._autosave_path = os.path.join( self._autosave_path = os.path.join(
os.path.dirname(os.path.abspath(__file__)), os.path.dirname(os.path.abspath(__file__)),
'.autosave', 'autosave_152.json' '.autosave', 'autosave_mufz.json'
) )
# Путь к файлу настроек импорта (словарь «что и откуда брать») # Путь к файлу настроек импорта (словарь «что и откуда брать»)
@@ -81,6 +82,26 @@ class DokoGenApp:
self._load_autosave() self._load_autosave()
self._start_autosave_timer() self._start_autosave_timer()
def _set_app_icon(self):
"""Устанавливает иконку приложения на окно (заголовок, панель задач).
Работает и из исходников, и из exe (frozen):
- исходники: dokogen/icon.ico рядом с модулем
- exe: dokogen/icon.ico из _MEIPASS (ресурсы PyInstaller)
"""
try:
if getattr(sys, 'frozen', False):
base = getattr(sys, '_MEIPASS', os.path.dirname(sys.executable))
ico = os.path.join(base, 'dokogen', 'icon.ico')
if not os.path.exists(ico):
ico = os.path.join(os.path.dirname(sys.executable), 'icon.ico')
else:
ico = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'icon.ico')
if os.path.exists(ico):
self.root.iconbitmap(ico)
except Exception:
pass
# ==================== МЕНЮ ==================== # ==================== МЕНЮ ====================
def _build_menu(self): def _build_menu(self):
menubar = tk.Menu(self.root) menubar = tk.Menu(self.root)
@@ -1736,7 +1757,7 @@ class DokoGenApp:
is_list=self.model.information_systems, is_list=self.model.information_systems,
doc_number=self.doc_number_var.get(), doc_number=self.doc_number_var.get(),
doc_date=self.doc_date_var.get(), doc_date=self.doc_date_var.get(),
direction='152fz', direction='mufz',
) )
results = [] results = []
@@ -1825,7 +1846,7 @@ class DokoGenApp:
def import_from_survey(self): def import_from_survey(self):
path = filedialog.askopenfilename( path = filedialog.askopenfilename(
title="Выберите опросный лист (152-ФЗ)", title="Выберите опросный лист (MU)",
filetypes=[("Excel", "*.xlsx *.xls")] filetypes=[("Excel", "*.xlsx *.xls")]
) )
if not path: if not path:
@@ -1835,8 +1856,8 @@ class DokoGenApp:
# Импорт в отдельном потоке # Импорт в отдельном потоке
def _run(): def _run():
try: try:
from .importer import import_152fz from .importer import import_mufz
data = import_152fz(path, self.log, settings_path=self.import_settings_path) data = import_mufz(path, self.log, settings_path=self.import_settings_path)
if data: if data:
self._apply_imported_data(data) self._apply_imported_data(data)
except ImportError: except ImportError:
@@ -2182,7 +2203,7 @@ class DokoGenApp:
def show_about(self): def show_about(self):
messagebox.showinfo("О программе", messagebox.showinfo("О программе",
"ДоКоГеНеРаТоР — 152-ФЗ\n" "ДоКоГеНеРаТоР — MU\n"
"Версия 1.0\n" "Версия 1.0\n"
"Генератор документов из шаблонов") "Генератор документов из шаблонов")
@@ -2230,7 +2251,7 @@ class DokoGenApp:
def download_survey(self): def download_survey(self):
from .help_docs import ensure_docs, survey_path from .help_docs import ensure_docs, survey_path
ensure_docs() ensure_docs()
self._save_resource(survey_path(), "Опросный_лист_152_ФЗ.xlsx", "Сохранить пустой опросник") self._save_resource(survey_path(), "Опросный_лист_MU.xlsx", "Сохранить пустой опросник")
def main(): def main():
+4 -4
View File
@@ -19,7 +19,7 @@ def _norm_name(s):
# ОПИСАНИЕ ПЕРЕМЕННЫХ # ОПИСАНИЕ ПЕРЕМЕННЫХ
# ============================================================ # ============================================================
# Формат: (имя_переменной, группа, описание, ключ_резолвера) # Формат: (имя_переменной, группа, описание, ключ_резолвера)
# Группы: A — общие, B — 152-ФЗ # Группы: A — общие, B — MU
VARIABLE_DEFS = [ VARIABLE_DEFS = [
# === Полное название организации (6 падежей) === # === Полное название организации (6 падежей) ===
@@ -68,7 +68,7 @@ VARIABLE_DEFS = [
('{{document_date}}', 'A', 'Дата документа', 'doc_date'), ('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
('{{document_number}}', 'A', 'Номер документа', 'doc_number'), ('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
# === 152-ФЗ === # === MU ===
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'), ('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'), ('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'), ('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
@@ -138,7 +138,7 @@ def build_replacements(
is_list: List = None, is_list: List = None,
doc_number: str = "", doc_number: str = "",
doc_date: str = "", doc_date: str = "",
direction: str = "152fz", direction: str = "mufz",
) -> Dict[str, str]: ) -> Dict[str, str]:
"""Строит полный словарь замен {{переменная}} → значение.""" """Строит полный словарь замен {{переменная}} → значение."""
if is_list is None: if is_list is None:
@@ -159,7 +159,7 @@ def build_replacements(
replacements = {} replacements = {}
for var_name, group, desc, source in VARIABLE_DEFS: for var_name, group, desc, source in VARIABLE_DEFS:
if direction == '152fz' and group not in ('A', 'B'): if direction == 'mufz' and group not in ('A', 'B'):
continue continue
value = _resolve(source, ctx) value = _resolve(source, ctx)
replacements[var_name] = str(value) if value else '' replacements[var_name] = str(value) if value else ''
+1 -1
View File
@@ -399,7 +399,7 @@ def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None
if not api_key: if not api_key:
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)" return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
system = ( system = (
"Ты — юрист по защите персональных данных (152-ФЗ). Проверь реквизиты организации. " "Ты — юрист по защите персональных данных (MU). Проверь реквизиты организации. "
"Верни ТОЛЬКО JSON: " "Верни ТОЛЬКО JSON: "
'{"оценка": "ok|warning|error", ' '{"оценка": "ok|warning|error", '
'"замечания": ["..."], ' '"замечания": ["..."], '
+1 -1
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
"""DokoGen — Генератор документов 152-ФЗ """DokoGen — Генератор документов MU
Точка входа: python main.py Точка входа: python main.py
""" """
import sys import sys
+2 -2
View File
@@ -87,7 +87,7 @@ def test_zip_loop_expansion():
is_list=company.information_systems, is_list=company.information_systems,
doc_number='001', doc_number='001',
doc_date='31.07.2026', doc_date='31.07.2026',
direction='152fz' direction='mufz'
) )
loops = replacements.get('_loops', {}) loops = replacements.get('_loops', {})
@@ -135,7 +135,7 @@ def test_zip_loop_expansion():
is_list=company.information_systems, is_list=company.information_systems,
doc_number='001', doc_number='001',
doc_date='31.07.2026', doc_date='31.07.2026',
direction='152fz' direction='mufz'
) )
try: try: