#!/usr/bin/env python # -*- coding: UTF-8 -*- import sys import os import logging from decimal import Decimal 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, QTextEdit, QHBoxLayout, QVBoxLayout, QWidget, QGridLayout, QLineEdit, QDoubleSpinBox) from neox.commons.action import Action from neox.commons.forms import GridForm, FieldMoney, ComboBox from neox.commons.messages import MessageBar from neox.commons.image import Image from neox.commons.dialogs import QuickDialog from neox.commons.table import TableView from neox.commons.model import TableModel, Modules from neox.commons.search_window import SearchWindow from neox.commons.frontwindow import FrontWindow from neox.commons.menu_buttons import MenuDash from .proxy import FastModel from .localdb import LocalStore from .reporting import Receipt from .buttonpad import Buttonpad from .manage_tables import ManageTables from .states import STATES, RE_SIGN from .common import get_icon, to_float, to_numeric 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) class MainWindow(FrontWindow): def __init__(self, connection, params): title = "PRESIK | SMART POS" global CONNECTION self.conn = connection CONNECTION = connection super(MainWindow, self).__init__(connection, params, title) print('Screen Size: > ', self.screen_size) self.set_style(SCREENS[self.screen_size]) self.is_clear_right_panel = True self.payment_ctx = {} self.set_keys() self.stock_context = None self.ctx = self._context self.ctx['params'] = params response = self.load_modules() if response is not True: d = self.dialog(response) d.exec_() super(MainWindow, self).close() return self.setup_sale_line() self.setup_payment() self.set_domains() self.create_gui() self.message_bar.load_stack(self.stack_msg) if not hasattr(self, 'auto_print_commission'): self.auto_print_commission = False self.active_usb_printers = [] if os.name == 'posix' and os.path.exists(PATH_PRINTERS): self.set_printers_usb(PATH_PRINTERS) self.set_printing_context() if not self.tablet_mode: self.create_statusbar() self.window().showMaximized() self.create_dialog_search_products() if not self.tablet_mode: self.grabKeyboard() self.reader_thread = None self._current_line_id = None self._amount_text = '' self._sign = None self.create_dialogs() self.createNewSale() if not hasattr(self, 'active_weighing'): self.active_weighing = False elif self.active_weighing is True: from .electronic_scale import ScaleReader self.reader_thread = ScaleReader() self.reader_thread.sigSetWeight.connect(self.set_weight_readed) self.do_invoice = DoInvoice(self, self._context) self.do_invoice.sigDoInvoice.connect(self.__do_invoice_thread) self.set_cache_company() self.set_cache_products() 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 filter_cache(self, data, filter, target): res = [] for d in data: for t in target: if t in d[filter]: res.append(d) return res def set_cache_company(self): self.store = LocalStore() self.store.create_table_config() self._local_config = self.store.get_config() if not self._local_config: company_id = self.device['shop']['company']['id'] self._local_config = self.store.set_config([company_id]) def set_cache_products(self): self.store.create_table_product() local_products = self.store.get_local_products() config = self.store.get_config() _sync_date = config[1] products = self.Product.sync_get_products({'write_date': _sync_date, 'shop_id': self.ctx['shop']}) self.store.update_products(products, local_products) now = datetime.now() self.store.set_config_sync(str(now)) def event(self, evento): event_type = super(MainWindow, self).event(evento) touch = QTouchEvent(event_type) return event_type def set_printers_usb(self, PATH_PRINTERS): for usb_dev in os.listdir(PATH_PRINTERS): if 'lp' not in usb_dev: continue path_device = os.path.join(PATH_PRINTERS, usb_dev) self.active_usb_printers.append(['usb', path_device]) def get_current_sale(self): if hasattr(self, '_sale') and self._sale['id']: sales = self.ModSale.find([ ('id', '=', self._sale['id']) ]) if not sales: return return sales[0] def check_empty_sale(self): sale = self.get_current_sale() if sale and self.model_sale_lines.rowCount() == 0 \ and sale['state'] == 'draft' and not sale['number']: self.delete_current_sale() def close(self): dialog = self.dialog('confirm_exit', response=True) response = dialog.exec_() if response == DIALOG_REPLY_YES: self.check_empty_sale() if self.active_weighing and self.reader_thread: self.reader_thread.onClose() super(MainWindow, self).close() def delete_current_sale(self): if self._sale['id']: self._PosSale.cancel_sale(self._sale['id'], self._context) def resize_window_tablet_dev(self): self.resize(690, self.get_geometry()[1]) def set_stack_messages(self): super(MainWindow, self).set_stack_messages() self.stack_msg.update({ 'system_ready': ('info', self.tr('SYSTEM READY...')), 'confirm_exit': ('warning', self.tr('DO YOU WANT TO EXIT?')), 'confirm_credit': ('question', self.tr('PLEASE CONFIRM YOUR PAYMENT TERM AS CREDIT?')), 'sale_number_not_found': ('warning', self.tr('SALE ORDER / INVOICE NUMBER NOT FOUND!')), 'sale_closed': ('error', self.tr('THIS SALE IS CLOSED, YOU CAN NOT TO MODIFY!')), 'discount_not_valid': ('warning', self.tr('DISCOUNT VALUE IS NOT VALID!')), 'add_payment_sale_draft': ('info', self.tr('YOU CAN NOT ADD PAYMENTS TO SALE ON DRAFT STATE!')), 'enter_quantity': ('question', self.tr('ENTER QUANTITY...')), 'enter_discount': ('question', self.tr('ENTER DISCOUNT...')), 'enter_payment': ('question', self.tr('ENTER PAYMENT AMOUNT BY: %s')), 'enter_new_price': ('question', self.tr('ENTER NEW PRICE...')), 'order_successfully': ('info', self.tr('ORDER SUCCESUFULLY SENT.')), 'order_failed': ('warning', self.tr('FAILED SEND ORDER!')), 'missing_agent': ('warning', self.tr('MISSING AGENT!')), 'missing_salesman': ('warning', self.tr('THERE IS NOT SALESMAN FOR THE SALE!')), 'sale_without_products': ('warning', self.tr('YOU CAN NOT CONFIRM A SALE WITHOUT PRODUCTS!')), 'user_without_permission': ('error', self.tr('USER WITHOUT PERMISSION FOR SALE POS!')), 'quantity_not_valid': ('error', self.tr('THE QUANTITY IS NOT VALID...!')), 'user_not_permissions_device': ('error', self.tr('THE USER HAVE NOT PERMISSIONS FOR ACCESS' \ ' TO DEVICE!')), 'missing_party_configuration': ('warning', self.tr('MISSING THE DEFAULT PARTY ON SHOP CONFIGURATION!')), 'missing_journal_device': ('error', self.tr('MISSING SET THE JOURNAL ON DEVICE!')), 'statement_closed': ('error', self.tr('THERE IS NOT A STATEMENT OPEN FOR THIS DEVICE!')), 'product_not_found': ('warning', self.tr('PRODUCT NOT FOUND!')), 'must_load_or_create_sale': ('warning', self.tr('FIRST YOU MUST CREATE/LOAD A SALE!')), 'new_sale': ('warning', self.tr('DO YOU WANT CREATE NEW SALE?')), 'cancel_sale': ('question', self.tr('ARE YOU WANT TO CANCEL SALE?')), 'not_permission_delete_sale': ('info', self.tr('YOU HAVE NOT PERMISSIONS FOR DELETE THIS SALE!')), 'not_permission_for_cancel': ('info', self.tr('YOU HAVE NOT PERMISSIONS FOR CANCEL THIS SALE!')), 'customer_not_credit': ('info', self.tr('THE CUSTOMER HAS NOT CREDIT!')), 'agent_not_found': ('warning', self.tr('AGENT NOT FOUND!')), 'invalid_commission': ('warning', self.tr('COMMISSION NOT VALID!')), 'credit_limit_exceed': ('info', self.tr('CREDIT LIMIT FOR CUSTOMER EXCEED!')), 'credit_limit_capacity': ('info', self.tr('THE CUSTOMER CREDIT CAPACITY IS ABOVE 80%')), 'not_can_force_assign': ('warning', self.tr('YOU CAN NOT FORCE ASSIGN!')), 'send_electronic_failed': ('info', self.tr('FALLO EL ENVIO DE FACTURA!')), 'invoice_done_failed': ('info', self.tr('FALLO FINALIZACIÓN DE FACTURA!')), }) def load_modules(self): modules = Modules(self, self.conn) self._sale_pos_restaurant = None self.Module = FastModel('ir.module', self.ctx) self.Config = FastModel('sale.configuration', self.ctx) self._config, = self.Config.find([('id', '=', 1)]) self.discount_method = self._config.get('discount_pos_method') self._commission_activated = self.Module.find([ ('name', '=', 'commission'), ('state', '=', 'activated'), ]) self._credit_limit_activated = self.Module.find([ ('name', '=', 'account_credit_limit'), ('state', '=', 'activated'), ]) _product = { 'name': 'product.product', 'fields': [ 'template.name', 'code', 'barcode', '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('brand.name') if self._config['encoded_sale_price']: _product['fields'].extend(['image', 'image_icon', 'encoded_sale_price']) _PosSale = { 'name': '_PosSale', 'model': 'sale.sale', 'fields': ['number', 'party', 'party.name', 'salesman', 'lines', 'position', 'total_amount_cache', 'salesman.party.name', 'payment_term', 'payment_term.name', 'invoices', 'payments', 'untaxed_amount', 'state', 'tax_amount', 'total_amount', 'residual_amount', 'paid_amount', 'invoice', 'invoice.state', 'invoice_number', 'invoice.number', 'invoices', 'delivery_charge', 'sale_date', 'invoice_type'], 'methods': ( 'get_printing_context', 'cancel_sale', 'get_amounts', 'get_discount_total', 'process_sale', 'reconcile_invoice', 'post_invoice', 'get_data', 'add_value', 'faster_add_product', 'get_product_prices', 'add_payment', 'get_order2print', 'get_sale_from_invoice', 'add_tax', 'check_state', 'to_quote', 'to_draft', 'new_sale', 'on_change', 'get_salesman_in_party', ) } _Agent = { 'name': '_Agent', 'model': 'commission.agent', 'fields': ('id', 'party.name', 'party.id_number', 'plan.percentage', 'active'), } _Commission = { 'name': '_Commission', 'model': 'commission', 'fields': ('id', 'origin', 'invoice_line', 'invoice_line.invoice'), } _Tables = self._Tables = None if self.enviroment == 'restaurant': self._sale_pos_restaurant = self.Module.find([ ('name', '=', 'sale_pos_frontend_rest'), ('state', '=', 'activated'), ]) if self._sale_pos_restaurant: _Tables = { 'name': '_Tables', 'model': 'sale.shop.table', 'fields': ('name', 'shop', 'capacity', 'state') } _PosSale['fields'].extend(['table_assigned', 'table_assigned.name', 'table_assigned.state']) if self._commission_activated: _PosSale['fields'].extend(['agent', 'agent.party.name', 'commission']) modules.set_models([_Agent, _Commission]) self.User = FastModel('res.user', self.ctx) self._user, = self.User.find([('login', '=', self.user)]) if not self._user['sale_device']: return 'user_not_permissions_device' self.ctx['user'] = self._user['id'] self.ModSale = FastModel('sale.sale', self.ctx) self.ModSaleLine = FastModel('sale.line', self.ctx) self.Product = FastModel('product.product', self.ctx) self.Journal = FastModel('account.statement.journal', self.ctx) self.Employee = FastModel('company.employee', self.ctx) self.Device = FastModel('sale.device', self.ctx) self.Category = FastModel('product.category', self.ctx) self.PaymentTerm = FastModel('account.invoice.payment_term', self.ctx) self.Party = FastModel('party.party', self.ctx) self.Taxes = FastModel('account.tax', self.ctx) self.ActionReport = FastModel('ir.action.report', self.ctx) models_to_work = [_PosSale] if _Tables: models_to_work.append(_Tables) modules.set_models(models_to_work) self.device, = self.Device.find([ ('id', '=', self._user['sale_device']['id']), ]) self.shop = self.device['shop'] self.shop_taxes = self.shop['taxes'] self.company = self.shop['company'] self._journals = dict([(j['id'], j) for j in self.device['journals']]) self.employees = self.Employee.find([ ('company', '=', self.company['id']), ]) self._payment_terms = self.PaymentTerm.get_payment_term_pos() self.type_pos_user = self._context.get('type_pos_user') if not self.type_pos_user: return 'user_without_permission' self.user_can_delete = self.type_pos_user in ('frontend_admin', 'cashier') self.product_categories = self.device['shop']['product_categories'] self.salesman_required = self.device['shop']['salesman_pos_required'] self.default_party = self.shop['party'] if not self.default_party: return 'missing_party_configuration' self.default_journal = self.device['journal'] if not self.default_journal: return 'missing_journal_device' self.default_payment_term = self.shop['payment_term'] self._password_admin = self._config.get('password_admin_pos') self._action_report, = self.ActionReport.find([ ('report_name', '=', 'account.invoice'), ]) if self._config['show_stock_pos'] in ('value', 'icon'): self.stock_context = { 'stock_date_end': date.today(), 'locations': [self.shop['warehouse']['id']], } return True def create_dialogs(self): self.create_dialog_position() self.create_dialog_comment() self.create_dialog_search_party() self.create_dialog_payment() self.create_dialog_salesman() self.create_dialog_voucher() self.create_dialog_print_invoice() self.create_dialog_global_discount() self.create_dialog_payment_term() self.create_dialog_search_sales() self.create_wizard_new_sale() self.create_dialog_stock() self.create_dialog_order() self.create_dialog_force_assign() self.create_dialog_taxes() self.create_dialog_cancel_invoice() self.create_dialog_sale_line() if self._commission_activated: self.create_dialog_agent() if self.enviroment == 'restaurant' and self._sale_pos_restaurant: self.create_dialog_manage_tables() def set_printing_context(self): # Printing invoice context if self.printer_sale_name: if ">" in self.printer_sale_name: self.printer_sale_name = str(str("\\") + self.printer_sale_name.replace('>', str('\\'))) ctx_printing = self._PosSale.get_printing_context( [self.device['id']], self.user, self._context) ctx_printing['row_characters'] = self.row_characters ctx_printing['delta_locale'] = DELTA_LOCALE self.receipt_sale = Receipt(ctx_printing) # Printing order context if self.print_order: self.receipt_order = Receipt(ctx_printing) 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], } if printer: self.receipt_sale.set_printer(printer) def button_new_sale_pressed(self): self.createNewSale() def button_send_to_pay_pressed(self): # Return sale to draft state self._PosSale.to_quote(self._sale['id'], self._context) if self.model_sale_lines.rowCount() > 0: if self.check_salesman(): self.state_disabled() def button_to_draft_pressed(self): # Return sale to draft state if hasattr(self, '_sale'): self._PosSale.to_draft(self._sale['id'], self._context) self.state_disabled() def create_gui(self): panels = QHBoxLayout() panel_left = QVBoxLayout() panel_right = QVBoxLayout() left_head = QHBoxLayout() left_table = None left_bottom = QHBoxLayout() self.message_bar = MessageBar() self.label_input = QLabel() self.label_input.setFocus() self.label_input.setObjectName('label_input') if self.enviroment == 'restaurant': values = self.get_product_by_categories() menu_dash = MenuDash(self, values, 'on_selected_item') if not self.tablet_mode: _label_invoice = QLabel(self.tr('INVOICE:')) _label_invoice.setObjectName('label_invoice') _label_invoice.setAlignment(alignRight | alignVCenter) self.field_invoice = QLineEdit() self.field_invoice.setReadOnly(True) self.field_invoice.setObjectName('field_invoice') if self.tablet_mode: self.field_invoice.setPlaceholderText(self.tr('INVOICE')) self.field_amount = FieldMoney(self, 'amount', {}) self.field_amount.setObjectName('field_amount') self.field_sign = QLabel(' ') self.field_sign.setObjectName('field_sign') layout_message = QGridLayout() layout_message.addLayout(self.message_bar, 1, 0, 1, 4) if not self.tablet_mode: layout_message.addWidget(self.label_input, 2, 0, 1, 2) layout_message.addWidget(_label_invoice, 2, 2) layout_message.addWidget(self.field_invoice, 2, 3) else: layout_message.addWidget(self.label_input, 2, 0, 1, 2) layout_message.addWidget(self.field_invoice, 2, 3) left_head.addLayout(layout_message, 0) left_head.addWidget(self.field_sign, 0) left_head.addWidget(self.field_amount, 0) info_fields = [ ('party', { 'name': self.tr('CUSTOMER'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': 'gray' }), ('date', { 'name': self.tr('DATE'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': 'gray' }), ('salesman', { 'name': self.tr('SALESMAN'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': 'gray' }), ('payment_term', { 'name': self.tr('PAYMENT TERM'), 'readonly': True, 'invisible': self.tablet_mode, 'placeholder': False, 'size': self.screen_size, 'color': 'gray' }), ('order_number', { 'name': self.tr('No ORDER'), 'placeholder': False, 'readonly': True, 'size': self.screen_size, 'color': 'gray' }), ('invoice_type', { 'name': self.tr('INVOICE TYPE'), 'placeholder': False, 'type': 'selection', 'on_change': 'action_invoice_type_selection_changed', 'values': [ ('', ''), ('C', self.tr('COMPUTADOR')), ('M', self.tr('MANUAL')), ('P', self.tr('POS')), ('1', self.tr('VENTA ELECTRONICA')), ('2', self.tr('VENTA DE EXPORTACION')), ('3', self.tr('FACTURA POR CONTINGENCIA FACTURADOR')), ('4', self.tr('FACTURA POR CONTINGENCIA DIAN')), ('91', self.tr('NOTA CREDITO ELECTRONICA')), ('92', self.tr('NOTA DEBITO ELECTRONICA')), ], 'size': self.screen_size, 'color': 'gray' }), ] self.field_invoice_type = None if self.tablet_mode or self._config['show_position_pos']: info_fields.append(('position', { 'name': self.tr('POSITION'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': 'gray' })) if self._commission_activated and not self.tablet_mode \ and self._config['show_agent_pos']: info_fields.append(('agent', { 'name': self.tr('AGENT'), 'placeholder': self.tablet_mode, 'readonly': True, 'size': self.screen_size, 'color': 'gray' })) _cols = 2 if self.enviroment == 'restaurant': _cols = 1 self.field_delivery_charge = None if self._config['show_delivery_charge']: info_fields.append( ('delivery_charge', { 'name': self.tr('DELIVERY CHARGE'), 'placeholder': False, 'type': 'selection', 'on_change': 'action_delivery_charge_selection_changed', 'values': [ ('', ''), ('customer', self.tr('CUSTOMER')), ('company', self.tr('COMPANY')), ], 'size': self.screen_size, 'color': 'gray' })) self.field_table_assigned = None if self.enviroment == 'restaurant' and self._sale_pos_restaurant: info_fields.append( ('table_assigned', { 'name': self.tr('ASSIGNED TABLE'), 'placeholder': False, 'size': self.screen_size, 'color': 'gray' })) self.grid_info = GridForm(self, OrderedDict(info_fields), col=_cols) col_sizes_tlines = [field['width'] for field in self.fields_sale_line] left_table = TableView('model_sale_lines', self.model_sale_lines, col_sizes_tlines, method_selected_row=self.sale_line_selected) self.table_sale_lines = left_table for i, f in enumerate(self.model_sale_lines._fields, 0): if f.get('invisible'): self.table_sale_lines.hideColumn(i) _fields_amounts = [ ('untaxed_amount', { 'name': self.tr('SUBTOTAL'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'gray' }), ('taxes_amount', { 'name': self.tr('TAXES'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'gray' }), ('discount', { 'name': self.tr('DISCOUNT'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'gray' }), ('total_amount', { 'name': self.tr('TOTAL'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'blue' }), ('paid', { 'name': self.tr('PAID'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'gray' }), ('pending', { 'name': self.tr('PENDIENTE'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'blue' }), ('change', { 'name': self.tr('CHANGE'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'orange' }) ] fields_amounts = OrderedDict(_fields_amounts) self.grid_amounts = GridForm(self, fields_amounts, col=1) self.buttonpad = Buttonpad(self) self.pixmap_pos = Image(self, 'pixmap_pos', FILE_BANNER) self.table_payment = TableView('table_payment', self.table_payment_lines, [250, STRETCH]) panel_left.addLayout(left_head, 0) panel_left.addWidget(left_table, 1) panel_right.addWidget(self.pixmap_pos, 0) left_bottom.addLayout(self.grid_info, 1) if self.enviroment == 'restaurant': panel_left.addLayout(self.buttonpad.functions, 1) left_bottom.addLayout(self.grid_amounts, 0) panel_right.addLayout(menu_dash, 1) panel_right.addLayout(self.buttonpad.stacked, 0) else: panel_right.addLayout(self.grid_amounts, 1) panel_right.addLayout(self.buttonpad.functions, 1) panel_right.addLayout(self.buttonpad.stacked, 0) panel_right.addWidget(self.table_payment) panel_left.addLayout(left_bottom, 0) panels.addLayout(panel_left, 1) panels.addLayout(panel_right, 0) widget = QWidget() widget.setLayout(panels) self.setCentralWidget(widget) def create_statusbar(self): values = OrderedDict([ ('stb_shop', {'name': self.tr('SHOP'), 'value': self.shop['name']}), ('stb_device', {'name': self.tr('DEVICE'), 'value': self.device['name']}), ('stb_database', {'name': self.tr('DATABASE'), 'value': self.database}), ('stb_user', {'name': self.tr('USER'), 'value': self.user}), ('stb_printer', {'name': self.tr('PRINTER'), 'value': self.printer_sale_name}) ]) self.set_statusbar(values) def button_plus_pressed(self): error = False if self._input_text == '' and self._amount_text == '0': return if self._state in ('paid', 'disabled'): return if self._sign in ('*', '-', '/'): if hasattr(self, '_sale_line') and self._sale_line \ and self._sale_line.get('type') and self._state == 'add' \ and self.model_sale_lines.rowCount() > 0: if self._sign == '*': self._process_quantity(self._amount_text) else: error = not(self._process_price(self._amount_text)) elif self._state in ['add', 'cancel', 'accept']: self.clear_right_panel() self.add_product(code=self._input_text) elif self._state == 'cash': is_paid = self._process_pay(self.field_amount.text()) if not is_paid: self.clear_input_text() self.clear_amount_text() return else: logging.warning('Unknown command/text') self._clear_context(error) def action_read_weight(self): self.reader_thread.start() def set_weight_readed(self): if not self.reader_thread or not self.reader_thread.best_weight: return if self.reader_thread.best_weight: self.amount_text_changed(self.reader_thread.best_weight) self._process_quantity(self._amount_text) self._clear_context(False) self.reader_thread.fZERO() def _clear_context(self, error=False): self.clear_input_text() self.clear_amount_text() self.clear_sign() if self._state not in ('warning', 'cash') and not error: self.message_bar.set('system_ready') else: self.set_state('add') def _process_quantity(self, text): eval_value = text.replace(',', '.') rec = {} try: quantity = Decimal(eval_value) if self._sale_line['product'].get('quantity'): if not self._check_stock_quantity(self._sale_line['product'], quantity): return if self._current_line_id: rec = self.ModSaleLine.faster_set_quantity({ 'id': self._current_line_id, 'quantity': float(quantity) }) except: return self.message_bar.set('quantity_not_valid') self.message_bar.set('system_ready') self.model_sale_lines.update_record(rec) self.update_total_amount() def _process_price(self, text): discount_valid = True eval_value = text.replace(',', '') value = float(eval_value) if self._sign == '-': if self.discount_method == 'percentage' and value > 90: discount_valid = False # Do discount else: discount_valid = self.set_discount(eval_value) elif self._sign == '/': if value <= 0: return sale_line, = self.ModSaleLine.find([ ('id', '=', self._current_line_id) ]) price_w_tax = sale_line['unit_price_w_tax'] if price_w_tax <= Decimal(value): # Change unit price discount_valid = self.set_unit_price(value) else: eval_value = (1 - (value / price_w_tax)) * 100 if self.discount_method == 'fixed': eval_value = price_w_tax - value discount_valid = self.set_discount(eval_value) if not discount_valid: self.message_bar.set('discount_not_valid') return False self.message_bar.set('system_ready') self.update_total_amount() return True def _process_pay(self, text): if not self.validate_done_sale(): return val = Decimal(text.replace(',', '')) if self._commission_activated: if self._journals[self.field_journal_id]['kind'] == 'payment': agent_id = None if self.field_agent_id: agent_id = self.field_agent_id else: agent_id = self.field_agent_ask.get_id() if not agent_id: self.message_bar.set('missing_agent') return False cash_received = Decimal(val) self.set_amounts() residual_amount = self._sale['residual_amount'] if residual_amount < 0: # The sale is paid self._done_sale() return True change = cash_received - residual_amount if residual_amount >= cash_received: amount_to_add = cash_received else: amount_to_add = residual_amount all_money = cash_received + self._sale['paid_amount'] # self.field_change.setText(change) if change < ZERO: self.field_pending.setText(abs(change)) else: self.field_pending.setText(str(ZERO)) self.set_amount_received(all_money) res = self.add_payment(amount_to_add, all_money, change) if res['residual_amount'] < 0: # The sale is paid self._done_sale() return True self.field_journal_id = self.default_journal['id'] if res['msg'] == 'missing_money': self.message_bar.set('enter_payment', self.default_journal['name']) return False if change < ZERO: self.message_bar.set('enter_payment', self.default_journal['name']) self.field_change.setText(change) # self.set_amount_received(all_money) self._sale.update({ 'residual_amount': res['residual_amount'] }) residual_amount = self._sale['residual_amount'] if self._sale['residual_amount'] <= 0: # The sale is paid self._done_sale() return True def validate_done_sale(self): if self.model_sale_lines.rowCount() == 0: self.dialog('sale_without_products') self.set_state('add') self.message_bar.set('system_ready') return return True def _get_total_amount(self): return self.model_sale_lines.get_sum('amount_w_tax') def set_discount_amount(self): res = 0 if self._sale['id']: res = self.ModSale.get_discount_total({ 'sale_id': self._sale['id'] }) self.field_discount.setText(res) def amount_text_changed(self, text=None): if text: self._amount_text += text self.field_amount.setText(self._amount_text) def input_text_changed(self, text=None): if text: self._input_text += text elif text == '': self._input_text = '' self.label_input.setText(self._input_text) def __do_invoice_thread(self): try: res = self.ModSale.faster_post_invoice({ 'sale_id': self.sale_to_post['id'] }) except: self.dialog('invoice_done_failed') return if not res['result']: self.dialog('send_electronic_failed') return if self.sale_to_post['is_credit']: return self.ModSale.reconcile_invoice({ 'sale_id': self.sale_to_post['id'] }) def _done_sale(self, is_credit=False): self._sale['is_credit'] = is_credit self.sale_to_post = self._sale self.do_invoice.start() if self._Tables and self._sale.get('table_assigned'): self._Tables.write([self._sale['table_assigned']], {'state': 'available'} ) try: if self.print_receipt == 'automatic': _copies = self.device['shop.invoice_copies'] if not is_credit: self.print_invoice(copies=_copies) if self.print_order and self.print_auto_order: self.action_print_order() except: logging.error(sys.exc_info()[0]) if not is_credit and self._commission_activated: agent_id = self.field_agent_ask.get_id() if self.auto_print_commission and agent_id: self.print_equivalent_invoice(self._sale['id']) if self.type_pos_user not in ('cashier', 'order') \ and self._config.get('new_sale_automatic'): self.createNewSale() else: self.state_disabled() return True def print_invoice(self, sale_id=None, copies=1): if not sale_id: sale_id = self._sale['id'] data = self._PosSale.get_data(sale_id, self._context) for i in range(copies): self.receipt_sale.print_sale(data) def button_accept_pressed(self): if not self._sale['id'] or not self.model_sale_lines.rowCount() > 0: return self.set_state('accept') def button_cash_pressed(self): if not self.check_salesman(): return if not self.validate_payment_term(): return sale_id = self._sale['id'] res, msg = self.ModSale.faster_process({'sale_id': sale_id}) # Remove deprecation res['res'] if msg: # msg = Mesagge error self.message_bar.set(msg) return self.set_amounts(res) self.field_invoice.setText(res['invoice_number']) self.field_amount.zero() if self.type_pos_user == 'salesman': self.print_invoice(sale_id) res = self._print_order(sale_id, 'delivery') self.createNewSale() return self.message_bar.set('enter_payment', self.default_journal['name']) self.set_state('cash') self.buttonpad.setFocus() def action_reservations(self): logging.info('Buscando reservas.....') def action_tables(self): self.dialog_manage_tables.exec_() 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 action_tip(self): if self._config['tip_product.code'] and self._config['tip_rate']: total_amount = int(self._get_total_amount()) self.add_product(code=self._config['tip_product.code']) self.button_plus_pressed() eval_value = int((self._config['tip_rate'] / 100) * total_amount) self.ModSaleLine.write( [self._current_line_id], {'unit_price': Decimal(eval_value)} ) self._PosSale.write([self._sale['id']], {'tip': Decimal(eval_value)}) self.update_total_amount() def action_salesman(self): self.dialog_salesman.exec_() def action_tax(self): self.dialog_tax.exec_() def action_payment(self): if self._state != 'cash': self.dialog('add_payment_sale_draft') return self.dialog_payment.exec_() def _check_credit_capacity(self, party): if party['credit_limit_amount']: if (party['credit_limit_amount'] * Decimal(RATE_CREDIT_LIMIT)) < (party['credit_amount'] + self._get_total_amount()): self.dialog('credit_limit_capacity') return True def validate_payment_term(self): is_credit = self._payment_terms[str(self.field_payment_term_id)]['is_credit'] party, = self.Party.find([('id', '=', self.party_id)]) if is_credit: if self.party_id == self.default_party['id']: self.dialog('customer_not_credit') return False if self._credit_limit_activated: if is_credit and not party['credit_limit_amount']: self.dialog('customer_not_credit') return False self._credit_amount = self.ModSale.get_credit_amount_party({'party_id': self.party_id}) self._credit_limit_amount = party['credit_limit_amount'] if is_credit and self._credit_limit_amount and \ self._credit_limit_amount < (self._credit_amount + self._get_total_amount()): self.dialog('credit_limit_exceed') return False return True def action_payment_term_selection_changed(self): is_credit = self._payment_terms[str(self.field_payment_term_id)]['is_credit'] self._PosSale.write([self._sale['id']], {'payment_term': self.field_payment_term_id}) if is_credit and self.type_pos_user != 'salesman': if self.validate_credit_limit(): self._done_sale(is_credit=True) def validate_credit_limit(self): if self._credit_limit_amount and self._credit_limit_amount < (self._credit_amount + self._get_total_amount()): self.dialog('credit_limit_exceed') return False else: return True def action_journal_selection_changed(self): self.message_bar.set('enter_payment', self.field_journal_name) def action_salesman_selection_changed(self): self._PosSale.write([self._sale['id']], {'salesman': self.field_salesman_id}) def action_delivery_charge_selection_changed(self, index): val = self.field_delivery_charge.get_id() if val: self._PosSale.write([self._sale['id']], {'delivery_charge': val}) def action_invoice_type_selection_changed(self, index): val = self.field_invoice_type.get_id() if val: self._PosSale.write([self._sale['id']], {'invoice_type': val}) def action_tax_selection_changed(self): res = self._PosSale.add_tax(self._sale['id'], self.field_tax_id, self._context) self._sale.update(res) self.set_amounts() def action_print_sale(self): number = self.field_invoice.text() if not number: number = self.field_order_number.text() if number: self.field_invoice_number_ask.setText(number) res = self.dialog_print_invoice.exec_() if res == DIALOG_REPLY_NO: return number = self.field_invoice_number_ask.text() printer_id = self.field_printer_ask.get_id() type_doc = self.field_type_ask.get_id() sale = {} if number: if type_doc == 'order': sales = self.ModSale.find([('number', '=', number)]) if sales: sale = sales[0] else: sale = self._PosSale.get_sale_from_invoice([1], number, self._context) if not sale: return self.message_bar.set('sale_number_not_found') sale_id = sale['id'] else: sale_id = self._sale['id'] if printer_id == '1': self.print_invoice(sale_id) else: self.print_odt_invoice(sale) def print_odt_invoice(self, sale): if not sale.get('invoices'): return invoice_id = sale['invoices'][0] model = u'account.invoice' data = { 'model': model, 'action_id': self._action_report['id'], 'id': invoice_id, 'ids': [invoice_id], } ctx = {'date_format': u'%d/%m/%Y'} ctx.update(self._context) Action.exec_report(self.conn, u'account.invoice', data, direct_print=True, context=ctx) def print_equivalent_invoice(self, sale_id): sale, = self.ModSale.find([('id', '=', sale_id)]) # if not sale['invoices']: # return # # invoice_id = sale['invoices'][0] # commissions = self._Commission.find([ # ('origin', '=', 'account.invoice,' + str(invoice_id)) # ]) # # # if not commissions: # # return # # commission = commissions[0] # # if not commission['invoice_line.invoice']: # # return # # # # model = u'account.invoice' # # data = { # # 'model': model, # # 'action_id': self._action_report_equivalent['id'], # # 'id': commission['invoice_line.invoice'], # # 'ids': [commission['invoice_line.invoice']], # # } # # ctx = {'date_format': u'%d/%m/%Y'} # # ctx.update(self._context) # # Action.exec_report(self.conn, u'account.invoice', # # data, direct_print=True, context=ctx) def action_comment(self): self.dialog_comment.exec_() comment = self.field_comment_ask.text() if comment: self._PosSale.write([self._sale['id']], {'comment': comment}) def action_position(self): self.dialog_position.exec_() position = self.field_position_ask.text() if hasattr(self, 'field_position') and position: self.field_position.setText(position) self._PosSale.write([self._sale['id']], {'position': position}) def action_agent(self): self.dialog_agent.exec_() res = self.field_commission_ask.text() if not res: return commission = float(res) sale, = self.ModSale.find([ ('id', '=', self._sale['id']), ]) self.field_agent_id = self.field_agent_ask.get_id() if self.field_agent_id and commission: agent, = self._Agent.find([ ('id', '=', self.field_agent_id), ]) if commission <= agent['plan.percentage']: self._PosSale.write([self._sale['id']], { 'agent': self.field_agent_id, 'commission': int(commission), }) else: self.message_bar.set('invalid_commission') return self.message_bar.set('system_ready') comm_string = str('[' + str(commission) + ']' + ' ') + (str(self.field_agent_ask.text())) self.field_agent.setText(comm_string) self._set_commission_amount(sale['untaxed_amount'], commission) def _set_commission_amount(self, untaxed_amount, commission): untaxed_amount = int(untaxed_amount) commission = int(commission) total = ((untaxed_amount * commission) / 100) self.field_commission_amount.setText(str(total)) def action_party(self): self.dialog_search_parties.clear_rows() self.dialog_search_parties.execute() def action_global_discount(self, sale_id=None): self.dialog_global_discount.exec_() discount = self.field_global_discount_ask.text() if discount and discount.isdigit(): if self.model_sale_lines.rowCount() > 0: lines = [line['id'] for line in self.model_sale_lines._data] self.set_discount(int(discount), lines) def _print_order(self, sale_id, kind, reversion=False): result = False try: orders = self._PosSale.get_order2print(sale_id, reversion, False, self._context) result = self.receipt_order.print_orders(orders, reversion, kind) except: logging.error('Printing order fail!') return result def action_print_order(self, sale_id=None, reversion=False): res = self.dialog_order.exec_() if res == DIALOG_REPLY_NO: return kind = 'delivery' if self.enviroment == 'restaurant': kind = 'command' if not self.print_order: return if not sale_id and self._sale['id']: sale_id = self._sale['id'] result = self._print_order(sale_id, kind) # try: # orders = self._PosSale.get_order2print(sale_id, reversion, # False, self._context) # result = self.receipt_order.print_orders(orders, reversion, kind) # print(result, orders) # except: # print('Error: printing order fail!') if result: # Show dialog if send sale order was successful self.dialog('order_successfully') else: self.dialog('order_failed') def action_payment_term(self): if self._state == 'cash' or self.type_pos_user == 'salesman': self.dialog_payment_term.exec_() def action_new_sale(self): if not self._sale['id']: return if self._ask_new_sale(): self.createNewSale() if self.enviroment == 'restaurant': self.wizard_new_sale() def wizard_new_sale(self): # self.action_position() # self.action_salesman() pass 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) res = dialog.exec_() if res == DIALOG_REPLY_NO: return False return True def action_cancel(self): if self.type_pos_user == 'cashier': self.dialog_cancel_invoice.exec_() password = self.field_password_for_cancel_ask.text() if password != self._password_admin: return self.dialog('not_permission_for_cancel') if not self._sale['id']: return if self._state == 'cash' and not self.user_can_delete: return self.dialog('not_permission_delete_sale') if self.type_pos_user in ('order', 'salesman') and \ self._sale.get('invoice_number'): return self.dialog('not_permission_delete_sale') dialog = self.dialog('cancel_sale', response=True) response = dialog.exec_() if response == DIALOG_REPLY_NO: return self._PosSale.cancel_sale(self._sale['id'], self._context) self.field_password_for_cancel_ask.setText('') self.set_state('cancel') self.clear_right_panel() self.createNewSale() def action_search_product(self): if self._state == 'cash': return self.dialog_search_products.show() def action_search_sale(self): delta = str(datetime.now() - timedelta(4)) if self.type_pos_user == 'cashier': dom = ['OR', [ ('create_date', '>=', delta), ('state', 'in', ['quotation', 'confirmed']), ], [ ('state', '=', 'processing'), ('invoice.state', '=', 'draft'), ('invoice.type', '=', 'out'), ]] elif self.type_pos_user in ('order', 'salesman'): dom = [ ('state', '=', 'draft'), ('create_date', '>=', delta), ] else: dom = [ ('state', 'in', ['draft', 'quotation', 'confirmed']), ('create_date', '>=', delta), ] sales = self.ModSale.find(dom, order=[('id', 'DESC')]) # for sale in sales: # if sale['salesman']: # print(sale['salesman']) self.dialog_search_sales.set_from_values(sales) if self.enviroment == 'retail': dom_draft = [ ('create_date', '>=', delta), ('state', '=', 'draft'), ('invoice_number', '!=', None), ] sales_draft = self.ModSale.find(dom_draft) self.dialog_search_sales.set_counter_control(sales_draft) response = self.dialog_search_sales.execute() if response == DIALOG_REPLY_NO: return def on_selected_sale(self): sale_id = self.dialog_search_sales.get_id() if not sale_id: return self.load_sale(sale_id) self.setFocus() self.label_input.setFocus() self.grabKeyboard() def on_selected_party(self): party_id = self.dialog_search_parties.get_id() if not party_id: return party, = self.Party.find([('id', '=', party_id)]) values = { 'party': party_id, 'invoice_address': party['addresses'][0]['id'], 'shipment_address': party['addresses'][0]['id'], } if party.get('customer_payment_term'): values['payment_term'] = party.get('customer_payment_term') self.field_payment_term.setText(str(party['customer_payment_term.name'])) self.field_payment_term_id = party.get('customer_payment_term') else: values['payment_term'] = self.default_payment_term['id'] self.field_payment_term_id = self.default_payment_term['id'] self.field_payment_term.setText(self.default_payment_term['name']) self._PosSale.write([self._sale['id']], values) self.party_id = party_id self.field_party.setText(party['name']) if party.get('salesman'): self.field_salesman_id = party['salesman']['id'] self._PosSale.write([self._sale['id']], {'salesman': self.field_salesman_id}) self.field_salesman.setText(party['salesman']['name']) # if self._credit_limit_activated: # self._check_credit_capacity(party) self.message_bar.set('system_ready') self.setFocus() self.label_input.setFocus() self.grabKeyboard() def load_sale(self, sale_id): # loads only draft sales self.is_clear_right_panel = True self.clear_data() self.clear_left_panel() self.clear_right_panel() sale, = self.ModSale.find([('id', '=', sale_id)]) self._sale.update(sale) self.table_payment_lines.reset() self.field_order_number.setText(sale['number'] or '') self._set_sale_date() if hasattr(self, 'field_position'): self.field_position.setText(sale['position'] or '') if sale.get('payment_term'): self.field_payment_term_id = sale['payment_term']['id'] self.field_payment_term.setText(sale['payment_term']['name'] or '') if sale.get('salesman'): self.field_salesman.setText(sale['salesman']['name'] or '') self.field_salesman_id = sale['salesman']['id'] res = self.ModSale.get_invoice_type({'sale_id': sale_id}) if res: self.field_invoice_type.set_from_id(res['invoice_type']) if sale.get('invoice_number'): self.field_invoice.setText(sale['invoice_number'] or '') else: self.field_invoice.setText('') if self.field_delivery_charge: self.field_delivery_charge.set_enabled(True) if self._sale.get('delivery_charge'): self.field_delivery_charge.set_from_id(self._sale['delivery_charge']) if sale.get('table_assigned'): self.field_table_assigned.setText(sale['table_assigned.name'] or '') self.field_change.zero() if self._commission_activated: if hasattr(self, 'field_agent') and sale.get('agent') \ and sale.get('commission'): commission = sale.get('commission') self.field_agent.setText('[' + str(int(commission)) + ']' + ' ' + sale['agent']['name']) self.field_agent_id = sale['agent']['id'] self.field_agent_ask.setText(sale['agent']['name']) self.field_commission_ask.setText(str(commission)) self._set_commission_amount(sale['untaxed_amount'], commission) self.line_ids = [l['id'] for l in sale.get('lines')] if sale.get('lines'): lines = self.ModSaleLine.find([ ('id', 'in', self.line_ids), ]) sale['lines'] = lines for line in lines: self.add_sale_line(line) if sale.get('payments'): for payment in sale['payments']: self.table_payment_lines.record_add(payment) self.set_state('add') self.party_id = sale['party']['id'] self.field_party.setText(sale['party']['name']) self.set_amounts(sale) self.set_amount_received() self.field_amount.zero() if self.type_pos_user in ('cashier', 'order', 'salesman'): self.table_sale_lines.setEnabled(True) def set_change_amount(self): amount_paid = self.table_payment_lines.get_sum('amount') res = amount_paid - self._get_total_amount() self.field_change.setText(res) def set_amount_received(self, cash_received=ZERO): residual_amount = self._sale['residual_amount'] if cash_received: amount = cash_received else: amount = self._sale['paid_amount'] self.field_pending.setText(residual_amount) self.field_paid.setText(amount) def update_total_amount(self): self.set_amounts() def set_amounts(self, res=None): if not res: res = self._PosSale.get_amounts([self._sale['id']], self._context) self._sale.update(res) self.field_untaxed_amount.setText(res['untaxed_amount']) self.field_taxes_amount.setText(res['tax_amount']) self.field_total_amount.setText(res['total_amount']) self.set_discount_amount() def _get_products_by_category(self, cat_id): records = self.Product.find([ ('code', '!=', None), ('template.salable', '=', True), ('template.categories', '=', cat_id), ]) return [ [r['id'], r['code'], r['name'], r['template']['sale_price_w_tax']] for r in records ] def get_product_by_categories(self): self.allow_categories = [pc for pc in self.product_categories if ( not pc['parent'] and not pc['accounting'])] for cat in self.allow_categories: cat['icon'] = get_icon(cat['name_icon']) cat['items'] = self._get_products_by_category(cat['id']) return self.allow_categories def _get_childs(self, parent_cat): res = {} for cat_id in parent_cat['childs']: sub_categories = self.Category.find([ ('parent', '=', parent_cat['id']) ]) for sub_cat in sub_categories: res.update(self._get_childs(sub_cat)) res = { 'id': parent_cat['id'], 'name': parent_cat['name'], 'childs': parent_cat['childs'], 'records': [], 'obj': parent_cat, } return res def get_category_items(self, records): records_by_category = {} def _get_tree_categories(cat): sub_categories = {} if not cat['childs']: sub_categories[cat['name']] = records_by_category.get(cat['id']) or [] else: for child in cat['childs']: sub_categories.update(_get_tree_categories( self.target_categories[child]['obj'])) return sub_categories for record in records: cat_id = record.get('template.account_category') if cat_id not in records_by_category.keys(): records_by_category[cat_id] = [] records_by_category[cat_id].append(record) res = {} for ac in self.allow_categories: res[ac['name']] = _get_tree_categories(ac) return res def on_selected_product(self): if self.dialog_search_products.current_row: self._current_line_id = None self.clear_right_panel() self.add_product(product=self.dialog_search_products.current_row) def on_selected_icon_product(self): if self.dialog_search_products.current_row: code = self.dialog_search_products.current_row['code'] products = self.Product.find([ ('code', '=', code) ]) if not products: return product = products[0] image = Image(name='product_icon') if not product['image']: return b64image = product['image'].encode() image.set_image(b64image, kind='bytes') image.activate() def on_selected_stock_product(self): if self.dialog_search_products.current_row: code = self.dialog_search_products.current_row['code'] res = self.Product.get_stock_by_locations({'code': code}) self.dialog_product_stock.update_values(res) self.dialog_product_stock.show() def on_selected_item(self, product_id): if product_id: self.clear_right_panel() self.add_product(id=product_id) def create_dialog_manage_tables(self): if not self._Tables: return tables = self._Tables.find([ ('shop', '=', self.shop['id']) ]) self.tables = ManageTables(self, tables, self.action_table_assigned) self.dialog_manage_tables = QuickDialog(self, 'action', widgets=[self.tables]) def create_dialog_search_sales(self): headers = OrderedDict() 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 = self.tr('SEARCH SALES...') methods = { 'on_selected_method': 'on_selected_sale', 'on_return_method': 'on_selected_sale' } self.dialog_search_sales = SearchWindow(self, headers, None, methods, filter_column=[1, 2, 3, 4], cols_width=widths, title=title, fill=True) self.dialog_search_sales.activate_counter() def create_dialog_search_products(self): _cols_width = [10, 80] headers = OrderedDict() headers['id'] = {'desc': self.tr('ID'), 'type': 'char'} headers['code'] = {'desc': self.tr('CODE'), 'type': 'char'} if self._config.get('show_stock_pos') in ['icon', 'value']: headers['quantity'] = {'desc': self.tr('STOCK'), 'type': 'number'} if self._config['show_stock_pos'] == 'icon': headers['quantity']['icon'] = 'stock' headers['quantity']['type'] = 'icon' _cols_width.append(60) headers['name'] = {'desc': self.tr('NAME'), 'type': 'char'} _cols_width.append(350) if self._config.get('show_description_pos'): headers['description'] = {'desc': self.tr('DESCRIPTION'), 'type': 'char'} _cols_width.append(300) if self._config.get('show_brand'): headers['template.brand'] = {'desc': self.tr('BRAND'), 'type': 'char'} _cols_width.append(100) price = {'desc': self.tr('PRICE'), 'type': 'number'} if not self._config.get('encoded_sale_price'): headers['template.sale_price_w_tax'] = price else: price['type'] = 'char' headers['encoded_sale_price'] = price _cols_width.append(100) if self._config.get('show_location_pos'): headers['location.name'] = {'desc': self.tr('LOCATION'), 'type': 'char'} _cols_width.append(100) if self._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': self.on_selected_icon_product, 'quantity': self.on_selected_stock_product } self.dialog_search_products = SearchWindow(self, headers, None, methods, cols_width=_cols_width, fill=True) def create_dialog_search_party(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', } self.dialog_search_parties = SearchWindow(self, headers, None, methods, filter_column=[], cols_width=[60, 120, 270, 190, 90], title=title, fill=True) def create_dialog_payment(self): data = { 'name': 'journal', 'values': sorted([(str(j), self._journals[j]['name']) for j in self._journals]), 'heads': [self.tr('ID'), self.tr('PAYMENT MODE:')], } string = self.tr('SELECT PAYMENT MODE:') self.dialog_payment = QuickDialog(self, 'selection', string, data) def create_dialog_stock(self): data = { 'name': 'stock', 'values': [], 'heads': [self.tr('WAREHOUSE'), self.tr('QUANTITY')], } label = self.tr('STOCK BY PRODUCT:') self.dialog_product_stock = QuickDialog(self.dialog_search_products, 'selection', label, data, readonly=True) def create_dialog_salesman(self): data = { 'name': 'salesman', 'values': [(str(e['id']), e['party']['name']) for e in self.employees], 'heads': [self.tr('Id'), self.tr('Salesman')], } string = self.tr('CHOOSE SALESMAN') self.dialog_salesman = QuickDialog(self, 'selection', string, data) def create_dialog_taxes(self): if self.shop_taxes: taxes = [(str(e['id']), e['name']) for e in self.shop_taxes] else: taxes = [] data = { 'name': 'tax', 'values': taxes, 'heads': [self.tr('Id'), self.tr('Salesman')], } string = self.tr('CHOOSE TAX') self.dialog_tax = QuickDialog(self, 'selection', string, data) def create_dialog_payment_term(self): data = { 'name': 'payment_term', 'values': [(p_id, self._payment_terms[p_id]['name']) for p_id in self._payment_terms], 'heads': [self.tr('ID'), self.tr('PAYMENT TERM')], } string = self.tr('SELECT PAYMENT TERM') self.dialog_payment_term = QuickDialog(self, 'selection', string, data) def on_search_product(self): target = self.dialog_search_products.filter_field.text() if not target: return target_words = target.split(' ') domain = [ ('template.active', '=', True), ('active', '=', True), ] for tw in target_words: if len(tw) <= 1: continue clause = ['OR', ('template.name', 'ilike', '%{:}%'.format(tw)), ('description', 'ilike', '%{:}%'.format(tw)), ] domain.append(clause) if self.shop.get('product_categories'): categories_id = [ct['id'] for ct in self.shop['product_categories']] domain.append(('template.account_category', 'in', categories_id)) if not domain: return if self.cache_local: products = self.store.find_product_elastic(domain, limit=100) else: products = self.Product.find(domain, limit=100, ctx=self.stock_context) self.dialog_search_products.set_from_values(products) def on_search_party(self): target = self.dialog_search_parties.filter_field.text() if not target: return target_words = target.split(' ') domain = [('id_number', '!=', None)] for tw in target_words: if len(tw) <= 2: continue or_clause = ['OR', ('name', 'ilike', '%' + tw + '%'), ('contact_mechanisms.value', 'like', tw + '%'), ('id_number', 'like', tw + '%'), ] domain.append(or_clause) parties = self.Party.find(domain) self.dialog_search_parties.set_from_values(parties) def create_dialog_print_invoice(self): view = [ ('invoice_number_ask', {'name': self.tr('INVOICE NUMBER')}), ('printer_ask', { 'name': self.tr('PRINTER'), 'type': 'selection', 'values': [ (1, 'POS'), (2, 'LASER') ], }), ('type_ask', { 'name': self.tr('TYPE'), 'type': 'selection', 'values': [ ('invoice', self.tr('INVOICE')), ('order', self.tr('ORDER')) ], }), ] self.dialog_print_invoice = QuickDialog(self, 'action', data=view) def create_dialog_cancel_invoice(self): view = [ ('password_for_cancel_ask', { 'name': self.tr('INSERT PASSWORD FOR CANCEL'), 'password': True }), ] self.dialog_cancel_invoice = QuickDialog(self, 'action', data=view) def create_dialog_global_discount(self): field = 'global_discount_ask' data = {'name': self.tr('GLOBAL DISCOUNT')} self.dialog_global_discount = QuickDialog(self, 'action', data=[(field, data)]) def create_dialog_force_assign(self): field = 'password_force_assign_ask' data = {'name': self.tr('PASSWORD FORCE ASSIGN')} self.dialog_force_assign = QuickDialog(self, 'action', data=[(field, data)]) self.field_password_force_assign_ask.setEchoMode(QLineEdit.Password) def create_dialog_voucher(self): field = 'voucher_ask' data = {'name': self.tr('VOUCHER NUMBER')} self.dialog_voucher = QuickDialog(self, 'action', data=[(field, data)]) def create_dialog_order(self): # field = 'Send Order' # data = {'name': self.tr('COPY NUMBER')} string = self.tr('DO YOU WANT TO CONFIRM THE SEND ORDER?') self.dialog_order = QuickDialog(self, 'action', string, data=[]) def create_dialog_position(self): field = 'position_ask' data = {'name': self.tr('POSITION')} self.dialog_position = QuickDialog(self, 'action', data=[(field, data)]) def create_dialog_agent(self): view = [ ('agent_ask', { 'name': self.tr('AGENT'), 'type': 'relation', 'model': self._Agent, 'domain': [('active', '=', True)], 'fields': [ ('id', self.tr('ID')), ('party.name', self.tr('NAME')), ('party.id_number', self.tr('ID NUMBER')), ]}), ('commission_ask', {'name': self.tr('COMMISSION')}), ('commission_amount', {'name': self.tr('AMOUNT'), 'readonly': True}), ] self.dialog_agent = QuickDialog(self, 'action', data=view, size=(600, 400)) def create_wizard_new_sale(self): pass def create_dialog_comment(self): field = 'comment_ask' data = {'name': self.tr('COMMENTS'), 'widget': 'text'} self.dialog_comment = QuickDialog(self, 'action', data=[(field, data)]) def clear_data(self): self._sale = { 'total_amount': 0 } self.party_name = None self._sale_line = {'id': None} self._total_amount = {} self._sale_lines_taxes = {} self.field_journal_id = self.default_journal['id'] def clear_left_panel(self): self.message_bar.set('system_ready') self.field_party.setText('') self.field_salesman.setText('') self.field_salesman_id = None self.field_invoice_type.set_from_id('') self.field_party_id = None self.field_agent_id = None self.field_payment_term_id = self.default_payment_term['id'] self.field_payment_term.setText(self.default_payment_term['name']) self.field_date.setText('') self.field_global_discount_ask.setText('') self.field_amount.zero() self.field_order_number.setText('') self.current_comment = '' if self.field_delivery_charge: self.field_delivery_charge.set_from_id('') if self.field_table_assigned: self.field_table_assigned.setText('') if hasattr(self, 'field_comment_ask'): # self.field_comment_ask.document().clear() pass if hasattr(self, 'field_points'): self.field_points.setText('') if self._commission_activated and hasattr(self, 'field_agent'): self.field_agent.setText('') self.field_agent_ask.setText('') self.field_commission_ask.setText('') self.field_commission_amount.setText('') if hasattr(self, 'field_position'): self.field_position.setText('') self.field_position_ask.setText('') self.model_sale_lines.reset() self.clear_input_text() self.clear_amount_text() def clear_right_panel(self): if self.is_clear_right_panel: return self.field_invoice.setText('') self.field_untaxed_amount.zero() self.field_taxes_amount.zero() self.field_total_amount.zero() self.field_change.zero() self.field_paid.zero() self.field_discount.zero() self.table_payment_lines.reset() self.is_clear_right_panel = True def state_enabled(self): pass def state_disabled(self): self.payment_ctx = {} self.clear_left_panel() self.table_sale_lines.setDisabled(True) self.set_state('disabled') if self.field_delivery_charge: self.field_delivery_charge.set_enabled(False) def createNewSale(self): self.check_empty_sale() if self.type_pos_user == 'cashier': self.state_disabled() return self.set_state('add') self.input_text_changed('') self.amount_text_changed('0') self.clear_sign() self.global_timer = 0 self.payment_ctx = {} self.is_clear_right_panel = False self.table_sale_lines.setEnabled(True) self.clear_data() self.clear_left_panel() self._sale = self._PosSale.new_sale([], { 'shop': self.shop['id'], 'invoice_type': 'P', 'company': self.company['id'], 'party': self.default_party['id'], 'sale_device': self.device['id'], 'payment_term': self.default_payment_term['id'] }, self._context) self.field_invoice_type.set_from_id(self._sale['invoice_type']) # FIXME ADD MORE self._sale.update({ 'total_amount': 0 }) self.party_id = self.default_party['id'] if self._sale.get('id'): self._set_sale_date() self.field_order_number.setText(self._sale['number']) if self.field_delivery_charge: self.field_delivery_charge.set_enabled(True) def _set_sale_date(self): if self._sale.get('sale_date'): local_date = self._sale['sale_date'] if isinstance(local_date, date): local_date = local_date.isoformat() self.field_date.setText(local_date) def _search_product(self, code): domain = [ ('template.salable', '=', True), ('template.account_category', '!=', None), ] domain.append(['OR', ('barcode', '=', code), ('code', '=', code), ]) products = self.Product.find(domain) if not products or len(products) > 1: self.message_bar.set('product_not_found') return False else: product = products[0] return product def check_salesman(self): if self.salesman_required and not self.field_salesman_id: dialog = self.dialog('missing_salesman') dialog.exec_() return False return True def add_product(self, id=None, code=None, product=None): if self._state == 'disabled': self.message_bar.set('must_load_or_create_sale') return product_id = None if id: product_id = id elif code: # REMOVE ME product = self._search_product(code) if product: product_id = product['id'] elif product: product_id = product['id'] if not product_id: self._state = 'warning' return data = { 'sale_id': self._sale['id'], 'product_id': product_id, 'qty': 1 } res = self.ModSale.faster_add_product(data) self._sale_line = res self._current_line_id = res['id'] self.add_sale_line(res) self._sale_line['product'] = product self.update_total_amount() self.set_state('add') def _check_stock_quantity(self, product, request_qty): if self._password_admin and product['quantity'] < request_qty: self.dialog_force_assign.exec_() password = self.field_password_force_assign_ask.text() self.field_password_force_assign_ask.setText('') if password != self._password_admin: self.message_bar.set('not_can_force_assign') return False return True def add_sale_line(self, record): if not record.get('unit.symbol'): record['unit.symbol'] = record['unit']['symbol'] if isinstance(record['product'], dict): 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'] rec = self.model_sale_lines.add_record(record) self.field_amount.setText(rec['amount_w_tax']) def sale_line_selected(self, product): if self._state == 'cash': return self._current_line_id = product['id'] self.label_product.setText(product['product.template.name']) self.row_field_description.setText(product['description']) self.row_field_qty.setValue(float(product['quantity'])) self.row_field_price.setText(str(product['unit_price_w_tax'])) # self.row_field_note.setText(str(product['note'])) self.dialog_product_edit.show() self.row_field_note.setFocus() def create_dialog_sale_line(self): self.state_line = {} vbox_product = QVBoxLayout() grid = QGridLayout() qty = 2 self.label_product = QLabel() self.label_product.setAlignment(alignCenter) self.label_product.setObjectName('label_product') vbox_product.addWidget(self.label_product) self.row_field_description = QLineEdit() self.row_field_description.setObjectName('row_field_description') self.row_field_description.textChanged.connect( lambda: self.update_sale_line('description') ) grid.addWidget(self.row_field_description, 1, 1, 1, 2) if self._config.get('show_fractions'): label_fraction = QLabel(self.tr('FRACTION:')) label_fraction.setObjectName('label_fraction') grid.addWidget(label_fraction, 2, 1) self.field_combobox_fraction = ComboBox(self, 'fraction', {'values': FRACTIONS}) grid.addWidget(self.field_combobox_fraction, 2, 2) self.field_combobox_fraction.currentIndexChanged.connect( lambda: self.update_sale_line('qty_fraction') ) label_qty = QLabel(self.tr('QUANTITY:')) label_qty.setObjectName('label_qty') grid.addWidget(label_qty, 3, 1) self.row_field_qty = QDoubleSpinBox() self.row_field_qty.setObjectName('row_field_qty') self.row_field_qty.setMinimum(0) self.row_field_qty.setMaximum(100000) if self._config.get('decimals_digits_quantity'): qty = self._config['decimals_digits_quantity'] self.row_field_qty.setDecimals(qty) self.row_field_qty.setAlignment(alignCenter) grid.addWidget(self.row_field_qty, 3, 2) self.row_field_qty.valueChanged.connect( lambda: self.update_sale_line('quantity') ) label_price = QLabel(self.tr('UNIT PRICE:')) label_price.setObjectName('label_price') grid.addWidget(label_price, 4, 1) self.row_field_price = FieldMoney(self, 'row_field_price', {}, readonly=False) self.row_field_price.setObjectName('row_field_price') grid.addWidget(self.row_field_price, 4, 2) self.row_field_price.textChanged.connect( lambda: self.update_sale_line('unit_price') ) self.row_field_note = QTextEdit('') self.row_field_note.setObjectName('row_field_note') grid.addWidget(self.row_field_note, 5, 1, 5, 2) self.row_field_note.textChanged.connect( lambda: self.update_sale_line('note') ) vbox_product.addLayout(grid) self.dialog_product_edit = QuickDialog(self, 'action', widgets=[vbox_product]) self.dialog_product_edit.accepted.connect(self.dialog_product_edit_accepted) 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 = {} # self.field_combobox_fraction.set_from_id(1) def setup_sale_line(self): product_code = { 'name': 'product.code', 'align': alignRight, 'description': self.tr('COD'), 'width': 80 } product = { 'name': 'product.template.name', 'align': alignLeft, 'description': self.tr('NAME'), 'width': STRETCH } description = { 'name': 'description', 'align': alignLeft, 'description': self.tr('DESCRIPTION'), 'width': 180 } uom = { 'name': 'unit.symbol', 'align': alignHCenter, 'description': self.tr('UNIT'), 'width': 50 } qty = { 'name': 'quantity', 'format': '{:3,.%sf}', 'align': alignRight, 'description': self.tr('QTY'), 'digits': ('unit.symbol', CONVERSION_DIGITS), 'width': 80 } discount = { 'name': 'discount', 'format': '{0:.0%}', 'align': alignRight, 'description': self.tr('DISC'), 'width': 50 } amount = { 'name': 'amount_w_tax', 'format': '{:5,.1f}', 'align': alignRight, 'description': self.tr('SUBTOTAL'), 'width': 100 } note = { 'name': 'note', 'align': alignLeft, 'description': self.tr('NOTE'), 'invisible': True, 'width': 100 } unit_price = { 'name': 'unit_price_w_tax', 'format': '{:5,.1f}', 'align': alignLeft, 'description': self.tr('UNIT PRICE W TAX'), 'invisible': True, 'width': 100 } qty_fraction = { 'name': 'qty_fraction', 'align': alignHCenter, 'description': self.tr('FRAC'), 'width': 50 } self.fields_sale_line = [product, uom, qty, discount, amount, note, unit_price] if self.enviroment == 'retail': self.fields_sale_line.insert(0, product_code) if self._config.get('show_description_pos'): self.fields_sale_line.insert(2, description) if self._config.get('show_fractions'): self.fields_sale_line.insert(4, qty_fraction) self.model_sale_lines = TableModel('sale.line', self.fields_sale_line) def setup_payment(self): pay_fields = [{ 'name': 'statement', 'align': alignLeft, 'description': self.tr('STATEMENT JOURNAL'), }, { 'name': 'amount', 'align': alignRight, 'format': '{:5,.1f}', 'description': self.tr('AMOUNT'), }, { 'name': 'voucher', 'align': alignCenter, 'description': self.tr('VOUCHER'), }] self.table_payment_lines = TableModel('account.statement.line', pay_fields) def action_table(self): self.table_sale_lines.setFocus() def on_change_line_selected(self, key): self.table_sale_lines.moved_selection(key) def action_delete_line(self): if self.model_sale_lines.rowCount() <= 0 or self._state == 'cash': return self.table_sale_lines.setFocus() removed_item = self.table_sale_lines.delete_item() self.ModSaleLine.delete([removed_item['id']]) self.set_amounts() self.update_total_amount() # self.clear_right_panel() self._current_line_id = None self.setFocus() self.label_input.setFocus() if self.enviroment == 'restaurant': if removed_item and self.print_order: self.action_print_order(self._sale['id'], removed_item) if self._config['tip_product.code'] == removed_item['product.code']: self._PosSale.write([self._sale['id']], {'tip': None}) def set_discount(self, eval_value, lines_ids=[]): res = False try: value = round(float(str(eval_value)), 6) except ValueError: logging.warning('ValueError > ', ValueError) return if float(value) <= 0: return if not lines_ids: target_lines = [self._current_line_id] else: target_lines = lines_ids records = self.ModSaleLine.faster_set_discount({ 'line_ids': target_lines, 'value': value }) for rec in records: self.model_sale_lines.update_record(rec) if records: res = True self.set_amounts() return res def set_unit_price(self, value): rec = self.ModSaleLine.set_faster_unit_price({ 'id': self._current_line_id, 'value': value, }) if rec: self.model_sale_lines.update_record(rec) self.update_total_amount() return True return False def add_payment(self, amount, cash_received=0, change=0): voucher_number = None if self._journals[self.field_journal_id]['require_voucher']: self.dialog_voucher.exec_() voucher_number = self.field_voucher_ask.text() if voucher_number is None or voucher_number == '': return self.add_payment(amount) res = self.ModSale.faster_add_payment({ 'sale_id': self._sale['id'], 'journal_id': self.field_journal_id, 'amount': to_numeric(amount), 'voucher_number': voucher_number, 'cash_received': to_numeric(cash_received), 'change': to_numeric(change) }) if res.get('msg') not in ('missing_money', 'ok'): self.dialog(res['msg']) return res self.table_payment_lines.add_record(res) return res def create_dialog_help(self): from .help import Help help = Help(self) help.show() def set_keys(self): self.keys_numbers = list(range(Qt.Key_0, Qt.Key_9 + 1)) self.keys_alpha = list(range(Qt.Key_A, Qt.Key_Z + 1)) self.keys_period = [Qt.Key_Period] self.show_keys = self.keys_numbers + self.keys_alpha + self.keys_period self.keys_special = [Qt.Key_Asterisk, Qt.Key_Comma, Qt.Key_Minus, Qt.Key_Slash] self.keys_input = [Qt.Key_Backspace] self.keys_input.extend(self.keys_special) self.keys_input.extend(self.show_keys) self.keys_input.extend(self.keys_numbers) self.keys_input.extend([Qt.Key_Return, Qt.Key_Plus]) def set_state(self, state='add'): self._state = state state = STATES[state] self._re = state['re'] if not self.type_pos_user == 'order': if not self.buttonpad.stacked.stacked.currentWidget(): return if state['button']: self.buttonpad.stacked.stacked.setCurrentWidget( getattr(self.buttonpad.stacked, state['button']) ) if not self.tablet_mode: self.buttonpad.stacked.stacked.currentWidget().setVisible(True) else: self.buttonpad.stacked.stacked.currentWidget().setVisible(False) def key_pressed(self, text): if not self._sign and self._state != 'cash': if self._re.match(self._input_text + text): self.input_text_changed(text) else: if RE_SIGN['quantity'].match(self._amount_text + text): self.amount_text_changed(text) def clear_sign(self): self._sign = None self.field_sign.setText(' {0} '.format(' ')) def sign_text_changed(self, sign): self._sign = sign self.field_sign.setText(' {0} '.format(sign)) if hasattr(self, '_sale_line') and self._sale_line: if sign == '-': self.message_bar.set('enter_discount') elif sign == '/': self.message_bar.set('enter_new_price') elif sign == '*': self.message_bar.set('enter_quantity') if self.active_weighing and self._sale_line['unit_symbol'] != 'u': self.action_read_weight() def key_special_pressed(self, value): self.clear_amount_text() self.clear_input_text() if value not in ['-', '/', '*']: return self.sign_text_changed(value) def key_backspace_pressed(self): if self._sign or self._state == 'cash': self._amount_text = self._amount_text[:-1] self.amount_text_changed() else: self._input_text = self._input_text[:-1] self.input_text_changed() def set_text(self, text): if not self._state == 'cash': self.input_text_changed(text) else: self.amount_text_changed(text) def clear_input_text(self): self.input_text_changed('') def clear_amount_text(self): self._amount_text = '0' self.amount_text_changed() def keyPressEvent(self, event): self._keyStates[event.key()] = True key = event.key() if self._state == 'add' and key not in self.keys_input and \ key not in (Qt.Key_Enter, Qt.Key_End): # Clear ui context when keys function are pressed self._clear_context() if key in (Qt.Key_Return, Qt.Key_Plus): self.button_plus_pressed() elif key in self.show_keys: # No allow change quantity o discount in state == cash if self._state == 'cash' and key not in self.keys_numbers: return self.key_pressed(event.text()) elif key in self.keys_special: if self._state == 'cash' or not self._current_line_id: return self.key_special_pressed(event.text()) elif key == Qt.Key_Backspace: self.key_backspace_pressed() elif key == Qt.Key_Escape: self.close() elif key == Qt.Key_F1: self.create_dialog_help() elif key == Qt.Key_F9: self.action_search_sale() elif key == Qt.Key_F11: self.action_new_sale() elif key == Qt.Key_F7: self.action_print_sale() elif self._state == 'disabled': self.message_bar.set('must_load_or_create_sale') return elif key in (Qt.Key_Enter, Qt.Key_End): if self.type_pos_user in ['order', 'salesman']: return if self._state == 'add': self.button_accept_pressed() elif self._state in ['accept', 'cash']: self.button_cash_pressed() elif key == Qt.Key_F2: self.action_search_product() elif key == Qt.Key_F3: self.action_payment() elif key == Qt.Key_F4: self.action_party() elif key == Qt.Key_F5: self.action_global_discount() elif key == Qt.Key_F6: self.action_print_order() elif key == Qt.Key_F8: self.action_payment_term() elif key == Qt.Key_F10: self.action_table() elif key == Qt.Key_F12: self.action_cancel() elif key == Qt.Key_Home: self.action_salesman() elif key == Qt.Key_Down or key == Qt.Key_Up: self.on_change_line_selected(key) elif key == Qt.Key_Delete: self.action_delete_line() elif key == Qt.Key_Insert: self.action_position() elif key == Qt.Key_Semicolon and self._commission_activated: sale = self.get_current_sale() if sale['state'] == 'draft' and self._state not in ['accept', 'cash']: self.action_agent() elif key == Qt.Key_QuoteDbl: self.action_comment() elif key == Qt.Key_Question: self.action_tax() else: pass @property def state(self): return self._state class DoInvoice(QThread): """ Process invoices using a thread """ sigDoInvoice = pyqtSignal() def __init__(self, main, context): QThread.__init__(self) def run(self): self.sigDoInvoice.emit()