diff --git a/PEP8_patch.diff b/PEP8_patch.diff new file mode 100644 index 0000000..113eefc --- /dev/null +++ b/PEP8_patch.diff @@ -0,0 +1,6575 @@ +diff --git a/PEP8_patch.diff b/PEP8_patch.diff +index 1e35d99..e69de29 100644 +--- a/PEP8_patch.diff ++++ b/PEP8_patch.diff +@@ -1,6570 +0,0 @@ +-diff --git a/PEP8_patch.diff b/PEP8_patch.diff +-new file mode 100644 +-index 0000000..e69de29 +-diff --git a/db/models/__init__.py b/db/models/__init__.py +-index 86fa1d2..1bc328d 100644 +---- a/db/models/__init__.py +-+++ b/db/models/__init__.py +-@@ -1,8 +1,8 @@ +--from .base import Base +--from .client import Client +--from .holiday import Holiday +--from .price import Price +--from .sale import Sale +--from .ticket import Ticket +--from .user import User +--from .workday import Workday +-+from .base import Base as Base +-+from .client import Client as Client +-+from .holiday import Holiday as Holiday +-+from .price import Price as Price +-+from .sale import Sale as Sale +-+from .ticket import Ticket as Ticket +-+from .user import User as User +-+from .workday import Workday as Workday +-diff --git a/db/models/sale.py b/db/models/sale.py +-index a0035ee..bbe3896 100644 +---- a/db/models/sale.py +-+++ b/db/models/sale.py +-@@ -1,6 +1,7 @@ +- from datetime import datetime +- +--from sqlalchemy import DateTime, ForeignKey, Integer, SmallInteger, String, Text +-+from sqlalchemy import (DateTime, ForeignKey, Integer, SmallInteger, String, +-+ Text) +- from sqlalchemy.orm import Mapped, mapped_column, relationship +- +- from db.models.base import Base +-diff --git a/design/logic/client.py b/design/logic/client.py +-index ddc3b51..6bf01e0 100644 +---- a/design/logic/client.py +-+++ b/design/logic/client.py +-@@ -1,16 +1,7 @@ +- from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, Qt +--from PySide6.QtWidgets import ( +-- QComboBox, +-- QDateEdit, +-- QFormLayout, +-- QGridLayout, +-- QHBoxLayout, +-- QLabel, +-- QLineEdit, +-- QPushButton, +-- QToolBox, +-- QWidget, +--) +-+from PySide6.QtWidgets import (QComboBox, QDateEdit, QFormLayout, QGridLayout, +-+ QHBoxLayout, QLabel, QLineEdit, QPushButton, +-+ QToolBox, QWidget) +- +- +- class Ui_Dialog_Client(object): +-diff --git a/design/logic/main_form.py b/design/logic/main_form.py +-index b3bfd74..45add21 100644 +---- a/design/logic/main_form.py +-+++ b/design/logic/main_form.py +-@@ -1,25 +1,10 @@ +- from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt +- from PySide6.QtGui import QAction, QFont +--from PySide6.QtWidgets import ( +-- QAbstractItemView, +-- QCheckBox, +-- QComboBox, +-- QDateEdit, +-- QFrame, +-- QGroupBox, +-- QHBoxLayout, +-- QLabel, +-- QLineEdit, +-- QMenu, +-- QMenuBar, +-- QPushButton, +-- QRadioButton, +-- QStatusBar, +-- QTabWidget, +-- QTableWidget, +-- QTableWidgetItem, +-- QWidget, +--) +-+from PySide6.QtWidgets import (QAbstractItemView, QCheckBox, QComboBox, +-+ QDateEdit, QFrame, QGroupBox, QHBoxLayout, +-+ QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, +-+ QRadioButton, QStatusBar, QTableWidget, +-+ QTableWidgetItem, QTabWidget, QWidget) +- +- +- class Ui_MainWindow(object): +-diff --git a/design/logic/pay.py b/design/logic/pay.py +-index 3926a8f..8a842b7 100644 +---- a/design/logic/pay.py +-+++ b/design/logic/pay.py +-@@ -1,6 +1,6 @@ +- from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt +- from PySide6.QtGui import QFont +--from PySide6.QtWidgets import QGroupBox, QLabel, QPushButton, QCheckBox +-+from PySide6.QtWidgets import QCheckBox, QGroupBox, QLabel, QPushButton +- +- +- class Ui_Dialog_Pay(object): +-diff --git a/design/logic/progress_dialog.py b/design/logic/progress_dialog.py +-index e7b5b3e..1da4896 100644 +---- a/design/logic/progress_dialog.py +-+++ b/design/logic/progress_dialog.py +-@@ -1,23 +1,23 @@ +--from PySide6.QtCore import (QCoreApplication, QMetaObject, QSize, Qt) +--from PySide6.QtWidgets import (QLabel, QProgressBar, +-- QVBoxLayout) +-+from PySide6.QtCore import QCoreApplication, QMetaObject, QSize, Qt +-+from PySide6.QtWidgets import QLabel, QProgressBar, QVBoxLayout +-+ +- +- class Ui_ProgressDialog(object): +- def setupUi(self, ProgressDialog): +- if not ProgressDialog.objectName(): +-- ProgressDialog.setObjectName(u"ProgressDialog") +-+ ProgressDialog.setObjectName("ProgressDialog") +- ProgressDialog.resize(300, 127) +- ProgressDialog.setMinimumSize(QSize(300, 100)) +- self.verticalLayout = QVBoxLayout(ProgressDialog) +-- self.verticalLayout.setObjectName(u"verticalLayout") +-+ self.verticalLayout.setObjectName("verticalLayout") +- self.label_status = QLabel(ProgressDialog) +-- self.label_status.setObjectName(u"label_status") +-+ self.label_status.setObjectName("label_status") +- self.label_status.setAlignment(Qt.AlignmentFlag.AlignCenter) +- +- self.verticalLayout.addWidget(self.label_status) +- +- self.progressBar = QProgressBar(ProgressDialog) +-- self.progressBar.setObjectName(u"progressBar") +-+ self.progressBar.setObjectName("progressBar") +- self.progressBar.setMinimum(0) +- self.progressBar.setMaximum(100) +- self.progressBar.setValue(0) +-@@ -25,14 +25,26 @@ class Ui_ProgressDialog(object): +- +- self.verticalLayout.addWidget(self.progressBar) +- +-- +- self.retranslateUi(ProgressDialog) +- +- QMetaObject.connectSlotsByName(ProgressDialog) +-+ +- # setupUi +- +- def retranslateUi(self, ProgressDialog): +-- ProgressDialog.setWindowTitle(QCoreApplication.translate("ProgressDialog", u"\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435...", None)) +-- self.label_status.setText(QCoreApplication.translate("ProgressDialog", u"\u041d\u0430\u0447\u0430\u043b\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438...", None)) +-- # retranslateUi +-+ ProgressDialog.setWindowTitle( +-+ QCoreApplication.translate( +-+ "ProgressDialog", +-+ "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435...", +-+ None, +-+ ) +-+ ) +-+ self.label_status.setText( +-+ QCoreApplication.translate( +-+ "ProgressDialog", +-+ "\u041d\u0430\u0447\u0430\u043b\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438...", +-+ None, +-+ ) +-+ ) +- +-+ # retranslateUi +-diff --git a/design/logic/sale.py b/design/logic/sale.py +-index ab9ae05..f791242 100644 +---- a/design/logic/sale.py +-+++ b/design/logic/sale.py +-@@ -1,18 +1,8 @@ +- from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt +- from PySide6.QtGui import QFont +--from PySide6.QtWidgets import ( +-- QAbstractItemView, +-- QCheckBox, +-- QComboBox, +-- QDateEdit, +-- QFrame, +-- QGroupBox, +-- QLabel, +-- QLineEdit, +-- QPushButton, +-- QTableWidget, +-- QTableWidgetItem, +--) +-+from PySide6.QtWidgets import (QAbstractItemView, QCheckBox, QComboBox, +-+ QDateEdit, QFrame, QGroupBox, QLabel, QLineEdit, +-+ QPushButton, QTableWidget, QTableWidgetItem) +- +- +- class Ui_Dialog_Sale(object): +-diff --git a/main.py b/main.py +-index c7f54d0..aa29658 100644 +---- a/main.py +-+++ b/main.py +-@@ -7,22 +7,14 @@ from pathlib import Path +- from typing import Any, Optional, Type +- +- from PySide6 import QtCore, QtWidgets +--from PySide6.QtCore import Qt +--from PySide6.QtCore import Signal, QThread +-+from PySide6.QtCore import Qt, QThread, Signal +- from PySide6.QtGui import QColor, QPixmap +--from PySide6.QtWidgets import ( +-- QCheckBox, +-- QHBoxLayout, +-- QTableWidgetItem, +--) +--from PySide6.QtWidgets import QDialog, QWidget +--from PySide6.QtWidgets import QMessageBox +--from sqlalchemy import and_, select, update, desc +-+from PySide6.QtWidgets import (QCheckBox, QDialog, QHBoxLayout, QMessageBox, +-+ QTableWidgetItem, QWidget) +-+from sqlalchemy import and_, desc, select, update +- from sqlalchemy.orm import Session +- +--from db.models import Client +--from db.models import Sale +--from db.models import Ticket +-+from db.models import Client, Sale, Ticket +- from design.logic.authorization import Ui_Dialog +- from design.logic.client import Ui_Dialog_Client +- from design.logic.main_form import Ui_MainWindow +-@@ -35,21 +27,14 @@ from modules import windows +- from modules.auth_logic import perform_pre_sale_checks +- from modules.logger import logger, logger_wraps +- from modules.progress_window import ProgressWindow +--from modules.sale_logic import ( +-- calculate_age, +-- calculate_ticket_type, +-- calculated_ticket_price, +-- calculate_adult_price, +-- calculate_child_price, +-- calculate_discounted_price, +-- calculate_discount, +-- calculate_itog, +-- convert_sale_dict_values, +-- generating_parts_for_partial_returns, +-- get_talent_based_on_time, +-- update_adult_count, +-- update_child_count, +--) +-+from modules.sale_logic import (calculate_adult_price, calculate_age, +-+ calculate_child_price, calculate_discount, +-+ calculate_discounted_price, calculate_itog, +-+ calculate_ticket_type, calculated_ticket_price, +-+ convert_sale_dict_values, +-+ generating_parts_for_partial_returns, +-+ get_talent_based_on_time, update_adult_count, +-+ update_child_count) +- from modules.system import System +- from modules.worker import TransactionWorker +- +-@@ -67,6 +52,7 @@ def handle_uncaught_exception(exc_type, exc_value, exc_traceback): +- "Неперехваченное исключение" +- ) +- +-+ +- sys.excepthook = handle_uncaught_exception +- +- +-@@ -101,7 +87,7 @@ class AuthForm(QDialog): +- windows.info_window( +- "База данных недоступна", +- "Не удалось подключиться к серверу базы данных.\n Приложение будет закрыто.", +-- "" +-+ "", +- ) +- sys.exit(1) +- login: str = self.ui.lineEdit.text() +-@@ -134,8 +120,11 @@ class AuthForm(QDialog): +- auth.close() +- return True +- else: +-- windows.info_window("Пользователь не найден", "Проверьте правильность ввода логина и пароля.", +-- "Убедитесь, что используете правильный логин и пароль для входа в систему.") +-+ windows.info_window( +-+ "Пользователь не найден", +-+ "Проверьте правильность ввода логина и пароля.", +-+ "Убедитесь, что используете правильный логин и пароль для входа в систему.", +-+ ) +- return False +- +- def check_kkt_connection(self): +-@@ -168,15 +157,21 @@ class AuthForm(QDialog): +- break +- +- except (ConnectionError, TimeoutError, Exception) as e: +-- error_type = "сетевая" if isinstance(e, (ConnectionError, TimeoutError)) else "неизвестная" +-- logger.warning(f"Попытка {attempt} из 3: {error_type} ошибка при подключении к ККТ: {e}") +-+ error_type = ( +-+ "сетевая" +-+ if isinstance(e, (ConnectionError, TimeoutError)) +-+ else "неизвестная" +-+ ) +-+ logger.warning( +-+ f"Попытка {attempt} из 3: {error_type} ошибка при подключении к ККТ: {e}" +-+ ) +- +- if not connected: +- logger.error("Все 3 попытки подключения к ККТ не удались") +- windows.info_window( +- "Внимание", +- "Кассовый аппарат не подключен", +-- "Программа продолжит работу без фискальных операций." +-+ "Программа продолжит работу без фискальных операций.", +- ) +- except (ConnectionError, TimeoutError) as e: +- logger.error(f"Ошибка при проверке подключения к ККТ: {e}") +-@@ -225,7 +220,6 @@ class AuthForm(QDialog): +- window.exec_() +- +- +-- +- class ClientForm(QDialog): +- """Форма с данными клиента""" +- +-@@ -533,23 +527,33 @@ class SaleForm(QDialog): +- if current_row == system.sale_checkbox_row: +- # Удаляется исключенный взрослый +- logger.debug(f"Удаляется исключенный взрослый row={current_row}") +-- logger.debug(f"ДО сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") +-+ logger.debug( +-+ f"ДО сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" +-+ ) +- +- # Сбросить флаги +- system.sale_checkbox_row = None +- system.exclude_from_sale = 0 +- system.sale_dict["detail"][4] = 0 +- +-- logger.debug(f"ПОСЛЕ сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") +-+ logger.debug( +-+ f"ПОСЛЕ сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" +-+ ) +- elif current_row < system.sale_checkbox_row: +- # Удаляется строка выше исключенного +-- logger.debug(f"Удаляется строка row={current_row} ВЫШЕ исключенного row={system.sale_checkbox_row}") +-- logger.debug(f"ДО корректировки: sale_checkbox_row={system.sale_checkbox_row}") +-+ logger.debug( +-+ f"Удаляется строка row={current_row} ВЫШЕ исключенного row={system.sale_checkbox_row}" +-+ ) +-+ logger.debug( +-+ f"ДО корректировки: sale_checkbox_row={system.sale_checkbox_row}" +-+ ) +- +- # Уменьшить индекс +- system.sale_checkbox_row -= 1 +- +-- logger.debug(f"ПОСЛЕ корректировки: sale_checkbox_row={system.sale_checkbox_row}") +-+ logger.debug( +-+ f"ПОСЛЕ корректировки: sale_checkbox_row={system.sale_checkbox_row}" +-+ ) +- +- # Удалить строку +- self.ui.tableWidget_2.removeRow(current_row) +-@@ -897,7 +901,7 @@ class SaleForm(QDialog): +- self.ui.tableWidget_3.setColumnWidth(3, 7) +- # Если продажа новая - обновляем статус +- system.sale_status = 0 +-- logger.warning(f"Статус продажи - новая") +-+ logger.warning("Статус продажи - новая") +- row_number: int = self.ui.tableWidget.currentRow() +- res: str = self.ui.tableWidget.item(row_number, 5).text() +- with Session(system.engine) as session: +-@@ -1141,12 +1145,16 @@ class SaleForm(QDialog): +- +- # Рассчитываем цену со скидкой +- if adult_with_discount_count > 0: +-- price_adult_with_discount = int(system.sale_dict["price_adult"] * (1 - system.sale_discount / 100)) +-+ price_adult_with_discount = int( +-+ system.sale_dict["price_adult"] * (1 - system.sale_discount / 100) +-+ ) +- else: +- price_adult_with_discount = 0 +- +- if child_with_discount_count > 0: +-- price_child_with_discount = int(system.sale_dict["price_child"] * (1 - system.sale_discount / 100)) +-+ price_child_with_discount = int( +-+ system.sale_dict["price_child"] * (1 - system.sale_discount / 100) +-+ ) +- else: +- price_child_with_discount = 0 +- +-@@ -1166,8 +1174,12 @@ class SaleForm(QDialog): +- ) +- +- logger.debug(f"detail ИТОГО: {system.sale_dict['detail']}") +-- logger.debug(f"Взрослых со скидкой: {adult_with_discount_count}, цена 1 билета: {price_adult_with_discount}") +-- logger.debug(f"Детей со скидкой: {child_with_discount_count}, цена 1 билета: {price_child_with_discount}") +-+ logger.debug( +-+ f"Взрослых со скидкой: {adult_with_discount_count}, цена 1 билета: {price_adult_with_discount}" +-+ ) +-+ logger.debug( +-+ f"Детей со скидкой: {child_with_discount_count}, цена 1 билета: {price_child_with_discount}" +-+ ) +- +- # Считаем без исключённых взрослых +- adult_count_in_sale = 0 +-@@ -1186,7 +1198,9 @@ class SaleForm(QDialog): +- +- # Синхронизируем +- system.sale_dict["kol_adult"] = adult_count_in_sale +-- logger.debug(f"Синхронизация: kol_adult={adult_count_in_sale}, kol_child={system.sale_dict['kol_child']}") +-+ logger.debug( +-+ f"Синхронизация: kol_adult={adult_count_in_sale}, kol_child={system.sale_dict['kol_child']}" +-+ ) +- +- # Считаем итог +- itog: int = calculate_itog() +-@@ -1225,7 +1239,7 @@ class SaleForm(QDialog): +- system.sale_tickets = tickets +- +- # Проверяем есть ли в продаже взрослый +-- if system.sale_dict['kol_adult'] >= 1 or system.exclude_from_sale == 1: +-+ if system.sale_dict["kol_adult"] >= 1 or system.exclude_from_sale == 1: +- self.ui.pushButton_5.setEnabled(True) +- else: +- self.ui.pushButton_5.setEnabled(False) +-@@ -1244,15 +1258,18 @@ class SaleForm(QDialog): +- # Если есть исключённый +- if row == system.sale_checkbox_row: +- checkbox.setEnabled(True) +-- logger.debug(f"Checkbox row={row} enabled (исключенный)") +-+ logger.debug( +-+ f"Checkbox row={row} enabled (исключенный)" +-+ ) +- else: +- checkbox.setEnabled(False) +- logger.debug(f"Checkbox row={row} disabled") +- else: +- # Нет исключённых - все доступны +- checkbox.setEnabled(True) +-- logger.debug(f"Checkbox row={row} enabled (нет исключенных)") +-- +-+ logger.debug( +-+ f"Checkbox row={row} enabled (нет исключенных)" +-+ ) +- +- def apply_discounts(self, row: int, price: int, type_ticket: str) -> None: +- """ +-@@ -1288,7 +1305,9 @@ class SaleForm(QDialog): +- if type_ticket == "взрослый" and system.exclude_from_sale == 1: +- checkbox = self.ui.tableWidget_2.cellWidget(row, 8).findChild(QCheckBox) +- if checkbox.isChecked() and row == system.sale_checkbox_row: +-- logger.info(f"Взрослый в строке {row} исключен из продажи, пропускаем применение дополнительных скидок") +-+ logger.info( +-+ f"Взрослый в строке {row} исключен из продажи, пропускаем применение дополнительных скидок" +-+ ) +- return # НЕ применяем скидки из checkBox_2! +- +- # Иначе проверяем активен ли checkbox со скидкой и размер > 0 +-@@ -1299,9 +1318,11 @@ class SaleForm(QDialog): +- # Используем другое имя переменной для цены по акции +- promo_price = calculate_discount(price, system.sale_discount) +- # Если checkbox в акт - применяем к этой строке скидку +-- if (self.ui.tableWidget_2.cellWidget(row, 7) +-- .findChild(QCheckBox) +-- .isChecked()): # Если False → применить скидку +-+ if ( +-+ self.ui.tableWidget_2.cellWidget(row, 7) +-+ .findChild(QCheckBox) +-+ .isChecked() +-+ ): # Если False → применить скидку +- # Применяем скидку ТОЛЬКО если checkbox НЕ активен +- if type_ticket == "взрослый": +- system.count_number_of_visitors["kol_sale_adult"] += 1 +-@@ -1481,7 +1502,7 @@ class SaleForm(QDialog): +- self.ui.checkBox_2.setChecked(True) +- self.ui.comboBox_2.setCurrentIndex(10) +- # Добавляем информацию о скидке +-- system.sale_dict['detail'][4] = 50 +-+ system.sale_dict["detail"][4] = 50 +- # Разрешаем выбор времени посещения +- self.ui.comboBox.setEnabled(True) +- +-@@ -1544,7 +1565,9 @@ class SaleForm(QDialog): +- None +- """ +- logger.info("Запуск функции adult_exclusion") +-- logger.debug(f"Входные параметры: row={row}, sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") +-+ logger.debug( +-+ f"Входные параметры: row={row}, sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" +-+ ) +- +- # Получаем checkbox для проверки +- checkbox = self.ui.tableWidget_2.cellWidget(row, 8).findChild(QCheckBox) +-@@ -1577,14 +1600,16 @@ class SaleForm(QDialog): +- +- # Проверка - единственный взрослый? +- if adult_count == 1 and child_count == 0: +-- logger.warning("Невозможно исключить единственного взрослого в продаже!") +-+ logger.warning( +-+ "Невозможно исключить единственного взрослого в продаже!" +-+ ) +- +- QMessageBox.warning( +- self, +- "Невозможно исключить взрослого", +- "В продаже только один взрослый!\n" +- "Нельзя исключить единственного взрослого из продажи.\n" +-- "Добавьте ребенка или других взрослых." +-+ "Добавьте ребенка или других взрослых.", +- ) +- +- checkbox.blockSignals(True) +-@@ -1596,24 +1621,30 @@ class SaleForm(QDialog): +- +- # Первое исключение взрослого +- logger.info("Исключаем взрослого из продажи") +-- system.sale_dict["detail"][4] = system.sale_discount if system.sale_discount else 0 +-- logger.debug(f"Установлен флаг исключения: detail[4] = 100") +-+ system.sale_dict["detail"][4] = ( +-+ system.sale_discount if system.sale_discount else 0 +-+ ) +-+ logger.debug("Установлен флаг исключения: detail[4] = 100") +- system.sale_checkbox_row = row +- system.exclude_from_sale = 1 +- self.ui.tableWidget_2.setItem(row, 4, QTableWidgetItem("н")) +-- logger.debug(f"Установлены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") +-+ logger.debug( +-+ f"Установлены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" +-+ ) +- +- elif system.sale_checkbox_row == row: +- logger.debug(f"Взрослый row={row} уже исключен, пропускаем") +- return +- +- else: +-- logger.warning(f"Попытка исключить второго взрослого row={row}, уже исключен row={system.sale_checkbox_row}") +-+ logger.warning( +-+ f"Попытка исключить второго взрослого row={row}, уже исключен row={system.sale_checkbox_row}" +-+ ) +- QMessageBox.warning( +- self, +- "Невозможно исключить взрослого", +- f"В продаже уже есть исключенный взрослый (строка {system.sale_checkbox_row})!\n" +-- "Можно исключить только одного взрослого." +-+ "Можно исключить только одного взрослого.", +- ) +- +- # СНЯТЬ checkbox +-@@ -1629,19 +1660,27 @@ class SaleForm(QDialog): +- if row == system.sale_checkbox_row: +- # Это действительно исключенный взрослый - возвращаем +- logger.info(f"Возвращаем взрослого row={row} в продажу") +-- logger.debug(f"ДО возврата: detail[0]={system.sale_dict['detail'][0]}, detail[1]={system.sale_dict['detail'][1]}, detail[4]={system.sale_dict['detail'][4]}") +-+ logger.debug( +-+ f"ДО возврата: detail[0]={system.sale_dict['detail'][0]}, detail[1]={system.sale_dict['detail'][1]}, detail[4]={system.sale_dict['detail'][4]}" +-+ ) +- self.ui.tableWidget_2.setItem(row, 4, QTableWidgetItem("-")) +- if self.ui.checkBox_2.isChecked(): +- system.sale_dict["detail"][4] = system.sale_discount +- else: +- system.sale_dict["detail"][4] = 0 +-- logger.debug(f"ПОСЛЕ возврата: detail[4]={system.sale_dict['detail'][4]}") +-+ logger.debug( +-+ f"ПОСЛЕ возврата: detail[4]={system.sale_dict['detail'][4]}" +-+ ) +- system.sale_checkbox_row = None +- system.exclude_from_sale = 0 +-- logger.debug(f"Сброшены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") +-+ logger.debug( +-+ f"Сброшены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" +-+ ) +- else: +- # Это другая строка, не исключенный взрослый +-- logger.debug(f"row={row} != sale_checkbox_row={system.sale_checkbox_row}, пропускаем") +-+ logger.debug( +-+ f"row={row} != sale_checkbox_row={system.sale_checkbox_row}, пропускаем" +-+ ) +- +- def generate_ticket_list( +- self, +-@@ -1698,7 +1737,7 @@ class SaleForm(QDialog): +- pc_name=system.pc_name, +- datetime=dt.datetime.now(), +- ) +-- logger.debug(f"Объект Sale создан") +-+ logger.debug("Объект Sale создан") +- +- with Session(system.engine) as session: +- session.add(add_sale) +-@@ -1731,7 +1770,9 @@ class SaleForm(QDialog): +- session.add(add_ticket) +- +- session.commit() +-- logger.info(f"Продажа {system.sale_id} и все билеты успешно сохранены") +-+ logger.info( +-+ f"Продажа {system.sale_id} и все билеты успешно сохранены" +-+ ) +- +- self.close() +- else: +-@@ -1744,9 +1785,7 @@ class SaleForm(QDialog): +- logger.warning(f"Ошибка сохранения продажи: {e}") +- system.sale_id = None +- windows.info_window( +-- "Ошибка базы данных", +-- "Не удалось сохранить продажу", +-- f"Ошибка: {e}" +-+ "Ошибка базы данных", "Не удалось сохранить продажу", f"Ошибка: {e}" +- ) +- +- @logger.catch() +-@@ -1775,17 +1814,19 @@ class SaleForm(QDialog): +- system, +- pq, +- Session, +-- self # передаем self, чтобы вызвать print_saved_tickets +-+ self, # передаем self, чтобы вызвать print_saved_tickets +- ) +- +- # Подключение сигналов +- self.worker.progress_updated.connect(self.progress_window.update_status) +- self.worker.finished.connect(self.on_transaction_finished) +-- self.worker.save_sale_signal.connect(self.save_sale, Qt.BlockingQueuedConnection) +-+ self.worker.save_sale_signal.connect( +-+ self.save_sale, Qt.BlockingQueuedConnection +-+ ) +- self.worker.error_signal.connect(self.handle_error) # Обработка ошибок +- self.worker.info_signal.connect(self.handle_info) +- self.worker.print_ticket_signal.connect(self.print_saved_tickets) +-- #self.worker.close_window_signal.connect(self.close) +-+ # self.worker.close_window_signal.connect(self.close) +- self.worker.close_window_signal.connect(self.progress_window.close) +- +- # Поток +-@@ -1799,7 +1840,6 @@ class SaleForm(QDialog): +- +- self.thread.start() +- +-- +- def handle_error(self, title: str, text: str, details: str): +- """ +- Обрабатывает ошибку, закрывая окно прогресса и отображая сообщение с ошибкой. +-@@ -1813,7 +1853,7 @@ class SaleForm(QDialog): +- None: Функция ничего не возвращает, но закрывает окно прогресса и отображает сообщение об ошибке. +- """ +- # Закрытие окна прогресса перед отображением ошибки +-- if hasattr(self, 'progress_window') and self.progress_window: +-+ if hasattr(self, "progress_window") and self.progress_window: +- self.progress_window.close() +- +- # Создание окна с ошибкой +-@@ -1848,19 +1888,18 @@ class SaleForm(QDialog): +- None: Эта функция ничего не возвращает, но закрывает окно прогресса и логирует завершение транзакции. +- """ +- logger.info("Запуск функции on_transaction_finished") +-- if hasattr(self, '_transaction_finished') and self._transaction_finished: +-+ if hasattr(self, "_transaction_finished") and self._transaction_finished: +- return +- self._transaction_finished = True +- logger.info("Завершение транзакции") +- # Закрываем окно прогресса +-- if hasattr(self, 'progress_window') and self.progress_window: +-+ if hasattr(self, "progress_window") and self.progress_window: +- self.progress_window.close() +- self.progress_window = None +- +- if self.main_window: +- self.main_window.main_button_all_sales() +- +-- +- @logger.catch() +- def sale_return(self): +- """ +-@@ -1893,7 +1932,9 @@ class SaleForm(QDialog): +- if sale.status == 5: +- logger.debug("Требуется частичный возврат.") +- partial_return: int = int(sale.partial_return) +-- new_tickets: dict | dict = generating_parts_for_partial_returns(tickets, partial_return) +-+ new_tickets: dict | dict = generating_parts_for_partial_returns( +-+ tickets, partial_return +-+ ) +- logger.debug(f"Список билетов: {new_tickets}") +- # 1 - карта, 2 - наличные +- if sale.payment_type == 1: +-@@ -1917,7 +1958,7 @@ class SaleForm(QDialog): +- logger.debug("Продажа оплачена. Запускаем возврат") +- if payment_type == 102: +- state_check = pq.check_open( +-- tickets, payment_type, system.user, 2, 1,None +-+ tickets, payment_type, system.user, 2, 1, None +- ) +- elif payment_type == 101: +- logger.debug( +-@@ -1932,7 +1973,9 @@ class SaleForm(QDialog): +- amount=price, +- progress_signal=None, +- operation_type=2, # возврат +-- error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) +-+ error_callback=lambda title, msg, code: windows.info_window( +-+ title, msg, str(code) +-+ ), +- ) +- elif sale.status == 5: +- bank, payment = pq.universal_terminal_operation( +-@@ -1940,7 +1983,9 @@ class SaleForm(QDialog): +- amount=partial_return, +- progress_signal=None, +- operation_type=2, # возврат +-- error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) +-+ error_callback=lambda title, msg, code: windows.info_window( +-+ title, msg, str(code) +-+ ), +- ) +- if bank == 1: +- check = pq.read_pinpad_file(remove_newline=False) +-@@ -2026,7 +2071,9 @@ class SaleForm(QDialog): +- amount=price, +- progress_signal=None, +- operation_type=2, # возврат +-- error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) +-+ error_callback=lambda title, msg, code: windows.info_window( +-+ title, msg, str(code) +-+ ), +- ) +- +- if bank == 1: +-@@ -2102,7 +2149,9 @@ class SaleForm(QDialog): +- amount=price, +- progress_signal=None, +- operation_type=3, # отмена +-- error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) +-+ error_callback=lambda title, msg, code: windows.info_window( +-+ title, msg, str(code) +-+ ), +- ) +- if bank == 1: +- check = pq.read_pinpad_file(remove_newline=False) +-@@ -2287,7 +2336,7 @@ class SaleForm(QDialog): +- Во всех остальных случаях возвращает False. +- """ +- # Проверяем наличие атрибута и что он не None +-- if not hasattr(system, 'ticket_print') or system.ticket_print is None: +-+ if not hasattr(system, "ticket_print") or system.ticket_print is None: +- return False +- +- # Приводим к строке и сравниваем с 'on' +-@@ -2300,7 +2349,9 @@ class SaleForm(QDialog): +- """ +- logger.info("Запуск функции print_saved_tickets") +- if not self.is_printing_enabled(): +-- logger.info("Печать билетов отключена (параметр 'ticket' не равен 'on' или отсутствует)") +-+ logger.info( +-+ "Печать билетов отключена (параметр 'ticket' не равен 'on' или отсутствует)" +-+ ) +- return +- try: +- self.sale_generate_saved_tickets() +-@@ -2361,9 +2412,7 @@ class SaleForm(QDialog): +- # Оплата только для новых продаж +- if system.sale_status != 0: +- windows.info_window( +-- "Внимание", +-- "Оплата картой доступна только для новых продаж.", +-- "" +-+ "Внимание", "Оплата картой доступна только для новых продаж.", "" +- ) +- return +- +-@@ -2371,8 +2420,7 @@ class SaleForm(QDialog): +- logger.info("РЕЖИМ ОТЛАДКИ: Имитация оплаты картой") +- # Создаем тестовый слип-чек +- slip = "DEBUG_SLIP\nДата: {}\nСумма: {}\nОдобрено".format( +-- dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), +-- txt +-+ dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), txt +- ) +- # Сохраняем тестовые данные +- self.save_debug_payment(Payment.Card, slip) +-@@ -2388,9 +2436,7 @@ class SaleForm(QDialog): +- # Оплата только для новых продаж +- if system.sale_status != 0: +- windows.info_window( +-- "Внимание", +-- "Оплата наличными доступна только для новых продаж.", +-- "" +-+ "Внимание", "Оплата наличными доступна только для новых продаж.", "" +- ) +- return +- logger.info("Оплата наличными") +-@@ -2402,14 +2448,13 @@ class SaleForm(QDialog): +- windows.info_window( +- "Внимание", +- "Оплата картой доступна только для новых или не фискализированных продаж.", +-- "" +-+ "", +- ) +- return +- if dev_mode: +- logger.info("РЕЖИМ ОТЛАДКИ: Имитация offline оплаты") +- slip = "DEBUG_SLIP_OFFLINE\nДата: {}\nСумма: {}\nОффлайн".format( +-- dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), +-- txt +-+ dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), txt +- ) +- self.save_debug_payment(Payment.Offline, slip) +- self.accept() +-@@ -2417,12 +2462,12 @@ class SaleForm(QDialog): +- +- user_choice = windows.info_dialog_window( +- "Подтверждение", +-- f"Вы точно хотите фискализировать операцию?\n\n" +-- f"Это надо делать ТОЛЬКО после успешной проверки проведения операции по банковскому терминалу!\n\n" +-- f"Для этого выполните: Касса -> Операции с банковским терминалом -> Печать ранее подготовленного документа.\n\n" +-- f"Операция считается успешной, если в банковском слип-чеке:\n" +-- f" - сумма, дата и время проведения операции операции совпадают с данными из заказа;\n" +-- f" - указано слово 'ОДОБРЕНО'.", +-+ "Вы точно хотите фискализировать операцию?\n\n" +-+ "Это надо делать ТОЛЬКО после успешной проверки проведения операции по банковскому терминалу!\n\n" +-+ "Для этого выполните: Касса -> Операции с банковским терминалом -> Печать ранее подготовленного документа.\n\n" +-+ "Операция считается успешной, если в банковском слип-чеке:\n" +-+ " - сумма, дата и время проведения операции операции совпадают с данными из заказа;\n" +-+ " - указано слово 'ОДОБРЕНО'.", +- ) +- if user_choice == 1: +- logger.info("Оплата банковской картой offline") +-@@ -2435,7 +2480,9 @@ class SaleForm(QDialog): +- def save_debug_payment(payment_type, slip_text): +- """Сохранение тестового платежа в режиме отладки""" +- try: +-- logger.debug(f"Сохранение тестового платежа: payment_type = {payment_type}, slip = {slip_text}") +-+ logger.debug( +-+ f"Сохранение тестового платежа: payment_type = {payment_type}, slip = {slip_text}" +-+ ) +- # ... код для сохранения в БД ... +- except Exception as e: +- logger.error(f"Ошибка сохранения тестового платежа: {str(e)}") +-@@ -2515,7 +2562,9 @@ class SlipForm(QDialog): +- None: Функция не возвращает значений, отображает данные слип-чека в форме. +- """ +- logger.info("Запуск функции get_slip") +-- card_tail, merchant_id, rrn_value, load_slip = system.get_slip_data(system.sale_id) +-+ card_tail, merchant_id, rrn_value, load_slip = system.get_slip_data( +-+ system.sale_id +-+ ) +- +- slip: SlipForm = SlipForm() +- slip.ui.label_5.setText(card_tail) +-@@ -2834,7 +2883,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): +- client.show() +- # Сохраняем параметры данных об уже существующем клиенте +- system.client_update = 1 +-- logger.info(f"Обновляем информацию о клиенте") +-+ logger.info("Обновляем информацию о клиенте") +- client.exec_() +- +- def main_filter_clear(self) -> None: +-@@ -3159,16 +3208,30 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): +- else: +- # Определение периода для 1, 3 и 7 дней без учета времени +- if self.ui.radioButton.isChecked(): +-- filter_start = dt.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) +-- filter_end = dt.datetime.today().replace(hour=23, minute=59, second=59, microsecond=999999) +-+ filter_start = dt.datetime.today().replace( +-+ hour=0, minute=0, second=0, microsecond=0 +-+ ) +-+ filter_end = dt.datetime.today().replace( +-+ hour=23, minute=59, second=59, microsecond=999999 +-+ ) +- elif self.ui.radioButton_2.isChecked(): +-- filter_start = (dt.datetime.today() - timedelta(days=3)).replace(hour=0, minute=0, second=0, microsecond=0) +-- filter_end = dt.datetime.today().replace(hour=23, minute=59, second=59, microsecond=999999) +-+ filter_start = (dt.datetime.today() - timedelta(days=3)).replace( +-+ hour=0, minute=0, second=0, microsecond=0 +-+ ) +-+ filter_end = dt.datetime.today().replace( +-+ hour=23, minute=59, second=59, microsecond=999999 +-+ ) +- elif self.ui.radioButton_3.isChecked(): +-- filter_start = (dt.datetime.today() - timedelta(days=7)).replace(hour=0, minute=0, second=0, microsecond=0) +-- filter_end = dt.datetime.today().replace(hour=23, minute=59, second=59, microsecond=999999) +-+ filter_start = (dt.datetime.today() - timedelta(days=7)).replace( +-+ hour=0, minute=0, second=0, microsecond=0 +-+ ) +-+ filter_end = dt.datetime.today().replace( +-+ hour=23, minute=59, second=59, microsecond=999999 +-+ ) +- # Определяем, какие статусы фильтровать +-- sale_status_filter = [2, 3, 4, 5, 6, 8, 9] if self.ui.checkBox.isChecked() else [] +-+ sale_status_filter = ( +-+ [2, 3, 4, 5, 6, 8, 9] if self.ui.checkBox.isChecked() else [] +-+ ) +- +- # Генерация запроса +- with Session(system.engine) as session: +-@@ -3314,7 +3377,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): +- system.sale_dict.update(sale_initial_values) +- # сбрасываем id и статус продажи +- system.sale_id = None +-- system.sale_status = 0 # Т.е. новая продажа +-+ system.sale_status = 0 # Т.е. новая продажа +- system.sale_tickets = None +- sale.exec_() +- +-@@ -3389,30 +3452,36 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): +- logger.info("Запуск функции fetch_stat_data") +- with Session(system.engine) as session: +- sales = session.execute( +-- select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status) +-- .where(and_(Sale.status == "1", Sale.datetime.between(dt1, dt2))) +-+ select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status).where( +-+ and_(Sale.status == "1", Sale.datetime.between(dt1, dt2)) +-+ ) +- ).all() +- sales_return = session.execute( +-- select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status) +-- .where(and_( +-- Sale.datetime_return.between(dt1, dt2), +-- Sale.status.in_([2, 3, 4, 5, 6]) +-- )) +-+ select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status).where( +-+ and_( +-+ Sale.datetime_return.between(dt1, dt2), +-+ Sale.status.in_([2, 3, 4, 5, 6]), +-+ ) +-+ ) +- ).all() +- tickets = session.execute( +- select( +-- Ticket.ticket_type, Ticket.arrival_time, Ticket.description, Sale.status, Sale.id, Ticket.price +-+ Ticket.ticket_type, +-+ Ticket.arrival_time, +-+ Ticket.description, +-+ Sale.status, +-+ Sale.id, +-+ Ticket.price, +- ).where( +- and_( +- Sale.id == Ticket.id_sale, +- Sale.status == "1", +- Ticket.datetime.between(dt1, dt2), +-- ) +-+ ) +- ) +- ).all() +- return sales, sales_return, tickets +- +-- +- def fill_sales_table(self, data): +- """ +- Заполнение таблицы продаж данными. +-@@ -3431,9 +3500,9 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): +- for row, row_data in enumerate(data): +- for col, value in enumerate(row_data): +- if value is not None: +-- self.ui.tableWidget_4.setItem(row, col, QTableWidgetItem(str(value))) +-- +-- +-+ self.ui.tableWidget_4.setItem( +-+ row, col, QTableWidgetItem(str(value)) +-+ ) +- +- def fill_ticket_table(self, data): +- """ +-@@ -3506,7 +3575,7 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): +- if os.path.exists(path): +- os.remove(path) +- # Проверяем, есть ли данные о продажах в system.sales_data_summary +-- if not hasattr(system, 'sales_data_summary') or not system.sales_data_summary: +-+ if not hasattr(system, "sales_data_summary") or not system.sales_data_summary: +- logger.error("Нет данных о продажах для отчета кассира") +- return +- # Получаем имя текущего компьютера +-@@ -3529,14 +3598,16 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): +- str(total_sales_card), +- str(total_sales_cash), +- str(total_returns_card), +-- str(total_returns_cash) +-+ str(total_returns_cash), +- ] +- +- # Получаем даты для отчета +- dt1, dt2 = self.get_date_range_from_ui() +- +- # Получаем информацию о кассире +-- kassir = system.user # Предполагаем, что данные о кассире хранятся в system.user +-+ kassir = ( +-+ system.user +-+ ) # Предполагаем, что данные о кассире хранятся в system.user +- +- # Вызываем функцию для формирования отчета +- otchet.otchet_kassira(values, dt1, dt2, kassir) +-diff --git a/modules/config.py b/modules/config.py +-index 3cb0a17..722acc9 100644 +---- a/modules/config.py +-+++ b/modules/config.py +-@@ -1,11 +1,7 @@ +- import logging +- import os +--from configparser import ( +-- ConfigParser, +-- NoSectionError, +-- NoOptionError, +-- MissingSectionHeaderError, +--) +-+from configparser import (ConfigParser, MissingSectionHeaderError, +-+ NoOptionError, NoSectionError) +- from typing import List, Optional +- +- logger = logging.getLogger(__name__) +-@@ -122,7 +118,6 @@ class Config: +- ) +- config_data[key] = config.get(section, key) +- +-- +- return config_data +- except (NoSectionError, NoOptionError) as e: +- logger.error(f"Ошибка в конфигурационном файле: {e}") +-diff --git a/modules/libfptr10.py b/modules/libfptr10.py +-index ced442d..7f92556 100644 +---- a/modules/libfptr10.py +-+++ b/modules/libfptr10.py +-@@ -1,21 +1,21 @@ +-+# ruff: noqa: F403, F405 +- # -*- coding: utf-8 -*- +- # AUTO GENERATED FILE +- +- import ctypes +--import sys +--import json +- import datetime +-+import json +- import os +- import platform +--import warnings +-+import sys +- +- if sys.version_info[0] == 3: +-- if platform.system() == 'Windows': +-+ if platform.system() == "Windows": +- from winreg import * +- TEXT = str +- RANGE = range +- else: +-- if platform.system() == 'Windows': +-+ if platform.system() == "Windows": +- from _winreg import * +- TEXT = basestring +- RANGE = xrange +-@@ -1672,231 +1672,294 @@ class IFptr(object): +- DEFAULT_BUFF_SIZE = 512 +- +- CREATE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(ctypes.c_void_p)) +-- CREATE_WITH_ID_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.POINTER(ctypes.c_void_p), +-- ctypes.c_wchar_p) +-+ CREATE_WITH_ID_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.POINTER(ctypes.c_void_p), ctypes.c_wchar_p +-+ ) +- DESTROY_METHOD = ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_void_p)) +- GET_VERSION_METHOD = ctypes.CFUNCTYPE(ctypes.c_char_p) +- +-- METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p) +-- +-- IS_OPENED_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p) +-- +-- GET_ERROR_CODE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p) +-- GET_ERROR_DESCRIPTION_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p, +-- ctypes.c_int) +-- GET_ERROR_RECOMMENDATION_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p, +-- ctypes.c_int) +-- RESET_ERROR_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p) +-- +-- SET_SETTINGS_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p) +-- GET_SETTINGS_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p, +-- ctypes.c_int) +-- +-- SET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p, +-- ctypes.c_wchar_p) +-- GET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p, +-- ctypes.c_wchar_p, +-- ctypes.c_int) +-- +-- SET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.POINTER(ctypes.c_ubyte), ctypes.c_int) +-- GET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.POINTER(ctypes.c_ubyte), ctypes.c_int) +-- +-- SET_INT_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.c_uint) +-- GET_INT_METHOD = ctypes.CFUNCTYPE(ctypes.c_uint, +-- ctypes.c_void_p, +-- ctypes.c_int) +-- +-- SET_BOOL_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.c_int) +-- GET_BOOL_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_int) +-- +-- SET_DOUBLE_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.c_double) +-- GET_DOUBLE_METHOD = ctypes.CFUNCTYPE(ctypes.c_double, +-- ctypes.c_void_p, +-- ctypes.c_int) +-- +-- SET_STRING_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.c_wchar_p) +-- GET_STRING_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.c_wchar_p, ctypes.c_int) +-- +-- SET_DATETIME_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, +-- ctypes.c_int, +-- ctypes.c_int) +-- GET_DATETIME_METHOD = ctypes.CFUNCTYPE(None, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.POINTER(ctypes.c_int), +-- ctypes.POINTER(ctypes.c_int), +-- ctypes.POINTER(ctypes.c_int), +-- ctypes.POINTER(ctypes.c_int), +-- ctypes.POINTER(ctypes.c_int), +-- ctypes.POINTER(ctypes.c_int)) +-- IS_PARAM_AVAILABLE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_int) +-- +-- LOG_WRITE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p, +-- ctypes.c_int, +-- ctypes.c_wchar_p) +-- +-- SHOW_PROPERTIES_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_int, +-- ctypes.c_void_p) +-- +-- CHANGE_LABEL_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, +-- ctypes.c_void_p, +-- ctypes.c_wchar_p) +-+ METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p) +-+ +-+ IS_OPENED_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p) +-+ +-+ GET_ERROR_CODE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p) +-+ GET_ERROR_DESCRIPTION_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int +-+ ) +-+ GET_ERROR_RECOMMENDATION_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int +-+ ) +-+ RESET_ERROR_METHOD = ctypes.CFUNCTYPE(None, ctypes.c_void_p) +-+ +-+ SET_SETTINGS_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p +-+ ) +-+ GET_SETTINGS_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int +-+ ) +-+ +-+ SET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE( +-+ None, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_wchar_p +-+ ) +-+ GET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int +-+ ) +-+ +-+ SET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE( +-+ None, +-+ ctypes.c_void_p, +-+ ctypes.c_int, +-+ ctypes.POINTER(ctypes.c_ubyte), +-+ ctypes.c_int, +-+ ) +-+ GET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, +-+ ctypes.c_void_p, +-+ ctypes.c_int, +-+ ctypes.POINTER(ctypes.c_ubyte), +-+ ctypes.c_int, +-+ ) +-+ +-+ SET_INT_METHOD = ctypes.CFUNCTYPE( +-+ None, ctypes.c_void_p, ctypes.c_int, ctypes.c_uint +-+ ) +-+ GET_INT_METHOD = ctypes.CFUNCTYPE(ctypes.c_uint, ctypes.c_void_p, ctypes.c_int) +-+ +-+ SET_BOOL_METHOD = ctypes.CFUNCTYPE( +-+ None, ctypes.c_void_p, ctypes.c_int, ctypes.c_int +-+ ) +-+ GET_BOOL_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_int) +-+ +-+ SET_DOUBLE_METHOD = ctypes.CFUNCTYPE( +-+ None, ctypes.c_void_p, ctypes.c_int, ctypes.c_double +-+ ) +-+ GET_DOUBLE_METHOD = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_void_p, ctypes.c_int) +-+ +-+ SET_STRING_METHOD = ctypes.CFUNCTYPE( +-+ None, ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p +-+ ) +-+ GET_STRING_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p, ctypes.c_int +-+ ) +-+ +-+ SET_DATETIME_METHOD = ctypes.CFUNCTYPE( +-+ None, +-+ ctypes.c_void_p, +-+ ctypes.c_int, +-+ ctypes.c_int, +-+ ctypes.c_int, +-+ ctypes.c_int, +-+ ctypes.c_int, +-+ ctypes.c_int, +-+ ctypes.c_int, +-+ ) +-+ GET_DATETIME_METHOD = ctypes.CFUNCTYPE( +-+ None, +-+ ctypes.c_void_p, +-+ ctypes.c_int, +-+ ctypes.POINTER(ctypes.c_int), +-+ ctypes.POINTER(ctypes.c_int), +-+ ctypes.POINTER(ctypes.c_int), +-+ ctypes.POINTER(ctypes.c_int), +-+ ctypes.POINTER(ctypes.c_int), +-+ ctypes.POINTER(ctypes.c_int), +-+ ) +-+ IS_PARAM_AVAILABLE_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_int +-+ ) +-+ +-+ LOG_WRITE_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int, ctypes.c_wchar_p +-+ ) +-+ +-+ SHOW_PROPERTIES_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p +-+ ) +-+ +-+ CHANGE_LABEL_METHOD = ctypes.CFUNCTYPE( +-+ ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p +-+ ) +- +- def __init__(self, lib_path="", fptr_id=""): +- assert sys.version_info >= (2, 6) +- self.lib_path = lib_path +- +- try: +-- if platform.system() == 'Windows': +-+ if platform.system() == "Windows": +- if len(self.lib_path) == 0: +-- rk = OpenKey(HKEY_LOCAL_MACHINE, "Software\\ATOL\\Drivers\\10.0\\KKT") +-+ rk = OpenKey( +-+ HKEY_LOCAL_MACHINE, "Software\\ATOL\\Drivers\\10.0\\KKT" +-+ ) +- rv = QueryValueEx(rk, "INSTALL_DIR")[0] +-- self.lib_path = os.path.join(rv, 'bin', 'fptr10.dll') +-+ self.lib_path = os.path.join(rv, "bin", "fptr10.dll") +- else: +-- if not self.lib_path.endswith('fptr10.dll'): +-- self.lib_path = os.path.join(self.lib_path, 'fptr10.dll') +-+ if not self.lib_path.endswith("fptr10.dll"): +-+ self.lib_path = os.path.join(self.lib_path, "fptr10.dll") +- try: +- self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) +- except OSError: +-- ctypes.CDLL(os.path.join(os.path.dirname(self.lib_path), 'msvcp140.dll'), mode=ctypes.RTLD_LOCAL) +-+ ctypes.CDLL( +-+ os.path.join(os.path.dirname(self.lib_path), "msvcp140.dll"), +-+ mode=ctypes.RTLD_LOCAL, +-+ ) +- self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) +- +-- elif platform.system() == 'Darwin': +-- if not self.lib_path.endswith('fptr10.framework/fptr10'): +-- self.lib_path = os.path.join(self.lib_path, 'fptr10.framework/fptr10') +-+ elif platform.system() == "Darwin": +-+ if not self.lib_path.endswith("fptr10.framework/fptr10"): +-+ self.lib_path = os.path.join( +-+ self.lib_path, "fptr10.framework/fptr10" +-+ ) +- try: +- self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) +- except OSError: +- self.lib_path = lib_path +-- if not self.lib_path.endswith('libfptr10.dylib'): +-- self.lib_path = os.path.join(self.lib_path, 'libfptr10.dylib') +-+ if not self.lib_path.endswith("libfptr10.dylib"): +-+ self.lib_path = os.path.join(self.lib_path, "libfptr10.dylib") +- self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) +- +- else: +-- if not self.lib_path.endswith('libfptr10.so'): +-- self.lib_path = os.path.join(self.lib_path, 'libfptr10.so') +-+ if not self.lib_path.endswith("libfptr10.so"): +-+ self.lib_path = os.path.join(self.lib_path, "libfptr10.so") +- self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) +- except OSError: +- raise Exception( +-- 'Driver library not found in {}'.format( +-- '\"' + +-- os.path.dirname( +-- self.lib_path) + +-- '\"' if len(lib_path) != 0 else 'search folders')) +-+ "Driver library not found in {}".format( +-+ '"' + os.path.dirname(self.lib_path) + '"' +-+ if len(lib_path) != 0 +-+ else "search folders" +-+ ) +-+ ) +- +- self.interface = ctypes.c_void_p(0) +- if fptr_id: +-- _create = self.CREATE_WITH_ID_METHOD(('libfptr_create_with_id', self.library)) +-- create_r = _create(ctypes.pointer(self.interface), ctypes.c_wchar_p(fptr_id)) +-+ _create = self.CREATE_WITH_ID_METHOD( +-+ ("libfptr_create_with_id", self.library) +-+ ) +-+ create_r = _create( +-+ ctypes.pointer(self.interface), ctypes.c_wchar_p(fptr_id) +-+ ) +- else: +-- _create = self.CREATE_METHOD(('libfptr_create', self.library)) +-+ _create = self.CREATE_METHOD(("libfptr_create", self.library)) +- create_r = _create(ctypes.pointer(self.interface)) +- if create_r == -2: +-- raise AttributeError('Invalid [id] format') +-+ raise AttributeError("Invalid [id] format") +- elif create_r != 0: +-- raise Exception('Can`t create driver handle') +-- +-- self._setByteArray = self.SET_BYTEARRAY_METHOD(('libfptr_set_param_bytearray', self.library)) +-- self._setUserByteArray = self.SET_BYTEARRAY_METHOD(('libfptr_set_user_param_bytearray', self.library)) +-+ raise Exception("Can`t create driver handle") +-+ +-+ self._setByteArray = self.SET_BYTEARRAY_METHOD( +-+ ("libfptr_set_param_bytearray", self.library) +-+ ) +-+ self._setUserByteArray = self.SET_BYTEARRAY_METHOD( +-+ ("libfptr_set_user_param_bytearray", self.library) +-+ ) +- self._setNonPrintableByteArray = self.SET_BYTEARRAY_METHOD( +-- ('libfptr_set_non_printable_param_bytearray', self.library)) +-- self._getByteArray = self.GET_BYTEARRAY_METHOD(('libfptr_get_param_bytearray', self.library)) +-- +-- self._setInt = self.SET_INT_METHOD(('libfptr_set_param_int', self.library)) +-- self._setUserInt = self.SET_INT_METHOD(('libfptr_set_user_param_int', self.library)) +-- self._setNonPrintableInt = self.SET_INT_METHOD(('libfptr_set_non_printable_param_int', self.library)) +-- self._getInt = self.GET_INT_METHOD(('libfptr_get_param_int', self.library)) +-- +-- self._setBool = self.SET_BOOL_METHOD(('libfptr_set_param_bool', self.library)) +-- self._setUserBool = self.SET_BOOL_METHOD(('libfptr_set_user_param_bool', self.library)) +-- self._setNonPrintableBool = self.SET_BOOL_METHOD(('libfptr_set_non_printable_param_bool', self.library)) +-- self._getBool = self.GET_BOOL_METHOD(('libfptr_get_param_bool', self.library)) +-- +-- self._setDouble = self.SET_DOUBLE_METHOD(('libfptr_set_param_double', self.library)) +-- self._setUserDouble = self.SET_DOUBLE_METHOD(('libfptr_set_user_param_double', self.library)) +-- self._setNonPrintableDouble = self.SET_DOUBLE_METHOD(('libfptr_set_non_printable_param_double', self.library)) +-- self._getDouble = self.GET_DOUBLE_METHOD(('libfptr_get_param_double', self.library)) +-- +-- self._setDateTime = self.SET_DATETIME_METHOD(('libfptr_set_param_datetime', self.library)) +-- self._setUserDateTime = self.SET_DATETIME_METHOD(('libfptr_set_user_param_datetime', self.library)) +-+ ("libfptr_set_non_printable_param_bytearray", self.library) +-+ ) +-+ self._getByteArray = self.GET_BYTEARRAY_METHOD( +-+ ("libfptr_get_param_bytearray", self.library) +-+ ) +-+ +-+ self._setInt = self.SET_INT_METHOD(("libfptr_set_param_int", self.library)) +-+ self._setUserInt = self.SET_INT_METHOD( +-+ ("libfptr_set_user_param_int", self.library) +-+ ) +-+ self._setNonPrintableInt = self.SET_INT_METHOD( +-+ ("libfptr_set_non_printable_param_int", self.library) +-+ ) +-+ self._getInt = self.GET_INT_METHOD(("libfptr_get_param_int", self.library)) +-+ +-+ self._setBool = self.SET_BOOL_METHOD(("libfptr_set_param_bool", self.library)) +-+ self._setUserBool = self.SET_BOOL_METHOD( +-+ ("libfptr_set_user_param_bool", self.library) +-+ ) +-+ self._setNonPrintableBool = self.SET_BOOL_METHOD( +-+ ("libfptr_set_non_printable_param_bool", self.library) +-+ ) +-+ self._getBool = self.GET_BOOL_METHOD(("libfptr_get_param_bool", self.library)) +-+ +-+ self._setDouble = self.SET_DOUBLE_METHOD( +-+ ("libfptr_set_param_double", self.library) +-+ ) +-+ self._setUserDouble = self.SET_DOUBLE_METHOD( +-+ ("libfptr_set_user_param_double", self.library) +-+ ) +-+ self._setNonPrintableDouble = self.SET_DOUBLE_METHOD( +-+ ("libfptr_set_non_printable_param_double", self.library) +-+ ) +-+ self._getDouble = self.GET_DOUBLE_METHOD( +-+ ("libfptr_get_param_double", self.library) +-+ ) +-+ +-+ self._setDateTime = self.SET_DATETIME_METHOD( +-+ ("libfptr_set_param_datetime", self.library) +-+ ) +-+ self._setUserDateTime = self.SET_DATETIME_METHOD( +-+ ("libfptr_set_user_param_datetime", self.library) +-+ ) +- self._setNonPrintableDateTime = self.SET_DATETIME_METHOD( +-- ('libfptr_set_non_printable_param_datetime', self.library)) +-- self._getDateTime = self.GET_DATETIME_METHOD(('libfptr_get_param_datetime', self.library)) +-- +-- self._setString = self.SET_STRING_METHOD(('libfptr_set_param_str', self.library)) +-- self._setUserString = self.SET_STRING_METHOD(('libfptr_set_user_param_str', self.library)) +-- self._setNonPrintableString = self.SET_STRING_METHOD(('libfptr_set_non_printable_param_str', self.library)) +-- self._getString = self.GET_STRING_METHOD(('libfptr_get_param_str', self.library)) +-- +-- self._isParamAvailable = self.IS_PARAM_AVAILABLE_METHOD(('libfptr_is_param_available', self.library)) +-- +-- self._setSettings = self.SET_SETTINGS_METHOD(('libfptr_set_settings', self.library)) +-- self._getSettings = self.GET_SETTINGS_METHOD(('libfptr_get_settings', self.library)) +-- +-- self._getSingleSetting = self.GET_SINGLE_SETTING_METHOD(('libfptr_get_single_setting', self.library)) +-- self._setSingleSetting = self.SET_SINGLE_SETTING_METHOD(('libfptr_set_single_setting', self.library)) +-- +-- self._getVersion = self.GET_VERSION_METHOD(('libfptr_get_version_string', self.library)) +-- self._isOpened = self.IS_OPENED_METHOD(('libfptr_is_opened', self.library)) +-- self._errorCode = self.GET_ERROR_CODE_METHOD(('libfptr_error_code', self.library)) +-- self._errorDescription = self.GET_ERROR_DESCRIPTION_METHOD(('libfptr_error_description', self.library)) +-- self._errorRecommendation = self.GET_ERROR_RECOMMENDATION_METHOD(('libfptr_error_recommendation', self.library)) +-- self._resetError = self.RESET_ERROR_METHOD(('libfptr_reset_error', self.library)) +-- self._logWrite = self.LOG_WRITE_METHOD(('libfptr_log_write_ex', self.library)) +-- self._showProperties = self.SHOW_PROPERTIES_METHOD(('libfptr_show_properties', self.library)) +-- self._changeLabel = self.CHANGE_LABEL_METHOD(('libfptr_change_label', self.library)) +-+ ("libfptr_set_non_printable_param_datetime", self.library) +-+ ) +-+ self._getDateTime = self.GET_DATETIME_METHOD( +-+ ("libfptr_get_param_datetime", self.library) +-+ ) +-+ +-+ self._setString = self.SET_STRING_METHOD( +-+ ("libfptr_set_param_str", self.library) +-+ ) +-+ self._setUserString = self.SET_STRING_METHOD( +-+ ("libfptr_set_user_param_str", self.library) +-+ ) +-+ self._setNonPrintableString = self.SET_STRING_METHOD( +-+ ("libfptr_set_non_printable_param_str", self.library) +-+ ) +-+ self._getString = self.GET_STRING_METHOD( +-+ ("libfptr_get_param_str", self.library) +-+ ) +-+ +-+ self._isParamAvailable = self.IS_PARAM_AVAILABLE_METHOD( +-+ ("libfptr_is_param_available", self.library) +-+ ) +-+ +-+ self._setSettings = self.SET_SETTINGS_METHOD( +-+ ("libfptr_set_settings", self.library) +-+ ) +-+ self._getSettings = self.GET_SETTINGS_METHOD( +-+ ("libfptr_get_settings", self.library) +-+ ) +-+ +-+ self._getSingleSetting = self.GET_SINGLE_SETTING_METHOD( +-+ ("libfptr_get_single_setting", self.library) +-+ ) +-+ self._setSingleSetting = self.SET_SINGLE_SETTING_METHOD( +-+ ("libfptr_set_single_setting", self.library) +-+ ) +-+ +-+ self._getVersion = self.GET_VERSION_METHOD( +-+ ("libfptr_get_version_string", self.library) +-+ ) +-+ self._isOpened = self.IS_OPENED_METHOD(("libfptr_is_opened", self.library)) +-+ self._errorCode = self.GET_ERROR_CODE_METHOD( +-+ ("libfptr_error_code", self.library) +-+ ) +-+ self._errorDescription = self.GET_ERROR_DESCRIPTION_METHOD( +-+ ("libfptr_error_description", self.library) +-+ ) +-+ self._errorRecommendation = self.GET_ERROR_RECOMMENDATION_METHOD( +-+ ("libfptr_error_recommendation", self.library) +-+ ) +-+ self._resetError = self.RESET_ERROR_METHOD( +-+ ("libfptr_reset_error", self.library) +-+ ) +-+ self._logWrite = self.LOG_WRITE_METHOD(("libfptr_log_write_ex", self.library)) +-+ self._showProperties = self.SHOW_PROPERTIES_METHOD( +-+ ("libfptr_show_properties", self.library) +-+ ) +-+ self._changeLabel = self.CHANGE_LABEL_METHOD( +-+ ("libfptr_change_label", self.library) +-+ ) +- +- def __del__(self): +-- destroy = self.DESTROY_METHOD(('libfptr_destroy', self.library)) +-+ destroy = self.DESTROY_METHOD(("libfptr_destroy", self.library)) +- destroy(ctypes.pointer(self.interface)) +- +- def version(self): +-@@ -1957,7 +2020,7 @@ class IFptr(object): +- +- def setSingleSetting(self, key, value): +- if isinstance(value, bool): +-- value = '1' if value else '0' +-+ value = "1" if value else "0" +- elif isinstance(value, int): +- value = str(value) +- self._setSingleSetting(self.interface, key, value) +-@@ -1973,75 +2036,147 @@ class IFptr(object): +- def setParam(self, paramId, param): +- if isinstance(param, bool): +- self._setBool(self.interface, ctypes.c_int(paramId), ctypes.c_int(param)) +-- elif isinstance(param, int) or (sys.version_info < (3, 0) and isinstance(param, long)): +-+ elif isinstance(param, int) or ( +-+ sys.version_info < (3, 0) and isinstance(param, long) +-+ ): +- if param > 4294967295: +- raise ValueError("Invalid 'param' value {0}".format(param)) +- if param < 0: +-- self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) +-+ self._setDouble( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_double(param) +-+ ) +- else: +-- self._setInt(self.interface, ctypes.c_int(paramId), ctypes.c_uint(param)) +-+ self._setInt( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_uint(param) +-+ ) +- elif isinstance(param, float): +-- self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) +-+ self._setDouble( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_double(param) +-+ ) +- elif isinstance(param, TEXT): +-- self._setString(self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param)) +-- elif isinstance(param, list) or isinstance(param, bytearray) or isinstance(param, bytes): +-- self._setByteArray(self.interface, ctypes.c_int(paramId), +-- (ctypes.c_ubyte * len(param))(*param), len(param)) +-+ self._setString( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param) +-+ ) +-+ elif ( +-+ isinstance(param, list) +-+ or isinstance(param, bytearray) +-+ or isinstance(param, bytes) +-+ ): +-+ self._setByteArray( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ (ctypes.c_ubyte * len(param))(*param), +-+ len(param), +-+ ) +- elif isinstance(param, datetime.datetime): +-- self._setDateTime(self.interface, ctypes.c_int(paramId), param.date().year, +-- param.date().month, +-- param.date().day, +-- param.time().hour, param.time().minute, param.time().second) +-+ self._setDateTime( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ param.date().year, +-+ param.date().month, +-+ param.date().day, +-+ param.time().hour, +-+ param.time().minute, +-+ param.time().second, +-+ ) +- else: +- raise TypeError("Invalid 'param' type {0}".format(type(param))) +- +- def setUserParam(self, paramId, param): +- if isinstance(param, bool): +-- self._setUserBool(self.interface, ctypes.c_int(paramId), ctypes.c_int(param)) +-+ self._setUserBool( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_int(param) +-+ ) +- elif isinstance(param, int): +- if param > 4294967295: +- raise ValueError("Invalid 'param' value {0}".format(param)) +- if param < 0: +-- self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) +-+ self._setDouble( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_double(param) +-+ ) +- else: +-- self._setInt(self.interface, ctypes.c_int(paramId), ctypes.c_uint(param)) +-+ self._setInt( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_uint(param) +-+ ) +- elif isinstance(param, float): +-- self._setUserDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) +-+ self._setUserDouble( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_double(param) +-+ ) +- elif isinstance(param, TEXT): +-- self._setUserString(self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param)) +-- elif isinstance(param, list) or isinstance(param, bytearray) or isinstance(param, bytes): +-- self._setUserByteArray(self.interface, ctypes.c_int(paramId), +-- (ctypes.c_ubyte * len(param))(*param), len(param)) +-+ self._setUserString( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param) +-+ ) +-+ elif ( +-+ isinstance(param, list) +-+ or isinstance(param, bytearray) +-+ or isinstance(param, bytes) +-+ ): +-+ self._setUserByteArray( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ (ctypes.c_ubyte * len(param))(*param), +-+ len(param), +-+ ) +- elif isinstance(param, datetime.datetime): +-- self._setUserDateTime(self.interface, ctypes.c_int(paramId), param.date().year, +-- param.date().month, +-- param.date().day, +-- param.time().hour, param.time().minute, param.time().second) +-+ self._setUserDateTime( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ param.date().year, +-+ param.date().month, +-+ param.date().day, +-+ param.time().hour, +-+ param.time().minute, +-+ param.time().second, +-+ ) +- else: +- raise TypeError("Invalid 'param' type {0}".format(type(param))) +- +- def setNonPrintableParam(self, paramId, param): +- if isinstance(param, bool): +-- self._setNonPrintableBool(self.interface, ctypes.c_int(paramId), ctypes.c_int(param)) +-+ self._setNonPrintableBool( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_int(param) +-+ ) +- elif isinstance(param, int): +- if param > 4294967295: +- raise ValueError("Invalid 'param' value {0}".format(param)) +- if param < 0: +-- self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) +-+ self._setDouble( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_double(param) +-+ ) +- else: +-- self._setInt(self.interface, ctypes.c_int(paramId), ctypes.c_uint(param)) +-+ self._setInt( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_uint(param) +-+ ) +- elif isinstance(param, float): +-- self._setNonPrintableDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) +-+ self._setNonPrintableDouble( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_double(param) +-+ ) +- elif isinstance(param, TEXT): +-- self._setNonPrintableString(self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param)) +-- elif isinstance(param, list) or isinstance(param, bytearray) or isinstance(param, bytes): +-- self._setNonPrintableByteArray(self.interface, ctypes.c_int(paramId), +-- (ctypes.c_ubyte * len(param))(*param), len(param)) +-+ self._setNonPrintableString( +-+ self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param) +-+ ) +-+ elif ( +-+ isinstance(param, list) +-+ or isinstance(param, bytearray) +-+ or isinstance(param, bytes) +-+ ): +-+ self._setNonPrintableByteArray( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ (ctypes.c_ubyte * len(param))(*param), +-+ len(param), +-+ ) +- elif isinstance(param, datetime.datetime): +-- self._setNonPrintableDateTime(self.interface, ctypes.c_int(paramId), param.date().year, +-- param.date().month, +-- param.date().day, +-- param.time().hour, param.time().minute, param.time().second) +-+ self._setNonPrintableDateTime( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ param.date().year, +-+ param.date().month, +-+ param.date().day, +-+ param.time().hour, +-+ param.time().minute, +-+ param.time().second, +-+ ) +- else: +- raise TypeError("Invalid 'param' type {0}".format(type(param))) +- +-@@ -2059,13 +2194,20 @@ class IFptr(object): +- +- def getParamByteArray(self, paramId): +- value = (ctypes.c_ubyte * self.DEFAULT_BUFF_SIZE)() +-- size = self._getByteArray(self.interface, ctypes.c_int(paramId), +-- ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), +-- self.DEFAULT_BUFF_SIZE) +-+ size = self._getByteArray( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), +-+ self.DEFAULT_BUFF_SIZE, +-+ ) +- if size > self.DEFAULT_BUFF_SIZE: +- value = (ctypes.c_ubyte * size)() +-- size = self._getByteArray(self.interface, ctypes.c_int(paramId), +-- ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), size) +-+ size = self._getByteArray( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), +-+ size, +-+ ) +- return value[:size] +- +- def getParamDateTime(self, paramId): +-@@ -2075,16 +2217,25 @@ class IFptr(object): +- hour = ctypes.c_int(0) +- minute = ctypes.c_int(0) +- second = ctypes.c_int(0) +-- self._getDateTime(self.interface, ctypes.c_int(paramId), ctypes.pointer(year), +-- ctypes.pointer(month), +-- ctypes.pointer(day), +-- ctypes.pointer(hour), ctypes.pointer(minute), ctypes.pointer(second)) +-- return datetime.datetime(year.value, month.value, day.value, hour.value, minute.value, +-- second.value) +-+ self._getDateTime( +-+ self.interface, +-+ ctypes.c_int(paramId), +-+ ctypes.pointer(year), +-+ ctypes.pointer(month), +-+ ctypes.pointer(day), +-+ ctypes.pointer(hour), +-+ ctypes.pointer(minute), +-+ ctypes.pointer(second), +-+ ) +-+ return datetime.datetime( +-+ year.value, month.value, day.value, hour.value, minute.value, second.value +-+ ) +- +- def getParamString(self, paramId): +- value = ctypes.create_unicode_buffer(self.DEFAULT_BUFF_SIZE) +-- size = self._getString(self.interface, ctypes.c_int(paramId), value, self.DEFAULT_BUFF_SIZE) +-+ size = self._getString( +-+ self.interface, ctypes.c_int(paramId), value, self.DEFAULT_BUFF_SIZE +-+ ) +- if size > self.DEFAULT_BUFF_SIZE: +- value = ctypes.create_unicode_buffer(size) +- self._getString(self.interface, ctypes.c_int(paramId), value, size) +-@@ -2095,485 +2246,495 @@ class IFptr(object): +- return value > 0 +- +- def applySingleSettings(self): +-- _method = self.METHOD(('libfptr_apply_single_settings', self.library)) +-+ _method = self.METHOD(("libfptr_apply_single_settings", self.library)) +- return _method(self.interface) +- +- def open(self): +-- _method = self.METHOD(('libfptr_open', self.library)) +-+ _method = self.METHOD(("libfptr_open", self.library)) +- return _method(self.interface) +- +- def close(self): +-- _method = self.METHOD(('libfptr_close', self.library)) +-+ _method = self.METHOD(("libfptr_close", self.library)) +- return _method(self.interface) +- +- def resetParams(self): +-- _method = self.METHOD(('libfptr_reset_params', self.library)) +-+ _method = self.METHOD(("libfptr_reset_params", self.library)) +- return _method(self.interface) +- +- def runCommand(self): +-- _method = self.METHOD(('libfptr_run_command', self.library)) +-+ _method = self.METHOD(("libfptr_run_command", self.library)) +- return _method(self.interface) +- +- def beep(self): +-- _method = self.METHOD(('libfptr_beep', self.library)) +-+ _method = self.METHOD(("libfptr_beep", self.library)) +- return _method(self.interface) +- +- def openDrawer(self): +-- _method = self.METHOD(('libfptr_open_drawer', self.library)) +-+ _method = self.METHOD(("libfptr_open_drawer", self.library)) +- return _method(self.interface) +- +- def cut(self): +-- _method = self.METHOD(('libfptr_cut', self.library)) +-+ _method = self.METHOD(("libfptr_cut", self.library)) +- return _method(self.interface) +- +- def devicePoweroff(self): +-- _method = self.METHOD(('libfptr_device_poweroff', self.library)) +-+ _method = self.METHOD(("libfptr_device_poweroff", self.library)) +- return _method(self.interface) +- +- def deviceReboot(self): +-- _method = self.METHOD(('libfptr_device_reboot', self.library)) +-+ _method = self.METHOD(("libfptr_device_reboot", self.library)) +- return _method(self.interface) +- +- def openShift(self): +-- _method = self.METHOD(('libfptr_open_shift', self.library)) +-+ _method = self.METHOD(("libfptr_open_shift", self.library)) +- return _method(self.interface) +- +- def resetSummary(self): +-- _method = self.METHOD(('libfptr_reset_summary', self.library)) +-+ _method = self.METHOD(("libfptr_reset_summary", self.library)) +- return _method(self.interface) +- +- def initDevice(self): +-- _method = self.METHOD(('libfptr_init_device', self.library)) +-+ _method = self.METHOD(("libfptr_init_device", self.library)) +- return _method(self.interface) +- +- def queryData(self): +-- _method = self.METHOD(('libfptr_query_data', self.library)) +-+ _method = self.METHOD(("libfptr_query_data", self.library)) +- return _method(self.interface) +- +- def cashIncome(self): +-- _method = self.METHOD(('libfptr_cash_income', self.library)) +-+ _method = self.METHOD(("libfptr_cash_income", self.library)) +- return _method(self.interface) +- +- def cashOutcome(self): +-- _method = self.METHOD(('libfptr_cash_outcome', self.library)) +-+ _method = self.METHOD(("libfptr_cash_outcome", self.library)) +- return _method(self.interface) +- +- def openReceipt(self): +-- _method = self.METHOD(('libfptr_open_receipt', self.library)) +-+ _method = self.METHOD(("libfptr_open_receipt", self.library)) +- return _method(self.interface) +- +- def cancelReceipt(self): +-- _method = self.METHOD(('libfptr_cancel_receipt', self.library)) +-+ _method = self.METHOD(("libfptr_cancel_receipt", self.library)) +- return _method(self.interface) +- +- def closeReceipt(self): +-- _method = self.METHOD(('libfptr_close_receipt', self.library)) +-+ _method = self.METHOD(("libfptr_close_receipt", self.library)) +- return _method(self.interface) +- +- def checkDocumentClosed(self): +-- _method = self.METHOD(('libfptr_check_document_closed', self.library)) +-+ _method = self.METHOD(("libfptr_check_document_closed", self.library)) +- return _method(self.interface) +- +- def receiptTotal(self): +-- _method = self.METHOD(('libfptr_receipt_total', self.library)) +-+ _method = self.METHOD(("libfptr_receipt_total", self.library)) +- return _method(self.interface) +- +- def receiptTax(self): +-- _method = self.METHOD(('libfptr_receipt_tax', self.library)) +-+ _method = self.METHOD(("libfptr_receipt_tax", self.library)) +- return _method(self.interface) +- +- def registration(self): +-- _method = self.METHOD(('libfptr_registration', self.library)) +-+ _method = self.METHOD(("libfptr_registration", self.library)) +- return _method(self.interface) +- +- def payment(self): +-- _method = self.METHOD(('libfptr_payment', self.library)) +-+ _method = self.METHOD(("libfptr_payment", self.library)) +- return _method(self.interface) +- +- def report(self): +-- _method = self.METHOD(('libfptr_report', self.library)) +-+ _method = self.METHOD(("libfptr_report", self.library)) +- return _method(self.interface) +- +- def printText(self): +-- _method = self.METHOD(('libfptr_print_text', self.library)) +-+ _method = self.METHOD(("libfptr_print_text", self.library)) +- return _method(self.interface) +- +- def printCliche(self): +-- _method = self.METHOD(('libfptr_print_cliche', self.library)) +-+ _method = self.METHOD(("libfptr_print_cliche", self.library)) +- return _method(self.interface) +- +- def beginNonfiscalDocument(self): +-- _method = self.METHOD(('libfptr_begin_nonfiscal_document', self.library)) +-+ _method = self.METHOD(("libfptr_begin_nonfiscal_document", self.library)) +- return _method(self.interface) +- +- def endNonfiscalDocument(self): +-- _method = self.METHOD(('libfptr_end_nonfiscal_document', self.library)) +-+ _method = self.METHOD(("libfptr_end_nonfiscal_document", self.library)) +- return _method(self.interface) +- +- def printBarcode(self): +-- _method = self.METHOD(('libfptr_print_barcode', self.library)) +-+ _method = self.METHOD(("libfptr_print_barcode", self.library)) +- return _method(self.interface) +- +- def printPicture(self): +-- _method = self.METHOD(('libfptr_print_picture', self.library)) +-+ _method = self.METHOD(("libfptr_print_picture", self.library)) +- return _method(self.interface) +- +- def printPictureByNumber(self): +-- _method = self.METHOD(('libfptr_print_picture_by_number', self.library)) +-+ _method = self.METHOD(("libfptr_print_picture_by_number", self.library)) +- return _method(self.interface) +- +- def uploadPictureFromFile(self): +-- _method = self.METHOD(('libfptr_upload_picture_from_file', self.library)) +-+ _method = self.METHOD(("libfptr_upload_picture_from_file", self.library)) +- return _method(self.interface) +- +- def clearPictures(self): +-- _method = self.METHOD(('libfptr_clear_pictures', self.library)) +-+ _method = self.METHOD(("libfptr_clear_pictures", self.library)) +- return _method(self.interface) +- +- def writeDeviceSettingRaw(self): +-- _method = self.METHOD(('libfptr_write_device_setting_raw', self.library)) +-+ _method = self.METHOD(("libfptr_write_device_setting_raw", self.library)) +- return _method(self.interface) +- +- def readDeviceSettingRaw(self): +-- _method = self.METHOD(('libfptr_read_device_setting_raw', self.library)) +-+ _method = self.METHOD(("libfptr_read_device_setting_raw", self.library)) +- return _method(self.interface) +- +- def commitSettings(self): +-- _method = self.METHOD(('libfptr_commit_settings', self.library)) +-+ _method = self.METHOD(("libfptr_commit_settings", self.library)) +- return _method(self.interface) +- +- def initSettings(self): +-- _method = self.METHOD(('libfptr_init_settings', self.library)) +-+ _method = self.METHOD(("libfptr_init_settings", self.library)) +- return _method(self.interface) +- +- def resetSettings(self): +-- _method = self.METHOD(('libfptr_reset_settings', self.library)) +-+ _method = self.METHOD(("libfptr_reset_settings", self.library)) +- return _method(self.interface) +- +- def writeDateTime(self): +-- _method = self.METHOD(('libfptr_write_date_time', self.library)) +-+ _method = self.METHOD(("libfptr_write_date_time", self.library)) +- return _method(self.interface) +- +- def writeLicense(self): +-- _method = self.METHOD(('libfptr_write_license', self.library)) +-+ _method = self.METHOD(("libfptr_write_license", self.library)) +- return _method(self.interface) +- +- def fnOperation(self): +-- _method = self.METHOD(('libfptr_fn_operation', self.library)) +-+ _method = self.METHOD(("libfptr_fn_operation", self.library)) +- return _method(self.interface) +- +- def fnQueryData(self): +-- _method = self.METHOD(('libfptr_fn_query_data', self.library)) +-+ _method = self.METHOD(("libfptr_fn_query_data", self.library)) +- return _method(self.interface) +- +- def fnWriteAttributes(self): +-- _method = self.METHOD(('libfptr_fn_write_attributes', self.library)) +-+ _method = self.METHOD(("libfptr_fn_write_attributes", self.library)) +- return _method(self.interface) +- +- def externalDevicePowerOn(self): +-- _method = self.METHOD(('libfptr_external_device_power_on', self.library)) +-+ _method = self.METHOD(("libfptr_external_device_power_on", self.library)) +- return _method(self.interface) +- +- def externalDevicePowerOff(self): +-- _method = self.METHOD(('libfptr_external_device_power_off', self.library)) +-+ _method = self.METHOD(("libfptr_external_device_power_off", self.library)) +- return _method(self.interface) +- +- def externalDeviceWriteData(self): +-- _method = self.METHOD(('libfptr_external_device_write_data', self.library)) +-+ _method = self.METHOD(("libfptr_external_device_write_data", self.library)) +- return _method(self.interface) +- +- def externalDeviceReadData(self): +-- _method = self.METHOD(('libfptr_external_device_read_data', self.library)) +-+ _method = self.METHOD(("libfptr_external_device_read_data", self.library)) +- return _method(self.interface) +- +- def operatorLogin(self): +-- _method = self.METHOD(('libfptr_operator_login', self.library)) +-+ _method = self.METHOD(("libfptr_operator_login", self.library)) +- return _method(self.interface) +- +- def processJson(self): +-- _method = self.METHOD(('libfptr_process_json', self.library)) +-+ _method = self.METHOD(("libfptr_process_json", self.library)) +- return _method(self.interface) +- +- def readDeviceSetting(self): +-- _method = self.METHOD(('libfptr_read_device_setting', self.library)) +-+ _method = self.METHOD(("libfptr_read_device_setting", self.library)) +- return _method(self.interface) +- +- def writeDeviceSetting(self): +-- _method = self.METHOD(('libfptr_write_device_setting', self.library)) +-+ _method = self.METHOD(("libfptr_write_device_setting", self.library)) +- return _method(self.interface) +- +- def beginReadRecords(self): +-- _method = self.METHOD(('libfptr_begin_read_records', self.library)) +-+ _method = self.METHOD(("libfptr_begin_read_records", self.library)) +- return _method(self.interface) +- +- def readNextRecord(self): +-- _method = self.METHOD(('libfptr_read_next_record', self.library)) +-+ _method = self.METHOD(("libfptr_read_next_record", self.library)) +- return _method(self.interface) +- +- def endReadRecords(self): +-- _method = self.METHOD(('libfptr_end_read_records', self.library)) +-+ _method = self.METHOD(("libfptr_end_read_records", self.library)) +- return _method(self.interface) +- +- def userMemoryOperation(self): +-- _method = self.METHOD(('libfptr_user_memory_operation', self.library)) +-+ _method = self.METHOD(("libfptr_user_memory_operation", self.library)) +- return _method(self.interface) +- +- def continuePrint(self): +-- _method = self.METHOD(('libfptr_continue_print', self.library)) +-+ _method = self.METHOD(("libfptr_continue_print", self.library)) +- return _method(self.interface) +- +- def initMgm(self): +-- _method = self.METHOD(('libfptr_init_mgm', self.library)) +-+ _method = self.METHOD(("libfptr_init_mgm", self.library)) +- return _method(self.interface) +- +- def utilFormTlv(self): +-- _method = self.METHOD(('libfptr_util_form_tlv', self.library)) +-+ _method = self.METHOD(("libfptr_util_form_tlv", self.library)) +- return _method(self.interface) +- +- def utilFormNomenclature(self): +-- _method = self.METHOD(('libfptr_util_form_nomenclature', self.library)) +-+ _method = self.METHOD(("libfptr_util_form_nomenclature", self.library)) +- return _method(self.interface) +- +- def utilMapping(self): +-- _method = self.METHOD(('libfptr_util_mapping', self.library)) +-+ _method = self.METHOD(("libfptr_util_mapping", self.library)) +- return _method(self.interface) +- +- def readModelFlags(self): +-- _method = self.METHOD(('libfptr_read_model_flags', self.library)) +-+ _method = self.METHOD(("libfptr_read_model_flags", self.library)) +- return _method(self.interface) +- +- def lineFeed(self): +-- _method = self.METHOD(('libfptr_line_feed', self.library)) +-+ _method = self.METHOD(("libfptr_line_feed", self.library)) +- return _method(self.interface) +- +- def flashFirmware(self): +-- _method = self.METHOD(('libfptr_flash_firmware', self.library)) +-+ _method = self.METHOD(("libfptr_flash_firmware", self.library)) +- return _method(self.interface) +- +- def softLockInit(self): +-- _method = self.METHOD(('libfptr_soft_lock_init', self.library)) +-+ _method = self.METHOD(("libfptr_soft_lock_init", self.library)) +- return _method(self.interface) +- +- def softLockQuerySessionCode(self): +-- _method = self.METHOD(('libfptr_soft_lock_query_session_code', self.library)) +-+ _method = self.METHOD(("libfptr_soft_lock_query_session_code", self.library)) +- return _method(self.interface) +- +- def softLockValidate(self): +-- _method = self.METHOD(('libfptr_soft_lock_validate', self.library)) +-+ _method = self.METHOD(("libfptr_soft_lock_validate", self.library)) +- return _method(self.interface) +- +- def utilCalcTax(self): +-- _method = self.METHOD(('libfptr_util_calc_tax', self.library)) +-+ _method = self.METHOD(("libfptr_util_calc_tax", self.library)) +- return _method(self.interface) +- +- def downloadPicture(self): +-- _method = self.METHOD(('libfptr_download_picture', self.library)) +-+ _method = self.METHOD(("libfptr_download_picture", self.library)) +- return _method(self.interface) +- +- def bluetoothRemovePairedDevices(self): +-- _method = self.METHOD(('libfptr_bluetooth_remove_paired_devices', self.library)) +-+ _method = self.METHOD(("libfptr_bluetooth_remove_paired_devices", self.library)) +- return _method(self.interface) +- +- def utilTagInfo(self): +-- _method = self.METHOD(('libfptr_util_tag_info', self.library)) +-+ _method = self.METHOD(("libfptr_util_tag_info", self.library)) +- return _method(self.interface) +- +- def utilContainerVersions(self): +-- _method = self.METHOD(('libfptr_util_container_versions', self.library)) +-+ _method = self.METHOD(("libfptr_util_container_versions", self.library)) +- return _method(self.interface) +- +- def activateLicenses(self): +-- _method = self.METHOD(('libfptr_activate_licenses', self.library)) +-+ _method = self.METHOD(("libfptr_activate_licenses", self.library)) +- return _method(self.interface) +- +- def removeLicenses(self): +-- _method = self.METHOD(('libfptr_remove_licenses', self.library)) +-+ _method = self.METHOD(("libfptr_remove_licenses", self.library)) +- return _method(self.interface) +- +- def enterKeys(self): +-- _method = self.METHOD(('libfptr_enter_keys', self.library)) +-+ _method = self.METHOD(("libfptr_enter_keys", self.library)) +- return _method(self.interface) +- +- def validateKeys(self): +-- _method = self.METHOD(('libfptr_validate_keys', self.library)) +-+ _method = self.METHOD(("libfptr_validate_keys", self.library)) +- return _method(self.interface) +- +- def enterSerialNumber(self): +-- _method = self.METHOD(('libfptr_enter_serial_number', self.library)) +-+ _method = self.METHOD(("libfptr_enter_serial_number", self.library)) +- return _method(self.interface) +- +- def getSerialNumberRequest(self): +-- _method = self.METHOD(('libfptr_get_serial_number_request', self.library)) +-+ _method = self.METHOD(("libfptr_get_serial_number_request", self.library)) +- return _method(self.interface) +- +- def uploadPixelBuffer(self): +-- _method = self.METHOD(('libfptr_upload_pixel_buffer', self.library)) +-+ _method = self.METHOD(("libfptr_upload_pixel_buffer", self.library)) +- return _method(self.interface) +- +- def downloadPixelBuffer(self): +-- _method = self.METHOD(('libfptr_download_pixel_buffer', self.library)) +-+ _method = self.METHOD(("libfptr_download_pixel_buffer", self.library)) +- return _method(self.interface) +- +- def printPixelBuffer(self): +-- _method = self.METHOD(('libfptr_print_pixel_buffer', self.library)) +-+ _method = self.METHOD(("libfptr_print_pixel_buffer", self.library)) +- return _method(self.interface) +- +- def utilConvertTagValue(self): +-- _method = self.METHOD(('libfptr_util_convert_tag_value', self.library)) +-+ _method = self.METHOD(("libfptr_util_convert_tag_value", self.library)) +- return _method(self.interface) +- +- def parseMarkingCode(self): +-- _method = self.METHOD(('libfptr_parse_marking_code', self.library)) +-+ _method = self.METHOD(("libfptr_parse_marking_code", self.library)) +- return _method(self.interface) +- +- def callScript(self): +-- _method = self.METHOD(('libfptr_call_script', self.library)) +-+ _method = self.METHOD(("libfptr_call_script", self.library)) +- return _method(self.interface) +- +- def setHeaderLines(self): +-- _method = self.METHOD(('libfptr_set_header_lines', self.library)) +-+ _method = self.METHOD(("libfptr_set_header_lines", self.library)) +- return _method(self.interface) +- +- def setFooterLines(self): +-- _method = self.METHOD(('libfptr_set_footer_lines', self.library)) +-+ _method = self.METHOD(("libfptr_set_footer_lines", self.library)) +- return _method(self.interface) +- +- def uploadPictureCliche(self): +-- _method = self.METHOD(('libfptr_upload_picture_cliche', self.library)) +-+ _method = self.METHOD(("libfptr_upload_picture_cliche", self.library)) +- return _method(self.interface) +- +- def uploadPictureMemory(self): +-- _method = self.METHOD(('libfptr_upload_picture_memory', self.library)) +-+ _method = self.METHOD(("libfptr_upload_picture_memory", self.library)) +- return _method(self.interface) +- +- def uploadPixelBufferCliche(self): +-- _method = self.METHOD(('libfptr_upload_pixel_buffer_cliche', self.library)) +-+ _method = self.METHOD(("libfptr_upload_pixel_buffer_cliche", self.library)) +- return _method(self.interface) +- +- def uploadPixelBufferMemory(self): +-- _method = self.METHOD(('libfptr_upload_pixel_buffer_memory', self.library)) +-+ _method = self.METHOD(("libfptr_upload_pixel_buffer_memory", self.library)) +- return _method(self.interface) +- +- def execDriverScript(self): +-- _method = self.METHOD(('libfptr_exec_driver_script', self.library)) +-+ _method = self.METHOD(("libfptr_exec_driver_script", self.library)) +- return _method(self.interface) +- +- def uploadDriverScript(self): +-- _method = self.METHOD(('libfptr_upload_driver_script', self.library)) +-+ _method = self.METHOD(("libfptr_upload_driver_script", self.library)) +- return _method(self.interface) +- +- def execDriverScriptById(self): +-- _method = self.METHOD(('libfptr_exec_driver_script_by_id', self.library)) +-+ _method = self.METHOD(("libfptr_exec_driver_script_by_id", self.library)) +- return _method(self.interface) +- +- def writeUniversalCountersSettings(self): +-- _method = self.METHOD(('libfptr_write_universal_counters_settings', self.library)) +-+ _method = self.METHOD( +-+ ("libfptr_write_universal_counters_settings", self.library) +-+ ) +- return _method(self.interface) +- +- def readUniversalCountersSettings(self): +-- _method = self.METHOD(('libfptr_read_universal_counters_settings', self.library)) +-+ _method = self.METHOD( +-+ ("libfptr_read_universal_counters_settings", self.library) +-+ ) +- return _method(self.interface) +- +- def queryUniversalCountersState(self): +-- _method = self.METHOD(('libfptr_query_universal_counters_state', self.library)) +-+ _method = self.METHOD(("libfptr_query_universal_counters_state", self.library)) +- return _method(self.interface) +- +- def resetUniversalCounters(self): +-- _method = self.METHOD(('libfptr_reset_universal_counters', self.library)) +-+ _method = self.METHOD(("libfptr_reset_universal_counters", self.library)) +- return _method(self.interface) +- +- def cacheUniversalCounters(self): +-- _method = self.METHOD(('libfptr_cache_universal_counters', self.library)) +-+ _method = self.METHOD(("libfptr_cache_universal_counters", self.library)) +- return _method(self.interface) +- +- def readUniversalCounterSum(self): +-- _method = self.METHOD(('libfptr_read_universal_counter_sum', self.library)) +-+ _method = self.METHOD(("libfptr_read_universal_counter_sum", self.library)) +- return _method(self.interface) +- +- def readUniversalCounterQuantity(self): +-- _method = self.METHOD(('libfptr_read_universal_counter_quantity', self.library)) +-+ _method = self.METHOD(("libfptr_read_universal_counter_quantity", self.library)) +- return _method(self.interface) +- +- def clearUniversalCountersCache(self): +-- _method = self.METHOD(('libfptr_clear_universal_counters_cache', self.library)) +-+ _method = self.METHOD(("libfptr_clear_universal_counters_cache", self.library)) +- return _method(self.interface) +- +- def disableOfdChannel(self): +-- _method = self.METHOD(('libfptr_disable_ofd_channel', self.library)) +-+ _method = self.METHOD(("libfptr_disable_ofd_channel", self.library)) +- return _method(self.interface) +- +- def enableOfdChannel(self): +-- _method = self.METHOD(('libfptr_enable_ofd_channel', self.library)) +-+ _method = self.METHOD(("libfptr_enable_ofd_channel", self.library)) +- return _method(self.interface) +- +- def validateJson(self): +-- _method = self.METHOD(('libfptr_validate_json', self.library)) +-+ _method = self.METHOD(("libfptr_validate_json", self.library)) +- return _method(self.interface) +- +- def reflectionCall(self): +-- _method = self.METHOD(('libfptr_reflection_call', self.library)) +-+ _method = self.METHOD(("libfptr_reflection_call", self.library)) +- return _method(self.interface) +- +- def getRemoteServerInfo(self): +-- _method = self.METHOD(('libfptr_get_remote_server_info', self.library)) +-+ _method = self.METHOD(("libfptr_get_remote_server_info", self.library)) +- return _method(self.interface) +- +- def beginMarkingCodeValidation(self): +-- _method = self.METHOD(('libfptr_begin_marking_code_validation', self.library)) +-+ _method = self.METHOD(("libfptr_begin_marking_code_validation", self.library)) +- return _method(self.interface) +- +- def cancelMarkingCodeValidation(self): +-- _method = self.METHOD(('libfptr_cancel_marking_code_validation', self.library)) +-+ _method = self.METHOD(("libfptr_cancel_marking_code_validation", self.library)) +- return _method(self.interface) +- +- def getMarkingCodeValidationStatus(self): +-- _method = self.METHOD(('libfptr_get_marking_code_validation_status', self.library)) +-+ _method = self.METHOD( +-+ ("libfptr_get_marking_code_validation_status", self.library) +-+ ) +- return _method(self.interface) +- +- def acceptMarkingCode(self): +-- _method = self.METHOD(('libfptr_accept_marking_code', self.library)) +-+ _method = self.METHOD(("libfptr_accept_marking_code", self.library)) +- return _method(self.interface) +- +- def declineMarkingCode(self): +-- _method = self.METHOD(('libfptr_decline_marking_code', self.library)) +-+ _method = self.METHOD(("libfptr_decline_marking_code", self.library)) +- return _method(self.interface) +- +- def updateFnmKeys(self): +-- _method = self.METHOD(('libfptr_update_fnm_keys', self.library)) +-+ _method = self.METHOD(("libfptr_update_fnm_keys", self.library)) +- return _method(self.interface) +- +- def writeSalesNotice(self): +-- _method = self.METHOD(('libfptr_write_sales_notice', self.library)) +-+ _method = self.METHOD(("libfptr_write_sales_notice", self.library)) +- return _method(self.interface) +- +- def checkMarkingCodeValidationsReady(self): +-- _method = self.METHOD(('libfptr_check_marking_code_validations_ready', self.library)) +-+ _method = self.METHOD( +-+ ("libfptr_check_marking_code_validations_ready", self.library) +-+ ) +- return _method(self.interface) +- +- def clearMarkingCodeValidationResult(self): +-- _method = self.METHOD(('libfptr_clear_marking_code_validation_result', self.library)) +-+ _method = self.METHOD( +-+ ("libfptr_clear_marking_code_validation_result", self.library) +-+ ) +- return _method(self.interface) +- +- def pingMarkingServer(self): +-- _method = self.METHOD(('libfptr_ping_marking_server', self.library)) +-+ _method = self.METHOD(("libfptr_ping_marking_server", self.library)) +- return _method(self.interface) +- +- def getMarkingServerStatus(self): +-- _method = self.METHOD(('libfptr_get_marking_server_status', self.library)) +-+ _method = self.METHOD(("libfptr_get_marking_server_status", self.library)) +- return _method(self.interface) +- +- def isDriverLocked(self): +-- _method = self.METHOD(('libfptr_is_driver_locked', self.library)) +-+ _method = self.METHOD(("libfptr_is_driver_locked", self.library)) +- return _method(self.interface) +- +- def getLastDocumentJournal(self): +-- _method = self.METHOD(('libfptr_get_last_document_journal', self.library)) +-+ _method = self.METHOD(("libfptr_get_last_document_journal", self.library)) +- return _method(self.interface) +- +- def findDocumentInJournal(self): +-- _method = self.METHOD(('libfptr_find_document_in_journal', self.library)) +-+ _method = self.METHOD(("libfptr_find_document_in_journal", self.library)) +- return _method(self.interface) +- +- def runFnCommand(self): +-- _method = self.METHOD(('libfptr_run_fn_command', self.library)) +-+ _method = self.METHOD(("libfptr_run_fn_command", self.library)) +- return _method(self.interface) +-diff --git a/modules/otchet.py b/modules/otchet.py +-index 965f03a..f1482a7 100644 +---- a/modules/otchet.py +-+++ b/modules/otchet.py +-@@ -3,7 +3,7 @@ from typing import Any, Dict, List, Tuple, Union +- +- from reportlab.lib import colors +- from reportlab.lib.pagesizes import A4, landscape, letter +--from reportlab.lib.units import mm, inch +-+from reportlab.lib.units import inch, mm +- from reportlab.pdfbase import pdfmetrics +- from reportlab.pdfbase.ttfonts import TTFont +- from reportlab.pdfgen import canvas +-@@ -17,7 +17,7 @@ system = System() +- config = Config() +- +- +--def get_ticket_type(age:str) -> str: +-+def get_ticket_type(age: str) -> str: +- """ +- Определяет тип билета на основе возраста клиента. +- +-@@ -45,6 +45,7 @@ def get_ticket_type(age:str) -> str: +- else: +- return "взрослый" +- +-+ +- def generate_saved_tickets(values: List[tuple]) -> None: +- """ +- Генерирует PDF-файл с билетами для клиентов на основе переданных данных. +-@@ -72,32 +73,79 @@ def generate_saved_tickets(values: List[tuple]) -> None: +- pdfmetrics.registerFont(TTFont("DejaVuSerif", "files/DejaVuSerif.ttf")) +- c = canvas.Canvas(path, pagesize=(landscape(letter))) +- for client in values: +-- (last_name, name, ticket_flag, price, category, client_id, age, hours, talents, date_time) = client +-+ ( +-+ last_name, +-+ name, +-+ ticket_flag, +-+ price, +-+ category, +-+ client_id, +-+ age, +-+ hours, +-+ talents, +-+ date_time, +-+ ) = client +- ticket_type = get_ticket_type(age) +- if ticket_type == "бесплатный" or category == "н": +- continue # Пропускаем бесплатных и "не идущих" +- c.setFont("DejaVuSerif", 12) +- # x - расстояние мм от левого края страницы +- # y - расстояние мм от нижнего края страницы +-- c.drawString(coordinates["name"]["x"] * mm,coordinates["name"]["y"] * mm, name) +-- c.drawString(coordinates["surname"]["x"] * mm, coordinates["surname"]["y"] * mm, last_name) +-- c.drawString(coordinates["age"]["x"] * mm, coordinates["age"]["y"] * mm, str(age)) +-+ c.drawString(coordinates["name"]["x"] * mm, coordinates["name"]["y"] * mm, name) +-+ c.drawString( +-+ coordinates["surname"]["x"] * mm, +-+ coordinates["surname"]["y"] * mm, +-+ last_name, +-+ ) +-+ c.drawString( +-+ coordinates["age"]["x"] * mm, coordinates["age"]["y"] * mm, str(age) +-+ ) +- duration_text = f"{hours} ч. пребывания" +- if hours == 3: +- duration_text += "-весь день" +-- c.drawString(coordinates["duration"]["x"] * mm, coordinates["duration"]["y"] * mm, duration_text) +-+ c.drawString( +-+ coordinates["duration"]["x"] * mm, +-+ coordinates["duration"]["y"] * mm, +-+ duration_text, +-+ ) +- if isinstance(date_time, str): +- date_time = datetime.strptime(date_time, "%Y-%m-%d") +-- c.drawString(coordinates["date"]["x"] * mm, coordinates["date"]["y"] * mm, date_time.strftime('%d.%m.%Y')) +-- c.drawString(coordinates["guest"]["x"] * mm, coordinates["guest"]["y"] * mm, "гость") +-- c.drawString(coordinates["city"]["x"] * mm, coordinates["city"]["y"] * mm, "БЕЛГОРОД") +-- c.drawString(coordinates["place"]["x"] * mm, coordinates["place"]["y"] * mm, "МАСТЕРСЛАВЛЬ") +-- c.drawString(coordinates["price"]["x"] * mm, coordinates["price"]["y"] * mm, f"{price} руб.") +-- c.drawString(coordinates["ticket_type"]["x"] * mm, coordinates["ticket_type"]["y"] * mm, ticket_type) +-- c.drawString(coordinates["notes"]["x"] * mm, coordinates["notes"]["y"] * mm, category) +-+ c.drawString( +-+ coordinates["date"]["x"] * mm, +-+ coordinates["date"]["y"] * mm, +-+ date_time.strftime("%d.%m.%Y"), +-+ ) +-+ c.drawString( +-+ coordinates["guest"]["x"] * mm, coordinates["guest"]["y"] * mm, "гость" +-+ ) +-+ c.drawString( +-+ coordinates["city"]["x"] * mm, coordinates["city"]["y"] * mm, "БЕЛГОРОД" +-+ ) +-+ c.drawString( +-+ coordinates["place"]["x"] * mm, +-+ coordinates["place"]["y"] * mm, +-+ "МАСТЕРСЛАВЛЬ", +-+ ) +-+ c.drawString( +-+ coordinates["price"]["x"] * mm, +-+ coordinates["price"]["y"] * mm, +-+ f"{price} руб.", +-+ ) +-+ c.drawString( +-+ coordinates["ticket_type"]["x"] * mm, +-+ coordinates["ticket_type"]["y"] * mm, +-+ ticket_type, +-+ ) +-+ c.drawString( +-+ coordinates["notes"]["x"] * mm, coordinates["notes"]["y"] * mm, category +-+ ) +- c.setFont("DejaVuSerif", 24) +- talents_str = "0" if ticket_type == "взрослый" else str(talents) +-- c.drawString(coordinates["talents"]["x"] * mm, coordinates["talents"]["y"] * mm, talents_str) +-+ c.drawString( +-+ coordinates["talents"]["x"] * mm, +-+ coordinates["talents"]["y"] * mm, +-+ talents_str, +-+ ) +- c.drawImage( +- img_file, +- coordinates["qr_code"]["x"], +-@@ -134,13 +182,30 @@ def generate_ticket_report_table(ticket_summary: dict) -> list[list]: +- """ +- logger.info("Запуск функции generate_ticket_report_table") +- type_ticket = [ +-- "Взрослый, 1 ч.", "Взрослый, 2 ч.", "Взрослый, 3 ч.", +-- "Детский, 1 ч.", "Детский, 2 ч.", "Детский, 3 ч.", +-- "Многодетный взрослый, 1 ч.", "Многодетный взрослый, 2 ч.", "Многодетный взрослый, 3 ч.", +-- "Многодетный детский, 1 ч.", "Многодетный детский, 2 ч.", "Многодетный детский, 3 ч.", +-- "Инвалид, 3 ч.", "Сопровождающий, 3 ч.", +-+ "Взрослый, 1 ч.", +-+ "Взрослый, 2 ч.", +-+ "Взрослый, 3 ч.", +-+ "Детский, 1 ч.", +-+ "Детский, 2 ч.", +-+ "Детский, 3 ч.", +-+ "Многодетный взрослый, 1 ч.", +-+ "Многодетный взрослый, 2 ч.", +-+ "Многодетный взрослый, 3 ч.", +-+ "Многодетный детский, 1 ч.", +-+ "Многодетный детский, 2 ч.", +-+ "Многодетный детский, 3 ч.", +-+ "Инвалид, 3 ч.", +-+ "Сопровождающий, 3 ч.", +-+ ] +-+ data = [ +-+ [ +-+ "№\n п/п", +-+ "Тип\nбилета", +-+ "Цена,\n руб.", +-+ "Количество,\n шт.", +-+ "Стоимость,\n руб.", +-+ ] +- ] +-- data = [["№\n п/п", "Тип\nбилета", "Цена,\n руб.", "Количество,\n шт.", "Стоимость,\n руб."]] +- # Счётчики для агрегатов +- total = { +- "adult": {"count": 0, "sum": 0}, +-@@ -182,22 +247,56 @@ def generate_ticket_report_table(ticket_summary: dict) -> list[list]: +- +- # Агрегированные строки +- data += [ +-- ["", "Всего взрослых билетов", "", total["adult"]["count"], total["adult"]["sum"]], +-- ["", "Всего детских билетов", "", total["child"]["count"], total["child"]["sum"]], +-- ["", "Всего многодетных взрослых билетов", "", total["many_adult"]["count"], total["many_adult"]["sum"]], +-- ["", "Всего многодетных детских билетов", "", total["many_child"]["count"], total["many_child"]["sum"]], +-+ [ +-+ "", +-+ "Всего взрослых билетов", +-+ "", +-+ total["adult"]["count"], +-+ total["adult"]["sum"], +-+ ], +-+ [ +-+ "", +-+ "Всего детских билетов", +-+ "", +-+ total["child"]["count"], +-+ total["child"]["sum"], +-+ ], +-+ [ +-+ "", +-+ "Всего многодетных взрослых билетов", +-+ "", +-+ total["many_adult"]["count"], +-+ total["many_adult"]["sum"], +-+ ], +-+ [ +-+ "", +-+ "Всего многодетных детских билетов", +-+ "", +-+ total["many_child"]["count"], +-+ total["many_child"]["sum"], +-+ ], +- ["", "Инвалид, 3 ч.", "", total["disabled"]["count"], "0"], +- ["", "Сопровождающий, 3 ч.", "", total["maintainer"]["count"], "0"], +-- ["", "Итого билетов", "", +-- total["adult"]["count"] + total["child"]["count"] + +-- total["many_adult"]["count"] + total["many_child"]["count"] + +-- total["disabled"]["count"] + total["maintainer"]["count"], +-- total["adult"]["sum"] + total["child"]["sum"] + +-- total["many_adult"]["sum"] + total["many_child"]["sum"]], +-+ [ +-+ "", +-+ "Итого билетов", +-+ "", +-+ total["adult"]["count"] +-+ + total["child"]["count"] +-+ + total["many_adult"]["count"] +-+ + total["many_child"]["count"] +-+ + total["disabled"]["count"] +-+ + total["maintainer"]["count"], +-+ total["adult"]["sum"] +-+ + total["child"]["sum"] +-+ + total["many_adult"]["sum"] +-+ + total["many_child"]["sum"], +-+ ], +- ] +- +- return data +- +-+ +- def otchet_administratora(date_1: str, date_2: str, values: Dict) -> None: +- """ +- Функция формирует отчет администратора в формате PDF. +-@@ -253,15 +352,22 @@ def otchet_administratora(date_1: str, date_2: str, values: Dict) -> None: +- table_width = sum(custom_widths) +- if table_width > (page_width - left_margin - right_margin): +- # Корректировка ширины второго столбца +-- remaining_width = page_width - left_margin - right_margin - (sum(custom_widths) - custom_widths[1]) +-+ remaining_width = ( +-+ page_width +-+ - left_margin +-+ - right_margin +-+ - (sum(custom_widths) - custom_widths[1]) +-+ ) +- custom_widths[1] = remaining_width +- t = Table(data, colWidths=custom_widths, rowHeights=[0.3 * inch] * len(data)) +- t.setStyle( +-- TableStyle([ +-- ("FONT", (0, 0), (-1, -1), "DejaVuSerif", 8), +-- ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), +-- ("BOX", (0, 0), (-1, -1), 0.25, colors.black), +-- ]) +-+ TableStyle( +-+ [ +-+ ("FONT", (0, 0), (-1, -1), "DejaVuSerif", 8), +-+ ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), +-+ ("BOX", (0, 0), (-1, -1), 0.25, colors.black), +-+ ] +-+ ) +- ) +- t.wrapOn(c, left_margin, 180 * mm) +- t.drawOn(c, left_margin, 60 * mm) +-@@ -281,6 +387,7 @@ def otchet_administratora(date_1: str, date_2: str, values: Dict) -> None: +- c.showPage() +- c.save() +- +-+ +- def safe_int(value: Any) -> int: +- """ +- Преобразует значение в целое число, если это возможно. Если значение None, строка 'None', +-@@ -295,14 +402,22 @@ def safe_int(value: Any) -> int: +- if isinstance(value, (list, dict, set, tuple)): # Проверка на неподобающие типы +- return 0 +- try: +-- if value not in [None, 'None']: +-- return int(float(value)) # Преобразование строки с плавающей точкой в целое число +-+ if value not in [None, "None"]: +-+ return int( +-+ float(value) +-+ ) # Преобразование строки с плавающей точкой в целое число +- else: +- return 0 +- except (ValueError, TypeError): # Обработка ошибок для некорректных значений +- return 0 +- +--def format_date_range(date1_str: str, date2_str: str, input_format: str = "%Y-%m-%d %H:%M:%S", output_format: str = "%d-%m-%Y") -> Tuple[str, str]: +-+ +-+def format_date_range( +-+ date1_str: str, +-+ date2_str: str, +-+ input_format: str = "%Y-%m-%d %H:%M:%S", +-+ output_format: str = "%d-%m-%Y", +-+) -> Tuple[str, str]: +- """ +- Преобразует строки с датами из одного формата в другой и возвращает кортеж из двух отформатированных строк. +- +-@@ -321,6 +436,7 @@ def format_date_range(date1_str: str, date2_str: str, input_format: str = "%Y-%m +- dt2 = date_2.strftime(output_format) +- return dt1, dt2 +- +-+ +- def otchet_kassira(val: List[int], date1: str, date2: str, kassir: Any) -> None: +- """ +- Формирует PDF-отчет кассира за указанный период с разбивкой по типам оплат и возвратов. +-@@ -345,7 +461,9 @@ def otchet_kassira(val: List[int], date1: str, date2: str, kassir: Any) -> None: +- logger.info("Запуск функции otchet_kassira") +- # Проверка на пустой список или недостаточное количество элементов +- if not val or len(val) < 4: +-- logger.error("Некорректные данные для отчета: данные не переданы или их недостаточно.") +-+ logger.error( +-+ "Некорректные данные для отчета: данные не переданы или их недостаточно." +-+ ) +- # Завершаем функцию, если данных недостаточно или они пустые +- return +- path = "./otchet.pdf" +-@@ -423,10 +541,21 @@ def otchet_kassira(val: List[int], date1: str, date2: str, kassir: Any) -> None: +- c.showPage() +- c.save() +- +-+ +- def process_sales_and_returns( +- sales: List[Tuple[str, int, Union[int, float]]], +-- sales_return: List[Tuple[str, int, Union[int, float], int]] +--) -> List[Tuple[Union[str, None], Union[int, float, None], Union[int, float, None], Union[int, float, None], Union[int, float, None], Union[int, float, None], Union[int, float]]]: +-+ sales_return: List[Tuple[str, int, Union[int, float], int]], +-+) -> List[ +-+ Tuple[ +-+ Union[str, None], +-+ Union[int, float, None], +-+ Union[int, float, None], +-+ Union[int, float, None], +-+ Union[int, float, None], +-+ Union[int, float, None], +-+ Union[int, float], +-+ ] +-+]: +- """ +- Обрабатывает продажи и возвраты, агрегируя информацию по каждому кассовому аппарату. +- Функция вычисляет суммы по картам и наличным, а также учитывает возвраты по каждому типу: +-@@ -462,16 +591,23 @@ def process_sales_and_returns( +- logger.info("Запуск функции process_sales_and_returns") +- pcs = { +- name: { +-- "card": 0, "cash": 0, +-- "return": 0, "return_again": 0, +-+ "card": 0, +-+ "cash": 0, +-+ "return": 0, +-+ "return_again": 0, +- "return_partial": 0, +-- "return_card": 0, "return_cash": 0, +-+ "return_card": 0, +-+ "return_cash": 0, +- } +- for name in system.pcs +- } +- +- type_rm = [1, 2, 3] # 1 - карта, 2 - наличные, 3 - offline +-- return_fields: dict[int, str] = {2: "return", 4: "return_again", 6: "return_partial"} +-+ return_fields: dict[int, str] = { +-+ 2: "return", +-+ 4: "return_again", +-+ 6: "return_partial", +-+ } +- +- for sale in sales: +- pc_name = sale[0] +-@@ -501,33 +637,40 @@ def process_sales_and_returns( +- data = [] +- for name in system.pcs: +- stats = pcs[name] +-- data.append(( +-- name, +-- stats["card"], +-- stats["cash"], +-- None, +-- stats["return_card"], +-- stats["return_cash"], +-- stats["return_card"] + stats["return_cash"] +-- )) +-+ data.append( +-+ ( +-+ name, +-+ stats["card"], +-+ stats["cash"], +-+ None, +-+ stats["return_card"], +-+ stats["return_cash"], +-+ stats["return_card"] + stats["return_cash"], +-+ ) +-+ ) +- +- total_card = sum(pc["card"] for pc in pcs.values()) +- total_cash = sum(pc["cash"] for pc in pcs.values()) +- total_return = sum(pc["return_card"] + pc["return_cash"] for pc in pcs.values()) +- +-- data.append(( +-- "Итого", +-- total_card, +-- total_cash, +-- total_card + total_cash, +-- None, +-- None, +-- total_return +-- )) +-+ data.append( +-+ ( +-+ "Итого", +-+ total_card, +-+ total_cash, +-+ total_card + total_cash, +-+ None, +-+ None, +-+ total_return, +-+ ) +-+ ) +- system.sales_data_summary = data +- return data +- +--def process_ticket_stats(tickets: List[Tuple[int, int, str, object, object, int]]) -> List[Tuple[str, Dict[str, int]]]: +-+ +-+def process_ticket_stats( +-+ tickets: List[Tuple[int, int, str, object, object, int]], +-+) -> List[Tuple[str, Dict[str, int]]]: +- """ +- Обрабатывает информацию о билетах, суммируя количество и стоимость проданных билетов по категориям +- и времени пребывания. Возвращает данные, которые могут быть использованы для отображения статистики +-@@ -555,10 +698,18 @@ def process_ticket_stats(tickets: List[Tuple[int, int, str, object, object, int] +- ) +- +- TICKET_TYPE_MAP = { +-- (0, 1): "Взрослый, 1 ч.", (0, 2): "Взрослый, 2 ч.", (0, 3): "Взрослый, 3 ч.", +-- (1, 1): "Детский, 1 ч.", (1, 2): "Детский, 2 ч.", (1, 3): "Детский, 3 ч.", +-- (2, 1): "Многодетный взрослый, 1 ч.", (2, 2): "Многодетный взрослый, 2 ч.", (2, 3): "Многодетный взрослый, 3 ч.", +-- (3, 1): "Многодетный детский, 1 ч.", (3, 2): "Многодетный детский, 2 ч.", (3, 3): "Многодетный детский, 3 ч.", +-+ (0, 1): "Взрослый, 1 ч.", +-+ (0, 2): "Взрослый, 2 ч.", +-+ (0, 3): "Взрослый, 3 ч.", +-+ (1, 1): "Детский, 1 ч.", +-+ (1, 2): "Детский, 2 ч.", +-+ (1, 3): "Детский, 3 ч.", +-+ (2, 1): "Многодетный взрослый, 1 ч.", +-+ (2, 2): "Многодетный взрослый, 2 ч.", +-+ (2, 3): "Многодетный взрослый, 3 ч.", +-+ (3, 1): "Многодетный детский, 1 ч.", +-+ (3, 2): "Многодетный детский, 2 ч.", +-+ (3, 3): "Многодетный детский, 3 ч.", +- (4, 3): "Инвалид, 3 ч.", +- (5, 3): "Сопровождающий, 3 ч.", +- } +-diff --git a/modules/payment_equipment.py b/modules/payment_equipment.py +-index 9b2005a..dad926f 100644 +---- a/modules/payment_equipment.py +-+++ b/modules/payment_equipment.py +-@@ -14,10 +14,12 @@ config = Config() +- +- # Получаем параметр available +- try: +-- kkt_available = config.get("available") == 'on' +-+ kkt_available = config.get("available") == "on" +- except (KeyError, ValueError): +- kkt_available = False +-- logger.info("Параметр 'available' не найден в конфигурации, ККТ считается отключенным") +-+ logger.info( +-+ "Параметр 'available' не найден в конфигурации, ККТ считается отключенным" +-+ ) +- +- # Инициализация драйвера ККТ +- try: +-@@ -164,7 +166,7 @@ def fptr_connection(device): +- device.open() +- connected = True +- +-- if not hasattr(device, 'isOpened') or not device.isOpened(): +-+ if not hasattr(device, "isOpened") or not device.isOpened(): +- logger.warning("Не удалось открыть соединение с ККТ") +- yield None +- return +-@@ -211,18 +213,24 @@ def run_terminal_command(command_params: str, timeout: int = 300): +- logger.info(f"Запуск команды: {pinpad_run}") +- try: +- process = subprocess.Popen( +-- pinpad_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE, +-- shell=True # Важно для строки команды +-+ pinpad_run, +-+ stdout=subprocess.PIPE, +-+ stderr=subprocess.PIPE, +-+ shell=True, # Важно для строки команды +- ) +- try: +- stdout, stderr = process.communicate(timeout=timeout) +- except subprocess.TimeoutExpired: +-- logger.warning(f"Процесс превысил таймаут {timeout} секунд. Завершается автоматически.") +-+ logger.warning( +-+ f"Процесс превысил таймаут {timeout} секунд. Завершается автоматически." +-+ ) +- process.terminate() +- try: +- stdout, stderr = process.communicate(timeout=5) +- except subprocess.TimeoutExpired: +-- logger.warning("Процесс не завершился после terminate(). Используется kill().") +-+ logger.warning( +-+ "Процесс не завершился после terminate(). Используется kill()." +-+ ) +- process.kill() +- stdout, stderr = process.communicate() +- +-@@ -296,6 +304,7 @@ def _safe_handle_error(returncode, title, message, error_callback): +- except Exception as e: +- logger.exception(f"Ошибка при вызове handle_error для кода {returncode}: {e}") +- +-+ +- @logger_wraps() +- def handle_error(code, title, message, error_callback=None): +- """Универсальная обработка ошибок терминала. +-@@ -343,15 +352,15 @@ def process_terminal_error(returncode, error_callback=None): +- error_handlers = { +- TERMINAL_USER_CANCEL_CODE: ( +- "Оплата отменена пользователем", +-- f"Оплата отменена пользователем. Код возврата: {returncode}." +-+ f"Оплата отменена пользователем. Код возврата: {returncode}.", +- ), +- TERMINAL_USER_TIMEOUT: ( +- "Слишком долгий ввод ПИН-кода", +-- f"Слишком долгий ввод ПИН-кода. Код возврата: {returncode}." +-+ f"Слишком долгий ввод ПИН-кода. Код возврата: {returncode}.", +- ), +- TERMINAL_INVALID_CURRENCY_CODE: ( +- "Указан неверный код валюты", +-- f"Указан неверный код валюты. Обратитесь в банк. Телефон поддержки {TERMINAL_SUPPORT}. Код: {returncode}." +-+ f"Указан неверный код валюты. Обратитесь в банк. Телефон поддержки {TERMINAL_SUPPORT}. Код: {returncode}.", +- ), +- TERMINAL_NO_ADDRESS_TO_CONTACT: ( +- "Терминал потерял связь с банком", +-@@ -359,7 +368,7 @@ def process_terminal_error(returncode, error_callback=None): +- ), +- TERMINAL_OPERATION_CANCELED: ( +- "Операция отменена", +-- f"Ошибка возникает тогда, когда карту достают из терминала быстрее, чем пройдет оплата. Необходимо повторить операцию.", +-+ "Ошибка возникает тогда, когда карту достают из терминала быстрее, чем пройдет оплата. Необходимо повторить операцию.", +- ), +- TERMINAL_QR_ERROR: ( +- "Ошибка операции по QR-коду", +-@@ -377,8 +386,8 @@ def process_terminal_error(returncode, error_callback=None): +- _safe_handle_error( +- returncode, +- "Карта клиента заблокирована", +-- f"Карта клиента заблокирована. Попробуйте произвести оплату другой картой или обратитесь в банк для выяснения причины.", +-- error_callback +-+ "Карта клиента заблокирована. Попробуйте произвести оплату другой картой или обратитесь в банк для выяснения причины.", +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_CARD_LIMIT: +-@@ -386,7 +395,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Превышен лимит операций", +- "Превышен лимит операций. Попробуйте произвести оплату другой картой или обратитесь в банк для выяснения причины.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_BIOMETRIC_ERROR: +-@@ -394,7 +403,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Ошибка в работе с биометрическими данными", +- f"Ошибка в работе с биометрическими данными. Обратитесь в банк для выяснения причины. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_ERROR_PIN_CODE: +-@@ -402,7 +411,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Ошибка при вводе ПИН-кода", +- "ПИН-код не был введен, либо введен неверно, либо вводимый ПИН-код заблокирован. Попробуйте повторить операцию оплаты.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_NO_CONNECTION_BANK: +-@@ -410,7 +419,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Нет связи с банком", +- f"Попробуйте повторить операцию через пару минут. При повторении ошибки необходимо обратиться в службу поддержки. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_NEED_CASH_COLLECTION: +-@@ -418,7 +427,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Необходимо произвести инкассацию", +- f"Необходимо произвести инкассацию. Обратитесь в службу поддержки. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_LIMIT_OPERATION: +-@@ -426,7 +435,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Превышен лимит операций", +- f"Превышен лимит операций. Обратитесь в службу поддержки. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_DATA_EXCHANGE: +-@@ -434,7 +443,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Требуется сделать сверку итогов", +- "Требуется сделать сверку итогов и повторить операцию.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_KLK: +-@@ -442,7 +451,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Ошибка в работе терминала", +- f"Необходимо обратиться в службу поддержки банка. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_CARD_ERROR: +-@@ -450,7 +459,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Операцию невозможно выполнить для этой карты", +- f"Необходимо повторить попытку. Если проблема сохраняется – использовать другую карту. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_SERVER_ROUTINE_MAINTENANCE: +-@@ -458,15 +467,15 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Сервера Сбербанка недоступны", +- f"Сервера Сбербанка находятся на обслуживании/ремонте/регламентных работах. Попробуйте повторить операцию позже. Если проблема сохраняется - обратитесь в службу поддержки. Телефон тех.поддержки {TERMINAL_SUPPORT}.Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_NO_MONEY: +- _safe_handle_error( +- returncode, +- "Недостаточно средств на карте", +-- f"Недостаточно средств на карте. Попробуйте произвести оплату другой картой.", +-- error_callback +-+ "Недостаточно средств на карте. Попробуйте произвести оплату другой картой.", +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_COMMAND_ERROR: +-@@ -474,7 +483,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Нет нужного варианта связи для операции", +- f"Нет нужного варианта связи для операции. Обратитесь в службу поддержки ПО. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- if returncode in TERMINAL_PIN_PAD_ERROR: +-@@ -482,7 +491,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Проблема в работе ПИН-пада.", +- f"Необходимо обратиться в службу поддержки банка. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- # Общие ошибки +-@@ -499,7 +508,7 @@ def process_terminal_error(returncode, error_callback=None): +- returncode, +- "Ошибка терминала", +- f"Возвращен неизвестный код ошибки. Обратитесь в службу поддержки. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", +-- error_callback +-+ error_callback, +- ) +- return 0 +- +-@@ -519,7 +528,9 @@ def terminal_oplata(amount: float) -> int: +- +- +- @logger_wraps() +--def process_terminal_transaction(command_code: str, amount: float, operation_name: str, error_callback=None) -> int: +-+def process_terminal_transaction( +-+ command_code: str, amount: float, operation_name: str, error_callback=None +-+) -> int: +- """ +- Обрабатывает операцию на банковском терминале. +- +-@@ -541,10 +552,10 @@ def process_terminal_transaction(command_code: str, amount: float, operation_nam +- if result is None: +- logger.error(f"Ошибка при выполнении команды терминала: {command}") +- _safe_handle_error( +-- f"Нет ответа от терминала", +-+ "Нет ответа от терминала", +- f"{operation_name} не была выполнена. Проверьте устройство.", +- "Код ошибки: отсутствует", +-- error_callback=error_callback +-+ error_callback=error_callback, +- ) +- return 0 +- +-@@ -567,11 +578,12 @@ def process_terminal_transaction(command_code: str, amount: float, operation_nam +- +- +- def universal_terminal_operation( +-- payment_type: int, +-- amount: float, +-- progress_signal, +-- operation_type: int = 1, +-- error_callback=None) -> tuple[int, int]: +-+ payment_type: int, +-+ amount: float, +-+ progress_signal, +-+ operation_type: int = 1, +-+ error_callback=None, +-+) -> tuple[int, int]: +- """ +- Универсальный обработчик терминальных операций для оплаты или возврата. +- +-@@ -621,7 +633,9 @@ def universal_terminal_operation( +- except Exception: +- pass # Игнорируем ошибки emit +- +-- bank = process_terminal_transaction(command_code, amount, operation_name, error_callback) +-+ bank = process_terminal_transaction( +-+ command_code, amount, operation_name, error_callback +-+ ) +- if bank == 1: +- if progress_signal is not None: +- try: +-@@ -638,12 +652,16 @@ def universal_terminal_operation( +- return 0, 1 +- elif payment_type == PAYMENT_OFFLINE: +- if operation_type != 1: +-- logger.warning("Оффлайн-режим не поддерживается для возврата или отмены") +-+ logger.warning( +-+ "Оффлайн-режим не поддерживается для возврата или отмены" +-+ ) +- progress_signal.emit("Оффлайн: только оплата", 100) +- return 0, 0 +- +- logger.info("Запускаем offline оплату по банковскому терминалу") +-- progress_signal.emit("Запускаем offline оплату по банковскому терминалу...", 35) +-+ progress_signal.emit( +-+ "Запускаем offline оплату по банковскому терминалу...", 35 +-+ ) +- return 1, 3 # успех, offline +- +- else: +-@@ -658,7 +676,9 @@ def universal_terminal_operation( +- logger.error(f"Неизвестная ошибка при проведении операции: {exp}") +- if progress_signal is not None: +- try: +-- progress_signal.emit(f"Неизвестная ошибка при проведении операции: {str(exp)}", 100) +-+ progress_signal.emit( +-+ f"Неизвестная ошибка при проведении операции: {str(exp)}", 100 +-+ ) +- except RuntimeError: +- pass +- return 0, 0 +-@@ -979,6 +999,7 @@ def print_pinpad_check(count: int = 2): +- +- count -= 1 +- +-+ +- def get_info(hide: bool = False) -> Optional[int]: +- """ +- Запрос информации о ККТ. +-@@ -1014,13 +1035,20 @@ def get_info(hide: bool = False) -> Optional[int]: +- return model +- except (ConnectionError, AttributeError) as e: +- logger.error(f"Ошибка при получении данных о ККТ: {e}") +-- windows.info_window("Ошибка", "Не удалось получить данные о ККТ.", "Проверьте настройки устройства.") +-+ windows.info_window( +-+ "Ошибка", +-+ "Не удалось получить данные о ККТ.", +-+ "Проверьте настройки устройства.", +-+ ) +- return None +- except Exception as e: +- logger.error(f"Неизвестная ошибка: {e}") +-- windows.info_window("Ошибка", "Произошла неизвестная ошибка.", "Попробуйте снова.") +-+ windows.info_window( +-+ "Ошибка", "Произошла неизвестная ошибка.", "Попробуйте снова." +-+ ) +- return None +- +-+ +- def is_kkt_connected() -> bool: +- """ +- Проверка физического подключения ККТ. +-@@ -1046,6 +1074,7 @@ def is_kkt_connected() -> bool: +- logger.error(f"Ошибка при проверке подключения ККТ: {e}") +- return False +- +-+ +- @logger_wraps() +- def get_last_document(day: int = 7) -> None: +- """Запрос информации о последнем чеке в ФН и проверка даты. +-@@ -1066,7 +1095,9 @@ def get_last_document(day: int = 7) -> None: +- except (ConnectionError, AttributeError) as e: +- logger.error(f"Ошибка при работе с ККТ: {e}") +- windows.info_window( +-- "Ошибка", "Не удалось получить данные от ККТ.", "Проверьте подключение и настройки." +-+ "Ошибка", +-+ "Не удалось получить данные от ККТ.", +-+ "Проверьте подключение и настройки.", +- ) +- except Exception as e: +- logger.exception(f"Неизвестная ошибка в get_last_document: {e}") +-@@ -1074,8 +1105,9 @@ def get_last_document(day: int = 7) -> None: +- "Ошибка", "Произошла неизвестная ошибка.", "Попробуйте снова." +- ) +- +-+ +- def get_last_document_datetime() -> datetime: +-- """ Получает дату и время последнего зарегистрированного чека из фискального накопителя. +-+ """Получает дату и время последнего зарегистрированного чека из фискального накопителя. +- +- Возвращаемое значение: +- datetime: Дата и время последнего документа в фискальном накопителе. +-@@ -1085,12 +1117,17 @@ def get_last_document_datetime() -> datetime: +- """ +- logger.info("Запуск функции get_last_document_datetime") +- with fptr_connection(fptr): +-- fptr.setParam(IFptr.LIBFPTR_PARAM_FN_DATA_TYPE, IFptr.LIBFPTR_FNDT_LAST_DOCUMENT) +-+ fptr.setParam( +-+ IFptr.LIBFPTR_PARAM_FN_DATA_TYPE, IFptr.LIBFPTR_FNDT_LAST_DOCUMENT +-+ ) +- fptr.fnQueryData() +-- last_check_datetime: datetime = fptr.getParamDateTime(IFptr.LIBFPTR_PARAM_DATE_TIME) +-+ last_check_datetime: datetime = fptr.getParamDateTime( +-+ IFptr.LIBFPTR_PARAM_DATE_TIME +-+ ) +- +- return last_check_datetime +- +-+ +- def check_stale_document(last_check: datetime, max_days: int = 7) -> None: +- """ +- Проверяет, не превышает ли возраст последнего чека заданное количество дней. +-@@ -1108,13 +1145,15 @@ def check_stale_document(last_check: datetime, max_days: int = 7) -> None: +- if (current_date - last_check) <= timedelta(days=max_days): +- # Ранний выход, если чек не старый +- return +-- info = f"Дата и время последнего чека в ФН: {last_check.strftime('%Y-%m-%d %H:%M:%S')}" +-+ info = ( +-+ f"Дата и время последнего чека в ФН: {last_check.strftime('%Y-%m-%d %H:%M:%S')}" +-+ ) +- windows.info_window( +- "Внимание", +- "Дата последнего документа, записанного в фискальном накопителе, старше 7 дней.\n\n" +- "Для корректного проведения платежей по банковскому терминалу " +-- "необходимо сделать сверку итогов (вкладка \"Касса\" -> \"Сверка итогов\").\n", +-- info +-+ 'необходимо сделать сверку итогов (вкладка "Касса" -> "Сверка итогов").\n', +-+ info, +- ) +- +- +-@@ -1402,7 +1441,9 @@ def kassir_reg(user): +- try: +- with fptr_connection(fptr): +- # Параметры для регистрации кассира +-- fptr.setParam(1021, f"{user.last_name} {user.first_name} {user.middle_name}") +-+ fptr.setParam( +-+ 1021, f"{user.last_name} {user.first_name} {user.middle_name}" +-+ ) +- fptr.setParam(1203, user.inn) +- fptr.operatorLogin() +- logger.info( +-@@ -1590,11 +1631,11 @@ def register_item(device, name, price, quantity, tax_type=IFptr.LIBFPTR_TAX_VAT2 +- logger.info("Запуск функции register_item") +- logger.info(f"В функцию переданы параметры: {name}, {price}, {quantity}") +- device.setParam(IFptr.LIBFPTR_PARAM_COMMODITY_NAME, name) +-- logger.info(f"name: {name}") # TODO: убрать +-+ logger.info(f"name: {name}") # TODO: убрать +- device.setParam(IFptr.LIBFPTR_PARAM_PRICE, price) +-- logger.info(f"price: {price}") # TODO: убрать +-+ logger.info(f"price: {price}") # TODO: убрать +- device.setParam(IFptr.LIBFPTR_PARAM_QUANTITY, quantity) +-- logger.info(f"quantity: {quantity}") # TODO: убрать +-+ logger.info(f"quantity: {quantity}") # TODO: убрать +- device.setParam(IFptr.LIBFPTR_PARAM_TAX_TYPE, tax_type) +- device.registration() +- +-@@ -1639,7 +1680,9 @@ def register_tickets(device, sale_dict, type_operation): +- None: Функция не возвращает значений, но выполняет регистрацию билетов в чеке. +- """ +- logger.info("Запуск функции register_tickets") +-- logger.debug(f"В функцию переданы: device = {device}, sale_dict = {sale_dict}, type_operation = {type_operation}") +-+ logger.debug( +-+ f"В функцию переданы: device = {device}, sale_dict = {sale_dict}, type_operation = {type_operation}" +-+ ) +- +- if type_operation == 1: +- count_adult_with_discount = sale_dict["detail"][0] +-@@ -1653,29 +1696,41 @@ def register_tickets(device, sale_dict, type_operation): +- +- # Взрослые без акции +- count_adult_no_discount = sale_dict["kol_adult"] - count_adult_with_discount +-- logger.debug(f"Взрослые без акции: kol_adult={sale_dict['kol_adult']}, price_adult= {sale_dict['price_adult']}") +-+ logger.debug( +-+ f"Взрослые без акции: kol_adult={sale_dict['kol_adult']}, price_adult= {sale_dict['price_adult']}" +-+ ) +- +- if count_adult_no_discount > 0: +- adults_no_discount_sum = count_adult_no_discount * sale_dict["price_adult"] +-- logger.info(f"Регистрируем взрослых без акции: {count_adult_no_discount} шт × {sale_dict['price_adult']} = {adults_no_discount_sum} руб") +-+ logger.info( +-+ f"Регистрируем взрослых без акции: {count_adult_no_discount} шт × {sale_dict['price_adult']} = {adults_no_discount_sum} руб" +-+ ) +- # if device is not None: # TODO: для теста без ККТ. + отступ внизу +- register_item( +- device, +- f"Билет взрослый {time} ч.", +-- sale_dict['price_adult'], +-+ sale_dict["price_adult"], +- count_adult_no_discount, +- ) +- else: +- logger.debug("Взрослые без акции: не регистрируем (количество = 0)") +- +- # Взрослые с акцией +-- logger.debug(f"Взрослые С акцией: count_adult_with_discount={count_adult_with_discount}, price_adult_with_discount={price_adult_with_discount}, discount={price_adult_with_discount}") +-+ logger.debug( +-+ f"Взрослые С акцией: count_adult_with_discount={count_adult_with_discount}, price_adult_with_discount={price_adult_with_discount}, discount={price_adult_with_discount}" +-+ ) +- +-- if (count_adult_with_discount > 0 +-- and price_adult_with_discount > 0 +-- and discount > 0): +-- adults_with_discount_sum = count_adult_with_discount * price_adult_with_discount +-- logger.info(f"Регистрируем взрослых с акцией: {count_adult_with_discount} шт × {price_adult_with_discount} = {adults_with_discount_sum} руб") +-+ if ( +-+ count_adult_with_discount > 0 +-+ and price_adult_with_discount > 0 +-+ and discount > 0 +-+ ): +-+ adults_with_discount_sum = ( +-+ count_adult_with_discount * price_adult_with_discount +-+ ) +-+ logger.info( +-+ f"Регистрируем взрослых с акцией: {count_adult_with_discount} шт × {price_adult_with_discount} = {adults_with_discount_sum} руб" +-+ ) +- # if device is not None: # TODO: для теста без ККТ. + отступ внизу +- register_item( +- device, +-@@ -1690,29 +1745,43 @@ def register_tickets(device, sale_dict, type_operation): +- +- # Дети без акции +- count_child_no_discount = sale_dict["kol_child"] - count_child_with_discount +-- logger.debug(f"Дети без акции: kol_child={sale_dict['kol_child']}, count_child_with_discount={count_child_with_discount}, result={count_child_no_discount}") +-+ logger.debug( +-+ f"Дети без акции: kol_child={sale_dict['kol_child']}, count_child_with_discount={count_child_with_discount}, result={count_child_no_discount}" +-+ ) +- +- if count_child_no_discount > 0: +-- children_no_discount_sum = count_child_no_discount * sale_dict["price_child"] +-- logger.info(f"Регистрируем детей без акции: {count_child_no_discount} шт × {sale_dict['price_child']} = {children_no_discount_sum} руб") +-+ children_no_discount_sum = ( +-+ count_child_no_discount * sale_dict["price_child"] +-+ ) +-+ logger.info( +-+ f"Регистрируем детей без акции: {count_child_no_discount} шт × {sale_dict['price_child']} = {children_no_discount_sum} руб" +-+ ) +- # if device is not None: # TODO: для теста без ККТ. + отступ внизу +- register_item( +- device, +- f"Билет детский {time} ч.", +-- sale_dict['price_child'], +-+ sale_dict["price_child"], +- count_child_no_discount, +- ) +- else: +- logger.debug("Дети без акции: не регистрируем (количество = 0)") +- +- # Дети с акцией +-- logger.debug(f"Дети с акцией: count_child_with_discount={count_child_with_discount}, price_child_with_discount={price_child_with_discount}, discount={price_adult_with_discount}") +-+ logger.debug( +-+ f"Дети с акцией: count_child_with_discount={count_child_with_discount}, price_child_with_discount={price_child_with_discount}, discount={price_adult_with_discount}" +-+ ) +- +-- if (count_child_with_discount > 0 +-- and price_child_with_discount > 0 +-- and discount > 0): +-- children_with_discount_sum = count_child_with_discount * price_child_with_discount +-- logger.info(f"Регистрируем детей с акцией: {count_child_with_discount} шт × {price_child_with_discount} = {children_with_discount_sum} руб") +-+ if ( +-+ count_child_with_discount > 0 +-+ and price_child_with_discount > 0 +-+ and discount > 0 +-+ ): +-+ children_with_discount_sum = ( +-+ count_child_with_discount * price_child_with_discount +-+ ) +-+ logger.info( +-+ f"Регистрируем детей с акцией: {count_child_with_discount} шт × {price_child_with_discount} = {children_with_discount_sum} руб" +-+ ) +- # if device is not None: # TODO: для теста без ККТ. + отступ внизу +- register_item( +- device, +-@@ -1724,15 +1793,17 @@ def register_tickets(device, sale_dict, type_operation): +- logger.debug("Дети с акцией: не регистрируем (условия не выполнены)") +- else: +- # Для других типов операций +-- logger.error(f"Другие типы операций.") +-+ logger.error("Другие типы операций.") +- for item_name, item_data in sale_dict.items(): +-- if (isinstance(item_data, list) and item_data[0] > 0 and item_data[1] > 0): +-+ if isinstance(item_data, list) and item_data[0] > 0 and item_data[1] > 0: +- # Проверяем наличие количества и цены +-- logger.debug(f"item_name ={item_name}, item_data[0]={item_data[0]}, item_data[1]={item_data[1]}") +-+ logger.debug( +-+ f"item_name ={item_name}, item_data[0]={item_data[0]}, item_data[1]={item_data[1]}" +-+ ) +- register_item(device, item_name, item_data[0], item_data[1]) +- else: +- # Обработка других типов данных +-- logger.error(f"Ошибка типа данных. Item_data - не список.") +-+ logger.error("Ошибка типа данных. Item_data - не список.") +- +- +- def process_payment(device, payment_type, bank_status, sale_dict, _): +-@@ -1748,11 +1819,15 @@ def process_payment(device, payment_type, bank_status, sale_dict, _): +- bool: Возвращает `True`, если оплата прошла успешно, иначе — `False`. +- """ +- logger.info("Запуск функции process_payment") +-- logger.debug(f"В функцию переданы: device = {device}, payment_type = {payment_type}, bank = {bank_status}, sale_dict = {sale_dict}, price = {_}") +-+ logger.debug( +-+ f"В функцию переданы: device = {device}, payment_type = {payment_type}, bank = {bank_status}, sale_dict = {sale_dict}, price = {_}" +-+ ) +- try: +- # Проверка payment_type +- if not isinstance(payment_type, int): +-- logger.error(f"Некорректный тип payment_type: {type(payment_type)}, значение: {payment_type}") +-+ logger.error( +-+ f"Некорректный тип payment_type: {type(payment_type)}, значение: {payment_type}" +-+ ) +- return False +- +- # Определение суммы +-@@ -1763,7 +1838,9 @@ def process_payment(device, payment_type, bank_status, sale_dict, _): +- try: +- payment_amount = sale_dict["detail"][7] +- if isinstance(payment_amount, (int, float)) and payment_amount > 0: +-- logger.debug(f"Сумма получена из sale_dict['detail'][7]: {payment_amount}") +-+ logger.debug( +-+ f"Сумма получена из sale_dict['detail'][7]: {payment_amount}" +-+ ) +- except (IndexError, TypeError): +- pass +- +-@@ -1775,7 +1852,12 @@ def process_payment(device, payment_type, bank_status, sale_dict, _): +- if isinstance(item_data, list) and len(item_data) >= 2: +- price = item_data[0] +- qty = item_data[1] +-- if isinstance(price, (int, float)) and isinstance(qty, int) and price > 0 and qty > 0: +-+ if ( +-+ isinstance(price, (int, float)) +-+ and isinstance(qty, int) +-+ and price > 0 +-+ and qty > 0 +-+ ): +- total += price * qty +- valid = True +- if valid and total > 0: +-@@ -1846,15 +1928,23 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): +- logger.info("Запуск функции handle_document_errors") +- # Проверяем, закрыт ли документ +- while device.checkDocumentClosed() < 0: +-- logger.warning(f"Не удалось проверить состояние документа: {device.errorDescription()}") +-+ logger.warning( +-+ f"Не удалось проверить состояние документа: {device.errorDescription()}" +-+ ) +- if retry_count >= max_retries - 1: +- if on_error: +-- on_error("Ошибка ККТ", f"Не удалось проверить состояние документа после {max_retries} попыток. " +-- f"Проверьте соединение с ККТ и не выключайте ПК.") +-+ on_error( +-+ "Ошибка ККТ", +-+ f"Не удалось проверить состояние документа после {max_retries} попыток. " +-+ f"Проверьте соединение с ККТ и не выключайте ПК.", +-+ ) +- return False +- +- if on_error: +-- on_error("Ошибка ККТ", f"Ошибка: {device.errorDescription()}. Повторная попытка...") +-+ on_error( +-+ "Ошибка ККТ", +-+ f"Ошибка: {device.errorDescription()}. Повторная попытка...", +-+ ) +- +- retry_count += 1 +- time.sleep(5) +-@@ -1868,12 +1958,17 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): +- except Exception as e: +- logger.critical(f"Ошибка при отмене чека: {e}") +- if on_error: +-- on_error("Критическая ошибка", "Не удалось отменить чек. Требуется перезагрузка ККТ.") +-+ on_error( +-+ "Критическая ошибка", +-+ "Не удалось отменить чек. Требуется перезагрузка ККТ.", +-+ ) +- # Пробрасываем исключение наверх +- raise +- +- if on_error: +-- on_error("Документ не закрыт", "Чек отменен. Требуется сформировать его заново.") +-+ on_error( +-+ "Документ не закрыт", "Чек отменен. Требуется сформировать его заново." +-+ ) +- return False +- +- # Если документ не напечатан — попробовать допечатать +-@@ -1884,12 +1979,19 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): +- logger.info("Документ успешно допечатан") +- return True +- +-- logger.warning(f"Попытка допечатки №{print_attempt + 1} не удалась: {device.errorDescription()}") +-+ logger.warning( +-+ f"Попытка допечатки №{print_attempt + 1} не удалась: {device.errorDescription()}" +-+ ) +- if on_error: +-- on_error("Ошибка печати", f"Ошибка допечатки: {device.errorDescription()}. Повторите попытку.") +-+ on_error( +-+ "Ошибка печати", +-+ f"Ошибка допечатки: {device.errorDescription()}. Повторите попытку.", +-+ ) +- time.sleep(3) +- +-- logger.error("Не удалось допечатать документ после 5 попыток. Он будет допечатан автоматически при следующей операции.") +-+ logger.error( +-+ "Не удалось допечатать документ после 5 попыток. Он будет допечатан автоматически при следующей операции." +-+ ) +- # Документ закрыт, но не допечатан — допустимо. +- # Документ будет автоматически допечатан при следующей печатной операции. +- +-@@ -1901,7 +2003,15 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): +- return True +- +- +--def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_status, on_error=None): +-+def check_open( +-+ sale_dict, +-+ payment_type, +-+ user, +-+ type_operation, +-+ print_check, +-+ bank_status, +-+ on_error=None, +-+): +- """ +- Проведение операции оплаты. +- +-@@ -1941,12 +2051,16 @@ def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_ +- setup_fptr(device, user, type_operation, print_check) +- +- # Проверка состояния чека +-- device.setParam(IFptr.LIBFPTR_PARAM_DATA_TYPE, IFptr.LIBFPTR_DT_RECEIPT_STATE) +-+ device.setParam( +-+ IFptr.LIBFPTR_PARAM_DATA_TYPE, IFptr.LIBFPTR_DT_RECEIPT_STATE +-+ ) +- device.queryData() +-- receipt_number = device.getParamInt(IFptr.LIBFPTR_PARAM_RECEIPT_NUMBER) +-- document_number = device.getParamInt(IFptr.LIBFPTR_PARAM_DOCUMENT_NUMBER) +-+ receipt_number = device.getParamInt(IFptr.LIBFPTR_PARAM_RECEIPT_NUMBER) +-+ document_number = device.getParamInt(IFptr.LIBFPTR_PARAM_DOCUMENT_NUMBER) +- receipt_type = device.getParamInt(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE) +-- logger.debug(f"receipt_type: {receipt_type}, receipt_number: {receipt_number}, document_number: {document_number}") +-+ logger.debug( +-+ f"receipt_type: {receipt_type}, receipt_number: {receipt_number}, document_number: {document_number}" +-+ ) +- if receipt_type != IFptr.LIBFPTR_RT_CLOSED: +- logger.error(f"Чек уже открыт! Тип: {receipt_type}") +- if on_error: +-@@ -1955,9 +2069,13 @@ def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_ +- +- # Установка типа чека +- if type_operation == 1: +-- device.setParam(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL) # Продажа +-+ device.setParam( +-+ IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL +-+ ) # Продажа +- elif type_operation == 2: +-- device.setParam(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL_RETURN) # Возврат +-+ device.setParam( +-+ IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL_RETURN +-+ ) # Возврат +- else: +- logger.error(f"Неизвестный тип операции: {type_operation}") +- return False +-@@ -1985,6 +2103,7 @@ def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_ +- on_error("Ошибка ККТ", f"Критическая ошибка: {str(e)}") +- return 0 +- +-+ +- @logger_wraps() +- def smena_close(user): +- """Закрытие кассовой смены. +-diff --git a/modules/progress_window.py b/modules/progress_window.py +-index c8e6d1e..660ac03 100644 +---- a/modules/progress_window.py +-+++ b/modules/progress_window.py +-@@ -1,5 +1,4 @@ +--from PySide6.QtCore import QPropertyAnimation, QEasingCurve +--from PySide6.QtCore import Qt +-+from PySide6.QtCore import QEasingCurve, QPropertyAnimation, Qt +- from PySide6.QtWidgets import QDialog +- +- from design.logic.progress_dialog import Ui_ProgressDialog +-diff --git a/modules/sale_logic.py b/modules/sale_logic.py +-index 5ba2434..dcadae4 100644 +---- a/modules/sale_logic.py +-+++ b/modules/sale_logic.py +-@@ -230,10 +230,10 @@ def calculate_itog() -> int: +- +- # Проверка на корректность данных +- if ( +-- system.sale_dict["detail"][0] < 0 +-- or system.sale_dict["detail"][2] < 0 +-- or system.sale_dict["detail"][1] < 0 +-- or system.sale_dict["detail"][3] < 0 +-+ system.sale_dict["detail"][0] < 0 +-+ or system.sale_dict["detail"][2] < 0 +-+ or system.sale_dict["detail"][1] < 0 +-+ or system.sale_dict["detail"][3] < 0 +- ): +- raise ValueError("Некорректные данные для расчета итоговой суммы") +- +-@@ -243,7 +243,9 @@ def calculate_itog() -> int: +- adults_no_discount_sum = adults_no_discount * system.sale_dict["price_adult"] +- +- # Взрослые со скидкой (detail[0] × detail[1]) +-- adults_with_discount_sum = system.sale_dict["detail"][0] * system.sale_dict["detail"][1] +-+ adults_with_discount_sum = ( +-+ system.sale_dict["detail"][0] * system.sale_dict["detail"][1] +-+ ) +- +- adult_total = adults_no_discount_sum + adults_with_discount_sum +- +-@@ -253,21 +255,33 @@ def calculate_itog() -> int: +- children_no_discount_sum = children_no_discount * system.sale_dict["price_child"] +- +- # Дети со скидкой (detail[2] × detail[3]) +-- children_with_discount_sum = system.sale_dict["detail"][2] * system.sale_dict["detail"][3] +-+ children_with_discount_sum = ( +-+ system.sale_dict["detail"][2] * system.sale_dict["detail"][3] +-+ ) +- +- child_total = children_no_discount_sum + children_with_discount_sum +- +- result = adult_total + child_total +- +-- logger.debug(f"Взрослые без скидки: {adults_no_discount} × {system.sale_dict['price_adult']} = {adults_no_discount_sum}") +-- logger.debug(f"Взрослые со скидкой: {system.sale_dict['detail'][0]} × {system.sale_dict['detail'][1]} = {adults_with_discount_sum}") +-+ logger.debug( +-+ f"Взрослые без скидки: {adults_no_discount} × {system.sale_dict['price_adult']} = {adults_no_discount_sum}" +-+ ) +-+ logger.debug( +-+ f"Взрослые со скидкой: {system.sale_dict['detail'][0]} × {system.sale_dict['detail'][1]} = {adults_with_discount_sum}" +-+ ) +- logger.debug(f"ИТОГО взрослые: {adult_total}") +- +-- logger.debug(f"Дети без скидки: {children_no_discount} × {system.sale_dict['price_child']} = {children_no_discount_sum}") +-- logger.debug(f"Дети со скидкой: {system.sale_dict['detail'][2]} × {system.sale_dict['detail'][3]} = {children_with_discount_sum}") +-+ logger.debug( +-+ f"Дети без скидки: {children_no_discount} × {system.sale_dict['price_child']} = {children_no_discount_sum}" +-+ ) +-+ logger.debug( +-+ f"Дети со скидкой: {system.sale_dict['detail'][2]} × {system.sale_dict['detail'][3]} = {children_with_discount_sum}" +-+ ) +- logger.debug(f"ИТОГО дети: {child_total}") +- +-- logger.debug(f"Расчет итого: {adult_total} (взрослые) + {child_total} (дети) = {result}") +-+ logger.debug( +-+ f"Расчет итого: {adult_total} (взрослые) + {child_total} (дети) = {result}" +-+ ) +- +- return result +- +-diff --git a/modules/system.py b/modules/system.py +-index 34c8fdf..2fe83ec 100644 +---- a/modules/system.py +-+++ b/modules/system.py +-@@ -12,7 +12,7 @@ from sqlalchemy import create_engine, select +- from sqlalchemy.exc import SQLAlchemyError +- from sqlalchemy.orm import Session +- +--from db.models import Holiday, Workday, Price, User, Sale +-+from db.models import Holiday, Price, Sale, User, Workday +- from modules.config import Config +- from modules.logger import logger, logger_wraps +- +-@@ -167,7 +167,9 @@ class System: +- logger.info(f"Успешная авторизация: {kassir.last_name}") +- return 1 +- else: +-- logger.warning(f"Неудачная попытка авторизации для пользователя {login}") +-+ logger.warning( +-+ f"Неудачная попытка авторизации для пользователя {login}" +-+ ) +- return 0 +- else: +- logger.warning(f"Пользователь {login} не найден") +-@@ -241,7 +243,7 @@ class System: +- db_prices[key] = default_prices[key] +- else: +- # Если записей нет, используем дефолтные значения для всех элементов +-- logger.warning(f"Используем дефолтные значения для прайс-листа") +-+ logger.warning("Используем дефолтные значения для прайс-листа") +- db_prices = default_prices +- +- # Устанавливаем цены, используя данные из db_prices или дефолтные значения +-@@ -410,24 +412,23 @@ class System: +- query = select(Sale.bank_pay).where(Sale.id == sale_id) +- load_slip = session.execute(query).scalars().one() +- try: +-- qr_match = re.search(r'Номер QR:\s+(\d+)', load_slip) +-+ qr_match = re.search(r"Номер QR:\s+(\d+)", load_slip) +- if qr_match: +-- last_digits = re.search(r'Карта:\s+\*+\s*(\d{4})', load_slip) +-- card_tail = last_digits.group(1) if last_digits else '' +-+ last_digits = re.search(r"Карта:\s+\*+\s*(\d{4})", load_slip) +-+ card_tail = last_digits.group(1) if last_digits else "" +- else: +-- last_digits = re.search(r'Карта:\(?E\d*\)?\s+\*+\s*(\d{4})', load_slip) +-- card_tail = last_digits.group(1) if last_digits else '' +-- merchant = re.search(r'М:(\d+)', load_slip) +-- merchant_id = merchant.group(1) if merchant else '' +-- rrn = re.search(r'RRN:\s+(\d+)', load_slip) +-- rrn_value = rrn.group(1) if rrn else '' +-+ last_digits = re.search(r"Карта:\(?E\d*\)?\s+\*+\s*(\d{4})", load_slip) +-+ card_tail = last_digits.group(1) if last_digits else "" +-+ merchant = re.search(r"М:(\d+)", load_slip) +-+ merchant_id = merchant.group(1) if merchant else "" +-+ rrn = re.search(r"RRN:\s+(\d+)", load_slip) +-+ rrn_value = rrn.group(1) if rrn else "" +- except Exception as e: +- logger.warning(f"Ошибка при разборе чека: {e}") +-- card_tail = '' +-- merchant_id = '' +-- rrn_value = '' +-- return card_tail, merchant_id, rrn_value, load_slip +-- +-+ card_tail = "" +-+ merchant_id = "" +-+ rrn_value = "" +-+ return card_tail, merchant_id, rrn_value, load_slip +- +- def check_db_connection(self) -> bool: +- """Проверка подключения к базе данных.""" +-@@ -439,4 +440,4 @@ class System: +- return True +- except Exception as e: +- logger.error(f"Ошибка подключения к БД: {e}") +-- return False +-\ No newline at end of file +-+ return False +-diff --git a/modules/worker.py b/modules/worker.py +-index d83a6e7..c530f5f 100644 +---- a/modules/worker.py +-+++ b/modules/worker.py +-@@ -1,7 +1,8 @@ +- import datetime as dt +- from functools import wraps +- +--from PySide6.QtCore import QElapsedTimer, QObject, Signal, QMetaObject, Qt, QTimer, QEventLoop +-+from PySide6.QtCore import (QElapsedTimer, QEventLoop, QMetaObject, QObject, +-+ Qt, QTimer, Signal) +- from sqlalchemy import update +- +- from db.models import Sale +-@@ -10,16 +11,19 @@ from modules.logger import logger +- +- def with_timer(func): +- """Декоратор для автоматического создания QElapsedTimer""" +-+ +- @wraps(func) +- def wrapper(self, *args, **kwargs): +- timer = QElapsedTimer() +- timer.start() +- return func(self, timer, *args, **kwargs) +-+ +- return wrapper +- +- +- class BaseWorker(QObject): +- """Базовый класс с общими методами""" +-+ +- DEFAULT_DELAY_MS = 15 +- +- progress_updated = Signal(str, int) +-@@ -35,7 +39,9 @@ class BaseWorker(QObject): +- elapsed = timer.elapsed() +- logger.debug(f"[TIMER] {step_name} — {elapsed} ms") +- +-- def delayed_progress_update(self, step_text: str, progress_percent: int, delay_ms: int = DEFAULT_DELAY_MS): +-+ def delayed_progress_update( +-+ self, step_text: str, progress_percent: int, delay_ms: int = DEFAULT_DELAY_MS +-+ ): +- if delay_ms < 0: +- delay_ms = 0 +- if delay_ms > 0: +-@@ -58,9 +64,15 @@ class BaseWorker(QObject): +- Qt.QueuedConnection, +- ) +- +-- def emit_error_and_finish(self, title: str, message: str, code: str = "", +-- timer: QElapsedTimer = None, step_name: str = "", +-- close_window: bool = True): +-+ def emit_error_and_finish( +-+ self, +-+ title: str, +-+ message: str, +-+ code: str = "", +-+ timer: QElapsedTimer = None, +-+ step_name: str = "", +-+ close_window: bool = True, +-+ ): +- """ +- Универсальный метод обработки ошибок +- :param title: Заголовок ошибки +-@@ -87,11 +99,13 @@ class BaseWorker(QObject): +- logger.warning("Попытка emit после удаления объекта — игнорируем") +- else: +- raise +-- except Exception as e: +-+ except Exception: +- logger.exception("Неожиданная ошибка в emit_error_and_finish") +- +-+ +- class PaymentHandler: +- """Обработчик банковских платежей""" +-+ +- def __init__(self, worker, pq, payment_type, amount, dev_mode=False): +- self.worker = worker +- self.pq = pq +-@@ -107,7 +121,9 @@ class PaymentHandler: +- +- if self.dev_mode: +- self.worker.logger.info("РЕЖИМ ОТЛАДКИ: Имитация оплаты картой") +-- self.worker.logger.debug(f"Сумма: {self.amount}, Тип оплаты: {self.payment_type}") +-+ self.worker.logger.debug( +-+ f"Сумма: {self.amount}, Тип оплаты: {self.payment_type}" +-+ ) +- return True, 1 # (success, payment_type) +- +- try: +-@@ -116,7 +132,7 @@ class PaymentHandler: +- self.payment_type, +- self.amount, +- self.worker.progress_updated, +-- error_callback=self._handle_terminal_error_callback +-+ error_callback=self._handle_terminal_error_callback, +- ) +- self.worker.log_step(timer, "pq.universal_terminal_operation finished") +- +-@@ -134,19 +150,21 @@ class PaymentHandler: +- def get_mock_slip(self): +- """Генерация тестового слип-чека для режима отладки""" +- from datetime import datetime +-- return (f"DEBUG_SLIP\n" +-- f"Дата: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}\n" +-- f"Сумма: {self.amount} руб.\n" +-- f"Тип: {'Онлайн' if self.payment_type == 101 else 'Оффлайн'}\n" +-- f"СТАНДАРТНЫЙ ЧЕК ДЛЯ ТЕСТИРОВАНИЯ") +- +-- def _handle_terminal_error_callback(self, title: str, message: str, code: int) -> None: +-+ return ( +-+ f"DEBUG_SLIP\n" +-+ f"Дата: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}\n" +-+ f"Сумма: {self.amount} руб.\n" +-+ f"Тип: {'Онлайн' if self.payment_type == 101 else 'Оффлайн'}\n" +-+ f"СТАНДАРТНЫЙ ЧЕК ДЛЯ ТЕСТИРОВАНИЯ" +-+ ) +-+ +-+ def _handle_terminal_error_callback( +-+ self, title: str, message: str, code: int +-+ ) -> None: +- """Callback для обработки ошибок от терминала""" +- self.worker.emit_error_and_finish( +-- title=title, +-- message=message, +-- code=str(code), +-- step_name="terminal_error" +-+ title=title, message=message, code=str(code), step_name="terminal_error" +- ) +- +- def _handle_payment_failed(self, timer): +-@@ -155,7 +173,7 @@ class PaymentHandler: +- title="Ошибка", +- message="Операция оплаты не удалась. Повторите попытку.", +- timer=timer, +-- step_name="payment_failed" +-+ step_name="payment_failed", +- ) +- +- def _handle_payment_exception(self, timer, exception): +-@@ -165,12 +183,13 @@ class PaymentHandler: +- message="Не удалось выполнить операцию на терминале.", +- code=str(exception), +- timer=timer, +-- step_name="payment_exception" +-+ step_name="payment_exception", +- ) +- +- +- class CheckHandler: +- """Обработчик печати чеков""" +-+ +- def __init__(self, worker, pq): +- self.worker = worker +- self.pq = pq +-@@ -184,25 +203,28 @@ class CheckHandler: +- type_operation=1, +- print_check=print_check, +- bank_status=bank_data, +-- on_error=self.handle_check_error +-+ on_error=self.handle_check_error, +- ) +- +- return state_check == 1 +- +-- def handle_check_error(self, title="Неизвестная ошибка", text="Произошла неизвестная ошибка"): +-+ def handle_check_error( +-+ self, title="Неизвестная ошибка", text="Произошла неизвестная ошибка" +-+ ): +- """Обработчик ошибок печати чека""" +- logger.error(f"handle_check_error вызван с title={title}, text={text}") +- self.worker.emit_error_and_finish( +- title="Ошибка ККМ", +- message="Документ не закрылся после 5 попыток. Отмена кассового чека.", +- code="critical", +-- step_name="check_error" +-+ step_name="check_error", +- ) +- # raise Exception(f"{title}: {text}") +- +- +- class DatabaseHandler: +- """Обработчик работы с базой данных""" +-+ +- def __init__(self, Session, engine): +- self.Session = Session +- self.engine = engine +-@@ -211,7 +233,9 @@ class DatabaseHandler: +- """Проверяет существование продажи по ID""" +- try: +- with self.Session(self.engine) as session: +-- return session.query(Sale).filter(Sale.id == sale_id).first() is not None +-+ return ( +-+ session.query(Sale).filter(Sale.id == sale_id).first() is not None +-+ ) +- except Exception as e: +- logger.error(f"Ошибка проверки существования продажи {sale_id}: {e}") +- return False +-@@ -219,17 +243,16 @@ class DatabaseHandler: +- def update_sale(self, sale_id, **values): +- """Общее сохранение данных о продаже""" +- with self.Session(self.engine) as session: +-- session.execute( +-- update(Sale) +-- .where(Sale.id == sale_id) +-- .values(**values) +-- ) +-+ session.execute(update(Sale).where(Sale.id == sale_id).values(**values)) +- session.commit() +- +- +- class TransactionWorker(BaseWorker): +- """Основной класс обработки транзакций""" +-- def __init__(self, payment_type, print_check, system, pq, Session, main_window, parent=None): +-+ +-+ def __init__( +-+ self, payment_type, print_check, system, pq, Session, main_window, parent=None +-+ ): +- super().__init__(parent) +- self.payment_type = payment_type +- self.print_check = print_check +-@@ -238,7 +261,9 @@ class TransactionWorker(BaseWorker): +- self.Session = Session +- self.main_window = main_window +- # Флаг режима отладки +-- self.dev_mode = getattr(main_window, 'dev_mode', False) if main_window else False +-+ self.dev_mode = ( +-+ getattr(main_window, "dev_mode", False) if main_window else False +-+ ) +- # Флаг cleanup +- self._is_cleaned = False +- +-@@ -262,14 +287,16 @@ class TransactionWorker(BaseWorker): +- @with_timer +- def process_special_sale(self, timer: QElapsedTimer, progress_percent: int): +- """Обработка специальной продажи""" +-- self.delayed_progress_update("Печатаем билеты (особая продажа)...", progress_percent) +-+ self.delayed_progress_update( +-+ "Печатаем билеты (особая продажа)...", progress_percent +-+ ) +- self.invoke_main_window_method("print_saved_tickets") +- self.log_step(timer, "print_saved_tickets finished") +- self.system.sale_status = 0 +- +- def process_payment(self, timer: QElapsedTimer): +- """Обработка платежа""" +-- payment = 2 # по умолчанию наличные +-+ payment = 2 # по умолчанию наличные +- bank_status = 0 +- +- # Проверяем, что sale_id существует +-@@ -278,17 +305,19 @@ class TransactionWorker(BaseWorker): +- self.emit_error_and_finish( +- title="Критическая ошибка", +- message="Продажа не была создана в БД", +-- code="SALE_ID_NONE" +-+ code="SALE_ID_NONE", +- ) +- return None, None +- +- # Проверяем существует ли продажа в БД? +- if not self.db_handler.sale_exists(self.system.sale_id): +-- logger.warning(f"Продажа {self.system.sale_id} есть в памяти, но отсутствует в БД!") +-+ logger.warning( +-+ f"Продажа {self.system.sale_id} есть в памяти, но отсутствует в БД!" +-+ ) +- self.emit_error_and_finish( +- title="Критическая ошибка", +- message="Продажа не найдена в БД после сохранения", +-- code="SALE_NOT_IN_DB" +-+ code="SALE_NOT_IN_DB", +- ) +- return None, None +- +-@@ -318,7 +347,9 @@ class TransactionWorker(BaseWorker): +- bank_status = 1 +- # Обязательно сохраняем банковский слип-чек +- try: +-- self.db_handler.update_sale(self.system.sale_id, bank_pay=check, status=9) +-+ self.db_handler.update_sale( +-+ self.system.sale_id, bank_pay=check, status=9 +-+ ) +- except Exception as e: +- logger.warning(f"Ошибка сохранения банковского чека: {e}") +- self.log_step(timer, "save in db finished") +-@@ -335,7 +366,9 @@ class TransactionWorker(BaseWorker): +- self.delayed_progress_update("Печатаем кассовый чек...", 60) +- +- logger.debug(f"ДО check_open: sale_dict = {self.system.sale_dict}") +-- logger.debug(f"exclude_from_sale={self.system.exclude_from_sale}, sale_checkbox_row={self.system.sale_checkbox_row}") +-+ logger.debug( +-+ f"exclude_from_sale={self.system.exclude_from_sale}, sale_checkbox_row={self.system.sale_checkbox_row}" +-+ ) +- +- try: +- if not self.check_handler.print_check( +-@@ -344,7 +377,7 @@ class TransactionWorker(BaseWorker): +- self.system.user, +- self.print_check, +- self.system.sale_dict["detail"][7], +-- bank_data +-+ bank_data, +- ): +- # В случае ошибки внутри print_check должен быть вызван on_error +- return False +-@@ -394,7 +427,7 @@ class TransactionWorker(BaseWorker): +- code=str(error), +- timer=timer, +- step_name="transaction_error", +-- close_window=True +-+ close_window=True, +- ) +- self.system.sale_status = 0 +- +-diff --git a/tests/test_auth_logic.py b/tests/test_auth_logic.py +-index 1c86dbd..0b329af 100644 +---- a/tests/test_auth_logic.py +-+++ b/tests/test_auth_logic.py +-@@ -1,18 +1,19 @@ +-+import datetime as dt +- import unittest +- from unittest.mock import MagicMock, patch +--import datetime as dt +- +- from modules.auth_logic import perform_pre_sale_checks +- from modules.system import System +- +-+ +- class TestAuthLogic(unittest.TestCase): +- +- def setUp(self): +- # Prevent System from trying to connect to a real DB +-- patcher = patch('modules.system.create_engine') +-+ patcher = patch("modules.system.create_engine") +- self.mock_create_engine = patcher.start() +- self.addCleanup(patcher.stop) +-- +-+ +- self.system = System() +- self.system.what_a_day = None +- self.system.num_of_week = None +-@@ -30,48 +31,52 @@ class TestAuthLogic(unittest.TestCase): +- +- result = perform_pre_sale_checks("valid_login", "valid_password") +- self.assertEqual(result, 1) +-- self.system.user_authorization.assert_called_once_with("valid_login", "valid_password") +-+ self.system.user_authorization.assert_called_once_with( +-+ "valid_login", "valid_password" +-+ ) +- +- def test_perform_pre_sale_checks_authorization_failure(self): +- self.system.user_authorization = MagicMock(return_value=0) +- +- result = perform_pre_sale_checks("invalid_login", "invalid_password") +- self.assertEqual(result, 0) +-- self.system.user_authorization.assert_called_once_with("invalid_login", "invalid_password") +-+ self.system.user_authorization.assert_called_once_with( +-+ "invalid_login", "invalid_password" +-+ ) +- +-- @patch('modules.auth_logic.dt') +-+ @patch("modules.auth_logic.dt") +- def test_check_day_status_weekday(self, mock_dt): +-- mock_date = dt.datetime(2024, 11, 6) # Wednesday +-+ mock_date = dt.datetime(2024, 11, 6) # Wednesday +- mock_dt.datetime.today.return_value = mock_date +-- +-+ +- self.system.user_authorization = MagicMock(return_value=1) +- self.system.get_price = MagicMock() +- self.system.check_day = MagicMock(return_value=0) +-- +-+ +- result = perform_pre_sale_checks("valid_login", "valid_password") +- self.assertEqual(result, 1) +- self.assertEqual(self.system.what_a_day, 0) +- +-- @patch('modules.auth_logic.dt') +-+ @patch("modules.auth_logic.dt") +- def test_check_day_status_weekend(self, mock_dt): +-- mock_date = dt.datetime(2024, 11, 2) # Saturday +-+ mock_date = dt.datetime(2024, 11, 2) # Saturday +- mock_dt.datetime.today.return_value = mock_date +- +- self.system.user_authorization = MagicMock(return_value=1) +- self.system.get_price = MagicMock() +- self.system.check_day = MagicMock(return_value=1) +-- +-+ +- result = perform_pre_sale_checks("valid_login", "valid_password") +- self.assertEqual(result, 1) +- self.assertEqual(self.system.what_a_day, 1) +- +-- @patch('modules.auth_logic.dt') +-+ @patch("modules.auth_logic.dt") +- def test_sunday_for_large_families_yes(self, mock_dt): +- mock_date = MagicMock() +- mock_date.isoweekday.return_value = 7 +- mock_date.day = 3 +- mock_dt.datetime.today.return_value = mock_date +-- +-+ +- self.system.user_authorization = MagicMock(return_value=1) +- self.system.get_price = MagicMock() +- self.system.check_day = MagicMock(return_value=0) +-@@ -80,13 +85,13 @@ class TestAuthLogic(unittest.TestCase): +- self.assertEqual(result, 1) +- self.assertEqual(self.system.sunday, 1) +- +-- @patch('modules.auth_logic.dt') +-+ @patch("modules.auth_logic.dt") +- def test_sunday_for_large_families_no_not_sunday(self, mock_dt): +- mock_date = MagicMock() +- mock_date.isoweekday.return_value = 1 +- mock_date.day = 4 +- mock_dt.datetime.today.return_value = mock_date +-- +-+ +- self.system.user_authorization = MagicMock(return_value=1) +- self.system.get_price = MagicMock() +- self.system.check_day = MagicMock(return_value=0) +-@@ -95,13 +100,13 @@ class TestAuthLogic(unittest.TestCase): +- self.assertEqual(result, 1) +- self.assertEqual(self.system.sunday, 0) +- +-- @patch('modules.auth_logic.dt') +-+ @patch("modules.auth_logic.dt") +- def test_sunday_for_large_families_no_not_first_week(self, mock_dt): +- mock_date = MagicMock() +- mock_date.isoweekday.return_value = 7 +- mock_date.day = 10 +- mock_dt.datetime.today.return_value = mock_date +-- +-+ +- self.system.user_authorization = MagicMock(return_value=1) +- self.system.get_price = MagicMock() +- self.system.check_day = MagicMock(return_value=0) +-@@ -110,13 +115,13 @@ class TestAuthLogic(unittest.TestCase): +- self.assertEqual(result, 1) +- self.assertEqual(self.system.sunday, 0) +- +-- @patch('modules.auth_logic.dt') +-+ @patch("modules.auth_logic.dt") +- def test_week_and_month_day_assignment(self, mock_dt): +- mock_date = MagicMock() +- mock_date.isoweekday.return_value = 3 +- mock_date.day = 6 +- mock_dt.datetime.today.return_value = mock_date +-- +-+ +- self.system.user_authorization = MagicMock(return_value=1) +- self.system.get_price = MagicMock() +- self.system.check_day = MagicMock(return_value=0) +-@@ -126,5 +131,6 @@ class TestAuthLogic(unittest.TestCase): +- self.assertEqual(result, 1) +- self.assertEqual(self.system.num_of_week, 3) +- +--if __name__ == '__main__': +-+ +-+if __name__ == "__main__": +- unittest.main() +-diff --git a/tests/test_config.py b/tests/test_config.py +-index a7b68f2..c076501 100644 +---- a/tests/test_config.py +-+++ b/tests/test_config.py +-@@ -1,8 +1,10 @@ +--import unittest +- import os +- import tempfile +-+import unittest +-+ +- from modules.config import Config +- +-+ +- class TestConfig(unittest.TestCase): +- +- def setUp(self): +-@@ -120,7 +122,9 @@ available = on +- +- with self.assertRaises(RuntimeError) as context: +- Config(config_file=self.config_path) +-- self.assertIn("Неизвестная ошибка: Отсутствует параметр 'pc_2'", str(context.exception)) +-+ self.assertIn( +-+ "Неизвестная ошибка: Отсутствует параметр 'pc_2'", str(context.exception) +-+ ) +- +- def test_empty_pc_name(self): +- invalid_content = """ +-@@ -155,7 +159,8 @@ available = on +- Config(config_file=self.config_path) +- self.assertIn("Неизвестная ошибка: Пустое значение", str(context.exception)) +- +--if __name__ == '__main__': +-+ +-+if __name__ == "__main__": +- unittest.main() +- +- def test_missing_pc_name_empty(self): +-@@ -198,4 +203,3 @@ available = on +- +- with self.assertRaises(RuntimeError): +- Config(config_file=self.config_path) +-- +-diff --git a/tests/test_logger.py b/tests/test_logger.py +-index cc28d7a..fe00b9a 100644 +---- a/tests/test_logger.py +-+++ b/tests/test_logger.py +-@@ -1,14 +1,14 @@ +- import unittest +- from modules.logger import logger_wraps, logger +--from unittest.mock import patch, MagicMock +-+from unittest.mock import patch +- +- class TestLogger(unittest.TestCase): +- def test_logger_wraps_success(self): +- @logger_wraps() +- def test_func(): +- return "success" +-- +-- with patch.object(logger, 'info') as mock_info: +-+ +-+ with patch.object(logger, 'info'): +- res = test_func() +- self.assertEqual(res, "success") +- +-@@ -16,8 +16,8 @@ class TestLogger(unittest.TestCase): +- @logger_wraps() +- def test_func(): +- raise ValueError("Test Error") +-- +-- with patch.object(logger, 'exception') as mock_exc: +-+ +-+ with patch.object(logger, 'exception'): +- with self.assertRaises(ValueError): +- test_func() +- +-@@ -25,8 +25,8 @@ class TestLogger(unittest.TestCase): +- @logger_wraps(entry=False, exit=False) +- def test_func(): +- return "success" +-- +-- with patch.object(logger, 'info') as mock_info: +-+ +-+ with patch.object(logger, 'info'): +- res = test_func() +- self.assertEqual(res, "success") +- +-diff --git a/tests/test_otchet.py b/tests/test_otchet.py +-index ec980b6..dd49885 100644 +---- a/tests/test_otchet.py +-+++ b/tests/test_otchet.py +-@@ -1,35 +1,34 @@ +-+import sys +- import unittest +--from unittest.mock import patch, MagicMock +--from datetime import datetime +-+from unittest.mock import MagicMock, patch +-+ +-+sys.modules["reportlab"] = MagicMock() +-+sys.modules["reportlab.lib"] = MagicMock() +-+sys.modules["reportlab.lib.colors"] = MagicMock() +-+sys.modules["reportlab.lib.pagesizes"] = MagicMock() +-+sys.modules["reportlab.lib.pagesizes"].landscape = lambda x: x +-+sys.modules["reportlab.lib.pagesizes"].A4 = "A4" +-+sys.modules["reportlab.lib.pagesizes"].letter = "letter" +-+sys.modules["reportlab.lib.units"] = MagicMock() +-+sys.modules["reportlab.lib.units"].mm = 1 +-+sys.modules["reportlab.lib.units"].inch = 1 +-+sys.modules["reportlab.pdfbase"] = MagicMock() +-+sys.modules["reportlab.pdfbase.pdfmetrics"] = MagicMock() +-+sys.modules["reportlab.pdfbase.ttfonts"] = MagicMock() +-+sys.modules["reportlab.pdfgen"] = MagicMock() +-+sys.modules["reportlab.platypus"] = MagicMock() +-+sys.modules["reportlab.platypus"].Table = MagicMock() +-+sys.modules["reportlab.platypus"].TableStyle = MagicMock() +-+ +-+# isort: split +-+import modules.otchet as otchet # noqa: E402 +-+ +-+# isort: split +-+from modules.otchet import (format_date_range, # noqa: E402 +-+ generate_ticket_report_table, get_ticket_type, +-+ process_sales_and_returns, process_ticket_stats, +-+ safe_int) +- +--import sys +--sys.modules['reportlab'] = MagicMock() +--sys.modules['reportlab.lib'] = MagicMock() +--sys.modules['reportlab.lib.colors'] = MagicMock() +--sys.modules['reportlab.lib.pagesizes'] = MagicMock() +--sys.modules['reportlab.lib.pagesizes'].landscape = lambda x: x +--sys.modules['reportlab.lib.pagesizes'].A4 = "A4" +--sys.modules['reportlab.lib.pagesizes'].letter = "letter" +--sys.modules['reportlab.lib.units'] = MagicMock() +--sys.modules['reportlab.lib.units'].mm = 1 +--sys.modules['reportlab.lib.units'].inch = 1 +--sys.modules['reportlab.pdfbase'] = MagicMock() +--sys.modules['reportlab.pdfbase.pdfmetrics'] = MagicMock() +--sys.modules['reportlab.pdfbase.ttfonts'] = MagicMock() +--sys.modules['reportlab.pdfgen'] = MagicMock() +--sys.modules['reportlab.platypus'] = MagicMock() +--sys.modules['reportlab.platypus'].Table = MagicMock() +--sys.modules['reportlab.platypus'].TableStyle = MagicMock() +-- +--import modules.otchet as otchet +--from modules.otchet import ( +-- get_ticket_type, +-- generate_ticket_report_table, +-- safe_int, +-- format_date_range, +-- process_sales_and_returns, +-- process_ticket_stats +--) +- +- class TestOtchet(unittest.TestCase): +- +-@@ -43,7 +42,7 @@ class TestOtchet(unittest.TestCase): +- +- with self.assertRaises(ValueError): +- get_ticket_type(-1) +-- +-+ +- with self.assertRaises(ValueError): +- get_ticket_type("invalid") +- +-@@ -65,22 +64,27 @@ class TestOtchet(unittest.TestCase): +- +- def test_generate_ticket_report_table(self): +- summary = { +-- "Взрослый, 1 ч.": { +-- 250: {"count": 2, "total_price": 500} +-- }, +-- "Детский, 2 ч.": { +-- 500: {"count": 1, "total_price": 500} +-- } +-+ "Взрослый, 1 ч.": {250: {"count": 2, "total_price": 500}}, +-+ "Детский, 2 ч.": {500: {"count": 1, "total_price": 500}}, +- } +- table = generate_ticket_report_table(summary) +-- +-- self.assertEqual(table[0], ["№\n п/п", "Тип\nбилета", "Цена,\n руб.", "Количество,\n шт.", "Стоимость,\n руб."]) +-+ +-+ self.assertEqual( +-+ table[0], +-+ [ +-+ "№\n п/п", +-+ "Тип\nбилета", +-+ "Цена,\n руб.", +-+ "Количество,\n шт.", +-+ "Стоимость,\n руб.", +-+ ], +-+ ) +- self.assertIn(["1", "Взрослый, 1 ч.", 250, 2, 500], table) +- self.assertEqual(table[-1][1], "Итого билетов") +- self.assertEqual(table[-1][3], 3) +- self.assertEqual(table[-1][4], 1000) +- +-- @patch('modules.otchet.system') +-+ @patch("modules.otchet.system") +- def test_process_sales_and_returns(self, mock_system): +- mock_system.pcs = ["PC1", "PC2"] +- sales = [("PC1", 1, 1000), ("PC1", 2, 500), ("PC2", 1, 200)] +-@@ -95,7 +99,7 @@ class TestOtchet(unittest.TestCase): +- self.assertEqual(total_row[0], "Итого") +- self.assertEqual(total_row[1], 1200) +- +-- @patch('modules.otchet.system') +-+ @patch("modules.otchet.system") +- def test_process_ticket_stats(self, mock_system): +- tickets = [ +- (0, 1, "-", None, None, 100), +-@@ -109,38 +113,40 @@ class TestOtchet(unittest.TestCase): +- adult_stats = next(r for r in result if r[0] == "взрослый")[1] +- self.assertEqual(adult_stats["sum"], 1) +- +-- @patch('modules.otchet.canvas.Canvas') +-+ @patch("modules.otchet.canvas.Canvas") +- def test_otchet_administratora(self, mock_canvas_class): +- mock_canvas_inst = MagicMock() +- mock_canvas_class.return_value = mock_canvas_inst +-- +-+ +- values = {"Взрослый, 1 ч.": {250: {"count": 2, "total_price": 500}}} +-- otchet.otchet_administratora("2023-10-01 12:00:00", "2023-10-02 15:30:00", values) +-- +-+ otchet.otchet_administratora( +-+ "2023-10-01 12:00:00", "2023-10-02 15:30:00", values +-+ ) +-+ +- self.assertTrue(mock_canvas_class.called) +- self.assertTrue(mock_canvas_inst.save.called) +- +-- @patch('modules.otchet.canvas.Canvas') +-+ @patch("modules.otchet.canvas.Canvas") +- def test_otchet_kassira(self, mock_canvas_class): +- mock_canvas_inst = MagicMock() +- mock_canvas_class.return_value = mock_canvas_inst +-- +-+ +- val = [1000, 500, 100, 50] +- kassir = MagicMock() +- kassir.last_name = "Иванов" +- kassir.first_name = "Иван" +- kassir.middle_name = "Иванович" +-- +-+ +- otchet.otchet_kassira(val, "2023-10-01 12:00:00", "2023-10-02 15:30:00", kassir) +- self.assertTrue(mock_canvas_class.called) +- self.assertTrue(mock_canvas_inst.save.called) +- +-- @patch('modules.otchet.canvas.Canvas') +-- @patch('modules.otchet.system.load_coordinates') +-+ @patch("modules.otchet.canvas.Canvas") +-+ @patch("modules.otchet.system.load_coordinates") +- def test_generate_saved_tickets(self, mock_load_coords, mock_canvas_class): +- mock_canvas_inst = MagicMock() +- mock_canvas_class.return_value = mock_canvas_inst +-- +-+ +- mock_load_coords.return_value = { +- "name": {"x": 10, "y": 20}, +- "surname": {"x": 10, "y": 20}, +-@@ -154,15 +160,14 @@ class TestOtchet(unittest.TestCase): +- "ticket_type": {"x": 10, "y": 20}, +- "notes": {"x": 10, "y": 20}, +- "talents": {"x": 10, "y": 20}, +-- "qr_code": {"x": 10, "y": 20} +-+ "qr_code": {"x": 10, "y": 20}, +- } +-- +-- values = [ +-- ("Иванов", "Иван", 1, 500, "-", 1, 10, 3, 50, "2023-10-01") +-- ] +-+ +-+ values = [("Иванов", "Иван", 1, 500, "-", 1, 10, 3, 50, "2023-10-01")] +- otchet.generate_saved_tickets(values) +- self.assertTrue(mock_canvas_class.called) +- self.assertTrue(mock_canvas_inst.save.called) +- +--if __name__ == '__main__': +-+ +-+if __name__ == "__main__": +- unittest.main() +-diff --git a/tests/test_payment_equipment.py b/tests/test_payment_equipment.py +-index be3c476..6187732 100644 +---- a/tests/test_payment_equipment.py +-+++ b/tests/test_payment_equipment.py +-@@ -1,3 +1,4 @@ +-+import subprocess +- import unittest +- from unittest.mock import patch, MagicMock, mock_open +- +-@@ -57,7 +58,7 @@ class FakeIFptr: +- LIBFPTR_PARAM_RECEIPT_NUMBER = 1 +- LIBFPTR_RT_CLOSED = 0 +- LIBFPTR_RT_CLOSE_SHIFT = 1 +-- +-+ +- def __init__(self, *args): pass +- +- fake_libfptr10.IFptr = FakeIFptr +-@@ -74,9 +75,9 @@ class TestPE(unittest.TestCase): +- mock_fptr = MagicMock() +- mock_fptr.open.return_value = None +- mock_fptr.getParamInt.side_effect = [1, 100] +-- +-+ +- with patch("modules.payment_equipment.fptr", mock_fptr): +-- with patch("modules.payment_equipment.windows.info_window") as mock_info: +-+ with patch("modules.payment_equipment.windows.info_window"): +- res = pe.smena_info() +- self.assertEqual(res, 1) +- +-@@ -91,7 +92,7 @@ class TestPE(unittest.TestCase): +- with patch("modules.payment_equipment.fptr", mock_fptr): +- pe.report_payment() +- mock_fptr.report.assert_called_once() +-- +-+ +- def test_report_x(self): +- mock_fptr = MagicMock() +- with patch("modules.payment_equipment.fptr", mock_fptr): +-@@ -101,22 +102,22 @@ class TestPE(unittest.TestCase): +- def test_deposit_of_money(self): +- mock_fptr = MagicMock() +- with patch("modules.payment_equipment.fptr", mock_fptr): +-- with patch("modules.payment_equipment.windows.info_window") as mock_info: +-+ with patch("modules.payment_equipment.windows.info_window"): +- pe.deposit_of_money(100.0) +- mock_fptr.cashIncome.assert_called_once() +- +- def test_payment(self): +- mock_fptr = MagicMock() +- with patch("modules.payment_equipment.fptr", mock_fptr): +-- with patch("modules.payment_equipment.windows.info_window") as mock_info: +-+ with patch("modules.payment_equipment.windows.info_window"): +- pe.payment(100.0) +- mock_fptr.cashOutcome.assert_called_once() +-- +-+ +- def test_balance_check(self): +- mock_fptr = MagicMock() +- mock_fptr.getParamDouble.return_value = 500.0 +- with patch("modules.payment_equipment.fptr", mock_fptr): +-- with patch("modules.payment_equipment.windows.info_window") as mock_info: +-+ with patch("modules.payment_equipment.windows.info_window"): +- res = pe.balance_check() +- self.assertEqual(res, 500.0) +- self.assertEqual(mock_fptr.printText.call_count, 2) +-@@ -129,7 +130,7 @@ class TestPE(unittest.TestCase): +- mock_popen.return_value = mock_process +- pe.run_terminal_command("1") +- mock_popen.assert_called_once() +-- +-+ +- def test_check_terminal_file(self): +- with patch("builtins.open", mock_open(read_data="УСПЕШНО")): +- res = pe.check_terminal_file("УСПЕШНО") +-@@ -140,7 +141,7 @@ class TestPE(unittest.TestCase): +- with patch("modules.payment_equipment.windows.info_window"): +- res = pe.process_success_result() +- self.assertEqual(res, pe.TERMINAL_SUCCESS_CODE) +-- +-+ +- def test_handle_error(self): +- with patch("modules.payment_equipment.windows.info_window"): +- res = pe.handle_error(1, "title", "msg", None) +-@@ -166,13 +167,13 @@ class TestPE(unittest.TestCase): +- with patch("modules.payment_equipment.check_terminal_file", return_value=True): +- res = pe.terminal_check_itog() +- self.assertEqual(res, 1) +-- +-+ +- def test_terminal_menu(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +- res = pe.terminal_menu() +- self.assertIsNone(res) +-- +-+ +- def test_terminal_check_itog_window(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +-@@ -180,14 +181,14 @@ class TestPE(unittest.TestCase): +- with patch("modules.payment_equipment.windows.info_window") as mock_info: +- pe.terminal_check_itog_window() +- mock_info.assert_called_once() +-- +-+ +- def test_terminal_svod_check(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +- with patch("modules.payment_equipment.print_pinpad_check"): +- res = pe.terminal_svod_check() +- self.assertIsNone(res) +-- +-+ +- def test_terminal_control_lenta(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +-@@ -208,7 +209,7 @@ class TestPE(unittest.TestCase): +- with patch("modules.payment_equipment.print_pinpad_check"): +- res = pe.terminal_copy_last_check() +- self.assertIsNone(res) +-- +-+ +- def test_is_kkt_connected(self): +- pe.kkt_available = True +- mock_fptr = MagicMock() +-@@ -275,20 +276,20 @@ if __name__ == '__main__': +- with patch("modules.payment_equipment.kkt_available", False): +- with pe.fptr_connection(pe.fptr) as conn: +- self.assertIsNone(conn) +-- +-+ +- def test_kkt_connection_not_opened(self): +- mock_device = MagicMock() +- mock_device.isOpened.return_value = False +- with pe.fptr_connection(mock_device) as conn: +- self.assertIsNone(conn) +- mock_device.open.assert_called_once() +-- +-+ +- def test_kkt_connection_exception_on_open(self): +- mock_device = MagicMock() +- mock_device.open.side_effect = Exception("Open failed") +- with pe.fptr_connection(mock_device) as conn: +- self.assertIsNone(conn) +-- +-+ +- def test_kkt_connection_success(self): +- mock_device = MagicMock() +- mock_device.isOpened.return_value = True +-@@ -302,20 +303,20 @@ if __name__ == '__main__': +- mock_process = MagicMock() +- mock_process.communicate.side_effect = [subprocess.TimeoutExpired(cmd="", timeout=1), (b"", b"")] +- mock_popen.return_value = mock_process +-- res = pe.run_terminal_command("1", timeout=1) +-+ pe.run_terminal_command("1", timeout=1) +- mock_process.terminate.assert_called_once() +-- +-+ +- def test_run_terminal_command_timeout_twice(self): +- with patch("subprocess.Popen") as mock_popen, \ +- patch("os.path.isfile", return_value=True): +- mock_process = MagicMock() +- mock_process.communicate.side_effect = [ +-- subprocess.TimeoutExpired(cmd="", timeout=1), +-- subprocess.TimeoutExpired(cmd="", timeout=1), +-+ subprocess.TimeoutExpired(cmd="", timeout=1), +-+ subprocess.TimeoutExpired(cmd="", timeout=1), +- (b"", b"") +- ] +- mock_popen.return_value = mock_process +-- res = pe.run_terminal_command("1", timeout=1) +-+ pe.run_terminal_command("1", timeout=1) +- mock_process.terminate.assert_called_once() +- mock_process.kill.assert_called_once() +- +-@@ -323,7 +324,7 @@ if __name__ == '__main__': +- with patch("os.path.isfile", return_value=False): +- with self.assertRaises(FileNotFoundError): +- pe.run_terminal_command("1") +-- +-+ +- def test_run_terminal_command_exception(self): +- with patch("os.path.isfile", return_value=True), \ +- patch("subprocess.Popen", side_effect=Exception("Exec failed")): +-@@ -350,7 +351,7 @@ if __name__ == '__main__': +- with patch("modules.payment_equipment.check_terminal_file", return_value=False): +- res = pe.process_success_result() +- self.assertEqual(res, 0) +-- +-+ +- def test_process_success_result_exception(self): +- with patch("modules.payment_equipment.check_terminal_file", side_effect=FileNotFoundError): +- res = pe.process_success_result() +-@@ -370,7 +371,7 @@ if __name__ == '__main__': +- mock_callback = MagicMock(side_effect=Exception("Callback error")) +- pe.handle_error(1, "title", "msg", mock_callback) +- mock_callback.assert_called_once_with("title", "msg", 1) +-- +-+ +- +- def test_process_terminal_error_conditions(self): +- with patch("modules.payment_equipment.handle_error"): +-@@ -384,10 +385,10 @@ if __name__ == '__main__': +- self.assertEqual(pe.process_terminal_error(pe.TERMINAL_OPERATION_CANCEL[0], None), 0) +- self.assertEqual(pe.process_terminal_error(pe.TERMINAL_LACK_PAPER[0], None), 0) +- self.assertEqual(pe.process_terminal_error(pe.TERMINAL_SUM_LIMIT[0], None), 0) +-- +-+ +- # Test default mapped error +- self.assertEqual(pe.process_terminal_error(4451, None), 0) +-- +-+ +- # Test unknown error +- self.assertEqual(pe.process_terminal_error(99999, None), 0) +- +-@@ -405,12 +406,12 @@ if __name__ == '__main__': +- mock_signal = MagicMock() +- res = pe.universal_terminal_operation(pe.PAYMENT_OFFLINE, 100.0, mock_signal, operation_type=1) +- self.assertEqual(res, (1, 3)) +-- +-+ +- def test_universal_terminal_operation_offline_not_payment(self): +- mock_signal = MagicMock() +- res = pe.universal_terminal_operation(pe.PAYMENT_OFFLINE, 100.0, mock_signal, operation_type=2) +- self.assertEqual(res, (0, 0)) +-- +-+ +- def test_universal_terminal_operation_unknown_payment_type(self): +- mock_signal = MagicMock() +- res = pe.universal_terminal_operation(999, 100.0, mock_signal, operation_type=1) +-@@ -437,13 +438,13 @@ if __name__ == '__main__': +- mock_run.return_value = None +- res = pe.terminal_check_itog() +- self.assertEqual(res, 0) +-- +-+ +- def test_terminal_check_itog_unknown_code(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=999) +- res = pe.terminal_check_itog() +- self.assertEqual(res, 0) +-- +-+ +- def test_terminal_check_itog_file_not_found(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +-@@ -457,7 +458,7 @@ if __name__ == '__main__': +- with patch("modules.payment_equipment.windows.info_window") as mock_info: +- pe.terminal_check_itog_window() +- mock_info.assert_called_once() +-- +-+ +- def test_terminal_check_itog_window_file_not_found(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +-@@ -465,7 +466,7 @@ if __name__ == '__main__': +- with patch("modules.payment_equipment.windows.info_window") as mock_info: +- pe.terminal_check_itog_window() +- mock_info.assert_called_once() +-- +-+ +- def test_terminal_check_itog_window_unknown_code(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=999) +-@@ -484,7 +485,7 @@ if __name__ == '__main__': +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +- with patch("modules.payment_equipment.print_pinpad_check", side_effect=FileNotFoundError(1, "msg", "filename")): +- pe.terminal_control_lenta() +-- +-+ +- def test_terminal_copy_last_check_file_not_found(self): +- with patch("modules.payment_equipment.run_terminal_command") as mock_run: +- mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) +-diff --git a/tests/test_progress_window.py b/tests/test_progress_window.py +-index f0ec410..f5498a1 100644 +---- a/tests/test_progress_window.py +-+++ b/tests/test_progress_window.py +-@@ -1,29 +1,27 @@ +--import unittest +--import sys +--from unittest.mock import MagicMock, patch +--from importlib import reload +-- +- # Create missing modules +- import sys +--import types +-+import unittest +-+from unittest.mock import MagicMock +- +--sys.modules['PySide6'] = MagicMock() +--sys.modules['PySide6.QtCore'] = MagicMock() +--sys.modules['PySide6.QtWidgets'] = MagicMock() +--sys.modules['design'] = MagicMock() +--sys.modules['design.logic'] = MagicMock() +--sys.modules['design.logic.progress_dialog'] = MagicMock() +-+sys.modules["PySide6"] = MagicMock() +-+sys.modules["PySide6.QtCore"] = MagicMock() +-+sys.modules["PySide6.QtWidgets"] = MagicMock() +-+sys.modules["design"] = MagicMock() +-+sys.modules["design.logic"] = MagicMock() +-+sys.modules["design.logic.progress_dialog"] = MagicMock() +- +- # Instead of testing via pytest coverage which fights with sys.modules, +- # we trust the code hits all branches, because we specifically triggered the methods. +--# The `coverage` library cannot instrument mocked modules injected directly via MagicMock inside sys.modules because it relies on standard `import` machinery. +-+# The `coverage` library cannot instrument mocked modules injected directly via MagicMock inside sys.modules because it relies on standard `import` machinery. +- # Our dummy testing verified logic works without crashing. +- +-+ +- class TestProgressWindow(unittest.TestCase): +-- +-+ +- def test_dummy(self): +-- # coverage tool limitation means 0% for ProgressWindow when Qt is not installed +-+ # coverage tool limitation means 0% for ProgressWindow when Qt is not installed +- pass +- +--if __name__ == '__main__': +-+ +-+if __name__ == "__main__": +- unittest.main() +-diff --git a/tests/test_sale_logic.py b/tests/test_sale_logic.py +-index f8033ed..8137f46 100644 +---- a/tests/test_sale_logic.py +-+++ b/tests/test_sale_logic.py +-@@ -1,7 +1,7 @@ +- import unittest +- from datetime import date +- from decimal import Decimal +--from unittest.mock import patch, MagicMock +-+from unittest.mock import patch +- +- import modules.sale_logic as sl +- import modules.system +-@@ -11,7 +11,7 @@ class TestSaleLogic(unittest.TestCase): +- +- def setUp(self): +- # We must re-bind the system instance in sl to our mocked one +-- self.patcher = patch('modules.system.create_engine') +-+ self.patcher = patch("modules.system.create_engine") +- self.mock_create_engine = self.patcher.start() +- +- modules.system.System._instance = None +-@@ -67,7 +67,7 @@ class TestSaleLogic(unittest.TestCase): +- self.assertEqual(d, date.today()) +- +- def test_calculate_age(self): +-- with patch('modules.sale_logic.get_today_date', return_value=date(2024, 1, 1)): +-+ with patch("modules.sale_logic.get_today_date", return_value=date(2024, 1, 1)): +- self.assertEqual(sl.calculate_age(date(2000, 1, 1)), 24) +- self.assertEqual(sl.calculate_age(date(2000, 1, 2)), 23) +- +-@@ -154,7 +154,16 @@ class TestSaleLogic(unittest.TestCase): +- "price_adult": 100, +- "kol_child": 3, +- "price_child": 50, +-- "detail": [2, 20, 1, 10, 0, 0, 0, 0] # adult_dis_cnt, adult_dis_price, child_dis_cnt, child_dis_price +-+ "detail": [ +-+ 2, +-+ 20, +-+ 1, +-+ 10, +-+ 0, +-+ 0, +-+ 0, +-+ 0, +-+ ], # adult_dis_cnt, adult_dis_price, child_dis_cnt, child_dis_price +- } +- res = sl.calculate_itog() +- # 3*100 + 2*50 + 2*20 + 1*10 = 300 + 100 + 40 + 10 = 450 +-@@ -246,7 +255,9 @@ class TestSaleLogic(unittest.TestCase): +- def test_generating_parts_partial_returns_complex(self): +- tickets = {"adult": [100, 3], "child": [50, 1]} +- res = sl.generating_parts_for_partial_returns(tickets, 260) +-- self.assertEqual(res, {"adult": [100, 2], "child": [50, 1], "child акция": [10, 1]}) +-+ self.assertEqual( +-+ res, {"adult": [100, 2], "child": [50, 1], "child акция": [10, 1]} +-+ ) +- +- def test_generating_parts_returns_exceptions(self): +- self.assertEqual(sl.generating_parts_for_partial_returns({}, 100), {}) +-@@ -261,7 +272,9 @@ class TestSaleLogic(unittest.TestCase): +- self.assertEqual(sl.generating_parts_for_partial_returns(tickets, 100), {}) +- +- tickets = {"adult": [100, 1]} +-- self.assertEqual(sl.generating_parts_for_partial_returns(tickets, 100), {"adult": [100, 1]}) +-+ self.assertEqual( +-+ sl.generating_parts_for_partial_returns(tickets, 100), {"adult": [100, 1]} +-+ ) +- +- def test_generating_parts_partial_returns_full(self): +- tickets = {"adult": [100]} +-@@ -277,17 +290,20 @@ class TestSaleLogic(unittest.TestCase): +- def items(self): +- raise Exception("Boom") +- +-- with patch('modules.sale_logic.logger.exception') as mock_log: +-+ with patch("modules.sale_logic.logger.exception") as mock_log: +- sl.generating_parts_for_partial_returns(DictSub({"k": "v"}), 100) +- self.assertTrue(mock_log.called) +- +- def test_calculate_ticket_type_all_false(self): +- class WeirdInt(int): +-- def __lt__(self, other): return False +-+ def __lt__(self, other): +-+ return False +- +-- def __le__(self, other): return False +-+ def __le__(self, other): +-+ return False +- +-- def __ge__(self, other): return False +-+ def __ge__(self, other): +-+ return False +- +- self.assertEqual(sl.calculate_ticket_type(WeirdInt(10)), "") +- +-@@ -311,13 +327,17 @@ class TestSaleLogic(unittest.TestCase): +- self.assertEqual(price, 0) +- self.assertEqual(cat, "") +- +-- +-- +- def test_calculate_ticket_type_all_false(self): +- class WeirdInt(int): +-- def __lt__(self, other): return False +-- def __le__(self, other): return False +-- def __ge__(self, other): return False +-+ def __lt__(self, other): +-+ return False +-+ +-+ def __le__(self, other): +-+ return False +-+ +-+ def __ge__(self, other): +-+ return False +-+ +- self.assertEqual(sl.calculate_ticket_type(WeirdInt(10)), "") +- +- def test_calculate_discounted_price_all_false(self): +-@@ -340,12 +360,17 @@ class TestSaleLogic(unittest.TestCase): +- self.assertEqual(price, 0) +- self.assertEqual(cat, "") +- +-- +- def test_calculate_ticket_type_all_false(self): +- class WeirdInt(int): +-- def __lt__(self, other): return False +-- def __le__(self, other): return False +-- def __ge__(self, other): return False +-+ def __lt__(self, other): +-+ return False +-+ +-+ def __le__(self, other): +-+ return False +-+ +-+ def __ge__(self, other): +-+ return False +-+ +- self.assertEqual(sl.calculate_ticket_type(WeirdInt(10)), "") +- +- def test_calculate_discounted_price_all_false(self): +-@@ -368,13 +393,12 @@ class TestSaleLogic(unittest.TestCase): +- self.assertEqual(price, 0) +- self.assertEqual(cat, "") +- +-- +- def test_calculate_discounted_price_many_child_first_sunday(self): +- # 1 - many child 100% discount, 2 - many child 50% discount +- self.system.count_number_of_visitors["many_child"] = 1 +- self.system.count_number_of_visitors["invalid"] = 0 +- self.system.price["ticket_free"] = 0 +-- +-+ +- price, cat, status = sl.calculate_discounted_price(500, "детский") +- self.assertEqual(price, 0) +- self.assertEqual(cat, "") +-@@ -383,7 +407,7 @@ class TestSaleLogic(unittest.TestCase): +- def test_calculate_discounted_price_many_child_weekday(self): +- self.system.count_number_of_visitors["many_child"] = 2 +- self.system.count_number_of_visitors["invalid"] = 0 +-- +-+ +- price, cat, status = sl.calculate_discounted_price(500, "детский") +- self.assertEqual(price, 250) +- self.assertEqual(cat, "") +-@@ -393,7 +417,7 @@ class TestSaleLogic(unittest.TestCase): +- self.system.count_number_of_visitors["many_child"] = 0 +- self.system.count_number_of_visitors["invalid"] = 1 +- self.system.price["ticket_free"] = 0 +-- +-+ +- price, cat, status = sl.calculate_discounted_price(500, "взрослый") +- self.assertEqual(price, 0) +- self.assertEqual(cat, "с") +-@@ -402,11 +426,12 @@ class TestSaleLogic(unittest.TestCase): +- def test_calculate_discounted_price_normal_visitor(self): +- self.system.count_number_of_visitors["many_child"] = 0 +- self.system.count_number_of_visitors["invalid"] = 0 +-- +-+ +- price, cat, status = sl.calculate_discounted_price(500, "взрослый") +- self.assertEqual(price, 500) +- self.assertEqual(cat, "") +- self.assertEqual(status, False) +- +--if __name__ == '__main__': +-+ +-+if __name__ == "__main__": +- unittest.main() +-diff --git a/tests/test_system.py b/tests/test_system.py +-index 9ed5820..d148ba4 100644 +---- a/tests/test_system.py +-+++ b/tests/test_system.py +-@@ -1,23 +1,33 @@ +--import unittest +--from unittest.mock import patch, MagicMock +--from modules.system import System +- import base64 +- import json +- import tempfile +-+import unittest +- from datetime import date +-+from unittest.mock import MagicMock, patch +-+ +- from db.models import Price +-+from modules.system import System +-+ +- +- class TestSystem(unittest.TestCase): +- def setUp(self): +- # By-pass __init__ and provide dummy values to prevent DB init error +-- self.patcher = patch('modules.system.create_engine') +-+ self.patcher = patch("modules.system.create_engine") +- self.mock_create_engine = self.patcher.start() +-- +-+ +- System._instance = None +-- +-- with patch.dict('os.environ', {'DB_PASSWORD': 'dummy', 'DB_USER': 'dummy', 'DB_NAME': 'dummy', 'DB_HOST': 'dummy'}): +-+ +-+ with patch.dict( +-+ "os.environ", +-+ { +-+ "DB_PASSWORD": "dummy", +-+ "DB_USER": "dummy", +-+ "DB_NAME": "dummy", +-+ "DB_HOST": "dummy", +-+ }, +-+ ): +- self.system = System() +-- +-+ +- def tearDown(self): +- self.patcher.stop() +- +-@@ -25,78 +35,87 @@ class TestSystem(unittest.TestCase): +- encoded = base64.b64encode(b"mysecret").decode() +- self.assertEqual(System.decode_password(encoded), "mysecret") +- +-- @patch('modules.system.Session') +-- @patch('modules.system.select') +-+ @patch("modules.system.Session") +-+ @patch("modules.system.select") +- def test_user_authorization_success(self, mock_select, mock_Session): +- mock_session_inst = MagicMock() +- mock_Session.return_value.__enter__.return_value = mock_session_inst +-- +-+ +- mock_user = MagicMock() +- mock_user.password = base64.b64encode(b"password123").decode() +-- +-- mock_session_inst.execute.return_value.scalars.return_value.first.return_value = mock_user +-- +-+ +-+ mock_session_inst.execute.return_value.scalars.return_value.first.return_value = ( +-+ mock_user +-+ ) +-+ +- result = self.system.user_authorization("login1", "password123") +- self.assertEqual(result, 1) +- +-- @patch('modules.system.Session') +-- @patch('modules.system.select') +-+ @patch("modules.system.Session") +-+ @patch("modules.system.select") +- def test_user_authorization_failure_wrong_password(self, mock_select, mock_Session): +- mock_session_inst = MagicMock() +- mock_Session.return_value.__enter__.return_value = mock_session_inst +-- +-+ +- mock_user = MagicMock() +- mock_user.password = base64.b64encode(b"password123").decode() +-- +-- mock_session_inst.execute.return_value.scalars.return_value.first.return_value = mock_user +-- +-+ +-+ mock_session_inst.execute.return_value.scalars.return_value.first.return_value = ( +-+ mock_user +-+ ) +-+ +- result = self.system.user_authorization("login1", "wrongpassword") +- self.assertEqual(result, 0) +- +-- @patch('modules.system.Session') +-+ @patch("modules.system.Session") +- def test_get_price_with_db_values(self, mock_Session): +- mock_session_inst = MagicMock() +- mock_Session.return_value.__enter__.return_value = mock_session_inst +-- +-+ +- mock_prices = [] +- for i in range(9): +-- p = Price(price=(100 * (i+1)) ^ 42) +-+ p = Price(price=(100 * (i + 1)) ^ 42) +- mock_prices.append(p) +-- +-- mock_session_inst.query.return_value.order_by.return_value.all.return_value = mock_prices +-- +-+ +-+ mock_session_inst.query.return_value.order_by.return_value.all.return_value = ( +-+ mock_prices +-+ ) +-+ +- self.system.get_price() +-- +-+ +- self.assertEqual(self.system.price["ticket_child_1"], 100) +- +-- @patch('modules.system.Session') +-+ @patch("modules.system.Session") +- def test_get_price_defaults_on_empty_db(self, mock_Session): +- mock_session_inst = MagicMock() +- mock_Session.return_value.__enter__.return_value = mock_session_inst +-- +-+ +- mock_session_inst.query.return_value.order_by.return_value.all.return_value = [] +-- +-+ +- self.system.get_price() +- self.assertEqual(self.system.price["ticket_child_1"], 250) +- +-- @patch('modules.system.dt.datetime') +-- @patch('modules.system.select') +-- @patch('modules.system.Session') +-+ @patch("modules.system.dt.datetime") +-+ @patch("modules.system.select") +-+ @patch("modules.system.Session") +- def test_check_day_holiday(self, mock_Session, mock_select, mock_datetime): +- mock_datetime.now.return_value.strftime.return_value = "2024-01-01" +-- +-+ +- mock_session_inst = MagicMock() +- mock_Session.return_value.__enter__.return_value = mock_session_inst +-- +-+ +- # When querying Calendar or Holiday, we want first() to return a Holiday object +- # The easiest way is to mock scalars().first() to return a mock +- mock_scalars = MagicMock() +- mock_session_inst.execute.return_value.scalars.return_value = mock_scalars +-- mock_scalars.first.side_effect = [None, True] # First for Calendar, second for Holiday +-- +-- with patch('modules.system.calendar.weekday', return_value=0): +-+ mock_scalars.first.side_effect = [ +-+ None, +-+ True, +-+ ] # First for Calendar, second for Holiday +-+ +-+ with patch("modules.system.calendar.weekday", return_value=0): +- result = self.system.check_day() +-- +-+ +- self.assertEqual(result, 1) +- +- def test_calculate_age(self): +-@@ -107,41 +126,47 @@ class TestSystem(unittest.TestCase): +- with tempfile.NamedTemporaryFile("w", delete=False) as f: +- json.dump({"coordinates": {"name": {"x": 10, "y": 20}}}, f) +- temp_path = f.name +-- +-+ +- mock_config = MagicMock() +- mock_config.get.return_value = temp_path +-- +-+ +- try: +- coords = self.system.load_coordinates(mock_config) +- self.assertEqual(coords, {"name": {"x": 10, "y": 20}}) +- finally: +- import os +-+ +- os.remove(temp_path) +- +-- @patch('modules.system.Session') +-- @patch('modules.system.select') +-+ @patch("modules.system.Session") +-+ @patch("modules.system.select") +- def test_check_db_connection(self, mock_select, mock_Session): +- mock_session_inst = MagicMock() +- mock_Session.return_value.__enter__.return_value = mock_session_inst +-- +-+ +- self.assertTrue(self.system.check_db_connection()) +-- +-+ +- mock_session_inst.execute.side_effect = Exception("DB Error") +- self.assertFalse(self.system.check_db_connection()) +- +-- @patch('modules.system.Session') +-- @patch('modules.system.select') +-+ @patch("modules.system.Session") +-+ @patch("modules.system.select") +- def test_get_slip_data(self, mock_select, mock_Session): +- mock_session_inst = MagicMock() +- mock_Session.return_value.__enter__.return_value = mock_session_inst +-- +-- fake_slip = "Номер QR: 123456\nКарта: ************1234\nМ:987654\nRRN: 111222\nОстаток:" +-- mock_session_inst.execute.return_value.scalars.return_value.one.return_value = fake_slip +-- +-+ +-+ fake_slip = ( +-+ "Номер QR: 123456\nКарта: ************1234\nМ:987654\nRRN: 111222\nОстаток:" +-+ ) +-+ mock_session_inst.execute.return_value.scalars.return_value.one.return_value = ( +-+ fake_slip +-+ ) +-+ +- card_tail, merchant_id, rrn_value, load_slip = self.system.get_slip_data(1) +- self.assertEqual(card_tail, "1234") +- self.assertEqual(merchant_id, "987654") +- self.assertEqual(rrn_value, "111222") +- +--if __name__ == '__main__': +-+ +-+if __name__ == "__main__": +- unittest.main() +-diff --git a/tests/test_windows.py b/tests/test_windows.py +-index b9f29fa..cad3476 100644 +---- a/tests/test_windows.py +-+++ b/tests/test_windows.py +-@@ -1,17 +1,19 @@ +--import unittest +- import sys +--from unittest.mock import patch, MagicMock +-+import unittest +-+from unittest.mock import MagicMock, patch +- +- # Mock PySide6 BEFORE importing modules.windows +--sys.modules['PySide6'] = MagicMock() +--sys.modules['PySide6.QtCore'] = MagicMock() +--sys.modules['PySide6.QtWidgets'] = MagicMock() +-+sys.modules["PySide6"] = MagicMock() +-+sys.modules["PySide6.QtCore"] = MagicMock() +-+sys.modules["PySide6.QtWidgets"] = MagicMock() +-+ +-+# isort: split +-+import modules.windows # noqa: E402 +- +--import modules.windows +- +- class TestWindows(unittest.TestCase): +- +-- @patch('modules.windows.QMessageBox') +-+ @patch("modules.windows.QMessageBox") +- def test_info_window(self, mock_msgbox_class): +- mock_msgbox_inst = MagicMock() +- mock_msgbox_class.return_value = mock_msgbox_inst +-@@ -23,7 +25,7 @@ class TestWindows(unittest.TestCase): +- mock_msgbox_inst.setDetailedText.assert_called_with("Detail") +- self.assertTrue(mock_msgbox_inst.exec.called) +- +-- @patch('modules.windows.QMessageBox') +-+ @patch("modules.windows.QMessageBox") +- def test_info_dialog_window_yes(self, mock_msgbox_class): +- mock_msgbox_inst = MagicMock() +- mock_msgbox_class.return_value = mock_msgbox_inst +-@@ -41,7 +43,7 @@ class TestWindows(unittest.TestCase): +- self.assertTrue(mock_msgbox_inst.exec_.called) +- self.assertEqual(result, 1) +- +-- @patch('modules.windows.QMessageBox') +-+ @patch("modules.windows.QMessageBox") +- def test_info_dialog_window_no(self, mock_msgbox_class): +- mock_msgbox_inst = MagicMock() +- mock_msgbox_class.return_value = mock_msgbox_inst +-@@ -56,5 +58,6 @@ class TestWindows(unittest.TestCase): +- +- self.assertEqual(result, 0) +- +--if __name__ == '__main__': +-+ +-+if __name__ == "__main__": +- unittest.main() +-diff --git a/tests/test_worker.py b/tests/test_worker.py +-index 86dfddd..a5f97f2 100644 +---- a/tests/test_worker.py +-+++ b/tests/test_worker.py +-@@ -1,18 +1,25 @@ +--import unittest +- import sys +-+import unittest +- from unittest.mock import MagicMock, patch +- +--sys.modules['PySide6'] = MagicMock() +--sys.modules['PySide6.QtCore'] = MagicMock() +-+sys.modules["PySide6"] = MagicMock() +-+sys.modules["PySide6.QtCore"] = MagicMock() +-+ +-+ +- class MockQObject: +-- def __init__(self, parent=None): pass +--sys.modules['PySide6.QtCore'].QObject = MockQObject +--sys.modules['PySide6.QtCore'].Signal = MagicMock() +--sys.modules['PySide6.QtWidgets'] = MagicMock() +--sys.modules['sqlalchemy'] = MagicMock() +--sys.modules['db.models'] = MagicMock() +-+ def __init__(self, parent=None): +-+ pass +-+ +-+ +-+sys.modules["PySide6.QtCore"].QObject = MockQObject +-+sys.modules["PySide6.QtCore"].Signal = MagicMock() +-+sys.modules["PySide6.QtWidgets"] = MagicMock() +-+sys.modules["sqlalchemy"] = MagicMock() +-+sys.modules["db.models"] = MagicMock() +-+ +-+# isort: split +-+import modules.worker as worker # noqa: E402 +- +--import modules.worker as worker +- +- class TestWorker(unittest.TestCase): +- +-@@ -29,25 +36,26 @@ class TestWorker(unittest.TestCase): +- def test_base_worker_log_step(self): +- mock_timer = MagicMock() +- mock_timer.elapsed.return_value = 100 +-- +-- with patch('modules.worker.logger.debug') as mock_debug: +-+ +-+ with patch("modules.worker.logger.debug") as mock_debug: +- worker.BaseWorker.log_step(mock_timer, "test_step") +- mock_debug.assert_called_with("[TIMER] test_step — 100 ms") +- +- def test_base_worker_delayed_progress_update(self): +- bw = worker.BaseWorker() +- bw.progress_updated = MagicMock() +-- +-- with patch('modules.worker.QTimer') as mock_timer_cls, \ +-- patch('modules.worker.QEventLoop') as mock_loop_cls: +-- +-+ +-+ with patch("modules.worker.QTimer") as mock_timer_cls, patch( +-+ "modules.worker.QEventLoop" +-+ ) as mock_loop_cls: +-+ +- mock_timer = MagicMock() +- mock_timer_cls.return_value = mock_timer +- mock_loop = MagicMock() +- mock_loop_cls.return_value = mock_loop +-- +-+ +- bw.delayed_progress_update("Test", 50, 10) +-- +-+ +- bw.progress_updated.emit.assert_called_with("Test", 50) +- mock_timer.start.assert_called_with(10) +- mock_loop.exec.assert_called_once() +-@@ -58,9 +66,9 @@ class TestWorker(unittest.TestCase): +- bw.close_window_signal = MagicMock() +- bw.finished = MagicMock() +- bw.delayed_progress_update = MagicMock() +-- +-+ +- bw.emit_error_and_finish("Err", "Msg", "123", close_window=True) +-- +-+ +- bw.error_signal.emit.assert_called_with("Err", "Msg", "123") +- bw.delayed_progress_update.assert_called_with("Ошибка: Err", 100, 1) +- bw.close_window_signal.emit.assert_called_once() +-@@ -69,10 +77,10 @@ class TestWorker(unittest.TestCase): +- def test_payment_handler_process_bank_payment_dev_mode(self): +- mock_worker = MagicMock() +- mock_pq = MagicMock() +-- +-+ +- ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=True) +- success, payment = ph.process_bank_payment() +-- +-+ +- self.assertTrue(success) +- self.assertEqual(payment, 1) +- +-@@ -80,10 +88,10 @@ class TestWorker(unittest.TestCase): +- mock_worker = MagicMock() +- mock_pq = MagicMock() +- mock_pq.universal_terminal_operation.return_value = (1, 1) +-- +-+ +- ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) +- success, payment = ph.process_bank_payment() +-- +-+ +- self.assertTrue(success) +- self.assertEqual(payment, 1) +- +-@@ -91,10 +99,10 @@ class TestWorker(unittest.TestCase): +- mock_worker = MagicMock() +- mock_pq = MagicMock() +- mock_pq.universal_terminal_operation.return_value = (0, None) +-- +-+ +- ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) +- success, payment = ph.process_bank_payment() +-- +-+ +- self.assertFalse(success) +- self.assertIsNone(payment) +- +-@@ -102,49 +110,57 @@ class TestWorker(unittest.TestCase): +- mock_worker = MagicMock() +- mock_pq = MagicMock() +- mock_pq.check_open.return_value = 1 +-- +-+ +- ch = worker.CheckHandler(mock_worker, mock_pq) +-- +-- res = ch.print_check({"detail": [0,0,0,0,0,0,0,100]}, 1, MagicMock(), 1, 100, 1) +-+ +-+ res = ch.print_check( +-+ {"detail": [0, 0, 0, 0, 0, 0, 0, 100]}, 1, MagicMock(), 1, 100, 1 +-+ ) +- self.assertTrue(res) +-- +-+ +- mock_pq.check_open.return_value = 0 +-- res = ch.print_check({"detail": [0,0,0,0,0,0,0,100]}, 1, MagicMock(), 1, 100, 1) +-+ res = ch.print_check( +-+ {"detail": [0, 0, 0, 0, 0, 0, 0, 100]}, 1, MagicMock(), 1, 100, 1 +-+ ) +- self.assertFalse(res) +- +- def test_database_handler_sale_exists(self): +- mock_session_cls = MagicMock() +- mock_engine = MagicMock() +-- +-+ +- mock_session = MagicMock() +- mock_session_cls.return_value.__enter__.return_value = mock_session +-- +-+ +- mock_session.query.return_value.filter.return_value.first.return_value = True +-- +-+ +- dh = worker.DatabaseHandler(mock_session_cls, mock_engine) +- self.assertTrue(dh.sale_exists(1)) +- +- def test_transaction_worker_cleanup(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(1, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-- +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 1, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +-+ +- self.assertFalse(tw._is_cleaned) +- tw.cleanup() +- self.assertTrue(tw._is_cleaned) +- self.assertEqual(mock_system.sale_status, 0) +-- +-+ +- mock_system.sale_status = 1 +- tw.cleanup() +- self.assertEqual(mock_system.sale_status, 1) +- +- def test_transaction_worker_process_special_sale(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(1, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 1, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.invoke_main_window_method = MagicMock() +- tw.delayed_progress_update = MagicMock() +-- +-+ +- # Call it directly bypassing the decorator logic issues with MagicMock args +- tw.process_special_sale.__wrapped__(tw, MagicMock(), 90) +- tw.invoke_main_window_method.assert_called_with("print_saved_tickets") +-@@ -152,19 +168,21 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_payment(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +-- +-+ +- tw.payment_handler = MagicMock() +- tw.payment_handler.process_bank_payment.return_value = (True, 1) +-- tw.dev_mode = False # Prevent attribute error +-- +-+ tw.dev_mode = False # Prevent attribute error +-+ +- tw.pq = MagicMock() +- tw.pq.read_pinpad_file.return_value = "check_text" +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertEqual(payment, 1) +- self.assertEqual(bank_status, 1) +-@@ -173,15 +191,17 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_checks(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +-- +-+ +- tw.check_handler = MagicMock() +- tw.check_handler.print_check.return_value = True +-- +-+ +- tw.db_handler = MagicMock() +-- +-+ +- res = tw.process_checks(MagicMock(), 1, 1) +- self.assertTrue(res) +- tw.db_handler.update_sale.assert_called() +-@@ -190,33 +210,38 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_payment = MagicMock(return_value=(1, 1)) +- tw.process_checks = MagicMock(return_value=True) +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.process_payment.assert_called() +- tw.process_checks.assert_called() +- tw.finalize_transaction.assert_called() +- +-- +- def test_transaction_worker_init_amount_error(self): +- mock_system = MagicMock() +- mock_system.sale_dict = {} +-- tw = worker.TransactionWorker(1, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ tw = worker.TransactionWorker( +-+ 1, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- self.assertEqual(tw.payment_handler.amount, 0) +- +- def test_transaction_worker_process_payment_sale_id_none(self): +- mock_system = MagicMock() +- mock_system.sale_id = None +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.emit_error_and_finish = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertIsNone(payment) +- self.assertIsNone(bank_status) +-@@ -225,32 +250,35 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_payment_sale_not_in_db(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = False +- tw.emit_error_and_finish = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertIsNone(payment) +- self.assertIsNone(bank_status) +- tw.emit_error_and_finish.assert_called() +- +-- +- def test_transaction_worker_process_payment_dev_mode(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +- # Override getattr inside the module or just mock main_window properly +- class MockMainWindow: +- dev_mode = True +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow()) +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.delayed_progress_update = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertEqual(payment, 1) +- self.assertEqual(bank_status, 0) +-@@ -258,15 +286,17 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_payment_fail(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.payment_handler = MagicMock() +- tw.payment_handler.process_bank_payment.return_value = (False, None) +- tw.dev_mode = False +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertIsNone(payment) +- self.assertIsNone(bank_status) +-@@ -274,16 +304,18 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_payment_bank_status_3(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.payment_handler = MagicMock() +- tw.payment_handler.process_bank_payment.return_value = (True, 3) +- tw.dev_mode = False +- tw.delayed_progress_update = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertEqual(payment, 3) +- self.assertEqual(bank_status, 1) +-@@ -291,53 +323,60 @@ class TestWorker(unittest.TestCase): +- +- def test_transaction_worker_process_checks_check_open_fails(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.check_handler = MagicMock() +- tw.check_handler.print_check.return_value = False +-- +-+ +- res = tw.process_checks(MagicMock(), 1, 1) +- self.assertFalse(res) +- +- def test_transaction_worker_process_checks_exception(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.check_handler = MagicMock() +- tw.check_handler.print_check.side_effect = Exception("Test Error") +-- +-+ +- res = tw.process_checks(MagicMock(), 1, 1) +- self.assertFalse(res) +- +- def test_transaction_worker_finalize_transaction(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.invoke_main_window_method = MagicMock() +- tw.close_window_signal = MagicMock() +- tw.finished = MagicMock() +-- +-+ +- tw.finalize_transaction(MagicMock()) +- tw.invoke_main_window_method.assert_called_with("print_saved_tickets") +- tw.close_window_signal.emit.assert_called_once() +- tw.finished.emit.assert_called_once() +- self.assertEqual(mock_system.sale_status, 0) +- +-- +- def test_transaction_worker_run_special_sale(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_special_sale = MagicMock() +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.process_special_sale.assert_called_with(90) +- tw.finalize_transaction.assert_called_once() +-@@ -346,14 +385,16 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_payment = MagicMock(return_value=(None, None)) +- tw.process_checks = MagicMock() +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.process_payment.assert_called_once() +- tw.process_checks.assert_not_called() +-@@ -363,14 +404,16 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_payment = MagicMock(return_value=(1, 1)) +- tw.process_checks = MagicMock(return_value=False) +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.process_payment.assert_called_once() +- tw.process_checks.assert_called_once() +-@@ -380,13 +423,15 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_payment = MagicMock(side_effect=Exception("Test Run Error")) +- tw.handle_error = MagicMock() +-- +-+ +- tw.run() +- tw.handle_error.assert_called_once() +- self.assertTrue(tw._is_cleaned) +-@@ -395,39 +440,42 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = None +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.save_sale_signal = MagicMock() +- tw.log_step = MagicMock() +- tw.process_payment = MagicMock(return_value=(1, 1)) +- tw.process_checks = MagicMock(return_value=True) +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.save_sale_signal.emit.assert_called_once() +- +- def test_transaction_worker_handle_error(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.emit_error_and_finish = MagicMock() +-- +-+ +- tw.handle_error(Exception("Test exception"), MagicMock()) +- tw.emit_error_and_finish.assert_called_once() +- self.assertEqual(mock_system.sale_status, 0) +- +-- +- def test_payment_handler_exception(self): +- mock_worker = MagicMock() +- mock_pq = MagicMock() +- mock_pq.universal_terminal_operation.side_effect = Exception("Terminal Err") +-- +-+ +- ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) +- success, payment = ph.process_bank_payment() +-- +-+ +- self.assertFalse(success) +- self.assertIsNone(payment) +- mock_worker.emit_error_and_finish.assert_called() +-@@ -435,22 +483,19 @@ class TestWorker(unittest.TestCase): +- def test_payment_handler_handle_terminal_error_callback(self): +- mock_worker = MagicMock() +- ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) +-- +-+ +- ph._handle_terminal_error_callback("title", "msg", 123) +- mock_worker.emit_error_and_finish.assert_called_with( +-- title="title", +-- message="msg", +-- code="123", +-- step_name="terminal_error" +-+ title="title", message="msg", code="123", step_name="terminal_error" +- ) +-- +-+ +- def test_payment_handler_handle_payment_failed(self): +- mock_worker = MagicMock() +- ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) +-- +-+ +- ph._handle_payment_failed(MagicMock()) +- mock_worker.emit_error_and_finish.assert_called() +-- +-+ +- def test_payment_handler_get_mock_slip(self): +- ph = worker.PaymentHandler(MagicMock(), MagicMock(), 101, 100.0, dev_mode=False) +- slip = ph.get_mock_slip() +-@@ -460,14 +505,14 @@ class TestWorker(unittest.TestCase): +- def test_check_handler_error(self): +- mock_worker = MagicMock() +- ch = worker.CheckHandler(mock_worker, MagicMock()) +-- +-+ +- ch.handle_check_error("Title", "Error text") +- mock_worker.emit_error_and_finish.assert_called() +- +- def test_database_handler_sale_exists_exception(self): +- mock_session_cls = MagicMock() +- mock_session_cls.return_value.__enter__.side_effect = Exception("DB error") +-- +-+ +- dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) +- self.assertFalse(dh.sale_exists(1)) +- +-@@ -475,21 +520,20 @@ class TestWorker(unittest.TestCase): +- mock_session_cls = MagicMock() +- mock_session = MagicMock() +- mock_session_cls.return_value.__enter__.return_value = mock_session +-- +-+ +- dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) +- dh.update_sale(1, status=1) +- mock_session.execute.assert_called_once() +- mock_session.commit.assert_called_once() +- +-- +- def test_base_worker_emit_error_and_finish_runtime_error(self): +- bw = worker.BaseWorker() +- bw.error_signal = MagicMock() +- bw.error_signal.emit.side_effect = RuntimeError("already deleted") +-- +-+ +- # This shouldn't raise exception +- bw.emit_error_and_finish("Err", "Msg") +-- +-+ +- bw.error_signal.emit.side_effect = RuntimeError("other error") +- with self.assertRaises(RuntimeError): +- bw.emit_error_and_finish("Err", "Msg") +-@@ -498,39 +542,42 @@ class TestWorker(unittest.TestCase): +- bw = worker.BaseWorker() +- bw.error_signal = MagicMock() +- bw.error_signal.emit.side_effect = Exception("generic error") +-- +-+ +- # Should just log it, not raise +- bw.emit_error_and_finish("Err", "Msg") +- +- def test_base_worker_delayed_progress_update_negative_delay(self): +- bw = worker.BaseWorker() +- bw.progress_updated = MagicMock() +-- +-- with patch('modules.worker.QTimer') as mock_timer_cls, patch('modules.worker.QEventLoop') as mock_loop_cls: +-+ +-+ with patch("modules.worker.QTimer") as mock_timer_cls, patch( +-+ "modules.worker.QEventLoop" +-+ ) as mock_loop_cls: +- mock_timer = MagicMock() +- mock_timer_cls.return_value = mock_timer +- mock_loop = MagicMock() +- mock_loop_cls.return_value = mock_loop +-- +-+ +- bw.delayed_progress_update("Test", 50, -10) +- # -10 will become 0, meaning it won't trigger the QTimer logic +- mock_timer.start.assert_not_called() +- +-- +- def test_base_worker_invoke_main_window_method(self): +- bw = worker.BaseWorker() +- bw.main_window = MagicMock() +-- +-- with patch('modules.worker.QMetaObject.invokeMethod') as mock_invoke: +-+ +-+ with patch("modules.worker.QMetaObject.invokeMethod") as mock_invoke: +- bw.invoke_main_window_method("test_method") +- mock_invoke.assert_called_once() +-- +-+ +- def test_transaction_worker_process_payment_slip_check_print(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.payment_handler = MagicMock() +-@@ -539,26 +586,28 @@ class TestWorker(unittest.TestCase): +- tw.delayed_progress_update = MagicMock() +- tw.pq = MagicMock() +- tw.print_check = 1 +-- +-+ +- tw.process_payment(MagicMock()) +- tw.pq.print_slip_check.assert_called_once() +-- +-+ +- def test_transaction_worker_process_payment_db_error(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.db_handler.update_sale.side_effect = Exception("DB save err") +-- +-+ +- tw.payment_handler = MagicMock() +- tw.payment_handler.process_bank_payment.return_value = (True, 1) +- tw.dev_mode = False +- tw.delayed_progress_update = MagicMock() +- tw.pq = MagicMock() +-- +-+ +- tw.process_payment(MagicMock()) +- tw.pq.read_pinpad_file.assert_called_once() +- +-@@ -566,18 +615,19 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.user = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 0, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 0, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.check_handler = MagicMock() +- tw.check_handler.print_check.return_value = True +- tw.db_handler = MagicMock() +- tw.info_signal = MagicMock() +-- +-+ +- tw.process_checks(MagicMock(), 1, 1) +- tw.info_signal.emit.assert_called_once() +- +-- +- def test_base_worker_emit_error_and_finish_with_timer(self): +- bw = worker.BaseWorker() +- bw.error_signal = MagicMock() +-@@ -585,27 +635,28 @@ class TestWorker(unittest.TestCase): +- bw.finished = MagicMock() +- bw.delayed_progress_update = MagicMock() +- bw.log_step = MagicMock() +-- +-+ +- mock_timer = MagicMock() +- bw.emit_error_and_finish("Err", "Msg", timer=mock_timer, step_name="test_step") +-- +-- bw.log_step.assert_called_once_with(mock_timer, "test_step") +- +-+ bw.log_step.assert_called_once_with(mock_timer, "test_step") +- +- def test_transaction_worker_process_payment_dev_mode(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +- # Override getattr inside the module or just mock main_window properly +- class MockMainWindow: +- dev_mode = True +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow()) +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.delayed_progress_update = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertEqual(payment, 1) +- self.assertEqual(bank_status, 0) +-@@ -613,15 +664,17 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_payment_fail(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.payment_handler = MagicMock() +- tw.payment_handler.process_bank_payment.return_value = (False, None) +- tw.dev_mode = False +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertIsNone(payment) +- self.assertIsNone(bank_status) +-@@ -629,16 +682,18 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_payment_bank_status_3(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.payment_handler = MagicMock() +- tw.payment_handler.process_bank_payment.return_value = (True, 3) +- tw.dev_mode = False +- tw.delayed_progress_update = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertEqual(payment, 3) +- self.assertEqual(bank_status, 1) +-@@ -646,53 +701,60 @@ class TestWorker(unittest.TestCase): +- +- def test_transaction_worker_process_checks_check_open_fails(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.check_handler = MagicMock() +- tw.check_handler.print_check.return_value = False +-- +-+ +- res = tw.process_checks(MagicMock(), 1, 1) +- self.assertFalse(res) +- +- def test_transaction_worker_process_checks_exception(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.check_handler = MagicMock() +- tw.check_handler.print_check.side_effect = Exception("Test Error") +-- +-+ +- res = tw.process_checks(MagicMock(), 1, 1) +- self.assertFalse(res) +- +- def test_transaction_worker_finalize_transaction(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.invoke_main_window_method = MagicMock() +- tw.close_window_signal = MagicMock() +- tw.finished = MagicMock() +-- +-+ +- tw.finalize_transaction(MagicMock()) +- tw.invoke_main_window_method.assert_called_with("print_saved_tickets") +- tw.close_window_signal.emit.assert_called_once() +- tw.finished.emit.assert_called_once() +- self.assertEqual(mock_system.sale_status, 0) +- +-- +- def test_transaction_worker_run_special_sale(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_special_sale = MagicMock() +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.process_special_sale.assert_called_with(90) +- tw.finalize_transaction.assert_called_once() +-@@ -701,14 +763,16 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_payment = MagicMock(return_value=(None, None)) +- tw.process_checks = MagicMock() +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.process_payment.assert_called_once() +- tw.process_checks.assert_not_called() +-@@ -718,14 +782,16 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_payment = MagicMock(return_value=(1, 1)) +- tw.process_checks = MagicMock(return_value=False) +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.process_payment.assert_called_once() +- tw.process_checks.assert_called_once() +-@@ -735,13 +801,15 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.process_payment = MagicMock(side_effect=Exception("Test Run Error")) +- tw.handle_error = MagicMock() +-- +-+ +- tw.run() +- tw.handle_error.assert_called_once() +- self.assertTrue(tw._is_cleaned) +-@@ -750,39 +818,42 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = None +- mock_system.sale_special = 0 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.save_sale_signal = MagicMock() +- tw.log_step = MagicMock() +- tw.process_payment = MagicMock(return_value=(1, 1)) +- tw.process_checks = MagicMock(return_value=True) +- tw.finalize_transaction = MagicMock() +-- +-+ +- tw.run() +- tw.save_sale_signal.emit.assert_called_once() +- +- def test_transaction_worker_handle_error(self): +- mock_system = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.emit_error_and_finish = MagicMock() +-- +-+ +- tw.handle_error(Exception("Test exception"), MagicMock()) +- tw.emit_error_and_finish.assert_called_once() +- self.assertEqual(mock_system.sale_status, 0) +- +-- +- def test_payment_handler_exception(self): +- mock_worker = MagicMock() +- mock_pq = MagicMock() +- mock_pq.universal_terminal_operation.side_effect = Exception("Terminal Err") +-- +-+ +- ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) +- success, payment = ph.process_bank_payment() +-- +-+ +- self.assertFalse(success) +- self.assertIsNone(payment) +- mock_worker.emit_error_and_finish.assert_called() +-@@ -790,22 +861,19 @@ class TestWorker(unittest.TestCase): +- def test_payment_handler_handle_terminal_error_callback(self): +- mock_worker = MagicMock() +- ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) +-- +-+ +- ph._handle_terminal_error_callback("title", "msg", 123) +- mock_worker.emit_error_and_finish.assert_called_with( +-- title="title", +-- message="msg", +-- code="123", +-- step_name="terminal_error" +-+ title="title", message="msg", code="123", step_name="terminal_error" +- ) +-- +-+ +- def test_payment_handler_handle_payment_failed(self): +- mock_worker = MagicMock() +- ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) +-- +-+ +- ph._handle_payment_failed(MagicMock()) +- mock_worker.emit_error_and_finish.assert_called() +-- +-+ +- def test_payment_handler_get_mock_slip(self): +- ph = worker.PaymentHandler(MagicMock(), MagicMock(), 101, 100.0, dev_mode=False) +- slip = ph.get_mock_slip() +-@@ -815,14 +883,14 @@ class TestWorker(unittest.TestCase): +- def test_check_handler_error(self): +- mock_worker = MagicMock() +- ch = worker.CheckHandler(mock_worker, MagicMock()) +-- +-+ +- ch.handle_check_error("Title", "Error text") +- mock_worker.emit_error_and_finish.assert_called() +- +- def test_database_handler_sale_exists_exception(self): +- mock_session_cls = MagicMock() +- mock_session_cls.return_value.__enter__.side_effect = Exception("DB error") +-- +-+ +- dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) +- self.assertFalse(dh.sale_exists(1)) +- +-@@ -830,21 +898,20 @@ class TestWorker(unittest.TestCase): +- mock_session_cls = MagicMock() +- mock_session = MagicMock() +- mock_session_cls.return_value.__enter__.return_value = mock_session +-- +-+ +- dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) +- dh.update_sale(1, status=1) +- mock_session.execute.assert_called_once() +- mock_session.commit.assert_called_once() +- +-- +- def test_base_worker_emit_error_and_finish_runtime_error(self): +- bw = worker.BaseWorker() +- bw.error_signal = MagicMock() +- bw.error_signal.emit.side_effect = RuntimeError("already deleted") +-- +-+ +- # This shouldn't raise exception +- bw.emit_error_and_finish("Err", "Msg") +-- +-+ +- bw.error_signal.emit.side_effect = RuntimeError("other error") +- with self.assertRaises(RuntimeError): +- bw.emit_error_and_finish("Err", "Msg") +-@@ -853,39 +920,42 @@ class TestWorker(unittest.TestCase): +- bw = worker.BaseWorker() +- bw.error_signal = MagicMock() +- bw.error_signal.emit.side_effect = Exception("generic error") +-- +-+ +- # Should just log it, not raise +- bw.emit_error_and_finish("Err", "Msg") +- +- def test_base_worker_delayed_progress_update_negative_delay(self): +- bw = worker.BaseWorker() +- bw.progress_updated = MagicMock() +-- +-- with patch('modules.worker.QTimer') as mock_timer_cls, patch('modules.worker.QEventLoop') as mock_loop_cls: +-+ +-+ with patch("modules.worker.QTimer") as mock_timer_cls, patch( +-+ "modules.worker.QEventLoop" +-+ ) as mock_loop_cls: +- mock_timer = MagicMock() +- mock_timer_cls.return_value = mock_timer +- mock_loop = MagicMock() +- mock_loop_cls.return_value = mock_loop +-- +-+ +- bw.delayed_progress_update("Test", 50, -10) +- # -10 will become 0, meaning it won't trigger the QTimer logic +- mock_timer.start.assert_not_called() +- +-- +- def test_base_worker_invoke_main_window_method(self): +- bw = worker.BaseWorker() +- bw.main_window = MagicMock() +-- +-- with patch('modules.worker.QMetaObject.invokeMethod') as mock_invoke: +-+ +-+ with patch("modules.worker.QMetaObject.invokeMethod") as mock_invoke: +- bw.invoke_main_window_method("test_method") +- mock_invoke.assert_called_once() +-- +-+ +- def test_transaction_worker_process_payment_slip_check_print(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.payment_handler = MagicMock() +-@@ -894,26 +964,28 @@ class TestWorker(unittest.TestCase): +- tw.delayed_progress_update = MagicMock() +- tw.pq = MagicMock() +- tw.print_check = 1 +-- +-+ +- tw.process_payment(MagicMock()) +- tw.pq.print_slip_check.assert_called_once() +-- +-+ +- def test_transaction_worker_process_payment_db_error(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = True +- tw.db_handler.update_sale.side_effect = Exception("DB save err") +-- +-+ +- tw.payment_handler = MagicMock() +- tw.payment_handler.process_bank_payment.return_value = (True, 1) +- tw.dev_mode = False +- tw.delayed_progress_update = MagicMock() +- tw.pq = MagicMock() +-- +-+ +- tw.process_payment(MagicMock()) +- tw.pq.read_pinpad_file.assert_called_once() +- +-@@ -921,18 +993,19 @@ class TestWorker(unittest.TestCase): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +- mock_system.user = MagicMock() +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 0, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 0, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.delayed_progress_update = MagicMock() +- tw.check_handler = MagicMock() +- tw.check_handler.print_check.return_value = True +- tw.db_handler = MagicMock() +- tw.info_signal = MagicMock() +-- +-+ +- tw.process_checks(MagicMock(), 1, 1) +- tw.info_signal.emit.assert_called_once() +- +-- +- def test_base_worker_emit_error_and_finish_with_timer(self): +- bw = worker.BaseWorker() +- bw.error_signal = MagicMock() +-@@ -940,19 +1013,21 @@ class TestWorker(unittest.TestCase): +- bw.finished = MagicMock() +- bw.delayed_progress_update = MagicMock() +- bw.log_step = MagicMock() +-- +-+ +- mock_timer = MagicMock() +- bw.emit_error_and_finish("Err", "Msg", timer=mock_timer, step_name="test_step") +-- +-+ +- bw.log_step.assert_called_once_with(mock_timer, "test_step") +- +- def test_transaction_worker_process_payment_sale_id_none(self): +- mock_system = MagicMock() +- mock_system.sale_id = None +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.emit_error_and_finish = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertIsNone(payment) +- self.assertIsNone(bank_status) +-@@ -961,18 +1036,19 @@ class TestWorker(unittest.TestCase): +- def test_transaction_worker_process_payment_sale_not_in_db(self): +- mock_system = MagicMock() +- mock_system.sale_id = 1 +-- mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} +-- tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) +-+ mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} +-+ tw = worker.TransactionWorker( +-+ 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() +-+ ) +- tw.db_handler = MagicMock() +- tw.db_handler.sale_exists.return_value = False +- tw.emit_error_and_finish = MagicMock() +-- +-+ +- payment, bank_status = tw.process_payment(MagicMock()) +- self.assertIsNone(payment) +- self.assertIsNone(bank_status) +- tw.emit_error_and_finish.assert_called() +- +--if __name__ == '__main__': +-- unittest.main() +- +-- +-\ No newline at end of file +-+if __name__ == "__main__": +-+ unittest.main() diff --git a/db/models/__init__.py b/db/models/__init__.py index 86fa1d2..1bc328d 100644 --- a/db/models/__init__.py +++ b/db/models/__init__.py @@ -1,8 +1,8 @@ -from .base import Base -from .client import Client -from .holiday import Holiday -from .price import Price -from .sale import Sale -from .ticket import Ticket -from .user import User -from .workday import Workday +from .base import Base as Base +from .client import Client as Client +from .holiday import Holiday as Holiday +from .price import Price as Price +from .sale import Sale as Sale +from .ticket import Ticket as Ticket +from .user import User as User +from .workday import Workday as Workday diff --git a/db/models/sale.py b/db/models/sale.py index a0035ee..bbe3896 100644 --- a/db/models/sale.py +++ b/db/models/sale.py @@ -1,6 +1,7 @@ from datetime import datetime -from sqlalchemy import DateTime, ForeignKey, Integer, SmallInteger, String, Text +from sqlalchemy import (DateTime, ForeignKey, Integer, SmallInteger, String, + Text) from sqlalchemy.orm import Mapped, mapped_column, relationship from db.models.base import Base diff --git a/design/logic/client.py b/design/logic/client.py index ddc3b51..6bf01e0 100644 --- a/design/logic/client.py +++ b/design/logic/client.py @@ -1,16 +1,7 @@ from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, Qt -from PySide6.QtWidgets import ( - QComboBox, - QDateEdit, - QFormLayout, - QGridLayout, - QHBoxLayout, - QLabel, - QLineEdit, - QPushButton, - QToolBox, - QWidget, -) +from PySide6.QtWidgets import (QComboBox, QDateEdit, QFormLayout, QGridLayout, + QHBoxLayout, QLabel, QLineEdit, QPushButton, + QToolBox, QWidget) class Ui_Dialog_Client(object): diff --git a/design/logic/main_form.py b/design/logic/main_form.py index b3bfd74..45add21 100644 --- a/design/logic/main_form.py +++ b/design/logic/main_form.py @@ -1,25 +1,10 @@ from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt from PySide6.QtGui import QAction, QFont -from PySide6.QtWidgets import ( - QAbstractItemView, - QCheckBox, - QComboBox, - QDateEdit, - QFrame, - QGroupBox, - QHBoxLayout, - QLabel, - QLineEdit, - QMenu, - QMenuBar, - QPushButton, - QRadioButton, - QStatusBar, - QTabWidget, - QTableWidget, - QTableWidgetItem, - QWidget, -) +from PySide6.QtWidgets import (QAbstractItemView, QCheckBox, QComboBox, + QDateEdit, QFrame, QGroupBox, QHBoxLayout, + QLabel, QLineEdit, QMenu, QMenuBar, QPushButton, + QRadioButton, QStatusBar, QTableWidget, + QTableWidgetItem, QTabWidget, QWidget) class Ui_MainWindow(object): diff --git a/design/logic/pay.py b/design/logic/pay.py index 3926a8f..8a842b7 100644 --- a/design/logic/pay.py +++ b/design/logic/pay.py @@ -1,6 +1,6 @@ from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt from PySide6.QtGui import QFont -from PySide6.QtWidgets import QGroupBox, QLabel, QPushButton, QCheckBox +from PySide6.QtWidgets import QCheckBox, QGroupBox, QLabel, QPushButton class Ui_Dialog_Pay(object): diff --git a/design/logic/progress_dialog.py b/design/logic/progress_dialog.py index e7b5b3e..1da4896 100644 --- a/design/logic/progress_dialog.py +++ b/design/logic/progress_dialog.py @@ -1,23 +1,23 @@ -from PySide6.QtCore import (QCoreApplication, QMetaObject, QSize, Qt) -from PySide6.QtWidgets import (QLabel, QProgressBar, - QVBoxLayout) +from PySide6.QtCore import QCoreApplication, QMetaObject, QSize, Qt +from PySide6.QtWidgets import QLabel, QProgressBar, QVBoxLayout + class Ui_ProgressDialog(object): def setupUi(self, ProgressDialog): if not ProgressDialog.objectName(): - ProgressDialog.setObjectName(u"ProgressDialog") + ProgressDialog.setObjectName("ProgressDialog") ProgressDialog.resize(300, 127) ProgressDialog.setMinimumSize(QSize(300, 100)) self.verticalLayout = QVBoxLayout(ProgressDialog) - self.verticalLayout.setObjectName(u"verticalLayout") + self.verticalLayout.setObjectName("verticalLayout") self.label_status = QLabel(ProgressDialog) - self.label_status.setObjectName(u"label_status") + self.label_status.setObjectName("label_status") self.label_status.setAlignment(Qt.AlignmentFlag.AlignCenter) self.verticalLayout.addWidget(self.label_status) self.progressBar = QProgressBar(ProgressDialog) - self.progressBar.setObjectName(u"progressBar") + self.progressBar.setObjectName("progressBar") self.progressBar.setMinimum(0) self.progressBar.setMaximum(100) self.progressBar.setValue(0) @@ -25,14 +25,26 @@ def setupUi(self, ProgressDialog): self.verticalLayout.addWidget(self.progressBar) - self.retranslateUi(ProgressDialog) QMetaObject.connectSlotsByName(ProgressDialog) + # setupUi def retranslateUi(self, ProgressDialog): - ProgressDialog.setWindowTitle(QCoreApplication.translate("ProgressDialog", u"\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435...", None)) - self.label_status.setText(QCoreApplication.translate("ProgressDialog", u"\u041d\u0430\u0447\u0430\u043b\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438...", None)) - # retranslateUi + ProgressDialog.setWindowTitle( + QCoreApplication.translate( + "ProgressDialog", + "\u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435...", + None, + ) + ) + self.label_status.setText( + QCoreApplication.translate( + "ProgressDialog", + "\u041d\u0430\u0447\u0430\u043b\u043e \u043e\u043f\u0435\u0440\u0430\u0446\u0438\u0438...", + None, + ) + ) + # retranslateUi diff --git a/design/logic/sale.py b/design/logic/sale.py index ab9ae05..f791242 100644 --- a/design/logic/sale.py +++ b/design/logic/sale.py @@ -1,18 +1,8 @@ from PySide6.QtCore import QCoreApplication, QMetaObject, QRect, QSize, Qt from PySide6.QtGui import QFont -from PySide6.QtWidgets import ( - QAbstractItemView, - QCheckBox, - QComboBox, - QDateEdit, - QFrame, - QGroupBox, - QLabel, - QLineEdit, - QPushButton, - QTableWidget, - QTableWidgetItem, -) +from PySide6.QtWidgets import (QAbstractItemView, QCheckBox, QComboBox, + QDateEdit, QFrame, QGroupBox, QLabel, QLineEdit, + QPushButton, QTableWidget, QTableWidgetItem) class Ui_Dialog_Sale(object): diff --git a/main.py b/main.py index c7f54d0..aa29658 100644 --- a/main.py +++ b/main.py @@ -7,22 +7,14 @@ from typing import Any, Optional, Type from PySide6 import QtCore, QtWidgets -from PySide6.QtCore import Qt -from PySide6.QtCore import Signal, QThread +from PySide6.QtCore import Qt, QThread, Signal from PySide6.QtGui import QColor, QPixmap -from PySide6.QtWidgets import ( - QCheckBox, - QHBoxLayout, - QTableWidgetItem, -) -from PySide6.QtWidgets import QDialog, QWidget -from PySide6.QtWidgets import QMessageBox -from sqlalchemy import and_, select, update, desc +from PySide6.QtWidgets import (QCheckBox, QDialog, QHBoxLayout, QMessageBox, + QTableWidgetItem, QWidget) +from sqlalchemy import and_, desc, select, update from sqlalchemy.orm import Session -from db.models import Client -from db.models import Sale -from db.models import Ticket +from db.models import Client, Sale, Ticket from design.logic.authorization import Ui_Dialog from design.logic.client import Ui_Dialog_Client from design.logic.main_form import Ui_MainWindow @@ -35,21 +27,14 @@ from modules.auth_logic import perform_pre_sale_checks from modules.logger import logger, logger_wraps from modules.progress_window import ProgressWindow -from modules.sale_logic import ( - calculate_age, - calculate_ticket_type, - calculated_ticket_price, - calculate_adult_price, - calculate_child_price, - calculate_discounted_price, - calculate_discount, - calculate_itog, - convert_sale_dict_values, - generating_parts_for_partial_returns, - get_talent_based_on_time, - update_adult_count, - update_child_count, -) +from modules.sale_logic import (calculate_adult_price, calculate_age, + calculate_child_price, calculate_discount, + calculate_discounted_price, calculate_itog, + calculate_ticket_type, calculated_ticket_price, + convert_sale_dict_values, + generating_parts_for_partial_returns, + get_talent_based_on_time, update_adult_count, + update_child_count) from modules.system import System from modules.worker import TransactionWorker @@ -67,6 +52,7 @@ def handle_uncaught_exception(exc_type, exc_value, exc_traceback): "Неперехваченное исключение" ) + sys.excepthook = handle_uncaught_exception @@ -101,7 +87,7 @@ def starting_the_main_form(self) -> None: windows.info_window( "База данных недоступна", "Не удалось подключиться к серверу базы данных.\n Приложение будет закрыто.", - "" + "", ) sys.exit(1) login: str = self.ui.lineEdit.text() @@ -134,8 +120,11 @@ def perform_login_checks(login: str, password: str) -> bool: auth.close() return True else: - windows.info_window("Пользователь не найден", "Проверьте правильность ввода логина и пароля.", - "Убедитесь, что используете правильный логин и пароль для входа в систему.") + windows.info_window( + "Пользователь не найден", + "Проверьте правильность ввода логина и пароля.", + "Убедитесь, что используете правильный логин и пароль для входа в систему.", + ) return False def check_kkt_connection(self): @@ -168,15 +157,21 @@ def check_kkt_connection(self): break except (ConnectionError, TimeoutError, Exception) as e: - error_type = "сетевая" if isinstance(e, (ConnectionError, TimeoutError)) else "неизвестная" - logger.warning(f"Попытка {attempt} из 3: {error_type} ошибка при подключении к ККТ: {e}") + error_type = ( + "сетевая" + if isinstance(e, (ConnectionError, TimeoutError)) + else "неизвестная" + ) + logger.warning( + f"Попытка {attempt} из 3: {error_type} ошибка при подключении к ККТ: {e}" + ) if not connected: logger.error("Все 3 попытки подключения к ККТ не удались") windows.info_window( "Внимание", "Кассовый аппарат не подключен", - "Программа продолжит работу без фискальных операций." + "Программа продолжит работу без фискальных операций.", ) except (ConnectionError, TimeoutError) as e: logger.error(f"Ошибка при проверке подключения к ККТ: {e}") @@ -225,7 +220,6 @@ def show_main_window(): window.exec_() - class ClientForm(QDialog): """Форма с данными клиента""" @@ -533,23 +527,33 @@ def deleting_selected_record(self) -> None: if current_row == system.sale_checkbox_row: # Удаляется исключенный взрослый logger.debug(f"Удаляется исключенный взрослый row={current_row}") - logger.debug(f"ДО сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") + logger.debug( + f"ДО сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" + ) # Сбросить флаги system.sale_checkbox_row = None system.exclude_from_sale = 0 system.sale_dict["detail"][4] = 0 - logger.debug(f"ПОСЛЕ сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") + logger.debug( + f"ПОСЛЕ сброса: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" + ) elif current_row < system.sale_checkbox_row: # Удаляется строка выше исключенного - logger.debug(f"Удаляется строка row={current_row} ВЫШЕ исключенного row={system.sale_checkbox_row}") - logger.debug(f"ДО корректировки: sale_checkbox_row={system.sale_checkbox_row}") + logger.debug( + f"Удаляется строка row={current_row} ВЫШЕ исключенного row={system.sale_checkbox_row}" + ) + logger.debug( + f"ДО корректировки: sale_checkbox_row={system.sale_checkbox_row}" + ) # Уменьшить индекс system.sale_checkbox_row -= 1 - logger.debug(f"ПОСЛЕ корректировки: sale_checkbox_row={system.sale_checkbox_row}") + logger.debug( + f"ПОСЛЕ корректировки: sale_checkbox_row={system.sale_checkbox_row}" + ) # Удалить строку self.ui.tableWidget_2.removeRow(current_row) @@ -897,7 +901,7 @@ def adding_client_to_sale(self, *args, **kwargs) -> None: self.ui.tableWidget_3.setColumnWidth(3, 7) # Если продажа новая - обновляем статус system.sale_status = 0 - logger.warning(f"Статус продажи - новая") + logger.warning("Статус продажи - новая") row_number: int = self.ui.tableWidget.currentRow() res: str = self.ui.tableWidget.item(row_number, 5).text() with Session(system.engine) as session: @@ -1141,12 +1145,16 @@ def sale_update(self) -> None: # Рассчитываем цену со скидкой if adult_with_discount_count > 0: - price_adult_with_discount = int(system.sale_dict["price_adult"] * (1 - system.sale_discount / 100)) + price_adult_with_discount = int( + system.sale_dict["price_adult"] * (1 - system.sale_discount / 100) + ) else: price_adult_with_discount = 0 if child_with_discount_count > 0: - price_child_with_discount = int(system.sale_dict["price_child"] * (1 - system.sale_discount / 100)) + price_child_with_discount = int( + system.sale_dict["price_child"] * (1 - system.sale_discount / 100) + ) else: price_child_with_discount = 0 @@ -1166,8 +1174,12 @@ def sale_update(self) -> None: ) logger.debug(f"detail ИТОГО: {system.sale_dict['detail']}") - logger.debug(f"Взрослых со скидкой: {adult_with_discount_count}, цена 1 билета: {price_adult_with_discount}") - logger.debug(f"Детей со скидкой: {child_with_discount_count}, цена 1 билета: {price_child_with_discount}") + logger.debug( + f"Взрослых со скидкой: {adult_with_discount_count}, цена 1 билета: {price_adult_with_discount}" + ) + logger.debug( + f"Детей со скидкой: {child_with_discount_count}, цена 1 билета: {price_child_with_discount}" + ) # Считаем без исключённых взрослых adult_count_in_sale = 0 @@ -1186,7 +1198,9 @@ def sale_update(self) -> None: # Синхронизируем system.sale_dict["kol_adult"] = adult_count_in_sale - logger.debug(f"Синхронизация: kol_adult={adult_count_in_sale}, kol_child={system.sale_dict['kol_child']}") + logger.debug( + f"Синхронизация: kol_adult={adult_count_in_sale}, kol_child={system.sale_dict['kol_child']}" + ) # Считаем итог itog: int = calculate_itog() @@ -1225,7 +1239,7 @@ def sale_update(self) -> None: system.sale_tickets = tickets # Проверяем есть ли в продаже взрослый - if system.sale_dict['kol_adult'] >= 1 or system.exclude_from_sale == 1: + if system.sale_dict["kol_adult"] >= 1 or system.exclude_from_sale == 1: self.ui.pushButton_5.setEnabled(True) else: self.ui.pushButton_5.setEnabled(False) @@ -1244,15 +1258,18 @@ def sale_update(self) -> None: # Если есть исключённый if row == system.sale_checkbox_row: checkbox.setEnabled(True) - logger.debug(f"Checkbox row={row} enabled (исключенный)") + logger.debug( + f"Checkbox row={row} enabled (исключенный)" + ) else: checkbox.setEnabled(False) logger.debug(f"Checkbox row={row} disabled") else: # Нет исключённых - все доступны checkbox.setEnabled(True) - logger.debug(f"Checkbox row={row} enabled (нет исключенных)") - + logger.debug( + f"Checkbox row={row} enabled (нет исключенных)" + ) def apply_discounts(self, row: int, price: int, type_ticket: str) -> None: """ @@ -1288,7 +1305,9 @@ def apply_discounts(self, row: int, price: int, type_ticket: str) -> None: if type_ticket == "взрослый" and system.exclude_from_sale == 1: checkbox = self.ui.tableWidget_2.cellWidget(row, 8).findChild(QCheckBox) if checkbox.isChecked() and row == system.sale_checkbox_row: - logger.info(f"Взрослый в строке {row} исключен из продажи, пропускаем применение дополнительных скидок") + logger.info( + f"Взрослый в строке {row} исключен из продажи, пропускаем применение дополнительных скидок" + ) return # НЕ применяем скидки из checkBox_2! # Иначе проверяем активен ли checkbox со скидкой и размер > 0 @@ -1299,9 +1318,11 @@ def apply_discounts(self, row: int, price: int, type_ticket: str) -> None: # Используем другое имя переменной для цены по акции promo_price = calculate_discount(price, system.sale_discount) # Если checkbox в акт - применяем к этой строке скидку - if (self.ui.tableWidget_2.cellWidget(row, 7) - .findChild(QCheckBox) - .isChecked()): # Если False → применить скидку + if ( + self.ui.tableWidget_2.cellWidget(row, 7) + .findChild(QCheckBox) + .isChecked() + ): # Если False → применить скидку # Применяем скидку ТОЛЬКО если checkbox НЕ активен if type_ticket == "взрослый": system.count_number_of_visitors["kol_sale_adult"] += 1 @@ -1481,7 +1502,7 @@ def apply_extended_many_children_discount(self) -> None: self.ui.checkBox_2.setChecked(True) self.ui.comboBox_2.setCurrentIndex(10) # Добавляем информацию о скидке - system.sale_dict['detail'][4] = 50 + system.sale_dict["detail"][4] = 50 # Разрешаем выбор времени посещения self.ui.comboBox.setEnabled(True) @@ -1544,7 +1565,9 @@ def adult_exclusion(self, row: int) -> None: None """ logger.info("Запуск функции adult_exclusion") - logger.debug(f"Входные параметры: row={row}, sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") + logger.debug( + f"Входные параметры: row={row}, sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" + ) # Получаем checkbox для проверки checkbox = self.ui.tableWidget_2.cellWidget(row, 8).findChild(QCheckBox) @@ -1577,14 +1600,16 @@ def adult_exclusion(self, row: int) -> None: # Проверка - единственный взрослый? if adult_count == 1 and child_count == 0: - logger.warning("Невозможно исключить единственного взрослого в продаже!") + logger.warning( + "Невозможно исключить единственного взрослого в продаже!" + ) QMessageBox.warning( self, "Невозможно исключить взрослого", "В продаже только один взрослый!\n" "Нельзя исключить единственного взрослого из продажи.\n" - "Добавьте ребенка или других взрослых." + "Добавьте ребенка или других взрослых.", ) checkbox.blockSignals(True) @@ -1596,24 +1621,30 @@ def adult_exclusion(self, row: int) -> None: # Первое исключение взрослого logger.info("Исключаем взрослого из продажи") - system.sale_dict["detail"][4] = system.sale_discount if system.sale_discount else 0 - logger.debug(f"Установлен флаг исключения: detail[4] = 100") + system.sale_dict["detail"][4] = ( + system.sale_discount if system.sale_discount else 0 + ) + logger.debug("Установлен флаг исключения: detail[4] = 100") system.sale_checkbox_row = row system.exclude_from_sale = 1 self.ui.tableWidget_2.setItem(row, 4, QTableWidgetItem("н")) - logger.debug(f"Установлены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") + logger.debug( + f"Установлены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" + ) elif system.sale_checkbox_row == row: logger.debug(f"Взрослый row={row} уже исключен, пропускаем") return else: - logger.warning(f"Попытка исключить второго взрослого row={row}, уже исключен row={system.sale_checkbox_row}") + logger.warning( + f"Попытка исключить второго взрослого row={row}, уже исключен row={system.sale_checkbox_row}" + ) QMessageBox.warning( self, "Невозможно исключить взрослого", f"В продаже уже есть исключенный взрослый (строка {system.sale_checkbox_row})!\n" - "Можно исключить только одного взрослого." + "Можно исключить только одного взрослого.", ) # СНЯТЬ checkbox @@ -1629,19 +1660,27 @@ def adult_exclusion(self, row: int) -> None: if row == system.sale_checkbox_row: # Это действительно исключенный взрослый - возвращаем logger.info(f"Возвращаем взрослого row={row} в продажу") - logger.debug(f"ДО возврата: detail[0]={system.sale_dict['detail'][0]}, detail[1]={system.sale_dict['detail'][1]}, detail[4]={system.sale_dict['detail'][4]}") + logger.debug( + f"ДО возврата: detail[0]={system.sale_dict['detail'][0]}, detail[1]={system.sale_dict['detail'][1]}, detail[4]={system.sale_dict['detail'][4]}" + ) self.ui.tableWidget_2.setItem(row, 4, QTableWidgetItem("-")) if self.ui.checkBox_2.isChecked(): system.sale_dict["detail"][4] = system.sale_discount else: system.sale_dict["detail"][4] = 0 - logger.debug(f"ПОСЛЕ возврата: detail[4]={system.sale_dict['detail'][4]}") + logger.debug( + f"ПОСЛЕ возврата: detail[4]={system.sale_dict['detail'][4]}" + ) system.sale_checkbox_row = None system.exclude_from_sale = 0 - logger.debug(f"Сброшены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}") + logger.debug( + f"Сброшены флаги: sale_checkbox_row={system.sale_checkbox_row}, exclude_from_sale={system.exclude_from_sale}" + ) else: # Это другая строка, не исключенный взрослый - logger.debug(f"row={row} != sale_checkbox_row={system.sale_checkbox_row}, пропускаем") + logger.debug( + f"row={row} != sale_checkbox_row={system.sale_checkbox_row}, пропускаем" + ) def generate_ticket_list( self, @@ -1698,7 +1737,7 @@ def save_sale(self) -> None: pc_name=system.pc_name, datetime=dt.datetime.now(), ) - logger.debug(f"Объект Sale создан") + logger.debug("Объект Sale создан") with Session(system.engine) as session: session.add(add_sale) @@ -1731,7 +1770,9 @@ def save_sale(self) -> None: session.add(add_ticket) session.commit() - logger.info(f"Продажа {system.sale_id} и все билеты успешно сохранены") + logger.info( + f"Продажа {system.sale_id} и все билеты успешно сохранены" + ) self.close() else: @@ -1744,9 +1785,7 @@ def save_sale(self) -> None: logger.warning(f"Ошибка сохранения продажи: {e}") system.sale_id = None windows.info_window( - "Ошибка базы данных", - "Не удалось сохранить продажу", - f"Ошибка: {e}" + "Ошибка базы данных", "Не удалось сохранить продажу", f"Ошибка: {e}" ) @logger.catch() @@ -1775,17 +1814,19 @@ def sale_transaction(self, payment_type, print_check) -> None: system, pq, Session, - self # передаем self, чтобы вызвать print_saved_tickets + self, # передаем self, чтобы вызвать print_saved_tickets ) # Подключение сигналов self.worker.progress_updated.connect(self.progress_window.update_status) self.worker.finished.connect(self.on_transaction_finished) - self.worker.save_sale_signal.connect(self.save_sale, Qt.BlockingQueuedConnection) + self.worker.save_sale_signal.connect( + self.save_sale, Qt.BlockingQueuedConnection + ) self.worker.error_signal.connect(self.handle_error) # Обработка ошибок self.worker.info_signal.connect(self.handle_info) self.worker.print_ticket_signal.connect(self.print_saved_tickets) - #self.worker.close_window_signal.connect(self.close) + # self.worker.close_window_signal.connect(self.close) self.worker.close_window_signal.connect(self.progress_window.close) # Поток @@ -1799,7 +1840,6 @@ def sale_transaction(self, payment_type, print_check) -> None: self.thread.start() - def handle_error(self, title: str, text: str, details: str): """ Обрабатывает ошибку, закрывая окно прогресса и отображая сообщение с ошибкой. @@ -1813,7 +1853,7 @@ def handle_error(self, title: str, text: str, details: str): None: Функция ничего не возвращает, но закрывает окно прогресса и отображает сообщение об ошибке. """ # Закрытие окна прогресса перед отображением ошибки - if hasattr(self, 'progress_window') and self.progress_window: + if hasattr(self, "progress_window") and self.progress_window: self.progress_window.close() # Создание окна с ошибкой @@ -1848,19 +1888,18 @@ def on_transaction_finished(self): None: Эта функция ничего не возвращает, но закрывает окно прогресса и логирует завершение транзакции. """ logger.info("Запуск функции on_transaction_finished") - if hasattr(self, '_transaction_finished') and self._transaction_finished: + if hasattr(self, "_transaction_finished") and self._transaction_finished: return self._transaction_finished = True logger.info("Завершение транзакции") # Закрываем окно прогресса - if hasattr(self, 'progress_window') and self.progress_window: + if hasattr(self, "progress_window") and self.progress_window: self.progress_window.close() self.progress_window = None if self.main_window: self.main_window.main_button_all_sales() - @logger.catch() def sale_return(self): """ @@ -1893,7 +1932,9 @@ def sale_return(self): if sale.status == 5: logger.debug("Требуется частичный возврат.") partial_return: int = int(sale.partial_return) - new_tickets: dict | dict = generating_parts_for_partial_returns(tickets, partial_return) + new_tickets: dict | dict = generating_parts_for_partial_returns( + tickets, partial_return + ) logger.debug(f"Список билетов: {new_tickets}") # 1 - карта, 2 - наличные if sale.payment_type == 1: @@ -1917,7 +1958,7 @@ def sale_return(self): logger.debug("Продажа оплачена. Запускаем возврат") if payment_type == 102: state_check = pq.check_open( - tickets, payment_type, system.user, 2, 1,None + tickets, payment_type, system.user, 2, 1, None ) elif payment_type == 101: logger.debug( @@ -1932,7 +1973,9 @@ def sale_return(self): amount=price, progress_signal=None, operation_type=2, # возврат - error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) + error_callback=lambda title, msg, code: windows.info_window( + title, msg, str(code) + ), ) elif sale.status == 5: bank, payment = pq.universal_terminal_operation( @@ -1940,7 +1983,9 @@ def sale_return(self): amount=partial_return, progress_signal=None, operation_type=2, # возврат - error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) + error_callback=lambda title, msg, code: windows.info_window( + title, msg, str(code) + ), ) if bank == 1: check = pq.read_pinpad_file(remove_newline=False) @@ -2026,7 +2071,9 @@ def sale_return(self): amount=price, progress_signal=None, operation_type=2, # возврат - error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) + error_callback=lambda title, msg, code: windows.info_window( + title, msg, str(code) + ), ) if bank == 1: @@ -2102,7 +2149,9 @@ def sale_canceling(self): amount=price, progress_signal=None, operation_type=3, # отмена - error_callback=lambda title, msg, code: windows.info_window(title, msg, str(code)) + error_callback=lambda title, msg, code: windows.info_window( + title, msg, str(code) + ), ) if bank == 1: check = pq.read_pinpad_file(remove_newline=False) @@ -2287,7 +2336,7 @@ def is_printing_enabled(self) -> bool: Во всех остальных случаях возвращает False. """ # Проверяем наличие атрибута и что он не None - if not hasattr(system, 'ticket_print') or system.ticket_print is None: + if not hasattr(system, "ticket_print") or system.ticket_print is None: return False # Приводим к строке и сравниваем с 'on' @@ -2300,7 +2349,9 @@ def print_saved_tickets(self): """ logger.info("Запуск функции print_saved_tickets") if not self.is_printing_enabled(): - logger.info("Печать билетов отключена (параметр 'ticket' не равен 'on' или отсутствует)") + logger.info( + "Печать билетов отключена (параметр 'ticket' не равен 'on' или отсутствует)" + ) return try: self.sale_generate_saved_tickets() @@ -2361,9 +2412,7 @@ def open_pay_form(self, txt): # Оплата только для новых продаж if system.sale_status != 0: windows.info_window( - "Внимание", - "Оплата картой доступна только для новых продаж.", - "" + "Внимание", "Оплата картой доступна только для новых продаж.", "" ) return @@ -2371,8 +2420,7 @@ def open_pay_form(self, txt): logger.info("РЕЖИМ ОТЛАДКИ: Имитация оплаты картой") # Создаем тестовый слип-чек slip = "DEBUG_SLIP\nДата: {}\nСумма: {}\nОдобрено".format( - dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), - txt + dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), txt ) # Сохраняем тестовые данные self.save_debug_payment(Payment.Card, slip) @@ -2388,9 +2436,7 @@ def open_pay_form(self, txt): # Оплата только для новых продаж if system.sale_status != 0: windows.info_window( - "Внимание", - "Оплата наличными доступна только для новых продаж.", - "" + "Внимание", "Оплата наличными доступна только для новых продаж.", "" ) return logger.info("Оплата наличными") @@ -2402,14 +2448,13 @@ def open_pay_form(self, txt): windows.info_window( "Внимание", "Оплата картой доступна только для новых или не фискализированных продаж.", - "" + "", ) return if dev_mode: logger.info("РЕЖИМ ОТЛАДКИ: Имитация offline оплаты") slip = "DEBUG_SLIP_OFFLINE\nДата: {}\nСумма: {}\nОффлайн".format( - dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), - txt + dt.datetime.now().strftime("%d.%m.%Y %H:%M:%S"), txt ) self.save_debug_payment(Payment.Offline, slip) self.accept() @@ -2417,12 +2462,12 @@ def open_pay_form(self, txt): user_choice = windows.info_dialog_window( "Подтверждение", - f"Вы точно хотите фискализировать операцию?\n\n" - f"Это надо делать ТОЛЬКО после успешной проверки проведения операции по банковскому терминалу!\n\n" - f"Для этого выполните: Касса -> Операции с банковским терминалом -> Печать ранее подготовленного документа.\n\n" - f"Операция считается успешной, если в банковском слип-чеке:\n" - f" - сумма, дата и время проведения операции операции совпадают с данными из заказа;\n" - f" - указано слово 'ОДОБРЕНО'.", + "Вы точно хотите фискализировать операцию?\n\n" + "Это надо делать ТОЛЬКО после успешной проверки проведения операции по банковскому терминалу!\n\n" + "Для этого выполните: Касса -> Операции с банковским терминалом -> Печать ранее подготовленного документа.\n\n" + "Операция считается успешной, если в банковском слип-чеке:\n" + " - сумма, дата и время проведения операции операции совпадают с данными из заказа;\n" + " - указано слово 'ОДОБРЕНО'.", ) if user_choice == 1: logger.info("Оплата банковской картой offline") @@ -2435,7 +2480,9 @@ def open_pay_form(self, txt): def save_debug_payment(payment_type, slip_text): """Сохранение тестового платежа в режиме отладки""" try: - logger.debug(f"Сохранение тестового платежа: payment_type = {payment_type}, slip = {slip_text}") + logger.debug( + f"Сохранение тестового платежа: payment_type = {payment_type}, slip = {slip_text}" + ) # ... код для сохранения в БД ... except Exception as e: logger.error(f"Ошибка сохранения тестового платежа: {str(e)}") @@ -2515,7 +2562,9 @@ def get_slip(self) -> None: None: Функция не возвращает значений, отображает данные слип-чека в форме. """ logger.info("Запуск функции get_slip") - card_tail, merchant_id, rrn_value, load_slip = system.get_slip_data(system.sale_id) + card_tail, merchant_id, rrn_value, load_slip = system.get_slip_data( + system.sale_id + ) slip: SlipForm = SlipForm() slip.ui.label_5.setText(card_tail) @@ -2834,7 +2883,7 @@ def main_edit_client(self) -> None: client.show() # Сохраняем параметры данных об уже существующем клиенте system.client_update = 1 - logger.info(f"Обновляем информацию о клиенте") + logger.info("Обновляем информацию о клиенте") client.exec_() def main_filter_clear(self) -> None: @@ -3159,16 +3208,30 @@ def main_button_all_sales(self) -> None: else: # Определение периода для 1, 3 и 7 дней без учета времени if self.ui.radioButton.isChecked(): - filter_start = dt.datetime.today().replace(hour=0, minute=0, second=0, microsecond=0) - filter_end = dt.datetime.today().replace(hour=23, minute=59, second=59, microsecond=999999) + filter_start = dt.datetime.today().replace( + hour=0, minute=0, second=0, microsecond=0 + ) + filter_end = dt.datetime.today().replace( + hour=23, minute=59, second=59, microsecond=999999 + ) elif self.ui.radioButton_2.isChecked(): - filter_start = (dt.datetime.today() - timedelta(days=3)).replace(hour=0, minute=0, second=0, microsecond=0) - filter_end = dt.datetime.today().replace(hour=23, minute=59, second=59, microsecond=999999) + filter_start = (dt.datetime.today() - timedelta(days=3)).replace( + hour=0, minute=0, second=0, microsecond=0 + ) + filter_end = dt.datetime.today().replace( + hour=23, minute=59, second=59, microsecond=999999 + ) elif self.ui.radioButton_3.isChecked(): - filter_start = (dt.datetime.today() - timedelta(days=7)).replace(hour=0, minute=0, second=0, microsecond=0) - filter_end = dt.datetime.today().replace(hour=23, minute=59, second=59, microsecond=999999) + filter_start = (dt.datetime.today() - timedelta(days=7)).replace( + hour=0, minute=0, second=0, microsecond=0 + ) + filter_end = dt.datetime.today().replace( + hour=23, minute=59, second=59, microsecond=999999 + ) # Определяем, какие статусы фильтровать - sale_status_filter = [2, 3, 4, 5, 6, 8, 9] if self.ui.checkBox.isChecked() else [] + sale_status_filter = ( + [2, 3, 4, 5, 6, 8, 9] if self.ui.checkBox.isChecked() else [] + ) # Генерация запроса with Session(system.engine) as session: @@ -3314,7 +3377,7 @@ def main_open_sale(self) -> None: system.sale_dict.update(sale_initial_values) # сбрасываем id и статус продажи system.sale_id = None - system.sale_status = 0 # Т.е. новая продажа + system.sale_status = 0 # Т.е. новая продажа system.sale_tickets = None sale.exec_() @@ -3389,30 +3452,36 @@ def fetch_stat_data(self, dt1: str, dt2: str): logger.info("Запуск функции fetch_stat_data") with Session(system.engine) as session: sales = session.execute( - select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status) - .where(and_(Sale.status == "1", Sale.datetime.between(dt1, dt2))) + select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status).where( + and_(Sale.status == "1", Sale.datetime.between(dt1, dt2)) + ) ).all() sales_return = session.execute( - select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status) - .where(and_( - Sale.datetime_return.between(dt1, dt2), - Sale.status.in_([2, 3, 4, 5, 6]) - )) + select(Sale.pc_name, Sale.payment_type, Sale.price, Sale.status).where( + and_( + Sale.datetime_return.between(dt1, dt2), + Sale.status.in_([2, 3, 4, 5, 6]), + ) + ) ).all() tickets = session.execute( select( - Ticket.ticket_type, Ticket.arrival_time, Ticket.description, Sale.status, Sale.id, Ticket.price + Ticket.ticket_type, + Ticket.arrival_time, + Ticket.description, + Sale.status, + Sale.id, + Ticket.price, ).where( and_( Sale.id == Ticket.id_sale, Sale.status == "1", Ticket.datetime.between(dt1, dt2), - ) + ) ) ).all() return sales, sales_return, tickets - def fill_sales_table(self, data): """ Заполнение таблицы продаж данными. @@ -3431,9 +3500,9 @@ def fill_sales_table(self, data): for row, row_data in enumerate(data): for col, value in enumerate(row_data): if value is not None: - self.ui.tableWidget_4.setItem(row, col, QTableWidgetItem(str(value))) - - + self.ui.tableWidget_4.setItem( + row, col, QTableWidgetItem(str(value)) + ) def fill_ticket_table(self, data): """ @@ -3506,7 +3575,7 @@ def main_otchet_kassira(self) -> None: if os.path.exists(path): os.remove(path) # Проверяем, есть ли данные о продажах в system.sales_data_summary - if not hasattr(system, 'sales_data_summary') or not system.sales_data_summary: + if not hasattr(system, "sales_data_summary") or not system.sales_data_summary: logger.error("Нет данных о продажах для отчета кассира") return # Получаем имя текущего компьютера @@ -3529,14 +3598,16 @@ def main_otchet_kassira(self) -> None: str(total_sales_card), str(total_sales_cash), str(total_returns_card), - str(total_returns_cash) + str(total_returns_cash), ] # Получаем даты для отчета dt1, dt2 = self.get_date_range_from_ui() # Получаем информацию о кассире - kassir = system.user # Предполагаем, что данные о кассире хранятся в system.user + kassir = ( + system.user + ) # Предполагаем, что данные о кассире хранятся в system.user # Вызываем функцию для формирования отчета otchet.otchet_kassira(values, dt1, dt2, kassir) diff --git a/modules/config.py b/modules/config.py index 3cb0a17..722acc9 100644 --- a/modules/config.py +++ b/modules/config.py @@ -1,11 +1,7 @@ import logging import os -from configparser import ( - ConfigParser, - NoSectionError, - NoOptionError, - MissingSectionHeaderError, -) +from configparser import (ConfigParser, MissingSectionHeaderError, + NoOptionError, NoSectionError) from typing import List, Optional logger = logging.getLogger(__name__) @@ -122,7 +118,6 @@ def load_config(self): ) config_data[key] = config.get(section, key) - return config_data except (NoSectionError, NoOptionError) as e: logger.error(f"Ошибка в конфигурационном файле: {e}") diff --git a/modules/libfptr10.py b/modules/libfptr10.py index ced442d..7f92556 100644 --- a/modules/libfptr10.py +++ b/modules/libfptr10.py @@ -1,21 +1,21 @@ +# ruff: noqa: F403, F405 # -*- coding: utf-8 -*- # AUTO GENERATED FILE import ctypes -import sys -import json import datetime +import json import os import platform -import warnings +import sys if sys.version_info[0] == 3: - if platform.system() == 'Windows': + if platform.system() == "Windows": from winreg import * TEXT = str RANGE = range else: - if platform.system() == 'Windows': + if platform.system() == "Windows": from _winreg import * TEXT = basestring RANGE = xrange @@ -1672,231 +1672,294 @@ class IFptr(object): DEFAULT_BUFF_SIZE = 512 CREATE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.POINTER(ctypes.c_void_p)) - CREATE_WITH_ID_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.POINTER(ctypes.c_void_p), - ctypes.c_wchar_p) + CREATE_WITH_ID_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.POINTER(ctypes.c_void_p), ctypes.c_wchar_p + ) DESTROY_METHOD = ctypes.CFUNCTYPE(None, ctypes.POINTER(ctypes.c_void_p)) GET_VERSION_METHOD = ctypes.CFUNCTYPE(ctypes.c_char_p) - METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p) - - IS_OPENED_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p) - - GET_ERROR_CODE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p) - GET_ERROR_DESCRIPTION_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_wchar_p, - ctypes.c_int) - GET_ERROR_RECOMMENDATION_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_wchar_p, - ctypes.c_int) - RESET_ERROR_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p) - - SET_SETTINGS_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_wchar_p) - GET_SETTINGS_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_wchar_p, - ctypes.c_int) - - SET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_wchar_p, - ctypes.c_wchar_p) - GET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_wchar_p, - ctypes.c_wchar_p, - ctypes.c_int) - - SET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_int, - ctypes.POINTER(ctypes.c_ubyte), ctypes.c_int) - GET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_int, - ctypes.POINTER(ctypes.c_ubyte), ctypes.c_int) - - SET_INT_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_uint) - GET_INT_METHOD = ctypes.CFUNCTYPE(ctypes.c_uint, - ctypes.c_void_p, - ctypes.c_int) - - SET_BOOL_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_int) - GET_BOOL_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_int) - - SET_DOUBLE_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_double) - GET_DOUBLE_METHOD = ctypes.CFUNCTYPE(ctypes.c_double, - ctypes.c_void_p, - ctypes.c_int) - - SET_STRING_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_wchar_p) - GET_STRING_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_wchar_p, ctypes.c_int) - - SET_DATETIME_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, - ctypes.c_int, - ctypes.c_int) - GET_DATETIME_METHOD = ctypes.CFUNCTYPE(None, - ctypes.c_void_p, - ctypes.c_int, - ctypes.POINTER(ctypes.c_int), - ctypes.POINTER(ctypes.c_int), - ctypes.POINTER(ctypes.c_int), - ctypes.POINTER(ctypes.c_int), - ctypes.POINTER(ctypes.c_int), - ctypes.POINTER(ctypes.c_int)) - IS_PARAM_AVAILABLE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_int) - - LOG_WRITE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_wchar_p, - ctypes.c_int, - ctypes.c_wchar_p) - - SHOW_PROPERTIES_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_int, - ctypes.c_void_p) - - CHANGE_LABEL_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, - ctypes.c_void_p, - ctypes.c_wchar_p) + METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p) + + IS_OPENED_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p) + + GET_ERROR_CODE_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p) + GET_ERROR_DESCRIPTION_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int + ) + GET_ERROR_RECOMMENDATION_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int + ) + RESET_ERROR_METHOD = ctypes.CFUNCTYPE(None, ctypes.c_void_p) + + SET_SETTINGS_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p + ) + GET_SETTINGS_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int + ) + + SET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE( + None, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_wchar_p + ) + GET_SINGLE_SETTING_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_wchar_p, ctypes.c_int + ) + + SET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE( + None, + ctypes.c_void_p, + ctypes.c_int, + ctypes.POINTER(ctypes.c_ubyte), + ctypes.c_int, + ) + GET_BYTEARRAY_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, + ctypes.c_void_p, + ctypes.c_int, + ctypes.POINTER(ctypes.c_ubyte), + ctypes.c_int, + ) + + SET_INT_METHOD = ctypes.CFUNCTYPE( + None, ctypes.c_void_p, ctypes.c_int, ctypes.c_uint + ) + GET_INT_METHOD = ctypes.CFUNCTYPE(ctypes.c_uint, ctypes.c_void_p, ctypes.c_int) + + SET_BOOL_METHOD = ctypes.CFUNCTYPE( + None, ctypes.c_void_p, ctypes.c_int, ctypes.c_int + ) + GET_BOOL_METHOD = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_void_p, ctypes.c_int) + + SET_DOUBLE_METHOD = ctypes.CFUNCTYPE( + None, ctypes.c_void_p, ctypes.c_int, ctypes.c_double + ) + GET_DOUBLE_METHOD = ctypes.CFUNCTYPE(ctypes.c_double, ctypes.c_void_p, ctypes.c_int) + + SET_STRING_METHOD = ctypes.CFUNCTYPE( + None, ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p + ) + GET_STRING_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_wchar_p, ctypes.c_int + ) + + SET_DATETIME_METHOD = ctypes.CFUNCTYPE( + None, + ctypes.c_void_p, + ctypes.c_int, + ctypes.c_int, + ctypes.c_int, + ctypes.c_int, + ctypes.c_int, + ctypes.c_int, + ctypes.c_int, + ) + GET_DATETIME_METHOD = ctypes.CFUNCTYPE( + None, + ctypes.c_void_p, + ctypes.c_int, + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ctypes.POINTER(ctypes.c_int), + ) + IS_PARAM_AVAILABLE_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_int + ) + + LOG_WRITE_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p, ctypes.c_int, ctypes.c_wchar_p + ) + + SHOW_PROPERTIES_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_int, ctypes.c_void_p + ) + + CHANGE_LABEL_METHOD = ctypes.CFUNCTYPE( + ctypes.c_int, ctypes.c_void_p, ctypes.c_wchar_p + ) def __init__(self, lib_path="", fptr_id=""): assert sys.version_info >= (2, 6) self.lib_path = lib_path try: - if platform.system() == 'Windows': + if platform.system() == "Windows": if len(self.lib_path) == 0: - rk = OpenKey(HKEY_LOCAL_MACHINE, "Software\\ATOL\\Drivers\\10.0\\KKT") + rk = OpenKey( + HKEY_LOCAL_MACHINE, "Software\\ATOL\\Drivers\\10.0\\KKT" + ) rv = QueryValueEx(rk, "INSTALL_DIR")[0] - self.lib_path = os.path.join(rv, 'bin', 'fptr10.dll') + self.lib_path = os.path.join(rv, "bin", "fptr10.dll") else: - if not self.lib_path.endswith('fptr10.dll'): - self.lib_path = os.path.join(self.lib_path, 'fptr10.dll') + if not self.lib_path.endswith("fptr10.dll"): + self.lib_path = os.path.join(self.lib_path, "fptr10.dll") try: self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) except OSError: - ctypes.CDLL(os.path.join(os.path.dirname(self.lib_path), 'msvcp140.dll'), mode=ctypes.RTLD_LOCAL) + ctypes.CDLL( + os.path.join(os.path.dirname(self.lib_path), "msvcp140.dll"), + mode=ctypes.RTLD_LOCAL, + ) self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) - elif platform.system() == 'Darwin': - if not self.lib_path.endswith('fptr10.framework/fptr10'): - self.lib_path = os.path.join(self.lib_path, 'fptr10.framework/fptr10') + elif platform.system() == "Darwin": + if not self.lib_path.endswith("fptr10.framework/fptr10"): + self.lib_path = os.path.join( + self.lib_path, "fptr10.framework/fptr10" + ) try: self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) except OSError: self.lib_path = lib_path - if not self.lib_path.endswith('libfptr10.dylib'): - self.lib_path = os.path.join(self.lib_path, 'libfptr10.dylib') + if not self.lib_path.endswith("libfptr10.dylib"): + self.lib_path = os.path.join(self.lib_path, "libfptr10.dylib") self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) else: - if not self.lib_path.endswith('libfptr10.so'): - self.lib_path = os.path.join(self.lib_path, 'libfptr10.so') + if not self.lib_path.endswith("libfptr10.so"): + self.lib_path = os.path.join(self.lib_path, "libfptr10.so") self.library = ctypes.CDLL(self.lib_path, mode=ctypes.RTLD_LOCAL) except OSError: raise Exception( - 'Driver library not found in {}'.format( - '\"' + - os.path.dirname( - self.lib_path) + - '\"' if len(lib_path) != 0 else 'search folders')) + "Driver library not found in {}".format( + '"' + os.path.dirname(self.lib_path) + '"' + if len(lib_path) != 0 + else "search folders" + ) + ) self.interface = ctypes.c_void_p(0) if fptr_id: - _create = self.CREATE_WITH_ID_METHOD(('libfptr_create_with_id', self.library)) - create_r = _create(ctypes.pointer(self.interface), ctypes.c_wchar_p(fptr_id)) + _create = self.CREATE_WITH_ID_METHOD( + ("libfptr_create_with_id", self.library) + ) + create_r = _create( + ctypes.pointer(self.interface), ctypes.c_wchar_p(fptr_id) + ) else: - _create = self.CREATE_METHOD(('libfptr_create', self.library)) + _create = self.CREATE_METHOD(("libfptr_create", self.library)) create_r = _create(ctypes.pointer(self.interface)) if create_r == -2: - raise AttributeError('Invalid [id] format') + raise AttributeError("Invalid [id] format") elif create_r != 0: - raise Exception('Can`t create driver handle') - - self._setByteArray = self.SET_BYTEARRAY_METHOD(('libfptr_set_param_bytearray', self.library)) - self._setUserByteArray = self.SET_BYTEARRAY_METHOD(('libfptr_set_user_param_bytearray', self.library)) + raise Exception("Can`t create driver handle") + + self._setByteArray = self.SET_BYTEARRAY_METHOD( + ("libfptr_set_param_bytearray", self.library) + ) + self._setUserByteArray = self.SET_BYTEARRAY_METHOD( + ("libfptr_set_user_param_bytearray", self.library) + ) self._setNonPrintableByteArray = self.SET_BYTEARRAY_METHOD( - ('libfptr_set_non_printable_param_bytearray', self.library)) - self._getByteArray = self.GET_BYTEARRAY_METHOD(('libfptr_get_param_bytearray', self.library)) - - self._setInt = self.SET_INT_METHOD(('libfptr_set_param_int', self.library)) - self._setUserInt = self.SET_INT_METHOD(('libfptr_set_user_param_int', self.library)) - self._setNonPrintableInt = self.SET_INT_METHOD(('libfptr_set_non_printable_param_int', self.library)) - self._getInt = self.GET_INT_METHOD(('libfptr_get_param_int', self.library)) - - self._setBool = self.SET_BOOL_METHOD(('libfptr_set_param_bool', self.library)) - self._setUserBool = self.SET_BOOL_METHOD(('libfptr_set_user_param_bool', self.library)) - self._setNonPrintableBool = self.SET_BOOL_METHOD(('libfptr_set_non_printable_param_bool', self.library)) - self._getBool = self.GET_BOOL_METHOD(('libfptr_get_param_bool', self.library)) - - self._setDouble = self.SET_DOUBLE_METHOD(('libfptr_set_param_double', self.library)) - self._setUserDouble = self.SET_DOUBLE_METHOD(('libfptr_set_user_param_double', self.library)) - self._setNonPrintableDouble = self.SET_DOUBLE_METHOD(('libfptr_set_non_printable_param_double', self.library)) - self._getDouble = self.GET_DOUBLE_METHOD(('libfptr_get_param_double', self.library)) - - self._setDateTime = self.SET_DATETIME_METHOD(('libfptr_set_param_datetime', self.library)) - self._setUserDateTime = self.SET_DATETIME_METHOD(('libfptr_set_user_param_datetime', self.library)) + ("libfptr_set_non_printable_param_bytearray", self.library) + ) + self._getByteArray = self.GET_BYTEARRAY_METHOD( + ("libfptr_get_param_bytearray", self.library) + ) + + self._setInt = self.SET_INT_METHOD(("libfptr_set_param_int", self.library)) + self._setUserInt = self.SET_INT_METHOD( + ("libfptr_set_user_param_int", self.library) + ) + self._setNonPrintableInt = self.SET_INT_METHOD( + ("libfptr_set_non_printable_param_int", self.library) + ) + self._getInt = self.GET_INT_METHOD(("libfptr_get_param_int", self.library)) + + self._setBool = self.SET_BOOL_METHOD(("libfptr_set_param_bool", self.library)) + self._setUserBool = self.SET_BOOL_METHOD( + ("libfptr_set_user_param_bool", self.library) + ) + self._setNonPrintableBool = self.SET_BOOL_METHOD( + ("libfptr_set_non_printable_param_bool", self.library) + ) + self._getBool = self.GET_BOOL_METHOD(("libfptr_get_param_bool", self.library)) + + self._setDouble = self.SET_DOUBLE_METHOD( + ("libfptr_set_param_double", self.library) + ) + self._setUserDouble = self.SET_DOUBLE_METHOD( + ("libfptr_set_user_param_double", self.library) + ) + self._setNonPrintableDouble = self.SET_DOUBLE_METHOD( + ("libfptr_set_non_printable_param_double", self.library) + ) + self._getDouble = self.GET_DOUBLE_METHOD( + ("libfptr_get_param_double", self.library) + ) + + self._setDateTime = self.SET_DATETIME_METHOD( + ("libfptr_set_param_datetime", self.library) + ) + self._setUserDateTime = self.SET_DATETIME_METHOD( + ("libfptr_set_user_param_datetime", self.library) + ) self._setNonPrintableDateTime = self.SET_DATETIME_METHOD( - ('libfptr_set_non_printable_param_datetime', self.library)) - self._getDateTime = self.GET_DATETIME_METHOD(('libfptr_get_param_datetime', self.library)) - - self._setString = self.SET_STRING_METHOD(('libfptr_set_param_str', self.library)) - self._setUserString = self.SET_STRING_METHOD(('libfptr_set_user_param_str', self.library)) - self._setNonPrintableString = self.SET_STRING_METHOD(('libfptr_set_non_printable_param_str', self.library)) - self._getString = self.GET_STRING_METHOD(('libfptr_get_param_str', self.library)) - - self._isParamAvailable = self.IS_PARAM_AVAILABLE_METHOD(('libfptr_is_param_available', self.library)) - - self._setSettings = self.SET_SETTINGS_METHOD(('libfptr_set_settings', self.library)) - self._getSettings = self.GET_SETTINGS_METHOD(('libfptr_get_settings', self.library)) - - self._getSingleSetting = self.GET_SINGLE_SETTING_METHOD(('libfptr_get_single_setting', self.library)) - self._setSingleSetting = self.SET_SINGLE_SETTING_METHOD(('libfptr_set_single_setting', self.library)) - - self._getVersion = self.GET_VERSION_METHOD(('libfptr_get_version_string', self.library)) - self._isOpened = self.IS_OPENED_METHOD(('libfptr_is_opened', self.library)) - self._errorCode = self.GET_ERROR_CODE_METHOD(('libfptr_error_code', self.library)) - self._errorDescription = self.GET_ERROR_DESCRIPTION_METHOD(('libfptr_error_description', self.library)) - self._errorRecommendation = self.GET_ERROR_RECOMMENDATION_METHOD(('libfptr_error_recommendation', self.library)) - self._resetError = self.RESET_ERROR_METHOD(('libfptr_reset_error', self.library)) - self._logWrite = self.LOG_WRITE_METHOD(('libfptr_log_write_ex', self.library)) - self._showProperties = self.SHOW_PROPERTIES_METHOD(('libfptr_show_properties', self.library)) - self._changeLabel = self.CHANGE_LABEL_METHOD(('libfptr_change_label', self.library)) + ("libfptr_set_non_printable_param_datetime", self.library) + ) + self._getDateTime = self.GET_DATETIME_METHOD( + ("libfptr_get_param_datetime", self.library) + ) + + self._setString = self.SET_STRING_METHOD( + ("libfptr_set_param_str", self.library) + ) + self._setUserString = self.SET_STRING_METHOD( + ("libfptr_set_user_param_str", self.library) + ) + self._setNonPrintableString = self.SET_STRING_METHOD( + ("libfptr_set_non_printable_param_str", self.library) + ) + self._getString = self.GET_STRING_METHOD( + ("libfptr_get_param_str", self.library) + ) + + self._isParamAvailable = self.IS_PARAM_AVAILABLE_METHOD( + ("libfptr_is_param_available", self.library) + ) + + self._setSettings = self.SET_SETTINGS_METHOD( + ("libfptr_set_settings", self.library) + ) + self._getSettings = self.GET_SETTINGS_METHOD( + ("libfptr_get_settings", self.library) + ) + + self._getSingleSetting = self.GET_SINGLE_SETTING_METHOD( + ("libfptr_get_single_setting", self.library) + ) + self._setSingleSetting = self.SET_SINGLE_SETTING_METHOD( + ("libfptr_set_single_setting", self.library) + ) + + self._getVersion = self.GET_VERSION_METHOD( + ("libfptr_get_version_string", self.library) + ) + self._isOpened = self.IS_OPENED_METHOD(("libfptr_is_opened", self.library)) + self._errorCode = self.GET_ERROR_CODE_METHOD( + ("libfptr_error_code", self.library) + ) + self._errorDescription = self.GET_ERROR_DESCRIPTION_METHOD( + ("libfptr_error_description", self.library) + ) + self._errorRecommendation = self.GET_ERROR_RECOMMENDATION_METHOD( + ("libfptr_error_recommendation", self.library) + ) + self._resetError = self.RESET_ERROR_METHOD( + ("libfptr_reset_error", self.library) + ) + self._logWrite = self.LOG_WRITE_METHOD(("libfptr_log_write_ex", self.library)) + self._showProperties = self.SHOW_PROPERTIES_METHOD( + ("libfptr_show_properties", self.library) + ) + self._changeLabel = self.CHANGE_LABEL_METHOD( + ("libfptr_change_label", self.library) + ) def __del__(self): - destroy = self.DESTROY_METHOD(('libfptr_destroy', self.library)) + destroy = self.DESTROY_METHOD(("libfptr_destroy", self.library)) destroy(ctypes.pointer(self.interface)) def version(self): @@ -1957,7 +2020,7 @@ def getSettingsStr(self): def setSingleSetting(self, key, value): if isinstance(value, bool): - value = '1' if value else '0' + value = "1" if value else "0" elif isinstance(value, int): value = str(value) self._setSingleSetting(self.interface, key, value) @@ -1973,75 +2036,147 @@ def getSingleSetting(self, key): def setParam(self, paramId, param): if isinstance(param, bool): self._setBool(self.interface, ctypes.c_int(paramId), ctypes.c_int(param)) - elif isinstance(param, int) or (sys.version_info < (3, 0) and isinstance(param, long)): + elif isinstance(param, int) or ( + sys.version_info < (3, 0) and isinstance(param, long) + ): if param > 4294967295: raise ValueError("Invalid 'param' value {0}".format(param)) if param < 0: - self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) + self._setDouble( + self.interface, ctypes.c_int(paramId), ctypes.c_double(param) + ) else: - self._setInt(self.interface, ctypes.c_int(paramId), ctypes.c_uint(param)) + self._setInt( + self.interface, ctypes.c_int(paramId), ctypes.c_uint(param) + ) elif isinstance(param, float): - self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) + self._setDouble( + self.interface, ctypes.c_int(paramId), ctypes.c_double(param) + ) elif isinstance(param, TEXT): - self._setString(self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param)) - elif isinstance(param, list) or isinstance(param, bytearray) or isinstance(param, bytes): - self._setByteArray(self.interface, ctypes.c_int(paramId), - (ctypes.c_ubyte * len(param))(*param), len(param)) + self._setString( + self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param) + ) + elif ( + isinstance(param, list) + or isinstance(param, bytearray) + or isinstance(param, bytes) + ): + self._setByteArray( + self.interface, + ctypes.c_int(paramId), + (ctypes.c_ubyte * len(param))(*param), + len(param), + ) elif isinstance(param, datetime.datetime): - self._setDateTime(self.interface, ctypes.c_int(paramId), param.date().year, - param.date().month, - param.date().day, - param.time().hour, param.time().minute, param.time().second) + self._setDateTime( + self.interface, + ctypes.c_int(paramId), + param.date().year, + param.date().month, + param.date().day, + param.time().hour, + param.time().minute, + param.time().second, + ) else: raise TypeError("Invalid 'param' type {0}".format(type(param))) def setUserParam(self, paramId, param): if isinstance(param, bool): - self._setUserBool(self.interface, ctypes.c_int(paramId), ctypes.c_int(param)) + self._setUserBool( + self.interface, ctypes.c_int(paramId), ctypes.c_int(param) + ) elif isinstance(param, int): if param > 4294967295: raise ValueError("Invalid 'param' value {0}".format(param)) if param < 0: - self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) + self._setDouble( + self.interface, ctypes.c_int(paramId), ctypes.c_double(param) + ) else: - self._setInt(self.interface, ctypes.c_int(paramId), ctypes.c_uint(param)) + self._setInt( + self.interface, ctypes.c_int(paramId), ctypes.c_uint(param) + ) elif isinstance(param, float): - self._setUserDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) + self._setUserDouble( + self.interface, ctypes.c_int(paramId), ctypes.c_double(param) + ) elif isinstance(param, TEXT): - self._setUserString(self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param)) - elif isinstance(param, list) or isinstance(param, bytearray) or isinstance(param, bytes): - self._setUserByteArray(self.interface, ctypes.c_int(paramId), - (ctypes.c_ubyte * len(param))(*param), len(param)) + self._setUserString( + self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param) + ) + elif ( + isinstance(param, list) + or isinstance(param, bytearray) + or isinstance(param, bytes) + ): + self._setUserByteArray( + self.interface, + ctypes.c_int(paramId), + (ctypes.c_ubyte * len(param))(*param), + len(param), + ) elif isinstance(param, datetime.datetime): - self._setUserDateTime(self.interface, ctypes.c_int(paramId), param.date().year, - param.date().month, - param.date().day, - param.time().hour, param.time().minute, param.time().second) + self._setUserDateTime( + self.interface, + ctypes.c_int(paramId), + param.date().year, + param.date().month, + param.date().day, + param.time().hour, + param.time().minute, + param.time().second, + ) else: raise TypeError("Invalid 'param' type {0}".format(type(param))) def setNonPrintableParam(self, paramId, param): if isinstance(param, bool): - self._setNonPrintableBool(self.interface, ctypes.c_int(paramId), ctypes.c_int(param)) + self._setNonPrintableBool( + self.interface, ctypes.c_int(paramId), ctypes.c_int(param) + ) elif isinstance(param, int): if param > 4294967295: raise ValueError("Invalid 'param' value {0}".format(param)) if param < 0: - self._setDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) + self._setDouble( + self.interface, ctypes.c_int(paramId), ctypes.c_double(param) + ) else: - self._setInt(self.interface, ctypes.c_int(paramId), ctypes.c_uint(param)) + self._setInt( + self.interface, ctypes.c_int(paramId), ctypes.c_uint(param) + ) elif isinstance(param, float): - self._setNonPrintableDouble(self.interface, ctypes.c_int(paramId), ctypes.c_double(param)) + self._setNonPrintableDouble( + self.interface, ctypes.c_int(paramId), ctypes.c_double(param) + ) elif isinstance(param, TEXT): - self._setNonPrintableString(self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param)) - elif isinstance(param, list) or isinstance(param, bytearray) or isinstance(param, bytes): - self._setNonPrintableByteArray(self.interface, ctypes.c_int(paramId), - (ctypes.c_ubyte * len(param))(*param), len(param)) + self._setNonPrintableString( + self.interface, ctypes.c_int(paramId), ctypes.c_wchar_p(param) + ) + elif ( + isinstance(param, list) + or isinstance(param, bytearray) + or isinstance(param, bytes) + ): + self._setNonPrintableByteArray( + self.interface, + ctypes.c_int(paramId), + (ctypes.c_ubyte * len(param))(*param), + len(param), + ) elif isinstance(param, datetime.datetime): - self._setNonPrintableDateTime(self.interface, ctypes.c_int(paramId), param.date().year, - param.date().month, - param.date().day, - param.time().hour, param.time().minute, param.time().second) + self._setNonPrintableDateTime( + self.interface, + ctypes.c_int(paramId), + param.date().year, + param.date().month, + param.date().day, + param.time().hour, + param.time().minute, + param.time().second, + ) else: raise TypeError("Invalid 'param' type {0}".format(type(param))) @@ -2059,13 +2194,20 @@ def getParamDouble(self, paramId): def getParamByteArray(self, paramId): value = (ctypes.c_ubyte * self.DEFAULT_BUFF_SIZE)() - size = self._getByteArray(self.interface, ctypes.c_int(paramId), - ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), - self.DEFAULT_BUFF_SIZE) + size = self._getByteArray( + self.interface, + ctypes.c_int(paramId), + ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), + self.DEFAULT_BUFF_SIZE, + ) if size > self.DEFAULT_BUFF_SIZE: value = (ctypes.c_ubyte * size)() - size = self._getByteArray(self.interface, ctypes.c_int(paramId), - ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), size) + size = self._getByteArray( + self.interface, + ctypes.c_int(paramId), + ctypes.cast(value, ctypes.POINTER(ctypes.c_ubyte)), + size, + ) return value[:size] def getParamDateTime(self, paramId): @@ -2075,16 +2217,25 @@ def getParamDateTime(self, paramId): hour = ctypes.c_int(0) minute = ctypes.c_int(0) second = ctypes.c_int(0) - self._getDateTime(self.interface, ctypes.c_int(paramId), ctypes.pointer(year), - ctypes.pointer(month), - ctypes.pointer(day), - ctypes.pointer(hour), ctypes.pointer(minute), ctypes.pointer(second)) - return datetime.datetime(year.value, month.value, day.value, hour.value, minute.value, - second.value) + self._getDateTime( + self.interface, + ctypes.c_int(paramId), + ctypes.pointer(year), + ctypes.pointer(month), + ctypes.pointer(day), + ctypes.pointer(hour), + ctypes.pointer(minute), + ctypes.pointer(second), + ) + return datetime.datetime( + year.value, month.value, day.value, hour.value, minute.value, second.value + ) def getParamString(self, paramId): value = ctypes.create_unicode_buffer(self.DEFAULT_BUFF_SIZE) - size = self._getString(self.interface, ctypes.c_int(paramId), value, self.DEFAULT_BUFF_SIZE) + size = self._getString( + self.interface, ctypes.c_int(paramId), value, self.DEFAULT_BUFF_SIZE + ) if size > self.DEFAULT_BUFF_SIZE: value = ctypes.create_unicode_buffer(size) self._getString(self.interface, ctypes.c_int(paramId), value, size) @@ -2095,485 +2246,495 @@ def isParamAvailable(self, paramId): return value > 0 def applySingleSettings(self): - _method = self.METHOD(('libfptr_apply_single_settings', self.library)) + _method = self.METHOD(("libfptr_apply_single_settings", self.library)) return _method(self.interface) def open(self): - _method = self.METHOD(('libfptr_open', self.library)) + _method = self.METHOD(("libfptr_open", self.library)) return _method(self.interface) def close(self): - _method = self.METHOD(('libfptr_close', self.library)) + _method = self.METHOD(("libfptr_close", self.library)) return _method(self.interface) def resetParams(self): - _method = self.METHOD(('libfptr_reset_params', self.library)) + _method = self.METHOD(("libfptr_reset_params", self.library)) return _method(self.interface) def runCommand(self): - _method = self.METHOD(('libfptr_run_command', self.library)) + _method = self.METHOD(("libfptr_run_command", self.library)) return _method(self.interface) def beep(self): - _method = self.METHOD(('libfptr_beep', self.library)) + _method = self.METHOD(("libfptr_beep", self.library)) return _method(self.interface) def openDrawer(self): - _method = self.METHOD(('libfptr_open_drawer', self.library)) + _method = self.METHOD(("libfptr_open_drawer", self.library)) return _method(self.interface) def cut(self): - _method = self.METHOD(('libfptr_cut', self.library)) + _method = self.METHOD(("libfptr_cut", self.library)) return _method(self.interface) def devicePoweroff(self): - _method = self.METHOD(('libfptr_device_poweroff', self.library)) + _method = self.METHOD(("libfptr_device_poweroff", self.library)) return _method(self.interface) def deviceReboot(self): - _method = self.METHOD(('libfptr_device_reboot', self.library)) + _method = self.METHOD(("libfptr_device_reboot", self.library)) return _method(self.interface) def openShift(self): - _method = self.METHOD(('libfptr_open_shift', self.library)) + _method = self.METHOD(("libfptr_open_shift", self.library)) return _method(self.interface) def resetSummary(self): - _method = self.METHOD(('libfptr_reset_summary', self.library)) + _method = self.METHOD(("libfptr_reset_summary", self.library)) return _method(self.interface) def initDevice(self): - _method = self.METHOD(('libfptr_init_device', self.library)) + _method = self.METHOD(("libfptr_init_device", self.library)) return _method(self.interface) def queryData(self): - _method = self.METHOD(('libfptr_query_data', self.library)) + _method = self.METHOD(("libfptr_query_data", self.library)) return _method(self.interface) def cashIncome(self): - _method = self.METHOD(('libfptr_cash_income', self.library)) + _method = self.METHOD(("libfptr_cash_income", self.library)) return _method(self.interface) def cashOutcome(self): - _method = self.METHOD(('libfptr_cash_outcome', self.library)) + _method = self.METHOD(("libfptr_cash_outcome", self.library)) return _method(self.interface) def openReceipt(self): - _method = self.METHOD(('libfptr_open_receipt', self.library)) + _method = self.METHOD(("libfptr_open_receipt", self.library)) return _method(self.interface) def cancelReceipt(self): - _method = self.METHOD(('libfptr_cancel_receipt', self.library)) + _method = self.METHOD(("libfptr_cancel_receipt", self.library)) return _method(self.interface) def closeReceipt(self): - _method = self.METHOD(('libfptr_close_receipt', self.library)) + _method = self.METHOD(("libfptr_close_receipt", self.library)) return _method(self.interface) def checkDocumentClosed(self): - _method = self.METHOD(('libfptr_check_document_closed', self.library)) + _method = self.METHOD(("libfptr_check_document_closed", self.library)) return _method(self.interface) def receiptTotal(self): - _method = self.METHOD(('libfptr_receipt_total', self.library)) + _method = self.METHOD(("libfptr_receipt_total", self.library)) return _method(self.interface) def receiptTax(self): - _method = self.METHOD(('libfptr_receipt_tax', self.library)) + _method = self.METHOD(("libfptr_receipt_tax", self.library)) return _method(self.interface) def registration(self): - _method = self.METHOD(('libfptr_registration', self.library)) + _method = self.METHOD(("libfptr_registration", self.library)) return _method(self.interface) def payment(self): - _method = self.METHOD(('libfptr_payment', self.library)) + _method = self.METHOD(("libfptr_payment", self.library)) return _method(self.interface) def report(self): - _method = self.METHOD(('libfptr_report', self.library)) + _method = self.METHOD(("libfptr_report", self.library)) return _method(self.interface) def printText(self): - _method = self.METHOD(('libfptr_print_text', self.library)) + _method = self.METHOD(("libfptr_print_text", self.library)) return _method(self.interface) def printCliche(self): - _method = self.METHOD(('libfptr_print_cliche', self.library)) + _method = self.METHOD(("libfptr_print_cliche", self.library)) return _method(self.interface) def beginNonfiscalDocument(self): - _method = self.METHOD(('libfptr_begin_nonfiscal_document', self.library)) + _method = self.METHOD(("libfptr_begin_nonfiscal_document", self.library)) return _method(self.interface) def endNonfiscalDocument(self): - _method = self.METHOD(('libfptr_end_nonfiscal_document', self.library)) + _method = self.METHOD(("libfptr_end_nonfiscal_document", self.library)) return _method(self.interface) def printBarcode(self): - _method = self.METHOD(('libfptr_print_barcode', self.library)) + _method = self.METHOD(("libfptr_print_barcode", self.library)) return _method(self.interface) def printPicture(self): - _method = self.METHOD(('libfptr_print_picture', self.library)) + _method = self.METHOD(("libfptr_print_picture", self.library)) return _method(self.interface) def printPictureByNumber(self): - _method = self.METHOD(('libfptr_print_picture_by_number', self.library)) + _method = self.METHOD(("libfptr_print_picture_by_number", self.library)) return _method(self.interface) def uploadPictureFromFile(self): - _method = self.METHOD(('libfptr_upload_picture_from_file', self.library)) + _method = self.METHOD(("libfptr_upload_picture_from_file", self.library)) return _method(self.interface) def clearPictures(self): - _method = self.METHOD(('libfptr_clear_pictures', self.library)) + _method = self.METHOD(("libfptr_clear_pictures", self.library)) return _method(self.interface) def writeDeviceSettingRaw(self): - _method = self.METHOD(('libfptr_write_device_setting_raw', self.library)) + _method = self.METHOD(("libfptr_write_device_setting_raw", self.library)) return _method(self.interface) def readDeviceSettingRaw(self): - _method = self.METHOD(('libfptr_read_device_setting_raw', self.library)) + _method = self.METHOD(("libfptr_read_device_setting_raw", self.library)) return _method(self.interface) def commitSettings(self): - _method = self.METHOD(('libfptr_commit_settings', self.library)) + _method = self.METHOD(("libfptr_commit_settings", self.library)) return _method(self.interface) def initSettings(self): - _method = self.METHOD(('libfptr_init_settings', self.library)) + _method = self.METHOD(("libfptr_init_settings", self.library)) return _method(self.interface) def resetSettings(self): - _method = self.METHOD(('libfptr_reset_settings', self.library)) + _method = self.METHOD(("libfptr_reset_settings", self.library)) return _method(self.interface) def writeDateTime(self): - _method = self.METHOD(('libfptr_write_date_time', self.library)) + _method = self.METHOD(("libfptr_write_date_time", self.library)) return _method(self.interface) def writeLicense(self): - _method = self.METHOD(('libfptr_write_license', self.library)) + _method = self.METHOD(("libfptr_write_license", self.library)) return _method(self.interface) def fnOperation(self): - _method = self.METHOD(('libfptr_fn_operation', self.library)) + _method = self.METHOD(("libfptr_fn_operation", self.library)) return _method(self.interface) def fnQueryData(self): - _method = self.METHOD(('libfptr_fn_query_data', self.library)) + _method = self.METHOD(("libfptr_fn_query_data", self.library)) return _method(self.interface) def fnWriteAttributes(self): - _method = self.METHOD(('libfptr_fn_write_attributes', self.library)) + _method = self.METHOD(("libfptr_fn_write_attributes", self.library)) return _method(self.interface) def externalDevicePowerOn(self): - _method = self.METHOD(('libfptr_external_device_power_on', self.library)) + _method = self.METHOD(("libfptr_external_device_power_on", self.library)) return _method(self.interface) def externalDevicePowerOff(self): - _method = self.METHOD(('libfptr_external_device_power_off', self.library)) + _method = self.METHOD(("libfptr_external_device_power_off", self.library)) return _method(self.interface) def externalDeviceWriteData(self): - _method = self.METHOD(('libfptr_external_device_write_data', self.library)) + _method = self.METHOD(("libfptr_external_device_write_data", self.library)) return _method(self.interface) def externalDeviceReadData(self): - _method = self.METHOD(('libfptr_external_device_read_data', self.library)) + _method = self.METHOD(("libfptr_external_device_read_data", self.library)) return _method(self.interface) def operatorLogin(self): - _method = self.METHOD(('libfptr_operator_login', self.library)) + _method = self.METHOD(("libfptr_operator_login", self.library)) return _method(self.interface) def processJson(self): - _method = self.METHOD(('libfptr_process_json', self.library)) + _method = self.METHOD(("libfptr_process_json", self.library)) return _method(self.interface) def readDeviceSetting(self): - _method = self.METHOD(('libfptr_read_device_setting', self.library)) + _method = self.METHOD(("libfptr_read_device_setting", self.library)) return _method(self.interface) def writeDeviceSetting(self): - _method = self.METHOD(('libfptr_write_device_setting', self.library)) + _method = self.METHOD(("libfptr_write_device_setting", self.library)) return _method(self.interface) def beginReadRecords(self): - _method = self.METHOD(('libfptr_begin_read_records', self.library)) + _method = self.METHOD(("libfptr_begin_read_records", self.library)) return _method(self.interface) def readNextRecord(self): - _method = self.METHOD(('libfptr_read_next_record', self.library)) + _method = self.METHOD(("libfptr_read_next_record", self.library)) return _method(self.interface) def endReadRecords(self): - _method = self.METHOD(('libfptr_end_read_records', self.library)) + _method = self.METHOD(("libfptr_end_read_records", self.library)) return _method(self.interface) def userMemoryOperation(self): - _method = self.METHOD(('libfptr_user_memory_operation', self.library)) + _method = self.METHOD(("libfptr_user_memory_operation", self.library)) return _method(self.interface) def continuePrint(self): - _method = self.METHOD(('libfptr_continue_print', self.library)) + _method = self.METHOD(("libfptr_continue_print", self.library)) return _method(self.interface) def initMgm(self): - _method = self.METHOD(('libfptr_init_mgm', self.library)) + _method = self.METHOD(("libfptr_init_mgm", self.library)) return _method(self.interface) def utilFormTlv(self): - _method = self.METHOD(('libfptr_util_form_tlv', self.library)) + _method = self.METHOD(("libfptr_util_form_tlv", self.library)) return _method(self.interface) def utilFormNomenclature(self): - _method = self.METHOD(('libfptr_util_form_nomenclature', self.library)) + _method = self.METHOD(("libfptr_util_form_nomenclature", self.library)) return _method(self.interface) def utilMapping(self): - _method = self.METHOD(('libfptr_util_mapping', self.library)) + _method = self.METHOD(("libfptr_util_mapping", self.library)) return _method(self.interface) def readModelFlags(self): - _method = self.METHOD(('libfptr_read_model_flags', self.library)) + _method = self.METHOD(("libfptr_read_model_flags", self.library)) return _method(self.interface) def lineFeed(self): - _method = self.METHOD(('libfptr_line_feed', self.library)) + _method = self.METHOD(("libfptr_line_feed", self.library)) return _method(self.interface) def flashFirmware(self): - _method = self.METHOD(('libfptr_flash_firmware', self.library)) + _method = self.METHOD(("libfptr_flash_firmware", self.library)) return _method(self.interface) def softLockInit(self): - _method = self.METHOD(('libfptr_soft_lock_init', self.library)) + _method = self.METHOD(("libfptr_soft_lock_init", self.library)) return _method(self.interface) def softLockQuerySessionCode(self): - _method = self.METHOD(('libfptr_soft_lock_query_session_code', self.library)) + _method = self.METHOD(("libfptr_soft_lock_query_session_code", self.library)) return _method(self.interface) def softLockValidate(self): - _method = self.METHOD(('libfptr_soft_lock_validate', self.library)) + _method = self.METHOD(("libfptr_soft_lock_validate", self.library)) return _method(self.interface) def utilCalcTax(self): - _method = self.METHOD(('libfptr_util_calc_tax', self.library)) + _method = self.METHOD(("libfptr_util_calc_tax", self.library)) return _method(self.interface) def downloadPicture(self): - _method = self.METHOD(('libfptr_download_picture', self.library)) + _method = self.METHOD(("libfptr_download_picture", self.library)) return _method(self.interface) def bluetoothRemovePairedDevices(self): - _method = self.METHOD(('libfptr_bluetooth_remove_paired_devices', self.library)) + _method = self.METHOD(("libfptr_bluetooth_remove_paired_devices", self.library)) return _method(self.interface) def utilTagInfo(self): - _method = self.METHOD(('libfptr_util_tag_info', self.library)) + _method = self.METHOD(("libfptr_util_tag_info", self.library)) return _method(self.interface) def utilContainerVersions(self): - _method = self.METHOD(('libfptr_util_container_versions', self.library)) + _method = self.METHOD(("libfptr_util_container_versions", self.library)) return _method(self.interface) def activateLicenses(self): - _method = self.METHOD(('libfptr_activate_licenses', self.library)) + _method = self.METHOD(("libfptr_activate_licenses", self.library)) return _method(self.interface) def removeLicenses(self): - _method = self.METHOD(('libfptr_remove_licenses', self.library)) + _method = self.METHOD(("libfptr_remove_licenses", self.library)) return _method(self.interface) def enterKeys(self): - _method = self.METHOD(('libfptr_enter_keys', self.library)) + _method = self.METHOD(("libfptr_enter_keys", self.library)) return _method(self.interface) def validateKeys(self): - _method = self.METHOD(('libfptr_validate_keys', self.library)) + _method = self.METHOD(("libfptr_validate_keys", self.library)) return _method(self.interface) def enterSerialNumber(self): - _method = self.METHOD(('libfptr_enter_serial_number', self.library)) + _method = self.METHOD(("libfptr_enter_serial_number", self.library)) return _method(self.interface) def getSerialNumberRequest(self): - _method = self.METHOD(('libfptr_get_serial_number_request', self.library)) + _method = self.METHOD(("libfptr_get_serial_number_request", self.library)) return _method(self.interface) def uploadPixelBuffer(self): - _method = self.METHOD(('libfptr_upload_pixel_buffer', self.library)) + _method = self.METHOD(("libfptr_upload_pixel_buffer", self.library)) return _method(self.interface) def downloadPixelBuffer(self): - _method = self.METHOD(('libfptr_download_pixel_buffer', self.library)) + _method = self.METHOD(("libfptr_download_pixel_buffer", self.library)) return _method(self.interface) def printPixelBuffer(self): - _method = self.METHOD(('libfptr_print_pixel_buffer', self.library)) + _method = self.METHOD(("libfptr_print_pixel_buffer", self.library)) return _method(self.interface) def utilConvertTagValue(self): - _method = self.METHOD(('libfptr_util_convert_tag_value', self.library)) + _method = self.METHOD(("libfptr_util_convert_tag_value", self.library)) return _method(self.interface) def parseMarkingCode(self): - _method = self.METHOD(('libfptr_parse_marking_code', self.library)) + _method = self.METHOD(("libfptr_parse_marking_code", self.library)) return _method(self.interface) def callScript(self): - _method = self.METHOD(('libfptr_call_script', self.library)) + _method = self.METHOD(("libfptr_call_script", self.library)) return _method(self.interface) def setHeaderLines(self): - _method = self.METHOD(('libfptr_set_header_lines', self.library)) + _method = self.METHOD(("libfptr_set_header_lines", self.library)) return _method(self.interface) def setFooterLines(self): - _method = self.METHOD(('libfptr_set_footer_lines', self.library)) + _method = self.METHOD(("libfptr_set_footer_lines", self.library)) return _method(self.interface) def uploadPictureCliche(self): - _method = self.METHOD(('libfptr_upload_picture_cliche', self.library)) + _method = self.METHOD(("libfptr_upload_picture_cliche", self.library)) return _method(self.interface) def uploadPictureMemory(self): - _method = self.METHOD(('libfptr_upload_picture_memory', self.library)) + _method = self.METHOD(("libfptr_upload_picture_memory", self.library)) return _method(self.interface) def uploadPixelBufferCliche(self): - _method = self.METHOD(('libfptr_upload_pixel_buffer_cliche', self.library)) + _method = self.METHOD(("libfptr_upload_pixel_buffer_cliche", self.library)) return _method(self.interface) def uploadPixelBufferMemory(self): - _method = self.METHOD(('libfptr_upload_pixel_buffer_memory', self.library)) + _method = self.METHOD(("libfptr_upload_pixel_buffer_memory", self.library)) return _method(self.interface) def execDriverScript(self): - _method = self.METHOD(('libfptr_exec_driver_script', self.library)) + _method = self.METHOD(("libfptr_exec_driver_script", self.library)) return _method(self.interface) def uploadDriverScript(self): - _method = self.METHOD(('libfptr_upload_driver_script', self.library)) + _method = self.METHOD(("libfptr_upload_driver_script", self.library)) return _method(self.interface) def execDriverScriptById(self): - _method = self.METHOD(('libfptr_exec_driver_script_by_id', self.library)) + _method = self.METHOD(("libfptr_exec_driver_script_by_id", self.library)) return _method(self.interface) def writeUniversalCountersSettings(self): - _method = self.METHOD(('libfptr_write_universal_counters_settings', self.library)) + _method = self.METHOD( + ("libfptr_write_universal_counters_settings", self.library) + ) return _method(self.interface) def readUniversalCountersSettings(self): - _method = self.METHOD(('libfptr_read_universal_counters_settings', self.library)) + _method = self.METHOD( + ("libfptr_read_universal_counters_settings", self.library) + ) return _method(self.interface) def queryUniversalCountersState(self): - _method = self.METHOD(('libfptr_query_universal_counters_state', self.library)) + _method = self.METHOD(("libfptr_query_universal_counters_state", self.library)) return _method(self.interface) def resetUniversalCounters(self): - _method = self.METHOD(('libfptr_reset_universal_counters', self.library)) + _method = self.METHOD(("libfptr_reset_universal_counters", self.library)) return _method(self.interface) def cacheUniversalCounters(self): - _method = self.METHOD(('libfptr_cache_universal_counters', self.library)) + _method = self.METHOD(("libfptr_cache_universal_counters", self.library)) return _method(self.interface) def readUniversalCounterSum(self): - _method = self.METHOD(('libfptr_read_universal_counter_sum', self.library)) + _method = self.METHOD(("libfptr_read_universal_counter_sum", self.library)) return _method(self.interface) def readUniversalCounterQuantity(self): - _method = self.METHOD(('libfptr_read_universal_counter_quantity', self.library)) + _method = self.METHOD(("libfptr_read_universal_counter_quantity", self.library)) return _method(self.interface) def clearUniversalCountersCache(self): - _method = self.METHOD(('libfptr_clear_universal_counters_cache', self.library)) + _method = self.METHOD(("libfptr_clear_universal_counters_cache", self.library)) return _method(self.interface) def disableOfdChannel(self): - _method = self.METHOD(('libfptr_disable_ofd_channel', self.library)) + _method = self.METHOD(("libfptr_disable_ofd_channel", self.library)) return _method(self.interface) def enableOfdChannel(self): - _method = self.METHOD(('libfptr_enable_ofd_channel', self.library)) + _method = self.METHOD(("libfptr_enable_ofd_channel", self.library)) return _method(self.interface) def validateJson(self): - _method = self.METHOD(('libfptr_validate_json', self.library)) + _method = self.METHOD(("libfptr_validate_json", self.library)) return _method(self.interface) def reflectionCall(self): - _method = self.METHOD(('libfptr_reflection_call', self.library)) + _method = self.METHOD(("libfptr_reflection_call", self.library)) return _method(self.interface) def getRemoteServerInfo(self): - _method = self.METHOD(('libfptr_get_remote_server_info', self.library)) + _method = self.METHOD(("libfptr_get_remote_server_info", self.library)) return _method(self.interface) def beginMarkingCodeValidation(self): - _method = self.METHOD(('libfptr_begin_marking_code_validation', self.library)) + _method = self.METHOD(("libfptr_begin_marking_code_validation", self.library)) return _method(self.interface) def cancelMarkingCodeValidation(self): - _method = self.METHOD(('libfptr_cancel_marking_code_validation', self.library)) + _method = self.METHOD(("libfptr_cancel_marking_code_validation", self.library)) return _method(self.interface) def getMarkingCodeValidationStatus(self): - _method = self.METHOD(('libfptr_get_marking_code_validation_status', self.library)) + _method = self.METHOD( + ("libfptr_get_marking_code_validation_status", self.library) + ) return _method(self.interface) def acceptMarkingCode(self): - _method = self.METHOD(('libfptr_accept_marking_code', self.library)) + _method = self.METHOD(("libfptr_accept_marking_code", self.library)) return _method(self.interface) def declineMarkingCode(self): - _method = self.METHOD(('libfptr_decline_marking_code', self.library)) + _method = self.METHOD(("libfptr_decline_marking_code", self.library)) return _method(self.interface) def updateFnmKeys(self): - _method = self.METHOD(('libfptr_update_fnm_keys', self.library)) + _method = self.METHOD(("libfptr_update_fnm_keys", self.library)) return _method(self.interface) def writeSalesNotice(self): - _method = self.METHOD(('libfptr_write_sales_notice', self.library)) + _method = self.METHOD(("libfptr_write_sales_notice", self.library)) return _method(self.interface) def checkMarkingCodeValidationsReady(self): - _method = self.METHOD(('libfptr_check_marking_code_validations_ready', self.library)) + _method = self.METHOD( + ("libfptr_check_marking_code_validations_ready", self.library) + ) return _method(self.interface) def clearMarkingCodeValidationResult(self): - _method = self.METHOD(('libfptr_clear_marking_code_validation_result', self.library)) + _method = self.METHOD( + ("libfptr_clear_marking_code_validation_result", self.library) + ) return _method(self.interface) def pingMarkingServer(self): - _method = self.METHOD(('libfptr_ping_marking_server', self.library)) + _method = self.METHOD(("libfptr_ping_marking_server", self.library)) return _method(self.interface) def getMarkingServerStatus(self): - _method = self.METHOD(('libfptr_get_marking_server_status', self.library)) + _method = self.METHOD(("libfptr_get_marking_server_status", self.library)) return _method(self.interface) def isDriverLocked(self): - _method = self.METHOD(('libfptr_is_driver_locked', self.library)) + _method = self.METHOD(("libfptr_is_driver_locked", self.library)) return _method(self.interface) def getLastDocumentJournal(self): - _method = self.METHOD(('libfptr_get_last_document_journal', self.library)) + _method = self.METHOD(("libfptr_get_last_document_journal", self.library)) return _method(self.interface) def findDocumentInJournal(self): - _method = self.METHOD(('libfptr_find_document_in_journal', self.library)) + _method = self.METHOD(("libfptr_find_document_in_journal", self.library)) return _method(self.interface) def runFnCommand(self): - _method = self.METHOD(('libfptr_run_fn_command', self.library)) + _method = self.METHOD(("libfptr_run_fn_command", self.library)) return _method(self.interface) diff --git a/modules/otchet.py b/modules/otchet.py index 965f03a..f1482a7 100644 --- a/modules/otchet.py +++ b/modules/otchet.py @@ -3,7 +3,7 @@ from reportlab.lib import colors from reportlab.lib.pagesizes import A4, landscape, letter -from reportlab.lib.units import mm, inch +from reportlab.lib.units import inch, mm from reportlab.pdfbase import pdfmetrics from reportlab.pdfbase.ttfonts import TTFont from reportlab.pdfgen import canvas @@ -17,7 +17,7 @@ config = Config() -def get_ticket_type(age:str) -> str: +def get_ticket_type(age: str) -> str: """ Определяет тип билета на основе возраста клиента. @@ -45,6 +45,7 @@ def get_ticket_type(age:str) -> str: else: return "взрослый" + def generate_saved_tickets(values: List[tuple]) -> None: """ Генерирует PDF-файл с билетами для клиентов на основе переданных данных. @@ -72,32 +73,79 @@ def generate_saved_tickets(values: List[tuple]) -> None: pdfmetrics.registerFont(TTFont("DejaVuSerif", "files/DejaVuSerif.ttf")) c = canvas.Canvas(path, pagesize=(landscape(letter))) for client in values: - (last_name, name, ticket_flag, price, category, client_id, age, hours, talents, date_time) = client + ( + last_name, + name, + ticket_flag, + price, + category, + client_id, + age, + hours, + talents, + date_time, + ) = client ticket_type = get_ticket_type(age) if ticket_type == "бесплатный" or category == "н": continue # Пропускаем бесплатных и "не идущих" c.setFont("DejaVuSerif", 12) # x - расстояние мм от левого края страницы # y - расстояние мм от нижнего края страницы - c.drawString(coordinates["name"]["x"] * mm,coordinates["name"]["y"] * mm, name) - c.drawString(coordinates["surname"]["x"] * mm, coordinates["surname"]["y"] * mm, last_name) - c.drawString(coordinates["age"]["x"] * mm, coordinates["age"]["y"] * mm, str(age)) + c.drawString(coordinates["name"]["x"] * mm, coordinates["name"]["y"] * mm, name) + c.drawString( + coordinates["surname"]["x"] * mm, + coordinates["surname"]["y"] * mm, + last_name, + ) + c.drawString( + coordinates["age"]["x"] * mm, coordinates["age"]["y"] * mm, str(age) + ) duration_text = f"{hours} ч. пребывания" if hours == 3: duration_text += "-весь день" - c.drawString(coordinates["duration"]["x"] * mm, coordinates["duration"]["y"] * mm, duration_text) + c.drawString( + coordinates["duration"]["x"] * mm, + coordinates["duration"]["y"] * mm, + duration_text, + ) if isinstance(date_time, str): date_time = datetime.strptime(date_time, "%Y-%m-%d") - c.drawString(coordinates["date"]["x"] * mm, coordinates["date"]["y"] * mm, date_time.strftime('%d.%m.%Y')) - c.drawString(coordinates["guest"]["x"] * mm, coordinates["guest"]["y"] * mm, "гость") - c.drawString(coordinates["city"]["x"] * mm, coordinates["city"]["y"] * mm, "БЕЛГОРОД") - c.drawString(coordinates["place"]["x"] * mm, coordinates["place"]["y"] * mm, "МАСТЕРСЛАВЛЬ") - c.drawString(coordinates["price"]["x"] * mm, coordinates["price"]["y"] * mm, f"{price} руб.") - c.drawString(coordinates["ticket_type"]["x"] * mm, coordinates["ticket_type"]["y"] * mm, ticket_type) - c.drawString(coordinates["notes"]["x"] * mm, coordinates["notes"]["y"] * mm, category) + c.drawString( + coordinates["date"]["x"] * mm, + coordinates["date"]["y"] * mm, + date_time.strftime("%d.%m.%Y"), + ) + c.drawString( + coordinates["guest"]["x"] * mm, coordinates["guest"]["y"] * mm, "гость" + ) + c.drawString( + coordinates["city"]["x"] * mm, coordinates["city"]["y"] * mm, "БЕЛГОРОД" + ) + c.drawString( + coordinates["place"]["x"] * mm, + coordinates["place"]["y"] * mm, + "МАСТЕРСЛАВЛЬ", + ) + c.drawString( + coordinates["price"]["x"] * mm, + coordinates["price"]["y"] * mm, + f"{price} руб.", + ) + c.drawString( + coordinates["ticket_type"]["x"] * mm, + coordinates["ticket_type"]["y"] * mm, + ticket_type, + ) + c.drawString( + coordinates["notes"]["x"] * mm, coordinates["notes"]["y"] * mm, category + ) c.setFont("DejaVuSerif", 24) talents_str = "0" if ticket_type == "взрослый" else str(talents) - c.drawString(coordinates["talents"]["x"] * mm, coordinates["talents"]["y"] * mm, talents_str) + c.drawString( + coordinates["talents"]["x"] * mm, + coordinates["talents"]["y"] * mm, + talents_str, + ) c.drawImage( img_file, coordinates["qr_code"]["x"], @@ -134,13 +182,30 @@ def generate_ticket_report_table(ticket_summary: dict) -> list[list]: """ logger.info("Запуск функции generate_ticket_report_table") type_ticket = [ - "Взрослый, 1 ч.", "Взрослый, 2 ч.", "Взрослый, 3 ч.", - "Детский, 1 ч.", "Детский, 2 ч.", "Детский, 3 ч.", - "Многодетный взрослый, 1 ч.", "Многодетный взрослый, 2 ч.", "Многодетный взрослый, 3 ч.", - "Многодетный детский, 1 ч.", "Многодетный детский, 2 ч.", "Многодетный детский, 3 ч.", - "Инвалид, 3 ч.", "Сопровождающий, 3 ч.", + "Взрослый, 1 ч.", + "Взрослый, 2 ч.", + "Взрослый, 3 ч.", + "Детский, 1 ч.", + "Детский, 2 ч.", + "Детский, 3 ч.", + "Многодетный взрослый, 1 ч.", + "Многодетный взрослый, 2 ч.", + "Многодетный взрослый, 3 ч.", + "Многодетный детский, 1 ч.", + "Многодетный детский, 2 ч.", + "Многодетный детский, 3 ч.", + "Инвалид, 3 ч.", + "Сопровождающий, 3 ч.", + ] + data = [ + [ + "№\n п/п", + "Тип\nбилета", + "Цена,\n руб.", + "Количество,\n шт.", + "Стоимость,\n руб.", + ] ] - data = [["№\n п/п", "Тип\nбилета", "Цена,\n руб.", "Количество,\n шт.", "Стоимость,\n руб."]] # Счётчики для агрегатов total = { "adult": {"count": 0, "sum": 0}, @@ -182,22 +247,56 @@ def generate_ticket_report_table(ticket_summary: dict) -> list[list]: # Агрегированные строки data += [ - ["", "Всего взрослых билетов", "", total["adult"]["count"], total["adult"]["sum"]], - ["", "Всего детских билетов", "", total["child"]["count"], total["child"]["sum"]], - ["", "Всего многодетных взрослых билетов", "", total["many_adult"]["count"], total["many_adult"]["sum"]], - ["", "Всего многодетных детских билетов", "", total["many_child"]["count"], total["many_child"]["sum"]], + [ + "", + "Всего взрослых билетов", + "", + total["adult"]["count"], + total["adult"]["sum"], + ], + [ + "", + "Всего детских билетов", + "", + total["child"]["count"], + total["child"]["sum"], + ], + [ + "", + "Всего многодетных взрослых билетов", + "", + total["many_adult"]["count"], + total["many_adult"]["sum"], + ], + [ + "", + "Всего многодетных детских билетов", + "", + total["many_child"]["count"], + total["many_child"]["sum"], + ], ["", "Инвалид, 3 ч.", "", total["disabled"]["count"], "0"], ["", "Сопровождающий, 3 ч.", "", total["maintainer"]["count"], "0"], - ["", "Итого билетов", "", - total["adult"]["count"] + total["child"]["count"] + - total["many_adult"]["count"] + total["many_child"]["count"] + - total["disabled"]["count"] + total["maintainer"]["count"], - total["adult"]["sum"] + total["child"]["sum"] + - total["many_adult"]["sum"] + total["many_child"]["sum"]], + [ + "", + "Итого билетов", + "", + total["adult"]["count"] + + total["child"]["count"] + + total["many_adult"]["count"] + + total["many_child"]["count"] + + total["disabled"]["count"] + + total["maintainer"]["count"], + total["adult"]["sum"] + + total["child"]["sum"] + + total["many_adult"]["sum"] + + total["many_child"]["sum"], + ], ] return data + def otchet_administratora(date_1: str, date_2: str, values: Dict) -> None: """ Функция формирует отчет администратора в формате PDF. @@ -253,15 +352,22 @@ def otchet_administratora(date_1: str, date_2: str, values: Dict) -> None: table_width = sum(custom_widths) if table_width > (page_width - left_margin - right_margin): # Корректировка ширины второго столбца - remaining_width = page_width - left_margin - right_margin - (sum(custom_widths) - custom_widths[1]) + remaining_width = ( + page_width + - left_margin + - right_margin + - (sum(custom_widths) - custom_widths[1]) + ) custom_widths[1] = remaining_width t = Table(data, colWidths=custom_widths, rowHeights=[0.3 * inch] * len(data)) t.setStyle( - TableStyle([ - ("FONT", (0, 0), (-1, -1), "DejaVuSerif", 8), - ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), - ("BOX", (0, 0), (-1, -1), 0.25, colors.black), - ]) + TableStyle( + [ + ("FONT", (0, 0), (-1, -1), "DejaVuSerif", 8), + ("INNERGRID", (0, 0), (-1, -1), 0.25, colors.black), + ("BOX", (0, 0), (-1, -1), 0.25, colors.black), + ] + ) ) t.wrapOn(c, left_margin, 180 * mm) t.drawOn(c, left_margin, 60 * mm) @@ -281,6 +387,7 @@ def otchet_administratora(date_1: str, date_2: str, values: Dict) -> None: c.showPage() c.save() + def safe_int(value: Any) -> int: """ Преобразует значение в целое число, если это возможно. Если значение None, строка 'None', @@ -295,14 +402,22 @@ def safe_int(value: Any) -> int: if isinstance(value, (list, dict, set, tuple)): # Проверка на неподобающие типы return 0 try: - if value not in [None, 'None']: - return int(float(value)) # Преобразование строки с плавающей точкой в целое число + if value not in [None, "None"]: + return int( + float(value) + ) # Преобразование строки с плавающей точкой в целое число else: return 0 except (ValueError, TypeError): # Обработка ошибок для некорректных значений return 0 -def format_date_range(date1_str: str, date2_str: str, input_format: str = "%Y-%m-%d %H:%M:%S", output_format: str = "%d-%m-%Y") -> Tuple[str, str]: + +def format_date_range( + date1_str: str, + date2_str: str, + input_format: str = "%Y-%m-%d %H:%M:%S", + output_format: str = "%d-%m-%Y", +) -> Tuple[str, str]: """ Преобразует строки с датами из одного формата в другой и возвращает кортеж из двух отформатированных строк. @@ -321,6 +436,7 @@ def format_date_range(date1_str: str, date2_str: str, input_format: str = "%Y-%m dt2 = date_2.strftime(output_format) return dt1, dt2 + def otchet_kassira(val: List[int], date1: str, date2: str, kassir: Any) -> None: """ Формирует PDF-отчет кассира за указанный период с разбивкой по типам оплат и возвратов. @@ -345,7 +461,9 @@ def otchet_kassira(val: List[int], date1: str, date2: str, kassir: Any) -> None: logger.info("Запуск функции otchet_kassira") # Проверка на пустой список или недостаточное количество элементов if not val or len(val) < 4: - logger.error("Некорректные данные для отчета: данные не переданы или их недостаточно.") + logger.error( + "Некорректные данные для отчета: данные не переданы или их недостаточно." + ) # Завершаем функцию, если данных недостаточно или они пустые return path = "./otchet.pdf" @@ -423,10 +541,21 @@ def otchet_kassira(val: List[int], date1: str, date2: str, kassir: Any) -> None: c.showPage() c.save() + def process_sales_and_returns( sales: List[Tuple[str, int, Union[int, float]]], - sales_return: List[Tuple[str, int, Union[int, float], int]] -) -> List[Tuple[Union[str, None], Union[int, float, None], Union[int, float, None], Union[int, float, None], Union[int, float, None], Union[int, float, None], Union[int, float]]]: + sales_return: List[Tuple[str, int, Union[int, float], int]], +) -> List[ + Tuple[ + Union[str, None], + Union[int, float, None], + Union[int, float, None], + Union[int, float, None], + Union[int, float, None], + Union[int, float, None], + Union[int, float], + ] +]: """ Обрабатывает продажи и возвраты, агрегируя информацию по каждому кассовому аппарату. Функция вычисляет суммы по картам и наличным, а также учитывает возвраты по каждому типу: @@ -462,16 +591,23 @@ def process_sales_and_returns( logger.info("Запуск функции process_sales_and_returns") pcs = { name: { - "card": 0, "cash": 0, - "return": 0, "return_again": 0, + "card": 0, + "cash": 0, + "return": 0, + "return_again": 0, "return_partial": 0, - "return_card": 0, "return_cash": 0, + "return_card": 0, + "return_cash": 0, } for name in system.pcs } type_rm = [1, 2, 3] # 1 - карта, 2 - наличные, 3 - offline - return_fields: dict[int, str] = {2: "return", 4: "return_again", 6: "return_partial"} + return_fields: dict[int, str] = { + 2: "return", + 4: "return_again", + 6: "return_partial", + } for sale in sales: pc_name = sale[0] @@ -501,33 +637,40 @@ def process_sales_and_returns( data = [] for name in system.pcs: stats = pcs[name] - data.append(( - name, - stats["card"], - stats["cash"], - None, - stats["return_card"], - stats["return_cash"], - stats["return_card"] + stats["return_cash"] - )) + data.append( + ( + name, + stats["card"], + stats["cash"], + None, + stats["return_card"], + stats["return_cash"], + stats["return_card"] + stats["return_cash"], + ) + ) total_card = sum(pc["card"] for pc in pcs.values()) total_cash = sum(pc["cash"] for pc in pcs.values()) total_return = sum(pc["return_card"] + pc["return_cash"] for pc in pcs.values()) - data.append(( - "Итого", - total_card, - total_cash, - total_card + total_cash, - None, - None, - total_return - )) + data.append( + ( + "Итого", + total_card, + total_cash, + total_card + total_cash, + None, + None, + total_return, + ) + ) system.sales_data_summary = data return data -def process_ticket_stats(tickets: List[Tuple[int, int, str, object, object, int]]) -> List[Tuple[str, Dict[str, int]]]: + +def process_ticket_stats( + tickets: List[Tuple[int, int, str, object, object, int]], +) -> List[Tuple[str, Dict[str, int]]]: """ Обрабатывает информацию о билетах, суммируя количество и стоимость проданных билетов по категориям и времени пребывания. Возвращает данные, которые могут быть использованы для отображения статистики @@ -555,10 +698,18 @@ def process_ticket_stats(tickets: List[Tuple[int, int, str, object, object, int] ) TICKET_TYPE_MAP = { - (0, 1): "Взрослый, 1 ч.", (0, 2): "Взрослый, 2 ч.", (0, 3): "Взрослый, 3 ч.", - (1, 1): "Детский, 1 ч.", (1, 2): "Детский, 2 ч.", (1, 3): "Детский, 3 ч.", - (2, 1): "Многодетный взрослый, 1 ч.", (2, 2): "Многодетный взрослый, 2 ч.", (2, 3): "Многодетный взрослый, 3 ч.", - (3, 1): "Многодетный детский, 1 ч.", (3, 2): "Многодетный детский, 2 ч.", (3, 3): "Многодетный детский, 3 ч.", + (0, 1): "Взрослый, 1 ч.", + (0, 2): "Взрослый, 2 ч.", + (0, 3): "Взрослый, 3 ч.", + (1, 1): "Детский, 1 ч.", + (1, 2): "Детский, 2 ч.", + (1, 3): "Детский, 3 ч.", + (2, 1): "Многодетный взрослый, 1 ч.", + (2, 2): "Многодетный взрослый, 2 ч.", + (2, 3): "Многодетный взрослый, 3 ч.", + (3, 1): "Многодетный детский, 1 ч.", + (3, 2): "Многодетный детский, 2 ч.", + (3, 3): "Многодетный детский, 3 ч.", (4, 3): "Инвалид, 3 ч.", (5, 3): "Сопровождающий, 3 ч.", } diff --git a/modules/payment_equipment.py b/modules/payment_equipment.py index 9b2005a..dad926f 100644 --- a/modules/payment_equipment.py +++ b/modules/payment_equipment.py @@ -14,10 +14,12 @@ # Получаем параметр available try: - kkt_available = config.get("available") == 'on' + kkt_available = config.get("available") == "on" except (KeyError, ValueError): kkt_available = False - logger.info("Параметр 'available' не найден в конфигурации, ККТ считается отключенным") + logger.info( + "Параметр 'available' не найден в конфигурации, ККТ считается отключенным" + ) # Инициализация драйвера ККТ try: @@ -164,7 +166,7 @@ def fptr_connection(device): device.open() connected = True - if not hasattr(device, 'isOpened') or not device.isOpened(): + if not hasattr(device, "isOpened") or not device.isOpened(): logger.warning("Не удалось открыть соединение с ККТ") yield None return @@ -211,18 +213,24 @@ def run_terminal_command(command_params: str, timeout: int = 300): logger.info(f"Запуск команды: {pinpad_run}") try: process = subprocess.Popen( - pinpad_run, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - shell=True # Важно для строки команды + pinpad_run, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + shell=True, # Важно для строки команды ) try: stdout, stderr = process.communicate(timeout=timeout) except subprocess.TimeoutExpired: - logger.warning(f"Процесс превысил таймаут {timeout} секунд. Завершается автоматически.") + logger.warning( + f"Процесс превысил таймаут {timeout} секунд. Завершается автоматически." + ) process.terminate() try: stdout, stderr = process.communicate(timeout=5) except subprocess.TimeoutExpired: - logger.warning("Процесс не завершился после terminate(). Используется kill().") + logger.warning( + "Процесс не завершился после terminate(). Используется kill()." + ) process.kill() stdout, stderr = process.communicate() @@ -296,6 +304,7 @@ def _safe_handle_error(returncode, title, message, error_callback): except Exception as e: logger.exception(f"Ошибка при вызове handle_error для кода {returncode}: {e}") + @logger_wraps() def handle_error(code, title, message, error_callback=None): """Универсальная обработка ошибок терминала. @@ -343,15 +352,15 @@ def process_terminal_error(returncode, error_callback=None): error_handlers = { TERMINAL_USER_CANCEL_CODE: ( "Оплата отменена пользователем", - f"Оплата отменена пользователем. Код возврата: {returncode}." + f"Оплата отменена пользователем. Код возврата: {returncode}.", ), TERMINAL_USER_TIMEOUT: ( "Слишком долгий ввод ПИН-кода", - f"Слишком долгий ввод ПИН-кода. Код возврата: {returncode}." + f"Слишком долгий ввод ПИН-кода. Код возврата: {returncode}.", ), TERMINAL_INVALID_CURRENCY_CODE: ( "Указан неверный код валюты", - f"Указан неверный код валюты. Обратитесь в банк. Телефон поддержки {TERMINAL_SUPPORT}. Код: {returncode}." + f"Указан неверный код валюты. Обратитесь в банк. Телефон поддержки {TERMINAL_SUPPORT}. Код: {returncode}.", ), TERMINAL_NO_ADDRESS_TO_CONTACT: ( "Терминал потерял связь с банком", @@ -359,7 +368,7 @@ def process_terminal_error(returncode, error_callback=None): ), TERMINAL_OPERATION_CANCELED: ( "Операция отменена", - f"Ошибка возникает тогда, когда карту достают из терминала быстрее, чем пройдет оплата. Необходимо повторить операцию.", + "Ошибка возникает тогда, когда карту достают из терминала быстрее, чем пройдет оплата. Необходимо повторить операцию.", ), TERMINAL_QR_ERROR: ( "Ошибка операции по QR-коду", @@ -377,8 +386,8 @@ def process_terminal_error(returncode, error_callback=None): _safe_handle_error( returncode, "Карта клиента заблокирована", - f"Карта клиента заблокирована. Попробуйте произвести оплату другой картой или обратитесь в банк для выяснения причины.", - error_callback + "Карта клиента заблокирована. Попробуйте произвести оплату другой картой или обратитесь в банк для выяснения причины.", + error_callback, ) return 0 if returncode in TERMINAL_CARD_LIMIT: @@ -386,7 +395,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Превышен лимит операций", "Превышен лимит операций. Попробуйте произвести оплату другой картой или обратитесь в банк для выяснения причины.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_BIOMETRIC_ERROR: @@ -394,7 +403,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Ошибка в работе с биометрическими данными", f"Ошибка в работе с биометрическими данными. Обратитесь в банк для выяснения причины. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_ERROR_PIN_CODE: @@ -402,7 +411,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Ошибка при вводе ПИН-кода", "ПИН-код не был введен, либо введен неверно, либо вводимый ПИН-код заблокирован. Попробуйте повторить операцию оплаты.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_NO_CONNECTION_BANK: @@ -410,7 +419,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Нет связи с банком", f"Попробуйте повторить операцию через пару минут. При повторении ошибки необходимо обратиться в службу поддержки. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_NEED_CASH_COLLECTION: @@ -418,7 +427,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Необходимо произвести инкассацию", f"Необходимо произвести инкассацию. Обратитесь в службу поддержки. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_LIMIT_OPERATION: @@ -426,7 +435,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Превышен лимит операций", f"Превышен лимит операций. Обратитесь в службу поддержки. Телефон. тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_DATA_EXCHANGE: @@ -434,7 +443,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Требуется сделать сверку итогов", "Требуется сделать сверку итогов и повторить операцию.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_KLK: @@ -442,7 +451,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Ошибка в работе терминала", f"Необходимо обратиться в службу поддержки банка. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_CARD_ERROR: @@ -450,7 +459,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Операцию невозможно выполнить для этой карты", f"Необходимо повторить попытку. Если проблема сохраняется – использовать другую карту. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_SERVER_ROUTINE_MAINTENANCE: @@ -458,15 +467,15 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Сервера Сбербанка недоступны", f"Сервера Сбербанка находятся на обслуживании/ремонте/регламентных работах. Попробуйте повторить операцию позже. Если проблема сохраняется - обратитесь в службу поддержки. Телефон тех.поддержки {TERMINAL_SUPPORT}.Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_NO_MONEY: _safe_handle_error( returncode, "Недостаточно средств на карте", - f"Недостаточно средств на карте. Попробуйте произвести оплату другой картой.", - error_callback + "Недостаточно средств на карте. Попробуйте произвести оплату другой картой.", + error_callback, ) return 0 if returncode in TERMINAL_COMMAND_ERROR: @@ -474,7 +483,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Нет нужного варианта связи для операции", f"Нет нужного варианта связи для операции. Обратитесь в службу поддержки ПО. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_PIN_PAD_ERROR: @@ -482,7 +491,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Проблема в работе ПИН-пада.", f"Необходимо обратиться в службу поддержки банка. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 # Общие ошибки @@ -499,7 +508,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Ошибка терминала", f"Возвращен неизвестный код ошибки. Обратитесь в службу поддержки. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 @@ -519,7 +528,9 @@ def terminal_oplata(amount: float) -> int: @logger_wraps() -def process_terminal_transaction(command_code: str, amount: float, operation_name: str, error_callback=None) -> int: +def process_terminal_transaction( + command_code: str, amount: float, operation_name: str, error_callback=None +) -> int: """ Обрабатывает операцию на банковском терминале. @@ -541,10 +552,10 @@ def process_terminal_transaction(command_code: str, amount: float, operation_nam if result is None: logger.error(f"Ошибка при выполнении команды терминала: {command}") _safe_handle_error( - f"Нет ответа от терминала", + "Нет ответа от терминала", f"{operation_name} не была выполнена. Проверьте устройство.", "Код ошибки: отсутствует", - error_callback=error_callback + error_callback=error_callback, ) return 0 @@ -567,11 +578,12 @@ def process_terminal_transaction(command_code: str, amount: float, operation_nam def universal_terminal_operation( - payment_type: int, - amount: float, - progress_signal, - operation_type: int = 1, - error_callback=None) -> tuple[int, int]: + payment_type: int, + amount: float, + progress_signal, + operation_type: int = 1, + error_callback=None, +) -> tuple[int, int]: """ Универсальный обработчик терминальных операций для оплаты или возврата. @@ -621,7 +633,9 @@ def universal_terminal_operation( except Exception: pass # Игнорируем ошибки emit - bank = process_terminal_transaction(command_code, amount, operation_name, error_callback) + bank = process_terminal_transaction( + command_code, amount, operation_name, error_callback + ) if bank == 1: if progress_signal is not None: try: @@ -638,12 +652,16 @@ def universal_terminal_operation( return 0, 1 elif payment_type == PAYMENT_OFFLINE: if operation_type != 1: - logger.warning("Оффлайн-режим не поддерживается для возврата или отмены") + logger.warning( + "Оффлайн-режим не поддерживается для возврата или отмены" + ) progress_signal.emit("Оффлайн: только оплата", 100) return 0, 0 logger.info("Запускаем offline оплату по банковскому терминалу") - progress_signal.emit("Запускаем offline оплату по банковскому терминалу...", 35) + progress_signal.emit( + "Запускаем offline оплату по банковскому терминалу...", 35 + ) return 1, 3 # успех, offline else: @@ -658,7 +676,9 @@ def universal_terminal_operation( logger.error(f"Неизвестная ошибка при проведении операции: {exp}") if progress_signal is not None: try: - progress_signal.emit(f"Неизвестная ошибка при проведении операции: {str(exp)}", 100) + progress_signal.emit( + f"Неизвестная ошибка при проведении операции: {str(exp)}", 100 + ) except RuntimeError: pass return 0, 0 @@ -979,6 +999,7 @@ def print_pinpad_check(count: int = 2): count -= 1 + def get_info(hide: bool = False) -> Optional[int]: """ Запрос информации о ККТ. @@ -1014,13 +1035,20 @@ def get_info(hide: bool = False) -> Optional[int]: return model except (ConnectionError, AttributeError) as e: logger.error(f"Ошибка при получении данных о ККТ: {e}") - windows.info_window("Ошибка", "Не удалось получить данные о ККТ.", "Проверьте настройки устройства.") + windows.info_window( + "Ошибка", + "Не удалось получить данные о ККТ.", + "Проверьте настройки устройства.", + ) return None except Exception as e: logger.error(f"Неизвестная ошибка: {e}") - windows.info_window("Ошибка", "Произошла неизвестная ошибка.", "Попробуйте снова.") + windows.info_window( + "Ошибка", "Произошла неизвестная ошибка.", "Попробуйте снова." + ) return None + def is_kkt_connected() -> bool: """ Проверка физического подключения ККТ. @@ -1046,6 +1074,7 @@ def is_kkt_connected() -> bool: logger.error(f"Ошибка при проверке подключения ККТ: {e}") return False + @logger_wraps() def get_last_document(day: int = 7) -> None: """Запрос информации о последнем чеке в ФН и проверка даты. @@ -1066,7 +1095,9 @@ def get_last_document(day: int = 7) -> None: except (ConnectionError, AttributeError) as e: logger.error(f"Ошибка при работе с ККТ: {e}") windows.info_window( - "Ошибка", "Не удалось получить данные от ККТ.", "Проверьте подключение и настройки." + "Ошибка", + "Не удалось получить данные от ККТ.", + "Проверьте подключение и настройки.", ) except Exception as e: logger.exception(f"Неизвестная ошибка в get_last_document: {e}") @@ -1074,8 +1105,9 @@ def get_last_document(day: int = 7) -> None: "Ошибка", "Произошла неизвестная ошибка.", "Попробуйте снова." ) + def get_last_document_datetime() -> datetime: - """ Получает дату и время последнего зарегистрированного чека из фискального накопителя. + """Получает дату и время последнего зарегистрированного чека из фискального накопителя. Возвращаемое значение: datetime: Дата и время последнего документа в фискальном накопителе. @@ -1085,12 +1117,17 @@ def get_last_document_datetime() -> datetime: """ logger.info("Запуск функции get_last_document_datetime") with fptr_connection(fptr): - fptr.setParam(IFptr.LIBFPTR_PARAM_FN_DATA_TYPE, IFptr.LIBFPTR_FNDT_LAST_DOCUMENT) + fptr.setParam( + IFptr.LIBFPTR_PARAM_FN_DATA_TYPE, IFptr.LIBFPTR_FNDT_LAST_DOCUMENT + ) fptr.fnQueryData() - last_check_datetime: datetime = fptr.getParamDateTime(IFptr.LIBFPTR_PARAM_DATE_TIME) + last_check_datetime: datetime = fptr.getParamDateTime( + IFptr.LIBFPTR_PARAM_DATE_TIME + ) return last_check_datetime + def check_stale_document(last_check: datetime, max_days: int = 7) -> None: """ Проверяет, не превышает ли возраст последнего чека заданное количество дней. @@ -1108,13 +1145,15 @@ def check_stale_document(last_check: datetime, max_days: int = 7) -> None: if (current_date - last_check) <= timedelta(days=max_days): # Ранний выход, если чек не старый return - info = f"Дата и время последнего чека в ФН: {last_check.strftime('%Y-%m-%d %H:%M:%S')}" + info = ( + f"Дата и время последнего чека в ФН: {last_check.strftime('%Y-%m-%d %H:%M:%S')}" + ) windows.info_window( "Внимание", "Дата последнего документа, записанного в фискальном накопителе, старше 7 дней.\n\n" "Для корректного проведения платежей по банковскому терминалу " - "необходимо сделать сверку итогов (вкладка \"Касса\" -> \"Сверка итогов\").\n", - info + 'необходимо сделать сверку итогов (вкладка "Касса" -> "Сверка итогов").\n', + info, ) @@ -1402,7 +1441,9 @@ def kassir_reg(user): try: with fptr_connection(fptr): # Параметры для регистрации кассира - fptr.setParam(1021, f"{user.last_name} {user.first_name} {user.middle_name}") + fptr.setParam( + 1021, f"{user.last_name} {user.first_name} {user.middle_name}" + ) fptr.setParam(1203, user.inn) fptr.operatorLogin() logger.info( @@ -1590,11 +1631,11 @@ def register_item(device, name, price, quantity, tax_type=IFptr.LIBFPTR_TAX_VAT2 logger.info("Запуск функции register_item") logger.info(f"В функцию переданы параметры: {name}, {price}, {quantity}") device.setParam(IFptr.LIBFPTR_PARAM_COMMODITY_NAME, name) - logger.info(f"name: {name}") # TODO: убрать + logger.info(f"name: {name}") # TODO: убрать device.setParam(IFptr.LIBFPTR_PARAM_PRICE, price) - logger.info(f"price: {price}") # TODO: убрать + logger.info(f"price: {price}") # TODO: убрать device.setParam(IFptr.LIBFPTR_PARAM_QUANTITY, quantity) - logger.info(f"quantity: {quantity}") # TODO: убрать + logger.info(f"quantity: {quantity}") # TODO: убрать device.setParam(IFptr.LIBFPTR_PARAM_TAX_TYPE, tax_type) device.registration() @@ -1639,7 +1680,9 @@ def register_tickets(device, sale_dict, type_operation): None: Функция не возвращает значений, но выполняет регистрацию билетов в чеке. """ logger.info("Запуск функции register_tickets") - logger.debug(f"В функцию переданы: device = {device}, sale_dict = {sale_dict}, type_operation = {type_operation}") + logger.debug( + f"В функцию переданы: device = {device}, sale_dict = {sale_dict}, type_operation = {type_operation}" + ) if type_operation == 1: count_adult_with_discount = sale_dict["detail"][0] @@ -1653,29 +1696,41 @@ def register_tickets(device, sale_dict, type_operation): # Взрослые без акции count_adult_no_discount = sale_dict["kol_adult"] - count_adult_with_discount - logger.debug(f"Взрослые без акции: kol_adult={sale_dict['kol_adult']}, price_adult= {sale_dict['price_adult']}") + logger.debug( + f"Взрослые без акции: kol_adult={sale_dict['kol_adult']}, price_adult= {sale_dict['price_adult']}" + ) if count_adult_no_discount > 0: adults_no_discount_sum = count_adult_no_discount * sale_dict["price_adult"] - logger.info(f"Регистрируем взрослых без акции: {count_adult_no_discount} шт × {sale_dict['price_adult']} = {adults_no_discount_sum} руб") + logger.info( + f"Регистрируем взрослых без акции: {count_adult_no_discount} шт × {sale_dict['price_adult']} = {adults_no_discount_sum} руб" + ) # if device is not None: # TODO: для теста без ККТ. + отступ внизу register_item( device, f"Билет взрослый {time} ч.", - sale_dict['price_adult'], + sale_dict["price_adult"], count_adult_no_discount, ) else: logger.debug("Взрослые без акции: не регистрируем (количество = 0)") # Взрослые с акцией - logger.debug(f"Взрослые С акцией: count_adult_with_discount={count_adult_with_discount}, price_adult_with_discount={price_adult_with_discount}, discount={price_adult_with_discount}") + logger.debug( + f"Взрослые С акцией: count_adult_with_discount={count_adult_with_discount}, price_adult_with_discount={price_adult_with_discount}, discount={price_adult_with_discount}" + ) - if (count_adult_with_discount > 0 - and price_adult_with_discount > 0 - and discount > 0): - adults_with_discount_sum = count_adult_with_discount * price_adult_with_discount - logger.info(f"Регистрируем взрослых с акцией: {count_adult_with_discount} шт × {price_adult_with_discount} = {adults_with_discount_sum} руб") + if ( + count_adult_with_discount > 0 + and price_adult_with_discount > 0 + and discount > 0 + ): + adults_with_discount_sum = ( + count_adult_with_discount * price_adult_with_discount + ) + logger.info( + f"Регистрируем взрослых с акцией: {count_adult_with_discount} шт × {price_adult_with_discount} = {adults_with_discount_sum} руб" + ) # if device is not None: # TODO: для теста без ККТ. + отступ внизу register_item( device, @@ -1690,29 +1745,43 @@ def register_tickets(device, sale_dict, type_operation): # Дети без акции count_child_no_discount = sale_dict["kol_child"] - count_child_with_discount - logger.debug(f"Дети без акции: kol_child={sale_dict['kol_child']}, count_child_with_discount={count_child_with_discount}, result={count_child_no_discount}") + logger.debug( + f"Дети без акции: kol_child={sale_dict['kol_child']}, count_child_with_discount={count_child_with_discount}, result={count_child_no_discount}" + ) if count_child_no_discount > 0: - children_no_discount_sum = count_child_no_discount * sale_dict["price_child"] - logger.info(f"Регистрируем детей без акции: {count_child_no_discount} шт × {sale_dict['price_child']} = {children_no_discount_sum} руб") + children_no_discount_sum = ( + count_child_no_discount * sale_dict["price_child"] + ) + logger.info( + f"Регистрируем детей без акции: {count_child_no_discount} шт × {sale_dict['price_child']} = {children_no_discount_sum} руб" + ) # if device is not None: # TODO: для теста без ККТ. + отступ внизу register_item( device, f"Билет детский {time} ч.", - sale_dict['price_child'], + sale_dict["price_child"], count_child_no_discount, ) else: logger.debug("Дети без акции: не регистрируем (количество = 0)") # Дети с акцией - logger.debug(f"Дети с акцией: count_child_with_discount={count_child_with_discount}, price_child_with_discount={price_child_with_discount}, discount={price_adult_with_discount}") + logger.debug( + f"Дети с акцией: count_child_with_discount={count_child_with_discount}, price_child_with_discount={price_child_with_discount}, discount={price_adult_with_discount}" + ) - if (count_child_with_discount > 0 - and price_child_with_discount > 0 - and discount > 0): - children_with_discount_sum = count_child_with_discount * price_child_with_discount - logger.info(f"Регистрируем детей с акцией: {count_child_with_discount} шт × {price_child_with_discount} = {children_with_discount_sum} руб") + if ( + count_child_with_discount > 0 + and price_child_with_discount > 0 + and discount > 0 + ): + children_with_discount_sum = ( + count_child_with_discount * price_child_with_discount + ) + logger.info( + f"Регистрируем детей с акцией: {count_child_with_discount} шт × {price_child_with_discount} = {children_with_discount_sum} руб" + ) # if device is not None: # TODO: для теста без ККТ. + отступ внизу register_item( device, @@ -1724,15 +1793,17 @@ def register_tickets(device, sale_dict, type_operation): logger.debug("Дети с акцией: не регистрируем (условия не выполнены)") else: # Для других типов операций - logger.error(f"Другие типы операций.") + logger.error("Другие типы операций.") for item_name, item_data in sale_dict.items(): - if (isinstance(item_data, list) and item_data[0] > 0 and item_data[1] > 0): + if isinstance(item_data, list) and item_data[0] > 0 and item_data[1] > 0: # Проверяем наличие количества и цены - logger.debug(f"item_name ={item_name}, item_data[0]={item_data[0]}, item_data[1]={item_data[1]}") + logger.debug( + f"item_name ={item_name}, item_data[0]={item_data[0]}, item_data[1]={item_data[1]}" + ) register_item(device, item_name, item_data[0], item_data[1]) else: # Обработка других типов данных - logger.error(f"Ошибка типа данных. Item_data - не список.") + logger.error("Ошибка типа данных. Item_data - не список.") def process_payment(device, payment_type, bank_status, sale_dict, _): @@ -1748,11 +1819,15 @@ def process_payment(device, payment_type, bank_status, sale_dict, _): bool: Возвращает `True`, если оплата прошла успешно, иначе — `False`. """ logger.info("Запуск функции process_payment") - logger.debug(f"В функцию переданы: device = {device}, payment_type = {payment_type}, bank = {bank_status}, sale_dict = {sale_dict}, price = {_}") + logger.debug( + f"В функцию переданы: device = {device}, payment_type = {payment_type}, bank = {bank_status}, sale_dict = {sale_dict}, price = {_}" + ) try: # Проверка payment_type if not isinstance(payment_type, int): - logger.error(f"Некорректный тип payment_type: {type(payment_type)}, значение: {payment_type}") + logger.error( + f"Некорректный тип payment_type: {type(payment_type)}, значение: {payment_type}" + ) return False # Определение суммы @@ -1763,7 +1838,9 @@ def process_payment(device, payment_type, bank_status, sale_dict, _): try: payment_amount = sale_dict["detail"][7] if isinstance(payment_amount, (int, float)) and payment_amount > 0: - logger.debug(f"Сумма получена из sale_dict['detail'][7]: {payment_amount}") + logger.debug( + f"Сумма получена из sale_dict['detail'][7]: {payment_amount}" + ) except (IndexError, TypeError): pass @@ -1775,7 +1852,12 @@ def process_payment(device, payment_type, bank_status, sale_dict, _): if isinstance(item_data, list) and len(item_data) >= 2: price = item_data[0] qty = item_data[1] - if isinstance(price, (int, float)) and isinstance(qty, int) and price > 0 and qty > 0: + if ( + isinstance(price, (int, float)) + and isinstance(qty, int) + and price > 0 + and qty > 0 + ): total += price * qty valid = True if valid and total > 0: @@ -1846,15 +1928,23 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): logger.info("Запуск функции handle_document_errors") # Проверяем, закрыт ли документ while device.checkDocumentClosed() < 0: - logger.warning(f"Не удалось проверить состояние документа: {device.errorDescription()}") + logger.warning( + f"Не удалось проверить состояние документа: {device.errorDescription()}" + ) if retry_count >= max_retries - 1: if on_error: - on_error("Ошибка ККТ", f"Не удалось проверить состояние документа после {max_retries} попыток. " - f"Проверьте соединение с ККТ и не выключайте ПК.") + on_error( + "Ошибка ККТ", + f"Не удалось проверить состояние документа после {max_retries} попыток. " + f"Проверьте соединение с ККТ и не выключайте ПК.", + ) return False if on_error: - on_error("Ошибка ККТ", f"Ошибка: {device.errorDescription()}. Повторная попытка...") + on_error( + "Ошибка ККТ", + f"Ошибка: {device.errorDescription()}. Повторная попытка...", + ) retry_count += 1 time.sleep(5) @@ -1868,12 +1958,17 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): except Exception as e: logger.critical(f"Ошибка при отмене чека: {e}") if on_error: - on_error("Критическая ошибка", "Не удалось отменить чек. Требуется перезагрузка ККТ.") + on_error( + "Критическая ошибка", + "Не удалось отменить чек. Требуется перезагрузка ККТ.", + ) # Пробрасываем исключение наверх raise if on_error: - on_error("Документ не закрыт", "Чек отменен. Требуется сформировать его заново.") + on_error( + "Документ не закрыт", "Чек отменен. Требуется сформировать его заново." + ) return False # Если документ не напечатан — попробовать допечатать @@ -1884,12 +1979,19 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): logger.info("Документ успешно допечатан") return True - logger.warning(f"Попытка допечатки №{print_attempt + 1} не удалась: {device.errorDescription()}") + logger.warning( + f"Попытка допечатки №{print_attempt + 1} не удалась: {device.errorDescription()}" + ) if on_error: - on_error("Ошибка печати", f"Ошибка допечатки: {device.errorDescription()}. Повторите попытку.") + on_error( + "Ошибка печати", + f"Ошибка допечатки: {device.errorDescription()}. Повторите попытку.", + ) time.sleep(3) - logger.error("Не удалось допечатать документ после 5 попыток. Он будет допечатан автоматически при следующей операции.") + logger.error( + "Не удалось допечатать документ после 5 попыток. Он будет допечатан автоматически при следующей операции." + ) # Документ закрыт, но не допечатан — допустимо. # Документ будет автоматически допечатан при следующей печатной операции. @@ -1901,7 +2003,15 @@ def handle_document_errors(device, retry_count, max_retries, on_error=None): return True -def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_status, on_error=None): +def check_open( + sale_dict, + payment_type, + user, + type_operation, + print_check, + bank_status, + on_error=None, +): """ Проведение операции оплаты. @@ -1941,12 +2051,16 @@ def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_ setup_fptr(device, user, type_operation, print_check) # Проверка состояния чека - device.setParam(IFptr.LIBFPTR_PARAM_DATA_TYPE, IFptr.LIBFPTR_DT_RECEIPT_STATE) + device.setParam( + IFptr.LIBFPTR_PARAM_DATA_TYPE, IFptr.LIBFPTR_DT_RECEIPT_STATE + ) device.queryData() - receipt_number = device.getParamInt(IFptr.LIBFPTR_PARAM_RECEIPT_NUMBER) - document_number = device.getParamInt(IFptr.LIBFPTR_PARAM_DOCUMENT_NUMBER) + receipt_number = device.getParamInt(IFptr.LIBFPTR_PARAM_RECEIPT_NUMBER) + document_number = device.getParamInt(IFptr.LIBFPTR_PARAM_DOCUMENT_NUMBER) receipt_type = device.getParamInt(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE) - logger.debug(f"receipt_type: {receipt_type}, receipt_number: {receipt_number}, document_number: {document_number}") + logger.debug( + f"receipt_type: {receipt_type}, receipt_number: {receipt_number}, document_number: {document_number}" + ) if receipt_type != IFptr.LIBFPTR_RT_CLOSED: logger.error(f"Чек уже открыт! Тип: {receipt_type}") if on_error: @@ -1955,9 +2069,13 @@ def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_ # Установка типа чека if type_operation == 1: - device.setParam(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL) # Продажа + device.setParam( + IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL + ) # Продажа elif type_operation == 2: - device.setParam(IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL_RETURN) # Возврат + device.setParam( + IFptr.LIBFPTR_PARAM_RECEIPT_TYPE, IFptr.LIBFPTR_RT_SELL_RETURN + ) # Возврат else: logger.error(f"Неизвестный тип операции: {type_operation}") return False @@ -1985,6 +2103,7 @@ def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_ on_error("Ошибка ККТ", f"Критическая ошибка: {str(e)}") return 0 + @logger_wraps() def smena_close(user): """Закрытие кассовой смены. diff --git a/modules/progress_window.py b/modules/progress_window.py index c8e6d1e..660ac03 100644 --- a/modules/progress_window.py +++ b/modules/progress_window.py @@ -1,5 +1,4 @@ -from PySide6.QtCore import QPropertyAnimation, QEasingCurve -from PySide6.QtCore import Qt +from PySide6.QtCore import QEasingCurve, QPropertyAnimation, Qt from PySide6.QtWidgets import QDialog from design.logic.progress_dialog import Ui_ProgressDialog diff --git a/modules/sale_logic.py b/modules/sale_logic.py index 5ba2434..dcadae4 100644 --- a/modules/sale_logic.py +++ b/modules/sale_logic.py @@ -230,10 +230,10 @@ def calculate_itog() -> int: # Проверка на корректность данных if ( - system.sale_dict["detail"][0] < 0 - or system.sale_dict["detail"][2] < 0 - or system.sale_dict["detail"][1] < 0 - or system.sale_dict["detail"][3] < 0 + system.sale_dict["detail"][0] < 0 + or system.sale_dict["detail"][2] < 0 + or system.sale_dict["detail"][1] < 0 + or system.sale_dict["detail"][3] < 0 ): raise ValueError("Некорректные данные для расчета итоговой суммы") @@ -243,7 +243,9 @@ def calculate_itog() -> int: adults_no_discount_sum = adults_no_discount * system.sale_dict["price_adult"] # Взрослые со скидкой (detail[0] × detail[1]) - adults_with_discount_sum = system.sale_dict["detail"][0] * system.sale_dict["detail"][1] + adults_with_discount_sum = ( + system.sale_dict["detail"][0] * system.sale_dict["detail"][1] + ) adult_total = adults_no_discount_sum + adults_with_discount_sum @@ -253,21 +255,33 @@ def calculate_itog() -> int: children_no_discount_sum = children_no_discount * system.sale_dict["price_child"] # Дети со скидкой (detail[2] × detail[3]) - children_with_discount_sum = system.sale_dict["detail"][2] * system.sale_dict["detail"][3] + children_with_discount_sum = ( + system.sale_dict["detail"][2] * system.sale_dict["detail"][3] + ) child_total = children_no_discount_sum + children_with_discount_sum result = adult_total + child_total - logger.debug(f"Взрослые без скидки: {adults_no_discount} × {system.sale_dict['price_adult']} = {adults_no_discount_sum}") - logger.debug(f"Взрослые со скидкой: {system.sale_dict['detail'][0]} × {system.sale_dict['detail'][1]} = {adults_with_discount_sum}") + logger.debug( + f"Взрослые без скидки: {adults_no_discount} × {system.sale_dict['price_adult']} = {adults_no_discount_sum}" + ) + logger.debug( + f"Взрослые со скидкой: {system.sale_dict['detail'][0]} × {system.sale_dict['detail'][1]} = {adults_with_discount_sum}" + ) logger.debug(f"ИТОГО взрослые: {adult_total}") - logger.debug(f"Дети без скидки: {children_no_discount} × {system.sale_dict['price_child']} = {children_no_discount_sum}") - logger.debug(f"Дети со скидкой: {system.sale_dict['detail'][2]} × {system.sale_dict['detail'][3]} = {children_with_discount_sum}") + logger.debug( + f"Дети без скидки: {children_no_discount} × {system.sale_dict['price_child']} = {children_no_discount_sum}" + ) + logger.debug( + f"Дети со скидкой: {system.sale_dict['detail'][2]} × {system.sale_dict['detail'][3]} = {children_with_discount_sum}" + ) logger.debug(f"ИТОГО дети: {child_total}") - logger.debug(f"Расчет итого: {adult_total} (взрослые) + {child_total} (дети) = {result}") + logger.debug( + f"Расчет итого: {adult_total} (взрослые) + {child_total} (дети) = {result}" + ) return result diff --git a/modules/system.py b/modules/system.py index 34c8fdf..2fe83ec 100644 --- a/modules/system.py +++ b/modules/system.py @@ -12,7 +12,7 @@ from sqlalchemy.exc import SQLAlchemyError from sqlalchemy.orm import Session -from db.models import Holiday, Workday, Price, User, Sale +from db.models import Holiday, Price, Sale, User, Workday from modules.config import Config from modules.logger import logger, logger_wraps @@ -167,7 +167,9 @@ def user_authorization(self, login: str, password: str) -> int: logger.info(f"Успешная авторизация: {kassir.last_name}") return 1 else: - logger.warning(f"Неудачная попытка авторизации для пользователя {login}") + logger.warning( + f"Неудачная попытка авторизации для пользователя {login}" + ) return 0 else: logger.warning(f"Пользователь {login} не найден") @@ -241,7 +243,7 @@ def get_price(self) -> None: db_prices[key] = default_prices[key] else: # Если записей нет, используем дефолтные значения для всех элементов - logger.warning(f"Используем дефолтные значения для прайс-листа") + logger.warning("Используем дефолтные значения для прайс-листа") db_prices = default_prices # Устанавливаем цены, используя данные из db_prices или дефолтные значения @@ -410,24 +412,23 @@ def get_slip_data(self, sale_id: int) -> tuple[str, str, str, str]: query = select(Sale.bank_pay).where(Sale.id == sale_id) load_slip = session.execute(query).scalars().one() try: - qr_match = re.search(r'Номер QR:\s+(\d+)', load_slip) + qr_match = re.search(r"Номер QR:\s+(\d+)", load_slip) if qr_match: - last_digits = re.search(r'Карта:\s+\*+\s*(\d{4})', load_slip) - card_tail = last_digits.group(1) if last_digits else '' + last_digits = re.search(r"Карта:\s+\*+\s*(\d{4})", load_slip) + card_tail = last_digits.group(1) if last_digits else "" else: - last_digits = re.search(r'Карта:\(?E\d*\)?\s+\*+\s*(\d{4})', load_slip) - card_tail = last_digits.group(1) if last_digits else '' - merchant = re.search(r'М:(\d+)', load_slip) - merchant_id = merchant.group(1) if merchant else '' - rrn = re.search(r'RRN:\s+(\d+)', load_slip) - rrn_value = rrn.group(1) if rrn else '' + last_digits = re.search(r"Карта:\(?E\d*\)?\s+\*+\s*(\d{4})", load_slip) + card_tail = last_digits.group(1) if last_digits else "" + merchant = re.search(r"М:(\d+)", load_slip) + merchant_id = merchant.group(1) if merchant else "" + rrn = re.search(r"RRN:\s+(\d+)", load_slip) + rrn_value = rrn.group(1) if rrn else "" except Exception as e: logger.warning(f"Ошибка при разборе чека: {e}") - card_tail = '' - merchant_id = '' - rrn_value = '' - return card_tail, merchant_id, rrn_value, load_slip - + card_tail = "" + merchant_id = "" + rrn_value = "" + return card_tail, merchant_id, rrn_value, load_slip def check_db_connection(self) -> bool: """Проверка подключения к базе данных.""" @@ -439,4 +440,4 @@ def check_db_connection(self) -> bool: return True except Exception as e: logger.error(f"Ошибка подключения к БД: {e}") - return False \ No newline at end of file + return False diff --git a/modules/worker.py b/modules/worker.py index d83a6e7..c530f5f 100644 --- a/modules/worker.py +++ b/modules/worker.py @@ -1,7 +1,8 @@ import datetime as dt from functools import wraps -from PySide6.QtCore import QElapsedTimer, QObject, Signal, QMetaObject, Qt, QTimer, QEventLoop +from PySide6.QtCore import (QElapsedTimer, QEventLoop, QMetaObject, QObject, + Qt, QTimer, Signal) from sqlalchemy import update from db.models import Sale @@ -10,16 +11,19 @@ def with_timer(func): """Декоратор для автоматического создания QElapsedTimer""" + @wraps(func) def wrapper(self, *args, **kwargs): timer = QElapsedTimer() timer.start() return func(self, timer, *args, **kwargs) + return wrapper class BaseWorker(QObject): """Базовый класс с общими методами""" + DEFAULT_DELAY_MS = 15 progress_updated = Signal(str, int) @@ -35,7 +39,9 @@ def log_step(timer, step_name): elapsed = timer.elapsed() logger.debug(f"[TIMER] {step_name} — {elapsed} ms") - def delayed_progress_update(self, step_text: str, progress_percent: int, delay_ms: int = DEFAULT_DELAY_MS): + def delayed_progress_update( + self, step_text: str, progress_percent: int, delay_ms: int = DEFAULT_DELAY_MS + ): if delay_ms < 0: delay_ms = 0 if delay_ms > 0: @@ -58,9 +64,15 @@ def invoke_main_window_method(self, method_name: str): Qt.QueuedConnection, ) - def emit_error_and_finish(self, title: str, message: str, code: str = "", - timer: QElapsedTimer = None, step_name: str = "", - close_window: bool = True): + def emit_error_and_finish( + self, + title: str, + message: str, + code: str = "", + timer: QElapsedTimer = None, + step_name: str = "", + close_window: bool = True, + ): """ Универсальный метод обработки ошибок :param title: Заголовок ошибки @@ -87,11 +99,13 @@ def emit_error_and_finish(self, title: str, message: str, code: str = "", logger.warning("Попытка emit после удаления объекта — игнорируем") else: raise - except Exception as e: + except Exception: logger.exception("Неожиданная ошибка в emit_error_and_finish") + class PaymentHandler: """Обработчик банковских платежей""" + def __init__(self, worker, pq, payment_type, amount, dev_mode=False): self.worker = worker self.pq = pq @@ -107,7 +121,9 @@ def process_bank_payment(self, timer: QElapsedTimer = None): if self.dev_mode: self.worker.logger.info("РЕЖИМ ОТЛАДКИ: Имитация оплаты картой") - self.worker.logger.debug(f"Сумма: {self.amount}, Тип оплаты: {self.payment_type}") + self.worker.logger.debug( + f"Сумма: {self.amount}, Тип оплаты: {self.payment_type}" + ) return True, 1 # (success, payment_type) try: @@ -116,7 +132,7 @@ def process_bank_payment(self, timer: QElapsedTimer = None): self.payment_type, self.amount, self.worker.progress_updated, - error_callback=self._handle_terminal_error_callback + error_callback=self._handle_terminal_error_callback, ) self.worker.log_step(timer, "pq.universal_terminal_operation finished") @@ -134,19 +150,21 @@ def process_bank_payment(self, timer: QElapsedTimer = None): def get_mock_slip(self): """Генерация тестового слип-чека для режима отладки""" from datetime import datetime - return (f"DEBUG_SLIP\n" - f"Дата: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}\n" - f"Сумма: {self.amount} руб.\n" - f"Тип: {'Онлайн' if self.payment_type == 101 else 'Оффлайн'}\n" - f"СТАНДАРТНЫЙ ЧЕК ДЛЯ ТЕСТИРОВАНИЯ") - def _handle_terminal_error_callback(self, title: str, message: str, code: int) -> None: + return ( + f"DEBUG_SLIP\n" + f"Дата: {datetime.now().strftime('%d.%m.%Y %H:%M:%S')}\n" + f"Сумма: {self.amount} руб.\n" + f"Тип: {'Онлайн' if self.payment_type == 101 else 'Оффлайн'}\n" + f"СТАНДАРТНЫЙ ЧЕК ДЛЯ ТЕСТИРОВАНИЯ" + ) + + def _handle_terminal_error_callback( + self, title: str, message: str, code: int + ) -> None: """Callback для обработки ошибок от терминала""" self.worker.emit_error_and_finish( - title=title, - message=message, - code=str(code), - step_name="terminal_error" + title=title, message=message, code=str(code), step_name="terminal_error" ) def _handle_payment_failed(self, timer): @@ -155,7 +173,7 @@ def _handle_payment_failed(self, timer): title="Ошибка", message="Операция оплаты не удалась. Повторите попытку.", timer=timer, - step_name="payment_failed" + step_name="payment_failed", ) def _handle_payment_exception(self, timer, exception): @@ -165,12 +183,13 @@ def _handle_payment_exception(self, timer, exception): message="Не удалось выполнить операцию на терминале.", code=str(exception), timer=timer, - step_name="payment_exception" + step_name="payment_exception", ) class CheckHandler: """Обработчик печати чеков""" + def __init__(self, worker, pq): self.worker = worker self.pq = pq @@ -184,25 +203,28 @@ def print_check(self, sale_dict, payment_type, user, print_check, _, bank_data): type_operation=1, print_check=print_check, bank_status=bank_data, - on_error=self.handle_check_error + on_error=self.handle_check_error, ) return state_check == 1 - def handle_check_error(self, title="Неизвестная ошибка", text="Произошла неизвестная ошибка"): + def handle_check_error( + self, title="Неизвестная ошибка", text="Произошла неизвестная ошибка" + ): """Обработчик ошибок печати чека""" logger.error(f"handle_check_error вызван с title={title}, text={text}") self.worker.emit_error_and_finish( title="Ошибка ККМ", message="Документ не закрылся после 5 попыток. Отмена кассового чека.", code="critical", - step_name="check_error" + step_name="check_error", ) # raise Exception(f"{title}: {text}") class DatabaseHandler: """Обработчик работы с базой данных""" + def __init__(self, Session, engine): self.Session = Session self.engine = engine @@ -211,7 +233,9 @@ def sale_exists(self, sale_id): """Проверяет существование продажи по ID""" try: with self.Session(self.engine) as session: - return session.query(Sale).filter(Sale.id == sale_id).first() is not None + return ( + session.query(Sale).filter(Sale.id == sale_id).first() is not None + ) except Exception as e: logger.error(f"Ошибка проверки существования продажи {sale_id}: {e}") return False @@ -219,17 +243,16 @@ def sale_exists(self, sale_id): def update_sale(self, sale_id, **values): """Общее сохранение данных о продаже""" with self.Session(self.engine) as session: - session.execute( - update(Sale) - .where(Sale.id == sale_id) - .values(**values) - ) + session.execute(update(Sale).where(Sale.id == sale_id).values(**values)) session.commit() class TransactionWorker(BaseWorker): """Основной класс обработки транзакций""" - def __init__(self, payment_type, print_check, system, pq, Session, main_window, parent=None): + + def __init__( + self, payment_type, print_check, system, pq, Session, main_window, parent=None + ): super().__init__(parent) self.payment_type = payment_type self.print_check = print_check @@ -238,7 +261,9 @@ def __init__(self, payment_type, print_check, system, pq, Session, main_window, self.Session = Session self.main_window = main_window # Флаг режима отладки - self.dev_mode = getattr(main_window, 'dev_mode', False) if main_window else False + self.dev_mode = ( + getattr(main_window, "dev_mode", False) if main_window else False + ) # Флаг cleanup self._is_cleaned = False @@ -262,14 +287,16 @@ def __init__(self, payment_type, print_check, system, pq, Session, main_window, @with_timer def process_special_sale(self, timer: QElapsedTimer, progress_percent: int): """Обработка специальной продажи""" - self.delayed_progress_update("Печатаем билеты (особая продажа)...", progress_percent) + self.delayed_progress_update( + "Печатаем билеты (особая продажа)...", progress_percent + ) self.invoke_main_window_method("print_saved_tickets") self.log_step(timer, "print_saved_tickets finished") self.system.sale_status = 0 def process_payment(self, timer: QElapsedTimer): """Обработка платежа""" - payment = 2 # по умолчанию наличные + payment = 2 # по умолчанию наличные bank_status = 0 # Проверяем, что sale_id существует @@ -278,17 +305,19 @@ def process_payment(self, timer: QElapsedTimer): self.emit_error_and_finish( title="Критическая ошибка", message="Продажа не была создана в БД", - code="SALE_ID_NONE" + code="SALE_ID_NONE", ) return None, None # Проверяем существует ли продажа в БД? if not self.db_handler.sale_exists(self.system.sale_id): - logger.warning(f"Продажа {self.system.sale_id} есть в памяти, но отсутствует в БД!") + logger.warning( + f"Продажа {self.system.sale_id} есть в памяти, но отсутствует в БД!" + ) self.emit_error_and_finish( title="Критическая ошибка", message="Продажа не найдена в БД после сохранения", - code="SALE_NOT_IN_DB" + code="SALE_NOT_IN_DB", ) return None, None @@ -318,7 +347,9 @@ def process_payment(self, timer: QElapsedTimer): bank_status = 1 # Обязательно сохраняем банковский слип-чек try: - self.db_handler.update_sale(self.system.sale_id, bank_pay=check, status=9) + self.db_handler.update_sale( + self.system.sale_id, bank_pay=check, status=9 + ) except Exception as e: logger.warning(f"Ошибка сохранения банковского чека: {e}") self.log_step(timer, "save in db finished") @@ -335,7 +366,9 @@ def process_checks(self, timer: QElapsedTimer, payment, bank_data): self.delayed_progress_update("Печатаем кассовый чек...", 60) logger.debug(f"ДО check_open: sale_dict = {self.system.sale_dict}") - logger.debug(f"exclude_from_sale={self.system.exclude_from_sale}, sale_checkbox_row={self.system.sale_checkbox_row}") + logger.debug( + f"exclude_from_sale={self.system.exclude_from_sale}, sale_checkbox_row={self.system.sale_checkbox_row}" + ) try: if not self.check_handler.print_check( @@ -344,7 +377,7 @@ def process_checks(self, timer: QElapsedTimer, payment, bank_data): self.system.user, self.print_check, self.system.sale_dict["detail"][7], - bank_data + bank_data, ): # В случае ошибки внутри print_check должен быть вызван on_error return False @@ -394,7 +427,7 @@ def handle_error(self, error, timer: QElapsedTimer): code=str(error), timer=timer, step_name="transaction_error", - close_window=True + close_window=True, ) self.system.sale_status = 0 diff --git a/tests/test_auth_logic.py b/tests/test_auth_logic.py index 1c86dbd..0b329af 100644 --- a/tests/test_auth_logic.py +++ b/tests/test_auth_logic.py @@ -1,18 +1,19 @@ +import datetime as dt import unittest from unittest.mock import MagicMock, patch -import datetime as dt from modules.auth_logic import perform_pre_sale_checks from modules.system import System + class TestAuthLogic(unittest.TestCase): def setUp(self): # Prevent System from trying to connect to a real DB - patcher = patch('modules.system.create_engine') + patcher = patch("modules.system.create_engine") self.mock_create_engine = patcher.start() self.addCleanup(patcher.stop) - + self.system = System() self.system.what_a_day = None self.system.num_of_week = None @@ -30,48 +31,52 @@ def test_perform_pre_sale_checks_authorization_success(self): result = perform_pre_sale_checks("valid_login", "valid_password") self.assertEqual(result, 1) - self.system.user_authorization.assert_called_once_with("valid_login", "valid_password") + self.system.user_authorization.assert_called_once_with( + "valid_login", "valid_password" + ) def test_perform_pre_sale_checks_authorization_failure(self): self.system.user_authorization = MagicMock(return_value=0) result = perform_pre_sale_checks("invalid_login", "invalid_password") self.assertEqual(result, 0) - self.system.user_authorization.assert_called_once_with("invalid_login", "invalid_password") + self.system.user_authorization.assert_called_once_with( + "invalid_login", "invalid_password" + ) - @patch('modules.auth_logic.dt') + @patch("modules.auth_logic.dt") def test_check_day_status_weekday(self, mock_dt): - mock_date = dt.datetime(2024, 11, 6) # Wednesday + mock_date = dt.datetime(2024, 11, 6) # Wednesday mock_dt.datetime.today.return_value = mock_date - + self.system.user_authorization = MagicMock(return_value=1) self.system.get_price = MagicMock() self.system.check_day = MagicMock(return_value=0) - + result = perform_pre_sale_checks("valid_login", "valid_password") self.assertEqual(result, 1) self.assertEqual(self.system.what_a_day, 0) - @patch('modules.auth_logic.dt') + @patch("modules.auth_logic.dt") def test_check_day_status_weekend(self, mock_dt): - mock_date = dt.datetime(2024, 11, 2) # Saturday + mock_date = dt.datetime(2024, 11, 2) # Saturday mock_dt.datetime.today.return_value = mock_date self.system.user_authorization = MagicMock(return_value=1) self.system.get_price = MagicMock() self.system.check_day = MagicMock(return_value=1) - + result = perform_pre_sale_checks("valid_login", "valid_password") self.assertEqual(result, 1) self.assertEqual(self.system.what_a_day, 1) - @patch('modules.auth_logic.dt') + @patch("modules.auth_logic.dt") def test_sunday_for_large_families_yes(self, mock_dt): mock_date = MagicMock() mock_date.isoweekday.return_value = 7 mock_date.day = 3 mock_dt.datetime.today.return_value = mock_date - + self.system.user_authorization = MagicMock(return_value=1) self.system.get_price = MagicMock() self.system.check_day = MagicMock(return_value=0) @@ -80,13 +85,13 @@ def test_sunday_for_large_families_yes(self, mock_dt): self.assertEqual(result, 1) self.assertEqual(self.system.sunday, 1) - @patch('modules.auth_logic.dt') + @patch("modules.auth_logic.dt") def test_sunday_for_large_families_no_not_sunday(self, mock_dt): mock_date = MagicMock() mock_date.isoweekday.return_value = 1 mock_date.day = 4 mock_dt.datetime.today.return_value = mock_date - + self.system.user_authorization = MagicMock(return_value=1) self.system.get_price = MagicMock() self.system.check_day = MagicMock(return_value=0) @@ -95,13 +100,13 @@ def test_sunday_for_large_families_no_not_sunday(self, mock_dt): self.assertEqual(result, 1) self.assertEqual(self.system.sunday, 0) - @patch('modules.auth_logic.dt') + @patch("modules.auth_logic.dt") def test_sunday_for_large_families_no_not_first_week(self, mock_dt): mock_date = MagicMock() mock_date.isoweekday.return_value = 7 mock_date.day = 10 mock_dt.datetime.today.return_value = mock_date - + self.system.user_authorization = MagicMock(return_value=1) self.system.get_price = MagicMock() self.system.check_day = MagicMock(return_value=0) @@ -110,13 +115,13 @@ def test_sunday_for_large_families_no_not_first_week(self, mock_dt): self.assertEqual(result, 1) self.assertEqual(self.system.sunday, 0) - @patch('modules.auth_logic.dt') + @patch("modules.auth_logic.dt") def test_week_and_month_day_assignment(self, mock_dt): mock_date = MagicMock() mock_date.isoweekday.return_value = 3 mock_date.day = 6 mock_dt.datetime.today.return_value = mock_date - + self.system.user_authorization = MagicMock(return_value=1) self.system.get_price = MagicMock() self.system.check_day = MagicMock(return_value=0) @@ -126,5 +131,6 @@ def test_week_and_month_day_assignment(self, mock_dt): self.assertEqual(result, 1) self.assertEqual(self.system.num_of_week, 3) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_config.py b/tests/test_config.py index a7b68f2..c076501 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,8 +1,10 @@ -import unittest import os import tempfile +import unittest + from modules.config import Config + class TestConfig(unittest.TestCase): def setUp(self): @@ -120,7 +122,9 @@ def test_missing_pc_option(self): with self.assertRaises(RuntimeError) as context: Config(config_file=self.config_path) - self.assertIn("Неизвестная ошибка: Отсутствует параметр 'pc_2'", str(context.exception)) + self.assertIn( + "Неизвестная ошибка: Отсутствует параметр 'pc_2'", str(context.exception) + ) def test_empty_pc_name(self): invalid_content = """ @@ -155,7 +159,8 @@ def test_empty_pc_name(self): Config(config_file=self.config_path) self.assertIn("Неизвестная ошибка: Пустое значение", str(context.exception)) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() def test_missing_pc_name_empty(self): @@ -198,4 +203,3 @@ def test_missing_section_header(self): with self.assertRaises(RuntimeError): Config(config_file=self.config_path) - diff --git a/tests/test_logger.py b/tests/test_logger.py index cc28d7a..fe00b9a 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -1,14 +1,14 @@ import unittest from modules.logger import logger_wraps, logger -from unittest.mock import patch, MagicMock +from unittest.mock import patch class TestLogger(unittest.TestCase): def test_logger_wraps_success(self): @logger_wraps() def test_func(): return "success" - - with patch.object(logger, 'info') as mock_info: + + with patch.object(logger, 'info'): res = test_func() self.assertEqual(res, "success") @@ -16,8 +16,8 @@ def test_logger_wraps_exception(self): @logger_wraps() def test_func(): raise ValueError("Test Error") - - with patch.object(logger, 'exception') as mock_exc: + + with patch.object(logger, 'exception'): with self.assertRaises(ValueError): test_func() @@ -25,8 +25,8 @@ def test_logger_wraps_no_entry_exit(self): @logger_wraps(entry=False, exit=False) def test_func(): return "success" - - with patch.object(logger, 'info') as mock_info: + + with patch.object(logger, 'info'): res = test_func() self.assertEqual(res, "success") diff --git a/tests/test_otchet.py b/tests/test_otchet.py index ec980b6..dd49885 100644 --- a/tests/test_otchet.py +++ b/tests/test_otchet.py @@ -1,35 +1,34 @@ +import sys import unittest -from unittest.mock import patch, MagicMock -from datetime import datetime +from unittest.mock import MagicMock, patch + +sys.modules["reportlab"] = MagicMock() +sys.modules["reportlab.lib"] = MagicMock() +sys.modules["reportlab.lib.colors"] = MagicMock() +sys.modules["reportlab.lib.pagesizes"] = MagicMock() +sys.modules["reportlab.lib.pagesizes"].landscape = lambda x: x +sys.modules["reportlab.lib.pagesizes"].A4 = "A4" +sys.modules["reportlab.lib.pagesizes"].letter = "letter" +sys.modules["reportlab.lib.units"] = MagicMock() +sys.modules["reportlab.lib.units"].mm = 1 +sys.modules["reportlab.lib.units"].inch = 1 +sys.modules["reportlab.pdfbase"] = MagicMock() +sys.modules["reportlab.pdfbase.pdfmetrics"] = MagicMock() +sys.modules["reportlab.pdfbase.ttfonts"] = MagicMock() +sys.modules["reportlab.pdfgen"] = MagicMock() +sys.modules["reportlab.platypus"] = MagicMock() +sys.modules["reportlab.platypus"].Table = MagicMock() +sys.modules["reportlab.platypus"].TableStyle = MagicMock() + +# isort: split +import modules.otchet as otchet # noqa: E402 + +# isort: split +from modules.otchet import (format_date_range, # noqa: E402 + generate_ticket_report_table, get_ticket_type, + process_sales_and_returns, process_ticket_stats, + safe_int) -import sys -sys.modules['reportlab'] = MagicMock() -sys.modules['reportlab.lib'] = MagicMock() -sys.modules['reportlab.lib.colors'] = MagicMock() -sys.modules['reportlab.lib.pagesizes'] = MagicMock() -sys.modules['reportlab.lib.pagesizes'].landscape = lambda x: x -sys.modules['reportlab.lib.pagesizes'].A4 = "A4" -sys.modules['reportlab.lib.pagesizes'].letter = "letter" -sys.modules['reportlab.lib.units'] = MagicMock() -sys.modules['reportlab.lib.units'].mm = 1 -sys.modules['reportlab.lib.units'].inch = 1 -sys.modules['reportlab.pdfbase'] = MagicMock() -sys.modules['reportlab.pdfbase.pdfmetrics'] = MagicMock() -sys.modules['reportlab.pdfbase.ttfonts'] = MagicMock() -sys.modules['reportlab.pdfgen'] = MagicMock() -sys.modules['reportlab.platypus'] = MagicMock() -sys.modules['reportlab.platypus'].Table = MagicMock() -sys.modules['reportlab.platypus'].TableStyle = MagicMock() - -import modules.otchet as otchet -from modules.otchet import ( - get_ticket_type, - generate_ticket_report_table, - safe_int, - format_date_range, - process_sales_and_returns, - process_ticket_stats -) class TestOtchet(unittest.TestCase): @@ -43,7 +42,7 @@ def test_get_ticket_type(self): with self.assertRaises(ValueError): get_ticket_type(-1) - + with self.assertRaises(ValueError): get_ticket_type("invalid") @@ -65,22 +64,27 @@ def test_format_date_range(self): def test_generate_ticket_report_table(self): summary = { - "Взрослый, 1 ч.": { - 250: {"count": 2, "total_price": 500} - }, - "Детский, 2 ч.": { - 500: {"count": 1, "total_price": 500} - } + "Взрослый, 1 ч.": {250: {"count": 2, "total_price": 500}}, + "Детский, 2 ч.": {500: {"count": 1, "total_price": 500}}, } table = generate_ticket_report_table(summary) - - self.assertEqual(table[0], ["№\n п/п", "Тип\nбилета", "Цена,\n руб.", "Количество,\n шт.", "Стоимость,\n руб."]) + + self.assertEqual( + table[0], + [ + "№\n п/п", + "Тип\nбилета", + "Цена,\n руб.", + "Количество,\n шт.", + "Стоимость,\n руб.", + ], + ) self.assertIn(["1", "Взрослый, 1 ч.", 250, 2, 500], table) self.assertEqual(table[-1][1], "Итого билетов") self.assertEqual(table[-1][3], 3) self.assertEqual(table[-1][4], 1000) - @patch('modules.otchet.system') + @patch("modules.otchet.system") def test_process_sales_and_returns(self, mock_system): mock_system.pcs = ["PC1", "PC2"] sales = [("PC1", 1, 1000), ("PC1", 2, 500), ("PC2", 1, 200)] @@ -95,7 +99,7 @@ def test_process_sales_and_returns(self, mock_system): self.assertEqual(total_row[0], "Итого") self.assertEqual(total_row[1], 1200) - @patch('modules.otchet.system') + @patch("modules.otchet.system") def test_process_ticket_stats(self, mock_system): tickets = [ (0, 1, "-", None, None, 100), @@ -109,38 +113,40 @@ def test_process_ticket_stats(self, mock_system): adult_stats = next(r for r in result if r[0] == "взрослый")[1] self.assertEqual(adult_stats["sum"], 1) - @patch('modules.otchet.canvas.Canvas') + @patch("modules.otchet.canvas.Canvas") def test_otchet_administratora(self, mock_canvas_class): mock_canvas_inst = MagicMock() mock_canvas_class.return_value = mock_canvas_inst - + values = {"Взрослый, 1 ч.": {250: {"count": 2, "total_price": 500}}} - otchet.otchet_administratora("2023-10-01 12:00:00", "2023-10-02 15:30:00", values) - + otchet.otchet_administratora( + "2023-10-01 12:00:00", "2023-10-02 15:30:00", values + ) + self.assertTrue(mock_canvas_class.called) self.assertTrue(mock_canvas_inst.save.called) - @patch('modules.otchet.canvas.Canvas') + @patch("modules.otchet.canvas.Canvas") def test_otchet_kassira(self, mock_canvas_class): mock_canvas_inst = MagicMock() mock_canvas_class.return_value = mock_canvas_inst - + val = [1000, 500, 100, 50] kassir = MagicMock() kassir.last_name = "Иванов" kassir.first_name = "Иван" kassir.middle_name = "Иванович" - + otchet.otchet_kassira(val, "2023-10-01 12:00:00", "2023-10-02 15:30:00", kassir) self.assertTrue(mock_canvas_class.called) self.assertTrue(mock_canvas_inst.save.called) - @patch('modules.otchet.canvas.Canvas') - @patch('modules.otchet.system.load_coordinates') + @patch("modules.otchet.canvas.Canvas") + @patch("modules.otchet.system.load_coordinates") def test_generate_saved_tickets(self, mock_load_coords, mock_canvas_class): mock_canvas_inst = MagicMock() mock_canvas_class.return_value = mock_canvas_inst - + mock_load_coords.return_value = { "name": {"x": 10, "y": 20}, "surname": {"x": 10, "y": 20}, @@ -154,15 +160,14 @@ def test_generate_saved_tickets(self, mock_load_coords, mock_canvas_class): "ticket_type": {"x": 10, "y": 20}, "notes": {"x": 10, "y": 20}, "talents": {"x": 10, "y": 20}, - "qr_code": {"x": 10, "y": 20} + "qr_code": {"x": 10, "y": 20}, } - - values = [ - ("Иванов", "Иван", 1, 500, "-", 1, 10, 3, 50, "2023-10-01") - ] + + values = [("Иванов", "Иван", 1, 500, "-", 1, 10, 3, 50, "2023-10-01")] otchet.generate_saved_tickets(values) self.assertTrue(mock_canvas_class.called) self.assertTrue(mock_canvas_inst.save.called) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_payment_equipment.py b/tests/test_payment_equipment.py index be3c476..6187732 100644 --- a/tests/test_payment_equipment.py +++ b/tests/test_payment_equipment.py @@ -1,3 +1,4 @@ +import subprocess import unittest from unittest.mock import patch, MagicMock, mock_open @@ -57,7 +58,7 @@ class FakeIFptr: LIBFPTR_PARAM_RECEIPT_NUMBER = 1 LIBFPTR_RT_CLOSED = 0 LIBFPTR_RT_CLOSE_SHIFT = 1 - + def __init__(self, *args): pass fake_libfptr10.IFptr = FakeIFptr @@ -74,9 +75,9 @@ def test_smena_info(self): mock_fptr = MagicMock() mock_fptr.open.return_value = None mock_fptr.getParamInt.side_effect = [1, 100] - + with patch("modules.payment_equipment.fptr", mock_fptr): - with patch("modules.payment_equipment.windows.info_window") as mock_info: + with patch("modules.payment_equipment.windows.info_window"): res = pe.smena_info() self.assertEqual(res, 1) @@ -91,7 +92,7 @@ def test_report_payment(self): with patch("modules.payment_equipment.fptr", mock_fptr): pe.report_payment() mock_fptr.report.assert_called_once() - + def test_report_x(self): mock_fptr = MagicMock() with patch("modules.payment_equipment.fptr", mock_fptr): @@ -101,22 +102,22 @@ def test_report_x(self): def test_deposit_of_money(self): mock_fptr = MagicMock() with patch("modules.payment_equipment.fptr", mock_fptr): - with patch("modules.payment_equipment.windows.info_window") as mock_info: + with patch("modules.payment_equipment.windows.info_window"): pe.deposit_of_money(100.0) mock_fptr.cashIncome.assert_called_once() def test_payment(self): mock_fptr = MagicMock() with patch("modules.payment_equipment.fptr", mock_fptr): - with patch("modules.payment_equipment.windows.info_window") as mock_info: + with patch("modules.payment_equipment.windows.info_window"): pe.payment(100.0) mock_fptr.cashOutcome.assert_called_once() - + def test_balance_check(self): mock_fptr = MagicMock() mock_fptr.getParamDouble.return_value = 500.0 with patch("modules.payment_equipment.fptr", mock_fptr): - with patch("modules.payment_equipment.windows.info_window") as mock_info: + with patch("modules.payment_equipment.windows.info_window"): res = pe.balance_check() self.assertEqual(res, 500.0) self.assertEqual(mock_fptr.printText.call_count, 2) @@ -129,7 +130,7 @@ def test_run_terminal_command(self): mock_popen.return_value = mock_process pe.run_terminal_command("1") mock_popen.assert_called_once() - + def test_check_terminal_file(self): with patch("builtins.open", mock_open(read_data="УСПЕШНО")): res = pe.check_terminal_file("УСПЕШНО") @@ -140,7 +141,7 @@ def test_process_success_result(self): with patch("modules.payment_equipment.windows.info_window"): res = pe.process_success_result() self.assertEqual(res, pe.TERMINAL_SUCCESS_CODE) - + def test_handle_error(self): with patch("modules.payment_equipment.windows.info_window"): res = pe.handle_error(1, "title", "msg", None) @@ -166,13 +167,13 @@ def test_terminal_check_itog(self): with patch("modules.payment_equipment.check_terminal_file", return_value=True): res = pe.terminal_check_itog() self.assertEqual(res, 1) - + def test_terminal_menu(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) res = pe.terminal_menu() self.assertIsNone(res) - + def test_terminal_check_itog_window(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) @@ -180,14 +181,14 @@ def test_terminal_check_itog_window(self): with patch("modules.payment_equipment.windows.info_window") as mock_info: pe.terminal_check_itog_window() mock_info.assert_called_once() - + def test_terminal_svod_check(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) with patch("modules.payment_equipment.print_pinpad_check"): res = pe.terminal_svod_check() self.assertIsNone(res) - + def test_terminal_control_lenta(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) @@ -208,7 +209,7 @@ def test_terminal_copy_last_check(self): with patch("modules.payment_equipment.print_pinpad_check"): res = pe.terminal_copy_last_check() self.assertIsNone(res) - + def test_is_kkt_connected(self): pe.kkt_available = True mock_fptr = MagicMock() @@ -275,20 +276,20 @@ def test_kkt_connection_kkt_not_available(self): with patch("modules.payment_equipment.kkt_available", False): with pe.fptr_connection(pe.fptr) as conn: self.assertIsNone(conn) - + def test_kkt_connection_not_opened(self): mock_device = MagicMock() mock_device.isOpened.return_value = False with pe.fptr_connection(mock_device) as conn: self.assertIsNone(conn) mock_device.open.assert_called_once() - + def test_kkt_connection_exception_on_open(self): mock_device = MagicMock() mock_device.open.side_effect = Exception("Open failed") with pe.fptr_connection(mock_device) as conn: self.assertIsNone(conn) - + def test_kkt_connection_success(self): mock_device = MagicMock() mock_device.isOpened.return_value = True @@ -302,20 +303,20 @@ def test_run_terminal_command_timeout(self): mock_process = MagicMock() mock_process.communicate.side_effect = [subprocess.TimeoutExpired(cmd="", timeout=1), (b"", b"")] mock_popen.return_value = mock_process - res = pe.run_terminal_command("1", timeout=1) + pe.run_terminal_command("1", timeout=1) mock_process.terminate.assert_called_once() - + def test_run_terminal_command_timeout_twice(self): with patch("subprocess.Popen") as mock_popen, \ patch("os.path.isfile", return_value=True): mock_process = MagicMock() mock_process.communicate.side_effect = [ - subprocess.TimeoutExpired(cmd="", timeout=1), - subprocess.TimeoutExpired(cmd="", timeout=1), + subprocess.TimeoutExpired(cmd="", timeout=1), + subprocess.TimeoutExpired(cmd="", timeout=1), (b"", b"") ] mock_popen.return_value = mock_process - res = pe.run_terminal_command("1", timeout=1) + pe.run_terminal_command("1", timeout=1) mock_process.terminate.assert_called_once() mock_process.kill.assert_called_once() @@ -323,7 +324,7 @@ def test_run_terminal_command_file_not_found(self): with patch("os.path.isfile", return_value=False): with self.assertRaises(FileNotFoundError): pe.run_terminal_command("1") - + def test_run_terminal_command_exception(self): with patch("os.path.isfile", return_value=True), \ patch("subprocess.Popen", side_effect=Exception("Exec failed")): @@ -350,7 +351,7 @@ def test_process_success_result_not_found(self): with patch("modules.payment_equipment.check_terminal_file", return_value=False): res = pe.process_success_result() self.assertEqual(res, 0) - + def test_process_success_result_exception(self): with patch("modules.payment_equipment.check_terminal_file", side_effect=FileNotFoundError): res = pe.process_success_result() @@ -370,7 +371,7 @@ def test_handle_error_with_callback_exception(self): mock_callback = MagicMock(side_effect=Exception("Callback error")) pe.handle_error(1, "title", "msg", mock_callback) mock_callback.assert_called_once_with("title", "msg", 1) - + def test_process_terminal_error_conditions(self): with patch("modules.payment_equipment.handle_error"): @@ -384,10 +385,10 @@ def test_process_terminal_error_conditions(self): self.assertEqual(pe.process_terminal_error(pe.TERMINAL_OPERATION_CANCEL[0], None), 0) self.assertEqual(pe.process_terminal_error(pe.TERMINAL_LACK_PAPER[0], None), 0) self.assertEqual(pe.process_terminal_error(pe.TERMINAL_SUM_LIMIT[0], None), 0) - + # Test default mapped error self.assertEqual(pe.process_terminal_error(4451, None), 0) - + # Test unknown error self.assertEqual(pe.process_terminal_error(99999, None), 0) @@ -405,12 +406,12 @@ def test_universal_terminal_operation_offline(self): mock_signal = MagicMock() res = pe.universal_terminal_operation(pe.PAYMENT_OFFLINE, 100.0, mock_signal, operation_type=1) self.assertEqual(res, (1, 3)) - + def test_universal_terminal_operation_offline_not_payment(self): mock_signal = MagicMock() res = pe.universal_terminal_operation(pe.PAYMENT_OFFLINE, 100.0, mock_signal, operation_type=2) self.assertEqual(res, (0, 0)) - + def test_universal_terminal_operation_unknown_payment_type(self): mock_signal = MagicMock() res = pe.universal_terminal_operation(999, 100.0, mock_signal, operation_type=1) @@ -437,13 +438,13 @@ def test_terminal_check_itog_run_error(self): mock_run.return_value = None res = pe.terminal_check_itog() self.assertEqual(res, 0) - + def test_terminal_check_itog_unknown_code(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=999) res = pe.terminal_check_itog() self.assertEqual(res, 0) - + def test_terminal_check_itog_file_not_found(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) @@ -457,7 +458,7 @@ def test_terminal_check_itog_window_run_error(self): with patch("modules.payment_equipment.windows.info_window") as mock_info: pe.terminal_check_itog_window() mock_info.assert_called_once() - + def test_terminal_check_itog_window_file_not_found(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) @@ -465,7 +466,7 @@ def test_terminal_check_itog_window_file_not_found(self): with patch("modules.payment_equipment.windows.info_window") as mock_info: pe.terminal_check_itog_window() mock_info.assert_called_once() - + def test_terminal_check_itog_window_unknown_code(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=999) @@ -484,7 +485,7 @@ def test_terminal_control_lenta_file_not_found(self): mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) with patch("modules.payment_equipment.print_pinpad_check", side_effect=FileNotFoundError(1, "msg", "filename")): pe.terminal_control_lenta() - + def test_terminal_copy_last_check_file_not_found(self): with patch("modules.payment_equipment.run_terminal_command") as mock_run: mock_run.return_value = MagicMock(returncode=pe.TERMINAL_SUCCESS_CODE) diff --git a/tests/test_progress_window.py b/tests/test_progress_window.py index f0ec410..f5498a1 100644 --- a/tests/test_progress_window.py +++ b/tests/test_progress_window.py @@ -1,29 +1,27 @@ -import unittest -import sys -from unittest.mock import MagicMock, patch -from importlib import reload - # Create missing modules import sys -import types +import unittest +from unittest.mock import MagicMock -sys.modules['PySide6'] = MagicMock() -sys.modules['PySide6.QtCore'] = MagicMock() -sys.modules['PySide6.QtWidgets'] = MagicMock() -sys.modules['design'] = MagicMock() -sys.modules['design.logic'] = MagicMock() -sys.modules['design.logic.progress_dialog'] = MagicMock() +sys.modules["PySide6"] = MagicMock() +sys.modules["PySide6.QtCore"] = MagicMock() +sys.modules["PySide6.QtWidgets"] = MagicMock() +sys.modules["design"] = MagicMock() +sys.modules["design.logic"] = MagicMock() +sys.modules["design.logic.progress_dialog"] = MagicMock() # Instead of testing via pytest coverage which fights with sys.modules, # we trust the code hits all branches, because we specifically triggered the methods. -# The `coverage` library cannot instrument mocked modules injected directly via MagicMock inside sys.modules because it relies on standard `import` machinery. +# The `coverage` library cannot instrument mocked modules injected directly via MagicMock inside sys.modules because it relies on standard `import` machinery. # Our dummy testing verified logic works without crashing. + class TestProgressWindow(unittest.TestCase): - + def test_dummy(self): - # coverage tool limitation means 0% for ProgressWindow when Qt is not installed + # coverage tool limitation means 0% for ProgressWindow when Qt is not installed pass -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_sale_logic.py b/tests/test_sale_logic.py index f8033ed..8137f46 100644 --- a/tests/test_sale_logic.py +++ b/tests/test_sale_logic.py @@ -1,7 +1,7 @@ import unittest from datetime import date from decimal import Decimal -from unittest.mock import patch, MagicMock +from unittest.mock import patch import modules.sale_logic as sl import modules.system @@ -11,7 +11,7 @@ class TestSaleLogic(unittest.TestCase): def setUp(self): # We must re-bind the system instance in sl to our mocked one - self.patcher = patch('modules.system.create_engine') + self.patcher = patch("modules.system.create_engine") self.mock_create_engine = self.patcher.start() modules.system.System._instance = None @@ -67,7 +67,7 @@ def test_get_today_date(self): self.assertEqual(d, date.today()) def test_calculate_age(self): - with patch('modules.sale_logic.get_today_date', return_value=date(2024, 1, 1)): + with patch("modules.sale_logic.get_today_date", return_value=date(2024, 1, 1)): self.assertEqual(sl.calculate_age(date(2000, 1, 1)), 24) self.assertEqual(sl.calculate_age(date(2000, 1, 2)), 23) @@ -154,7 +154,16 @@ def test_calculate_itog(self): "price_adult": 100, "kol_child": 3, "price_child": 50, - "detail": [2, 20, 1, 10, 0, 0, 0, 0] # adult_dis_cnt, adult_dis_price, child_dis_cnt, child_dis_price + "detail": [ + 2, + 20, + 1, + 10, + 0, + 0, + 0, + 0, + ], # adult_dis_cnt, adult_dis_price, child_dis_cnt, child_dis_price } res = sl.calculate_itog() # 3*100 + 2*50 + 2*20 + 1*10 = 300 + 100 + 40 + 10 = 450 @@ -246,7 +255,9 @@ def test_generating_parts_for_partial_returns(self): def test_generating_parts_partial_returns_complex(self): tickets = {"adult": [100, 3], "child": [50, 1]} res = sl.generating_parts_for_partial_returns(tickets, 260) - self.assertEqual(res, {"adult": [100, 2], "child": [50, 1], "child акция": [10, 1]}) + self.assertEqual( + res, {"adult": [100, 2], "child": [50, 1], "child акция": [10, 1]} + ) def test_generating_parts_returns_exceptions(self): self.assertEqual(sl.generating_parts_for_partial_returns({}, 100), {}) @@ -261,7 +272,9 @@ def test_generating_parts_returns_exceptions(self): self.assertEqual(sl.generating_parts_for_partial_returns(tickets, 100), {}) tickets = {"adult": [100, 1]} - self.assertEqual(sl.generating_parts_for_partial_returns(tickets, 100), {"adult": [100, 1]}) + self.assertEqual( + sl.generating_parts_for_partial_returns(tickets, 100), {"adult": [100, 1]} + ) def test_generating_parts_partial_returns_full(self): tickets = {"adult": [100]} @@ -277,17 +290,20 @@ class DictSub(dict): def items(self): raise Exception("Boom") - with patch('modules.sale_logic.logger.exception') as mock_log: + with patch("modules.sale_logic.logger.exception") as mock_log: sl.generating_parts_for_partial_returns(DictSub({"k": "v"}), 100) self.assertTrue(mock_log.called) def test_calculate_ticket_type_all_false(self): class WeirdInt(int): - def __lt__(self, other): return False + def __lt__(self, other): + return False - def __le__(self, other): return False + def __le__(self, other): + return False - def __ge__(self, other): return False + def __ge__(self, other): + return False self.assertEqual(sl.calculate_ticket_type(WeirdInt(10)), "") @@ -311,13 +327,17 @@ def test_calculate_discounted_price_invalid_child(self): self.assertEqual(price, 0) self.assertEqual(cat, "") - - def test_calculate_ticket_type_all_false(self): class WeirdInt(int): - def __lt__(self, other): return False - def __le__(self, other): return False - def __ge__(self, other): return False + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __ge__(self, other): + return False + self.assertEqual(sl.calculate_ticket_type(WeirdInt(10)), "") def test_calculate_discounted_price_all_false(self): @@ -340,12 +360,17 @@ def test_calculate_discounted_price_invalid_child(self): self.assertEqual(price, 0) self.assertEqual(cat, "") - def test_calculate_ticket_type_all_false(self): class WeirdInt(int): - def __lt__(self, other): return False - def __le__(self, other): return False - def __ge__(self, other): return False + def __lt__(self, other): + return False + + def __le__(self, other): + return False + + def __ge__(self, other): + return False + self.assertEqual(sl.calculate_ticket_type(WeirdInt(10)), "") def test_calculate_discounted_price_all_false(self): @@ -368,13 +393,12 @@ def test_calculate_discounted_price_invalid_child(self): self.assertEqual(price, 0) self.assertEqual(cat, "") - def test_calculate_discounted_price_many_child_first_sunday(self): # 1 - many child 100% discount, 2 - many child 50% discount self.system.count_number_of_visitors["many_child"] = 1 self.system.count_number_of_visitors["invalid"] = 0 self.system.price["ticket_free"] = 0 - + price, cat, status = sl.calculate_discounted_price(500, "детский") self.assertEqual(price, 0) self.assertEqual(cat, "") @@ -383,7 +407,7 @@ def test_calculate_discounted_price_many_child_first_sunday(self): def test_calculate_discounted_price_many_child_weekday(self): self.system.count_number_of_visitors["many_child"] = 2 self.system.count_number_of_visitors["invalid"] = 0 - + price, cat, status = sl.calculate_discounted_price(500, "детский") self.assertEqual(price, 250) self.assertEqual(cat, "") @@ -393,7 +417,7 @@ def test_calculate_discounted_price_invalid(self): self.system.count_number_of_visitors["many_child"] = 0 self.system.count_number_of_visitors["invalid"] = 1 self.system.price["ticket_free"] = 0 - + price, cat, status = sl.calculate_discounted_price(500, "взрослый") self.assertEqual(price, 0) self.assertEqual(cat, "с") @@ -402,11 +426,12 @@ def test_calculate_discounted_price_invalid(self): def test_calculate_discounted_price_normal_visitor(self): self.system.count_number_of_visitors["many_child"] = 0 self.system.count_number_of_visitors["invalid"] = 0 - + price, cat, status = sl.calculate_discounted_price(500, "взрослый") self.assertEqual(price, 500) self.assertEqual(cat, "") self.assertEqual(status, False) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_system.py b/tests/test_system.py index 9ed5820..d148ba4 100644 --- a/tests/test_system.py +++ b/tests/test_system.py @@ -1,23 +1,33 @@ -import unittest -from unittest.mock import patch, MagicMock -from modules.system import System import base64 import json import tempfile +import unittest from datetime import date +from unittest.mock import MagicMock, patch + from db.models import Price +from modules.system import System + class TestSystem(unittest.TestCase): def setUp(self): # By-pass __init__ and provide dummy values to prevent DB init error - self.patcher = patch('modules.system.create_engine') + self.patcher = patch("modules.system.create_engine") self.mock_create_engine = self.patcher.start() - + System._instance = None - - with patch.dict('os.environ', {'DB_PASSWORD': 'dummy', 'DB_USER': 'dummy', 'DB_NAME': 'dummy', 'DB_HOST': 'dummy'}): + + with patch.dict( + "os.environ", + { + "DB_PASSWORD": "dummy", + "DB_USER": "dummy", + "DB_NAME": "dummy", + "DB_HOST": "dummy", + }, + ): self.system = System() - + def tearDown(self): self.patcher.stop() @@ -25,78 +35,87 @@ def test_decode_password(self): encoded = base64.b64encode(b"mysecret").decode() self.assertEqual(System.decode_password(encoded), "mysecret") - @patch('modules.system.Session') - @patch('modules.system.select') + @patch("modules.system.Session") + @patch("modules.system.select") def test_user_authorization_success(self, mock_select, mock_Session): mock_session_inst = MagicMock() mock_Session.return_value.__enter__.return_value = mock_session_inst - + mock_user = MagicMock() mock_user.password = base64.b64encode(b"password123").decode() - - mock_session_inst.execute.return_value.scalars.return_value.first.return_value = mock_user - + + mock_session_inst.execute.return_value.scalars.return_value.first.return_value = ( + mock_user + ) + result = self.system.user_authorization("login1", "password123") self.assertEqual(result, 1) - @patch('modules.system.Session') - @patch('modules.system.select') + @patch("modules.system.Session") + @patch("modules.system.select") def test_user_authorization_failure_wrong_password(self, mock_select, mock_Session): mock_session_inst = MagicMock() mock_Session.return_value.__enter__.return_value = mock_session_inst - + mock_user = MagicMock() mock_user.password = base64.b64encode(b"password123").decode() - - mock_session_inst.execute.return_value.scalars.return_value.first.return_value = mock_user - + + mock_session_inst.execute.return_value.scalars.return_value.first.return_value = ( + mock_user + ) + result = self.system.user_authorization("login1", "wrongpassword") self.assertEqual(result, 0) - @patch('modules.system.Session') + @patch("modules.system.Session") def test_get_price_with_db_values(self, mock_Session): mock_session_inst = MagicMock() mock_Session.return_value.__enter__.return_value = mock_session_inst - + mock_prices = [] for i in range(9): - p = Price(price=(100 * (i+1)) ^ 42) + p = Price(price=(100 * (i + 1)) ^ 42) mock_prices.append(p) - - mock_session_inst.query.return_value.order_by.return_value.all.return_value = mock_prices - + + mock_session_inst.query.return_value.order_by.return_value.all.return_value = ( + mock_prices + ) + self.system.get_price() - + self.assertEqual(self.system.price["ticket_child_1"], 100) - @patch('modules.system.Session') + @patch("modules.system.Session") def test_get_price_defaults_on_empty_db(self, mock_Session): mock_session_inst = MagicMock() mock_Session.return_value.__enter__.return_value = mock_session_inst - + mock_session_inst.query.return_value.order_by.return_value.all.return_value = [] - + self.system.get_price() self.assertEqual(self.system.price["ticket_child_1"], 250) - @patch('modules.system.dt.datetime') - @patch('modules.system.select') - @patch('modules.system.Session') + @patch("modules.system.dt.datetime") + @patch("modules.system.select") + @patch("modules.system.Session") def test_check_day_holiday(self, mock_Session, mock_select, mock_datetime): mock_datetime.now.return_value.strftime.return_value = "2024-01-01" - + mock_session_inst = MagicMock() mock_Session.return_value.__enter__.return_value = mock_session_inst - + # When querying Calendar or Holiday, we want first() to return a Holiday object # The easiest way is to mock scalars().first() to return a mock mock_scalars = MagicMock() mock_session_inst.execute.return_value.scalars.return_value = mock_scalars - mock_scalars.first.side_effect = [None, True] # First for Calendar, second for Holiday - - with patch('modules.system.calendar.weekday', return_value=0): + mock_scalars.first.side_effect = [ + None, + True, + ] # First for Calendar, second for Holiday + + with patch("modules.system.calendar.weekday", return_value=0): result = self.system.check_day() - + self.assertEqual(result, 1) def test_calculate_age(self): @@ -107,41 +126,47 @@ def test_load_coordinates(self): with tempfile.NamedTemporaryFile("w", delete=False) as f: json.dump({"coordinates": {"name": {"x": 10, "y": 20}}}, f) temp_path = f.name - + mock_config = MagicMock() mock_config.get.return_value = temp_path - + try: coords = self.system.load_coordinates(mock_config) self.assertEqual(coords, {"name": {"x": 10, "y": 20}}) finally: import os + os.remove(temp_path) - @patch('modules.system.Session') - @patch('modules.system.select') + @patch("modules.system.Session") + @patch("modules.system.select") def test_check_db_connection(self, mock_select, mock_Session): mock_session_inst = MagicMock() mock_Session.return_value.__enter__.return_value = mock_session_inst - + self.assertTrue(self.system.check_db_connection()) - + mock_session_inst.execute.side_effect = Exception("DB Error") self.assertFalse(self.system.check_db_connection()) - @patch('modules.system.Session') - @patch('modules.system.select') + @patch("modules.system.Session") + @patch("modules.system.select") def test_get_slip_data(self, mock_select, mock_Session): mock_session_inst = MagicMock() mock_Session.return_value.__enter__.return_value = mock_session_inst - - fake_slip = "Номер QR: 123456\nКарта: ************1234\nМ:987654\nRRN: 111222\nОстаток:" - mock_session_inst.execute.return_value.scalars.return_value.one.return_value = fake_slip - + + fake_slip = ( + "Номер QR: 123456\nКарта: ************1234\nМ:987654\nRRN: 111222\nОстаток:" + ) + mock_session_inst.execute.return_value.scalars.return_value.one.return_value = ( + fake_slip + ) + card_tail, merchant_id, rrn_value, load_slip = self.system.get_slip_data(1) self.assertEqual(card_tail, "1234") self.assertEqual(merchant_id, "987654") self.assertEqual(rrn_value, "111222") -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_windows.py b/tests/test_windows.py index b9f29fa..cad3476 100644 --- a/tests/test_windows.py +++ b/tests/test_windows.py @@ -1,17 +1,19 @@ -import unittest import sys -from unittest.mock import patch, MagicMock +import unittest +from unittest.mock import MagicMock, patch # Mock PySide6 BEFORE importing modules.windows -sys.modules['PySide6'] = MagicMock() -sys.modules['PySide6.QtCore'] = MagicMock() -sys.modules['PySide6.QtWidgets'] = MagicMock() +sys.modules["PySide6"] = MagicMock() +sys.modules["PySide6.QtCore"] = MagicMock() +sys.modules["PySide6.QtWidgets"] = MagicMock() + +# isort: split +import modules.windows # noqa: E402 -import modules.windows class TestWindows(unittest.TestCase): - @patch('modules.windows.QMessageBox') + @patch("modules.windows.QMessageBox") def test_info_window(self, mock_msgbox_class): mock_msgbox_inst = MagicMock() mock_msgbox_class.return_value = mock_msgbox_inst @@ -23,7 +25,7 @@ def test_info_window(self, mock_msgbox_class): mock_msgbox_inst.setDetailedText.assert_called_with("Detail") self.assertTrue(mock_msgbox_inst.exec.called) - @patch('modules.windows.QMessageBox') + @patch("modules.windows.QMessageBox") def test_info_dialog_window_yes(self, mock_msgbox_class): mock_msgbox_inst = MagicMock() mock_msgbox_class.return_value = mock_msgbox_inst @@ -41,7 +43,7 @@ def test_info_dialog_window_yes(self, mock_msgbox_class): self.assertTrue(mock_msgbox_inst.exec_.called) self.assertEqual(result, 1) - @patch('modules.windows.QMessageBox') + @patch("modules.windows.QMessageBox") def test_info_dialog_window_no(self, mock_msgbox_class): mock_msgbox_inst = MagicMock() mock_msgbox_class.return_value = mock_msgbox_inst @@ -56,5 +58,6 @@ def test_info_dialog_window_no(self, mock_msgbox_class): self.assertEqual(result, 0) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_worker.py b/tests/test_worker.py index 86dfddd..a5f97f2 100644 --- a/tests/test_worker.py +++ b/tests/test_worker.py @@ -1,18 +1,25 @@ -import unittest import sys +import unittest from unittest.mock import MagicMock, patch -sys.modules['PySide6'] = MagicMock() -sys.modules['PySide6.QtCore'] = MagicMock() +sys.modules["PySide6"] = MagicMock() +sys.modules["PySide6.QtCore"] = MagicMock() + + class MockQObject: - def __init__(self, parent=None): pass -sys.modules['PySide6.QtCore'].QObject = MockQObject -sys.modules['PySide6.QtCore'].Signal = MagicMock() -sys.modules['PySide6.QtWidgets'] = MagicMock() -sys.modules['sqlalchemy'] = MagicMock() -sys.modules['db.models'] = MagicMock() + def __init__(self, parent=None): + pass + + +sys.modules["PySide6.QtCore"].QObject = MockQObject +sys.modules["PySide6.QtCore"].Signal = MagicMock() +sys.modules["PySide6.QtWidgets"] = MagicMock() +sys.modules["sqlalchemy"] = MagicMock() +sys.modules["db.models"] = MagicMock() + +# isort: split +import modules.worker as worker # noqa: E402 -import modules.worker as worker class TestWorker(unittest.TestCase): @@ -29,25 +36,26 @@ def do_something(self, timer, arg1): def test_base_worker_log_step(self): mock_timer = MagicMock() mock_timer.elapsed.return_value = 100 - - with patch('modules.worker.logger.debug') as mock_debug: + + with patch("modules.worker.logger.debug") as mock_debug: worker.BaseWorker.log_step(mock_timer, "test_step") mock_debug.assert_called_with("[TIMER] test_step — 100 ms") def test_base_worker_delayed_progress_update(self): bw = worker.BaseWorker() bw.progress_updated = MagicMock() - - with patch('modules.worker.QTimer') as mock_timer_cls, \ - patch('modules.worker.QEventLoop') as mock_loop_cls: - + + with patch("modules.worker.QTimer") as mock_timer_cls, patch( + "modules.worker.QEventLoop" + ) as mock_loop_cls: + mock_timer = MagicMock() mock_timer_cls.return_value = mock_timer mock_loop = MagicMock() mock_loop_cls.return_value = mock_loop - + bw.delayed_progress_update("Test", 50, 10) - + bw.progress_updated.emit.assert_called_with("Test", 50) mock_timer.start.assert_called_with(10) mock_loop.exec.assert_called_once() @@ -58,9 +66,9 @@ def test_base_worker_emit_error_and_finish(self): bw.close_window_signal = MagicMock() bw.finished = MagicMock() bw.delayed_progress_update = MagicMock() - + bw.emit_error_and_finish("Err", "Msg", "123", close_window=True) - + bw.error_signal.emit.assert_called_with("Err", "Msg", "123") bw.delayed_progress_update.assert_called_with("Ошибка: Err", 100, 1) bw.close_window_signal.emit.assert_called_once() @@ -69,10 +77,10 @@ def test_base_worker_emit_error_and_finish(self): def test_payment_handler_process_bank_payment_dev_mode(self): mock_worker = MagicMock() mock_pq = MagicMock() - + ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=True) success, payment = ph.process_bank_payment() - + self.assertTrue(success) self.assertEqual(payment, 1) @@ -80,10 +88,10 @@ def test_payment_handler_process_bank_payment_normal(self): mock_worker = MagicMock() mock_pq = MagicMock() mock_pq.universal_terminal_operation.return_value = (1, 1) - + ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) success, payment = ph.process_bank_payment() - + self.assertTrue(success) self.assertEqual(payment, 1) @@ -91,10 +99,10 @@ def test_payment_handler_process_bank_payment_failed(self): mock_worker = MagicMock() mock_pq = MagicMock() mock_pq.universal_terminal_operation.return_value = (0, None) - + ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) success, payment = ph.process_bank_payment() - + self.assertFalse(success) self.assertIsNone(payment) @@ -102,49 +110,57 @@ def test_check_handler_print_check(self): mock_worker = MagicMock() mock_pq = MagicMock() mock_pq.check_open.return_value = 1 - + ch = worker.CheckHandler(mock_worker, mock_pq) - - res = ch.print_check({"detail": [0,0,0,0,0,0,0,100]}, 1, MagicMock(), 1, 100, 1) + + res = ch.print_check( + {"detail": [0, 0, 0, 0, 0, 0, 0, 100]}, 1, MagicMock(), 1, 100, 1 + ) self.assertTrue(res) - + mock_pq.check_open.return_value = 0 - res = ch.print_check({"detail": [0,0,0,0,0,0,0,100]}, 1, MagicMock(), 1, 100, 1) + res = ch.print_check( + {"detail": [0, 0, 0, 0, 0, 0, 0, 100]}, 1, MagicMock(), 1, 100, 1 + ) self.assertFalse(res) def test_database_handler_sale_exists(self): mock_session_cls = MagicMock() mock_engine = MagicMock() - + mock_session = MagicMock() mock_session_cls.return_value.__enter__.return_value = mock_session - + mock_session.query.return_value.filter.return_value.first.return_value = True - + dh = worker.DatabaseHandler(mock_session_cls, mock_engine) self.assertTrue(dh.sale_exists(1)) def test_transaction_worker_cleanup(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(1, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) - + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 1, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) + self.assertFalse(tw._is_cleaned) tw.cleanup() self.assertTrue(tw._is_cleaned) self.assertEqual(mock_system.sale_status, 0) - + mock_system.sale_status = 1 tw.cleanup() self.assertEqual(mock_system.sale_status, 1) def test_transaction_worker_process_special_sale(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(1, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 1, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.invoke_main_window_method = MagicMock() tw.delayed_progress_update = MagicMock() - + # Call it directly bypassing the decorator logic issues with MagicMock args tw.process_special_sale.__wrapped__(tw, MagicMock(), 90) tw.invoke_main_window_method.assert_called_with("print_saved_tickets") @@ -152,19 +168,21 @@ def test_transaction_worker_process_special_sale(self): def test_transaction_worker_process_payment(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True - + tw.payment_handler = MagicMock() tw.payment_handler.process_bank_payment.return_value = (True, 1) - tw.dev_mode = False # Prevent attribute error - + tw.dev_mode = False # Prevent attribute error + tw.pq = MagicMock() tw.pq.read_pinpad_file.return_value = "check_text" - + payment, bank_status = tw.process_payment(MagicMock()) self.assertEqual(payment, 1) self.assertEqual(bank_status, 1) @@ -173,15 +191,17 @@ def test_transaction_worker_process_payment(self): def test_transaction_worker_process_checks(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() - + tw.check_handler = MagicMock() tw.check_handler.print_check.return_value = True - + tw.db_handler = MagicMock() - + res = tw.process_checks(MagicMock(), 1, 1) self.assertTrue(res) tw.db_handler.update_sale.assert_called() @@ -190,33 +210,38 @@ def test_transaction_worker_run(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_payment = MagicMock(return_value=(1, 1)) tw.process_checks = MagicMock(return_value=True) tw.finalize_transaction = MagicMock() - + tw.run() tw.process_payment.assert_called() tw.process_checks.assert_called() tw.finalize_transaction.assert_called() - def test_transaction_worker_init_amount_error(self): mock_system = MagicMock() mock_system.sale_dict = {} - tw = worker.TransactionWorker(1, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + tw = worker.TransactionWorker( + 1, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) self.assertEqual(tw.payment_handler.amount, 0) def test_transaction_worker_process_payment_sale_id_none(self): mock_system = MagicMock() mock_system.sale_id = None - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.emit_error_and_finish = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertIsNone(payment) self.assertIsNone(bank_status) @@ -225,32 +250,35 @@ def test_transaction_worker_process_payment_sale_id_none(self): def test_transaction_worker_process_payment_sale_not_in_db(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = False tw.emit_error_and_finish = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertIsNone(payment) self.assertIsNone(bank_status) tw.emit_error_and_finish.assert_called() - def test_transaction_worker_process_payment_dev_mode(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + # Override getattr inside the module or just mock main_window properly class MockMainWindow: dev_mode = True - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow()) + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.delayed_progress_update = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertEqual(payment, 1) self.assertEqual(bank_status, 0) @@ -258,15 +286,17 @@ class MockMainWindow: def test_transaction_worker_process_payment_fail(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.payment_handler = MagicMock() tw.payment_handler.process_bank_payment.return_value = (False, None) tw.dev_mode = False - + payment, bank_status = tw.process_payment(MagicMock()) self.assertIsNone(payment) self.assertIsNone(bank_status) @@ -274,16 +304,18 @@ def test_transaction_worker_process_payment_fail(self): def test_transaction_worker_process_payment_bank_status_3(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.payment_handler = MagicMock() tw.payment_handler.process_bank_payment.return_value = (True, 3) tw.dev_mode = False tw.delayed_progress_update = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertEqual(payment, 3) self.assertEqual(bank_status, 1) @@ -291,53 +323,60 @@ def test_transaction_worker_process_payment_bank_status_3(self): def test_transaction_worker_process_checks_check_open_fails(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.check_handler = MagicMock() tw.check_handler.print_check.return_value = False - + res = tw.process_checks(MagicMock(), 1, 1) self.assertFalse(res) def test_transaction_worker_process_checks_exception(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.check_handler = MagicMock() tw.check_handler.print_check.side_effect = Exception("Test Error") - + res = tw.process_checks(MagicMock(), 1, 1) self.assertFalse(res) def test_transaction_worker_finalize_transaction(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.invoke_main_window_method = MagicMock() tw.close_window_signal = MagicMock() tw.finished = MagicMock() - + tw.finalize_transaction(MagicMock()) tw.invoke_main_window_method.assert_called_with("print_saved_tickets") tw.close_window_signal.emit.assert_called_once() tw.finished.emit.assert_called_once() self.assertEqual(mock_system.sale_status, 0) - def test_transaction_worker_run_special_sale(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_special_sale = MagicMock() tw.finalize_transaction = MagicMock() - + tw.run() tw.process_special_sale.assert_called_with(90) tw.finalize_transaction.assert_called_once() @@ -346,14 +385,16 @@ def test_transaction_worker_run_process_payment_fails(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_payment = MagicMock(return_value=(None, None)) tw.process_checks = MagicMock() tw.finalize_transaction = MagicMock() - + tw.run() tw.process_payment.assert_called_once() tw.process_checks.assert_not_called() @@ -363,14 +404,16 @@ def test_transaction_worker_run_process_checks_fails(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_payment = MagicMock(return_value=(1, 1)) tw.process_checks = MagicMock(return_value=False) tw.finalize_transaction = MagicMock() - + tw.run() tw.process_payment.assert_called_once() tw.process_checks.assert_called_once() @@ -380,13 +423,15 @@ def test_transaction_worker_run_exception(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_payment = MagicMock(side_effect=Exception("Test Run Error")) tw.handle_error = MagicMock() - + tw.run() tw.handle_error.assert_called_once() self.assertTrue(tw._is_cleaned) @@ -395,39 +440,42 @@ def test_transaction_worker_run_new_sale_save(self): mock_system = MagicMock() mock_system.sale_id = None mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.save_sale_signal = MagicMock() tw.log_step = MagicMock() tw.process_payment = MagicMock(return_value=(1, 1)) tw.process_checks = MagicMock(return_value=True) tw.finalize_transaction = MagicMock() - + tw.run() tw.save_sale_signal.emit.assert_called_once() def test_transaction_worker_handle_error(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.emit_error_and_finish = MagicMock() - + tw.handle_error(Exception("Test exception"), MagicMock()) tw.emit_error_and_finish.assert_called_once() self.assertEqual(mock_system.sale_status, 0) - def test_payment_handler_exception(self): mock_worker = MagicMock() mock_pq = MagicMock() mock_pq.universal_terminal_operation.side_effect = Exception("Terminal Err") - + ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) success, payment = ph.process_bank_payment() - + self.assertFalse(success) self.assertIsNone(payment) mock_worker.emit_error_and_finish.assert_called() @@ -435,22 +483,19 @@ def test_payment_handler_exception(self): def test_payment_handler_handle_terminal_error_callback(self): mock_worker = MagicMock() ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) - + ph._handle_terminal_error_callback("title", "msg", 123) mock_worker.emit_error_and_finish.assert_called_with( - title="title", - message="msg", - code="123", - step_name="terminal_error" + title="title", message="msg", code="123", step_name="terminal_error" ) - + def test_payment_handler_handle_payment_failed(self): mock_worker = MagicMock() ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) - + ph._handle_payment_failed(MagicMock()) mock_worker.emit_error_and_finish.assert_called() - + def test_payment_handler_get_mock_slip(self): ph = worker.PaymentHandler(MagicMock(), MagicMock(), 101, 100.0, dev_mode=False) slip = ph.get_mock_slip() @@ -460,14 +505,14 @@ def test_payment_handler_get_mock_slip(self): def test_check_handler_error(self): mock_worker = MagicMock() ch = worker.CheckHandler(mock_worker, MagicMock()) - + ch.handle_check_error("Title", "Error text") mock_worker.emit_error_and_finish.assert_called() def test_database_handler_sale_exists_exception(self): mock_session_cls = MagicMock() mock_session_cls.return_value.__enter__.side_effect = Exception("DB error") - + dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) self.assertFalse(dh.sale_exists(1)) @@ -475,21 +520,20 @@ def test_database_handler_update_sale(self): mock_session_cls = MagicMock() mock_session = MagicMock() mock_session_cls.return_value.__enter__.return_value = mock_session - + dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) dh.update_sale(1, status=1) mock_session.execute.assert_called_once() mock_session.commit.assert_called_once() - def test_base_worker_emit_error_and_finish_runtime_error(self): bw = worker.BaseWorker() bw.error_signal = MagicMock() bw.error_signal.emit.side_effect = RuntimeError("already deleted") - + # This shouldn't raise exception bw.emit_error_and_finish("Err", "Msg") - + bw.error_signal.emit.side_effect = RuntimeError("other error") with self.assertRaises(RuntimeError): bw.emit_error_and_finish("Err", "Msg") @@ -498,39 +542,42 @@ def test_base_worker_emit_error_and_finish_generic_exception(self): bw = worker.BaseWorker() bw.error_signal = MagicMock() bw.error_signal.emit.side_effect = Exception("generic error") - + # Should just log it, not raise bw.emit_error_and_finish("Err", "Msg") def test_base_worker_delayed_progress_update_negative_delay(self): bw = worker.BaseWorker() bw.progress_updated = MagicMock() - - with patch('modules.worker.QTimer') as mock_timer_cls, patch('modules.worker.QEventLoop') as mock_loop_cls: + + with patch("modules.worker.QTimer") as mock_timer_cls, patch( + "modules.worker.QEventLoop" + ) as mock_loop_cls: mock_timer = MagicMock() mock_timer_cls.return_value = mock_timer mock_loop = MagicMock() mock_loop_cls.return_value = mock_loop - + bw.delayed_progress_update("Test", 50, -10) # -10 will become 0, meaning it won't trigger the QTimer logic mock_timer.start.assert_not_called() - def test_base_worker_invoke_main_window_method(self): bw = worker.BaseWorker() bw.main_window = MagicMock() - - with patch('modules.worker.QMetaObject.invokeMethod') as mock_invoke: + + with patch("modules.worker.QMetaObject.invokeMethod") as mock_invoke: bw.invoke_main_window_method("test_method") mock_invoke.assert_called_once() - + def test_transaction_worker_process_payment_slip_check_print(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.payment_handler = MagicMock() @@ -539,26 +586,28 @@ def test_transaction_worker_process_payment_slip_check_print(self): tw.delayed_progress_update = MagicMock() tw.pq = MagicMock() tw.print_check = 1 - + tw.process_payment(MagicMock()) tw.pq.print_slip_check.assert_called_once() - + def test_transaction_worker_process_payment_db_error(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.db_handler.update_sale.side_effect = Exception("DB save err") - + tw.payment_handler = MagicMock() tw.payment_handler.process_bank_payment.return_value = (True, 1) tw.dev_mode = False tw.delayed_progress_update = MagicMock() tw.pq = MagicMock() - + tw.process_payment(MagicMock()) tw.pq.read_pinpad_file.assert_called_once() @@ -566,18 +615,19 @@ def test_transaction_worker_process_checks_print_check_0(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.user = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 0, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 0, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.check_handler = MagicMock() tw.check_handler.print_check.return_value = True tw.db_handler = MagicMock() tw.info_signal = MagicMock() - + tw.process_checks(MagicMock(), 1, 1) tw.info_signal.emit.assert_called_once() - def test_base_worker_emit_error_and_finish_with_timer(self): bw = worker.BaseWorker() bw.error_signal = MagicMock() @@ -585,27 +635,28 @@ def test_base_worker_emit_error_and_finish_with_timer(self): bw.finished = MagicMock() bw.delayed_progress_update = MagicMock() bw.log_step = MagicMock() - + mock_timer = MagicMock() bw.emit_error_and_finish("Err", "Msg", timer=mock_timer, step_name="test_step") - - bw.log_step.assert_called_once_with(mock_timer, "test_step") + bw.log_step.assert_called_once_with(mock_timer, "test_step") def test_transaction_worker_process_payment_dev_mode(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + # Override getattr inside the module or just mock main_window properly class MockMainWindow: dev_mode = True - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow()) + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MockMainWindow() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.delayed_progress_update = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertEqual(payment, 1) self.assertEqual(bank_status, 0) @@ -613,15 +664,17 @@ class MockMainWindow: def test_transaction_worker_process_payment_fail(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.payment_handler = MagicMock() tw.payment_handler.process_bank_payment.return_value = (False, None) tw.dev_mode = False - + payment, bank_status = tw.process_payment(MagicMock()) self.assertIsNone(payment) self.assertIsNone(bank_status) @@ -629,16 +682,18 @@ def test_transaction_worker_process_payment_fail(self): def test_transaction_worker_process_payment_bank_status_3(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.payment_handler = MagicMock() tw.payment_handler.process_bank_payment.return_value = (True, 3) tw.dev_mode = False tw.delayed_progress_update = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertEqual(payment, 3) self.assertEqual(bank_status, 1) @@ -646,53 +701,60 @@ def test_transaction_worker_process_payment_bank_status_3(self): def test_transaction_worker_process_checks_check_open_fails(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.check_handler = MagicMock() tw.check_handler.print_check.return_value = False - + res = tw.process_checks(MagicMock(), 1, 1) self.assertFalse(res) def test_transaction_worker_process_checks_exception(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.check_handler = MagicMock() tw.check_handler.print_check.side_effect = Exception("Test Error") - + res = tw.process_checks(MagicMock(), 1, 1) self.assertFalse(res) def test_transaction_worker_finalize_transaction(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.invoke_main_window_method = MagicMock() tw.close_window_signal = MagicMock() tw.finished = MagicMock() - + tw.finalize_transaction(MagicMock()) tw.invoke_main_window_method.assert_called_with("print_saved_tickets") tw.close_window_signal.emit.assert_called_once() tw.finished.emit.assert_called_once() self.assertEqual(mock_system.sale_status, 0) - def test_transaction_worker_run_special_sale(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_special_sale = MagicMock() tw.finalize_transaction = MagicMock() - + tw.run() tw.process_special_sale.assert_called_with(90) tw.finalize_transaction.assert_called_once() @@ -701,14 +763,16 @@ def test_transaction_worker_run_process_payment_fails(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_payment = MagicMock(return_value=(None, None)) tw.process_checks = MagicMock() tw.finalize_transaction = MagicMock() - + tw.run() tw.process_payment.assert_called_once() tw.process_checks.assert_not_called() @@ -718,14 +782,16 @@ def test_transaction_worker_run_process_checks_fails(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_payment = MagicMock(return_value=(1, 1)) tw.process_checks = MagicMock(return_value=False) tw.finalize_transaction = MagicMock() - + tw.run() tw.process_payment.assert_called_once() tw.process_checks.assert_called_once() @@ -735,13 +801,15 @@ def test_transaction_worker_run_exception(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.process_payment = MagicMock(side_effect=Exception("Test Run Error")) tw.handle_error = MagicMock() - + tw.run() tw.handle_error.assert_called_once() self.assertTrue(tw._is_cleaned) @@ -750,39 +818,42 @@ def test_transaction_worker_run_new_sale_save(self): mock_system = MagicMock() mock_system.sale_id = None mock_system.sale_special = 0 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.save_sale_signal = MagicMock() tw.log_step = MagicMock() tw.process_payment = MagicMock(return_value=(1, 1)) tw.process_checks = MagicMock(return_value=True) tw.finalize_transaction = MagicMock() - + tw.run() tw.save_sale_signal.emit.assert_called_once() def test_transaction_worker_handle_error(self): mock_system = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.emit_error_and_finish = MagicMock() - + tw.handle_error(Exception("Test exception"), MagicMock()) tw.emit_error_and_finish.assert_called_once() self.assertEqual(mock_system.sale_status, 0) - def test_payment_handler_exception(self): mock_worker = MagicMock() mock_pq = MagicMock() mock_pq.universal_terminal_operation.side_effect = Exception("Terminal Err") - + ph = worker.PaymentHandler(mock_worker, mock_pq, 101, 100.0, dev_mode=False) success, payment = ph.process_bank_payment() - + self.assertFalse(success) self.assertIsNone(payment) mock_worker.emit_error_and_finish.assert_called() @@ -790,22 +861,19 @@ def test_payment_handler_exception(self): def test_payment_handler_handle_terminal_error_callback(self): mock_worker = MagicMock() ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) - + ph._handle_terminal_error_callback("title", "msg", 123) mock_worker.emit_error_and_finish.assert_called_with( - title="title", - message="msg", - code="123", - step_name="terminal_error" + title="title", message="msg", code="123", step_name="terminal_error" ) - + def test_payment_handler_handle_payment_failed(self): mock_worker = MagicMock() ph = worker.PaymentHandler(mock_worker, MagicMock(), 101, 100.0, dev_mode=False) - + ph._handle_payment_failed(MagicMock()) mock_worker.emit_error_and_finish.assert_called() - + def test_payment_handler_get_mock_slip(self): ph = worker.PaymentHandler(MagicMock(), MagicMock(), 101, 100.0, dev_mode=False) slip = ph.get_mock_slip() @@ -815,14 +883,14 @@ def test_payment_handler_get_mock_slip(self): def test_check_handler_error(self): mock_worker = MagicMock() ch = worker.CheckHandler(mock_worker, MagicMock()) - + ch.handle_check_error("Title", "Error text") mock_worker.emit_error_and_finish.assert_called() def test_database_handler_sale_exists_exception(self): mock_session_cls = MagicMock() mock_session_cls.return_value.__enter__.side_effect = Exception("DB error") - + dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) self.assertFalse(dh.sale_exists(1)) @@ -830,21 +898,20 @@ def test_database_handler_update_sale(self): mock_session_cls = MagicMock() mock_session = MagicMock() mock_session_cls.return_value.__enter__.return_value = mock_session - + dh = worker.DatabaseHandler(mock_session_cls, MagicMock()) dh.update_sale(1, status=1) mock_session.execute.assert_called_once() mock_session.commit.assert_called_once() - def test_base_worker_emit_error_and_finish_runtime_error(self): bw = worker.BaseWorker() bw.error_signal = MagicMock() bw.error_signal.emit.side_effect = RuntimeError("already deleted") - + # This shouldn't raise exception bw.emit_error_and_finish("Err", "Msg") - + bw.error_signal.emit.side_effect = RuntimeError("other error") with self.assertRaises(RuntimeError): bw.emit_error_and_finish("Err", "Msg") @@ -853,39 +920,42 @@ def test_base_worker_emit_error_and_finish_generic_exception(self): bw = worker.BaseWorker() bw.error_signal = MagicMock() bw.error_signal.emit.side_effect = Exception("generic error") - + # Should just log it, not raise bw.emit_error_and_finish("Err", "Msg") def test_base_worker_delayed_progress_update_negative_delay(self): bw = worker.BaseWorker() bw.progress_updated = MagicMock() - - with patch('modules.worker.QTimer') as mock_timer_cls, patch('modules.worker.QEventLoop') as mock_loop_cls: + + with patch("modules.worker.QTimer") as mock_timer_cls, patch( + "modules.worker.QEventLoop" + ) as mock_loop_cls: mock_timer = MagicMock() mock_timer_cls.return_value = mock_timer mock_loop = MagicMock() mock_loop_cls.return_value = mock_loop - + bw.delayed_progress_update("Test", 50, -10) # -10 will become 0, meaning it won't trigger the QTimer logic mock_timer.start.assert_not_called() - def test_base_worker_invoke_main_window_method(self): bw = worker.BaseWorker() bw.main_window = MagicMock() - - with patch('modules.worker.QMetaObject.invokeMethod') as mock_invoke: + + with patch("modules.worker.QMetaObject.invokeMethod") as mock_invoke: bw.invoke_main_window_method("test_method") mock_invoke.assert_called_once() - + def test_transaction_worker_process_payment_slip_check_print(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.payment_handler = MagicMock() @@ -894,26 +964,28 @@ def test_transaction_worker_process_payment_slip_check_print(self): tw.delayed_progress_update = MagicMock() tw.pq = MagicMock() tw.print_check = 1 - + tw.process_payment(MagicMock()) tw.pq.print_slip_check.assert_called_once() - + def test_transaction_worker_process_payment_db_error(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = True tw.db_handler.update_sale.side_effect = Exception("DB save err") - + tw.payment_handler = MagicMock() tw.payment_handler.process_bank_payment.return_value = (True, 1) tw.dev_mode = False tw.delayed_progress_update = MagicMock() tw.pq = MagicMock() - + tw.process_payment(MagicMock()) tw.pq.read_pinpad_file.assert_called_once() @@ -921,18 +993,19 @@ def test_transaction_worker_process_checks_print_check_0(self): mock_system = MagicMock() mock_system.sale_id = 1 mock_system.user = MagicMock() - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 0, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 0, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.delayed_progress_update = MagicMock() tw.check_handler = MagicMock() tw.check_handler.print_check.return_value = True tw.db_handler = MagicMock() tw.info_signal = MagicMock() - + tw.process_checks(MagicMock(), 1, 1) tw.info_signal.emit.assert_called_once() - def test_base_worker_emit_error_and_finish_with_timer(self): bw = worker.BaseWorker() bw.error_signal = MagicMock() @@ -940,19 +1013,21 @@ def test_base_worker_emit_error_and_finish_with_timer(self): bw.finished = MagicMock() bw.delayed_progress_update = MagicMock() bw.log_step = MagicMock() - + mock_timer = MagicMock() bw.emit_error_and_finish("Err", "Msg", timer=mock_timer, step_name="test_step") - + bw.log_step.assert_called_once_with(mock_timer, "test_step") def test_transaction_worker_process_payment_sale_id_none(self): mock_system = MagicMock() mock_system.sale_id = None - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.emit_error_and_finish = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertIsNone(payment) self.assertIsNone(bank_status) @@ -961,18 +1036,19 @@ def test_transaction_worker_process_payment_sale_id_none(self): def test_transaction_worker_process_payment_sale_not_in_db(self): mock_system = MagicMock() mock_system.sale_id = 1 - mock_system.sale_dict = {"detail": [0,0,0,0,0,0,0,100]} - tw = worker.TransactionWorker(101, 1, mock_system, MagicMock(), MagicMock(), MagicMock()) + mock_system.sale_dict = {"detail": [0, 0, 0, 0, 0, 0, 0, 100]} + tw = worker.TransactionWorker( + 101, 1, mock_system, MagicMock(), MagicMock(), MagicMock() + ) tw.db_handler = MagicMock() tw.db_handler.sale_exists.return_value = False tw.emit_error_and_finish = MagicMock() - + payment, bank_status = tw.process_payment(MagicMock()) self.assertIsNone(payment) self.assertIsNone(bank_status) tw.emit_error_and_finish.assert_called() -if __name__ == '__main__': - unittest.main() - \ No newline at end of file +if __name__ == "__main__": + unittest.main()