|
|
@@ -0,0 +1,382 @@
|
|
|
+from datetime import datetime
|
|
|
+from ara_imgui import imgui
|
|
|
+import psycopg2
|
|
|
+import db
|
|
|
+
|
|
|
+# Страница компаний
|
|
|
+def gui_organizations():
|
|
|
+ imgui.text("Список компаний:")
|
|
|
+
|
|
|
+ # Таблица значений
|
|
|
+ if imgui.begin_child("##organizations_table"):
|
|
|
+ imgui.columns(3)
|
|
|
+
|
|
|
+ # Заголовки столбцов
|
|
|
+ for title in ["id", "Наименование", "Тип"]:
|
|
|
+ imgui.text(title)
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ imgui.separator()
|
|
|
+
|
|
|
+ # Значения
|
|
|
+ for o in db.organizations:
|
|
|
+ imgui.text(str(o["id"]))
|
|
|
+ imgui.next_column()
|
|
|
+ imgui.text(o["name"])
|
|
|
+ imgui.next_column()
|
|
|
+ imgui.text(o["legal_form"])
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ imgui.columns(1)
|
|
|
+ imgui.end_child()
|
|
|
+
|
|
|
+# Страница сотрудников
|
|
|
+def gui_employees():
|
|
|
+ imgui.text("Список сотрудников:")
|
|
|
+
|
|
|
+ # Таблица значений
|
|
|
+ if imgui.begin_child("##employees_table"):
|
|
|
+ imgui.columns(5)
|
|
|
+
|
|
|
+ # Заголовки столбцов
|
|
|
+ for title in ["id", "Имя", "Фамилия", "Отчество", "Компания"]:
|
|
|
+ imgui.text(title)
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ imgui.separator()
|
|
|
+
|
|
|
+ # Значения
|
|
|
+ for e in db.employees:
|
|
|
+ org = [o for o in db.organizations if o["id"] == e["organization_id"]][0]
|
|
|
+ imgui.text(str(e["id"]))
|
|
|
+ imgui.next_column()
|
|
|
+ imgui.text(e["name"])
|
|
|
+ imgui.next_column()
|
|
|
+ imgui.text(e["lastname"])
|
|
|
+ imgui.next_column()
|
|
|
+ imgui.text(e["patronymic"])
|
|
|
+ imgui.next_column()
|
|
|
+ imgui.text(f'{org["legal_form"]} "{org["name"]}"')
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ imgui.columns(1)
|
|
|
+ imgui.end_child()
|
|
|
+
|
|
|
+# Страница корреспонденций
|
|
|
+DOCUMENT_TYPES = [
|
|
|
+ 'информационный',
|
|
|
+ 'организационно-распорядительный',
|
|
|
+ 'запросно-ответный',
|
|
|
+ 'инициативный',
|
|
|
+ 'другой'
|
|
|
+]
|
|
|
+
|
|
|
+selected_correspondence_id = None # Выбранная корреспонденция (для редактирования)
|
|
|
+total_correspondencies = None # Общее количество корреспонденций для выбранной компании
|
|
|
+
|
|
|
+selected_employee_pos = 0 # Выбранный сотрудник
|
|
|
+selected_organization_pos = 0 # Выбранная компания
|
|
|
+receipt_date_input = "" # Введенная дата поступления
|
|
|
+execution_date_input = "" # Введенная дата исполнения
|
|
|
+document_type_pos = 0 # Выбраный тип документа
|
|
|
+document_number_input = "" # Введенный номер документа
|
|
|
+
|
|
|
+search_document_name = "" # Имя докумнета для поиска
|
|
|
+
|
|
|
+error_text = None # Текст ошибки (если есть)
|
|
|
+
|
|
|
+def gui_correspondencies():
|
|
|
+ global selected_correspondence_id
|
|
|
+ global total_correspondencies
|
|
|
+ global selected_employee_pos, selected_organization_pos
|
|
|
+ global receipt_date_input, execution_date_input
|
|
|
+ global document_type_pos, document_number_input
|
|
|
+ global search_document_name
|
|
|
+ global error_text
|
|
|
+
|
|
|
+ # Данные для списка компаний
|
|
|
+ organizations_list = [f'{o["legal_form"]} "{o["name"]}"' for o in db.organizations]
|
|
|
+ selected_organization_id = db.organizations[selected_organization_pos]["id"]
|
|
|
+
|
|
|
+ # Выпадающий список для выбора компании
|
|
|
+ updated, selected_organization_pos = imgui.combo("Компания", selected_organization_pos, organizations_list)
|
|
|
+
|
|
|
+ if updated or (total_correspondencies is None):
|
|
|
+ selected_organization_id = db.organizations[selected_organization_pos]["id"]
|
|
|
+ total_correspondencies = db.count_correspondencies_by_organization(selected_organization_id)
|
|
|
+
|
|
|
+
|
|
|
+ imgui.text(f"Всего корреспонденций: {total_correspondencies}")
|
|
|
+
|
|
|
+ # Панель редактирования
|
|
|
+ imgui.separator()
|
|
|
+ imgui.text("Добавить корреспонденцию")
|
|
|
+
|
|
|
+ # Дата поступления
|
|
|
+ if imgui.button("Сегодня ##1"):
|
|
|
+ receipt_date_input = datetime.today().strftime("%d-%m-%Y")
|
|
|
+
|
|
|
+ imgui.same_line()
|
|
|
+
|
|
|
+ receipt_date_input = imgui.input_text("Дата поступления", receipt_date_input)[1]
|
|
|
+ if imgui.is_item_hovered():
|
|
|
+ imgui.set_tooltip("В формате ДД-ММ-ГГГГ")
|
|
|
+
|
|
|
+ # Дата исполнения
|
|
|
+ if imgui.button("Сегодня ##2"):
|
|
|
+ execution_date_input = datetime.today().strftime("%d-%m-%Y")
|
|
|
+
|
|
|
+ imgui.same_line()
|
|
|
+
|
|
|
+ execution_date_input = imgui.input_text("Дата исполнения (опционально)", execution_date_input)[1]
|
|
|
+ if imgui.is_item_hovered():
|
|
|
+ imgui.set_tooltip("В формате ДД-ММ-ГГГГ")
|
|
|
+
|
|
|
+ # Данные для списка сотрудников (только сотрудники выбранной компании)
|
|
|
+ employees_list = [f'{e["lastname"]} {e["name"]} {e["patronymic"]}' for e in db.employees if e["organization_id"] == selected_organization_id]
|
|
|
+
|
|
|
+ # Выпадающие список для выбора сотрудника
|
|
|
+ selected_employee_pos = imgui.combo("Сотрудник", selected_employee_pos, employees_list)[1]
|
|
|
+
|
|
|
+ # Выпадающий список для выбора типа документа
|
|
|
+ document_type_pos = imgui.combo("Тип документа", document_type_pos, DOCUMENT_TYPES)[1]
|
|
|
+
|
|
|
+ # Ввод номера документа
|
|
|
+ document_number_input = imgui.input_text("Номер документа", document_number_input)[1]
|
|
|
+
|
|
|
+ if selected_correspondence_id:
|
|
|
+ # Кнопка сохранения изменений
|
|
|
+ if imgui.button("Сохранить изменения"):
|
|
|
+ error_text = None
|
|
|
+
|
|
|
+ # Парсим введенные даты
|
|
|
+ try:
|
|
|
+ receipt_date = datetime.strptime(receipt_date_input, "%d-%m-%Y")
|
|
|
+
|
|
|
+ except ValueError:
|
|
|
+ error_text = "Неверный формат даты поступления"
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ execution_date = datetime.strptime(execution_date_input, "%d-%m-%Y") if execution_date_input else None
|
|
|
+
|
|
|
+ except ValueError:
|
|
|
+ error_text = "Неверный формат даты исполнения"
|
|
|
+ return
|
|
|
+
|
|
|
+ # Проверяем, что дата исполнения не раньше даты поступления
|
|
|
+ if execution_date and execution_date < receipt_date:
|
|
|
+ error_text = "Дата исполнения не может быть раньше даты поступления"
|
|
|
+ return
|
|
|
+
|
|
|
+ # Проверяем, что номер документа не пустой
|
|
|
+ if not document_number_input:
|
|
|
+ error_text = "Номер документа не может быть пустым"
|
|
|
+ return
|
|
|
+
|
|
|
+ # Изменяем корреспонденцию в базе данных
|
|
|
+ db.edit_correspondence(
|
|
|
+ correspondence_id=selected_correspondence_id,
|
|
|
+ document_type=DOCUMENT_TYPES[document_type_pos],
|
|
|
+ document_number=document_number_input,
|
|
|
+ receipt_date=receipt_date,
|
|
|
+ execution_date=execution_date,
|
|
|
+ employee_id=db.employees[selected_employee_pos]["id"]
|
|
|
+ )
|
|
|
+ db.get_all_correspondencies()
|
|
|
+ selected_correspondence_id = None
|
|
|
+ receipt_date_input = ""
|
|
|
+ execution_date_input = ""
|
|
|
+ document_type_pos = 0
|
|
|
+ document_number_input = ""
|
|
|
+ selected_organization_pos = 0
|
|
|
+ selected_employee_pos = 0
|
|
|
+
|
|
|
+ # Кнопка отмены редактирования
|
|
|
+ imgui.same_line()
|
|
|
+
|
|
|
+ if imgui.button("Отмена"):
|
|
|
+ selected_correspondence_id = None
|
|
|
+ receipt_date_input = ""
|
|
|
+ execution_date_input = ""
|
|
|
+ document_type_pos = 0
|
|
|
+ document_number_input = ""
|
|
|
+ selected_organization_pos = 0
|
|
|
+ selected_employee_pos = 0
|
|
|
+ error_text = None
|
|
|
+
|
|
|
+ else:
|
|
|
+ # Кнопка добавления новой корреспонденции
|
|
|
+ if imgui.button("Добавить"):
|
|
|
+ error_text = None
|
|
|
+
|
|
|
+ # Парсим введенные даты
|
|
|
+ try:
|
|
|
+ receipt_date = datetime.strptime(receipt_date_input, "%d-%m-%Y")
|
|
|
+
|
|
|
+ except ValueError:
|
|
|
+ error_text = "Неверный формат даты поступления"
|
|
|
+ return
|
|
|
+
|
|
|
+ try:
|
|
|
+ execution_date = datetime.strptime(execution_date_input, "%d-%m-%Y") if execution_date_input else None
|
|
|
+
|
|
|
+ except ValueError:
|
|
|
+ error_text = "Неверный формат даты исполнения"
|
|
|
+ return
|
|
|
+
|
|
|
+ # Проверяем, что дата исполнения не раньше даты поступления
|
|
|
+ if execution_date and execution_date < receipt_date:
|
|
|
+ error_text = "Дата исполнения не может быть раньше даты поступления"
|
|
|
+ return
|
|
|
+
|
|
|
+ # Проверяем, что номер документа не пустой
|
|
|
+ if not document_number_input:
|
|
|
+ error_text = "Номер документа не может быть пустым"
|
|
|
+ return
|
|
|
+
|
|
|
+ # Добавляем новую корреспонденцию в базу данных
|
|
|
+ try:
|
|
|
+ selected_employee_id = db.employees[selected_employee_pos]["id"]
|
|
|
+ db.add_correspondence(
|
|
|
+ document_type=DOCUMENT_TYPES[document_type_pos],
|
|
|
+ document_number=document_number_input,
|
|
|
+ receipt_date=receipt_date,
|
|
|
+ execution_date=execution_date,
|
|
|
+ employee_id=selected_employee_id
|
|
|
+ )
|
|
|
+
|
|
|
+ # Обновляем список корреспонденций
|
|
|
+ db.get_all_correspondencies()
|
|
|
+ except psycopg2.errors.UniqueViolation:
|
|
|
+ error_text = "Корреспонденция с таким номером уже существует"
|
|
|
+
|
|
|
+ # Выводим текст ошибки (если есть)
|
|
|
+ if error_text:
|
|
|
+ imgui.text_colored(imgui.ImVec4(1.0, 0.3, 0.3, 1.0), error_text)
|
|
|
+
|
|
|
+ # Панель поиска
|
|
|
+ imgui.separator()
|
|
|
+ search_document_name = imgui.input_text("Поиск по номеру документа", search_document_name)[1]
|
|
|
+
|
|
|
+ # Список всех корреспонденций
|
|
|
+ if imgui.begin_child("##correspondencies_table"):
|
|
|
+ imgui.columns(6)
|
|
|
+
|
|
|
+ # Заголовки столбцов
|
|
|
+ for title in ["Дата поступления", "Дата исполнения", "Сотрудник", "Тип документа", "Номер документа", "Действия"]:
|
|
|
+ imgui.text(title)
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ imgui.separator()
|
|
|
+
|
|
|
+ # Значения
|
|
|
+ for i, c in enumerate(db.correspondencies):
|
|
|
+ # Данные сотрудника и компании для текущей корреспонденции
|
|
|
+ employee = next(e for e in db.employees if e["id"] == c["employee_id"])
|
|
|
+ organization = next(o for o in db.organizations if o["id"] == employee["organization_id"])
|
|
|
+
|
|
|
+ if organization["id"] != selected_organization_id:
|
|
|
+ continue
|
|
|
+
|
|
|
+ if search_document_name.lower() not in c["document_number"].lower():
|
|
|
+ continue
|
|
|
+
|
|
|
+ # Дата поступления/исполнения
|
|
|
+ imgui.text(c["receipt_date"].strftime("%d-%m-%Y"))
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ imgui.text(c["execution_date"].strftime("%d-%m-%Y") if c["execution_date"] else "-")
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ # Сотрудник
|
|
|
+ imgui.text(f'{employee["lastname"]} {employee["name"]} {employee["patronymic"]}')
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ # Тип и номер документа
|
|
|
+ imgui.text(c["document_type"])
|
|
|
+ imgui.next_column()
|
|
|
+ imgui.text(c["document_number"])
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ # Действия
|
|
|
+ if imgui.button(f"Изменить ##{i}"):
|
|
|
+ selected_correspondence_id = c["id"]
|
|
|
+ receipt_date_input = c["receipt_date"].strftime("%d-%m-%Y")
|
|
|
+ execution_date_input = c["execution_date"].strftime("%d-%m-%Y") if c["execution_date"] else ""
|
|
|
+ document_type_pos = DOCUMENT_TYPES.index(c["document_type"])
|
|
|
+ document_number_input = c["document_number"]
|
|
|
+ selected_employee_pos = employees_list.index(f'{employee["lastname"]} {employee["name"]} {employee["patronymic"]}')
|
|
|
+ error_text = None
|
|
|
+
|
|
|
+ imgui.same_line()
|
|
|
+
|
|
|
+ if imgui.button(f"Удалить ##{i}"):
|
|
|
+ db.delete_correspondence(c["id"])
|
|
|
+ db.get_all_correspondencies()
|
|
|
+ selected_correspondence_id = None
|
|
|
+
|
|
|
+ imgui.next_column()
|
|
|
+
|
|
|
+ imgui.columns(1)
|
|
|
+ imgui.end_child()
|
|
|
+
|
|
|
+# Страница авторизации
|
|
|
+connected = False
|
|
|
+db_username = ""
|
|
|
+db_password = ""
|
|
|
+auth_error_text = None
|
|
|
+def gui_auth():
|
|
|
+ global connected
|
|
|
+ global db_username, db_password, auth_error_text
|
|
|
+
|
|
|
+ imgui.text("Авторизация")
|
|
|
+
|
|
|
+ imgui.text("Введите имя пользователя и пароль для подключения к базе данных")
|
|
|
+
|
|
|
+ db_username = imgui.input_text("Имя пользователя", db_username)[1]
|
|
|
+ db_password = imgui.input_text("Пароль", db_password)[1]
|
|
|
+
|
|
|
+ if auth_error_text:
|
|
|
+ imgui.text_colored(imgui.ImVec4(1.0, 0.3, 0.3, 1.0), auth_error_text)
|
|
|
+
|
|
|
+ if imgui.button("Подключиться"):
|
|
|
+ try:
|
|
|
+ init(username=db_username, password=db_password)
|
|
|
+ connected = True
|
|
|
+ except psycopg2.OperationalError:
|
|
|
+ auth_error_text = "Ошибка подключения к базе данных. Проверьте имя пользователя и пароль."
|
|
|
+
|
|
|
+
|
|
|
+# Главное меню
|
|
|
+selected_tab = 0 # Выбранная страница
|
|
|
+
|
|
|
+def gui_main():
|
|
|
+ global selected_tab
|
|
|
+
|
|
|
+ if not connected:
|
|
|
+ gui_auth()
|
|
|
+ else:
|
|
|
+ for i, title in enumerate(["Компании","Сотрудники", "Корреспонденция"]):
|
|
|
+ if imgui.button(title):
|
|
|
+ selected_tab = i
|
|
|
+
|
|
|
+ if i < 2:
|
|
|
+ imgui.same_line()
|
|
|
+
|
|
|
+
|
|
|
+ if selected_tab == 0:
|
|
|
+ gui_organizations()
|
|
|
+ elif selected_tab == 1:
|
|
|
+ gui_employees()
|
|
|
+ elif selected_tab == 2:
|
|
|
+ gui_correspondencies()
|
|
|
+ else:
|
|
|
+ imgui.text("Ошибка: неизвестная страница")
|
|
|
+
|
|
|
+
|
|
|
+def init(username, password):
|
|
|
+ db.init_connection(username=username, password=password)
|
|
|
+ db.get_all_organizations()
|
|
|
+ db.get_all_employees()
|
|
|
+ db.get_all_correspondencies()
|