Compare commits
10
Commits
86836c2c9c
..
source
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3147d563c | ||
|
|
da4946cb59 | ||
|
|
ac122451b3 | ||
|
|
eee1842421 | ||
|
|
bb987894b2 | ||
|
|
19b74fc72a | ||
|
|
5a407f2568 | ||
|
|
faefa786c5 | ||
|
|
4893899d5d | ||
|
|
73edfdf533 |
+3
-1
@@ -11,8 +11,10 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
||||
datas = [
|
||||
# Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json)
|
||||
('dokogen/dictionaries', 'dokogen/dictionaries'),
|
||||
# Пустой опросник 152-ФЗ (для «Справка → Пустой опросник»)
|
||||
# Пустой опросник 149-ФЗ (117 ПР ФСТЭК) (для «Справка → Пустой опросник»)
|
||||
('dokogen/data', 'dokogen/data'),
|
||||
# Иконка приложения (для окна программы в exe)
|
||||
('dokogen/icon.ico', 'dokogen'),
|
||||
]
|
||||
|
||||
# ---------- Словари pymorphy3 (обязательно!) ----------
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Пакет генератора документов 152-ФЗ"""
|
||||
"""DokoGen — Пакет генератора документов 149-ФЗ (117 ПР ФСТЭК)"""
|
||||
|
||||
from .models import Company, CommissionMember, InformationSystem, PaperDocument
|
||||
from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials
|
||||
|
||||
@@ -14,8 +14,8 @@ import sys
|
||||
import json
|
||||
|
||||
DEFAULT_SETTINGS = {
|
||||
'dadata_token': '',
|
||||
'ai_api_key': '',
|
||||
'dadata_token': '431aa11706bf83f571cef0319259289aa750bcb1',
|
||||
'ai_api_key': 'sk-3c7e6673f245412697d0e14b6096435f',
|
||||
'ai_model': 'deepseek-chat',
|
||||
'ai_base_url': 'https://api.deepseek.com',
|
||||
}
|
||||
|
||||
@@ -1,22 +1,17 @@
|
||||
[
|
||||
"Сбор персональных данных",
|
||||
"Запись персональных данных",
|
||||
"Систематизация персональных данных",
|
||||
"Накопление персональных данных",
|
||||
"Хранение персональных данных",
|
||||
"Уточнение (обновление, изменение) персональных данных",
|
||||
"Извлечение персональных данных",
|
||||
"Использование персональных данных",
|
||||
"Передача (распространение, предоставление, доступ) персональных данных",
|
||||
"Обезличивание персональных данных",
|
||||
"Блокирование персональных данных",
|
||||
"Удаление персональных данных",
|
||||
"Уничтожение персональных данных",
|
||||
"Трансграничная передача персональных данных",
|
||||
"Автоматизированная обработка персональных данных",
|
||||
"Неавтоматизированная обработка персональных данных",
|
||||
"Смешанная обработка персональных данных",
|
||||
"Формирование и ведение баз данных",
|
||||
"Передача данных третьим лицам",
|
||||
"Внутренняя передача данных между подразделениями"
|
||||
"сбор",
|
||||
"систематизация",
|
||||
"хранение",
|
||||
"извлечение",
|
||||
"передача (предоставление доступа)",
|
||||
"блокирование",
|
||||
"уничтожение",
|
||||
"запись",
|
||||
"накопление",
|
||||
"уточнение (обновление, изменение)",
|
||||
"использование",
|
||||
"обезличивание",
|
||||
"удаление",
|
||||
"распространение",
|
||||
"иные действия"
|
||||
]
|
||||
@@ -1,8 +1,16 @@
|
||||
[
|
||||
"работники оператора",
|
||||
"клиенты",
|
||||
"контрагенты",
|
||||
"родственники работников",
|
||||
"соискатели",
|
||||
"иные лица"
|
||||
"Сотрудникам организации",
|
||||
"Соискателям",
|
||||
"Родственникам сотрудников",
|
||||
"Уволенным сотрудникам",
|
||||
"Контрагентам",
|
||||
"Представителям контрагентов",
|
||||
"Клиентам",
|
||||
"Посетителям сайта",
|
||||
"Выгодоприобретателям по договорам",
|
||||
"Субъект персональных данных, предоставивший согласие на трансграничную передачу персональных данных",
|
||||
"Учащимя",
|
||||
"Студентам",
|
||||
"Законным представителям",
|
||||
"Иным категориям субъектов персональных данных, персональные данные которых обрабатываются"
|
||||
]
|
||||
@@ -1,28 +1,44 @@
|
||||
[
|
||||
"Фамилия, имя, отчество",
|
||||
"Фамилия, Имя, Отчество",
|
||||
"Год рождения",
|
||||
"Месяц рождения",
|
||||
"Дата рождения",
|
||||
"Место рождения",
|
||||
"Паспортные данные",
|
||||
"Адрес регистрации",
|
||||
"Адрес проживания",
|
||||
"ИНН",
|
||||
"СНИЛС",
|
||||
"Номер телефона",
|
||||
"Адрес электронной почты",
|
||||
"Образование",
|
||||
"Профессия",
|
||||
"Сведения о доходах",
|
||||
"Семейное положение",
|
||||
"Состав семьи",
|
||||
"Состояние здоровья",
|
||||
"Национальность",
|
||||
"Гражданство",
|
||||
"Сведения о судимости",
|
||||
"Фотография",
|
||||
"Биометрические данные",
|
||||
"Рабочий e-mail",
|
||||
"Социальное положение",
|
||||
"Имущественное положение",
|
||||
"Сведения о доходах",
|
||||
"Пол",
|
||||
"Адрес электронной почты",
|
||||
"Адрес места жительства",
|
||||
"Адрес регистрации",
|
||||
"Номер телефона",
|
||||
"Страховой номер индивидуального лицевого счета (СНИЛС)",
|
||||
"Идентификационный номер налогоплательщика (ИНН);",
|
||||
"Сведения о гражданстве",
|
||||
"Данные документа, удостоверяющего личность",
|
||||
"Данные водительского удостоверения",
|
||||
"Данные документа, удостоверяющего личность за пределами Российской Федерации",
|
||||
"Данные документа, содержащиеся в свидетельстве о рождении",
|
||||
"Реквизиты банковских карт",
|
||||
"Номер расчетного счета",
|
||||
"Номер лицевого счета",
|
||||
"Профессия",
|
||||
"Должность",
|
||||
"Табельный номер",
|
||||
"Сведения о воинском учете",
|
||||
"Сведения о командировках"
|
||||
"Сведения о трудовой деятельности (в том числе стаж работы, данные о трудовой занятости на текущее время с указанием наименования и расчетного счета организации)",
|
||||
"Сведения об отношении к воинской обязанности, о воинском учете",
|
||||
"Иные персональные данные",
|
||||
"Сведения, собираемые посредством метрических программ (например, Яндекс.Метрика)",
|
||||
"Сведения об образовании",
|
||||
"Фото-видео изображение лица",
|
||||
"Данные голоса человека",
|
||||
"Сведения о состоянии здоровья",
|
||||
"Национальная принадлежность",
|
||||
"Политические взгляды",
|
||||
"Религиозные или философские убеждения",
|
||||
"Сведения о судимости",
|
||||
"Данные изображения лица, полученные с помощью фото- видео устройств, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
|
||||
"Данные голоса человека, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
|
||||
"Изображение папиллярных узоров пальцев рук",
|
||||
"Иные биометрические персональные данные"
|
||||
]
|
||||
@@ -5,7 +5,7 @@
|
||||
рядом с программой (при сборке .exe — рядом с exe):
|
||||
- Инструкция_пользователя.docx
|
||||
- Инструкция_администратора.docx
|
||||
- Опросный_лист_152_ФЗ.xlsx (пустой шаблон)
|
||||
- Опросный_лист_149_FZ.xlsx (пустой шаблон)
|
||||
"""
|
||||
|
||||
import os
|
||||
@@ -45,7 +45,7 @@ def variables_reference_path():
|
||||
|
||||
|
||||
def survey_path():
|
||||
return os.path.join(docs_dir(), 'Опросный_лист_152_ФЗ.xlsx')
|
||||
return os.path.join(docs_dir(), 'Опросный_лист_149_FZ.xlsx')
|
||||
|
||||
|
||||
def _h(doc, text, size=14):
|
||||
@@ -83,7 +83,7 @@ def _generate_variables_reference(path):
|
||||
_p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст '
|
||||
'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.')
|
||||
|
||||
groups = {'A': 'Общие переменные', 'B': '152-ФЗ (персональные данные)'}
|
||||
groups = {'A': 'Общие переменные', 'B': '149-ФЗ (117 ПР ФСТЭК) (персональные данные)'}
|
||||
by_group = {'A': [], 'B': []}
|
||||
for var, group, desc, _ in VARIABLE_DEFS:
|
||||
by_group.setdefault(group, []).append((var, desc))
|
||||
@@ -108,11 +108,11 @@ def _generate_variables_reference(path):
|
||||
def _generate_user_manual(path):
|
||||
"""Инструкция пользователя: программа, циклы, переменные, проверки."""
|
||||
doc = docx.Document()
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ')
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)')
|
||||
_h(doc, 'Инструкция пользователя', 13)
|
||||
|
||||
_h(doc, '1. Что это за программа', 12)
|
||||
_p(doc, 'Программа автоматически заполняет шаблоны документов по 152-ФЗ '
|
||||
_p(doc, 'Программа автоматически заполняет шаблоны документов по 149-ФЗ (117 ПР ФСТЭК) '
|
||||
'(акты обследования, политики, инструкции, приказы и т.д.) данными '
|
||||
'об организации, информационных системах, комиссии и сотрудниках. '
|
||||
'Данные можно ввести вручную на вкладках или импортировать из '
|
||||
@@ -173,7 +173,7 @@ def _generate_user_manual(path):
|
||||
def _generate_admin_manual(path):
|
||||
"""Инструкция администратора: словари, API-ключи, лимиты."""
|
||||
doc = docx.Document()
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 152-ФЗ')
|
||||
_h(doc, 'ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)')
|
||||
_h(doc, 'Инструкция администратора', 13)
|
||||
|
||||
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
||||
@@ -240,8 +240,8 @@ def _ensure_survey():
|
||||
return target
|
||||
# Ищем шаблон опросника: рядом с программой или встроенный
|
||||
candidates = [
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_152_ФЗ.xlsx'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_152_ФЗ.xlsx'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_149_FZ.xlsx'),
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_149_FZ.xlsx'),
|
||||
]
|
||||
for c in candidates:
|
||||
if os.path.exists(c):
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 63 KiB After Width: | Height: | Size: 63 KiB |
+9
-9
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Модуль импорта из Excel (опросный лист 152-ФЗ)"""
|
||||
"""DokoGen — Модуль импорта из Excel (опросный лист 149-ФЗ (117 ПР ФСТЭК))"""
|
||||
|
||||
import re
|
||||
import traceback
|
||||
@@ -122,8 +122,8 @@ def clean_value(val):
|
||||
return text
|
||||
|
||||
|
||||
def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
||||
"""Импорт данных из опросного листа Excel (152-ФЗ).
|
||||
def import_149fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
||||
"""Импорт данных из опросного листа Excel (149-ФЗ (117 ПР ФСТЭК)).
|
||||
|
||||
settings_path — путь к JSON-файлу настроек импорта (что и откуда брать).
|
||||
Если None — файл import_settings.json рядом с программой; если его нет —
|
||||
@@ -154,9 +154,9 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
||||
return data
|
||||
|
||||
try:
|
||||
_import_152_common(wb, data, settings, log_fn)
|
||||
_import_152_is_list(wb, data, settings, log_fn)
|
||||
_import_152_employees(wb, data, settings, log_fn)
|
||||
_import_149_common(wb, data, settings, log_fn)
|
||||
_import_149_is_list(wb, data, settings, log_fn)
|
||||
_import_149_employees(wb, data, settings, log_fn)
|
||||
except Exception as e:
|
||||
log_fn(f"❌ Ошибка при импорте: {e}")
|
||||
log_fn(traceback.format_exc())
|
||||
@@ -179,7 +179,7 @@ def import_152fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
||||
return data
|
||||
|
||||
|
||||
def _import_152_common(wb, data, settings, log_fn):
|
||||
def _import_149_common(wb, data, settings, log_fn):
|
||||
"""Импорт общих сведений по словарю настроек.
|
||||
|
||||
Для каждого поля: сначала пробуем конкретную ячейку (cell),
|
||||
@@ -247,7 +247,7 @@ def _import_152_common(wb, data, settings, log_fn):
|
||||
break
|
||||
|
||||
|
||||
def _import_152_is_list(wb, data, settings, log_fn):
|
||||
def _import_149_is_list(wb, data, settings, log_fn):
|
||||
"""Импорт информационных систем из остальных листов."""
|
||||
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
||||
is_sheets = []
|
||||
@@ -447,7 +447,7 @@ def _import_152_is_list(wb, data, settings, log_fn):
|
||||
log_fn(f"\n✅ Всего импортировано ИС: {len(is_list)}")
|
||||
|
||||
|
||||
def _import_152_employees(wb, data, settings, log_fn):
|
||||
def _import_149_employees(wb, data, settings, log_fn):
|
||||
"""Импорт комиссии и сотрудников из листа «Сотрудники»."""
|
||||
sheet_name = None
|
||||
sheet_kw = settings.get('employees_sheet_keyword', 'сотруд')
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Модели данных (только 152-ФЗ, только нужные поля)"""
|
||||
"""DokoGen — Модели данных (только 149-ФЗ (117 ПР ФСТЭК), только нужные поля)"""
|
||||
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from typing import List, Dict, Any
|
||||
@@ -49,7 +49,7 @@ class PaperDocument:
|
||||
|
||||
@dataclass
|
||||
class Company:
|
||||
"""Организация — основной объект (152-ФЗ)"""
|
||||
"""Организация — основной объект (149-ФЗ (117 ПР ФСТЭК))"""
|
||||
# Полное название
|
||||
full_name: str = ""
|
||||
company_name_1: str = "" # именительный
|
||||
|
||||
+29
-8
@@ -31,9 +31,10 @@ class DokoGenApp:
|
||||
|
||||
def __init__(self, root: tk.Tk):
|
||||
self.root = root
|
||||
root.title("ДоКоГеНеРаТоР — 152-ФЗ (Персональные данные)")
|
||||
root.title("ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)")
|
||||
root.geometry("1200x800")
|
||||
root.minsize(900, 600)
|
||||
self._set_app_icon()
|
||||
|
||||
self.model = Company()
|
||||
self.current_file: Optional[str] = None
|
||||
@@ -43,7 +44,7 @@ class DokoGenApp:
|
||||
self._last_template_dir: str = ""
|
||||
self._autosave_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
'.autosave', 'autosave_152.json'
|
||||
'.autosave', 'autosave_149fz.json'
|
||||
)
|
||||
|
||||
# Путь к файлу настроек импорта (словарь «что и откуда брать»)
|
||||
@@ -81,6 +82,26 @@ class DokoGenApp:
|
||||
self._load_autosave()
|
||||
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):
|
||||
menubar = tk.Menu(self.root)
|
||||
@@ -1736,7 +1757,7 @@ class DokoGenApp:
|
||||
is_list=self.model.information_systems,
|
||||
doc_number=self.doc_number_var.get(),
|
||||
doc_date=self.doc_date_var.get(),
|
||||
direction='152fz',
|
||||
direction='149fz',
|
||||
)
|
||||
|
||||
results = []
|
||||
@@ -1825,7 +1846,7 @@ class DokoGenApp:
|
||||
|
||||
def import_from_survey(self):
|
||||
path = filedialog.askopenfilename(
|
||||
title="Выберите опросный лист (152-ФЗ)",
|
||||
title="Выберите опросный лист (149-ФЗ (117 ПР ФСТЭК))",
|
||||
filetypes=[("Excel", "*.xlsx *.xls")]
|
||||
)
|
||||
if not path:
|
||||
@@ -1835,8 +1856,8 @@ class DokoGenApp:
|
||||
# Импорт в отдельном потоке
|
||||
def _run():
|
||||
try:
|
||||
from .importer import import_152fz
|
||||
data = import_152fz(path, self.log, settings_path=self.import_settings_path)
|
||||
from .importer import import_149fz
|
||||
data = import_149fz(path, self.log, settings_path=self.import_settings_path)
|
||||
if data:
|
||||
self._apply_imported_data(data)
|
||||
except ImportError:
|
||||
@@ -2182,7 +2203,7 @@ class DokoGenApp:
|
||||
|
||||
def show_about(self):
|
||||
messagebox.showinfo("О программе",
|
||||
"ДоКоГеНеРаТоР — 152-ФЗ\n"
|
||||
"ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)\n"
|
||||
"Версия 1.0\n"
|
||||
"Генератор документов из шаблонов")
|
||||
|
||||
@@ -2230,7 +2251,7 @@ class DokoGenApp:
|
||||
def download_survey(self):
|
||||
from .help_docs import ensure_docs, survey_path
|
||||
ensure_docs()
|
||||
self._save_resource(survey_path(), "Опросный_лист_152_ФЗ.xlsx", "Сохранить пустой опросник")
|
||||
self._save_resource(survey_path(), "Опросный_лист_149_FZ.xlsx", "Сохранить пустой опросник")
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
@@ -19,7 +19,7 @@ def _norm_name(s):
|
||||
# ОПИСАНИЕ ПЕРЕМЕННЫХ
|
||||
# ============================================================
|
||||
# Формат: (имя_переменной, группа, описание, ключ_резолвера)
|
||||
# Группы: A — общие, B — 152-ФЗ
|
||||
# Группы: A — общие, B — 149-ФЗ (117 ПР ФСТЭК)
|
||||
|
||||
VARIABLE_DEFS = [
|
||||
# === Полное название организации (6 падежей) ===
|
||||
@@ -68,7 +68,7 @@ VARIABLE_DEFS = [
|
||||
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
|
||||
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
||||
|
||||
# === 152-ФЗ ===
|
||||
# === 149-ФЗ (117 ПР ФСТЭК) ===
|
||||
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
||||
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
||||
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
||||
@@ -138,7 +138,7 @@ def build_replacements(
|
||||
is_list: List = None,
|
||||
doc_number: str = "",
|
||||
doc_date: str = "",
|
||||
direction: str = "152fz",
|
||||
direction: str = "149fz",
|
||||
) -> Dict[str, str]:
|
||||
"""Строит полный словарь замен {{переменная}} → значение."""
|
||||
if is_list is None:
|
||||
@@ -159,7 +159,7 @@ def build_replacements(
|
||||
|
||||
replacements = {}
|
||||
for var_name, group, desc, source in VARIABLE_DEFS:
|
||||
if direction == '152fz' and group not in ('A', 'B'):
|
||||
if direction == '149fz' and group not in ('A', 'B'):
|
||||
continue
|
||||
value = _resolve(source, ctx)
|
||||
replacements[var_name] = str(value) if value else '—'
|
||||
|
||||
+1
-1
@@ -399,7 +399,7 @@ def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None
|
||||
if not api_key:
|
||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||
system = (
|
||||
"Ты — юрист по защите персональных данных (152-ФЗ). Проверь реквизиты организации. "
|
||||
"Ты — юрист по защите персональных данных (149-ФЗ (117 ПР ФСТЭК)). Проверь реквизиты организации. "
|
||||
"Верни ТОЛЬКО JSON: "
|
||||
'{"оценка": "ok|warning|error", '
|
||||
'"замечания": ["..."], '
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""DokoGen — Генератор документов 152-ФЗ
|
||||
"""DokoGen — Генератор документов 149-ФЗ (117 ПР ФСТЭК)
|
||||
Точка входа: python main.py
|
||||
"""
|
||||
import sys
|
||||
|
||||
+2
-2
@@ -87,7 +87,7 @@ def test_zip_loop_expansion():
|
||||
is_list=company.information_systems,
|
||||
doc_number='001',
|
||||
doc_date='31.07.2026',
|
||||
direction='152fz'
|
||||
direction='149fz'
|
||||
)
|
||||
|
||||
loops = replacements.get('_loops', {})
|
||||
@@ -135,7 +135,7 @@ def test_zip_loop_expansion():
|
||||
is_list=company.information_systems,
|
||||
doc_number='001',
|
||||
doc_date='31.07.2026',
|
||||
direction='152fz'
|
||||
direction='149fz'
|
||||
)
|
||||
|
||||
try:
|
||||
|
||||
Reference in New Issue
Block a user