diff --git a/db/models/sale.py b/db/models/sale.py index a0035ee..4e59786 100644 --- a/db/models/sale.py +++ b/db/models/sale.py @@ -37,7 +37,7 @@ class Sale(Base): datetime_return: Mapped[DateTime | None] = mapped_column( DateTime, comment="Дата и время возврата продажи" ) - bank_return: Mapped[str | None | None] = mapped_column( + bank_return: Mapped[str | None] = mapped_column( Text, comment="Банковский чек возврата" ) partial_return: Mapped[int] = mapped_column( diff --git a/design/logic/authorization.py b/design/logic/authorization.py index 6238708..6242cff 100644 --- a/design/logic/authorization.py +++ b/design/logic/authorization.py @@ -3,7 +3,7 @@ from PySide6.QtWidgets import QFrame, QLabel, QLineEdit, QPushButton -class Ui_Dialog(object): +class Ui_Dialog: def setupUi(self, Dialog): if not Dialog.objectName(): Dialog.setObjectName("Dialog") diff --git a/design/logic/client.py b/design/logic/client.py index ddc3b51..a12ecad 100644 --- a/design/logic/client.py +++ b/design/logic/client.py @@ -13,7 +13,7 @@ ) -class Ui_Dialog_Client(object): +class Ui_Dialog_Client: def setupUi(self, Dialog_Client): if not Dialog_Client.objectName(): Dialog_Client.setObjectName("Dialog_Client") diff --git a/design/logic/main_form.py b/design/logic/main_form.py index b3bfd74..f0088d0 100644 --- a/design/logic/main_form.py +++ b/design/logic/main_form.py @@ -15,14 +15,14 @@ QPushButton, QRadioButton, QStatusBar, - QTabWidget, QTableWidget, QTableWidgetItem, + QTabWidget, QWidget, ) -class Ui_MainWindow(object): +class Ui_MainWindow: def setupUi(self, MainWindow): if not MainWindow.objectName(): MainWindow.setObjectName("MainWindow") diff --git a/design/logic/pay.py b/design/logic/pay.py index 3926a8f..8d243c4 100644 --- a/design/logic/pay.py +++ b/design/logic/pay.py @@ -1,9 +1,9 @@ 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): +class Ui_Dialog_Pay: def setupUi(self, Dialog_Pay): if not Dialog_Pay.objectName(): Dialog_Pay.setObjectName("Dialog_Pay") diff --git a/design/logic/progress_dialog.py b/design/logic/progress_dialog.py index e7b5b3e..7d2596a 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): + +class Ui_ProgressDialog: 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..56b8bb8 100644 --- a/design/logic/sale.py +++ b/design/logic/sale.py @@ -15,7 +15,7 @@ ) -class Ui_Dialog_Sale(object): +class Ui_Dialog_Sale: def setupUi(self, Dialog_Sale): if not Dialog_Sale.objectName(): Dialog_Sale.setObjectName("Dialog_Sale") diff --git a/design/logic/slip.py b/design/logic/slip.py index 16b50c0..9e2b262 100644 --- a/design/logic/slip.py +++ b/design/logic/slip.py @@ -3,7 +3,7 @@ from PySide6.QtWidgets import QLabel, QLineEdit, QTabWidget, QTextEdit, QWidget -class Ui_Dialog_Slip(object): +class Ui_Dialog_Slip: def setupUi(self, Dialog_Slip): if not Dialog_Slip.objectName(): Dialog_Slip.setObjectName("Dialog_Slip") diff --git a/main.py b/main.py index c7f54d0..2c8fea7 100644 --- a/main.py +++ b/main.py @@ -4,46 +4,43 @@ import sys from datetime import date, timedelta from pathlib import Path -from typing import Any, Optional, Type +from typing import Any 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, + QDialog, QHBoxLayout, + QMessageBox, QTableWidgetItem, + QWidget, ) -from PySide6.QtWidgets import QDialog, QWidget -from PySide6.QtWidgets import QMessageBox -from sqlalchemy import and_, select, update, desc +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 from design.logic.pay import Ui_Dialog_Pay from design.logic.sale import Ui_Dialog_Sale from design.logic.slip import Ui_Dialog_Slip -from modules import otchet +from modules import otchet, windows from modules import payment_equipment as pq -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_age, calculate_child_price, - calculate_discounted_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, @@ -67,6 +64,7 @@ def handle_uncaught_exception(exc_type, exc_value, exc_traceback): "Неперехваченное исключение" ) + sys.excepthook = handle_uncaught_exception @@ -101,7 +99,7 @@ def starting_the_main_form(self) -> None: windows.info_window( "База данных недоступна", "Не удалось подключиться к серверу базы данных.\n Приложение будет закрыто.", - "" + "", ) sys.exit(1) login: str = self.ui.lineEdit.text() @@ -134,8 +132,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 +169,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 +232,6 @@ def show_main_window(): window.exec_() - class ClientForm(QDialog): """Форма с данными клиента""" @@ -320,7 +326,7 @@ def client_save(self): gender=str(self.ui.comboBox.currentText()), phone=self.ui.lineEdit_4.text(), email=self.ui.lineEdit_5.text(), - privilege=str((self.ui.comboBox_2.currentText())), + privilege=str(self.ui.comboBox_2.currentText()), ) ) session.commit() @@ -396,7 +402,7 @@ def edit_client_in_sale(self) -> None: client_id: str = self.ui.tableWidget.item(row_number, 5).text() # При типизации указываем, что search_client может быть либо экземпляром Client, либо None with Session(system.engine) as session: - search_client: Optional[Client] = ( + search_client: Client | None = ( session.query(Client).filter_by(id=client_id).first() ) # Проверяем, найден ли клиент @@ -508,9 +514,7 @@ def tracking_button_pressing(self, event) -> None: None """ logger.info("Запуск функции tracking_button_pressing") - if event.key() == QtCore.Qt.Key_Delete: - self.deleting_selected_record() - elif event.key() == QtCore.Qt.Key_Backspace: + if event.key() == QtCore.Qt.Key_Delete or event.key() == QtCore.Qt.Key_Backspace: self.deleting_selected_record() def deleting_selected_record(self) -> None: @@ -533,23 +537,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) @@ -775,7 +789,7 @@ def sale_search_clients(self) -> None: query: str = "%" + self.ui.lineEdit.text() + "%" self.ui.tableWidget.setRowCount(0) with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client).filter(Client.last_name.ilike(query)).all() ) for client in search: @@ -791,12 +805,12 @@ def sale_search_clients(self) -> None: elif index == 0: # Поиск по фамилии и имени self.ui.tableWidget.setRowCount(0) - search: list[Type[Client]] = self.ui.lineEdit.text().title() + search: list[type[Client]] = self.ui.lineEdit.text().title() # Разбиваем поисковую фразу на две lst: Any = search.split() if len(lst) == 2: with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client) .filter( and_( @@ -826,7 +840,7 @@ def sale_search_clients(self) -> None: # Поиск инвалидов self.ui.tableWidget.setRowCount(0) with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client) .filter(Client.privilege.ilike("%" + "и")) .all() @@ -845,7 +859,7 @@ def sale_search_clients(self) -> None: # Поиск многодетных self.ui.tableWidget.setRowCount(0) with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client) .filter(Client.privilege.ilike("%" + "м")) .all() @@ -897,11 +911,11 @@ 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: - search_client: Type[Client] = ( + search_client: type[Client] = ( session.query(Client).filter_by(id=res).first() ) # Вычисляем возраст клиента @@ -1141,12 +1155,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 +1184,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 +1208,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 +1249,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 +1268,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 +1315,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 +1328,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 +1512,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 +1575,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 +1610,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 +1631,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 +1670,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 +1747,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 +1780,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 +1795,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 +1824,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 +1850,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 +1863,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 +1898,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 +1942,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 = generating_parts_for_partial_returns( + tickets, partial_return + ) logger.debug(f"Список билетов: {new_tickets}") # 1 - карта, 2 - наличные if sale.payment_type == 1: @@ -1917,7 +1968,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 +1983,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 +1993,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 +2081,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 +2159,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 +2346,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 +2359,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 +2422,7 @@ def open_pay_form(self, txt): # Оплата только для новых продаж if system.sale_status != 0: windows.info_window( - "Внимание", - "Оплата картой доступна только для новых продаж.", - "" + "Внимание", "Оплата картой доступна только для новых продаж.", "" ) return @@ -2371,8 +2430,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 +2446,7 @@ def open_pay_form(self, txt): # Оплата только для новых продаж if system.sale_status != 0: windows.info_window( - "Внимание", - "Оплата наличными доступна только для новых продаж.", - "" + "Внимание", "Оплата наличными доступна только для новых продаж.", "" ) return logger.info("Оплата наличными") @@ -2402,14 +2458,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 +2472,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,10 +2490,12 @@ 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)}") + logger.error(f"Ошибка сохранения тестового платежа: {e!s}") class PayForm(QDialog): @@ -2515,7 +2572,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) @@ -2663,7 +2722,7 @@ def main_search_clients(self) -> None: # Поиск по номеру телефона self.ui.tableWidget.setRowCount(0) with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client).filter(Client.phone.ilike(user_filter)).all() ) for client in search: @@ -2682,7 +2741,7 @@ def main_search_clients(self) -> None: # Поиск по фамилии self.ui.tableWidget.setRowCount(0) with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client) .filter(Client.last_name.ilike(user_filter)) .all() @@ -2706,10 +2765,10 @@ def main_search_clients(self) -> None: # Разбиваем поисковую фразу на две lst: Any = user_filter.split() # Инициализация переменной search пустым списком, чтобы избежать ошибки - search: list[Type[Client]] = [] + search: list[type[Client]] = [] if len(lst) == 2: with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client) .filter( and_( @@ -2741,7 +2800,7 @@ def main_search_clients(self) -> None: # Поиск инвалидов self.ui.tableWidget.setRowCount(0) with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client) .filter(Client.privilege.ilike("%" + "и")) .all() @@ -2762,7 +2821,7 @@ def main_search_clients(self) -> None: # Поиск многодетных self.ui.tableWidget.setRowCount(0) with Session(system.engine) as session: - search: list[Type[Client]] = ( + search: list[type[Client]] = ( session.query(Client) .filter(Client.privilege.ilike("%" + "м")) .all() @@ -2804,7 +2863,7 @@ def main_edit_client(self) -> None: logger.error("Ячейка пуста или не существует.") return with Session(system.engine) as session: - search_client: Type[Client] = ( + search_client: type[Client] = ( session.query(Client).filter_by(id=client_id).first() ) if search_client is None: @@ -2834,7 +2893,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: @@ -2972,7 +3031,7 @@ def main_search_selected_sale(self) -> None: sale.ui.pushButton_7.setEnabled(False) sale.ui.pushButton_8.setEnabled(False) # Если продажа требует повторный возврат по банковскому терминалу - elif sale_status == 3: + elif sale_status == 3 or sale_status == 5: # Кнопка сохранить sale.ui.pushButton_3.setEnabled(False) # Кнопка оплатить @@ -2987,37 +3046,7 @@ def main_search_selected_sale(self) -> None: sale.ui.pushButton_7.setEnabled(False) sale.ui.pushButton_8.setEnabled(False) # Если продажа требует частичный возврат - elif sale_status == 5: - # Кнопка сохранить - sale.ui.pushButton_3.setEnabled(False) - # Кнопка оплатить - sale.ui.pushButton_5.setEnabled(False) - # Кнопка обновить - sale.ui.pushButton_10.setEnabled(False) - # Кнопка возврат - sale.ui.pushButton_6.setEnabled(True) - # Кнопка отмены платежа по банковской карте - sale.ui.pushButton_14.setEnabled(False) - # Кнопки просмотра и печати билетов - sale.ui.pushButton_7.setEnabled(False) - sale.ui.pushButton_8.setEnabled(False) - # Если продажа требует частичный возврат - elif sale_status == 7: - # Кнопка сохранить - sale.ui.pushButton_3.setEnabled(False) - # Кнопка оплатить - sale.ui.pushButton_5.setEnabled(False) - # Кнопка обновить - sale.ui.pushButton_10.setEnabled(False) - # Кнопка возврат - sale.ui.pushButton_6.setEnabled(False) - # Кнопка отмены платежа по банковской карте - sale.ui.pushButton_14.setEnabled(False) - # Кнопки просмотра и печати билетов - sale.ui.pushButton_7.setEnabled(False) - sale.ui.pushButton_8.setEnabled(False) - # Если продажа отменена - elif sale_status == 8: + elif sale_status == 7 or sale_status == 8: # Кнопка сохранить sale.ui.pushButton_3.setEnabled(False) # Кнопка оплатить @@ -3159,16 +3188,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 +3357,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 +3432,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 +3480,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 +3555,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 +3578,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..61938a7 100644 --- a/modules/config.py +++ b/modules/config.py @@ -2,11 +2,10 @@ import os from configparser import ( ConfigParser, - NoSectionError, - NoOptionError, MissingSectionHeaderError, + NoOptionError, + NoSectionError, ) -from typing import List, Optional logger = logging.getLogger(__name__) @@ -18,7 +17,7 @@ def __init__(self, config_file="config.ini"): # Инициализируем список ПК self.pcs = self._get_pc_list() - def _get_pc_list(self) -> List[str]: + def _get_pc_list(self) -> list[str]: """Возвращает список имен ПК из конфигурации""" pc_count = int(self.get("kol")) pc_list = [] @@ -122,7 +121,6 @@ def load_config(self): ) config_data[key] = config.get(section, key) - return config_data except (NoSectionError, NoOptionError) as e: logger.error(f"Ошибка в конфигурационном файле: {e}") @@ -136,6 +134,6 @@ def load_config(self): logger.error(f"Неизвестная ошибка при чтении файла конфигурации: {e}") raise RuntimeError(f"Неизвестная ошибка: {e}") - def get(self, key: str) -> Optional[str]: + def get(self, key: str) -> str | None: value = self.config_data.get(key) return value diff --git a/modules/libfptr10.py b/modules/libfptr10.py index ced442d..b0c9fc1 100644 --- a/modules/libfptr10.py +++ b/modules/libfptr10.py @@ -1,27 +1,25 @@ -# -*- 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 -class IFptr(object): +class IFptr: ( LIBFPTR_PARAM_TEXT, LIBFPTR_PARAM_TEXT_WRAP, @@ -1672,231 +1670,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 +2018,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,77 +2034,149 @@ 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)) + raise ValueError(f"Invalid 'param' value {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))) + raise TypeError(f"Invalid 'param' type {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)) + raise ValueError(f"Invalid 'param' value {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))) + raise TypeError(f"Invalid 'param' type {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)) + raise ValueError(f"Invalid 'param' value {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))) + raise TypeError(f"Invalid 'param' type {type(param)}") def getParamInt(self, paramId): value = self._getInt(self.interface, ctypes.c_int(paramId)) @@ -2059,13 +2192,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 +2215,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 +2244,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..ba46312 100644 --- a/modules/otchet.py +++ b/modules/otchet.py @@ -1,9 +1,9 @@ from datetime import datetime -from typing import Any, Dict, List, Tuple, Union +from typing import Any 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,7 +45,8 @@ def get_ticket_type(age:str) -> str: else: return "взрослый" -def generate_saved_tickets(values: List[tuple]) -> None: + +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,23 +247,57 @@ 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: + +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,7 +436,8 @@ 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: + +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: list[tuple[str, int, int | float]], + sales_return: list[tuple[str, int, int | float, int]], +) -> list[ + tuple[ + str | None, + int | float | None, + int | float | None, + int | float | None, + int | float | None, + int | float | None, + int | float, + ] +]: """ Обрабатывает продажи и возвраты, агрегируя информацию по каждому кассовому аппарату. Функция вычисляет суммы по картам и наличным, а также учитывает возвраты по каждому типу: @@ -462,24 +591,29 @@ 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] if pc_name not in pcs: continue - if sale[1] == type_rm[0] or sale[1] == type_rm[2]: - pcs[pc_name]["card"] += sale[2] - elif sale[1] == type_rm[2]: + if sale[1] == type_rm[0] or sale[1] == type_rm[2] or sale[1] == type_rm[2]: pcs[pc_name]["card"] += sale[2] else: pcs[pc_name]["cash"] += sale[2] @@ -501,33 +635,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 +696,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..49695eb 100644 --- a/modules/payment_equipment.py +++ b/modules/payment_equipment.py @@ -3,7 +3,6 @@ import time from contextlib import contextmanager from datetime import datetime, timedelta -from typing import Optional from modules import windows from modules.config import Config @@ -14,10 +13,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: @@ -27,7 +28,7 @@ fptr = None logger.info("ККТ отключен в конфигурации (available ≠ on)") except Exception as e: - logger.warning(f"Не установлен драйвер ККТ: {str(e)}") + logger.warning(f"Не установлен драйвер ККТ: {e!s}") kkt_available = False fptr = None @@ -111,7 +112,6 @@ 4144, 4145, 4146, - 4146, 4202, 4203, 4208, @@ -164,7 +164,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 @@ -172,7 +172,7 @@ def fptr_connection(device): yield device except Exception as e: - logger.error(f"Ошибка подключения к ККТ: {str(e)}") + logger.error(f"Ошибка подключения к ККТ: {e!s}") yield None finally: @@ -211,18 +211,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 +302,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 +350,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 +366,7 @@ def process_terminal_error(returncode, error_callback=None): ), TERMINAL_OPERATION_CANCELED: ( "Операция отменена", - f"Ошибка возникает тогда, когда карту достают из терминала быстрее, чем пройдет оплата. Необходимо повторить операцию.", + "Ошибка возникает тогда, когда карту достают из терминала быстрее, чем пройдет оплата. Необходимо повторить операцию.", ), TERMINAL_QR_ERROR: ( "Ошибка операции по QR-коду", @@ -377,8 +384,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 +393,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Превышен лимит операций", "Превышен лимит операций. Попробуйте произвести оплату другой картой или обратитесь в банк для выяснения причины.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_BIOMETRIC_ERROR: @@ -394,7 +401,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 +409,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 +417,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 +425,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 +433,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 +441,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Требуется сделать сверку итогов", "Требуется сделать сверку итогов и повторить операцию.", - error_callback + error_callback, ) return 0 if returncode in TERMINAL_KLK: @@ -442,7 +449,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 +457,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 +465,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 +481,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 +489,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Проблема в работе ПИН-пада.", f"Необходимо обратиться в службу поддержки банка. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 # Общие ошибки @@ -499,7 +506,7 @@ def process_terminal_error(returncode, error_callback=None): returncode, "Ошибка терминала", f"Возвращен неизвестный код ошибки. Обратитесь в службу поддержки. Телефон тех.поддержки {TERMINAL_SUPPORT}. Код возврата: {returncode}.", - error_callback + error_callback, ) return 0 @@ -519,7 +526,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 +550,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 +576,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 +631,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 +650,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: @@ -652,13 +668,15 @@ def universal_terminal_operation( return 0, 0 except ValueError as ve: logger.error(f"Ошибка: {ve}") - progress_signal.emit(f"Ошибка: {str(ve)}", 100) + progress_signal.emit(f"Ошибка: {ve!s}", 100) return 0, 0 except Exception as exp: logger.error(f"Неизвестная ошибка при проведении операции: {exp}") if progress_signal is not None: try: - progress_signal.emit(f"Неизвестная ошибка при проведении операции: {str(exp)}", 100) + progress_signal.emit( + f"Неизвестная ошибка при проведении операции: {exp!s}", 100 + ) except RuntimeError: pass return 0, 0 @@ -979,7 +997,8 @@ def print_pinpad_check(count: int = 2): count -= 1 -def get_info(hide: bool = False) -> Optional[int]: + +def get_info(hide: bool = False) -> int | None: """ Запрос информации о ККТ. Запрашивает информацию о кассовом аппарате (модель, наименование и версия ПО). @@ -1014,13 +1033,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 +1072,7 @@ def is_kkt_connected() -> bool: logger.error(f"Ошибка при проверке подключения ККТ: {e}") return False + @logger_wraps() def get_last_document(day: int = 7) -> None: """Запрос информации о последнем чеке в ФН и проверка даты. @@ -1066,7 +1093,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 +1103,9 @@ def get_last_document(day: int = 7) -> None: "Ошибка", "Произошла неизвестная ошибка.", "Попробуйте снова." ) + def get_last_document_datetime() -> datetime: - """ Получает дату и время последнего зарегистрированного чека из фискального накопителя. + """Получает дату и время последнего зарегистрированного чека из фискального накопителя. Возвращаемое значение: datetime: Дата и время последнего документа в фискальном накопителе. @@ -1085,12 +1115,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 +1143,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 +1439,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 +1629,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 +1678,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 +1694,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 +1743,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 +1791,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 +1817,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 +1836,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 +1850,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 +1926,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 +1956,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 +1977,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 +2001,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 +2049,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 +2067,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 @@ -1980,11 +2096,12 @@ def check_open(sale_dict, payment_type, user, type_operation, print_check, bank_ return 1 except Exception as e: - logger.error(f"Критическая ошибка в check_open: {str(e)}") + logger.error(f"Критическая ошибка в check_open: {e!s}") if on_error: - on_error("Ошибка ККТ", f"Критическая ошибка: {str(e)}") + on_error("Ошибка ККТ", f"Критическая ошибка: {e!s}") 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..0c7b82d 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 @@ -24,7 +24,7 @@ class System: def __new__(cls, *args, **kwargs): if not cls._instance: - cls._instance = super(System, cls).__new__(cls, *args, **kwargs) + cls._instance = super().__new__(cls, *args, **kwargs) return cls._instance def __init__(self): @@ -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..15f6a25 100644 --- a/modules/worker.py +++ b/modules/worker.py @@ -1,7 +1,15 @@ 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 +18,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,9 +46,10 @@ 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): - if delay_ms < 0: - delay_ms = 0 + def delayed_progress_update( + self, step_text: str, progress_percent: int, delay_ms: int = DEFAULT_DELAY_MS + ): + delay_ms = max(delay_ms, 0) if delay_ms > 0: # Создаем локальный таймер для обеспечения задержки timer = QTimer(self) @@ -58,9 +70,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 +105,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 +127,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 +138,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 +156,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 +179,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 +189,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 +209,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 +239,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 +249,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 +267,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 +293,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 +311,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 +353,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 +372,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 +383,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 +433,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..8f512a2 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -1,14 +1,16 @@ import unittest -from modules.logger import logger_wraps, logger -from unittest.mock import patch, MagicMock +from unittest.mock import patch + +from modules.logger import logger, logger_wraps + 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") as mock_info: res = test_func() self.assertEqual(res, "success") @@ -16,8 +18,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") as mock_exc: with self.assertRaises(ValueError): test_func() @@ -25,10 +27,11 @@ 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") as mock_info: res = test_func() self.assertEqual(res, "success") -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() diff --git a/tests/test_otchet.py b/tests/test_otchet.py index ec980b6..65a3617 100644 --- a/tests/test_otchet.py +++ b/tests/test_otchet.py @@ -1,36 +1,39 @@ +import sys import unittest -from unittest.mock import patch, MagicMock -from datetime import datetime +from unittest.mock import MagicMock, patch -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 +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 +from modules import otchet + +# isort: split from modules.otchet import ( - get_ticket_type, - generate_ticket_report_table, - safe_int, format_date_range, + generate_ticket_report_table, + get_ticket_type, process_sales_and_returns, - process_ticket_stats + process_ticket_stats, + safe_int, ) + class TestOtchet(unittest.TestCase): def test_get_ticket_type(self): @@ -43,7 +46,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 +68,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 +103,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 +117,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 +164,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..468d91d 100644 --- a/tests/test_payment_equipment.py +++ b/tests/test_payment_equipment.py @@ -1,13 +1,16 @@ +import sys import unittest -from unittest.mock import patch, MagicMock, mock_open +from unittest.mock import MagicMock, mock_open, patch -import sys -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() import types + fake_libfptr10 = types.ModuleType("modules.libfptr10") + + class FakeIFptr: LIBFPTR_PARAM_TEXT_WRAP = 1 LIBFPTR_TW_WORDS = 1 @@ -57,14 +60,18 @@ class FakeIFptr: LIBFPTR_PARAM_RECEIPT_NUMBER = 1 LIBFPTR_RT_CLOSED = 0 LIBFPTR_RT_CLOSE_SHIFT = 1 - - def __init__(self, *args): pass + + def __init__(self, *args): + pass + fake_libfptr10.IFptr = FakeIFptr -sys.modules['modules.libfptr10'] = fake_libfptr10 +sys.modules["modules.libfptr10"] = fake_libfptr10 +# isort: split import modules.payment_equipment as pe + class TestPE(unittest.TestCase): def setUp(self): pe.dev_mode = False @@ -74,7 +81,7 @@ 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: res = pe.smena_info() @@ -91,7 +98,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): @@ -111,7 +118,7 @@ def test_payment(self): with patch("modules.payment_equipment.windows.info_window") as mock_info: 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 @@ -129,18 +136,17 @@ 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("УСПЕШНО") self.assertTrue(res) - 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) @@ -155,7 +161,9 @@ def test_process_terminal_error(self): self.assertEqual(res, 0) def test_terminal_oplata(self): - with patch("modules.payment_equipment.process_terminal_transaction") as mock_ptt: + with patch( + "modules.payment_equipment.process_terminal_transaction" + ) as mock_ptt: mock_ptt.return_value = 1 res = pe.terminal_oplata(100.0) self.assertEqual(res, 1) @@ -163,31 +171,36 @@ def test_terminal_oplata(self): def test_terminal_check_itog(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.check_terminal_file", return_value=True): + 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) - with patch("modules.payment_equipment.check_terminal_file", return_value=True): - with patch("modules.payment_equipment.windows.info_window") as mock_info: - pe.terminal_check_itog_window() - mock_info.assert_called_once() - + with patch( + "modules.payment_equipment.check_terminal_file", return_value=True + ), 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) @@ -196,9 +209,11 @@ def test_terminal_control_lenta(self): self.assertIsNone(res) def test_terminal_print_file(self): - with patch("modules.payment_equipment.read_pinpad_file", return_value="print_data"), \ - patch("modules.payment_equipment.fptr") as mock_fptr, \ - patch("modules.payment_equipment.windows.info_window"): + with patch( + "modules.payment_equipment.read_pinpad_file", return_value="print_data" + ), patch("modules.payment_equipment.fptr") as mock_fptr, patch( + "modules.payment_equipment.windows.info_window" + ): pe.terminal_print_file() mock_fptr.printText.assert_called() @@ -208,7 +223,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() @@ -223,47 +238,66 @@ def test_process_terminal_transaction(self): mock_proc = MagicMock() mock_proc.returncode = pe.TERMINAL_SUCCESS_CODE mock_run.return_value = mock_proc - with patch("modules.payment_equipment.check_terminal_file", return_value=True): - with patch("modules.payment_equipment.terminal_print_file"): - res = pe.process_terminal_transaction("1", 100.0, "Оплата") - self.assertEqual(res, 1) + with patch( + "modules.payment_equipment.check_terminal_file", return_value=True + ), patch("modules.payment_equipment.terminal_print_file"): + res = pe.process_terminal_transaction("1", 100.0, "Оплата") + self.assertEqual(res, 1) def test_universal_terminal_operation(self): - with patch("modules.payment_equipment.process_terminal_transaction") as mock_ptt: + with patch( + "modules.payment_equipment.process_terminal_transaction" + ) as mock_ptt: mock_ptt.return_value = 1 mock_signal = MagicMock() - res = pe.universal_terminal_operation(pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1) + res = pe.universal_terminal_operation( + pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1 + ) self.assertEqual(res, (1, 1)) def test_print_slip_check(self): - with patch("modules.payment_equipment.read_pinpad_file", return_value="slip_data"), \ - patch("modules.payment_equipment.fptr") as mock_fptr, \ - patch("modules.payment_equipment.windows.info_window"): + with patch( + "modules.payment_equipment.read_pinpad_file", return_value="slip_data" + ), patch("modules.payment_equipment.fptr") as mock_fptr, patch( + "modules.payment_equipment.windows.info_window" + ): pe.print_slip_check() mock_fptr.printText.assert_called() def test_print_pinpad_check(self): - with patch("modules.payment_equipment.read_pinpad_file", return_value=["line1", "line2"]), \ - patch("modules.payment_equipment.fptr") as mock_fptr, \ - patch("modules.payment_equipment.windows.info_window"): + with patch( + "modules.payment_equipment.read_pinpad_file", + return_value=["line1", "line2"], + ), patch("modules.payment_equipment.fptr") as mock_fptr, patch( + "modules.payment_equipment.windows.info_window" + ): pe.print_pinpad_check() mock_fptr.printText.assert_called() def test_universal_terminal_operation_refund(self): - with patch("modules.payment_equipment.process_terminal_transaction") as mock_ptt: + with patch( + "modules.payment_equipment.process_terminal_transaction" + ) as mock_ptt: mock_ptt.return_value = 1 mock_signal = MagicMock() - res = pe.universal_terminal_operation(pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=2) + res = pe.universal_terminal_operation( + pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=2 + ) self.assertEqual(res, (1, 1)) def test_universal_terminal_operation_cancel(self): - with patch("modules.payment_equipment.process_terminal_transaction") as mock_ptt: + with patch( + "modules.payment_equipment.process_terminal_transaction" + ) as mock_ptt: mock_ptt.return_value = 1 mock_signal = MagicMock() - res = pe.universal_terminal_operation(pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=3) + res = pe.universal_terminal_operation( + pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=3 + ) self.assertEqual(res, (1, 1)) -if __name__ == '__main__': + +if __name__ == "__main__": unittest.main() def test_kkt_connection_dev_mode(self): @@ -275,20 +309,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 @@ -297,22 +331,27 @@ def test_kkt_connection_success(self): mock_device.close.assert_called_once() def test_run_terminal_command_timeout(self): - with patch("subprocess.Popen") as mock_popen, \ - patch("os.path.isfile", return_value=True): + 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), (b"", b"")] + 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) 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): + 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), - (b"", b"") + 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) @@ -323,13 +362,13 @@ 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")): - res = pe.run_terminal_command("1") - self.assertIsNone(res) + def test_run_terminal_command_exception(self): + with patch("os.path.isfile", return_value=True), patch( + "subprocess.Popen", side_effect=Exception("Exec failed") + ): + res = pe.run_terminal_command("1") + self.assertIsNone(res) def test_check_terminal_file_not_found_word(self): with patch("builtins.open", mock_open(read_data="НЕУСПЕШНО")): @@ -342,7 +381,10 @@ def test_check_terminal_file_file_not_found(self): self.assertFalse(res) def test_check_terminal_file_decode_error(self): - with patch("builtins.open", side_effect=UnicodeDecodeError("codec", b"", 0, 1, "reason")): + with patch( + "builtins.open", + side_effect=UnicodeDecodeError("codec", b"", 0, 1, "reason"), + ): res = pe.check_terminal_file("УСПЕШНО") self.assertFalse(res) @@ -350,14 +392,19 @@ 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): + with patch( + "modules.payment_equipment.check_terminal_file", + side_effect=FileNotFoundError, + ): res = pe.process_success_result() self.assertEqual(res, 0) def test_safe_handle_error(self): - with patch("modules.payment_equipment.handle_error", side_effect=Exception("Error")): + with patch( + "modules.payment_equipment.handle_error", side_effect=Exception("Error") + ): res = pe._safe_handle_error(1, "title", "msg", None) self.assertIsNone(res) @@ -370,66 +417,107 @@ 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"): # Test each error category - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_CARD_BLOCKED[0], None), 0) - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_CARD_LIMIT[0], None), 0) - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_BIOMETRIC_ERROR[0], None), 0) - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_ERROR_PIN_CODE[0], None), 0) - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_NO_CONNECTION_BANK[0], None), 0) - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_HARDWARE_ERROR[0], None), 0) - 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) - + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_CARD_BLOCKED[0], None), 0 + ) + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_CARD_LIMIT[0], None), 0 + ) + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_BIOMETRIC_ERROR[0], None), 0 + ) + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_ERROR_PIN_CODE[0], None), 0 + ) + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_NO_CONNECTION_BANK[0], None), 0 + ) + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_HARDWARE_ERROR[0], None), 0 + ) + 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) def test_process_terminal_error_more_conditions(self): with patch("modules.payment_equipment.handle_error"): - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_COMMAND_ERROR[0], None), 0) - self.assertEqual(pe.process_terminal_error(pe.TERMINAL_PIN_PAD_ERROR[0], None), 0) + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_COMMAND_ERROR[0], None), 0 + ) + self.assertEqual( + pe.process_terminal_error(pe.TERMINAL_PIN_PAD_ERROR[0], None), 0 + ) def test_universal_terminal_operation_unsupported_operation_type(self): mock_signal = MagicMock() - res = pe.universal_terminal_operation(pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=99) + res = pe.universal_terminal_operation( + pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=99 + ) self.assertEqual(res, (0, 0)) 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) + 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) + 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) self.assertEqual(res, (0, 0)) def test_universal_terminal_operation_transaction_fails(self): - with patch("modules.payment_equipment.process_terminal_transaction") as mock_ptt: + with patch( + "modules.payment_equipment.process_terminal_transaction" + ) as mock_ptt: mock_ptt.return_value = 0 mock_signal = MagicMock() - res = pe.universal_terminal_operation(pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1) + res = pe.universal_terminal_operation( + pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1 + ) self.assertEqual(res, (0, 1)) def test_universal_terminal_operation_exceptions(self): mock_signal = MagicMock() - with patch("modules.payment_equipment.process_terminal_transaction", side_effect=ValueError("Test")): - res = pe.universal_terminal_operation(pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1) + with patch( + "modules.payment_equipment.process_terminal_transaction", + side_effect=ValueError("Test"), + ): + res = pe.universal_terminal_operation( + pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1 + ) self.assertEqual(res, (0, 0)) - with patch("modules.payment_equipment.process_terminal_transaction", side_effect=Exception("Test2")): - res = pe.universal_terminal_operation(pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1) + with patch( + "modules.payment_equipment.process_terminal_transaction", + side_effect=Exception("Test2"), + ): + res = pe.universal_terminal_operation( + pe.PAYMENT_ELECTRONIC, 100.0, mock_signal, operation_type=1 + ) self.assertEqual(res, (0, 0)) def test_terminal_check_itog_run_error(self): @@ -437,17 +525,20 @@ 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) - with patch("modules.payment_equipment.check_terminal_file", side_effect=FileNotFoundError): + with patch( + "modules.payment_equipment.check_terminal_file", + side_effect=FileNotFoundError, + ): res = pe.terminal_check_itog() self.assertEqual(res, 0) @@ -457,15 +548,19 @@ 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) - with patch("modules.payment_equipment.read_pinpad_file", side_effect=FileNotFoundError): - with patch("modules.payment_equipment.windows.info_window") as mock_info: - pe.terminal_check_itog_window() - mock_info.assert_called_once() - + with patch( + "modules.payment_equipment.read_pinpad_file", + side_effect=FileNotFoundError, + ), 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) @@ -476,17 +571,26 @@ def test_terminal_check_itog_window_unknown_code(self): def test_terminal_svod_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) - with patch("modules.payment_equipment.print_pinpad_check", side_effect=FileNotFoundError(1, "msg", "filename")): + with patch( + "modules.payment_equipment.print_pinpad_check", + side_effect=FileNotFoundError(1, "msg", "filename"), + ): pe.terminal_svod_check() def test_terminal_control_lenta_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) - with patch("modules.payment_equipment.print_pinpad_check", side_effect=FileNotFoundError(1, "msg", "filename")): + 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) - with patch("modules.payment_equipment.print_pinpad_check", side_effect=FileNotFoundError(1, "msg", "filename")): + with patch( + "modules.payment_equipment.print_pinpad_check", + side_effect=FileNotFoundError(1, "msg", "filename"), + ): pe.terminal_copy_last_check() 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..a58f8c1 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 + 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..34da7a7 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 +from modules import worker -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()