feat: вкладка «Бумажные документы» + импорт из опросника

- Новая вкладка: название документа + место хранения, добавить/удалить
- Импорт из опросника: «Названия документов» (строка 61) и «Место
  хранения» (строка 62) → цикл {{paper_documents}} ({{document}}/{{storage}})
- Сохранение/автосохранение/генерация собирают данные вкладки
- Загрузка файла проекта восстанавливает список документов
This commit is contained in:
2026-08-03 12:27:32 +04:00
parent 353e7403d0
commit 1faf6196af
+129
View File
@@ -116,6 +116,7 @@ class DokoGenApp:
self._build_is_tab() self._build_is_tab()
self._build_commission_tab() self._build_commission_tab()
self._build_users_tab() self._build_users_tab()
self._build_paper_tab()
self._build_generation_tab() self._build_generation_tab()
self._build_log_tab() self._build_log_tab()
@@ -548,6 +549,102 @@ class DokoGenApp:
if emp['position'] or emp['fio']: if emp['position'] or emp['fio']:
self.model.employees_access.append(emp) self.model.employees_access.append(emp)
# ---------- ВКЛАДКА "БУМАЖНЫЕ ДОКУМЕНТЫ" ----------
def _build_paper_tab(self):
tab = ttk.Frame(self.notebook)
self.notebook.add(tab, text="Бумажные документы")
canvas = tk.Canvas(tab, highlightthickness=0)
scrollbar = ttk.Scrollbar(tab, orient=tk.VERTICAL, command=canvas.yview)
scrollable = ttk.Frame(canvas)
scrollable.bind("<Configure>",
lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=scrollable, anchor="nw")
canvas.configure(yscrollcommand=scrollbar.set)
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
frame = ttk.Frame(scrollable, padding=10)
frame.pack(fill=tk.BOTH, expand=True)
ttk.Label(frame, text="Бумажные носители ПДн (документы)",
font=('Arial', 11, 'bold')).pack(anchor=tk.W, pady=(0, 10))
# Заголовки
header = ttk.Frame(frame)
header.pack(fill=tk.X)
for w, col in [(45, "Название документа"), (45, "Место хранения")]:
ttk.Label(header, text=col, font=('', 9, 'bold'),
width=w, anchor=tk.W).pack(side=tk.LEFT, padx=2)
self._paper_frame = ttk.Frame(frame)
self._paper_frame.pack(fill=tk.X, pady=5)
btn_frame = ttk.Frame(frame)
btn_frame.pack(fill=tk.X, pady=5)
ttk.Button(btn_frame, text="+ Добавить документ",
command=self._add_paper_row_ui).pack(side=tk.LEFT, padx=2)
ttk.Button(btn_frame, text="- Удалить выбранный",
command=self._delete_paper_selected).pack(side=tk.LEFT, padx=2)
self._paper_count_label = ttk.Label(frame, text="Добавлено: 0")
self._paper_count_label.pack(anchor=tk.W, pady=5)
self._paper_entries = []
# Загрузка существующих
for p in self.model.paper_documents_list:
self._add_paper_row(p.name, p.storage)
self._update_paper_count()
def _add_paper_row(self, name="", storage=""):
"""Добавляет одну строку документа."""
row = ttk.Frame(self._paper_frame)
row.pack(fill=tk.X, pady=2)
name_entry = ttk.Entry(row, width=45)
name_entry.insert(0, name)
name_entry.pack(side=tk.LEFT, padx=2)
storage_entry = ttk.Entry(row, width=45)
storage_entry.insert(0, storage)
storage_entry.pack(side=tk.LEFT, padx=2)
del_btn = ttk.Button(row, text="", width=3,
command=lambda: self._delete_paper_row(row))
del_btn.pack(side=tk.LEFT, padx=5)
self._paper_entries.append((row, name_entry, storage_entry))
self._update_paper_count()
def _add_paper_row_ui(self):
self._add_paper_row()
def _delete_paper_row(self, row_frame):
self._paper_entries = [e for e in self._paper_entries if e[0] != row_frame]
row_frame.destroy()
self._update_paper_count()
def _delete_paper_selected(self):
if self._paper_entries:
row_frame = self._paper_entries[-1][0]
self._delete_paper_row(row_frame)
def _update_paper_count(self):
count = len(self._paper_entries)
if hasattr(self, '_paper_count_label'):
self._paper_count_label.config(text=f"Добавлено: {count}")
def _collect_paper_documents(self):
"""Собирает данные бумажных документов из GUI в модель."""
self.model.paper_documents_list.clear()
for row, name_e, storage_e in self._paper_entries:
name = name_e.get().strip()
storage = storage_e.get().strip()
if name or storage:
self.model.paper_documents_list.append(
PaperDocument(name=name, storage=storage)
)
# ---------- ВКЛАДКА "ГЕНЕРАЦИЯ" ---------- # ---------- ВКЛАДКА "ГЕНЕРАЦИЯ" ----------
def _build_generation_tab(self): def _build_generation_tab(self):
tab = ttk.Frame(self.notebook) tab = ttk.Frame(self.notebook)
@@ -710,6 +807,15 @@ class DokoGenApp:
) )
self._update_emp_count() self._update_emp_count()
# Бумажные документы
if hasattr(self, '_paper_entries'):
for row_frame, _, _ in self._paper_entries:
row_frame.destroy()
self._paper_entries.clear()
for p in c.paper_documents_list:
self._add_paper_row(p.name, p.storage)
self._update_paper_count()
# Шаблоны # Шаблоны
self._on_templates_changed() self._on_templates_changed()
@@ -1163,6 +1269,7 @@ class DokoGenApp:
self._collect_commission() self._collect_commission()
self._collect_is() self._collect_is()
self._collect_employees() self._collect_employees()
self._collect_paper_documents()
# Проверка # Проверка
if not self._validate_before_generate(): if not self._validate_before_generate():
@@ -1446,6 +1553,26 @@ class DokoGenApp:
) )
self._update_emp_count() self._update_emp_count()
# Бумажные документы (из «Названия документов» / «Место хранения»)
if data.get('paperDocuments'):
self.model.paper_documents_list.clear()
names = [n.strip() for n in data['paperDocuments'].replace('\n', ';').split(';') if n.strip()]
storages = [s.strip() for s in (data.get('paperStorage') or '').replace('\n', ';').split(';') if s.strip()]
for i, name in enumerate(names):
storage = storages[i] if i < len(storages) else (storages[0] if storages else '')
self.model.paper_documents_list.append(
PaperDocument(name=name, storage=storage)
)
# Обновляем GUI
if hasattr(self, '_paper_entries'):
for row_frame, _, _ in self._paper_entries:
row_frame.destroy()
self._paper_entries.clear()
for p in self.model.paper_documents_list:
self._add_paper_row(p.name, p.storage)
self._update_paper_count()
self.log(f" Бумажные документы: {len(self.model.paper_documents_list)} шт.")
self.log("✅ Импорт применён") self.log("✅ Импорт применён")
self.root.after(0, _apply) self.root.after(0, _apply)
@@ -1490,6 +1617,7 @@ class DokoGenApp:
self._collect_commission() self._collect_commission()
self._collect_is() self._collect_is()
self._collect_employees() self._collect_employees()
self._collect_paper_documents()
try: try:
with open(filename, 'w', encoding='utf-8') as f: with open(filename, 'w', encoding='utf-8') as f:
json.dump(self.model.to_dict(), f, ensure_ascii=False, indent=2) json.dump(self.model.to_dict(), f, ensure_ascii=False, indent=2)
@@ -1515,6 +1643,7 @@ class DokoGenApp:
self._collect_commission() self._collect_commission()
self._collect_is() self._collect_is()
self._collect_employees() self._collect_employees()
self._collect_paper_documents()
try: try:
os.makedirs(os.path.dirname(self._autosave_path), exist_ok=True) os.makedirs(os.path.dirname(self._autosave_path), exist_ok=True)
with open(self._autosave_path, 'w', encoding='utf-8') as f: with open(self._autosave_path, 'w', encoding='utf-8') as f: