Compare commits
36 Commits
10678bd324
...
418856a116
Author | SHA1 | Date |
---|---|---|
Wilson Gomez | 418856a116 | |
Wilson Gomez | c36e7277d5 | |
Wilson Gomez | 3d6ab678fe | |
Wilson Gomez | 9c8618207b | |
Wilson Gomez | 0d4fecd578 | |
Wilson Gomez | 299528fa97 | |
Wilson Gomez | 5777776a30 | |
Wilson Gomez | 81e1be2211 | |
Wilson Gomez | 0ac16c993e | |
Wilson Gomez | f3b49f5a3f | |
Wilson Gomez | ef61961792 | |
Wilson Gomez | 19d28390e6 | |
Wilson Gomez | 372b706ade | |
Wilson Gomez | cde0f93520 | |
Wilson Gomez | 1180a750ee | |
Wilson Gomez | 42de1961f0 | |
Wilson Gomez | e915d72c13 | |
Wilson Gomez | 30f6bc25f4 | |
Wilson Gomez | 48829a0a10 | |
Wilson Gomez | fa16cab0ca | |
Wilson Gomez | 8fabce1d6a | |
Wilson Gomez | 4f64c01b99 | |
Wilson Gomez | f81774c446 | |
Wilson Gomez | f8ec07c3ce | |
Wilson Gomez | aac950797a | |
Wilson Gomez | d735ffccde | |
Wilson Gomez | e4cbbd8995 | |
Wilson Gomez | e4eb03b4b7 | |
Wilson Gomez | 4ac3de04dd | |
Wilson Gomez | d7b5d1ce58 | |
Wilson Gomez | 554911ca39 | |
Wilson Gomez | e58740f557 | |
Wilson Gomez | 5b6e44c827 | |
Wilson Gomez | 853997bfd5 | |
Wilson Gomez | 543385d0fa | |
Wilson Gomez | c9b2f22b29 |
|
@ -21,6 +21,7 @@
|
|||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
*.log
|
||||
|
||||
package-lock*
|
||||
|
||||
|
|
2
INSTALL
2
INSTALL
|
@ -32,6 +32,8 @@ The following packages must be installed using PIP
|
|||
pip3 install orjson
|
||||
pip3 install escpos
|
||||
pip3 install PySide6
|
||||
pip3 install psutil
|
||||
pip3 install setproctitle
|
||||
|
||||
|
||||
For Windows add:
|
||||
|
|
|
@ -34,6 +34,8 @@ Los siguientes paquetes se deben instalar usando PIP
|
|||
pip3 install orjson
|
||||
pip3 install escpos
|
||||
pip3 install PySide6
|
||||
pip3 install psutil
|
||||
pip3 install setproctitle
|
||||
|
||||
|
||||
Tener en cuenta que algunos paquetes se deben instalar con pip para python3.
|
||||
|
|
|
@ -35,7 +35,7 @@ class StartButtons(QVBoxLayout):
|
|||
grid.rowStretch(1)
|
||||
grid.parent = parent
|
||||
columns = 4
|
||||
rows = 3
|
||||
rows = 4
|
||||
|
||||
values = []
|
||||
values_extend = values.extend
|
||||
|
@ -63,16 +63,20 @@ class StartButtons(QVBoxLayout):
|
|||
values_extend([
|
||||
['button_control_panel', 'PANEL DE CONTROL', 'action_control_panel', 'settings'],
|
||||
['button_reports', 'REPORTES', 'action_reports', 'reports'],
|
||||
['button_historic_sales', 'HISTORIAL', 'action_historic_sales', 'sales_history'],
|
||||
])
|
||||
if parent.environment == 'retail':
|
||||
values_extend([
|
||||
['button_collection', 'RECAUDO', 'action_collection', 'collection']
|
||||
])
|
||||
else:
|
||||
values_extend([
|
||||
['button_historic_sales', 'HISTORIAL', 'action_historic_sales', 'sales_history'],
|
||||
])
|
||||
|
||||
values_extend([
|
||||
['button_help', 'AYUDA', 'action_help', 'help'],
|
||||
['button_product_info', 'INFO. PRODUCTO', 'action_info_product', 'product_info']
|
||||
['button_product_info', 'INFO. PRODUCTO', 'action_info_product', 'product_info'],
|
||||
['button_test_print', 'TEST IMPRESION', 'action_test_print', 'test_print']
|
||||
])
|
||||
|
||||
positions = [(i, j) for i in range(rows) for j in range(columns)]
|
||||
|
@ -156,7 +160,7 @@ class ButtonsFunction(QGridLayout):
|
|||
['button_change_salesman', 'CAMBIO VENDEDOR', 'action_change_salesman'],
|
||||
])
|
||||
pos_user = self.parent.type_pos_user
|
||||
if pos_user in ('order' ,'salesman'):
|
||||
if pos_user in ('order', 'salesman'):
|
||||
self.values.extend([
|
||||
['button_to_draft', 'BORRADOR', 'button_to_draft_pressed'],
|
||||
['button_to_quote', 'COTIZAR', 'button_to_quote_pressed'],
|
||||
|
@ -238,6 +242,15 @@ class ButtonsStacked(QWidget):
|
|||
method='action_payment_term'
|
||||
)
|
||||
|
||||
self.button_transfer = CustomButton(
|
||||
id='button_transfer',
|
||||
parent=parent,
|
||||
title='TRANSFERIR',
|
||||
icon=get_icon('transfer'),
|
||||
name_style='toolbar',
|
||||
method='action_transfer_sale'
|
||||
)
|
||||
|
||||
if pos_user not in ('order', 'salesman'):
|
||||
self.stacked.addWidget(self.button_accept)
|
||||
self.stacked.addWidget(self.button_checkout)
|
||||
|
@ -245,6 +258,8 @@ class ButtonsStacked(QWidget):
|
|||
|
||||
hbox.addWidget(self.stacked, 0)
|
||||
if pos_user in ('cashier', 'frontend_admin'):
|
||||
if parent._hotel_activated:
|
||||
hbox.addWidget(self.button_transfer, 0)
|
||||
hbox.addWidget(self.button_payment_term, 0)
|
||||
if parent.environment == 'retail':
|
||||
if pos_user == 'cashier' and getattr(parent, 'button_to_draft_active', None):
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import os
|
||||
import shutil
|
||||
# from PyQt5.QtCore import QSettings
|
||||
from PySide6.QtCore import QSettings
|
||||
|
||||
|
@ -12,7 +13,8 @@ class Params(object):
|
|||
def __init__(self, file_):
|
||||
self.file = file_
|
||||
|
||||
dirx = os.path.abspath(os.path.join(__file__, '..', '..'))
|
||||
current_path = os.path.abspath(__file__)
|
||||
dirx = os.path.abspath(os.path.join(current_path, '..', '..', '..'))
|
||||
|
||||
if os.name == 'posix':
|
||||
homex = 'HOME'
|
||||
|
@ -24,21 +26,23 @@ class Params(object):
|
|||
HOME_DIR = os.getenv(homex)
|
||||
default_dir = os.path.join(HOME_DIR, dirconfig)
|
||||
|
||||
if os.path.exists(default_dir):
|
||||
config_file = os.path.join(default_dir, self.file)
|
||||
else:
|
||||
config_file = os.path.join(dirx, self.file)
|
||||
if not os.path.exists(default_dir):
|
||||
os.makedirs(default_dir)
|
||||
|
||||
self.config_file = os.path.join(default_dir, self.file)
|
||||
|
||||
if not os.path.exists(config_file):
|
||||
config_file = self.file
|
||||
if not os.path.exists(self.config_file):
|
||||
config_file_path = os.path.join(dirx, self.file)
|
||||
shutil.copy2(config_file_path, self.config_file)
|
||||
|
||||
settings = QSettings(config_file, QSettings.IniFormat)
|
||||
settings = QSettings(self.config_file, QSettings.IniFormat)
|
||||
self.params = {
|
||||
"server": "localhost",
|
||||
"port": "8010",
|
||||
"mode": "http",
|
||||
"database": "DBNAME",
|
||||
"user": None,
|
||||
"sale_device_code": None,
|
||||
"printer_sale_name": None,
|
||||
"profile_printer": "TM-P80",
|
||||
"row_characters": "48",
|
||||
|
@ -50,7 +54,7 @@ class Params(object):
|
|||
"button_to_draft_active": False,
|
||||
"server_printer": False,
|
||||
"active_timeout": True,
|
||||
"tasks_station": True,
|
||||
"tasks_station": False,
|
||||
"timeout": 10000,
|
||||
"tablet_mode": False,
|
||||
"theme": "base",
|
||||
|
|
|
@ -4,18 +4,18 @@ import sys
|
|||
import os
|
||||
import gettext
|
||||
import logging
|
||||
import time
|
||||
import ssl
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
from PySide6.QtWidgets import (QMainWindow, QDialog)
|
||||
from PySide6.QtWidgets import QDialog
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtGui import QPixmap
|
||||
from http.client import HTTPConnection, HTTPSConnection
|
||||
import orjson as json
|
||||
from app.threads import VerifyConn
|
||||
from app.commons.config import Params
|
||||
from app.commons.dialogs import ConfigEditDialog
|
||||
from app.commons.ui_login import Ui_Login
|
||||
from logger_config import logger
|
||||
context_http = ssl._create_unverified_context()
|
||||
|
||||
_ = gettext.gettext
|
||||
|
@ -42,6 +42,7 @@ class Login(QDialog):
|
|||
self.context = {}
|
||||
params = Params(file_config)
|
||||
self.params = params.params
|
||||
self.config_file_path = params.config_file
|
||||
self.setObjectName('dialog_login')
|
||||
self.ui = Ui_Login()
|
||||
self.ui.setupUi(self)
|
||||
|
@ -57,8 +58,7 @@ class Login(QDialog):
|
|||
conn.close()
|
||||
option = u"online"
|
||||
icon_conn = path_circle_green
|
||||
except Exception as e:
|
||||
print(e, 'error')
|
||||
except Exception:
|
||||
icon_conn = path_circle_red
|
||||
option = u"offline"
|
||||
self.ui.label_conn.setText(option)
|
||||
|
@ -135,7 +135,8 @@ class Login(QDialog):
|
|||
msg = 'Error: conexion del servidor'
|
||||
elif result['status'] == 500:
|
||||
msg = 'Error: interno del servidor \n' + result['message']
|
||||
print(msg, 'msg ,,,,', result['status'])
|
||||
logger.error(result)
|
||||
logger.info(msg)
|
||||
self.ui.label_error.setText(msg)
|
||||
self.error_message()
|
||||
else:
|
||||
|
@ -145,6 +146,19 @@ class Login(QDialog):
|
|||
|
||||
def error_message(self):
|
||||
self.ui.label_error.show()
|
||||
|
||||
def open_config_file(self):
|
||||
# config_file_path = "/home/psk/.tryton/config_pos.ini" # Reemplaza con tu ruta real
|
||||
dialog = ConfigEditDialog(self.config_file_path)
|
||||
dialog.exec_()
|
||||
|
||||
def show_about(self):
|
||||
# Aquí debes implementar la lógica para mostrar el diálogo de configuración
|
||||
# Puedes crear una nueva ventana de configuración o un cuadro de diálogo modal.
|
||||
# Por ejemplo:
|
||||
print('pasa a mos')
|
||||
# config_dialog = ConfigDialog(self)
|
||||
# config_dialog.exec_()
|
||||
|
||||
|
||||
def xconnection(mode, user, password, host, database, port):
|
||||
|
|
|
@ -7,19 +7,18 @@ from collections import OrderedDict
|
|||
# )
|
||||
# from PyQt5.QtGui import QStandardItem, QStandardItemModel, QPixmap
|
||||
# from PyQt5.QtCore import Qt, pyqtSlot, QModelIndex
|
||||
from operator import methodcaller
|
||||
from PySide6.QtWidgets import (
|
||||
QDialog, QAbstractItemView, QVBoxLayout, QHBoxLayout, QLabel, QWidget,
|
||||
QTreeView, QLineEdit, QTableView, QCompleter
|
||||
QTreeView, QLineEdit, QTableView, QCompleter, QPushButton, QTextEdit
|
||||
)
|
||||
from PySide6.QtGui import QStandardItem, QStandardItemModel, QPixmap, QGuiApplication
|
||||
from PySide6.QtGui import QStandardItem, QStandardItemModel, QPixmap, QGuiApplication, QFont
|
||||
from PySide6.QtCore import Qt, Slot, QModelIndex
|
||||
|
||||
from .qt_models import get_simple_model
|
||||
from .forms import GridForm
|
||||
from .buttons import ActionButton
|
||||
|
||||
__all__ = ['QuickDialog', 'SearchDialog', 'HelpDialog', 'FactoryIcons']
|
||||
__all__ = ['QuickDialog', 'SearchDialog', 'HelpDialog', 'FactoryIcons', 'ConfigEditDialog']
|
||||
|
||||
current_dir = os.path.dirname(__file__)
|
||||
|
||||
|
@ -132,8 +131,10 @@ class QuickDialog(QDialog):
|
|||
|
||||
if kind in ('info', 'error'):
|
||||
self.show()
|
||||
self.adjustSize()
|
||||
self.setMinimumSize(500, 200)
|
||||
|
||||
def exec_(self, args=None):
|
||||
def exec(self, args=None):
|
||||
res = None
|
||||
res = super(QuickDialog, self).exec()
|
||||
if hasattr(self, 'data_widget') and isinstance(self.data_widget, GridForm):
|
||||
|
@ -179,7 +180,7 @@ class QuickDialog(QDialog):
|
|||
print('cancell quitdialog')
|
||||
try:
|
||||
self.parent.label_input.setFocus()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
self.setResult(0)
|
||||
self.hide()
|
||||
|
@ -380,3 +381,47 @@ class FactoryIcons(object):
|
|||
_icon_label.setAlignment(Qt.AlignCenter | Qt.AlignCenter)
|
||||
_icon_label.setPixmap(_qpixmap_icon.scaledToHeight(48))
|
||||
self.icons[name] = _icon_label
|
||||
|
||||
|
||||
class ConfigEditDialog(QDialog):
|
||||
def __init__(self, config_file_path):
|
||||
super(ConfigEditDialog, self).__init__()
|
||||
|
||||
self.setWindowTitle("Editar Configuración")
|
||||
width, height = get_screen()
|
||||
self.setGeometry(100, 100, int(width*0.8), int(height*0.8))
|
||||
|
||||
self.config_file_path = config_file_path
|
||||
|
||||
self.layout = QVBoxLayout()
|
||||
|
||||
self.text_edit = QTextEdit(self)
|
||||
font = QFont()
|
||||
font.setPointSize(16)
|
||||
self.text_edit.setFont(font)
|
||||
self.layout.addWidget(self.text_edit)
|
||||
|
||||
self.save_button = QPushButton("Guardar")
|
||||
self.save_button.clicked.connect(self.save_config)
|
||||
self.layout.addWidget(self.save_button)
|
||||
|
||||
self.load_config()
|
||||
|
||||
self.setLayout(self.layout)
|
||||
|
||||
def load_config(self):
|
||||
try:
|
||||
with open(self.config_file_path, 'r') as file:
|
||||
config_text = file.read()
|
||||
self.text_edit.setPlainText(config_text)
|
||||
except FileNotFoundError:
|
||||
self.text_edit.setPlainText("Archivo de configuración no encontrado.")
|
||||
|
||||
def save_config(self):
|
||||
config_text = self.text_edit.toPlainText()
|
||||
try:
|
||||
with open(self.config_file_path, 'w') as file:
|
||||
file.write(config_text)
|
||||
except Exception as e:
|
||||
print(f"Error al guardar el archivo de configuración: {str(e)}")
|
||||
|
||||
|
|
|
@ -377,7 +377,7 @@ class ComboBox(QComboBox):
|
|||
selection_model = get_simple_model(obj, values, heads)
|
||||
self.setModel(selection_model)
|
||||
self.setModelColumn(1)
|
||||
selection_model.findItems(str(3), column=0)
|
||||
# selection_model.findItems(str(3), column=0)
|
||||
self.method_on_change = None
|
||||
self.currentIndexChanged.connect(self.on_change)
|
||||
if data.get('default'):
|
||||
|
@ -426,6 +426,23 @@ class ComboBox(QComboBox):
|
|||
idx = model.indexFromItem(items[0])
|
||||
self.setCurrentIndex(idx.row())
|
||||
|
||||
def updateComboBox(self, id_):
|
||||
"only call this function if you don't need require execute onchange"
|
||||
# Block signals temporarily
|
||||
self.blockSignals(True)
|
||||
|
||||
# Set the desired value in the QComboBox
|
||||
if id_:
|
||||
model = self.model()
|
||||
items = model.findItems(str(id_), column=0)
|
||||
idx = model.indexFromItem(items[0])
|
||||
self.setCurrentIndex(idx.row())
|
||||
else:
|
||||
self.setCurrentIndex(-1)
|
||||
|
||||
# Unblock signals after setting the value
|
||||
self.blockSignals(False)
|
||||
|
||||
def set_none(self):
|
||||
self.setCurrentIndex(0)
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ from PySide6.QtCore import (QSize, Qt, QRect)
|
|||
from PySide6.QtGui import (QFont, QIcon, QPixmap)
|
||||
from PySide6.QtWidgets import (QGridLayout, QGroupBox, QLabel,
|
||||
QLayout, QPushButton, QLineEdit, QVBoxLayout, QWidget,
|
||||
QHBoxLayout)
|
||||
QHBoxLayout, QMenuBar)
|
||||
|
||||
from ..version import __version__
|
||||
from ..tools import get_screen
|
||||
|
@ -45,6 +45,7 @@ class Ui_Login(object):
|
|||
MainWindow.setWindowIcon(icon)
|
||||
MainWindow.setWindowOpacity(1.0)
|
||||
MainWindow.setStyleSheet(STYLE)
|
||||
MainWindow.setWindowTitle("Login presik pos")
|
||||
# MainWindow.setStyleSheet(u"margin:0;background-color: rgb(246, 245, 244);")
|
||||
self.centralwidget = QWidget(MainWindow)
|
||||
self.centralwidget.setObjectName(u"centralwidget")
|
||||
|
@ -255,3 +256,16 @@ class Ui_Login(object):
|
|||
self.pushButtonOk.clicked.connect(MainWindow.accept)
|
||||
self.pushButtonCancel.clicked.connect(MainWindow.reject)
|
||||
self.field_password.editingFinished.connect(MainWindow.accept)
|
||||
|
||||
menubar = QMenuBar(self.centralwidget)
|
||||
file_menu = menubar.addMenu("Opciones")
|
||||
|
||||
action_config = file_menu.addAction("Configuracion")
|
||||
action_about = file_menu.addAction("Acerca de")
|
||||
|
||||
# Connect the action to a slot or function for handling the action
|
||||
action_config.triggered.connect(MainWindow.open_config_file)
|
||||
action_about.triggered.connect(MainWindow.show_about)
|
||||
|
||||
self.gridLayout_2.setMenuBar(menubar)
|
||||
|
||||
|
|
|
@ -97,3 +97,17 @@ TYPE_VEHICLE = [
|
|||
('bicycle', 'Bicycle'),
|
||||
('car', 'Car'),
|
||||
]
|
||||
|
||||
TYPE_PRINTER = [
|
||||
('', ''),
|
||||
('usb', 'USB'),
|
||||
('network', 'RED'),
|
||||
]
|
||||
|
||||
ROW_CHARACTERS = [
|
||||
('', ''),
|
||||
('28', '28'),
|
||||
('33', '33'),
|
||||
('42', '42'),
|
||||
('48', '48'),
|
||||
]
|
303
app/dialogs.py
303
app/dialogs.py
|
@ -1,21 +1,24 @@
|
|||
from decimal import Decimal
|
||||
from datetime import datetime
|
||||
from operator import itemgetter, attrgetter
|
||||
from operator import itemgetter
|
||||
from .commons.dialogs import HelpDialog, QuickDialog
|
||||
# from PyQt5.QtCore import Qt, QSize
|
||||
# from PyQt5.QtWidgets import (
|
||||
# QCheckBox, QTextEdit, QVBoxLayout, QGridLayout, QLineEdit, QPlainTextEdit,
|
||||
# QScrollArea, QHBoxLayout, QDoubleSpinBox, QLabel, QMessageBox
|
||||
# )
|
||||
from PySide6.QtCore import Qt, QSize
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QCheckBox, QTextEdit, QVBoxLayout, QGridLayout, QLineEdit, QPlainTextEdit,
|
||||
QScrollArea, QHBoxLayout, QDoubleSpinBox, QLabel, QMessageBox, QWidget
|
||||
QScrollArea, QHBoxLayout, QDoubleSpinBox, QLabel, QWidget, QDialog
|
||||
)
|
||||
|
||||
from .proxy import Report
|
||||
from .buttonpad import ButtonsFunction
|
||||
from .constants import alignCenter, alignLeft, FRACTIONS, TYPE_VEHICLE, MONEY
|
||||
from .constants import (alignCenter, alignLeft, FRACTIONS, TYPE_VEHICLE,
|
||||
MONEY, TYPE_PRINTER, ROW_CHARACTERS)
|
||||
from .constants import (alignCenter, alignLeft, FRACTIONS, TYPE_VEHICLE,
|
||||
MONEY, TYPE_PRINTER, ROW_CHARACTERS)
|
||||
from .commons.forms import FieldMoney, ComboBox, GridForm
|
||||
from .commons.search_window import SearchWindow
|
||||
from collections import OrderedDict
|
||||
|
@ -25,6 +28,8 @@ from app.commons.table import TableView
|
|||
from app.commons.model import TableEdit
|
||||
from .commons.custom_button import CustomButton
|
||||
from .tools import get_icon, get_screen
|
||||
from .reporting import Receipt
|
||||
from .reporting import Receipt
|
||||
|
||||
__all__ = [
|
||||
'ControlPanel', 'SearchSale', 'SearchParty', 'SearchProduct', 'Position',
|
||||
|
@ -37,7 +42,9 @@ __all__ = [
|
|||
'DialogCancelInvoice', 'DialogForceAssign', 'CombineProduct',
|
||||
'DialogReports', 'DialogFixedDiscounts', 'DialogFixedDiscountsManual',
|
||||
'DialogExpenses', 'DialogInfoProduct', 'DialogAdvance',
|
||||
'DialogDeleteProduct', 'DialogCollection'
|
||||
'DialogDeleteProduct', 'DialogCollection', 'DialogTestPrinter',
|
||||
'DialogSaleToTransfer', 'DialogSearchFolioTransfer',
|
||||
'DialogSearchSaleTransfer'
|
||||
]
|
||||
|
||||
WIZARDS = {
|
||||
|
@ -287,8 +294,101 @@ class SearchParty(SearchWindow):
|
|||
# 'street': parent.on_selected_street_party
|
||||
}
|
||||
super(SearchParty, self).__init__(parent, headers, None, methods,
|
||||
filter_column=[], cols_width=[60, 120, 270, 190, 90],
|
||||
title=title, fill=True)
|
||||
filter_column=[], cols_width=[60, 120, 270, 190, 90],
|
||||
title=title, fill=True)
|
||||
|
||||
|
||||
class DialogSaleToTransfer(QuickDialog):
|
||||
def __init__(self, parent):
|
||||
|
||||
buttons_layout_filter = QHBoxLayout()
|
||||
self.pushButtonSale = CustomButton(
|
||||
id='button_search_sale',
|
||||
parent=self,
|
||||
# icon=get_icon('history'),
|
||||
title='VENTA',
|
||||
name_style='start',
|
||||
record='sale',
|
||||
method='show_table_sale',
|
||||
size='small',
|
||||
)
|
||||
buttons_layout_filter.addWidget(self.pushButtonSale)
|
||||
|
||||
if parent._hotel_activated:
|
||||
self.pushButtonFolio = CustomButton(
|
||||
id='button_search_folio',
|
||||
parent=self,
|
||||
# icon=get_icon('history'),
|
||||
title='FOLIO',
|
||||
name_style='start',
|
||||
record='folio',
|
||||
method='show_table_folio',
|
||||
size='small',
|
||||
)
|
||||
buttons_layout_filter.addWidget(self.pushButtonFolio)
|
||||
|
||||
self.table_sale = parent.dialog_search_sale_transfer
|
||||
self.table_folio = parent.dialog_search_folio_transfer
|
||||
|
||||
super(DialogSaleToTransfer, self).__init__(parent, 'form', widgets=[buttons_layout_filter])
|
||||
self.setWindowTitle("TRANSFERIR VENTA")
|
||||
width, height = get_screen()
|
||||
self.setFixedSize(int(width * 0.4), int(height * 0.2))
|
||||
|
||||
def show_table_sale(self, *args):
|
||||
fields = ['party.name', 'number', 'sale_date', 'description']
|
||||
sales = self.parent.Sale.find(
|
||||
[('state', 'in', ['draft', 'quotation'])],
|
||||
fields=fields)
|
||||
self.table_sale.show()
|
||||
self.table_sale.set_from_values(sales)
|
||||
self.close()
|
||||
|
||||
def show_table_folio(self, *args):
|
||||
folios = self.parent.Folio.get_current_folios()
|
||||
self.table_folio.show()
|
||||
self.table_folio.set_from_values(folios)
|
||||
self.close()
|
||||
|
||||
|
||||
class DialogSearchFolioTransfer(SearchWindow):
|
||||
def __init__(self, parent):
|
||||
headers = OrderedDict()
|
||||
headers['id'] = {'desc': 'ID', 'type': 'char'}
|
||||
headers['room.code'] = {'desc': 'HABITACION', 'type': 'char'}
|
||||
headers['main_guest.name'] = {'desc': 'NOMBRE HUESPED', 'type': 'char'}
|
||||
headers['registration_state'] = {'desc': 'ESTADO', 'type': 'char'}
|
||||
headers['arrival_date'] = {'desc': 'FECHA INGRESO', 'type': 'char'}
|
||||
headers['departure_date'] = {'desc': 'FECHA SALIDA', 'type': 'char'}
|
||||
|
||||
title = 'BUSCAR FOLIO'
|
||||
methods = {
|
||||
'on_selected_method': 'on_selected_folio_transfer',
|
||||
# 'on_return_method': 'on_search_folio',
|
||||
}
|
||||
super(DialogSearchFolioTransfer, self).__init__(parent, headers, None, methods,
|
||||
filter_column=[1, 2], cols_width=[10, 220, 670, 220, 220, 220],
|
||||
title=title, fill=True)
|
||||
|
||||
|
||||
class DialogSearchSaleTransfer(SearchWindow):
|
||||
def __init__(self, parent):
|
||||
headers = OrderedDict()
|
||||
headers['id'] = {'desc': 'ID', 'type': 'char'}
|
||||
headers['number'] = {'desc': 'NUMERO', 'type': 'char'}
|
||||
headers['party.name'] = {'desc': 'TERCERO', 'type': 'char'}
|
||||
headers['sale_date'] = {'desc': 'FECHA VENTA', 'type': 'char'}
|
||||
headers['description'] = {'desc': 'DESCRIPCION', 'type': 'char'}
|
||||
|
||||
title = 'BUSCAR VENTA'
|
||||
methods = {
|
||||
'on_selected_method': 'on_selected_sale_transfer',
|
||||
# 'on_return_method': 'on_search_folio',
|
||||
}
|
||||
|
||||
super(DialogSearchSaleTransfer, self).__init__(parent, headers, None, methods,
|
||||
filter_column=[1, 2], cols_width=[10, 220, 670, 220, 220, 220],
|
||||
title=title, fill=True)
|
||||
|
||||
|
||||
class SearchProduct(SearchWindow):
|
||||
|
@ -350,7 +450,7 @@ class SearchProduct(SearchWindow):
|
|||
fields_names = list(headers.keys())
|
||||
try:
|
||||
fields_names.remove('image')
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
self.fields_names = fields_names
|
||||
super(SearchProduct, self).__init__(parent, headers, None, methods,
|
||||
|
@ -594,7 +694,7 @@ class DialogSaleForm(QuickDialog):
|
|||
elements = (
|
||||
'id', 'party.', 'number', 'sale_date',
|
||||
'total_amount_cache', 'invoice_number', 'lines.'
|
||||
)
|
||||
)
|
||||
id, party, number, sale_date, total_amount_cache, invoice_number, lines = itemgetter(*elements)(data)
|
||||
self.sale_customer_selected = id
|
||||
self.field_party.setText(party['name'])
|
||||
|
@ -887,7 +987,7 @@ class DialogTableDeliveryParty(QuickDialog):
|
|||
method_selected_row=parent.delivery_party_selected
|
||||
)
|
||||
width, height = get_screen()
|
||||
table.setFixedSize(int(width/2.2), int(height/2.2))
|
||||
table.setFixedSize(int(width / 2.2), int(height / 2.2))
|
||||
vbox_ = QVBoxLayout()
|
||||
|
||||
grid = QGridLayout()
|
||||
|
@ -923,7 +1023,7 @@ class DialogMoneyCount(QuickDialog):
|
|||
grid = QGridLayout()
|
||||
_sizes = (160, 120, 240)
|
||||
fields = (
|
||||
{'label': 'MONEDA', 'type': 'integer', 'readonly': True},
|
||||
{'label': 'MONEDA', 'type': 'integer', 'readonly': True},
|
||||
{'label': 'CANTIDAD', 'type': 'integer', 'change': 'set_total'},
|
||||
{'label': 'SUBTOTAL', 'type': 'integer', 'readonly': True},
|
||||
)
|
||||
|
@ -963,9 +1063,10 @@ class DialogMoneyCount(QuickDialog):
|
|||
widgets=[grid])
|
||||
self.setWindowTitle('CONTEO DE DINERO')
|
||||
|
||||
def exec(self, kind):
|
||||
def exec_(self, kind):
|
||||
print(kind, 'clase')
|
||||
self.kind = kind
|
||||
self.exec_()
|
||||
self.exec()
|
||||
|
||||
def set_total(self, row):
|
||||
_row = self.model._data[row]
|
||||
|
@ -1029,8 +1130,8 @@ class DialogExpenses(QuickDialog):
|
|||
|
||||
_sizes = (140, 290, 210, 150)
|
||||
fields = (
|
||||
{'name': 'id', 'label': 'ID', 'type': 'integer', 'invisible': True},
|
||||
{'name': 'invoice_number', 'label': 'FACTURA', 'type': 'char'},
|
||||
{'name': 'id', 'label': 'ID', 'type': 'integer', 'invisible': True},
|
||||
{'name': 'invoice_number', 'label': 'FACTURA', 'type': 'char'},
|
||||
{'name': 'description', 'label': 'DESCRIPCION', 'type': 'char'},
|
||||
{'name': 'reference', 'label': 'REFERENCIA', 'type': 'char'},
|
||||
{'name': 'amount', 'label': 'VALOR', 'type': 'float', 'change': 'set_total'},
|
||||
|
@ -1126,7 +1227,7 @@ class DialogTaxes(QuickDialog):
|
|||
'heads': ['ID', 'VALOR'],
|
||||
}
|
||||
string = 'ESCOJA EL IMPUESTO'
|
||||
super(DialogTaxes, self).__init__(parent, 'selection', string, data)
|
||||
super(DialogTaxes, self).__init__(parent, 'selection', string, data)
|
||||
|
||||
|
||||
class DialogSource(QuickDialog):
|
||||
|
@ -1485,7 +1586,7 @@ class DialogComboProduct(QuickDialog):
|
|||
self.label_qty_min = QLabel('CANT. MIN.')
|
||||
self.label_qty_min.setObjectName('label_qty_min')
|
||||
self.hbox.addWidget(self.label_qty_min)
|
||||
|
||||
|
||||
self.label_qty_min_req = QLabel("")
|
||||
self.label_qty_min_req.setObjectName('label_qty_min_req')
|
||||
self.hbox.addWidget(self.label_qty_min_req)
|
||||
|
@ -1550,7 +1651,7 @@ class DialogComboProduct(QuickDialog):
|
|||
if qty_req:
|
||||
if not qty_add or int(qty_req) - int(qty_add) > 0:
|
||||
dialog = self.parent.dialog('qty_combo_min_req', extra_message=f"cantidad minima {qty_req} has agregado {qty_add}")
|
||||
dialog.exec_()
|
||||
dialog.exec()
|
||||
return False
|
||||
return True
|
||||
|
||||
|
@ -1582,12 +1683,12 @@ class DialogSplitSale(QuickDialog):
|
|||
msg = 'DESEA CREAR UN PEDIDO CON LOS PRODUCTOS SELECCIONADOS?'
|
||||
self.label.setText(msg)
|
||||
self.label_number.setText('')
|
||||
return self.exec_()
|
||||
return self.exec()
|
||||
|
||||
def info(self, number):
|
||||
self.label.setText('PEDIDO CREADO EXITOSAMENTE!')
|
||||
self.label_number.setText(number)
|
||||
self.exec_()
|
||||
self.exec()
|
||||
|
||||
|
||||
class Help(HelpDialog):
|
||||
|
@ -1609,7 +1710,8 @@ class Help(HelpDialog):
|
|||
('DOMICILIARIO', 'F12'),
|
||||
('POSICION', 'Insert'),
|
||||
('FACTURAR', 'End'),
|
||||
('COMENTARIO', 'Quotation Marks'),
|
||||
('COMENTARIO', 'COMILLAS DOBLE (")'),
|
||||
('AGENTE', 'PUNTO Y COMA (;)'),
|
||||
)
|
||||
|
||||
self.set_shortcuts(shortcuts)
|
||||
|
@ -1618,7 +1720,6 @@ class Help(HelpDialog):
|
|||
class DialogListProduct(QuickDialog):
|
||||
def __init__(self, parent):
|
||||
self._parent = parent
|
||||
vbox = QVBoxLayout()
|
||||
grid = QGridLayout()
|
||||
label_code = QLabel('CODIGO:')
|
||||
label_code.setObjectName('label_info_product_code')
|
||||
|
@ -1629,6 +1730,127 @@ class DialogListProduct(QuickDialog):
|
|||
grid.addWidget(self.input_code, 1, 2)
|
||||
|
||||
|
||||
class DialogTestPrinter(QuickDialog):
|
||||
|
||||
def __init__(self, parent):
|
||||
self._parent = parent
|
||||
width, height = get_screen()
|
||||
grid = QGridLayout()
|
||||
if parent.environment == 'restaurant':
|
||||
_sizes = (100, 210, 210, 100)
|
||||
fields = (
|
||||
{'name': 'id', 'label': 'ID', 'type': 'integer', 'invisible': True},
|
||||
{'name': 'name', 'label': 'NOMBRE', 'type': 'char', 'readonly': True},
|
||||
{'name': 'interface', 'label': 'INTERFACE', 'type': 'char', 'readonly': True},
|
||||
{'name': 'server', 'label': 'SERVIDOR', 'type': 'char', 'readonly': True},
|
||||
{'name': 'row_characters', 'label': 'CARACTERES POR FILA', 'type': 'integer', 'readonly': True},
|
||||
)
|
||||
self.model = TableEdit(self, [], fields)
|
||||
self.table = TableView('model_printers', self.model, _sizes, self.fill_fields)
|
||||
self.table.hideColumn(0)
|
||||
|
||||
grid.addWidget(self.table, 0, 0, 2, 7)
|
||||
|
||||
label_interface = QLabel('INTERFACE:')
|
||||
label_interface.setObjectName('label_interface')
|
||||
grid.addWidget(label_interface, 3, 1)
|
||||
self.combo_interface = ComboBox(parent, 'type_printer', {'values': TYPE_PRINTER})
|
||||
grid.addWidget(self.combo_interface, 3, 2)
|
||||
|
||||
label_server = QLabel('SERVIDOR:')
|
||||
label_server.setObjectName('label_server')
|
||||
grid.addWidget(label_server, 3, 3)
|
||||
self.input_server = QLineEdit()
|
||||
self.input_server.setObjectName('input_server')
|
||||
grid.addWidget(self.input_server, 3, 4)
|
||||
|
||||
label_row_characters = QLabel('CARACTERES POR FILA:')
|
||||
label_row_characters.setObjectName('label_row_characters')
|
||||
grid.addWidget(label_row_characters, 3, 5)
|
||||
self.combo_row_characters = ComboBox(
|
||||
parent, 'row_characters', {'values': ROW_CHARACTERS}
|
||||
)
|
||||
grid.addWidget(self.combo_row_characters, 3, 6)
|
||||
|
||||
super(DialogTestPrinter, self).__init__(
|
||||
parent, 'action', widgets=[grid]
|
||||
)
|
||||
self.setWindowTitle('TEST IMPRESORA')
|
||||
self.setFixedSize(int(width * 0.4), int(height * 0.3))
|
||||
|
||||
def load(self):
|
||||
printers_shop = self.parent.printers_shop
|
||||
if getattr(self, 'table', None) and printers_shop:
|
||||
model_add = self.model.add_record
|
||||
for k, v in printers_shop.items():
|
||||
data = [
|
||||
k,
|
||||
v.get('name', ''),
|
||||
v['interface'],
|
||||
v['host'],
|
||||
v['row_characters'],
|
||||
]
|
||||
model_add(data)
|
||||
|
||||
def exec_(self):
|
||||
self.clear()
|
||||
self.load()
|
||||
super(DialogTestPrinter, self).exec_()
|
||||
|
||||
def clear(self):
|
||||
self.model.clearData()
|
||||
self.input_server.setText("")
|
||||
self.combo_interface.set_from_id("")
|
||||
self.combo_row_characters.set_from_id("")
|
||||
|
||||
def test_printer(self):
|
||||
server = self.input_server.text()
|
||||
interface = self.combo_interface.get_id()
|
||||
row_characters = self.combo_row_characters.get_id()
|
||||
|
||||
printer_test = {
|
||||
'interface': interface,
|
||||
'device': str(server),
|
||||
'profile': 'TM-P80',
|
||||
'row_characters': row_characters,
|
||||
}
|
||||
|
||||
ctx_printing = {}
|
||||
ctx_printing['company'] = 'OSCORP INC'
|
||||
ctx_printing['sale_device'] = 'CAJA-10'
|
||||
ctx_printing['shop'] = 'Shop Wall Boulevard'
|
||||
ctx_printing['street'] = 'Cll 21 # 172-81. Central Park'
|
||||
ctx_printing['user'] = 'Charles Chapplin'
|
||||
ctx_printing['city'] = 'Dallas'
|
||||
ctx_printing['zip'] = '0876'
|
||||
ctx_printing['phone'] = '591 5513 455'
|
||||
ctx_printing['id_number'] = '123456789-0'
|
||||
ctx_printing['tax_regime'] = 'none'
|
||||
receipt = Receipt(ctx_printing)
|
||||
receipt.config_printer(printer_test)
|
||||
return receipt.test_printer()
|
||||
|
||||
def dialog_accepted(self):
|
||||
result = self.test_printer()
|
||||
if result:
|
||||
msg = 'Impresion Exitosa ✔'
|
||||
else:
|
||||
msg = '''\n\n Test de impresión ha fallado,
|
||||
es posible que haya un problema con la conexión de energía, USB o red.
|
||||
Para solucionar el problema, asegúrese de que la impresora esté conectada
|
||||
correctamente a la fuente de alimentación y que el cable USB o Ethernet
|
||||
esté conectado correctamente a la impresora y
|
||||
al dispositivo desde el que está intentando imprimir.'''
|
||||
self._parent.dialog('test_printer', extra_message=msg)
|
||||
if result:
|
||||
super(DialogTestPrinter, self).dialog_accepted()
|
||||
|
||||
def fill_fields(self, data):
|
||||
self.input_server.setText(data[3])
|
||||
self.combo_interface.set_from_id(data[2])
|
||||
self.combo_row_characters.set_from_id(str(data[4]))
|
||||
|
||||
|
||||
class DialogInfoProduct(QuickDialog):
|
||||
def __init__(self, parent):
|
||||
self._parent = parent
|
||||
|
@ -1695,31 +1917,24 @@ class DialogInfoProduct(QuickDialog):
|
|||
|
||||
def search(self, values=None):
|
||||
self.ok_button.setDefault(False)
|
||||
filter = self.input_filter.text()
|
||||
if not filter:
|
||||
filter_ = self.input_filter.text()
|
||||
if not filter_:
|
||||
return
|
||||
|
||||
domain = [
|
||||
('template.salable', '=', True),
|
||||
('template.account_category', '!=', None),
|
||||
]
|
||||
domain.append([
|
||||
domain_or = [
|
||||
'OR',
|
||||
('barcode', 'ilike', '%' + filter + '%'),
|
||||
('code', 'ilike', '%' + filter + '%'),
|
||||
('name', 'ilike', '%' + filter + '%')
|
||||
])
|
||||
('code', 'ilike', '%' + filter_ + '%'),
|
||||
('name', 'ilike', '%' + filter_ + '%')
|
||||
]
|
||||
|
||||
# if self._parent.cache_local:
|
||||
# clause = [
|
||||
# 'OR',
|
||||
# ('barcode', 'ilike', '%{:}%'.format(filter)),
|
||||
# ('code', 'ilike', '%{:}%'.format(filter)),
|
||||
# ('code', 'ilike', '%{:}%'.format(filter)),
|
||||
# ]
|
||||
# domain = [clause]
|
||||
# products = self._parent.local_db.find_product_elastic(domain, limit=100)
|
||||
# else:
|
||||
if self.parent._onebarcode_activated:
|
||||
domain_or.append(('barcode', 'ilike', '%' + filter_ + '%'))
|
||||
|
||||
domain.append(domain_or)
|
||||
products = self._parent.Product.find(domain, ctx=self._parent.stock_context,
|
||||
fields=['name', 'code', 'description',
|
||||
'id', 'list_price',
|
||||
|
@ -1806,7 +2021,7 @@ class DialogCollection(QuickDialog):
|
|||
self.input_filter.setObjectName('input_collection_filter')
|
||||
self.input_filter.editingFinished.connect(lambda: self.search())
|
||||
self.input_filter.returnPressed.connect(lambda: self.search())
|
||||
|
||||
|
||||
grid.addWidget(self.input_filter, 1, 2)
|
||||
|
||||
label_id_number = QLabel('DOCUMENTO:')
|
||||
|
@ -1836,8 +2051,8 @@ class DialogCollection(QuickDialog):
|
|||
label_payment_method.setObjectName('label_collection_payment_method')
|
||||
grid.addWidget(label_payment_method, 5, 1)
|
||||
self.combobox_payment_method = ComboBox(
|
||||
parent, 'payment_method_collection', {'values': self.get_payment_methods()}
|
||||
)
|
||||
parent, 'payment_method_collection', {'values': self.get_payment_methods()}
|
||||
)
|
||||
grid.addWidget(self.combobox_payment_method, 5, 2)
|
||||
|
||||
label_voucher_number = QLabel('NUMERO COMPROBANTE:')
|
||||
|
@ -1862,8 +2077,8 @@ class DialogCollection(QuickDialog):
|
|||
|
||||
def get_payment_methods(self):
|
||||
domain = [
|
||||
["sale_device", "=", self._parent.device['id']],
|
||||
["state", "=", "draft"]
|
||||
["sale_device", "=", self._parent.device['id']],
|
||||
["state", "=", "draft"]
|
||||
]
|
||||
statements = self._parent.Statement.find(domain,
|
||||
fields=['rec_name', 'journal.require_voucher'])
|
||||
|
|
|
@ -1,17 +1,13 @@
|
|||
import os
|
||||
import time
|
||||
from datetime import date
|
||||
import logging
|
||||
import os
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
# from PyQt5.QtWidgets import QMainWindow, QDesktopWidget, QLineEdit
|
||||
# from PyQt5.QtCore import QTimer, QThread, pyqtSignal
|
||||
from PySide6.QtWidgets import QMainWindow, QLineEdit
|
||||
from PySide6.QtCore import QTimer, QThread, Signal
|
||||
|
||||
from PySide6.QtGui import QGuiApplication
|
||||
from PySide6.QtWidgets import QLineEdit, QMainWindow
|
||||
|
||||
from .commons.dialogs import QuickDialog
|
||||
# from .commons.dblogin import safe_reconnect
|
||||
from .proxy import Model, logout
|
||||
from .constants import SCREENS
|
||||
from .constants import DIALOG_REPLY_YES, SCREENS
|
||||
from .dialogs import (
|
||||
Help, ControlPanel, SearchSale, SearchParty, SearchProduct, Position,
|
||||
Comment, DialogPayment, DialogVoucher, DialogPrintInvoice, ProductEdit,
|
||||
|
@ -23,17 +19,19 @@ from .dialogs import (
|
|||
DialogDeliveryParty, TipAmount, DeliveryAmount, DialogSalesmanCode,
|
||||
DialogFixedDiscounts, DialogFixedDiscountsManual, DialogComboProduct,
|
||||
DialogSplitSale, DialogExpenses, DialogInfoProduct, DialogAdvance,
|
||||
DialogDeleteProduct, DialogCollection
|
||||
DialogDeleteProduct, DialogCollection, DialogTestPrinter,
|
||||
DialogSaleToTransfer, DialogSearchFolioTransfer,
|
||||
DialogSearchSaleTransfer
|
||||
)
|
||||
from .constants import DIALOG_REPLY_YES
|
||||
from .proxy import Model, logout
|
||||
from .version import __version__
|
||||
|
||||
|
||||
__all__ = ['FrontWindow', 'ClearUi']
|
||||
__all__ = ['FrontWindow']
|
||||
parent = Path(__file__).parent
|
||||
|
||||
file_base_css = os.path.join(str(parent), 'css', 'base.css')
|
||||
_DEFAULT_TIMEOUT = 60000 # on ms (100 minutes)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class FrontWindow(QMainWindow):
|
||||
|
@ -50,7 +48,6 @@ class FrontWindow(QMainWindow):
|
|||
# self.conn = connection
|
||||
self.version = __version__
|
||||
self.set_params(params)
|
||||
self.logger = logging.getLogger('app_logger')
|
||||
|
||||
"""
|
||||
We need get the size of screen (display)
|
||||
|
@ -170,8 +167,13 @@ class FrontWindow(QMainWindow):
|
|||
self.dialog_split_sale = DialogSplitSale(self)
|
||||
self.dialog_expenses = DialogExpenses(self)
|
||||
self.dialog_info_product = DialogInfoProduct(self)
|
||||
self.dialog_test_printer = DialogTestPrinter(self)
|
||||
if self._commission_activated:
|
||||
self.dialog_agent = DialogAgent(self)
|
||||
if self._hotel_activated:
|
||||
self.dialog_search_folio_transfer = DialogSearchFolioTransfer(self)
|
||||
self.dialog_search_sale_transfer = DialogSearchSaleTransfer(self)
|
||||
self.dialog_sale_to_transfer = DialogSaleToTransfer(self)
|
||||
if self.environment == 'restaurant' and self._sale_pos_restaurant:
|
||||
self.dialog_combine_product = CombineProduct(self)
|
||||
self.dialog_combo_product = DialogComboProduct(self)
|
||||
|
@ -199,9 +201,16 @@ class FrontWindow(QMainWindow):
|
|||
|
||||
def load_modules(self):
|
||||
self._sale_pos_restaurant = None
|
||||
time1 = time.time()
|
||||
self.Module = Model('ir.module', self.ctx, main_window=self)
|
||||
print(time.time() - time1, 'final')
|
||||
module_names = [
|
||||
'sale_pos_frontend_rest', 'sale_pos',
|
||||
'sale_pos_frontend', 'account_credit_limit',
|
||||
'product_onebarcode', 'hotel']
|
||||
modules = self.Module.find([
|
||||
('name', 'in', module_names),
|
||||
('state', '=', 'activated'),
|
||||
])
|
||||
self.modules = {m['name']: m for m in modules}
|
||||
self.Config = Model('sale.configuration', self.ctx, main_window=self)
|
||||
self.Sale = Model('sale.sale', self.ctx, main_window=self)
|
||||
self._config, = self.Config.find([('id', '=', 1)])
|
||||
|
@ -228,45 +237,40 @@ class FrontWindow(QMainWindow):
|
|||
self.sale_automatic = True
|
||||
res = self.Sale.fields_get(['commission'])
|
||||
self._commission_activated = True if res.get('commission') else False
|
||||
self._credit_limit_activated = self.Module.find([
|
||||
('name', '=', 'account_credit_limit'),
|
||||
('state', '=', 'activated'),
|
||||
])
|
||||
|
||||
# _product = {
|
||||
# 'name': 'product.product',
|
||||
# 'fields': [
|
||||
# 'template.name', 'code', 'write_date',
|
||||
# 'description', 'template.sale_price_w_tax',
|
||||
# 'template.account_category'
|
||||
# ]
|
||||
# }
|
||||
# # self.cache_local = self._config.get('cache_products_local')
|
||||
|
||||
# if self._config['show_location_pos']:
|
||||
# _product['fields'].append('location_')
|
||||
|
||||
# if self._config['show_stock_pos'] in ('value', 'icon'):
|
||||
# if self._config['show_stock_pos'] == 'value':
|
||||
# _product['fields'].append('quantity')
|
||||
# if self._config['show_brand']:
|
||||
# _product['fields'].append('template.brand.name')
|
||||
|
||||
# if self._config['encoded_sale_price']:
|
||||
# _product['fields'].extend(
|
||||
# ['image', 'image_icon', 'encoded_sale_price'])
|
||||
self._credit_limit_activated = 'account_credit_limit' in self.modules
|
||||
self._onebarcode_activated = 'product_onebarcode' in self.modules
|
||||
self._hotel_activated = 'hotel' in self.modules
|
||||
|
||||
self.User = Model('res.user', self.ctx, main_window=self)
|
||||
self._user, = self.User.find([('login', '=', self.user)])
|
||||
self.Company = Model('company.company', self.ctx, main_window=self)
|
||||
self.Device = Model('sale.device', self.ctx, main_window=self)
|
||||
self._company, = self.Company.find([('id', '=', 1)])
|
||||
self.logo = self._company['logo']
|
||||
|
||||
sale_device = self._user['sale_device']
|
||||
if not sale_device:
|
||||
if not sale_device and not self.sale_device_code:
|
||||
return 'user_not_permissions_device'
|
||||
|
||||
if self.sale_device_code:
|
||||
dom_device = ('code', '=', self.sale_device_code)
|
||||
else:
|
||||
dom_device = ('id', '=', sale_device)
|
||||
|
||||
devices = self.Device.find([dom_device])
|
||||
|
||||
if not devices:
|
||||
return 'user_not_permissions_device'
|
||||
|
||||
self.device = devices[0]
|
||||
|
||||
shop_id = self.device['shop.']['id']
|
||||
if shop_id not in self._user['shops']:
|
||||
return 'user_not_permissions_device'
|
||||
|
||||
self.sale_device = self.device['id']
|
||||
self.ctx['user'] = self._user['id']
|
||||
self.ctx['sale_device'] = sale_device
|
||||
self.ctx['sale_device'] = self.device['id']
|
||||
self.ctx['shop'] = self._user['shop']
|
||||
|
||||
self.SaleLine = Model('sale.line', self.ctx, main_window=self)
|
||||
|
@ -277,7 +281,6 @@ class FrontWindow(QMainWindow):
|
|||
self.Employee = Model('company.employee', self.ctx, main_window=self)
|
||||
self.Shop = Model('sale.delivery_party', self.ctx, main_window=self)
|
||||
self.SaleDiscont = Model('sale.discount', self.ctx, main_window=self)
|
||||
self.Device = Model('sale.device', self.ctx, main_window=self)
|
||||
self.Category = Model('product.category', self.ctx, main_window=self)
|
||||
self.PaymentTerm = Model('account.invoice.payment_term', self.ctx, main_window=self)
|
||||
self.Party = Model('party.party', self.ctx, main_window=self)
|
||||
|
@ -291,15 +294,12 @@ class FrontWindow(QMainWindow):
|
|||
self.Agent = Model('commission.agent', self.ctx, main_window=self)
|
||||
self.Comission = Model('commission', self.ctx, main_window=self)
|
||||
|
||||
self.device, = self.Device.find([
|
||||
('id', '=', self._user['sale_device']),
|
||||
])
|
||||
|
||||
self.shop = self.device['shop.']
|
||||
self.shop_taxes = self.shop['taxes.']
|
||||
self.company = self.shop['company']
|
||||
self._journals = self.device['journals.']
|
||||
self.salesman_ids = [s['id'] for s in self.shop.get('salesmans', [])]
|
||||
self.sellers = {s['code']: s for s in self.shop.get('salesmans.', [])}
|
||||
|
||||
# dom_salesman = [
|
||||
# ('company', '=', self.company),
|
||||
|
@ -340,20 +340,12 @@ class FrontWindow(QMainWindow):
|
|||
self._password_admin = self._config.get('password_admin_pos')
|
||||
self._password_force_assign = self._config.get('password_force_assign')
|
||||
if self.environment == 'restaurant':
|
||||
self._sale_pos_restaurant = self.Module.find([
|
||||
('name', '=', 'sale_pos_frontend_rest'),
|
||||
('state', '=', 'activated'),
|
||||
])
|
||||
self._sale_pos_restaurant = 'sale_pos_frontend_rest' in self.modules
|
||||
if self._sale_pos_restaurant:
|
||||
self.RestTables = Model('sale.shop.table', self.ctx, main_window=self)
|
||||
self.Consumer = Model('party.consumer', self.ctx, main_window=self)
|
||||
# TODO get product and printers
|
||||
self.printers_shop, self.products_printers = None, None
|
||||
try:
|
||||
self.printers_shop, self.products_printers = self.Sale.get_product_printers(
|
||||
self.shop['id'])
|
||||
except Exception:
|
||||
pass
|
||||
self.printers_shop, self.products_printers = self.Sale.get_product_printers(
|
||||
args=[self.shop['id']])
|
||||
|
||||
self._action_report_invoice, = self.ActionReport.find([
|
||||
('report_name', '=', 'account.invoice'),
|
||||
|
@ -370,21 +362,9 @@ class FrontWindow(QMainWindow):
|
|||
'stock_date_end': date.today().strftime("%Y-%m-%d"),
|
||||
'locations': [self.shop['warehouse']],
|
||||
}
|
||||
if self._hotel_activated:
|
||||
self.Folio = Model('hotel.folio', self.ctx, main_window=self)
|
||||
|
||||
self.Source = Model('sale.source', self.ctx, main_window=self)
|
||||
self.Pricelist = Model('product.price_list', self.ctx, main_window=self)
|
||||
return True
|
||||
|
||||
|
||||
class ClearUi(QThread):
|
||||
# sigActionClear = pyqtSignal()
|
||||
sigActionClear = Signal()
|
||||
state = None
|
||||
|
||||
def __init__(self, wait_time):
|
||||
QThread.__init__(self)
|
||||
self.wait_time = wait_time
|
||||
|
||||
def run(self):
|
||||
time.sleep(self.wait_time)
|
||||
self.sigActionClear.emit()
|
||||
|
|
470
app/main.py
470
app/main.py
|
@ -1,22 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: UTF-8 -*-
|
||||
import sys
|
||||
import os
|
||||
import logging
|
||||
import base64
|
||||
import time
|
||||
import traceback
|
||||
import threading
|
||||
from decimal import Decimal
|
||||
from datetime import datetime, timedelta, date
|
||||
from datetime import datetime, timedelta
|
||||
from collections import OrderedDict
|
||||
from PySide6 import QtGui
|
||||
from PySide6.QtCore import Qt, QTimer
|
||||
from PySide6.QtCore import Qt
|
||||
from PySide6.QtWidgets import (
|
||||
QLabel, QHBoxLayout, QVBoxLayout,
|
||||
QWidget)
|
||||
from app.commons.image import Image
|
||||
from .frontwindow import FrontWindow
|
||||
from .tools import get_icon, to_float, to_numeric
|
||||
from .tools import get_icon, to_float, to_numeric, compare_versions
|
||||
from .status_bar import StatusBar
|
||||
from .stack_messages import StackMessages
|
||||
# from app.commons.action import Action
|
||||
|
@ -31,10 +31,10 @@ from .proxy import Report
|
|||
from .buttonpad import ButtonsStacked, ButtonsFunction, StartButtons
|
||||
from .states import STATES, RE_SIGN
|
||||
from .commons.custom_button import CustomButton
|
||||
from .threads import DoInvoice, VerifyOrderCommand
|
||||
from .threads import DoInvoice
|
||||
from .store import StoreView
|
||||
from .constants import (
|
||||
PATH_PRINTERS, DELTA_LOCALE, STRETCH, alignRight, alignLeft, alignCenter,
|
||||
DELTA_LOCALE, STRETCH, alignRight, alignLeft, alignCenter,
|
||||
alignHCenter, DIALOG_REPLY_NO, ZERO, RATE_CREDIT_LIMIT, CONVERSION_DIGITS,
|
||||
SALE_FIELDS, KIND_REST, KIND_RETAIL, DIALOG_REPLY_YES,
|
||||
)
|
||||
|
@ -51,6 +51,8 @@ _SALE_HISTORIC = [
|
|||
'lines.note'
|
||||
]
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class AppWindow(FrontWindow):
|
||||
|
||||
|
@ -61,7 +63,6 @@ class AppWindow(FrontWindow):
|
|||
super(AppWindow, self).__init__(context, params, title)
|
||||
self.payment_ctx = {}
|
||||
self.set_keys()
|
||||
time1 = time.time()
|
||||
StackMessages(self)
|
||||
self.stock_context = None
|
||||
self.sale_id = None
|
||||
|
@ -76,19 +77,18 @@ class AppWindow(FrontWindow):
|
|||
return
|
||||
self.setup_sale_line()
|
||||
self.setup_delivery_party()
|
||||
self.setup_sale_consumer()
|
||||
if self.environment == 'restaurant':
|
||||
self.setup_sale_consumer()
|
||||
self.setup_payment()
|
||||
# self.set_domains()
|
||||
print(time.time() - time1, 'set domains')
|
||||
|
||||
self.create_gui()
|
||||
print(time.time() - time1, 'create gui')
|
||||
self.message_bar.load_stack(self.stack_msg)
|
||||
self.journal = {}
|
||||
self.active_usb_printers = []
|
||||
|
||||
if os.name == 'posix' and os.path.exists(PATH_PRINTERS):
|
||||
self.set_printers_usb(PATH_PRINTERS)
|
||||
# if os.name == 'posix' and os.path.exists(PATH_PRINTERS):
|
||||
# self.set_printers_usb(PATH_PRINTERS)
|
||||
|
||||
self.set_printing_context()
|
||||
self.statusbar = StatusBar(self)
|
||||
|
@ -99,31 +99,27 @@ class AppWindow(FrontWindow):
|
|||
# self._input_text = ''
|
||||
# self._sign = None
|
||||
self.create_dialogs()
|
||||
print(time.time() - time1, 'create dialogs')
|
||||
self.store = StoreView(self, SALE_FIELDS)
|
||||
if self.active_weighing:
|
||||
from .electronic_scale import ScaleReader
|
||||
self.reader_thread = ScaleReader()
|
||||
self.reader_thread.sigSetWeight.connect(self.set_weight_readed)
|
||||
|
||||
if self.server_printer:
|
||||
self.verify_command_order_th = VerifyOrderCommand(self)
|
||||
self.verify_command_order_th.sigVerifyOrderCommand.connect(self.verify_print_order_automatic)
|
||||
self.verify_command_order_th.start()
|
||||
if self.server_printer and self.environment == 'restaurant':
|
||||
thread_secondary = threading.Thread(target=self.verify_print_order_automatic)
|
||||
thread_secondary.daemon = True
|
||||
thread_secondary.start()
|
||||
# self.timer = QTimer()
|
||||
# self.timer.singleShot(30000, self.verify_print_order_automatic)
|
||||
|
||||
self.do_invoice = DoInvoice(self, self.ctx)
|
||||
self.do_invoice.sigDoInvoice.connect(self.__do_invoice_thread)
|
||||
self.set_state('disabled')
|
||||
self.change_view_to('start_front')
|
||||
|
||||
# if self.cache_local:
|
||||
# self.set_cache_company()
|
||||
# self.set_cache_products()
|
||||
matching_journal = next((j for j in self._journals if j['id'] == self.default_journal['id']), None)
|
||||
if matching_journal:
|
||||
self.default_journal = matching_journal
|
||||
time6 = time.time()
|
||||
print(time6 - time1, 'load final')
|
||||
|
||||
def get_defaults_variables(self):
|
||||
self._clear_invoice_number = False
|
||||
|
@ -136,59 +132,46 @@ class AppWindow(FrontWindow):
|
|||
self.field_agent = None
|
||||
|
||||
def verify_print_order_automatic(self):
|
||||
records = self.Sale.get_orders_to_command({'shop': self.shop['id']})
|
||||
for record in records:
|
||||
orders = record['orders']
|
||||
tasks = record['tasks']
|
||||
try:
|
||||
result = self.receipt_order.print_orders(orders.values())
|
||||
if result:
|
||||
self.Sale.mark_commanded({'sale_id': record['id'], 'lines_ids': result})
|
||||
receipt = Receipt(context={}, environment='restaurant')
|
||||
receipt.print_tasks(tasks)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_exc()
|
||||
while True:
|
||||
print('ingresa a validar comand')
|
||||
args = {'shop': self.shop['id']}
|
||||
orders = []
|
||||
tasks = []
|
||||
version = compare_versions(self.modules['sale_pos_frontend_rest']['version'], '6.0.7')
|
||||
if version in ('equal', 'higher'):
|
||||
if self.tasks_station:
|
||||
result = self.SaleLine.get_data_command_and_task(args)
|
||||
if isinstance(result, dict):
|
||||
orders = result.get('orders', [])
|
||||
tasks = result.get('tasks', [])
|
||||
else:
|
||||
orders = self.SaleLine.get_data_command(args)
|
||||
# TODO: for remove this option
|
||||
else:
|
||||
logger.warning('The backend sale_pos_frontend_rest is deprecation, update version')
|
||||
records = self.Sale.get_orders_to_command(args)
|
||||
for record in records:
|
||||
orders += record['orders'].values()
|
||||
tasks += record['tasks'].values()
|
||||
|
||||
timer = QTimer()
|
||||
timer.timeout.connect(self.verify_print_order_automatic)
|
||||
timer.start(30000)
|
||||
self.verify_command_order_th.exit(0)
|
||||
result = self.receipt_order.print_orders(orders)
|
||||
if result:
|
||||
status = 'error_network'
|
||||
while status == 'error_network':
|
||||
status = self.Sale.mark_commanded({'lines_ids': result})
|
||||
time.sleep(1)
|
||||
|
||||
# def set_domains(self):
|
||||
# self.domain_search_product = [
|
||||
# ('code', '!=', None),
|
||||
# ('active', '=', True),
|
||||
# ('template.salable', '=', True),
|
||||
# ('template.account_category', '!=', None),
|
||||
# ]
|
||||
|
||||
# if self.shop['product_categories.']:
|
||||
# self.domain_search_product.append(
|
||||
# ('account_category', 'in', self.shop['product_categories.'])
|
||||
# )
|
||||
|
||||
# def set_cache_company(self):
|
||||
# self.local_db = LocalDB()
|
||||
# self.local_db.create_table_config()
|
||||
# self._local_config = self.local_db.get_config()
|
||||
# if not self._local_config:
|
||||
# company_id = self.company
|
||||
# self._local_config = self.local_db.set_config([company_id])
|
||||
|
||||
# def set_cache_products(self):
|
||||
# self.local_db.create_table_product()
|
||||
# local_products = self.local_db.get_local_products()
|
||||
|
||||
# config = self.local_db.get_config()
|
||||
# _sync_date = config[1]
|
||||
# products = self.Product.sync_get_products({
|
||||
# 'write_date': _sync_date,
|
||||
# 'shop_id': self.ctx['shop']
|
||||
# })
|
||||
# self.local_db.update_products(products, local_products)
|
||||
# now = datetime.now()
|
||||
# self.local_db.set_config_sync(str(now))
|
||||
receipt = Receipt(context={}, environment='restaurant')
|
||||
result_task = receipt.print_tasks(tasks)
|
||||
if any(result_task):
|
||||
status = 'error_network'
|
||||
while status == 'error_network':
|
||||
status = self.SaleLine.mark_tasks_printed({'task_ids': result_task})
|
||||
time.sleep(1)
|
||||
time.sleep(30)
|
||||
# timer = QTimer()
|
||||
# timer.singleShot(30000, self.verify_print_order_automatic)
|
||||
# self.verify_command_order_th.exit(0)
|
||||
|
||||
def set_printers_usb(self, PATH_PRINTERS):
|
||||
for usb_dev in os.listdir(PATH_PRINTERS):
|
||||
|
@ -227,8 +210,9 @@ class AppWindow(FrontWindow):
|
|||
def set_printing_context(self):
|
||||
|
||||
ctx_printing = self.Sale.get_printing_context(
|
||||
{'device_id': self.device['id']})
|
||||
args=[{'device_id': self.device['id']}])
|
||||
ctx_printing['row_characters'] = self.row_characters
|
||||
ctx_printing['params'] = self.ctx['params']
|
||||
ctx_printing['delta_locale'] = DELTA_LOCALE
|
||||
locale_logo = ''
|
||||
if self.logo:
|
||||
|
@ -250,16 +234,16 @@ class AppWindow(FrontWindow):
|
|||
self.set_default_printer()
|
||||
|
||||
def set_default_printer(self, printer=None):
|
||||
if self.active_usb_printers:
|
||||
self.printer_sale_name = self.active_usb_printers[0]
|
||||
if not printer and self.printer_sale_name:
|
||||
printer = {
|
||||
'interface': self.printer_sale_name[0],
|
||||
'device': self.printer_sale_name[1],
|
||||
'profile': self.profile_printer,
|
||||
}
|
||||
if printer:
|
||||
self.receipt_sale.config_printer(printer)
|
||||
# if self.active_usb_printers:
|
||||
# self.printer_sale_name = self.active_usb_printers[0]
|
||||
# if not printer and self.printer_sale_name:
|
||||
# printer = {
|
||||
# 'interface': self.printer_sale_name[0],
|
||||
# 'device': self.printer_sale_name[1],
|
||||
# 'profile': self.profile_printer,
|
||||
# }
|
||||
# if printer:
|
||||
self.receipt_sale.config_printer(printer)
|
||||
|
||||
def button_new_sale_pressed(self):
|
||||
self.create_new_sale()
|
||||
|
@ -720,13 +704,10 @@ class AppWindow(FrontWindow):
|
|||
|
||||
if self.environment == 'restaurant':
|
||||
self.panel_right_box = QWidget()
|
||||
time3 = time.time()
|
||||
# values = self.get_product_by_categories_dash()
|
||||
values = self.product_categories
|
||||
|
||||
print(time.time() - time3, 'load products categories dash')
|
||||
self.menu_dash = MenuDash(self, values, 'on_selected_item')
|
||||
print('final dash')
|
||||
self.order_front.hide()
|
||||
vbox_order_front.addLayout(self.buttons_function, 0)
|
||||
vbox_order_front.addLayout(self.grid_info, 0)
|
||||
|
@ -876,8 +857,8 @@ class AppWindow(FrontWindow):
|
|||
if hasattr(self, 'field_list_price') and self._config.get('use_price_list'):
|
||||
ctx = {'price_list': self.field_list_price.get_id()}
|
||||
rec = self.SaleLine.faster_set_quantity(args, ctx=ctx)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
except Exception:
|
||||
logger.exception('Error al procesar cantidad de linea producto')
|
||||
return self.message_bar.set('quantity_not_valid')
|
||||
|
||||
self.message_bar.set('system_ready')
|
||||
|
@ -1043,16 +1024,14 @@ class AppWindow(FrontWindow):
|
|||
self.do_invoice.start()
|
||||
try:
|
||||
if self.print_receipt == 'automatic':
|
||||
_copies = self.shop['invoice_copies']
|
||||
# _copies = self.shop['invoice_copies']
|
||||
if self.environment == 'restaurant':
|
||||
for n in range(_copies):
|
||||
self.action_print_sale()
|
||||
self.action_print_sale()
|
||||
else:
|
||||
if not is_credit:
|
||||
self.print_invoice(copies=_copies, open_box=True)
|
||||
except Exception as e:
|
||||
print('Error', e)
|
||||
logging.error(sys.exc_info()[0])
|
||||
self.print_invoice(open_box=True)
|
||||
except Exception:
|
||||
logging.exception('Error de impresion venta o factura')
|
||||
|
||||
if self.sale_automatic and self.type_pos_user not in ('cashier', 'order'):
|
||||
self.create_new_sale()
|
||||
|
@ -1062,7 +1041,7 @@ class AppWindow(FrontWindow):
|
|||
self.message_bar.set('system_ready')
|
||||
return True
|
||||
|
||||
def print_invoice(self, sale_id=None, type_doc='invoice', open_box=False, copies=1):
|
||||
def print_invoice(self, sale_id=None, type_doc='invoice', open_box=False):
|
||||
if not sale_id:
|
||||
sale_id = self.store.sale_id
|
||||
args = {
|
||||
|
@ -1072,11 +1051,9 @@ class AppWindow(FrontWindow):
|
|||
data = self.Sale.get_data(args)
|
||||
if not data:
|
||||
return
|
||||
for i in range(copies):
|
||||
msg = self.receipt_sale.print_sale(data, type_doc, open_box)
|
||||
if isinstance(msg, dict) and msg.get('error'):
|
||||
self.dialog('print_error', extra_message=msg['error'])
|
||||
break
|
||||
msg = self.receipt_sale.print_sale(data, type_doc, open_box)
|
||||
if isinstance(msg, dict) and msg.get('error'):
|
||||
self.dialog('print_error', extra_message=msg['error'])
|
||||
|
||||
# def button_duplicate_sale(self):
|
||||
# if self.sale_customer_selected:
|
||||
|
@ -1141,9 +1118,11 @@ class AppWindow(FrontWindow):
|
|||
elif environment == 'retail':
|
||||
if not self._check_quantity():
|
||||
return
|
||||
self.Sale.button_method('quote', [sale_id])
|
||||
self.load_sale(sale_id)
|
||||
self.Sale.write([sale_id], {'state': 'quotation'})
|
||||
# self.load_sale(sale_id)
|
||||
self.store.set({'state': 'quotation'})
|
||||
self.set_state('accept')
|
||||
self.set_amounts()
|
||||
|
||||
def button_checkout_pressed(self):
|
||||
if not self.check_salesman():
|
||||
|
@ -1217,8 +1196,7 @@ class AppWindow(FrontWindow):
|
|||
return
|
||||
if not salesman:
|
||||
return self.dialog('error_salesman_wrong')
|
||||
else:
|
||||
self.salesman = salesman
|
||||
self.salesman = salesman
|
||||
self.Sale.write([self.sale_id], {'salesman': salesman['id']})
|
||||
self.store.update({'salesman': salesman})
|
||||
|
||||
|
@ -1275,16 +1253,17 @@ class AppWindow(FrontWindow):
|
|||
return
|
||||
result = self.dialog_salesman_code.exec_()
|
||||
code = self.field_salesman_code_ask.text()
|
||||
if result == 0:
|
||||
# Cancelled the action
|
||||
return result
|
||||
if not code:
|
||||
return
|
||||
salesman_list = self.Employee.find([
|
||||
('code', '=', code)
|
||||
])
|
||||
if salesman_list:
|
||||
return salesman_list[0]
|
||||
if result != 0 and code:
|
||||
seller = None
|
||||
if code in self.sellers:
|
||||
seller = self.sellers[code]
|
||||
else:
|
||||
sellers = self.Employee.find([('code', '=', code)])
|
||||
if sellers:
|
||||
seller = sellers[0]
|
||||
self.sellers[code] = seller
|
||||
return seller
|
||||
return 0
|
||||
|
||||
def action_delivery_party(self):
|
||||
self.dialog_delivery_party.exec_()
|
||||
|
@ -1304,14 +1283,14 @@ class AppWindow(FrontWindow):
|
|||
|
||||
def action_expenses(self):
|
||||
if self.set_expenses():
|
||||
self.dialog_expenses.exec()
|
||||
self.dialog_expenses.exec_()
|
||||
|
||||
def action_open_statement(self):
|
||||
self.dialog_money_count.exec('open')
|
||||
self.dialog_money_count.exec_('open')
|
||||
|
||||
def open_statement_accepted(self, value):
|
||||
res = self.Sale.faster_open_statement({
|
||||
'device': self.device['id'],
|
||||
'device': self.sale_device,
|
||||
'total_money': Decimal(value),
|
||||
})
|
||||
if res['result']:
|
||||
|
@ -1326,7 +1305,7 @@ class AppWindow(FrontWindow):
|
|||
if not salesman:
|
||||
return self.dialog('error_salesman_wrong')
|
||||
|
||||
self.dialog_money_count.exec('close')
|
||||
self.dialog_money_count.exec_('close')
|
||||
|
||||
def close_statement_accepted(self, values):
|
||||
if self.salesman_statement:
|
||||
|
@ -1334,7 +1313,7 @@ class AppWindow(FrontWindow):
|
|||
send_mail = self.field_send_mail.isChecked()
|
||||
self.field_send_mail.setChecked(False)
|
||||
res = self.Sale.faster_close_statement({
|
||||
'device': self.device['id'],
|
||||
'device': self.sale_device,
|
||||
'data': values,
|
||||
'salesman': salesman['id'],
|
||||
'send_mail': send_mail,
|
||||
|
@ -1403,10 +1382,10 @@ class AppWindow(FrontWindow):
|
|||
'state': 'draft',
|
||||
'comment': _sale['comment'],
|
||||
}
|
||||
sale = self.Sale.new_sale(to_create)
|
||||
sale = self.Sale.new_sale(args=[to_create])
|
||||
|
||||
self.SaleLine.write(lines_ids, {'sale': sale['id']})
|
||||
number = self.Sale.set_sale_number({'sale_id': sale['id']})
|
||||
number = self.Sale.set_sale_number(args=[{'sale_id': sale['id']}])
|
||||
self.dialog_split_sale.info(number)
|
||||
self.set_amounts()
|
||||
|
||||
|
@ -1474,6 +1453,7 @@ class AppWindow(FrontWindow):
|
|||
sale_id = self.store.store.get('id')
|
||||
if val and sale_id:
|
||||
args = {'invoice_type': val}
|
||||
self.store.set(args)
|
||||
if val == 'M':
|
||||
args['invoice_method'] = 'manual'
|
||||
self.Sale.write([sale_id], args)
|
||||
|
@ -1540,7 +1520,9 @@ class AppWindow(FrontWindow):
|
|||
'cash_received': to_numeric(float(amount)),
|
||||
'voucher_number': voucher_number,
|
||||
'extra_paid': False,
|
||||
}, ctx={'advance': True, 'reservation': reservation})
|
||||
'advance': True,
|
||||
'reservation': reservation
|
||||
})
|
||||
|
||||
if res.get('msg') not in ('missing_money', 'ok'):
|
||||
self.dialog(res['msg'])
|
||||
|
@ -1576,8 +1558,8 @@ class AppWindow(FrontWindow):
|
|||
sale = self.store.store
|
||||
if number:
|
||||
if type_doc in ['order', 'delivery', 'quotation']:
|
||||
if number != sale.get('number'):
|
||||
sale_id = sale['id']
|
||||
if number != sale.get('number') and sale.get('id'):
|
||||
sale_id = sale.get('id')
|
||||
else:
|
||||
sales = self.Sale.find([('number', '=', number)], fields=['id'])
|
||||
sale = sales[0]
|
||||
|
@ -1745,7 +1727,7 @@ class AppWindow(FrontWindow):
|
|||
}
|
||||
orders, sale_number = self.Sale.get_reversion(args)
|
||||
self.receipt_order.print_orders(orders.values(), reversion=True)
|
||||
except Exception as e:
|
||||
except Exception:
|
||||
print(self.print_order, 'validar variable en opcion print_order=True en config_pos.ini')
|
||||
logging.error('Printing order reversion fail!')
|
||||
|
||||
|
@ -1823,10 +1805,11 @@ class AppWindow(FrontWindow):
|
|||
if res == DIALOG_REPLY_NO:
|
||||
return False
|
||||
result = None
|
||||
if not sale.get('number'):
|
||||
result_set = self.Sale.set_sale_number({'sale_id': sale_id})
|
||||
self.store.set({'number': result_set})
|
||||
self.order_number.setText(result_set)
|
||||
number = sale.get('number')
|
||||
if not number:
|
||||
number = self.Sale.set_sale_number({'sale_id': sale_id})
|
||||
self.store.set({'number': number})
|
||||
self.order_number.setText(number)
|
||||
if self.environment == 'restaurant':
|
||||
if self.print_order:
|
||||
result = self.print_command(sale)
|
||||
|
@ -1841,14 +1824,16 @@ class AppWindow(FrontWindow):
|
|||
return True
|
||||
|
||||
def get_header_sale(self, sale):
|
||||
|
||||
order = {
|
||||
'id': self.sale_id,
|
||||
'sale_number': sale['number'],
|
||||
'number': sale.get('invoice_number'),
|
||||
'turn': sale.get('turn'),
|
||||
'position': sale['position'],
|
||||
'party': sale['party']['name'],
|
||||
'kind': sale.get('kind'),
|
||||
'delivery_amount': sale.get('delivery_amount'),
|
||||
'delivery_amount': sale['delivery_amount'] if sale.get('delivery_amount') else 0,
|
||||
'salesman': sale['salesman']['rec_name'] if sale.get('salesman') else '',
|
||||
'comment': sale['comment'],
|
||||
'payment_term': sale['payment_term']['rec_name'] if sale.get('payment_term') else '',
|
||||
|
@ -1875,65 +1860,86 @@ class AppWindow(FrontWindow):
|
|||
return result
|
||||
|
||||
def print_command(self, sale, line_reversion=None):
|
||||
order = self.get_header_sale(sale)
|
||||
version = compare_versions(self.modules['sale_pos_frontend_rest']['version'], '6.0.7')
|
||||
reversion = False if not line_reversion else True
|
||||
lines = [line_reversion] if reversion else self.model_sale_lines._data
|
||||
orders = {}
|
||||
lines_ids = []
|
||||
for ln in lines:
|
||||
if not reversion and ln['order_sended'] in (True, '✔'):
|
||||
continue
|
||||
pd_id = ln['product.']['id']
|
||||
try:
|
||||
printers, cat_id = self.products_printers.get(str(pd_id))
|
||||
cat_id = str(cat_id[0])
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
printers, cat_id = None, None
|
||||
if printers:
|
||||
for printer_id in printers:
|
||||
printer = self.printers_shop.get(str(printer_id))
|
||||
if printer_id not in orders.keys():
|
||||
orders[printer_id] = {
|
||||
**order,
|
||||
**printer,
|
||||
'lines_ids': []
|
||||
lines = [line_reversion] if reversion else self.model_sale_lines._data
|
||||
if version in ('equal', 'higher'):
|
||||
order = self.get_header_sale(sale)
|
||||
for ln in lines:
|
||||
if not reversion and ln['order_sended'] in (True, '✔'):
|
||||
continue
|
||||
pd_id = ln['product.']['id']
|
||||
try:
|
||||
printers, cat_id = self.products_printers.get(str(pd_id))
|
||||
cat_id = str(cat_id[0])
|
||||
except Exception:
|
||||
traceback.print_exc()
|
||||
printers, cat_id = None, None
|
||||
if printers:
|
||||
for printer_id in printers:
|
||||
printer = self.printers_shop.get(str(printer_id))
|
||||
if printer_id not in orders.keys():
|
||||
orders[printer_id] = {
|
||||
**order,
|
||||
**printer,
|
||||
'lines_ids': []
|
||||
}
|
||||
if printer.get('categories'):
|
||||
orders[printer_id]['lines'] = {**printer['lines']}
|
||||
orders[printer_id]['categories'] = {**printer['categories']}
|
||||
else:
|
||||
orders[printer_id]['lines'] = []
|
||||
value_line = {
|
||||
'name': ln['product.']['name'],
|
||||
'quantity': str(ln['quantity']),
|
||||
'sale_price_taxed': '',
|
||||
'amount_w_tax': ln['amount_w_tax'],
|
||||
'note': ln['note']
|
||||
}
|
||||
if printer.get('categories'):
|
||||
orders[printer_id]['lines'] = {**printer['lines']}
|
||||
orders[printer_id]['categories'] = {**printer['categories']}
|
||||
if isinstance(orders[printer_id]['lines'], list):
|
||||
orders[printer_id]['lines'].append(value_line)
|
||||
orders[printer_id]['lines_ids'].append(ln['id'])
|
||||
else:
|
||||
orders[printer_id]['lines'] = []
|
||||
value_line = {
|
||||
'name': ln['product.']['name'],
|
||||
'quantity': str(ln['quantity']),
|
||||
'sale_price_taxed': '',
|
||||
'amount_w_tax': ln['amount_w_tax'],
|
||||
'note': ln['note']
|
||||
}
|
||||
if isinstance(orders[printer_id]['lines'], list):
|
||||
orders[printer_id]['lines'].append(value_line)
|
||||
orders[printer_id]['lines_ids'].append(ln['id'])
|
||||
else:
|
||||
try:
|
||||
key_id = orders[printer_id]['categories'][cat_id]
|
||||
except Exception:
|
||||
if 'others' not in orders[printer_id]['lines'].keys():
|
||||
orders[printer_id]['lines']['others'] = {'name': 'OTROS', 'lines':[]}
|
||||
key_id = 'others'
|
||||
try:
|
||||
orders[printer_id]['lines'][key_id]['lines'].append(value_line)
|
||||
orders[printer_id]['lines_ids'].append(ln['id'])
|
||||
except Exception:
|
||||
orders[printer_id]['lines'][key_id]['lines'] = [value_line]
|
||||
orders[printer_id]['lines_ids'].append(ln['id'])
|
||||
try:
|
||||
key_id = orders[printer_id]['categories'][cat_id]
|
||||
except Exception:
|
||||
if 'others' not in orders[printer_id]['lines'].keys():
|
||||
orders[printer_id]['lines']['others'] = {'name': 'OTROS', 'lines': []}
|
||||
key_id = 'others'
|
||||
try:
|
||||
orders[printer_id]['lines'][key_id]['lines'].append(value_line)
|
||||
orders[printer_id]['lines_ids'].append(ln['id'])
|
||||
except Exception:
|
||||
orders[printer_id]['lines'][key_id]['lines'] = [value_line]
|
||||
orders[printer_id]['lines_ids'].append(ln['id'])
|
||||
else:
|
||||
lines_ids.append(ln['id'])
|
||||
else:
|
||||
# for remove this code
|
||||
if reversion:
|
||||
args = {'sale_id': self.sale_id, 'sale_line_id': line_reversion['id']}
|
||||
orders, sale_number = self.Sale.get_reversion(args)
|
||||
else:
|
||||
lines_ids.append(ln['id'])
|
||||
args = {'sale_id': self.sale_id}
|
||||
orders, sale_number = self.Sale.get_order2print(args)
|
||||
|
||||
if self.environment == 'restaurant' and self.tasks_station:
|
||||
data_station = self.Sale.method_instance('get_data_for_stations', self.sale_id)
|
||||
receipt = Receipt(context={}, environment='restaurant')
|
||||
receipt.print_tasks(data_station)
|
||||
if version in ('equal', 'higher'):
|
||||
args = {
|
||||
'shop': self.shop['id'],
|
||||
'sale_id': self.sale_id
|
||||
}
|
||||
tasks = self.SaleLine.get_data_tasks(args)
|
||||
receipt = Receipt(context={}, environment='restaurant')
|
||||
result_print = receipt.print_tasks(tasks)
|
||||
if any(result_print):
|
||||
self.SaleLine.mark_tasks_printed(result_print)
|
||||
else:
|
||||
data_station = self.Sale.method_instance('get_data_for_stations', self.sale_id)
|
||||
receipt = Receipt(context={}, environment='restaurant')
|
||||
receipt.print_tasks(data_station.values())
|
||||
result = self.receipt_order.print_orders(orders.values(), reversion)
|
||||
lines_sended = result + lines_ids
|
||||
if not reversion and lines_sended:
|
||||
|
@ -1941,7 +1947,7 @@ class AppWindow(FrontWindow):
|
|||
if line['id'] in lines_sended:
|
||||
line['order_sended'] = '✔'
|
||||
self.model_sale_lines.update_record(line)
|
||||
self.Sale.mark_commanded({'sale_id': self.sale_id, 'lines_ids': lines_sended})
|
||||
self.Sale.mark_commanded({'lines_ids': lines_sended})
|
||||
return result
|
||||
|
||||
def action_source(self):
|
||||
|
@ -1960,11 +1966,12 @@ class AppWindow(FrontWindow):
|
|||
if self._ask_new_sale():
|
||||
self.create_new_sale()
|
||||
|
||||
def numpad_price_clicked(self):
|
||||
code = self.label_input.text()
|
||||
product = self._search_product(code)
|
||||
if not product:
|
||||
return
|
||||
# for remove
|
||||
# def numpad_price_clicked(self):
|
||||
# code = self.label_input.text()
|
||||
# product = self._search_product(code)
|
||||
# if not product:
|
||||
# return
|
||||
|
||||
def _ask_new_sale(self):
|
||||
dialog = self.dialog('new_sale', response=True)
|
||||
|
@ -2100,11 +2107,11 @@ class AppWindow(FrontWindow):
|
|||
('payment_term', '=', self.default_payment_term['id']),
|
||||
('payment_term', '=', None),
|
||||
],
|
||||
('reservation', '!=', True)
|
||||
('reservation', 'in', [None, False]),
|
||||
])
|
||||
elif _type == 'credit':
|
||||
dom.extend([
|
||||
['reservation', '!=', True],
|
||||
['reservation', 'in', [None, False]],
|
||||
['payment_term', '!=', self.default_payment_term['id']]
|
||||
])
|
||||
elif _type == 'reservation':
|
||||
|
@ -2115,7 +2122,6 @@ class AppWindow(FrontWindow):
|
|||
dom.append(('state', '=', 'quotation'))
|
||||
|
||||
# dom.append(['payment_method', '!=', 'all_paid'])
|
||||
time1 = time.time()
|
||||
fields = self.dialog_search_sales.fields_names
|
||||
sales = self.Sale.find(dom, fields=fields, order=[('id', 'DESC')])
|
||||
self.dialog_search_sales.set_from_values(sales)
|
||||
|
@ -2127,7 +2133,6 @@ class AppWindow(FrontWindow):
|
|||
]
|
||||
sales_draft = self.Sale.search_count(dom_draft)
|
||||
self.dialog_search_sales.set_counter_control(sales_draft)
|
||||
print(time.time() - time1, 'search sales')
|
||||
|
||||
def on_search_sale_by_number(self):
|
||||
target = self.dialog_search_sales.filter_field.text()
|
||||
|
@ -2235,7 +2240,7 @@ class AppWindow(FrontWindow):
|
|||
if sale.get('payments'):
|
||||
for payment in sale['payments']:
|
||||
# FIXME
|
||||
#self.table_payment_lines.record_add(payment)
|
||||
# self.table_payment_lines.record_add(payment)
|
||||
pass
|
||||
|
||||
self.party_id = sale['party.']['id']
|
||||
|
@ -2350,12 +2355,13 @@ class AppWindow(FrontWindow):
|
|||
('state', '=', 'draft'),
|
||||
('date', '=', values['date']),
|
||||
('company', '=', company),
|
||||
('sale_device.shop', '=', shop)
|
||||
('sale_device.shop', '=', shop),
|
||||
], fields=['id'])
|
||||
if statements:
|
||||
return
|
||||
values['company'] = company
|
||||
values['shop'] = shop
|
||||
values['device'] = self.sale_device
|
||||
self.dialog_reports.open_report(report_name, values)
|
||||
|
||||
def action_terminal_journal_report(self):
|
||||
|
@ -2431,7 +2437,7 @@ class AppWindow(FrontWindow):
|
|||
qty_text = self.dialog_combo_product.label_qty_add.text()
|
||||
try:
|
||||
qty = int(qty_text) + 1
|
||||
except:
|
||||
except Exception:
|
||||
qty = 1
|
||||
self.dialog_combo_product.label_qty_add.setText(str(qty))
|
||||
self.on_selected_item(record, list_price)
|
||||
|
@ -2539,8 +2545,9 @@ class AppWindow(FrontWindow):
|
|||
('template.name', 'ilike', '%{:}%'.format(tw)),
|
||||
('description', 'ilike', '%{:}%'.format(tw)),
|
||||
('code', 'ilike', '%{:}%'.format(tw)),
|
||||
('barcode', '=', '%{:}%'.format(tw)),
|
||||
]
|
||||
if self._onebarcode_activated:
|
||||
clause.append(('barcode', '=', '%{:}%'.format(tw)))
|
||||
if self.environment == 'retail':
|
||||
clause.append(('template.reference', 'ilike', '%{:}%'.format(tw)))
|
||||
domain.append(clause)
|
||||
|
@ -2557,7 +2564,7 @@ class AppWindow(FrontWindow):
|
|||
|
||||
ctx = self.stock_context
|
||||
if not self.stock_context:
|
||||
ctx = {'price_list': self.shop['price_list.']['id']}
|
||||
ctx = {'price_list': self.shop['price_list.']['id']}
|
||||
else:
|
||||
ctx['price_list'] = self.shop['price_list.']['id']
|
||||
fields = self.dialog_search_products.fields_names
|
||||
|
@ -2659,7 +2666,7 @@ class AppWindow(FrontWindow):
|
|||
'invoice_type': invoice_type[0][0],
|
||||
'company': self.company,
|
||||
'party': self.default_party['id'],
|
||||
'sale_device': self.device['id'],
|
||||
'sale_device': self.sale_device,
|
||||
'payment_method': 'cash',
|
||||
'payment_term': self.default_payment_term['id'],
|
||||
'kind': 'take_away',
|
||||
|
@ -2700,7 +2707,6 @@ class AppWindow(FrontWindow):
|
|||
if self.type_pos_user == 'cashier':
|
||||
self.message_bar.set('not_sale')
|
||||
return
|
||||
|
||||
if self.salesman_required and not self.salesman:
|
||||
salesman = self.action_salesman_code()
|
||||
if salesman == 0:
|
||||
|
@ -2708,8 +2714,7 @@ class AppWindow(FrontWindow):
|
|||
return
|
||||
if not salesman:
|
||||
return self.dialog('error_salesman_wrong')
|
||||
else:
|
||||
self.salesman = salesman
|
||||
self.salesman = salesman
|
||||
|
||||
if self.environment == 'restaurant' and self._sale_pos_restaurant:
|
||||
self.dialog_consumer.clear()
|
||||
|
@ -2739,7 +2744,7 @@ class AppWindow(FrontWindow):
|
|||
'invoice_type': invoice_type[0][0],
|
||||
'company': self.company,
|
||||
'party': self.default_party['id'],
|
||||
'sale_device': self.device['id'],
|
||||
'sale_device': self.sale_device,
|
||||
'payment_method': 'cash',
|
||||
'payment_term': self.default_payment_term['id'],
|
||||
'kind': kind,
|
||||
|
@ -2749,7 +2754,7 @@ class AppWindow(FrontWindow):
|
|||
to_create.update({'salesman': self.salesman['id']})
|
||||
if self.environment != 'restaurant' and self._config.get('use_price_list'):
|
||||
self.field_list_price.set_from_id(self.shop['price_list.']['id'])
|
||||
sale = self.Sale.new_sale(to_create)
|
||||
sale = self.Sale.new_sale(args=[to_create])
|
||||
self.store.set(sale)
|
||||
self.sale_id = sale['id']
|
||||
|
||||
|
@ -2795,14 +2800,14 @@ class AppWindow(FrontWindow):
|
|||
('barcode', '=', code),
|
||||
('code', '=', code)
|
||||
])
|
||||
# 'image', 'image_icon', 'write_date' fields remove from domain
|
||||
products = self.Product.find(
|
||||
domain,
|
||||
fields=[
|
||||
'name', 'code', 'categories', 'description',
|
||||
'id', 'image', 'image_icon', 'list_price',
|
||||
'quantity', 'rec_name', 'template',
|
||||
'id', 'list_price', 'quantity', 'rec_name', 'template',
|
||||
'extra_tax', 'template.sale_price_w_tax',
|
||||
'template.default_uom', 'write_date'])
|
||||
'template.default_uom'])
|
||||
|
||||
if not products or len(products) > 1:
|
||||
self.message_bar.set('product_not_found')
|
||||
|
@ -2856,6 +2861,7 @@ class AppWindow(FrontWindow):
|
|||
product_id = record['id'] if record else None
|
||||
if not product_id and code:
|
||||
# REMOVE ME THIS OPTION IS FOR BARCODE
|
||||
# product = self._search_product(code)
|
||||
product = self._search_product(code)
|
||||
if product:
|
||||
product_id = product['id']
|
||||
|
@ -3282,7 +3288,8 @@ class AppWindow(FrontWindow):
|
|||
if note:
|
||||
self.SaleLine.write([removed_item['id']], {'note': note})
|
||||
prd_code = removed_item['product.']['code']
|
||||
if prd_code and self._config['tip_product.']['code'] == prd_code:
|
||||
tip_product = self._config.get('tip_product.')
|
||||
if prd_code and tip_product and tip_product['code'] == prd_code:
|
||||
self.Sale.write([self.sale_id], {'tip': None})
|
||||
self.SaleLine.delete([removed_item['id']])
|
||||
self.set_amounts()
|
||||
|
@ -3352,10 +3359,13 @@ class AppWindow(FrontWindow):
|
|||
res = self.Sale.faster_add_payment({
|
||||
'sale_id': self.sale_id,
|
||||
'journal_id': journal['id'],
|
||||
'sale_device': self.sale_device,
|
||||
'cash_received': to_numeric(amount),
|
||||
'voucher_number': voucher_number,
|
||||
'extra_paid': False,
|
||||
}, ctx={'advance': False, 'reservation': False})
|
||||
'advance': False,
|
||||
'reservation': False
|
||||
})
|
||||
if res.get("msg") and res.get('msg') not in ('missing_money', 'ok') or res.get('error'):
|
||||
if res.get("msg"):
|
||||
self.dialog(res['msg'])
|
||||
|
@ -3408,6 +3418,9 @@ class AppWindow(FrontWindow):
|
|||
if self.environment == 'restaurant':
|
||||
self.menu_dash.setDisabled(True)
|
||||
|
||||
def action_transfer_sale(self):
|
||||
self.dialog_sale_to_transfer.show()
|
||||
|
||||
def key_pressed(self, text):
|
||||
if self._state == 'disabled':
|
||||
return
|
||||
|
@ -3709,3 +3722,42 @@ class AppWindow(FrontWindow):
|
|||
self.change_view_to('order_front')
|
||||
self.load_sale(sale)
|
||||
self.dialog_manage_tables.hide()
|
||||
|
||||
def action_test_print(self):
|
||||
self.dialog_test_printer.exec_()
|
||||
|
||||
def on_selected_folio_transfer(self):
|
||||
sale = self.store.store
|
||||
folio_id = self.dialog_search_folio_transfer.get_id()
|
||||
if not folio_id:
|
||||
return
|
||||
sale_id = self.sale_id
|
||||
current_row = self.dialog_search_folio_transfer.current_row
|
||||
guest = current_row['main_guest.name']
|
||||
room = current_row['room.code']
|
||||
msg = f"\n\n HABITACION: {room} \n HUESPED: {guest}"
|
||||
dialog = self.dialog('confirm_transfer_sale', response=True, extra_message=msg)
|
||||
dialog.adjustSize()
|
||||
response = dialog.exec_()
|
||||
if response == DIALOG_REPLY_NO:
|
||||
return
|
||||
result = self.Sale.transfer_to_folio(args=[sale_id, folio_id])
|
||||
if result == 'ok':
|
||||
self.dialog('transfer_sale', extra_message='\n' + current_row['main_guest.name'])
|
||||
self.change_view_to('start_front')
|
||||
|
||||
def on_selected_sale_transfer(self):
|
||||
target_sale_id = self.dialog_search_sale_transfer.get_id()
|
||||
if not target_sale_id:
|
||||
return
|
||||
current_row = self.dialog_search_sale_transfer.current_row
|
||||
msg = f"\n\n VENTA: {current_row['number']} \n CLIENTE: {current_row['party.name']} \n DESCRIPCION: {current_row['description']}"
|
||||
dialog = self.dialog('confirm_transfer_sale', response=True, extra_message=msg)
|
||||
dialog.adjustSize()
|
||||
response = dialog.exec_()
|
||||
if response == DIALOG_REPLY_NO:
|
||||
return
|
||||
result = self.Sale.transfer_to_sale(args=[self.sale_id, target_sale_id])
|
||||
if result == 'ok':
|
||||
self.dialog('transfer_sale', extra_message='\n' + current_row['party.name'])
|
||||
self.change_view_to('start_front')
|
||||
|
|
|
@ -3,7 +3,7 @@ MODELS_RESTAURANT = {
|
|||
'ir.module': {
|
||||
'rec_name': 'name',
|
||||
'fields': [
|
||||
'name', 'state'
|
||||
'name', 'state', 'version'
|
||||
]
|
||||
},
|
||||
'company.company': {
|
||||
|
@ -15,7 +15,7 @@ MODELS_RESTAURANT = {
|
|||
},
|
||||
'res.user': {
|
||||
'rec_name': 'name',
|
||||
'fields': ['name', 'sale_device', 'type_pos_user', 'shop']
|
||||
'fields': ['name', 'sale_device', 'type_pos_user', 'shop', 'shops']
|
||||
},
|
||||
'product.category': {
|
||||
'rec_name': 'name',
|
||||
|
@ -124,7 +124,7 @@ MODELS_RESTAURANT = {
|
|||
'shop.invoice_copies', 'shop.pos_authorization', 'shop.discounts',
|
||||
'shop.computer_authorization', 'shop.manual_authorization',
|
||||
'shop.credit_note_electronic_authorization',
|
||||
'shop.salesmans.rec_name', 'shop.discount_pos_method',
|
||||
'shop.salesmans.rec_name', 'shop.salesmans.code', 'shop.discount_pos_method',
|
||||
'shop.debit_note_electronic_authorization',
|
||||
'shop.delivery_man.rec_name', 'shop.price_list.name',
|
||||
'shop.order_copies', 'shop.delivery_man.active',
|
||||
|
@ -134,7 +134,7 @@ MODELS_RESTAURANT = {
|
|||
'rec_name': 'name',
|
||||
'fields': [
|
||||
'taxes', 'product_categories', 'party', 'invoice_copies',
|
||||
'warehouse', 'payment_term', 'salesmans', 'delivery_man',
|
||||
'warehouse', 'payment_term', 'delivery_man',
|
||||
'discounts', 'price_list', 'order_copies']
|
||||
},
|
||||
'product.product': {
|
||||
|
@ -216,6 +216,12 @@ MODELS_RESTAURANT = {
|
|||
'fields': [
|
||||
'name'
|
||||
],
|
||||
},
|
||||
'hotel.folio': {
|
||||
'rec_name': 'rec_name',
|
||||
'fields': [
|
||||
'id'
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +230,7 @@ MODELS_RETAIL = {
|
|||
'ir.module': {
|
||||
'rec_name': 'name',
|
||||
'fields': [
|
||||
'name', 'state'
|
||||
'name', 'state', 'version'
|
||||
]
|
||||
},
|
||||
'company.company': {
|
||||
|
@ -236,7 +242,7 @@ MODELS_RETAIL = {
|
|||
},
|
||||
'res.user': {
|
||||
'rec_name': 'name',
|
||||
'fields': ['name', 'sale_device', 'type_pos_user', 'shop']
|
||||
'fields': ['name', 'sale_device', 'type_pos_user', 'shop', 'shops']
|
||||
},
|
||||
'product.category': {
|
||||
'rec_name': 'name',
|
||||
|
@ -341,7 +347,7 @@ MODELS_RETAIL = {
|
|||
'shop.invoice_copies', 'shop.pos_authorization', 'shop.discounts',
|
||||
'shop.computer_authorization', 'shop.manual_authorization',
|
||||
'shop.credit_note_electronic_authorization',
|
||||
'shop.salesmans.rec_name', 'shop.delivery_man.active',
|
||||
'shop.salesmans.rec_name', 'shop.salesmans.code', 'shop.delivery_man.active',
|
||||
'shop.debit_note_electronic_authorization',
|
||||
'shop.delivery_man.rec_name', 'shop.price_list.name',
|
||||
'shop.order_copies'
|
||||
|
@ -351,7 +357,7 @@ MODELS_RETAIL = {
|
|||
'rec_name': 'name',
|
||||
'fields': [
|
||||
'taxes', 'product_categories', 'party', 'invoice_copies',
|
||||
'warehouse', 'payment_term', 'salesmans', 'delivery_man',
|
||||
'warehouse', 'payment_term', 'delivery_man',
|
||||
'discounts', 'price_list', 'order_copies']
|
||||
},
|
||||
'product.product': {
|
||||
|
@ -388,7 +394,6 @@ MODELS_RETAIL = {
|
|||
'new_sale_automatic', 'show_product_image', 'delivery_product.code',
|
||||
'encoded_sale_price', 'delivery_product.list_price',
|
||||
'delivery_product.name', 'allow_discount_handle',
|
||||
'no_remove_commanded',
|
||||
'cache_products_local', 'show_party_categories',
|
||||
'print_lines_product', 'uvt_pos'
|
||||
]
|
||||
|
|
45
app/proxy.py
45
app/proxy.py
|
@ -2,6 +2,7 @@
|
|||
import os
|
||||
import base64
|
||||
import tempfile
|
||||
import logging
|
||||
from datetime import date
|
||||
from decimal import Decimal
|
||||
from http.client import HTTPConnection, HTTPSConnection
|
||||
|
@ -43,9 +44,9 @@ def logout(ctx):
|
|||
host = ctx['params']['server']
|
||||
db = ctx['params']['database']
|
||||
if ctx['params']['mode'] == 'http':
|
||||
conn = HTTPConnection(host, port=port)
|
||||
conn = HTTPConnection(host, port=port, timeout=30)
|
||||
else:
|
||||
conn = HTTPSConnection(host, port=port, context=context_http)
|
||||
conn = HTTPSConnection(host, port=port, timeout=30, context=context_http)
|
||||
url = '/' + db + '/logout'
|
||||
payload = json.dumps({
|
||||
'context': {
|
||||
|
@ -54,8 +55,10 @@ def logout(ctx):
|
|||
}, default=encoder)
|
||||
conn.request('POST', url, body=payload, headers=HEADERS)
|
||||
response = conn.getresponse()
|
||||
print(response.status, 'status logout')
|
||||
if response.status != 200:
|
||||
res = response.read()
|
||||
logging.error(f'error request {res}')
|
||||
else:
|
||||
res = json.loads(response.read())
|
||||
conn.close()
|
||||
|
@ -201,31 +204,41 @@ class Model(object):
|
|||
conn = self.conn(
|
||||
self.host,
|
||||
port=self.port,
|
||||
timeout=30,
|
||||
context=self.context_http)
|
||||
else:
|
||||
conn = self.conn(self.host, port=self.port)
|
||||
conn.request(method, url, body=payload, headers=HEADERS)
|
||||
response = conn.getresponse()
|
||||
res = json.loads(response.read())
|
||||
if response.status != 200:
|
||||
if self.main_window and hasattr(self.main_window, 'statusbar'):
|
||||
print(res, 'valida')
|
||||
self.main_window.dialog(
|
||||
'error_server',
|
||||
extra_message=res['detail']['error'])
|
||||
res = res['detail']
|
||||
conn.close()
|
||||
conn = self.conn(self.host, port=self.port, timeout=30)
|
||||
res = 'error_network'
|
||||
try:
|
||||
conn.request(method, url, body=payload, headers=HEADERS)
|
||||
response = conn.getresponse()
|
||||
res = json.loads(response.read())
|
||||
if response.status != 200:
|
||||
logging.error(f'error request {str(res)}')
|
||||
if self.main_window and hasattr(self.main_window, 'statusbar'):
|
||||
self.main_window.dialog(
|
||||
'error_server',
|
||||
extra_message=res['detail']['error'])
|
||||
res = res['detail']
|
||||
conn.close()
|
||||
except Exception:
|
||||
logging.exception('error de conexion')
|
||||
return res
|
||||
|
||||
def __call__(self, values=None, ctx=None):
|
||||
def __call__(self, values=None, args=None, ctx=None):
|
||||
if ctx:
|
||||
self.ctx.update(ctx)
|
||||
args_ = {
|
||||
'model': self.model,
|
||||
'method': self.method,
|
||||
'args': [values],
|
||||
'args': [],
|
||||
'context': self.ctx,
|
||||
}
|
||||
if values:
|
||||
logging.warning(f'deprecate option values call method with list instance. \n model: {self.model} method: {self.method}')
|
||||
args_['args'] = [values]
|
||||
if args:
|
||||
args_['args'] = args
|
||||
res = self.get_connection('POST', '/method', args_)
|
||||
return res
|
||||
|
||||
|
|
264
app/reporting.py
264
app/reporting.py
|
@ -3,12 +3,10 @@
|
|||
|
||||
import os
|
||||
import logging
|
||||
import traceback
|
||||
from datetime import datetime
|
||||
from decimal import Decimal
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from .printing.protocols import FileSSH
|
||||
|
||||
try:
|
||||
|
@ -61,23 +59,22 @@ SSH_PORT = 23
|
|||
|
||||
|
||||
def money(value):
|
||||
if type(value) is int:
|
||||
if isinstance(value, str):
|
||||
value = int(value)
|
||||
return '{:,.2f}'.format(value)
|
||||
return '{:,.0f}'.format(value)
|
||||
|
||||
|
||||
dev_printers = {}
|
||||
if os.name == 'posix' and os.path.exists('/dev/usb'):
|
||||
for fl in os.listdir('/dev/usb'):
|
||||
if 'lp' in fl:
|
||||
dev_printers['usb'] = fl
|
||||
dev_printers['usb'] = '/dev/usb/' + fl
|
||||
|
||||
|
||||
class Receipt(object):
|
||||
__name__ = 'frontend_pos.ticket'
|
||||
|
||||
def __init__(self, context={}, row_characters=None, logo=None, environment='retail'):
|
||||
self.logger = logging.getLogger('reporting')
|
||||
self._company = context.get('company')
|
||||
self._sale_device = context.get('sale_device')
|
||||
self._shop = context.get('shop')
|
||||
|
@ -97,6 +94,7 @@ class Receipt(object):
|
|||
self.order_copies = context.get('order_copies') or 0
|
||||
self.invoice_copies = context.get('invoice_copies') or 0
|
||||
self.order_kind = 'command' if environment == 'restaurant' else 'dispatch'
|
||||
self.params = context.get('params')
|
||||
|
||||
self._row_characters = _ROW_CHARACTERS
|
||||
if context.get('row_characters'):
|
||||
|
@ -135,49 +133,67 @@ class Receipt(object):
|
|||
return self._printer
|
||||
|
||||
def test_printer(self):
|
||||
if self._interface == 'usb':
|
||||
if OS_NAME == 'posix':
|
||||
self._printer = printer.File(
|
||||
self._device, profile=self._profile)
|
||||
elif OS_NAME == 'nt':
|
||||
self._printer = printer.Win32Raw(self._device)
|
||||
self._printer.open()
|
||||
elif self._interface == 'network':
|
||||
try:
|
||||
host, port = self._device.split(":")
|
||||
except Exception:
|
||||
host, port = self._device, None
|
||||
if port:
|
||||
self._printer = printer.Network(host, port=int(port), timeout=15)
|
||||
else:
|
||||
self._printer = printer.Network(host, timeout=15)
|
||||
elif self._interface == 'ssh':
|
||||
self._printer = FileSSH(*self._device.split('@'))
|
||||
self._printer.open()
|
||||
if not self._printer:
|
||||
return
|
||||
self.print_enter()
|
||||
self.set_printer()
|
||||
try:
|
||||
self._printer.image(image_test_file, center=True)
|
||||
except Exception as e:
|
||||
print(e, 'error')
|
||||
self.print_enter()
|
||||
self.print_header()
|
||||
self._printer.ln(3)
|
||||
self._printer.cut()
|
||||
self._printer.cashdraw(2)
|
||||
self._printer.close()
|
||||
# if self._interface == 'usb':
|
||||
# if OS_NAME == 'posix':
|
||||
# self._printer = printer.File(
|
||||
# self._device, profile=self._profile)
|
||||
# elif OS_NAME == 'nt':
|
||||
# self._printer = printer.Win32Raw(self._device)
|
||||
# self._printer.open()
|
||||
# elif self._interface == 'network':
|
||||
# try:
|
||||
# host, port = self._device.split(":")
|
||||
# except Exception:
|
||||
# host, port = self._device, None
|
||||
# if port:
|
||||
# self._printer = printer.Network(host, port=int(port), timeout=15)
|
||||
# else:
|
||||
# self._printer = printer.Network(host, timeout=15)
|
||||
# elif self._interface == 'ssh':
|
||||
# self._printer = FileSSH(*self._device.split('@'))
|
||||
# self._printer.open()
|
||||
if not self._printer:
|
||||
return False
|
||||
self.print_enter()
|
||||
try:
|
||||
self._printer.image(image_test_file, center=True)
|
||||
except Exception:
|
||||
logging.exception('Error impresion imagen')
|
||||
self.print_enter()
|
||||
self.print_header()
|
||||
self._printer.ln(3)
|
||||
self._printer.cut()
|
||||
self._printer.cashdraw(2)
|
||||
self._printer.close()
|
||||
result = True
|
||||
except Exception:
|
||||
logging.exception(f'Error test impresion; interface: {self._interface} device: {self._device}')
|
||||
result = False
|
||||
return result
|
||||
# self._printer.beep()
|
||||
|
||||
def config_printer(self, printer):
|
||||
if dev_printers.get(printer['device']):
|
||||
device = dev_printers[printer['device']]
|
||||
if printer:
|
||||
self._device = printer['device']
|
||||
self._interface = printer['interface']
|
||||
self._profile = printer['profile']
|
||||
elif dev_printers:
|
||||
self._interface = 'usb'
|
||||
self._device = dev_printers['usb']
|
||||
self._profile = self.params.get('profile_printer')
|
||||
else:
|
||||
device = printer['device']
|
||||
self._device = self.params.get('printer_sale_name')[1]
|
||||
self._interface = self.params.get('printer_sale_name')[0]
|
||||
self._profile = self.params.get('profile_printer')
|
||||
|
||||
self._interface = printer['interface']
|
||||
self._device = device
|
||||
self._profile = printer['profile']
|
||||
# if dev_printers.get(printer['device']):
|
||||
# device = dev_printers[printer['device']]
|
||||
# else:
|
||||
# device = printer['device']
|
||||
|
||||
# self._profile = printer['profile']
|
||||
|
||||
def check_status(self, interface, device):
|
||||
status = False
|
||||
|
@ -217,18 +233,17 @@ class Receipt(object):
|
|||
except Exception:
|
||||
host, port = self._device, None
|
||||
if port:
|
||||
self._printer = printer.Network(host, port=int(port), timeout=15)
|
||||
self._printer = printer.Network(host, port=int(port), timeout=5)
|
||||
else:
|
||||
self._printer = printer.Network(host, timeout=15)
|
||||
self._printer = printer.Network(host, timeout=5)
|
||||
if not self._printer:
|
||||
msg = "Warning: Can not found Printer!"
|
||||
self.logger.info("Warning: Can not found Printer!")
|
||||
logging.info("Warning: Can not found Printer!")
|
||||
return msg
|
||||
self.logger.info("Info: Printer is OK!")
|
||||
except Exception as e:
|
||||
traceback.print_exc()
|
||||
self.logger.info(
|
||||
"Warning: Printer error or device not found!", str(e))
|
||||
except Exception:
|
||||
logging.exception(
|
||||
"Warning: Printer error or device not found!")
|
||||
logging.info(f'interface: {self._interface}, device: {self._device}')
|
||||
|
||||
def print_sale(self, sale, type_doc=None, open_box=False):
|
||||
try:
|
||||
|
@ -236,44 +251,17 @@ class Receipt(object):
|
|||
msg = self.set_printer()
|
||||
if msg:
|
||||
return {"error": msg}
|
||||
# if self._interface == 'usb':
|
||||
# if OS_NAME == 'posix':
|
||||
# self._printer = printer.File(
|
||||
# self._device, profile=self._profile)
|
||||
# elif OS_NAME == 'nt':
|
||||
# self._printer = printer.Win32Raw(self._device)
|
||||
# self._printer.open()
|
||||
# elif self._interface == 'network':
|
||||
# try:
|
||||
# host, port = self._device.split(":")
|
||||
# except:
|
||||
# host, port = self._device, None
|
||||
# if port:
|
||||
# self._printer = printer.Network(host, port=int(port), timeout=15)
|
||||
# else:
|
||||
# self._printer = printer.Network(host, timeout=15)
|
||||
# elif self._interface == 'ssh':
|
||||
# self._printer = FileSSH(*self._device.split('@'))
|
||||
# self._printer.open()
|
||||
# elif self._interface == 'cups':
|
||||
# self.conn = cups.Connection()
|
||||
# self._file = open(TEMP_INVOICE_FILE, 'w')
|
||||
# self._printer = CupsPrinter(self._file, self._row_characters)
|
||||
# if not self._printer:
|
||||
# self.logger.info("Warning: Can not found Printer!")
|
||||
# return
|
||||
# self.logger.info("Info: Printer is OK!")
|
||||
try:
|
||||
if type_doc in ('invoice', 'quotation') or self._environment == 'retail':
|
||||
self._print_sale(sale, type_doc, open_box)
|
||||
for copie in range(self.invoice_copies):
|
||||
self._print_sale(sale, type_doc, open_box)
|
||||
else:
|
||||
self._print_sale_verification(sale)
|
||||
except Exception:
|
||||
pass
|
||||
self._printer.close()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
self.logger.info("Warning: Printer error or device not found!")
|
||||
except Exception:
|
||||
logging.exception("Warning: Printer error or device not found!")
|
||||
|
||||
def _print_sale(self, sale, type_doc=None, open_box=False):
|
||||
short = sale['short_invoice']
|
||||
|
@ -432,7 +420,7 @@ class Receipt(object):
|
|||
self._printer.set(align='center')
|
||||
self._printer.image(self._img_logo, center=True)
|
||||
self.print_enter()
|
||||
except:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def print_qrcode(self, qrcode):
|
||||
|
@ -440,11 +428,15 @@ class Receipt(object):
|
|||
self.print_enter()
|
||||
|
||||
def print_tasks(self, data):
|
||||
for value in data.values():
|
||||
tasks_printed = []
|
||||
for value in data:
|
||||
try:
|
||||
self._printer = None
|
||||
self.config_printer(value['printer'])
|
||||
self.set_printer()
|
||||
if not self._printer:
|
||||
logging.error('impresora no disponible error impresion tarea %s', str(value['printer']))
|
||||
continue
|
||||
title = value['sale'] + ' - ' + value['work_station']
|
||||
self._printer.set(custom_size=True, width=2,
|
||||
height=2, align='center')
|
||||
|
@ -453,17 +445,18 @@ class Receipt(object):
|
|||
height=2, align='left')
|
||||
self._printer.ln(2)
|
||||
for ln in value['lines']:
|
||||
line_ = str(int(ln['qty'])) + ' ' + ln['name']
|
||||
line_ = str(int(ln['quantity'])) + ' ' + ln['name']
|
||||
self._printer.textln(line_)
|
||||
if ln.get('note'):
|
||||
self._printer.textln('NOTA:' + ln.get('note'))
|
||||
self._printer.ln(2)
|
||||
tasks_printed.append(ln['task'])
|
||||
self._printer.cut()
|
||||
self._printer.close()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_exc()
|
||||
except Exception:
|
||||
logging.exception('error tarea')
|
||||
time.sleep(1)
|
||||
return tasks_printed
|
||||
|
||||
def print_header(self, short=False):
|
||||
if not short:
|
||||
|
@ -508,9 +501,9 @@ class Receipt(object):
|
|||
def print_split(self, left, right):
|
||||
len_left = self._row_characters - len(right) - 1
|
||||
left = left[:len_left]
|
||||
if type(left) == bytes:
|
||||
if isinstance(left, bytes):
|
||||
left = left.decode("utf-8")
|
||||
if type(right) == bytes:
|
||||
if isinstance(right, bytes):
|
||||
right = right.decode("utf-8")
|
||||
left += (len_left - len(left) + 1) * ' '
|
||||
self._printer.text(left)
|
||||
|
@ -534,11 +527,11 @@ class Receipt(object):
|
|||
if self._show_discount and discount and Decimal(discount) > 0:
|
||||
amount_w_tax = Decimal(line['amount_w_tax'])
|
||||
discount = Decimal(discount)
|
||||
initial = 'DCTO - ' + str(round(discount*100, 0)) + '%'
|
||||
initial = 'DCTO - ' + str(round(discount * 100, 0)) + '%'
|
||||
if discount == 1:
|
||||
line_total = 0
|
||||
else:
|
||||
line_total = amount_w_tax/(1-discount)
|
||||
line_total = amount_w_tax / (1 - discount)
|
||||
discount = '-' + money(line_total - amount_w_tax)
|
||||
line_total = money(line_total)
|
||||
|
||||
|
@ -547,7 +540,7 @@ class Receipt(object):
|
|||
length_name = self._row_characters - 11
|
||||
first_line = code + ' ' + line['name'][:length_name]
|
||||
|
||||
if type(first_line) == bytes:
|
||||
if isinstance(first_line, bytes):
|
||||
first_line = first_line.decode('utf-8')
|
||||
|
||||
self._printer.text(first_line + '\n')
|
||||
|
@ -626,9 +619,15 @@ class Receipt(object):
|
|||
consumer = sale.get('consumer', None)
|
||||
if consumer:
|
||||
payment_method = sale.get('payment_method', None)
|
||||
consumer_name = consumer.get('consumer_name', None)
|
||||
consumer_address = consumer.get('consumer_address', '')
|
||||
consumer_phone = consumer.get('consumer_phone', '')
|
||||
if consumer.get('name') or consumer.get('phone'):
|
||||
consumer_name = consumer.get('name', None)
|
||||
consumer_address = consumer.get('address', '')
|
||||
consumer_phone = consumer.get('phone', '')
|
||||
else:
|
||||
# for remove this option
|
||||
consumer_name = consumer.get('consumer_name', None)
|
||||
consumer_address = consumer.get('consumer_address', '')
|
||||
consumer_phone = consumer.get('consumer_phone', '')
|
||||
self.print_horinzontal_line()
|
||||
self.print_enter()
|
||||
self._printer.set(align='center')
|
||||
|
@ -648,7 +647,7 @@ class Receipt(object):
|
|||
payment_method = '- PAGA EN EFECTIVO'
|
||||
self._printer.text(f'MEDIO DE PAGO: {payment_method}')
|
||||
self._printer.ln(2)
|
||||
delivery_amount = money(sale.get('delivery_amount', 0))
|
||||
delivery_amount = money(sale.get('delivery_amount', 0) or 0)
|
||||
self._printer.text(f'Domicilio: {delivery_amount}')
|
||||
self._printer.ln(2)
|
||||
|
||||
|
@ -657,9 +656,15 @@ class Receipt(object):
|
|||
self._printer.set(align='left')
|
||||
if type_doc == 'invoice':
|
||||
if sale['number']:
|
||||
text_title = ""
|
||||
invoice_type = sale.get('invoice_type', '')
|
||||
if invoice_type == 'P':
|
||||
text_title = "SISTEMA POS: "
|
||||
elif invoice_type == '1':
|
||||
text_title = "FACTURA DE VENTA ELECTRONICA: "
|
||||
if sale['total_amount'] >= 0:
|
||||
self._printer.text(
|
||||
'FACTURA DE VENTA No. ' + sale['number'])
|
||||
text_title + sale['number'])
|
||||
else:
|
||||
self._printer.text('NOTA CREDITO No. ' + sale['number'])
|
||||
elif type_doc in ['order', 'delivery']:
|
||||
|
@ -866,38 +871,40 @@ class Receipt(object):
|
|||
if not hasattr(self, '_printer') or not self._printer and self._environment != 'restaurant':
|
||||
self.set_printer()
|
||||
if not self._printer:
|
||||
self.logger.info(
|
||||
logging.info(
|
||||
"Warning: Interface not found for printer!")
|
||||
# res.append(None)
|
||||
return False
|
||||
|
||||
self.logger.info("Info: Printer is OK!")
|
||||
logging.info("Info: Printer is OK!")
|
||||
try:
|
||||
self._print_order(order, reversion)
|
||||
result = self._print_order(order, reversion)
|
||||
# if True:
|
||||
res.extend(order['lines_ids'])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_exc()
|
||||
if result:
|
||||
res.extend(result)
|
||||
elif order.get('lines_ids') and True:
|
||||
res.extend(order['lines_ids'])
|
||||
except Exception:
|
||||
logging.exception('error de impresora')
|
||||
self._printer.close()
|
||||
time.sleep(1)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
traceback.print_exc()
|
||||
self.logger.info("Warning: Can not found Printer!")
|
||||
except Exception:
|
||||
logging.exception('Can not found Printer!')
|
||||
return res
|
||||
|
||||
def _print_order(self, order, reversion):
|
||||
lines_printed = []
|
||||
for copie in range(self.order_copies):
|
||||
self.print_body_order(order, reversion)
|
||||
lines_printed = self.print_body_order(order, reversion)
|
||||
self._print_info_consumer(order)
|
||||
self._printer.cut()
|
||||
return True
|
||||
return lines_printed
|
||||
|
||||
def print_body_order(self, order, reversion):
|
||||
# Exists 2 types of order:
|
||||
# command => For restaurants kitchens
|
||||
# dispatch => For external dispatch
|
||||
lines_printed = []
|
||||
self._printer.set(font=_FONT_B)
|
||||
self._printer.set(align='center')
|
||||
turn = order.get('turn')
|
||||
|
@ -994,21 +1001,25 @@ class Receipt(object):
|
|||
|
||||
def print_lines_order(lines, group=False):
|
||||
for line in lines:
|
||||
if self.order_kind == 'command':
|
||||
self._printer.set(custom_size=True, width=1, height=2)
|
||||
qty = ' ' + str(int(Decimal(line['quantity'])))
|
||||
name = line['name']
|
||||
if group:
|
||||
name = ' ' + name
|
||||
self.print_col(qty, self.order_col_1)
|
||||
self.print_col(name, col_width_name)
|
||||
if line['note']:
|
||||
self.print_enter()
|
||||
for msg_note in line['note'].split('\n'):
|
||||
self._printer.text(f' NOTA -> {msg_note}')
|
||||
try:
|
||||
if self.order_kind == 'command':
|
||||
self._printer.set(custom_size=True, width=1, height=2)
|
||||
qty = ' ' + str(int(Decimal(line['quantity'])))
|
||||
name = line['name']
|
||||
if group:
|
||||
name = ' ' + name
|
||||
self.print_col(qty, self.order_col_1)
|
||||
self.print_col(name, col_width_name)
|
||||
if line['note']:
|
||||
self.print_enter()
|
||||
for msg_note in line['note'].split('\n'):
|
||||
self._printer.text(f' NOTA -> {msg_note}')
|
||||
self.print_enter()
|
||||
if line.get('id'):
|
||||
lines_printed.append(line['id'])
|
||||
except Exception:
|
||||
logging.exception(f'Error impresion linea de orden {str(line)}')
|
||||
self.print_enter()
|
||||
print('este es el camino de la comanda')
|
||||
self.print_enter()
|
||||
if isinstance(order['lines'], list):
|
||||
print_lines_order(order['lines'])
|
||||
|
@ -1031,6 +1042,7 @@ class Receipt(object):
|
|||
self._printer.text(str(order['comment']))
|
||||
self.print_enter()
|
||||
self._printer.ln(2)
|
||||
return lines_printed
|
||||
# if self._environment != 'retail':
|
||||
# self._printer.set(custom_size=True, width=2, height=2, align='center')
|
||||
# title = f'+ + + {_kind} + + +'
|
||||
|
@ -1043,11 +1055,13 @@ class Receipt(object):
|
|||
res = ''
|
||||
kind = auth['kind']
|
||||
start_date_auth = auth['start_date_auth']
|
||||
end_date_auth = auth['end_date_auth']
|
||||
from_auth = auth['from_auth']
|
||||
to_auth = auth['to_auth']
|
||||
number = auth['number']
|
||||
prefix = auth.get('prefix', "")
|
||||
if auth:
|
||||
res = f"Autorizacion de facturacion {kind} No {number} del {start_date_auth}, habilita desde {from_auth} a {to_auth}"
|
||||
res = f"Autorizacion de facturacion {kind} No {number} del {start_date_auth} hasta {end_date_auth}, habilita desde {prefix} {from_auth} a {prefix} {to_auth}"
|
||||
return res
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="25px" y="25px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
|
||||
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
|
||||
<g><g><g><path fill="#000000" d="M110.9,11.2c-40.7,6.2-75.2,32.7-91.4,70.2c-23,53.5-3.3,115.8,46.4,146.9c5.2,3.3,16.5,8.8,22.1,10.8c6.2,2.2,17.1,4.8,24,5.8c8.6,1.2,23.7,1.2,32.1-0.1c25.9-3.7,49.2-15.4,67.5-33.7c39.5-39.7,45.7-102,14.8-148.3c-16.8-25.1-42.2-42.8-71.1-49.7C142,10,124,9.2,110.9,11.2z M142,22.5c44.6,5.8,81.1,39.8,90.2,83.8c6.2,29.8-0.8,61.1-19.1,85.5c-3.9,5.2-13,14.6-18,18.7c-15,12.1-32.7,19.9-51.9,22.8c-7.7,1.2-22.3,1.2-30,0c-23.3-3.5-43.8-13.8-60.3-30.3c-32-32.1-40.3-81-20.4-121.5C38,70.2,43.6,62.4,52.6,53.2C69,36.6,90.1,26,113.6,22.5C120.2,21.6,134.6,21.6,142,22.5z"/><path fill="#000000" d="M92.7,75.1c-1.5,0.8-2.8,2.1-3.9,3.8c-1.5,2.4-1.6,2.8-1.7,9.5l-0.2,7l-5.7,0.3c-7.2,0.4-9.9,1.5-12.3,5.4l-1.6,2.5L67.1,130c-0.2,29.1-0.1,30.7,2.7,33.9c2.7,3.1,5,3.8,11.6,3.8H87v10.4c0,6,0.3,11.3,0.6,12.5c0.8,2.7,4.3,6.3,7.1,7.1c3,0.8,64.2,0.8,66.7,0c2.8-0.9,5.2-3.1,6.3-5.4c0.8-1.9,1-3.6,1-13.3v-11.1l6.5-0.2c6.3-0.2,6.6-0.2,8.8-1.7c1.2-0.9,2.8-2.6,3.5-3.9l1.2-2.3v-27.7c0-27.8,0-27.8-1.2-30.1c-2.4-4.7-6.3-6.3-15-6.3h-3.8v-6.9c0-7.4-0.5-9.4-3.3-12c-3-2.8-2.5-2.8-37.7-2.8H95.1L92.7,75.1z M160.1,82.5c0.9,0.9,0.9,33.1,0,34c-0.5,0.5-8.4,0.6-32.4,0.6c-30.8,0-31.8-0.1-32.3-1c-0.7-1.4-0.7-31.9,0-33.3c0.5-0.9,1.5-1,32.3-1C151.8,81.8,159.7,82,160.1,82.5z M159.8,157.2c0.9,0.5,1,1.3,1,16.2c0,11.5-0.2,15.9-0.6,16.3c-0.5,0.5-8.3,0.6-32.1,0.6c-27.9,0-31.6-0.1-32.3-0.8c-0.7-0.7-0.8-2.9-0.8-16c0-15.1,0-15.2,1.2-16c1.1-0.7,5-0.8,31.9-0.8C148.2,156.7,159.1,156.9,159.8,157.2z"/><path fill="#000000" d="M103.9,167.2v3.7h24.2h24.2v-3.7v-3.7h-24.2h-24.2V167.2z"/><path fill="#000000" d="M103.9,181.2v4h24.2h24.2v-4v-4h-24.2h-24.2V181.2z"/></g></g></g>
|
||||
</svg>
|
After Width: | Height: | Size: 2.1 KiB |
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
|
||||
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
|
||||
<g><g><path fill="#000000" d="M244.9,179.5l-40.2-35.8c-1.1-1.1-2.7-1.4-4.1-0.8c-1.4,0.6-2.3,2-2.3,3.5v19.5c-25.8,0-60.1,12.5-60.1,76.5c11.3-40.8,26.5-43.9,60.1-43.9v19.8c0,1.5,0.8,2.9,2.3,3.5c0.5,0.2,1,0.3,1.4,0.3c1,0,2-0.4,2.7-1.1l40.2-36.1C246.4,183.4,246.4,181,244.9,179.5z"/><path fill="#000000" d="M46.4,222.8c-10,0-18.2-8.2-18.2-18.2V50c0-10,8.2-18.2,18.2-18.2l57.2,0c6.6,1.7,6.5,9.7,6.5,17.8v27.7c0,5,4.1,9.1,9.1,9.1h27.3c9.1,0,18.2,0,18.2,9.1V141c5.7-2.4,11.8-4,18.2-4.8V86.4c0-9.7-9-18.9-24.9-34.5c-2.2-2.2-4.5-4.4-6.7-6.7c-2.3-2.2-4.5-4.5-6.7-6.7C129,22.6,119.7,13.6,110,13.6H46.4C26.3,13.6,10,29.9,10,50v154.6c0,20.1,16.3,36.4,36.4,36.4h90.9c-4.5-5.4-8.3-11.6-11-18.2L46.4,222.8L46.4,222.8z"/></g></g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.2 KiB |
|
@ -69,5 +69,8 @@ class StackMessages(QWidget):
|
|||
'confirm_agent': ('question', 'Confirmar comision de agente'),
|
||||
'qty_combo_min_req': ('error', 'Cantidad minima requerida!'),
|
||||
'error_server': ('error', 'Error'),
|
||||
'print_error': ('error', 'Error de Impresión')
|
||||
'print_error': ('error', 'Error de Impresión'),
|
||||
'test_printer': ('error', 'Resultado Test: '),
|
||||
'transfer_sale': ('info', 'VENTA TRANSFERIDA A: '),
|
||||
'confirm_transfer_sale': ('question', 'ESTA SEGURO QUE DESEA TRANSFERIR ESTA VENTA?')
|
||||
}
|
||||
|
|
|
@ -53,10 +53,11 @@ class StoreView(object):
|
|||
val = tr(val)
|
||||
field.setText(str(val))
|
||||
elif isinstance(field, ComboBox):
|
||||
if v:
|
||||
field.set_from_id(v)
|
||||
else:
|
||||
field.set_none()
|
||||
field.updateComboBox(v)
|
||||
# if v:
|
||||
# field.set_from_id(v)
|
||||
# else:
|
||||
# field.set_none()
|
||||
|
||||
def update(self, values):
|
||||
"""
|
||||
|
|
14
app/tools.py
14
app/tools.py
|
@ -27,3 +27,17 @@ def get_screen():
|
|||
width = screen.width()
|
||||
height = screen.height()
|
||||
return width, height
|
||||
|
||||
def compare_versions(version_validate, version_required):
|
||||
v1_parts = version_validate.split('.')
|
||||
v2_parts = version_required.split('.')
|
||||
|
||||
for i in range(max(len(v1_parts), len(v2_parts))):
|
||||
v1_num = int(v1_parts[i]) if i < len(v1_parts) else 0
|
||||
v2_num = int(v2_parts[i]) if i < len(v2_parts) else 0
|
||||
|
||||
if v1_num < v2_num:
|
||||
return "lower"
|
||||
elif v1_num > v2_num:
|
||||
return "higher"
|
||||
return 'equal'
|
||||
|
|
|
@ -1 +1 @@
|
|||
__version__ = "6.0.24"
|
||||
__version__ = "6.0.30"
|
||||
|
|
|
@ -5,6 +5,7 @@ port=8010
|
|||
mode=http
|
||||
database=DBNAME
|
||||
user=admin
|
||||
sale_device_code=001
|
||||
|
||||
#########################################
|
||||
# Printer Interface and Printer name
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
import os
|
||||
import logging
|
||||
|
||||
if os.name == 'posix':
|
||||
homex = 'HOME'
|
||||
dirconfig = '.tryton'
|
||||
temp_log = '/tmp'
|
||||
elif os.name == 'nt':
|
||||
homex = 'USERPROFILE'
|
||||
dirconfig = 'AppData/Local/tryton'
|
||||
temp_log = 'AppData/Local/Temp'
|
||||
|
||||
HOME_DIR = os.getenv(homex)
|
||||
default_dir = os.path.join(HOME_DIR, dirconfig)
|
||||
|
||||
if os.path.exists(default_dir):
|
||||
log_file_path = default_dir + '/presik_pos.log'
|
||||
else:
|
||||
log_file_path = temp_log + '/presik_pos.log'
|
||||
|
||||
log_formater = logging.Formatter(
|
||||
'%(asctime)s - %(levelname)s - %(funcName)s - %(name)s - %(message)s',
|
||||
datefmt='%Y-%m-%d %H:%M:%S', style="%")
|
||||
|
||||
|
||||
handlerConsole = logging.StreamHandler()
|
||||
handlerConsole.setFormatter(log_formater)
|
||||
|
||||
handlerFile = logging.FileHandler(log_file_path, mode='a', encoding='utf-8')
|
||||
handlerFile.setFormatter(log_formater)
|
||||
|
||||
logger = logging.getLogger()
|
||||
logger.setLevel(logging.DEBUG)
|
||||
logger.addHandler(handlerConsole)
|
||||
logger.addHandler(handlerFile)
|
46
pospro
46
pospro
|
@ -3,10 +3,13 @@
|
|||
import os
|
||||
import sys
|
||||
import argparse
|
||||
from PySide6.QtWidgets import QApplication
|
||||
# from PyQt5.QtWidgets import QApplication
|
||||
import setproctitle
|
||||
import psutil
|
||||
import ctypes
|
||||
from PySide6.QtWidgets import QApplication, QMessageBox
|
||||
from app.commons.dblogin import Login
|
||||
from app import main
|
||||
from logger_config import logger
|
||||
|
||||
parser = argparse.ArgumentParser(description='Process some integers.')
|
||||
parser.add_argument('-o', metavar="theme", help='execute argument')
|
||||
|
@ -20,15 +23,52 @@ try:
|
|||
except NameError:
|
||||
pass
|
||||
|
||||
PROCESS_NAME = "app_presik_pos"
|
||||
OS_NAME = os.name
|
||||
|
||||
|
||||
class SingleInstanceApp(QApplication):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
app_open = False
|
||||
num_opens = 0
|
||||
for process in psutil.process_iter(['pid', 'name', 'cmdline']):
|
||||
name = process.info['name']
|
||||
cmdline = process.info['cmdline']
|
||||
if name == PROCESS_NAME and OS_NAME == 'posix':
|
||||
app_open = True
|
||||
elif OS_NAME == 'nt' and isinstance(cmdline, list) and 'pospro' in cmdline:
|
||||
num_opens += 1
|
||||
|
||||
if app_open or num_opens > 1:
|
||||
logger.warning('La aplicación ya esta en ejecución')
|
||||
self.show_already_running_dialog()
|
||||
sys.exit(1)
|
||||
|
||||
def show_already_running_dialog(self):
|
||||
msg = QMessageBox()
|
||||
msg.setIcon(QMessageBox.Warning)
|
||||
msg.setText("La aplicación ya está en ejecución.")
|
||||
msg.setWindowTitle("Aplicación en ejecución")
|
||||
msg.setStandardButtons(QMessageBox.Ok)
|
||||
msg.exec()
|
||||
|
||||
|
||||
class Client(object):
|
||||
|
||||
def __init__(self, parent=None):
|
||||
self.app = QApplication(sys.argv)
|
||||
# self.app = QApplication(sys.argv)
|
||||
self.app = SingleInstanceApp(sys.argv)
|
||||
self.app.setOrganizationName("PRESIK SAS")
|
||||
self.app.setOrganizationDomain("presik.com")
|
||||
self.app.setApplicationName("SMART POS")
|
||||
self.app.setStyle("fusion")
|
||||
if OS_NAME == 'posix':
|
||||
setproctitle.setproctitle(PROCESS_NAME)
|
||||
elif OS_NAME == 'nt':
|
||||
kernel32 = ctypes.windll.kernel32
|
||||
kernel32.SetConsoleTitleW(PROCESS_NAME)
|
||||
|
||||
def init_login(self):
|
||||
_file_config = 'config_pos.ini'
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
pyusb
|
||||
pillow
|
||||
qrcode
|
||||
paramiko
|
||||
orjson
|
||||
escpos
|
||||
PySide6
|
||||
psutil
|
||||
setproctitle
|
4
setup.py
4
setup.py
|
@ -71,6 +71,8 @@ setup(
|
|||
'paramiko',
|
||||
'orjson',
|
||||
'escpos',
|
||||
'PySide6>=6.4.1'
|
||||
'PySide6>=6.4.1',
|
||||
'psutil',
|
||||
'setproctitle'
|
||||
]
|
||||
)
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import os
|
||||
from cx_Freeze import setup, Executable
|
||||
|
||||
base = None
|
||||
|
||||
# Determine the base for the current operating system
|
||||
if os.name == "posix":
|
||||
base = "Console"
|
||||
elif os.name == "nt":
|
||||
base = "Win32GUI"
|
||||
|
||||
executables = [
|
||||
Executable("pospro", base=base) # Reemplaza "pospro.py" con tu script principal
|
||||
]
|
||||
|
||||
options = {
|
||||
"build_exe": {
|
||||
"packages": [
|
||||
"tests",
|
||||
"app",
|
||||
"app.printing",
|
||||
"app.commons",
|
||||
"app.css",
|
||||
"app.locale",
|
||||
],
|
||||
"include_msvcr": True, # Incluye las bibliotecas de tiempo de ejecución de MSVC
|
||||
}
|
||||
}
|
||||
|
||||
setup(
|
||||
name="presik_pos",
|
||||
version="1.0", # Reemplaza con la versión adecuada
|
||||
description="POS Client for Tryton",
|
||||
author="Oscar Alvarez",
|
||||
author_email="gerente@presik.com",
|
||||
url="www.presik.com",
|
||||
download_url="http://www.bitbucket.org",
|
||||
options=options,
|
||||
executables=executables,
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: GNU General Public License (GPL)",
|
||||
"Operating System :: OS Independent",
|
||||
"Natural Language :: English",
|
||||
"Natural Language :: Spanish",
|
||||
"Programming Language :: Python3.10",
|
||||
"Topic :: Office/Business",
|
||||
],
|
||||
license="GPL",
|
||||
install_requires=[
|
||||
"pyusb",
|
||||
"pillow",
|
||||
"qrcode",
|
||||
"paramiko",
|
||||
"orjson",
|
||||
"escpos",
|
||||
"PySide6>=6.4.1",
|
||||
"psutil",
|
||||
"setproctitle",
|
||||
]
|
||||
)
|
Loading…
Reference in New Issue