#!/usr/bin/env python # -*- coding: UTF-8 -*- import sys import os import logging import base64 from decimal import Decimal from PyQt5.QtCore import Qt, QThread, pyqtSignal, QDate from .tools import get_icon, to_float, to_numeric from .dialogs import ( Help, ControlPanel, SearchSale, SearchParty, SearchProduct, Position, Comment, DialogPayment, DialogSalesman, DialogVoucher, DialogPrintInvoice, DialogGlobalDiscount, DialogPaymentTerm, DialogStock, DialogOrder, DialogForceAssign, DialogTaxes, DialogCancelInvoice, SaleLine, DialogAgent, DialogConsumer, DialogManageTables, DialogDeliveryMen, DialogChannel, DialogTableMoneyCount, DialogGlobalDiscountTable, DeliveryPartySelected, DialogTableDeliveryParty, DialogTableSaleConsumer, SaleConsumerSelected ) from datetime import datetime, timedelta, date from collections import OrderedDict from PyQt5.QtWidgets import ( QLabel, QHBoxLayout, QVBoxLayout, QWidget, QLineEdit ) from app.commons.action import Action 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 from app.commons.model import TableModel, Modules, TableModelEdit from app.commons.frontwindow import FrontWindow from app.commons.menu_buttons import MenuDash from .proxy import FastModel from .localdb import LocalStore from .reporting import Receipt from .buttonpad import Buttonpad, ButtonsFunction from .states import STATES, RE_SIGN from .constants import ( PATH_PRINTERS, DELTA_LOCALE, STRETCH, alignRight, alignLeft, alignCenter, alignHCenter, alignVCenter, DIALOG_REPLY_NO, DIALOG_REPLY_YES, ZERO, RATE_CREDIT_LIMIT, SCREENS, FILE_BANNER, CONVERSION_DIGITS ) MINIMUM_DATE = QDate(0, 1, 1) 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) _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 self.payment_ctx = {} self.set_keys() self.stock_context = None self.ctx = self._context self.ctx['params'] = params self.label_color = 'gray' self.label_color_2 = '' if _theme == 'dark': self.label_color = 'light' self.label_color_2 = 'orange' 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_delivery_party() self.setup_sale_consumer() self.setup_payment() self.set_domains() self.setup_money_count() 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() self.create_statusbar() self.window().showMaximized() 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) if self.cache_local: 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!')), 'without_stock_quantity': ('info', self.tr('PRODUCT WITHOUT STOCK: %s')), 'not_sale': ('info', self.tr('NOT SALE!...')), 'statement_created': ('info', self.tr('STATEMENTS CREATED!')), 'statement_finish': ('info', self.tr('STATEMENTS CLOSED!')), 'order_dispatched': ('info', self.tr('ORDER DISPATCHED!')), 'error_order_dispatched': ('error', self.tr('ERROR TO DISPATCHED ORDER!')), }) 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._web_channel = self.Module.find([ ('name', '=', 'sale_web_channel'), ('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', ) } _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: self.PartyConsumer = FastModel('party.consumer', self.ctx) _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']) self.User = FastModel('res.user', self.ctx) self._user, = self.User.find([('login', '=', self.user)]) self.Company = FastModel('company.company', self.ctx) self._company, = self.Company.find([('id', '=', 1)]) self.logo = self._company['logo'] 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.Shop = FastModel('sale.delivery_party', self.ctx) self.SaleDiscont = FastModel('sale.discount', 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.DeliveryParty = FastModel('sale.delivery_party', self.ctx) self.Taxes = FastModel('account.tax', self.ctx) self.ActionReport = FastModel('ir.action.report', self.ctx) if self._commission_activated: self.Agent = FastModel('commission.agent', self.ctx) self.Comission = FastModel('commission', 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.salesman_ids = [s['id'] for s in self.shop['salesmans']] \ if self.shop.get('salesmans') else [] dom_salesman = [ ('company', '=', self.company['id']), ] if self.salesman_ids: dom_salesman.append(('id', 'in', self.salesman_ids)) self.employees = self.Employee.find(dom_salesman) self.discounts = self.shop['discounts'] if self.shop.get('discounts') else [] self.delivery_man_table = self.shop['delivery_man'] if self.shop.get('delivery_man') else [] self.delivery_man = [d for d in self.delivery_man_table if d['active']] 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._password_force_assign = self._config.get('password_force_assign') 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_wizard_new_sale() self.dialog_control_panel = ControlPanel(self).get(self.menu_control_panel) self.dialog_money_count = DialogTableMoneyCount(self).get() 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() self.dialog_comment = Comment(self).get() self.dialog_payment = DialogPayment(self).get() self.dialog_salesman = DialogSalesman(self).get() self.dialog_voucher = DialogVoucher(self).get() self.dialog_print_invoice = DialogPrintInvoice(self).get() self.dialog_global_discount = DialogGlobalDiscount(self).get() self.dialog_global_discount_table = DialogGlobalDiscountTable(self).get() self.dialog_payment_term = DialogPaymentTerm(self).get() self.dialog_search_sales.activate_counter() self.dialog_product_stock = DialogStock(self.dialog_search_products).get() self.dialog_order = DialogOrder(self).get() self.dialog_force_assign = DialogForceAssign(self).get() self.field_password_force_assign_ask.setEchoMode(QLineEdit.Password) self.dialog_tax = DialogTaxes(self).get() self.dialog_cancel_invoice = DialogCancelInvoice(self).get() self.dialog_product_edit = SaleLine(self).get() self.dialog_table_delivery_party = DialogTableDeliveryParty(self).get() self.dialog_delivery_party_selected = DeliveryPartySelected(self).get() self.dialog_delivery_men = DialogDeliveryMen(self).get() if self._web_channel: self.dialog_channel = DialogChannel(self).get() if self._commission_activated: self.dialog_agent = DialogAgent(self).get() if self.enviroment == 'restaurant' and self._sale_pos_restaurant: self.dialog_table_sale_consumer = DialogTableSaleConsumer(self).get() self.dialog_sale_consumer_selected = SaleConsumerSelected(self).get() self.dialog_search_consumer = DialogConsumer(self).get() self.dialog_manage_tables = DialogManageTables(self).get() 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 locale_logo = '' if self.logo: locale_logo = os.path.join(os.path.abspath( os.path.dirname(__file__)), 'logo.png') f = open(locale_logo, "wb") f.write(base64.decodestring(self.logo.encode())) f.close() self.receipt_sale = Receipt(ctx_printing, logo=locale_logo) # 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], 'profile': self.profile_printer, } 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 if not self._check_quantity(): return self._PosSale.to_quote(self._sale['id'], self._context) if self.model_sale_lines.rowCount() > 0: if self.check_salesman(): self.state_disabled() try: self.ModSale.generate_shipment({ 'sale_id': self._sale['id'], }) self.dialog('order_dispatched') except: self.dialog('error_order_dispatched') 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 = QWidget() panel_left.setObjectName('panel_left') vbox_left = QVBoxLayout() if self.screen_size == 'small': max_size = 620 else: max_size = 1300 panel_left.setMaximumWidth(max_size) panel_left.setLayout(vbox_left) panel_right = QVBoxLayout() panel_right.setObjectName('panel_right') left_head = QHBoxLayout() left_invoice = QHBoxLayout() left_table = None # LEFT HEAD COMPONENTS self.message_bar = MessageBar() self.field_amount = FieldNumeric(self, 'amount', {}) self.field_amount.setObjectName('field_amount') self.field_sign = QLabel(' ') self.field_sign.setObjectName('field_sign') left_head.addLayout(self.message_bar, 1) left_head.addWidget(self.field_sign, 0) left_head.addWidget(self.field_amount, 1) # LEFT INVOICE COMPONENTS self.label_input = QLabel() self.label_input.setFocus() self.label_input.setObjectName('label_input') size_input = 100 if self.screen_size == 'large': size_input = 300 self.label_input.setMaximumWidth(size_input) invoice_type = [('P', ('POS'))] if self.shop.get('manual_authorization'): invoice_type.append(('M', 'MANUAL')) if self.shop.get('electronic_authorization'): invoice_type.append(('1', ('VENTA ELECTRONICA'))) self.field_invoice_type = ComboBox( self, 'invoice_type', { 'values': invoice_type, 'on_change': 'action_invoice_type_selection_changed' }) left_invoice.addWidget(self.label_input, 1) left_invoice.addWidget(self.field_invoice_type, 0) _label_type_invoice = QLabel(self.tr('INVOICE:')) _label_type_invoice.setObjectName('label_invoice') _label_type_invoice.setAlignment(alignRight | alignVCenter) self.field_invoice = QLineEdit('') self.field_invoice.setReadOnly(True) self.field_invoice.setObjectName('field_invoice') left_invoice.addWidget(self.field_invoice, 1) self.field_state = QLineEdit('STATE') self.field_state.setReadOnly(True) self.field_state.setObjectName('field_state') left_invoice.addWidget(self.field_state) # LEFT TABLE COMPONENTS values = self.get_menu_control_panel() self.menu_control_panel = ButtonsFunction(self, values=values) if self.enviroment == 'restaurant': values = self.get_product_by_categories() menu_dash = MenuDash(self, values, 'on_selected_item') info_fields = [ ('party', { 'name': self.tr('CUSTOMER'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': self.label_color }), ('date', { 'name': self.tr('DATE'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': self.label_color }), ('salesman', { 'name': self.tr('SALESMAN'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': self.label_color }), ('order_number', { 'name': self.tr('No ORDER'), 'placeholder': False, 'readonly': True, 'size': self.screen_size, 'color': self.label_color }), ('payment_term', { 'name': self.tr('PAYMENT TERM'), 'readonly': True, 'invisible': False, 'placeholder': False, 'size': self.screen_size, 'color': self.label_color }), ('delivery_men', { 'name': self.tr('DELIVERY MEN'), 'placeholder': False, 'size': self.screen_size, 'color': self.label_color }) ] if self._web_channel: self.Channel = FastModel('sale.web_channel', self.ctx) self.channels = self.Channel.find([]) info_fields.append( ('channel', { 'name': self.tr('CHANNEL'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': self.label_color })) if self._config['show_position_pos']: info_fields.append(('position', { 'name': self.tr('POSITION'), 'readonly': True, 'placeholder': False, 'size': self.screen_size, 'color': self.label_color })) if self._commission_activated and self._config['show_agent_pos']: info_fields.append(('agent', { 'name': self.tr('AGENT'), 'placeholder': False, 'readonly': True, 'size': self.screen_size, 'color': self.label_color })) _cols = 2 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': self.label_color })) self.field_table_assigned = None if self._sale_pos_restaurant: # info_fields.append( # ('delivery_time', { # 'name': self.tr('DELIVERY TIME'), # 'placeholder': False, # 'type': 'money', # 'size': self.screen_size, # 'color': self.label_color # })) DELIVERY_WAY = [ ('', ''), ('take_away', self.tr('TAKE AWAY')), ('delivery', self.tr('DELIVERY')), ('table', self.tr('TABLE')) ] info_fields.append( ('delivery_way', { 'name': self.tr('DELIVERY WAY'), 'placeholder': False, 'type': 'selection', 'on_change': 'action_delivery_way_selection_changed', 'values': DELIVERY_WAY, 'size': self.screen_size, 'color': self.label_color })) info_fields.append( ('table_assigned', { 'name': self.tr('ASSIGNED TABLE'), 'placeholder': False, 'size': self.screen_size, 'color': self.label_color })) col_sizes_tlines = [field['width'] for field in self.fields_sale_line] self.table_sale_lines = TableView('model_sale_lines', self.model_sale_lines, col_sizes_tlines, method_selected_row=self.sale_line_selected ) for i, f in enumerate(self.model_sale_lines._fields, 0): if f.get('invisible'): self.table_sale_lines.hideColumn(i) invisible = False if self.enviroment == 'restaurant': invisible = True _fields_amounts = [ ('untaxed_amount', { 'name': self.tr('SUBTOTAL'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': self.label_color, 'invisible': invisible, }), ('taxes_amount', { 'name': self.tr('TAXES'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': self.label_color, 'invisible': invisible, }), ('discount', { 'name': self.tr('DISCOUNT'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': self.label_color, 'invisible': invisible, }), ('total_amount', { 'name': self.tr('TOTAL'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': 'black', 'font_size': 'big', }), ('paid', { 'name': self.tr('PAID'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': self.label_color, }), ('pending', { 'name': self.tr('PENDIENTE'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': self.label_color_2 or 'blue' }), ('change', { 'name': self.tr('CHANGE'), 'readonly': True, 'type': 'money', 'size': self.screen_size, 'color': self.label_color_2 or '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]) vbox_left.addLayout(left_head, 0) vbox_left.addLayout(left_invoice, 0) vbox_left.addWidget(self.table_sale_lines, 1) panel_right.addWidget(self.pixmap_pos, 0) self.grid_info = GridForm(self, OrderedDict(info_fields), col=_cols) if self.enviroment == 'restaurant': vbox_left.addLayout(self.buttonpad.functions, 1) vbox_left.addLayout(self.grid_info, 0) panel_right.addLayout(menu_dash, 1) panel_right.addLayout(self.grid_amounts, 0) panel_right.addLayout(self.buttonpad.stacked, 0) else: vbox_left.addLayout(self.grid_info, 1) 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) panels.addWidget(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 == 'disabled': self.message_bar.set('not_sale') elif 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) product_id = self.product_id if product_id and self.stock_context: _product, = self.Product.find([ ('id', '=', product_id)], ctx=self.stock_context ) if _product and 'quantity' in _product: if not self._check_stock_quantity(_product, quantity): return False if self._current_line_id: self._sale_line['quantity'] = float(quantity) rec = self.ModSaleLine.faster_set_quantity(self._sale_line) 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 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['product']['sale_price_w_tax'] if price_w_tax <= Decimal(value): # Change unit price discount_valid = self.set_unit_price(value) self.set_discount(0) 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'] res = self.add_payment(amount_to_add, all_money, change) if res.get('msg') == 'statement_closed': self.dialog('statement_closed') return False if change < ZERO: self.field_pending.setText(abs(change)) else: self.field_pending.setText(str(ZERO)) self.set_amount_received(all_money) if res['residual_amount'] < 0: 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._sale.update({ 'residual_amount': res['residual_amount'] }) residual_amount = self._sale['residual_amount'] if self._sale['residual_amount'] <= 0: 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'] }) # self.set_state('finished') 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() self.message_bar.set('system_ready') 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_duplicate_sale(self): if self.sale_customer_selected: res = self.ModSale.duplicate_sale({ 'sale_id': self.sale_customer_selected, }) self.load_sale(res['sale_id']) self.dialog_search_consumer.close() self.dialog_table_sale_consumer.close() self.dialog_sale_consumer_selected.close() def button_create_delivery_party(self): self.clear_delivery_party() self.dialog_delivery_party_selected.show() def button_sale_consumer_history(self): if self._consumer: delta = str(datetime.now() - timedelta(180)) dom = [ ('create_date', '>=', delta), ('state', 'in', ['processing', 'done']), ('consumer', '=', self._consumer['id']), ] self.model_sale_consumer.reset() sales = self.ModSale.find(dom, limit=10) for record in sales: self.model_sale_consumer.add_record(record) self.dialog_table_sale_consumer.exec_() else: pass def button_accept_pressed(self): if not self._check_quantity(): return 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}) if msg: self.message_bar.set(msg) return self.set_amounts(res) self.field_invoice.setText(res['invoice_number']) self.field_amount.setText('') if self.type_pos_user == 'salesman': self.print_invoice(sale_id) res = self._print_order(sale_id, 'delivery') self.createNewSale() return if self.type_pos_user != 'cashier': self.field_invoice_type.set_enabled(False) self.message_bar.set('enter_payment', self.default_journal['name']) self.set_state('cash') self.buttonpad.setFocus() self.label_input.setFocus() def action_reservations(self): logging.info('Buscando reservas.....') def action_tables(self): self.dialog_manage_tables.exec_() def action_tip(self): if self._state in ['cash']: return if self._config['tip_product'] 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.set_unit_price(eval_value) self.update_total_amount() def action_salesman(self): if self._state in ['cash']: return self.dialog_salesman.exec_() def action_delivery_men(self): self.dialog_delivery_men.exec_() def action_delivery_men_panel(self): self.dialog_control_panel.close() self.dialog_table_delivery_party.exec_() def action_control_panel(self): self.dialog_control_panel.exec_() def action_help(self): Help(self).show() def clear_money_count_table(self): new_data = [[d[0], 0, 0] for d in self.model_money_count._data] self.model_money_count._data = new_data def dialog_money_count_accepted(self): return True def action_open_statement(self): if not self.dialog_money_count.exec_(): return res = self.ModSale.faster_open_statement({ 'device': self.device['id'], 'total_money': Decimal( self.row_field_total_money.text().replace(',', '') ), }) if res['result']: self.dialog('statement_created') self.row_field_total_money.setText('0') self.clear_money_count_table() def action_closed_statement(self): if not self.dialog_money_count.exec_(): return res = self.ModSale.faster_close_statement({ 'device': self.device['id'], 'data': self.model_money_count._data, }) if res['result']: self.dialog('statement_finish') self.row_field_total_money.setText('0') self.clear_money_count_table() def action_table_discount(self): self.dialog_global_discount_table.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}) self.field_payment_term.setText(self._payment_terms[str(self.field_payment_term_id)]['name']) 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_delivery_way_selection_changed(self, index): val = self.field_delivery_way.get_id() if val: self._PosSale.write([self._sale['id']], {'delivery_way': val}) def action_invoice_type_selection_changed(self, index): val = self.field_invoice_type.get_id() if val and self._sale and self._sale.get('id'): 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_consumer(self): self.clear_dialog_consumer() self.dialog_search_consumer.show() self.row_field_phone.setFocus() 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 in ['order', 'delivery']: 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 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): if self._state in ['cash']: return self.dialog_search_parties.clear_rows() self.dialog_search_parties.execute() def action_global_discount(self, sale_id=None): if self._state in ['accept', 'cash']: return self.dialog_global_discount.exec_() discount = self.field_global_discount_ask.text() if discount and discount.isdigit(): self.validate_discount(discount) def validate_discount(self, discount): if self.model_sale_lines.rowCount() > 0: lines = [line['id'] for line in self.model_sale_lines._data] res = self.set_discount(int(discount), lines) if not res: self.message_bar.set('discount_not_valid') return False 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_channel(self): if self._state != 'cash': self.dialog_channel.exec_() 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 self._state in ['cash']: return 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._state == 'disabled': return 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(2)) 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), ] fields = self.dialog_search_sales.fields_names print('fields ...', fields) sales = self.ModSale.find(dom, fields=fields, order=[('id', 'DESC')]) print('sales ...', sales) 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() self.field_invoice_type.set_enabled(True) 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=None): if not party_id: 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'] if party['addresses'] else '', 'shipment_address': party['addresses'][0]['id'] if party['addresses'] else '', } payment_term = party.get('customer_payment_term') if payment_term: values['payment_term'] = payment_term['id'] self.field_payment_term.setText(str(payment_term['name'])) self.field_payment_term_id = payment_term['id'] 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.field_state.setText(sale['state'] 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 sale.get('delivery_charge'): self.field_delivery_charge.set_from_id(sale['delivery_charge']) if sale.get('table_assigned'): self.field_table_assigned.setText(sale['table_assigned.name'] or '') if sale.get('channel'): self.field_channel.setText(sale['channel']['rec_name'].upper()) if sale.get('delivery_way'): self.field_delivery_way.set_from_id(sale['delivery_way']) if sale.get('delivery_party'): self.field_delivery_men.setText(sale['delivery_party']['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.setText('') 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) residual_amount = 0 if res.get('residual_amount'): residual_amount = res['residual_amount'] self.field_pending.setText(residual_amount) 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_menu_control_panel(self): menu_dash = [ ['button_open', self.tr('OPEN STATEMENTS'), 'action_open_statement'], ['button_closed', self.tr('CLOSED STATEMENTS'), 'action_closed_statement'], # ['button_money', self.tr('COUNT MONEY'), 'action_count_money'], ['button_discount', self.tr('GLOBAL DISCOUNT'), 'action_table_discount'], ['button_delivery_men', self.tr('DELIVERY MEN'), 'action_delivery_men_panel'], ['button_help', self.tr('HELP'), 'action_help'], ] return menu_dash 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 on_selected_salesman(self, salesman_id): if salesman_id: self._PosSale.write([self._sale['id']], {'salesman': salesman_id}) sale, = self.ModSale.find([('id', '=', self._sale['id'])]) self.field_salesman.setText(sale['salesman']['name'] or '') self.field_salesman_id = salesman_id self.dialog_salesman.close() def on_selected_discount(self, discount): if discount: self.validate_discount(discount) self.dialog_global_discount_table.close() self.dialog_control_panel.close() def on_selected_delivery_men(self, delivery_men_id): if delivery_men_id: self._PosSale.write([self._sale['id']], {'delivery_party': delivery_men_id}) sale, = self.ModSale.find([('id', '=', self._sale['id'])]) self.field_delivery_men.setText(sale['delivery_party']['name'] or '') self.field_delivery_men_id = delivery_men_id self.dialog_delivery_men.close() def on_selected_payment_term(self, payterm_id): if payterm_id: self.field_payment_term_id = payterm_id self.action_payment_term_selection_changed() self.dialog_payment_term.close() def on_selected_channel(self, channel_id): if channel_id: channel, = self.channels = self.Channel.find([ ('id', '=', int(channel_id)) ]) self.field_channel.setText(channel['rec_name'].upper()) self._PosSale.write([self._sale['id']], {'channel': channel_id}) self.dialog_channel.close() def on_selected_payment(self, payment_id): if payment_id: self.field_journal_id = payment_id self.dialog_payment.close() name_ = self._journals[payment_id]['name'] self.message_bar.set('enter_payment', name_) 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), ('template.salable', '=', True), ] for tw in target_words: if len(tw) <= 1: continue clause = [ 'OR', ('template.name', 'ilike', '%{:}%'.format(tw)), ('description', 'ilike', '%{:}%'.format(tw)), ('code', 'ilike', '%{:}%'.format(tw)) ] domain.append(clause) if not self._sale_pos_restaurant and self.shop.get('product_categories'): categories_id = [ct['id'] for ct in self.shop['product_categories']] domain.append(('template.categories', 'in', categories_id)) if not domain: return if self.cache_local: domain = [clause] products = self.store.find_product_elastic(domain, limit=100) else: fields = self.dialog_search_products.fields_names products = self.Product.find(domain, fields=fields, 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) fields_names = self.dialog_search_parties.fields_names parties = self.Party.find(domain, fields=fields_names) self.dialog_search_parties.set_from_values(parties) 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('P') 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.setText('') self.field_order_number.setText('') self.field_state.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.field_pending.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): if self.enviroment == 'restaurant' and self._sale_pos_restaurant: self.clear_dialog_consumer() self.check_empty_sale() if self.type_pos_user == 'cashier': self.message_bar.set('not_sale') 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.consumer_id = None self.payment_ctx = {} self.is_clear_right_panel = False self.table_sale_lines.setEnabled(True) self.field_invoice_type.set_enabled(True) self.clear_data() self.clear_left_panel() self.message_bar.set('system_ready') 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_party.setText(self._sale['party_name']) self.field_invoice_type.set_from_id(self._sale['invoice_type']) self.field_state.setText(self._sale['state']) # FIXME ADD MORE self._sale.update({ 'total_amount': 0 }) if hasattr(self, 'field_channel'): self.field_channel.setText('') if hasattr(self, 'field_delivery_way'): self.field_delivery_way.set_from_id('') if hasattr(self, 'field_delivery_men'): self.field_delivery_men.setText('') 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)]) if self.cache_local: clause = ['OR', ('barcode', '=', '%{:}%'.format(code)), ('code', '=', '%{:}%'.format(code))] domain = [clause] products = self.store.find_product_elastic(domain, limit=100) else: products = self.Product.find(domain, ctx=self.stock_context) if not products or len(products) > 1: self.message_bar.set('product_not_found') return False else: product = products[0] return product def _check_quantity(self): for line in self.model_sale_lines._data: product_id = None if isinstance(line['product'], int): product_id = line['product'] elif isinstance(line['product'], dict): product_id = line['product']['id'] if product_id: product, = self.Product.find([('id', '=', product_id)], ctx=self.stock_context) if 'quantity' in product and not self._check_stock_quantity(product): return False return True 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.product_id = product_id self.update_total_amount() self.set_state('add') def _check_stock_quantity(self, product, request_qty=None): if not isinstance(product['quantity'], (str, int, float, Decimal)): return True qty_ = Decimal(product['quantity']) if self._password_force_assign: if (request_qty and qty_ < request_qty) or (qty_ <= 0): self.message_bar.set('without_stock_quantity', product['name']) self.dialog_force_assign.exec_() password = self.field_password_force_assign_ask.text() self.field_password_force_assign_ask.setText('') if password != self._password_force_assign: self.message_bar.set('not_can_force_assign') return False else: self.message_bar.set('system_ready') 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'] if record.get('order_sended'): record['order_sended'] = '✔' rec = self.model_sale_lines.add_record(record) self.field_amount.setText(rec['amount_w_tax']) def sale_line_selected(self, line): if self._state == 'cash': return self._current_line_id = line['id'] product_id = None if isinstance(line['product'], int): product_id = line['product'] elif isinstance(line['product'], dict): product_id = line['product']['id'] self.product_id = product_id self.label_product.setText(line['product.template.name']) self.row_field_description.setText(line['description']) self.row_field_qty.setValue(float(line['quantity'])) self.row_field_price.setText(str(line['unit_price_w_tax'])) # self.row_field_note.setText(str(line['note'])) self.dialog_product_edit.show() self.row_field_note.setFocus() def sale_consumer_selected(self, data): self.sale_customer_selected = data['id'] self.row_sale_party.setText(data['party']['name']) self.row_sale_number.setText(data['number']) self.row_sale_date.setText(data['sale_date']) self.row_sale_tax_amount.setText(str(data['tax_amount'])) self.row_sale_untaxed.setText(str(data['untaxed_amount'])) self.row_sale_total.setText(str(data['total_amount_cache'])) self.model_sale_customer_lines.reset() for line in data['lines']: self.model_sale_customer_lines.add_record(line) self.dialog_sale_consumer_selected.exec_() def clear_delivery_party(self): self._delivery_party_selected = None self.row_delivery_men.setText('') self.row_id_number.setText('') self.row_phone.setText('') self.row_number_plate.setText('') self.row_type_vehicle.set_from_id('') self.row_delivery_men_active.setChecked(True) def delivery_party_selected(self, data): # return self._delivery_party_selected = data['id'] self.row_delivery_men.setText(data['rec_name']) self.row_id_number.setText(data['party']['id_number']) self.row_number_plate.setText(data['number_plate']) if data.get('type_vehicle'): self.row_type_vehicle.set_from_id(data['type_vehicle']) self.row_delivery_men_active.setChecked(data['active']) self.dialog_delivery_party_selected.show() self.row_delivery_men_active.setFocus() def setup_money_count(self): money = [[100000, 0, 0], [50000, 0, 0], [20000, 0, 0], [10000, 0, 0], [5000, 0, 0], [2000, 0, 0], [1000, 0, 0], [500, 0, 0], [200, 0, 0], [100, 0, 0], [50, 0, 0], ] self.model_money_count = TableModelEdit(self, money) 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': '{:,d}', '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 } commanded = { 'name': 'order_sended', 'format': '{:5,.1f}', 'align': alignHCenter, 'description': self.tr('COMMANDED'), 'width': 60 } 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.enviroment == 'restaurant': self.fields_sale_line.insert(1, commanded) 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) self.model_sale_customer_lines = TableModel('sale.line', self.fields_sale_line) def setup_sale_consumer(self): number = { 'name': 'number', 'align': alignCenter, 'description': self.tr('NUMBER'), 'width': 200 } invoice_number = { 'name': 'invoice_number', 'align': alignCenter, 'description': self.tr('INVOICE NUMBER'), 'width': 200 } consumer = { 'name': 'consumer.name', 'align': alignCenter, 'description': self.tr('CONSUMER'), 'width': 300 } sale_date = { 'name': 'sale_date', 'align': alignCenter, 'description': self.tr('DATE'), 'width': 300 } total_amount_cache = { 'name': 'total_amount_cache', 'align': alignCenter, 'description': self.tr('TOTAL AMOUNT'), 'width': 300 } self.fields_sale_consumer = [number, invoice_number, consumer, sale_date, total_amount_cache] self.model_sale_consumer = TableModel('sale.sale', self.fields_sale_consumer) def setup_delivery_party(self): party = { 'name': 'rec_name', 'align': alignLeft, 'description': self.tr('PARTY'), 'width': 380 } id_number = { 'name': 'party.id_number', 'align': alignLeft, 'description': self.tr('ID NUMBER'), 'width': 200 } phone = { 'name': 'party.phone', 'align': alignLeft, 'description': self.tr('PHONE'), 'width': 200 } number_plate = { 'name': 'number_plate', 'align': alignCenter, 'description': self.tr('NUMBER PLATE'), 'width': 200 } type_vehicle = { 'name': 'type_vehicle', 'align': alignCenter, 'description': self.tr('TYPE VEHICLE'), 'width': 300 } self.fields_delivery_party = [party, id_number, phone, number_plate, type_vehicle] self.model_delivery_party = TableModel('sale.delivery_party', self.fields_delivery_party) for record in self.delivery_man_table: self.model_delivery_party.add_record(record) 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() """Delete product """ 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 False 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 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']) ) 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: if self._state not in ['cash']: self._clear_context() # self.close() elif key == Qt.Key_F1: # help = Help(self).show() self.action_control_panel() 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 ['cash', 'accept']: 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 ['cash']: self.action_agent() elif key == Qt.Key_QuoteDbl: self.action_comment() elif key == Qt.Key_Question: self.action_tax() @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('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('quantity'): quantity = self.state_line.pop('quantity') if self._process_quantity(str(quantity)): _record = self.ModSaleLine.faster_set_quantity({ 'id': self._current_line_id, 'quantity': to_float(quantity, 2) }) 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, }) else: res = self.ModSale.create_consumer({ 'fields': self.state_consumer, }) if res['msg'] == 'ok': self.consumer_id = res['consumer'] self._PosSale.write([self._sale['id']], {'consumer': self.consumer_id}) if res.get('party'): self.on_selected_party(res['party']) def dialog_delivery_men_accepted(self): if not self.state_delivery_party: return if hasattr(self, '_delivery_party_selected') and self._delivery_party_selected: res = self.ModSale.update_delivery_party({ 'id': self._delivery_party_selected, 'data': self.state_delivery_party }) self._delivery_party_selected = None self.model_delivery_party.update_record(res) else: res = self.ModSale.create_delivery_party({ 'data': self.state_delivery_party, 'shop_id': self.ctx['shop'] }) self.model_delivery_party.add_record(res) self.delivery_man = [d for d in self.model_delivery_party._data if d['active']] self.dialog_delivery_men = DialogDeliveryMen(self).get() def update_delivery_party(self, field=''): if field == 'delivery_men': self.state_delivery_party['party'] = self.row_delivery_men.text() if field == 'row_phone': self.state_delivery_party['phone'] = self.row_phone.text() if field == 'id_number': self.state_delivery_party['id_number'] = self.row_id_number.text() if field == 'number_plate': self.state_delivery_party['number_plate'] = self.row_number_plate.text() if field == 'type_vehicle': self.state_delivery_party['type_vehicle'] = self.row_type_vehicle.get_id() if field == 'delivery_men_active': self.state_delivery_party['active'] = self.row_delivery_men_active.isChecked() def update_consumer_data(self, field=''): if field == 'preferences': self.state_consumer['preferences'] = self.row_field_consumer_preference.toPlainText() 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(MINIMUM_DATE) self.row_field_consumer_preference.clear() else: self.button_history_customer.setVisible(True) 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']) self.row_field_consumer_preference.insertPlainText( self._consumer['preferences']) 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_consumer(self): self._consumer = {} 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(2000, 1, 1)) self.row_field_consumer_preference.clear() self.button_history_customer.setVisible(False) # 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) class DoInvoice(QThread): """ Process invoices using a thread """ sigDoInvoice = pyqtSignal() def __init__(self, main, context): QThread.__init__(self) def run(self): self.sigDoInvoice.emit()