From e55a11fb18e11c76193200f50f0236015f0dc5e3 Mon Sep 17 00:00:00 2001 From: Camilo Sarmiento Date: Tue, 28 Jul 2020 16:54:15 -0500 Subject: [PATCH] change new version --- app/buttonpad.py | 8 +- app/commons/forms.py | 44 +++ app/css/dark.css | 4 + app/dialogs.py | 660 +++++++++++++++------------------ app/image_test.jpeg | Bin 0 -> 13039 bytes app/locale/__init__.pyc | Bin 0 -> 357 bytes app/locale/i18n_es.qm | Bin 15373 -> 16674 bytes app/locale/i18n_es.ts | 785 +++++++++++++++++++++++----------------- app/mainwindow.py | 212 +++++++++-- app/reporting.py | 45 ++- config_pos.ini | 3 +- setup.py | 2 +- 12 files changed, 1016 insertions(+), 747 deletions(-) create mode 100644 app/image_test.jpeg create mode 100644 app/locale/__init__.pyc diff --git a/app/buttonpad.py b/app/buttonpad.py index b1c6670..a72a10b 100644 --- a/app/buttonpad.py +++ b/app/buttonpad.py @@ -97,7 +97,7 @@ class ButtonsStacked(QHBoxLayout): id='button_accept', parent=parent, icon=get_icon('accept'), - title='FINALIZE', + title=self.tr('FINISH'), name_style='toolbar', method='button_accept_pressed' ) @@ -105,7 +105,7 @@ class ButtonsStacked(QHBoxLayout): id='button_cash', parent=parent, icon=get_icon('cash'), - title='PAY', + title=self.tr('PAY'), name_style='toolbar', method='button_cash_pressed' ) @@ -119,7 +119,7 @@ class ButtonsStacked(QHBoxLayout): self.button_to_draft = CustomButton( id='button_to_draft', parent=parent, - title='RETURN TO SALESMAN', + title=self.tr('RETURN TO DRAFT'), icon=get_icon('draft'), name_style='toolbar', method='button_to_draft_pressed' @@ -140,7 +140,7 @@ class ButtonsStacked(QHBoxLayout): id='button_send_to_pay', icon=get_icon('draft'), parent=parent, - title='SEND TO PAY', + title=self.tr('GO TO PAY'), method='button_send_to_pay_pressed', name_style='toolbar' ) diff --git a/app/commons/forms.py b/app/commons/forms.py index 681a1c1..6a8ba3b 100644 --- a/app/commons/forms.py +++ b/app/commons/forms.py @@ -188,6 +188,50 @@ class FieldMoney(QLineEdit): pass +class FieldNumeric(QLineEdit): + + def __init__(self, obj, key, value, amount=None, digits=2, readonly=True): + super(FieldNumeric, self).__init__() + setattr(obj, 'field_' + key, self) + set_object_name(self, 'field_', value) + self.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + self.digits = 2 + self.value_changed = False + self.textEdited.connect(self.value_edited) + self._text = '0' + self.amount = 0 + self.setReadOnly(readonly) + validator = QDoubleValidator() + validator.setDecimals(2) + self.setValidator(validator) + if not amount: + self.zero() + + def __str__(self): + return self.format_text() + + # def format_text(self, text_): + # amount = float(text_) + # return "{:,}".format(round(amount, self.digits)) + + def setText(self, amount): + if not amount: + text = '' + else: + # text = self.format_text(amount) + text = amount + super(FieldNumeric, self).setText(str(text)) + + def zero(self): + self.setText(str(0)) + + def value_edited(self, amount): + self.value_changed = True + + def show(self): + pass + + class ComboBox(QComboBox): def __init__(self, obj, key, data): diff --git a/app/css/dark.css b/app/css/dark.css index ecedebd..8a8141f 100644 --- a/app/css/dark.css +++ b/app/css/dark.css @@ -4,6 +4,10 @@ background-color: #424242; } +#label_input { + color : #424242; +} + QListView { font: bold 46px; color: #424242; diff --git a/app/dialogs.py b/app/dialogs.py index 1fd453b..49eb1ea 100644 --- a/app/dialogs.py +++ b/app/dialogs.py @@ -1,87 +1,94 @@ from .commons.dialogs import HelpDialog, QuickDialog -from PyQt5.QtCore import Qt, QThread, pyqtSignal, QRect, QDate -from PyQt5.QtWidgets import (QLabel, QTextEdit, QHBoxLayout, QVBoxLayout, - QWidget, QGridLayout, QLineEdit, QDoubleSpinBox, QDateEdit) -from .constants import (PATH_PRINTERS, DELTA_LOCALE, STRETCH, alignRight, - alignLeft, alignCenter, alignHCenter, alignVCenter, DIALOG_REPLY_NO, - DIALOG_REPLY_YES, ZERO, FRACTIONS, RATE_CREDIT_LIMIT, SCREENS, FILE_BANNER, - CONVERSION_DIGITS) -from .commons.forms import GridForm, FieldMoney, ComboBox +from PyQt5.QtCore import QRect +from PyQt5.QtWidgets import (QWidget, QLabel, QTextEdit, QVBoxLayout, + QGridLayout, QLineEdit, QDoubleSpinBox, QDateEdit) +from .constants import (alignCenter, FRACTIONS) +from .commons.forms import FieldMoney, ComboBox from .commons.search_window import SearchWindow from collections import OrderedDict from .manage_tables import ManageTables -from decimal import Decimal -from .tools import get_icon, to_float, to_numeric -class SearchSale(SearchWindow): +class SearchSale(QWidget): def __init__(self, parent): + super(SearchSale, self).__init__(parent) + self._parent = parent + + def get(self): headers = OrderedDict() - headers['id'] = {'desc': parent.tr('ID'), 'type': 'char'} - headers['number'] = {'desc': parent.tr('NUMBER'), 'type': 'char'} - headers['invoice_number'] = {'desc': parent.tr('INVOICE'), 'type': 'char'} - headers['party.name'] = {'desc': parent.tr('PARTY'), 'type': 'char'} - headers['sale_date'] = {'desc': parent.tr('DATE'), 'type': 'char'} - headers['salesman.name'] = {'desc': parent.tr('SALESMAN'), 'type': 'char'} - headers['position'] = {'desc': parent.tr('POSITION'), 'type': 'char'} - headers['total_amount_cache'] = {'desc': parent.tr('TOTAL AMOUNT'), 'type': 'number'} + headers['id'] = {'desc': self.tr('ID'), 'type': 'char'} + headers['number'] = {'desc': self.tr('NUMBER'), 'type': 'char'} + headers['invoice_number'] = {'desc': self.tr('INVOICE'), 'type': 'char'} + headers['party.name'] = {'desc': self.tr('PARTY'), 'type': 'char'} + headers['sale_date'] = {'desc': self.tr('DATE'), 'type': 'char'} + headers['salesman.name'] = {'desc': self.tr('SALESMAN'), 'type': 'char'} + headers['position'] = {'desc': self.tr('POSITION'), 'type': 'char'} + headers['total_amount_cache'] = {'desc': self.tr('TOTAL AMOUNT'), 'type': 'number'} widths = [20, 100, 150, 100, 90, 150, 100, 100] - title = parent.tr('SEARCH SALES...') + title = self.tr('SEARCH SALES...') methods = { 'on_selected_method': 'on_selected_sale', 'on_return_method': 'on_selected_sale' } - super(SearchSale, self).__init__(parent, headers, None, methods, filter_column=[1, 2, 3, 4], - cols_width=widths, title=title, fill=True) + return SearchWindow(self._parent, headers, None, methods, filter_column=[1, 2, 3, 4], + cols_width=widths, title=title, fill=True) -class SearchParty(SearchWindow): +class SearchParty(QWidget): def __init__(self, parent): - headers = OrderedDict() - headers['id'] = {'desc': parent.tr('ID'), 'type': 'char'} - headers['id_number'] = {'desc': parent.tr('ID NUMBER'), 'type': 'char'} - headers['name'] = {'desc': parent.tr('NAME'), 'type': 'char'} - headers['street'] = {'desc': parent.tr('ADDRESS'), 'type': 'char'} - headers['phone'] = {'desc': parent.tr('PHONE'), 'type': 'char'} + super(SearchParty, self).__init__(parent) + self._parent = parent - title = parent.tr('SEARCH CUSTOMER') + def get(self): + headers = OrderedDict() + headers['id'] = {'desc': self.tr('ID'), 'type': 'char'} + headers['id_number'] = {'desc': self.tr('ID NUMBER'), 'type': 'char'} + headers['name'] = {'desc': self.tr('NAME'), 'type': 'char'} + headers['street'] = {'desc': self.tr('ADDRESS'), 'type': 'char'} + headers['phone'] = {'desc': self.tr('PHONE'), 'type': 'char'} + + title = self.tr('SEARCH CUSTOMER') methods = { 'on_selected_method': 'on_selected_party', 'on_return_method': 'on_search_party', } - super(SearchParty, self).__init__(parent, headers, None, methods, - filter_column=[], cols_width=[60, 120, 270, 190, 90], - title=title, fill=True) + return SearchWindow(self._parent, headers, None, methods, + filter_column=[], cols_width=[60, 120, 270, 190, 90], + title=title, fill=True) -class SearchProduct(SearchWindow): +class SearchProduct(QWidget): def __init__(self, parent): + super(SearchProduct, self).__init__(parent) + self._parent = parent + + def get(self): _cols_width = [10, 80] headers = OrderedDict() - headers['id'] = {'desc': parent.tr('ID'), 'type': 'char'} - headers['code'] = {'desc': parent.tr('CODE'), 'type': 'char'} - if parent._config.get('show_stock_pos') in ['icon', 'value']: - headers['quantity'] = {'desc': parent.tr('STOCK'), 'type': 'char'} - if parent._config['show_stock_pos'] == 'icon': + headers['id'] = {'desc': self.tr('ID'), 'type': 'char'} + headers['code'] = {'desc': self.tr('CODE'), 'type': 'char'} + if self._parent._config.get('show_stock_pos') in ['icon', 'value']: + headers['quantity'] = {'desc': self.tr('STOCK'), 'type': 'char'} + if self._parent._config['show_stock_pos'] == 'icon': headers['quantity']['icon'] = 'stock' headers['quantity']['type'] = 'icon' _cols_width.append(60) - headers['name'] = {'desc': parent.tr('NAME'), 'type': 'char'} + headers['name'] = {'desc': self.tr('NAME'), 'type': 'char'} _cols_width.append(350) - if parent._config.get('show_description_pos'): - headers['description'] = {'desc': parent.tr('DESCRIPTION'), 'type': 'char'} + if self._parent._config.get('show_description_pos'): + headers['description'] = {'desc': self.tr('DESCRIPTION'), 'type': 'char'} _cols_width.append(300) - if parent._config.get('show_brand'): - headers['template.brand'] = {'desc': parent.tr('BRAND'), 'type': 'char'} + if self._parent._config.get('show_brand'): + headers['template.brand'] = {'desc': self.tr('BRAND'), 'type': 'char'} _cols_width.append(100) - price = {'desc': parent.tr('PRICE'), 'type': 'number'} - if not parent._config.get('encoded_sale_price'): + price = {'desc': self.tr('PRICE'), 'type': 'number'} + if not self._parent._config.get('encoded_sale_price'): headers['template.sale_price_w_tax'] = price else: price['type'] = 'char' @@ -89,244 +96,206 @@ class SearchProduct(SearchWindow): _cols_width.append(100) - if parent._config.get('show_location_pos'): - headers['location.name'] = {'desc': parent.tr('LOCATION'), 'type': 'char'} + if self._parent._config.get('show_location_pos'): + headers['location.name'] = {'desc': self.tr('LOCATION'), 'type': 'char'} _cols_width.append(100) - if parent._config['show_product_image']: - headers['image'] = {'desc': parent.tr('IMAGE'), 'icon': 'image', 'type': 'icon'} + if self._parent._config['show_product_image']: + headers['image'] = {'desc': self.tr('IMAGE'), 'icon': 'image', 'type': 'icon'} _cols_width.append(30) methods = { 'on_selected_method': 'on_selected_product', 'on_return_method': 'on_search_product', - 'image': parent.on_selected_icon_product, - 'quantity': parent.on_selected_stock_product + 'image': self._parent.on_selected_icon_product, + 'quantity': self._parent.on_selected_stock_product } - - super(SearchProduct, self).__init__(parent, headers, None, methods, - cols_width=_cols_width, fill=True) + return SearchWindow(self._parent, headers, None, methods, + cols_width=_cols_width, fill=True) -class DialogManageTables(QuickDialog): - def __init__(self, parent, kind): - if not parent._Tables: +class DialogManageTables(QWidget): + def __init__(self, parent): + super(DialogManageTables, self).__init__(parent) + self._parent = parent + + def get(self, kind): + if not self._Tables: return - tables = parent._Tables.find([ - ('shop', '=', parent.shop['id']) + tables = self._Tables.find([ + ('shop', '=', self.shop['id']) ]) - _tables = ManageTables(parent, tables, self.action_table_assigned) - super(DialogManageTables, self).__init__(parent, kind, widgets=[_tables]) + _tables = ManageTables(self._parent, tables, self.action_table_assigned) + return QuickDialog(self._parent, kind, widgets=[_tables]) - def action_table_assigned(self, id, name, prev_state, new_state): - table_assigned = id +class DialogConsumer(QWidget): + def __init__(self, parent): + super(DialogConsumer, self).__init__(parent) + self._parent = parent - if self.parent._sale.get('table_assigned') != id and prev_state == 'occupied': - return False - - if self.parent._sale.get('table_assigned') == id and new_state == 'available': - name = '' - table_assigned = None - - self.parent._sale['table_assigned'] = table_assigned - self.parent._sale['table_assigned.state'] = new_state - - self.parent._Tables.write([id], {'state': new_state}) - - self.parent._PosSale.write([self.parent._sale['id']], {'table_assigned': table_assigned}) - self.parent.field_table_assigned.setText(name) - return True - - -class DialogConsumer(QuickDialog): - def __init__(self, parent, kind): - parent.state_consumer = {} - parent._consumer = None + def get(self, kind): + self._parent.state_consumer = {} + self._parent._consumer = None vbox_consumer = QVBoxLayout() grid = QGridLayout() - label_phone = QLabel(parent.tr('PHONE:')) + label_phone = QLabel(self.tr('PHONE:')) label_phone.setObjectName('label_phone') grid.addWidget(label_phone, 1, 1) - parent.row_field_phone = QLineEdit() - parent.row_field_phone.setObjectName('row_field_phone') - parent.row_field_phone.editingFinished.connect( - lambda: self.update_consumer_data('phone') + self._parent.row_field_phone = QLineEdit() + self._parent.row_field_phone.setObjectName('row_field_phone') + self._parent.row_field_phone.editingFinished.connect( + lambda: self._parent.update_consumer_data('phone') ) - grid.addWidget(parent.row_field_phone, 1, 2) + grid.addWidget(self._parent.row_field_phone, 1, 2) - label_consumer = QLabel(parent.tr('CONSUMER:')) + label_consumer = QLabel(self.tr('CONSUMER:')) label_consumer.setObjectName('label_consumer') grid.addWidget(label_consumer, 2, 1) - parent.row_field_consumer = QLineEdit() - parent.row_field_consumer.setObjectName('row_field_consumer') - parent.row_field_consumer.textChanged.connect( - lambda: self.update_consumer_data('name') + self._parent.row_field_consumer = QLineEdit() + self._parent.row_field_consumer.setObjectName('row_field_consumer') + self._parent.row_field_consumer.textChanged.connect( + lambda: self._parent.update_consumer_data('name') ) - grid.addWidget(parent.row_field_consumer, 2, 2) + grid.addWidget(self.row_field_consumer, 2, 2) - label_address = QLabel(parent.tr('ADDRESS:')) + label_address = QLabel(self.tr('ADDRESS:')) label_address.setObjectName('label_address') grid.addWidget(label_address, 3, 1) - parent.row_field_address = QLineEdit() - parent.row_field_address.setObjectName('row_field_address') - parent.row_field_address.textChanged.connect( - lambda: self.update_consumer_data('address') + self._parent.row_field_address = QLineEdit() + self._parent.row_field_address.setObjectName('row_field_address') + self._parent.row_field_address.textChanged.connect( + lambda: self._parent.update_consumer_data('address') ) - grid.addWidget(parent.row_field_address, 3, 2) + grid.addWidget(self.row_field_address, 3, 2) - label_id_number = QLabel(parent.tr('ID NUMBER:')) + label_id_number = QLabel(self.tr('ID NUMBER:')) label_id_number.setObjectName('label_id_number') grid.addWidget(label_id_number, 4, 1) - parent.row_field_id_number = QLineEdit() - parent.row_field_id_number.setObjectName('row_field_id_number') - parent.row_field_id_number.textChanged.connect( - lambda: self.update_consumer_data('id_number') + self._parent.row_field_id_number = QLineEdit() + self._parent.row_field_id_number.setObjectName('row_field_id_number') + self._parent.row_field_id_number.textChanged.connect( + lambda: self._parent.update_consumer_data('id_number') ) - grid.addWidget(parent.row_field_id_number, 4, 2) + grid.addWidget(self._parent.row_field_id_number, 4, 2) - label_birthday = QLabel(parent.tr('BIRTHDAY:')) + label_birthday = QLabel(self.tr('BIRTHDAY:')) label_birthday.setObjectName('label_birthday') grid.addWidget(label_birthday, 5, 1) item_date = QDateEdit() item_date.setGeometry(QRect(120, 18, 140, 18)) item_date.setDisplayFormat('dd/MM/yyyy') item_date.setCalendarPopup(True) - parent.row_field_birthday = item_date - parent.row_field_birthday.setObjectName('row_field_birthday') - parent.row_field_birthday.dateTimeChanged.connect( - lambda: self.update_consumer_data('birthday') + self._parent.row_field_birthday = item_date + self._parent.row_field_birthday.setObjectName('row_field_birthday') + self._parent.row_field_birthday.dateTimeChanged.connect( + lambda: self._parent.update_consumer_data('birthday') ) - grid.addWidget(parent.row_field_birthday, 5, 2) + grid.addWidget(self._parent.row_field_birthday, 5, 2) vbox_consumer.addLayout(grid) - super(DialogConsumer, self).__init__(parent, kind, widgets=[vbox_consumer]) - self.setWindowTitle('CONSUMER') - self.accepted.connect(self.dialog_search_consumer_accepted) + dialog = QuickDialog(self._parent, kind, widgets=[vbox_consumer]) + dialog.setWindowTitle('CONSUMER') + dialog.accepted.connect(self._parent.dialog_search_consumer_accepted) - def clear_dialog(self): - self.parent.row_field_phone.setText('') - self.parent.row_field_address.setText('') - self.parent.row_field_consumer.setText('') - self.parent.row_field_id_number.setText('') - # self.row_field_birthday.setDate(QDate.currentDate()) - - def dialog_search_consumer_accepted(self): - if not self.parent.state_consumer: - return - if self.parent._consumer: - res = self.parent.ModSale.update_consumer({ - 'id': self.parent._consumer['id'], - 'fields': self.parent.state_consumer, - }) - if res['msg'] != 'ok': - print('error') - else: - res = self.parent.ModSale.create_consumer({ - 'fields': self.parent.state_consumer, - }) - if res['msg'] != 'ok': - print('error') - - def update_consumer_data(self, field=''): - if field == 'address': - self.parent.state_consumer['address'] = self.parent.row_field_address.text() - elif field == 'id_number': - self.parent.state_consumer['id_number'] = self.parent.row_field_id_number.text() - elif field == 'name': - self.parent.state_consumer['name'] = self.parent.row_field_consumer.text() - elif field == 'birthday': - self.parent.state_consumer['birthday'] = self.parent.row_field_birthday.date().toPyDate() - elif field == 'phone': - self.parent._text_field_phone = self.parent.row_field_phone.text() - if self.parent._text_field_phone: - self.parent.state_consumer['phone'] = self.parent._text_field_phone - consumers = self.parent.PartyConsumer.find([ - ('phone', '=', self.parent._text_field_phone), - ]) - if not consumers: - self.parent._consumer = {} - self.parent.row_field_address.setText('') - self.parent.row_field_consumer.setText('') - self.parent.row_field_id_number.setText('') - self.parent.row_field_birthday.setDate() - else: - self.parent._consumer = consumers[0] - self.parent.row_field_address.setText(self.parent._consumer['address']) - self.parent.row_field_consumer.setText(self.parent._consumer['name']) - self.parent.row_field_id_number.setText(self.parent._consumer['id_number']) - if self.parent._consumer.get('birthday'): - y, m, d = self.parent._consumer['birthday'].split('-') - self.parent.row_field_birthday.setDate(QDate(int(y), int(m), int(d))) + return dialog -class DialogAgent(QuickDialog): - def __init__(self, parent, kind): +class DialogAgent(QWidget): + def __init__(self, parent): + super(DialogAgent, self).__init__(parent) + self._parent = parent + + def get(self, kind): view = [ ('agent_ask', { - 'name': parent.tr('AGENT'), + 'name': self.tr('AGENT'), 'type': 'relation', - 'model': parent.Agent, + 'model': self.Agent, 'domain': [], 'fields': [ - ('id', parent.tr('ID')), - ('party.rec_name', parent.tr('NAME')), - ('party.id_number', parent.tr('ID NUMBER')), + ('id', self.tr('ID')), + ('party.rec_name', self.tr('NAME')), + ('party.id_number', self.tr('ID NUMBER')), ]}), - ('commission_ask', {'name': parent.tr('COMMISSION')}), - ('commission_amount', {'name': parent.tr('AMOUNT'), 'readonly': True}), + ('commission_ask', {'name': self.tr('COMMISSION')}), + ('commission_amount', {'name': self.tr('AMOUNT'), 'readonly': True}), ] - super(DialogAgent, self).__init__(parent, kind, data=view) + return QuickDialog(self._parent, kind, data=view) -class DialogCancelInvoice(QuickDialog): - def __init__(self, parent, kind): +class DialogCancelInvoice(QWidget): + def __init__(self, parent): + super(DialogCancelInvoice, self).__init__(parent) + self._parent = parent + + def get(self, kind): view = [ ('password_for_cancel_ask', { - 'name': parent.tr('INSERT PASSWORD FOR CANCEL'), + 'name': self.tr('INSERT PASSWORD FOR CANCEL'), 'password': True }), ] - super(DialogCancelInvoice, self).__init__(parent, kind, data=view) + return QuickDialog(self._parent, kind, data=view) -class DialogForceAssing(QuickDialog): - def __init__(self, parent, kind): +class DialogForceAssign(QWidget): + def __init__(self, parent): + super(DialogForceAssign, self).__init__(parent) + self._parent = parent + + def get(self, kind): field = 'password_force_assign_ask' - data = {'name': parent.tr('PASSWORD FORCE ASSIGN')} - super(DialogForceAssing, self).__init__(parent, kind, data=[(field, data)]) + data = {'name': self.tr('PASSWORD FORCE ASSIGN')} + return QuickDialog(self._parent, kind, data=[(field, data)]) -class DialogOrder(QuickDialog): - def __init__(self, parent, kind): - string = parent.tr('DO YOU WANT TO CONFIRM THE SEND ORDER?') - super(DialogOrder, self).__init__(parent, kind, string, data=[]) +class DialogOrder(QWidget): + def __init__(self, parent): + super(DialogOrder, self).__init__(parent) + self._parent = parent + + def get(self, kind): + string = self.tr('DO YOU WANT TO CONFIRM THE SEND ORDER?') + return QuickDialog(self._parent, kind, string, data=[]) -class Dialogstock(QuickDialog): - def __init__(self, parent, kind): +class DialogStock(QWidget): + def __init__(self, parent): + super(DialogStock, self).__init__(parent) + self._parent = parent + + def get(self, kind): data = { 'name': 'stock', 'values': [], - 'heads': [parent.tr('WAREHOUSE'), parent.tr('QUANTITY')], + 'heads': [self.tr('WAREHOUSE'), self.tr('QUANTITY')], } - label = parent.tr('STOCK BY PRODUCT:') - super(Dialogstock, self).__init__(parent, kind, label, data, readonly=True) + label = self.tr('STOCK BY PRODUCT:') + return QuickDialog(self._parent, kind, label, data, readonly=True) -class DialogGlobalDiscount(QuickDialog): - def __init__(self, parent, kind): +class DialogGlobalDiscount(QWidget): + def __init__(self, parent): + super(DialogGlobalDiscount, self).__init__(parent) + self._parent = parent + + def get(self, kind): field = 'global_discount_ask' - data = {'name': parent.tr('GLOBAL DISCOUNT')} - super(DialogGlobalDiscount, self).__init__(parent, kind, data=[(field, data)]) + data = {'name': self.tr('GLOBAL DISCOUNT')} + return QuickDialog(self._parent, kind, data=[(field, data)]) -class DialogPrintInvoice(QuickDialog): - def __init__(self, parent, kind): +class DialogPrintInvoice(QWidget): + def __init__(self, parent): + super(DialogPrintInvoice, self).__init__(parent) + self._parent = parent + + def get(self, kind): view = [ - ('invoice_number_ask', {'name': parent.tr('INVOICE NUMBER')}), + ('invoice_number_ask', {'name': self.tr('INVOICE NUMBER')}), ('printer_ask', { - 'name': parent.tr('PRINTER'), + 'name': self.tr('PRINTER'), 'type': 'selection', 'values': [ (1, 'POS'), @@ -334,236 +303,193 @@ class DialogPrintInvoice(QuickDialog): ], }), ('type_ask', { - 'name': parent.tr('TYPE'), + 'name': self.tr('TYPE'), 'type': 'selection', 'values': [ - ('invoice', parent.tr('INVOICE')), - ('order', parent.tr('ORDER')) + ('invoice', self.tr('INVOICE')), + ('order', self.tr('ORDER')) ], }), ] - super(DialogPrintInvoice, self).__init__(parent, kind, data=view) + return QuickDialog(self._parent, kind, data=view) +class DialogVoucher(QWidget): + def __init__(self, parent): + super(DialogVoucher, self).__init__(parent) + self._parent = parent -class DialogVoucher(QuickDialog): - def __init__(self, parent, kind): + def get(self, kind): field = 'voucher_ask' - data = {'name': parent.tr('VOUCHER NUMBER')} - super(DialogVoucher, self).__init__(parent, kind, data=[(field, data)]) + data = {'name': self.tr('VOUCHER NUMBER')} + return QuickDialog(self._parent, kind, data=[(field, data)]) +class DialogSalesman(QWidget): + def __init__(self, parent): + super(DialogSalesman, self).__init__(parent) + self._parent = parent -class DialogSalesman(QuickDialog): - def __init__(self, parent, kind): + def get(self, kind): data = { 'name': 'salesman', 'values': [(str(e['id']), e['party']['name']) - for e in parent.employees], - 'heads': [parent.tr('Id'), parent.tr('Salesman')], + for e in self._parent.employees], + 'heads': [self.tr('Id'), self.tr('Salesman')], } - string = parent.tr('CHOOSE SALESMAN') - super(DialogSalesman, self).__init__(parent, kind, string, data) + string = self.tr('CHOOSE SALESMAN') + return QuickDialog(self._parent, kind, string, data) -class DialogTaxes(QuickDialog): - def __init__(self, parent, kind): - if parent.shop_taxes: - taxes = [(str(e['id']), e['name']) for e in parent.shop_taxes] +class DialogTaxes(QWidget): + def __init__(self, parent): + super(DialogTaxes, self).__init__(parent) + self._parent = parent + + def get(self, kind): + if self._parent.shop_taxes: + taxes = [(str(e['id']), e['name']) for e in self._parent.shop_taxes] else: taxes = [] data = { 'name': 'tax', 'values': taxes, - 'heads': [parent.tr('Id'), parent.tr('Salesman')], + 'heads': [self.tr('Id'), self.tr('Salesman')], } - string = parent.tr('CHOOSE TAX') - super(DialogTaxes, self).__init__(parent, kind, string, data) + string = self.tr('CHOOSE TAX') + return QuickDialog(self._parent, kind, string, data) -class DialogPaymentTerm(QuickDialog): - def __init__(self, parent, kind): +class DialogPaymentTerm(QWidget): + def __init__(self, parent): + super(DialogPaymentTerm, self).__init__(parent) + self._parent = parent + + def get(self, kind): data = { 'name': 'payment_term', - 'values': [(p_id, parent._payment_terms[p_id]['name']) - for p_id in parent._payment_terms], - 'heads': [parent.tr('ID'), parent.tr('PAYMENT TERM')], + 'values': [(p_id, self._parent._payment_terms[p_id]['name']) + for p_id in self._parent._payment_terms], + 'heads': [self.tr('ID'), self.tr('PAYMENT TERM')], } - string = parent.tr('SELECT PAYMENT TERM') - super(DialogPaymentTerm, self).__init__(parent, kind, string, data) + string = self.tr('SELECT PAYMENT TERM') + return QuickDialog(self._parent, kind, string, data) -class DialogPayment(QuickDialog): - def __init__(self, parent, kind): +class DialogPayment(QWidget): + def __init__(self, parent): + super(DialogPayment, self).__init__(parent) + self._parent = parent + + def get(self, kind): data = { 'name': 'journal', - 'values': sorted([(str(j), parent._journals[j]['name']) - for j in parent._journals]), - 'heads': [parent.tr('ID'), parent.tr('PAYMENT MODE:')], + 'values': sorted([(str(j), self._parent._journals[j]['name']) + for j in self._parent._journals]), + 'heads': [self.tr('ID'), self.tr('PAYMENT MODE:')], } - string = parent.tr('SELECT PAYMENT MODE:') - super(DialogPayment, self).__init__(parent, kind, string, data) + string = self.tr('SELECT PAYMENT MODE:') + return QuickDialog(self._parent, kind, string, data) -class Position(QuickDialog): - def __init__(self, parent, kind): +class Position(QWidget): + def __init__(self, parent): + super(Position, self).__init__(parent) + self._parent = parent + + def get(self, kind): _field = 'position_ask' - _data = {'name': parent.tr('POSITION')} - super(Position, self).__init__(parent, kind, data=[(_field, _data)]) + _data = {'name': self.tr('POSITION')} + return QuickDialog(self._parent, kind, data=[(_field, _data)]) -class Comment(QuickDialog): - def __init__(self, parent, kind): +class Comment(QWidget): + def __init__(self, parent): + super(Comment, self).__init__(parent) + self._parent = parent + + def get(self, kind): _field = 'comment_ask' - _data = {'name': parent.tr('COMMENTS'), 'widget': 'text'} - super(Comment, self).__init__(parent, kind, data=[(_field, _data)]) + _data = {'name': self.tr('COMMENTS'), 'widget': 'text'} + return QuickDialog(self._parent, kind, data=[(_field, _data)]) -class SaleLine(QuickDialog): - def __init__(self, parent, kind): - parent.state_line = {} +class SaleLine(QWidget): + def __init__(self, parent): + super(SaleLine, self).__init__(parent) + self._parent = parent + def get(self, kind): + self._parent.state_line = {} vbox_product = QVBoxLayout() grid = QGridLayout() qty = 2 - parent.label_product = QLabel() - parent.label_product.setAlignment(alignCenter) - parent.label_product.setObjectName('label_product') - vbox_product.addWidget(parent.label_product) - parent.row_field_description = QLineEdit() - parent.row_field_description.setObjectName('row_field_description') - parent.row_field_description.textChanged.connect( - lambda: self.update_sale_line('description') + self._parent.label_product = QLabel() + self._parent.label_product.setAlignment(alignCenter) + self._parent.label_product.setObjectName('label_product') + vbox_product.addWidget(self._parent.label_product) + self._parent.row_field_description = QLineEdit() + self._parent.row_field_description.setObjectName('row_field_description') + self._parent.row_field_description.textChanged.connect( + lambda: self._parent.update_sale_line('description') ) - grid.addWidget(parent.row_field_description, 1, 1, 1, 2) + grid.addWidget(self._parent.row_field_description, 1, 1, 1, 2) - if parent._config.get('show_fractions'): - label_fraction = QLabel(parent.tr('FRACTION:')) + if self._parent._config.get('show_fractions'): + label_fraction = QLabel(self.tr('FRACTION:')) label_fraction.setObjectName('label_fraction') grid.addWidget(label_fraction, 2, 1) - parent.field_combobox_fraction = ComboBox(parent, 'fraction', + self._parent.field_combobox_fraction = ComboBox(self._parent, 'fraction', {'values': FRACTIONS}) - grid.addWidget(parent.field_combobox_fraction, 2, 2) - parent.field_combobox_fraction.currentIndexChanged.connect( - lambda: self.update_sale_line('qty_fraction') + grid.addWidget(self._parent.field_combobox_fraction, 2, 2) + self._parent.field_combobox_fraction.currentIndexChanged.connect( + lambda: self._parent.update_sale_line('qty_fraction') ) - label_qty = QLabel(parent.tr('QUANTITY:')) + label_qty = QLabel(self.tr('QUANTITY:')) label_qty.setObjectName('label_qty') grid.addWidget(label_qty, 3, 1) - parent.row_field_qty = QDoubleSpinBox() - parent.row_field_qty.setObjectName('row_field_qty') - parent.row_field_qty.setMinimum(0) - parent.row_field_qty.setMaximum(100000) - if parent._config.get('decimals_digits_quantity'): - qty = parent._config['decimals_digits_quantity'] + self._parent.row_field_qty = QDoubleSpinBox() + self._parent.row_field_qty.setObjectName('row_field_qty') + self._parent.row_field_qty.setMinimum(0) + self._parent.row_field_qty.setMaximum(100000) + if self._parent._config.get('decimals_digits_quantity'): + qty = self._parent._config['decimals_digits_quantity'] - parent.row_field_qty.setDecimals(qty) - parent.row_field_qty.setAlignment(alignCenter) - grid.addWidget(parent.row_field_qty, 3, 2) - parent.row_field_qty.valueChanged.connect( - lambda: self.update_sale_line('quantity') + self._parent.row_field_qty.setDecimals(qty) + self._parent.row_field_qty.setAlignment(alignCenter) + grid.addWidget(self._parent.row_field_qty, 3, 2) + self._parent.row_field_qty.valueChanged.connect( + lambda: self._parent.update_sale_line('quantity') ) - label_price = QLabel(parent.tr('UNIT PRICE:')) + label_price = QLabel(self.tr('UNIT PRICE:')) label_price.setObjectName('label_price') grid.addWidget(label_price, 4, 1) - parent.row_field_price = FieldMoney(parent, 'row_field_price', {}, readonly=False) - parent.row_field_price.setObjectName('row_field_price') - grid.addWidget(parent.row_field_price, 4, 2) - parent.row_field_price.textChanged.connect( - lambda: self.update_sale_line('unit_price') + self._parent.row_field_price = FieldMoney(self, 'row_field_price', {}, readonly=False) + self._parent.row_field_price.setObjectName('row_field_price') + grid.addWidget(self._parent.row_field_price, 4, 2) + self._parent.row_field_price.textChanged.connect( + lambda: self._parent.update_sale_line('unit_price') ) - parent.row_field_note = QTextEdit('') - parent.row_field_note.setObjectName('row_field_note') - grid.addWidget(parent.row_field_note, 5, 1, 5, 2) - parent.row_field_note.textChanged.connect( - lambda: self.update_sale_line('note') + self._parent.row_field_note = QTextEdit('') + self._parent.row_field_note.setObjectName('row_field_note') + grid.addWidget(self._parent.row_field_note, 5, 1, 5, 2) + self._parent.row_field_note.textChanged.connect( + lambda: self._parent.update_sale_line('note') ) - vbox_product.addLayout(grid) - super(SaleLine, self).__init__(parent, kind, widgets=[vbox_product]) - self.accepted.connect(self.dialog_product_edit_accepted) - - def update_sale_line(self, field): - value = None - self.parent.state_line['id'] = self.parent._current_line_id - if field == 'quantity': - value = Decimal(self.parent.row_field_qty.value()) - if field == 'unit_price': - value = self.parent.row_field_price.text() - if field == 'qty_fraction': - qty = self.parent.field_combobox_fraction.get_id() - self.parent.row_field_qty.setValue(float(qty)) - value = self.parent.field_combobox_fraction.get_label() - self.parent.state_line['quantity'] = qty - price_ = self.parent.ModSale.get_product_prices({ - 'ids': [self.parent._sale_line['product']['id']], - 'quantity': float(qty), - 'sale_id': self.parent._sale['id'], - }) - if price_ and price_.get('unit_price_w_tax'): - price_list = str(price_['unit_price_w_tax']) - self.parent.row_field_price.setText(price_list) - self.parent.state_line['unit_price'] = price_list - - if field == 'description': - value = self.parent.row_field_description.text() - if field == 'note': - value = self.parent.row_field_note.toPlainText() - - if value: - self.parent.state_line[field] = value - - def dialog_product_edit_accepted(self): - if not self.parent.state_line: - return - - _record = None - - if self.parent.state_line.get('quantity'): - quantity = self.parent.state_line.pop('quantity') - _record = self.parent.ModSaleLine.faster_set_quantity({ - 'id': self.parent._current_line_id, - 'quantity': to_float(quantity, 2) - }) - - if self.parent.state_line.get('unit_price'): - unit_price = self.parent.state_line.pop('unit_price') - self.parent._sign = '/' - self.parent._process_price(unit_price) - self.parent._sign = None - _record = self.parent.ModSaleLine.write([self.parent._current_line_id], {}) - - if self.parent.state_line.get('description'): - _record = self.parent.ModSaleLine.write([self.parent._current_line_id], { - 'description': self.parent.state_line['description'] - }) - - if self.parent.state_line.get('note'): - _record = self.parent.ModSaleLine.write([self.parent._current_line_id], { - 'note': self.parent.state_line['note'] - }) - - if _record: - if not _record.get('unit.symbol'): - _record['unit.symbol'] = _record['unit']['symbol'] - if not _record.get('product.template.name'): - _record['product.template.name'] = _record['product']['template']['name'] - if not _record.get('product.code'): - _record['product.code'] = _record['product']['code'] - self.parent.model_sale_lines.update_record(_record) - - self.parent.update_total_amount() - self.parent.state_line = {} + dialog = QuickDialog(self._parent, kind, widgets=[vbox_product]) + dialog.accepted.connect(self._parent.dialog_product_edit_accepted) + return dialog class Help(HelpDialog): def __init__(self, parent): - super(Help, self).__init__(parent) + super(Help, self).__init__(self) shortcuts = [ (self.tr('HELP'), 'F1'), (self.tr('SEARCH PRODUCT'), 'F2'), diff --git a/app/image_test.jpeg b/app/image_test.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..d711bf511ee19470ff558b1274ce48f04387cf29 GIT binary patch literal 13039 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 literal 0 HcmV?d00001 diff --git a/app/locale/__init__.pyc b/app/locale/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..38759db1b87c8103800ab4e43ebe3e20e83b86e7 GIT binary patch literal 357 zcmYLEO;5ux40S(XXuzExz@ZlwI3RIA2&9!0YtdFA(Tf#UqouS>)C45X`$7CTegHV7 z6PEM*UgT#d(f9H6>+}TIFh?07?J?r~|0O9f@`%2HJyd;Ij&08$w*} zLpkxzRTA+O^}~_5iKiD=9gaBnLD$R9CamBetYj%Rx+PK32Q}JIFIFV|EHO`?(ue$Uk!FP`md{0= zjpgieb}sIhc~+#^d?idzR=uIoR!C#S-*SPdI^_^&=5u)9b*roQhHe7hX#E$D!e95a BRJ;HH literal 0 HcmV?d00001 diff --git a/app/locale/i18n_es.qm b/app/locale/i18n_es.qm index 0c47c5404ee7bbae1617cfb1e1bf0f64c6fe4719..7d849d251bc523d6afc595b99ef0ae0f1d406129 100644 GIT binary patch delta 3231 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 delta 2244 zcmYjQc~n$&6#gFb-kUdXX5Ik905XcK!!iRS10)T@5=x2&#{&$aq>1I8RG_G&P9%E3 zQlm+&j#6M~i(8S^@!&3IcE}W$EZb;Tk7#KwnWu|S-t@{ff zJctOU5Z!l_l43Ib*MW$Ue;2n5MXV5hEyC|JWM9ix)+K-|1R4kAZ4fovngdf9Q zMMQp;D1UMjF!%t<`zJMbqTEXb({z|H`cY~q39VmV1_D-~y{sM>>cSCC2M}0*&p)jM zjDInj^`vfO1Cu&^DG;LeFr{<3=tvXAMhOY6uF?k2ze^kZvv{K>URH}blhzrzl4;N!)ym>9;yJ8p?-WS^PnW8zelkgxtvb6s0EmfTBexjoLK&OU9tB`4`{4X$ z!1Ojdbqpz>t&uTN%}%c-iG!YEXG|vI_%e2G2Tks{#n!~eQ)9hsZQKTWU(MF-B!0on zE}lw~>q^dzQX7u4>zdlhL{r!+lTH9pB75Z`nJ6rsVVjsxnCF zK`k<-{KPGqz7STZD!BUEK9cZbZu!%+TU9gH_)Z;QyUaDsJ_ICGbDQJN0~*e|N)_%= zNAY3<4Qwux(eWG~$-hhk_43hkx(RJEhK`YOST7&rM;qe$_>6rMNuV+O)Ds8D^TYX> zLjOG9^UJ4RC;A6ulmx!<0C`l*;x`7EXs*NjHtSx}rSTqmV2kBVukxhvgRvdZJei*&NIZO{C;m< zl2P9yu6>3iW-KytpNMw2Er&Ut^DxLTx(JuWteB^*YPmiKI}j?p4oC>eRSGcje>qMu5V1gtZKj>;^itZE)zoOA zl$n)IjmF8Cyh$3RYA5AxNDGTTBxNF`Ln%dcFIXzQSWFFs2TE7npOGoDq^pk+v9#6T zw|fM6;dO&)CJ{@02J>y&q40XC-m=^-@2E_hnt!^odRo;y?+5y1wmYE@ojOO>|85}=1sI746v2%`b84WX34*)ohCr zCkVbfOVmTWIXR+o_M0?Eu3O`ut0d2dIF;Dn|$LF8>fRc|h bMkw)nPG6nXCZC+!f)We^+iRqi_%Y0X$7W$f diff --git a/app/locale/i18n_es.ts b/app/locale/i18n_es.ts index 468215e..24bda49 100644 --- a/app/locale/i18n_es.ts +++ b/app/locale/i18n_es.ts @@ -97,6 +97,256 @@ CONSUMIDOR + + ButtonsStacked + + + FINISH + FINALIZAR + + + + PAY + PAGAR + + + + RETURN TO DRAFT + DEVOLVER A BORRADOR + + + + GO TO PAY + IR A PAGAR + + + + Comment + + + COMMENTS + COMENTARIOS + + + + DialogAgent + + + AGENT + AGENTE + + + + ID + ID + + + + NAME + NOMBRE + + + + ID NUMBER + NUMERO ID + + + + COMMISSION + COMISIÓN + + + + AMOUNT + VALOR + + + + DialogCancelInvoice + + + INSERT PASSWORD FOR CANCEL + INGRESE LA CONTRASEÑA PARA CANCELAR + + + + DialogConsumer + + + PHONE: + TELEFONO + + + + CONSUMER: + CONSUMIDOR + + + + ADDRESS: + DIRECCIÓN + + + + ID NUMBER: + NÚMERO ID + + + + BIRTHDAY: + CUMPLEAÑOS + + + + DialogForceAssign + + + PASSWORD FORCE ASSIGN + CONTRASEÑA PARA FORZAR ASIGNACIÓN + + + + DialogGlobalDiscount + + + GLOBAL DISCOUNT + DESCUENTO GLOBAL + + + + DialogOrder + + + DO YOU WANT TO CONFIRM THE SEND ORDER? + DESEAS CONFIRMAR EL ENVIO DE LA ORDEN? + + + + DialogPayment + + + ID + ID + + + + PAYMENT MODE: + MEDIO DE PAGO: + + + + SELECT PAYMENT MODE: + SELECCIONE EL MEDIO DE PAGO: + + + + DialogPaymentTerm + + + ID + ID + + + + PAYMENT TERM + PLAZO DE PAGO + + + + SELECT PAYMENT TERM + SELECCIONE EL MODO DE PAGO + + + + DialogPrintInvoice + + + INVOICE NUMBER + NUMERO DE FACTURA + + + + PRINTER + IMPRESORA + + + + TYPE + TIPO + + + + INVOICE + FACTURA + + + + ORDER + ENV. ORDEN + + + + DialogSalesman + + + Id + Id + + + + Salesman + Vendedor + + + + CHOOSE SALESMAN + ESCOGE EL VENDEDOR + + + + DialogStock + + + WAREHOUSE + BODEGA + + + + QUANTITY + CANTIDAD + + + + STOCK BY PRODUCT: + INVENTARIO POR PRODUCTO: + + + + DialogTaxes + + + Id + Id + + + + Salesman + Vendedor + + + + CHOOSE TAX + ESCOJA EL IMPUESTO + + + + DialogVoucher + + + VOUCHER NUMBER + NÚMERO DE VOUCHER + + FrontWindow @@ -108,82 +358,82 @@ Help - + HELP AYUDA - + SEARCH PRODUCT BUSCAR PRODUCTO - + PAYMENT MODE MODO DE PAGO - + SEARCH CUSTOMER BUSCAR CLIENTE - + GLOBAL DISCOUNT DESCUENTO GLOBAL - + PRINT ORDER IMPRIMIR ORDEN - + PRINT INVOICE IMPRIMIR FACTURA - + PAYMENT TERM PLAZO DE PAGO - + SEARCH SALES BUSCAR VENTAS - + ACTIVATE TABLE ACTIVAR TABLA - + NEW SALE NUEVA VENTA - + CANCEL SALE CANCELAR VENTA - + SALESMAN VENDEDOR - + POSITION POSICIÓN - + CASH PAGO - + COMMENT COMENTARIO @@ -387,147 +637,147 @@ NO PUEDE FORZAR UNA ASIGACIÓN! - + INVOICE FACTURA - + DATE FECHA - + SALESMAN VENDEDOR - + PAYMENT TERM PLAZO DE PAGO - + No ORDER No PEDIDO - + POSITION POSICION - + AGENT AGENTE - + DELIVERY CHARGE CARGO DOMICILIO - + SUBTOTAL SUBTOTAL - + TAXES IMPUESTOS - + DISCOUNT DESCUENTO - + TOTAL TOTAL - + PAID PAGADO - + CHANGE CAMBIO - + SHOP TIENDA - + DEVICE CAJA - + DATABASE BD - + USER USUARIO - + PRINTER IMPRESORA - + NAME NOMBRE - + DESCRIPTION DESCRIPCIÓN - + AMOUNT VALOR - + COD COD - + UNIT UND - + QTY CANT - + DISC DESC - + NOTE NOTA - + UNIT PRICE W TAX PRECIO UNIT CON IMP - + STATEMENT JOURNAL ESTADO DE CUENTA @@ -557,17 +807,17 @@ EL CLIENTE NO TIENE CREDITO! - + CUSTOMER CLIENTE - + COMPANY COMPAÑIA - + ASSIGNED TABLE MESA ASIGNADA @@ -577,7 +827,7 @@ PRIMERO DEBE AGREGAR/CARGAR UNA VENTA! - + FRAC FRAC @@ -592,17 +842,17 @@ FALLO FINALIZACIÓN DE FACTURA° - + STATE ESTADO - + PENDIENTE - + VOUCHER COMPROBANTE @@ -617,22 +867,22 @@ NO HAY VENTA - + TYPE INVOICE: TIPO DE FACTURA - + DELIVERY MEN DOMICILIARIO - + DELIVERY TIME TIEMPO DE ENTREGA - + DELIVERY WAY FORMA DE ENTREGA @@ -655,6 +905,14 @@ &REGRESAR + + Position + + + POSITION + POSICIÓN + + QuickDialog @@ -698,6 +956,24 @@ Dialogo... + + SaleLine + + + FRACTION: + FRACCIÓN: + + + + QUANTITY: + CANTIDAD: + + + + UNIT PRICE: + PRECIO UNITARIO: + + SearchDialog @@ -706,6 +982,135 @@ Buscar Productos + + SearchParty + + + ID + ID + + + + ID NUMBER + NUMERO ID + + + + NAME + NOMBRE + + + + ADDRESS + DIRECCION + + + + PHONE + TELÉFONO + + + + SEARCH CUSTOMER + BUSCAR CLIENTE + + + + SearchProduct + + + ID + ID + + + + CODE + CÓDIGO + + + + STOCK + INVENTARIO + + + + NAME + NOMBRE + + + + DESCRIPTION + DESCRIPCIÓN + + + + BRAND + MARCA + + + + PRICE + PRECIO + + + + LOCATION + LOCACIÓN + + + + IMAGE + IMAGEN + + + + SearchSale + + + ID + ID + + + + NUMBER + NÚMERO + + + + INVOICE + FACTURA + + + + PARTY + CLIENTE + + + + DATE + FECHA + + + + SALESMAN + VENDEDOR + + + + POSITION + POSICIÓN + + + + TOTAL AMOUNT + VALOR TOTAL + + + + SEARCH SALES... + BUSCAR VENTAS... + + SearchWindow @@ -727,272 +1132,4 @@ INGRESA TU CONTRASEÑA - - parent - - - ID - ID - - - - NUMBER - NÚMERO - - - - INVOICE - FACTURA - - - - PARTY - CLIENTE - - - - DATE - FECHA - - - - SALESMAN - VENDEDOR - - - - POSITION - POSICION - - - - TOTAL AMOUNT - VALOR TOTAL - - - - SEARCH SALES... - BUSCAR VENTAS... - - - - ID NUMBER - NUMERO ID - - - - NAME - NOMBRE - - - - ADDRESS - DIRECCION - - - - PHONE - TELÉFONO - - - - SEARCH CUSTOMER - BUSCAR CLIENTE - - - - CODE - CÓDIGO - - - - STOCK - INVENTARIO - - - - DESCRIPTION - DESCRIPCIÓN - - - - BRAND - MARCA - - - - PRICE - PRECIO - - - - LOCATION - LOCACIÓN - - - - IMAGE - IMAGEN - - - - PHONE: - TELÉFONO - - - - CONSUMER: - CONSUMIDOR - - - - ADDRESS: - DIRECCIÓN - - - - ID NUMBER: - ID NUMBER - - - - BIRTHDAY: - CUMPLEAÑOS - - - - AGENT - AGENTE - - - - COMMISSION - COMISIÓN - - - - AMOUNT - VALOR - - - - INSERT PASSWORD FOR CANCEL - INGRESE LA CONTRASEÑA PARA CANCELAR - - - - PASSWORD FORCE ASSIGN - CONTRASEÑA PARA FORZAR ASIGNACIÓN - - - - DO YOU WANT TO CONFIRM THE SEND ORDER? - DESEAS CONFIRMAR EL ENVIO DE LA ORDEN? - - - - WAREHOUSE - BODEGA - - - - QUANTITY - CANTIDAD - - - - STOCK BY PRODUCT: - INVENTARIO POR PRODUCTO: - - - - GLOBAL DISCOUNT - DESCUENTO GLOBAL - - - - INVOICE NUMBER - NUMERO DE FACTURA - - - - PRINTER - IMPRESORA - - - - TYPE - TIPO - - - - ORDER - ENV. ORDEN - - - - VOUCHER NUMBER - NÚMERO DE VOUCHER - - - - Id - Id - - - - Salesman - Vendedor - - - - CHOOSE SALESMAN - ESCOGE EL VENDEDOR - - - - CHOOSE TAX - ESCOJA EL IMPUESTO - - - - PAYMENT TERM - PLAZO DE PAGO - - - - SELECT PAYMENT TERM - SELECCIONE EL MODO DE PAGO - - - - PAYMENT MODE: - MEDIO DE PAGO: - - - - SELECT PAYMENT MODE: - SELECCIONE EL MEDIO DE PAGO: - - - - COMMENTS - COMENTARIOS - - - - FRACTION: - FRACCIÓN: - - - - QUANTITY: - CANTIDAD: - - - - UNIT PRICE: - PRECIO UNITARIO: - - diff --git a/app/mainwindow.py b/app/mainwindow.py index da46e87..115b87a 100644 --- a/app/mainwindow.py +++ b/app/mainwindow.py @@ -4,20 +4,20 @@ import sys import os import logging from decimal import Decimal -from .dialogs import (Position, Comment, SearchParty, DialogPayment, - DialogSalesman, DialogVoucher, DialogPrintInvoice, - DialogGlobalDiscount, DialogPaymentTerm, SearchSale, - Dialogstock, DialogOrder, DialogForceAssing, DialogTaxes, - DialogCancelInvoice, SaleLine, DialogAgent, Help, - DialogConsumer, DialogManageTables, SearchProduct) +from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDate +from .tools import get_icon, to_float, to_numeric +from .dialogs import (Help, SearchSale, SearchParty, SearchProduct, Position, + Comment, DialogPayment, DialogSalesman, DialogVoucher, DialogPrintInvoice, + DialogGlobalDiscount, DialogPaymentTerm, DialogStock, DialogOrder, + DialogForceAssign, DialogTaxes, DialogCancelInvoice, SaleLine, DialogAgent, + DialogConsumer, DialogManageTables) from datetime import datetime, timedelta, date from collections import OrderedDict -from PyQt5.QtCore import Qt, QThread, pyqtSignal # from PyQt5.QtGui import QTouchEvent from PyQt5.QtWidgets import (QLabel, QHBoxLayout, QVBoxLayout, QWidget, QGridLayout, QLineEdit) from app.commons.action import Action -from app.commons.forms import GridForm, FieldMoney, ComboBox +from app.commons.forms import GridForm, ComboBox, FieldNumeric from app.commons.messages import MessageBar from app.commons.image import Image from app.commons.table import TableView @@ -29,7 +29,6 @@ from .localdb import LocalStore from .reporting import Receipt from .buttonpad import Buttonpad from .states import STATES, RE_SIGN -from .tools import get_icon, to_numeric from .constants import (PATH_PRINTERS, DELTA_LOCALE, STRETCH, alignRight, alignLeft, alignCenter, alignHCenter, alignVCenter, DIALOG_REPLY_NO, DIALOG_REPLY_YES, ZERO, @@ -49,6 +48,7 @@ class MainWindow(FrontWindow): super(MainWindow, self).__init__(connection, params, title) print('Screen Size: > ', self.screen_size) _theme = params['theme'] if params.get('theme') else None + self.profile_printer = params['profile_printer'] if params.get('profile_printer') else 'TM-P80' self.set_style(SCREENS[self.screen_size], _theme) self.is_clear_right_panel = True @@ -395,30 +395,30 @@ class MainWindow(FrontWindow): def create_dialogs(self): # self.create_wizard_new_sale() - self.dialog_search_products = SearchProduct(self) - self.dialog_position = Position(self, 'action') - self.dialog_comment = Comment(self, 'action') - self.dialog_search_parties = SearchParty(self) - self.dialog_payment = DialogPayment(self, 'selection') - self.dialog_salesman = DialogSalesman(self, 'selection') - self.dialog_voucher = DialogVoucher(self, 'action') - self.dialog_print_invoice = DialogPrintInvoice(self, 'action') - self.dialog_global_discount = DialogGlobalDiscount(self, 'action') - self.dialog_payment_term = DialogPaymentTerm(self, 'selection') - self.dialog_search_sales = SearchSale(self) + self.dialog_search_sales = SearchSale(self).get() + self.dialog_search_parties = SearchParty(self).get() + self.dialog_search_products = SearchProduct(self).get() + self.dialog_position = Position(self).get('action') + self.dialog_comment = Comment(self).get('action') + self.dialog_payment = DialogPayment(self).get('selection') + self.dialog_salesman = DialogSalesman(self).get('selection') + self.dialog_voucher = DialogVoucher(self).get('action') + self.dialog_print_invoice = DialogPrintInvoice(self).get('action') + self.dialog_global_discount = DialogGlobalDiscount(self).get('action') + self.dialog_payment_term = DialogPaymentTerm(self).get('selection') self.dialog_search_sales.activate_counter() - self.dialog_product_stock = Dialogstock(self.dialog_search_products, 'selection') - self.dialog_order = DialogOrder(self, 'action') - self.dialog_force_assign = DialogForceAssing(self, 'action') + self.dialog_product_stock = DialogStock(self.dialog_search_products).get('selection') + self.dialog_order = DialogOrder(self).get('action') + self.dialog_force_assign = DialogForceAssign(self).get('action') self.field_password_force_assign_ask.setEchoMode(QLineEdit.Password) - self.dialog_tax = DialogTaxes(self, 'selection') - self.dialog_cancel_invoice = DialogCancelInvoice(self, 'action') - self.dialog_product_edit = SaleLine(self, 'action') + self.dialog_tax = DialogTaxes(self).get('selection') + self.dialog_cancel_invoice = DialogCancelInvoice(self).get('action') + self.dialog_product_edit = SaleLine(self).get('action') if self._commission_activated: - self.dialog_agent = DialogAgent(self, 'action') + self.dialog_agent = DialogAgent(self).get('action') if self.enviroment == 'restaurant' and self._sale_pos_restaurant: - self.dialog_search_consumer = DialogConsumer(self, 'action') - self.dialog_manage_tables = DialogManageTables(self, 'action') + self.dialog_search_consumer = DialogConsumer(self).get('action') + self.dialog_manage_tables = DialogManageTables(self).get('action') def set_printing_context(self): # Printing invoice context @@ -445,6 +445,7 @@ class MainWindow(FrontWindow): printer = { 'interface': self.printer_sale_name[0], 'device': self.printer_sale_name[1], + 'profile': self.profile_printer, } if printer: self.receipt_sale.set_printer(printer) @@ -490,7 +491,7 @@ class MainWindow(FrontWindow): _label_type_invoice.setObjectName('label_invoice') _label_type_invoice.setAlignment(alignRight | alignVCenter) - self.field_amount = FieldMoney(self, 'amount', {}) + self.field_amount = FieldNumeric(self, 'amount', {}) self.field_amount.setObjectName('field_amount') self.field_sign = QLabel(' ') self.field_sign.setObjectName('field_sign') @@ -1011,6 +1012,7 @@ class MainWindow(FrontWindow): if not sale_id: sale_id = self._sale['id'] data = self._PosSale.get_data(sale_id, self._context) + print(data['qr_code']) for i in range(copies): self.receipt_sale.print_sale(data) @@ -1760,6 +1762,7 @@ class MainWindow(FrontWindow): self.dialog_search_consumer.clear_dialog() self.check_empty_sale() if self.type_pos_user == 'cashier': + self.message_bar.set('not_sale') self.state_disabled() return self.set_state('add') @@ -2264,13 +2267,158 @@ class MainWindow(FrontWindow): self.action_comment() elif key == Qt.Key_Question: self.action_tax() - sale = self.get_current_sale() - self.field_state.setText(sale['state']) @property def state(self): return self._state + def update_sale_line(self, field): + value = None + self.state_line['id'] = self._current_line_id + if field == 'quantity': + value = Decimal(self.row_field_qty.value()) + if field == 'unit_price': + value = self.row_field_price.text() + if field == 'qty_fraction': + qty = self.field_combobox_fraction.get_id() + self.row_field_qty.setValue(float(qty)) + value = self.field_combobox_fraction.get_label() + self.state_line['quantity'] = qty + price_ = self.ModSale.get_product_prices({ + 'ids': [self._sale_line['product']['id']], + 'quantity': float(qty), + 'sale_id': self._sale['id'], + }) + if price_ and price_.get('unit_price_w_tax'): + price_list = str(price_['unit_price_w_tax']) + self.row_field_price.setText(price_list) + self.state_line['unit_price'] = price_list + + if field == 'description': + value = self.row_field_description.text() + if field == 'note': + value = self.row_field_note.toPlainText() + + if value: + self.state_line[field] = value + + def dialog_product_edit_accepted(self): + if not self.state_line: + return + + _record = None + + if self.state_line.get('quantity'): + quantity = self.state_line.pop('quantity') + _record = self.ModSaleLine.faster_set_quantity({ + 'id': self._current_line_id, + 'quantity': to_float(quantity, 2) + }) + + if self.state_line.get('unit_price'): + unit_price = self.state_line.pop('unit_price') + self._sign = '/' + self._process_price(unit_price) + self._sign = None + _record = self.ModSaleLine.write([self._current_line_id], {}) + + if self.state_line.get('description'): + _record = self.ModSaleLine.write([self._current_line_id], { + 'description': self.state_line['description'] + }) + + if self.state_line.get('note'): + _record = self.ModSaleLine.write([self._current_line_id], { + 'note': self.state_line['note'] + }) + + if _record: + if not _record.get('unit.symbol'): + _record['unit.symbol'] = _record['unit']['symbol'] + if not _record.get('product.template.name'): + _record['product.template.name'] = _record['product']['template']['name'] + if not _record.get('product.code'): + _record['product.code'] = _record['product']['code'] + self.model_sale_lines.update_record(_record) + + self.update_total_amount() + self.state_line = {} + + def dialog_search_consumer_accepted(self): + if not self.state_consumer: + return + if self._consumer: + res = self.ModSale.update_consumer({ + 'id': self._consumer['id'], + 'fields': self.state_consumer, + }) + if res['msg'] != 'ok': + print('error') + else: + res = self.ModSale.create_consumer({ + 'fields': self.state_consumer, + }) + if res['msg'] != 'ok': + print('error') + + def update_consumer_data(self, field=''): + if field == 'address': + self.state_consumer['address'] = self.row_field_address.text() + elif field == 'id_number': + self.state_consumer['id_number'] = self.row_field_id_number.text() + elif field == 'name': + self.state_consumer['name'] = self.row_field_consumer.text() + elif field == 'birthday': + self.state_consumer['birthday'] = self.row_field_birthday.date().toPyDate() + elif field == 'phone': + self._text_field_phone = self.row_field_phone.text() + if self._text_field_phone: + self.state_consumer['phone'] = self._text_field_phone + consumers = self.PartyConsumer.find([ + ('phone', '=', self._text_field_phone), + ]) + if not consumers: + self._consumer = {} + self.row_field_address.setText('') + self.row_field_consumer.setText('') + self.row_field_id_number.setText('') + self.row_field_birthday.setDate() + else: + self._consumer = consumers[0] + self.row_field_address.setText(self._consumer['address']) + self.row_field_consumer.setText(self._consumer['name']) + self.row_field_id_number.setText(self._consumer['id_number']) + if self._consumer.get('birthday'): + y, m, d = self._consumer['birthday'].split('-') + self.row_field_birthday.setDate(QDate(int(y), int(m), int(d))) + + + def action_table_assigned(self, id, name, prev_state, new_state): + table_assigned = id + + if self._sale.get('table_assigned') != id and prev_state == 'occupied': + return False + + if self._sale.get('table_assigned') == id and new_state == 'available': + name = '' + table_assigned = None + + self._sale['table_assigned'] = table_assigned + self._sale['table_assigned.state'] = new_state + + self._Tables.write([id], {'state': new_state}) + + self._PosSale.write([self._sale['id']], {'table_assigned': table_assigned}) + self.field_table_assigned.setText(name) + return True + + def clear_dialog(self): + self.row_field_phone.setText('') + self.row_field_address.setText('') + self.row_field_consumer.setText('') + self.row_field_id_number.setText('') + # self.row_field_birthday.setDate(QDate.currentDate()) + # def print_equivalent_invoice(self, sale_id): # sale, = self.ModSale.find([('id', '=', sale_id)]) diff --git a/app/reporting.py b/app/reporting.py index 01273fe..7c79d18 100755 --- a/app/reporting.py +++ b/app/reporting.py @@ -14,11 +14,12 @@ try: except: logging.warning("Pyudev module not found!") -try: +if 1: from escpos import printer -except: - logging.warning("Escpos module not found!") - + # from escpos.printer import File +# except: +# logging.warning("Escpos module not found!") +# from escpos.printer import File try: import cups except: @@ -125,7 +126,7 @@ class Receipt(object): def test_printer(self): if self._interface == 'usb': if os.name == 'posix': - self._printer = printer.File(self._device) + self._printer = printer.File(self._device, profile=self._profile) elif os.name == 'nt': self._printer = printer.UsbWin(self._device) self._printer.open() @@ -136,16 +137,15 @@ class Receipt(object): self._printer.open() if not self._printer: return - if self._img_logo: - self.print_logo() self.print_enter() + self._printer.image('image_test.jpeg', center=True) self.print_enter() self.print_header() self.print_enter() self.print_enter() + self.print_enter() self._printer.cut() self._printer.cashdraw(2) - self._printer.close() def set_printer(self, printer): if dev_printers.get(printer['device']): @@ -155,12 +155,13 @@ class Receipt(object): self._interface = printer['interface'] self._device = device + self._profile = printer['profile'] def print_sale(self, sale): try: if self._interface == 'usb': if os.name == 'posix': - self._printer = printer.File(self._device) + self._printer = printer.File(self._device, profile=self._profile) elif os.name == 'nt': self._printer = printer.UsbWin(self._device) self._printer.open() @@ -184,18 +185,25 @@ class Receipt(object): def _print_sale(self, sale): self.print_header() self.print_body(sale) + if sale.get('cufe'): + self._printer.text('CUFE: ' + sale['cufe']) + if sale.get('qr_code'): + self.print_qrcode(sale['qr_code']) self.print_footer() # self.print_extra_info(sale) - if self._interface in ['usb', 'ssh', 'network']: - self._printer.close() - elif self._interface == 'cups': + if self._interface == 'cups': self._file.close() self.conn.printFile(self._printer_name, TEMP_INVOICE_FILE, 'POS Invoice', {}) def print_logo(self): - self._printer.set(align='center') - self._printer.image(self._img_logo) + # self._printer.set(align='center') + self._printer.image(self._img_logo, center=True) + self.print_enter() + + def print_qrcode(self, qrcode): + # self._printer.set(align='center') + self._printer.qr(qrcode, center=True, size=5) self.print_enter() def print_header(self): @@ -264,6 +272,8 @@ class Receipt(object): #mod_hours = sale["create_date"] + timedelta(hours=self._delta_locale) #time_ = mod_hours.strftime('%I:%M %p') self._printer.text('Fecha:%s' % sale['date']) + if sale.get('invoice_time'): + self._printer.text(' Hora:%s' % sale['invoice_time']) if sale.get('turn') and sale['turn'] != 0: self._printer.text('Turno: %s - ' % str(sale['turn'])) self.print_enter() @@ -472,8 +482,6 @@ class Receipt(object): self.print_body_order(order, reversion) self._printer.cut() self._row_characters = order['row_characters'] - if order['interface'] in ('network', 'usb', 'ssh'): - self._printer.close() return True def print_body_order(self, order, reversion): @@ -615,10 +623,10 @@ if __name__ == '__main__': # Test for Escpos interface printer Linux # Network example - device = 'network', '192.168.0.32' + # device = 'network', '192.168.0.32' # Unix-like Usb example - # device = 'usb','/dev/usb/lp1' + device = 'usb','/dev/usb/lp1' # Windows Usb example for printer nameb SATPOS # device = 'usb', 'SATPOS' @@ -629,6 +637,7 @@ if __name__ == '__main__': example_dev = { 'interface': device[0], 'device': device[1], + 'profile': 'TM-P80', } ctx_printing = {} diff --git a/config_pos.ini b/config_pos.ini index 236dd16..ffac1e7 100644 --- a/config_pos.ini +++ b/config_pos.ini @@ -19,10 +19,11 @@ user=admin # ########################################## printer_sale_name=usb,/dev/usb/lp0 +profile_printer=TM-P80 # ROW CHARACTERS: 33 / 48 / 28 # --- EPSON TM-T20 = 33 -# --- TALLY DT-230 = 48 +# --- SAT 23T = 48 # --- SAT = 42 row_characters=48 diff --git a/setup.py b/setup.py index bf84f73..71ff8c7 100644 --- a/setup.py +++ b/setup.py @@ -34,7 +34,7 @@ if os.name == 'posix': setup(name='presik_pos', - version='5.0.2', + version='5.0.3', description='POS Client for Tryton', author='Oscar Alvarez', author_email='gerente@presik.com',