Compare commits

...

36 Commits

Author SHA1 Message Date
Wilson Gomez 418856a116 fix print data in invoice 2023-11-08 14:52:15 -05:00
Wilson Gomez c36e7277d5 add prefix in print authorization 2023-11-07 16:07:09 -05:00
Wilson Gomez 3d6ab678fe minor fix read config file path 2023-11-07 08:54:43 -05:00
Wilson Gomez 9c8618207b minor fix 2023-11-07 08:25:05 -05:00
Wilson Gomez 0d4fecd578 add option for read and config app 2023-11-06 13:18:24 -05:00
Wilson Gomez 299528fa97 add option payment with sale_device 2023-11-06 09:17:18 -05:00
Wilson Gomez 5777776a30 minor fix printer default 2023-11-05 14:11:10 -05:00
Wilson Gomez 81e1be2211 minor fix print info consumer when
delivery_amount is None
2023-11-03 21:31:37 -05:00
Wilson Gomez 0ac16c993e minor fix 2023-11-03 17:30:32 -05:00
Wilson Gomez f3b49f5a3f refactory printer receip_sale 2023-11-02 16:22:50 -05:00
Wilson Gomez ef61961792 realse version 6.0.28 2023-11-02 13:08:08 -05:00
Wilson Gomez 19d28390e6 option for transfer sale to other sale 2023-11-02 13:07:47 -05:00
Wilson Gomez 372b706ade fix when error conection network 2023-10-28 15:45:44 -05:00
Wilson Gomez cde0f93520 add file requirements 2023-10-28 11:54:40 -05:00
Wilson Gomez 1180a750ee add button for transfer sales to folios
release version 6.0.26
2023-10-28 11:45:09 -05:00
Wilson Gomez 42de1961f0 fix change invoice_type 2023-10-27 16:32:48 -05:00
Wilson Gomez e915d72c13 minor fix 2023-10-27 13:33:05 -05:00
Wilson Gomez 30f6bc25f4 minor fix to request in network failed 2023-10-27 13:23:29 -05:00
Wilson Gomez 48829a0a10 minor fix print info consumer
add button transfer sale_to_folio for hotels
2023-10-26 14:33:30 -05:00
Wilson Gomez fa16cab0ca minor fix 2023-10-25 18:52:57 -05:00
Wilson Gomez 8fabce1d6a fix info consumer for invoice and command 2023-10-25 18:14:58 -05:00
Wilson Gomez 4f64c01b99 minor fix 2023-10-25 17:36:33 -05:00
Wilson Gomez f81774c446 minor fix delivery amount 2023-10-25 12:32:38 -05:00
Wilson Gomez f8ec07c3ce minor fix 2023-10-25 12:16:38 -05:00
Wilson Gomez aac950797a minor fix exec 2023-10-25 12:10:55 -05:00
Wilson Gomez d735ffccde add option for test printers 2023-10-25 12:00:37 -05:00
Wilson Gomez e4cbbd8995 add option for test printers 2023-10-25 10:29:13 -05:00
Wilson Gomez e4eb03b4b7 minor fix change save log 2023-10-24 12:22:04 -05:00
Wilson Gomez 4ac3de04dd configure setup for build app 2023-10-23 16:30:03 -05:00
Wilson Gomez d7b5d1ce58 minor fix 2023-10-23 15:08:59 -05:00
Wilson Gomez 554911ca39 fix validate open app in platform nt 2023-10-21 12:26:40 -05:00
Wilson Gomez e58740f557 minor fix 2023-10-21 12:13:23 -05:00
Wilson Gomez 5b6e44c827 validate os 2023-10-21 11:47:53 -05:00
Wilson Gomez 853997bfd5 restrict open application multiple times 2023-10-21 11:18:07 -05:00
Wilson Gomez 543385d0fa new release version add option debug 2023-10-20 16:43:40 -05:00
Wilson Gomez c9b2f22b29 add field version in fields for model ir.module 2023-10-19 09:11:21 -05:00
29 changed files with 1105 additions and 516 deletions

1
.gitignore vendored
View File

@ -21,6 +21,7 @@
npm-debug.log*
yarn-debug.log*
yarn-error.log*
*.log
package-lock*

View File

@ -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:

View File

@ -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.

View File

@ -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):

View File

@ -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",

View File

@ -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):

View File

@ -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)}")

View File

@ -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)

View File

@ -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)

View File

@ -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'),
]

View File

@ -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'])

View File

@ -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()

View File

@ -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')

View File

@ -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'
]

View File

@ -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

View File

@ -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

7
app/share/test_print.svg Normal file
View File

@ -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

View File

@ -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

View File

@ -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?')
}

View File

@ -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):
"""

View File

@ -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'

View File

@ -1 +1 @@
__version__ = "6.0.24"
__version__ = "6.0.30"

View File

@ -5,6 +5,7 @@ port=8010
mode=http
database=DBNAME
user=admin
sale_device_code=001
#########################################
# Printer Interface and Printer name

35
logger_config.py Normal file
View File

@ -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
View File

@ -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'

9
requirements.txt Normal file
View File

@ -0,0 +1,9 @@
pyusb
pillow
qrcode
paramiko
orjson
escpos
PySide6
psutil
setproctitle

View File

@ -71,6 +71,8 @@ setup(
'paramiko',
'orjson',
'escpos',
'PySide6>=6.4.1'
'PySide6>=6.4.1',
'psutil',
'setproctitle'
]
)

63
setup_freeze.py Normal file
View File

@ -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",
]
)

0
tests/__init__.py Normal file
View File