presik_pos/app/mainwindow.py

2644 lines
96 KiB
Python
Raw Normal View History

2020-04-19 17:54:08 +02:00
#!/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:
2020-04-23 22:52:05 +02:00
company_id = self.device['shop']['company']['id']
2020-04-19 17:54:08 +02:00
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!')),
2020-04-25 00:30:54 +02:00
'send_electronic_failed': ('info', self.tr('FALLO EL ENVIO DE FACTURA!')),
'invoice_done_failed': ('info', self.tr('FALLO FINALIZACIÓN DE FACTURA!')),
2020-04-19 17:54:08 +02:00
})
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']
2020-04-23 22:52:05 +02:00
self.company = self.shop['company']
2020-04-19 17:54:08 +02:00
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(),
2020-04-23 22:52:05 +02:00
'locations': [self.shop['warehouse']['id']],
2020-04-19 17:54:08 +02:00
}
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)
])
2020-04-23 22:52:05 +02:00
price_w_tax = sale_line['unit_price_w_tax']
2020-04-19 17:54:08 +02:00
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
2020-04-22 02:26:19 +02:00
self.field_journal_id = self.default_journal['id']
2020-04-19 17:54:08 +02:00
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):
2020-04-25 00:30:54 +02:00
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
2020-04-19 17:54:08 +02:00
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']
2020-04-23 22:52:05 +02:00
party, = self.Party.find([('id', '=', self.party_id)])
2020-04-19 17:54:08 +02:00
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
2020-04-21 04:13:03 +02:00
2020-04-19 17:54:08 +02:00
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')])
2020-04-23 22:52:05 +02:00
# for sale in sales:
# if sale['salesman']:
# print(sale['salesman'])
2020-04-19 17:54:08 +02:00
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
2020-04-22 02:26:19 +02:00
party, = self.Party.find([('id', '=', party_id)])
2020-04-19 17:54:08 +02:00
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'])
2020-04-22 02:26:19 +02:00
if party.get('salesman'):
self.field_salesman_id = party['salesman']['id']
2020-04-19 17:54:08 +02:00
self._PosSale.write([self._sale['id']], {'salesman': self.field_salesman_id})
2020-04-22 02:26:19 +02:00
self.field_salesman.setText(party['salesman']['name'])
2020-04-19 17:54:08 +02:00
# 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 '')
2020-04-24 21:44:30 +02:00
if sale.get('payment_term'):
self.field_payment_term_id = sale['payment_term']['id']
self.field_payment_term.setText(sale['payment_term']['name'] or '')
2020-04-19 17:54:08 +02:00
if sale.get('salesman'):
2020-04-23 22:52:05 +02:00
self.field_salesman.setText(sale['salesman']['name'] or '')
self.field_salesman_id = sale['salesman']['id']
2020-04-19 17:54:08 +02:00
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:
2020-04-23 22:52:05 +02:00
if hasattr(self, 'field_agent') and sale.get('agent') \
2020-04-19 17:54:08 +02:00
and sale.get('commission'):
commission = sale.get('commission')
2020-04-23 22:52:05 +02:00
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'])
2020-04-19 17:54:08 +02:00
self.field_commission_ask.setText(str(commission))
self._set_commission_amount(sale['untaxed_amount'], commission)
2020-04-23 22:52:05 +02:00
self.line_ids = [l['id'] for l in sale.get('lines')]
2020-04-19 17:54:08 +02:00
if sale.get('lines'):
lines = self.ModSaleLine.find([
2020-04-23 22:52:05 +02:00
('id', 'in', self.line_ids),
2020-04-19 17:54:08 +02:00
])
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')
2020-04-23 22:52:05 +02:00
self.party_id = sale['party']['id']
self.field_party.setText(sale['party']['name'])
2020-04-19 17:54:08 +02:00
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['template.name'], r['template.sale_price_w_tax']]
2020-04-21 04:13:03 +02:00
for r in records
]
2020-04-19 17:54:08 +02:00
def get_product_by_categories(self):
domain = [
('parent', '=', None),
('accounting', '=', False),
('id', 'in', self.product_categories)
]
self.allow_categories = self.Category.find(domain)
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):
2020-04-21 04:13:03 +02:00
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'}
2020-04-23 22:52:05 +02:00
headers['sale_date'] = {'desc': self.tr('DATE'), 'type': 'char'}
headers['salesman.name'] = {'desc': self.tr('SALESMAN'), 'type': 'char'}
2020-04-21 04:13:03 +02:00
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]
2020-04-19 17:54:08 +02:00
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]
2020-04-21 04:13:03 +02:00
headers = OrderedDict()
headers['id'] = {'desc': self.tr('ID'), 'type': 'char'}
headers['code'] = {'desc': self.tr('CODE'), 'type': 'char'}
2020-04-19 17:54:08 +02:00
if self._config.get('show_stock_pos') in ['icon', 'value']:
2020-04-21 04:13:03 +02:00
headers['quantity'] = {'desc': self.tr('STOCK'), 'type': 'number'}
2020-04-19 17:54:08 +02:00
if self._config['show_stock_pos'] == 'icon':
2020-04-21 04:13:03 +02:00
headers['quantity']['icon'] = 'stock'
headers['quantity']['type'] = 'icon'
_cols_width.append(60)
2020-04-19 17:54:08 +02:00
2020-04-21 04:13:03 +02:00
headers['name'] = {'desc': self.tr('NAME'), 'type': 'char'}
2020-04-19 17:54:08 +02:00
_cols_width.append(350)
if self._config.get('show_description_pos'):
2020-04-21 04:13:03 +02:00
headers['description'] = {'desc': self.tr('DESCRIPTION'), 'type': 'char'}
2020-04-19 17:54:08 +02:00
_cols_width.append(300)
if self._config.get('show_brand'):
2020-04-21 04:13:03 +02:00
headers['brand'] = {'desc': self.tr('BRAND'), 'type': 'char'}
2020-04-19 17:54:08 +02:00
_cols_width.append(100)
2020-04-21 04:13:03 +02:00
price = {'desc': self.tr('PRICE'), 'type': 'number'}
2020-04-19 17:54:08 +02:00
if not self._config.get('encoded_sale_price'):
2020-04-21 04:13:03 +02:00
headers['template.sale_price_w_tax'] = price
2020-04-19 17:54:08 +02:00
else:
2020-04-23 22:52:05 +02:00
price['type'] = 'char'
2020-04-21 04:13:03 +02:00
headers['encoded_sale_price'] = price
_cols_width.append(100)
2020-04-19 17:54:08 +02:00
if self._config.get('show_location_pos'):
2020-04-21 04:13:03 +02:00
headers['location.name'] = {'desc': self.tr('LOCATION'), 'type': 'char'}
2020-04-19 17:54:08 +02:00
_cols_width.append(100)
2020-04-21 04:13:03 +02:00
2020-04-19 17:54:08 +02:00
if self._config['show_product_image']:
2020-04-21 04:13:03 +02:00
headers['image'] = {'desc': self.tr('IMAGE'), 'icon': 'image', 'type': 'icon'}
2020-04-19 17:54:08 +02:00
_cols_width.append(30)
methods = {
'on_selected_method': 'on_selected_product',
'on_return_method': 'on_search_product',
2020-04-21 04:13:03 +02:00
'image': self.on_selected_icon_product,
2020-04-19 17:54:08 +02:00
'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):
2020-04-21 04:13:03 +02:00
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'}
2020-04-19 17:54:08 +02:00
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 = []
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 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):
2020-04-23 22:52:05 +02:00
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']
2020-04-19 17:54:08 +02:00
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'],
2020-04-22 02:26:19 +02:00
})
2020-04-19 17:54:08 +02:00
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:
2020-04-23 22:52:05 +02:00
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']
2020-04-19 17:54:08 +02:00
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()