presik_pos/app/main.py

3712 lines
135 KiB
Python

#!/usr/bin/env python
# -*- coding: UTF-8 -*-
import sys
import os
import logging
import base64
import time
import traceback
from decimal import Decimal
from datetime import datetime, timedelta, date
from collections import OrderedDict
from PySide6 import QtGui
from PySide6.QtCore import Qt, QTimer
from PySide6.QtWidgets import (
QLabel, QHBoxLayout, QVBoxLayout,
QWidget)
from app.commons.image import Image
from .frontwindow import FrontWindow
from .tools import get_icon, to_float, to_numeric
from .status_bar import StatusBar
from .stack_messages import StackMessages
# from app.commons.action import Action
from app.commons.forms import GridForm, ComboBox, FieldNumeric
from app.commons.messages import MessageBar
from app.commons.table import TableView
from app.commons.model import TableModel
from app.commons.menu_buttons import MenuDash
# from .localdb import LocalDB
from .reporting import Receipt
from .proxy import Report
from .buttonpad import ButtonsStacked, ButtonsFunction, StartButtons
from .states import STATES, RE_SIGN
from .commons.custom_button import CustomButton
from .threads import DoInvoice, VerifyOrderCommand
from .store import StoreView
from .constants import (
PATH_PRINTERS, DELTA_LOCALE, STRETCH, alignRight, alignLeft, alignCenter,
alignHCenter, DIALOG_REPLY_NO, ZERO, RATE_CREDIT_LIMIT, CONVERSION_DIGITS,
SALE_FIELDS, KIND_REST, KIND_RETAIL, DIALOG_REPLY_YES,
)
from .dialogs import DialogDeliveryParty
from sync_scale import read_files_scale
invoice_type = []
_SALE_HISTORIC = [
'party.name', 'sale_date', 'number', 'invoice_number', 'consumer.name',
'total_amount_cache', 'lines.quantity', 'lines.product.name', 'lines.unit.symbol',
'lines.amount', 'lines.unit_price_w_tax', 'lines.amount_w_tax',
'lines.note'
]
class AppWindow(FrontWindow):
def __init__(self, context, params):
# default_params of configpos.ini is defined in app/commons/config.py
title = "PRESIK | SMART POS"
self.data_expenses = None
super(AppWindow, self).__init__(context, params, title)
self.payment_ctx = {}
self.set_keys()
time1 = time.time()
StackMessages(self)
self.stock_context = None
self.sale_id = None
self.ctx = context
self.ctx['params'] = params
self.get_defaults_variables()
response = self.load_modules()
if response is not True:
d = self.dialog(response)
d.exec_()
super(AppWindow, self).close()
return
self.setup_sale_line()
self.setup_delivery_party()
self.setup_sale_consumer()
self.setup_payment()
# self.set_domains()
print(time.time() - time1, 'set domains')
self.create_gui()
print(time.time() - time1, 'create gui')
self.message_bar.load_stack(self.stack_msg)
self.journal = {}
self.active_usb_printers = []
if os.name == 'posix' and os.path.exists(PATH_PRINTERS):
self.set_printers_usb(PATH_PRINTERS)
self.set_printing_context()
self.statusbar = StatusBar(self)
# self._clear_invoice_number = False
# self.reader_thread = None
# self._current_line_id = None
# self._amount_text = ''
# self._input_text = ''
# self._sign = None
self.create_dialogs()
print(time.time() - time1, 'create dialogs')
self.store = StoreView(self, SALE_FIELDS)
if self.active_weighing:
from .electronic_scale import ScaleReader
self.reader_thread = ScaleReader()
self.reader_thread.sigSetWeight.connect(self.set_weight_readed)
if self.server_printer:
self.verify_command_order_th = VerifyOrderCommand(self)
self.verify_command_order_th.sigVerifyOrderCommand.connect(self.verify_print_order_automatic)
self.verify_command_order_th.start()
self.do_invoice = DoInvoice(self, self.ctx)
self.do_invoice.sigDoInvoice.connect(self.__do_invoice_thread)
self.set_state('disabled')
self.change_view_to('start_front')
# if self.cache_local:
# self.set_cache_company()
# self.set_cache_products()
matching_journal = next((j for j in self._journals if j['id'] == self.default_journal['id']), None)
if matching_journal:
self.default_journal = matching_journal
time6 = time.time()
print(time6 - time1, 'load final')
def get_defaults_variables(self):
self._clear_invoice_number = False
self.reader_thread = None
self._current_line_id = None
self._amount_text = ''
self._input_text = ''
self._sign = None
self._sale_line = None
self.field_agent = None
def verify_print_order_automatic(self):
records = self.Sale.get_orders_to_command({'shop': self.shop['id']})
for record in records:
orders = record['orders']
tasks = record['tasks']
try:
result = self.receipt_order.print_orders(orders.values())
if result:
self.Sale.mark_commanded({'sale_id': record['id'], 'lines_ids': result})
receipt = Receipt(context={}, environment='restaurant')
receipt.print_tasks(tasks)
except Exception as e:
print(e)
traceback.print_exc()
timer = QTimer()
timer.timeout.connect(self.verify_print_order_automatic)
timer.start(30000)
self.verify_command_order_th.exit(0)
# def set_domains(self):
# self.domain_search_product = [
# ('code', '!=', None),
# ('active', '=', True),
# ('template.salable', '=', True),
# ('template.account_category', '!=', None),
# ]
# if self.shop['product_categories.']:
# self.domain_search_product.append(
# ('account_category', 'in', self.shop['product_categories.'])
# )
# def set_cache_company(self):
# self.local_db = LocalDB()
# self.local_db.create_table_config()
# self._local_config = self.local_db.get_config()
# if not self._local_config:
# company_id = self.company
# self._local_config = self.local_db.set_config([company_id])
# def set_cache_products(self):
# self.local_db.create_table_product()
# local_products = self.local_db.get_local_products()
# config = self.local_db.get_config()
# _sync_date = config[1]
# products = self.Product.sync_get_products({
# 'write_date': _sync_date,
# 'shop_id': self.ctx['shop']
# })
# self.local_db.update_products(products, local_products)
# now = datetime.now()
# self.local_db.set_config_sync(str(now))
def set_printers_usb(self, PATH_PRINTERS):
for usb_dev in os.listdir(PATH_PRINTERS):
# # fix me return for set printer default usb
# return
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):
sale_id = self.sale_id
if sale_id:
sales = self.Sale.find([('id', '=', sale_id)],
fields=['state', 'number'])
if not sales:
return
return sales[0]
def get_price_lists(self):
price_lists = self.Pricelist.find([])
price_lists = [(ln['id'], ln['name']) for ln in price_lists]
return price_lists
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 delete_current_sale(self):
if self.sale_id:
args = {'id': self.sale_id}
self.Sale.cancel_sale(args)
def set_printing_context(self):
ctx_printing = self.Sale.get_printing_context(
{'device_id': self.device['id']})
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.decodebytes(self.logo.encode()))
f.close()
self.receipt_sale = Receipt(
ctx_printing,
logo=locale_logo,
environment=self.environment
)
self.receipt_order = None
# Printing order context
if self.print_order:
self.receipt_order = Receipt(ctx_printing, environment=self.environment)
self.set_default_printer()
def set_default_printer(self, printer=None):
if self.active_usb_printers:
self.printer_sale_name = self.active_usb_printers[0]
if not printer and self.printer_sale_name:
printer = {
'interface': self.printer_sale_name[0],
'device': self.printer_sale_name[1],
'profile': self.profile_printer,
}
if printer:
self.receipt_sale.config_printer(printer)
def button_new_sale_pressed(self):
self.create_new_sale()
def button_send_to_pay_pressed(self):
# Return sale to draft state
if not self._check_quantity():
return
if not self.validate_payment_term():
return
sale_id = self.sale_id
# sale, = self.Sale.find([['id', '=', sale_id]], fields=['invoice_type', 'total_amount', 'reservation'])
sale = self.store.store
if sale['invoice_type'] == 'P' and self._config.get('uvt_pos') and sale['untaxed_amount'] > self._config['uvt_pos']:
return self.dialog('base_uvt_pos')
args = {'sale_id': sale_id}
result = self.Sale.to_quote(args)
self.store.set(result)
if sale.get('reservation'):
self.Sale.write([sale_id], {'reservation': False})
if self.field_agent and self.field_agent.text():
msg = 'Agente: ' + result.get('agent.').get('rec_name') + \
"\n Comision: " + str(result['commission']) + "%"
dialog = self.dialog('confirm_agent', response=True, widgets=[], extra_message=msg)
response = dialog.exec_()
if response == DIALOG_REPLY_NO:
self.Sale.write([sale_id], {'state': 'draft'})
return
if self.model_sale_lines.rowCount() > 0:
self.check_salesman()
res = self.action_send_order(sale_id=sale_id, init_view=False)
try:
if res:
self.Sale.generate_shipment({'sale_id': sale_id})
self.dialog('order_dispatched')
self.see_start_front()
else:
self.Sale.write([sale_id], {'state': 'draft'})
except Exception:
self.dialog('error_order_dispatched')
def button_to_force_draft_pressed(self):
# Return sale to draft state force draft
if self.sale_id:
args = {'sale_id': self.sale_id}
self.Sale.to_draft(args)
self.see_start_front()
def button_to_draft_pressed(self):
# Return sale to draft state when state quotation
self.store.update({'state': 'draft'})
# self.Sale.button_method('draft', [self.sale_id])
# self._sale['state'] = 'draft'
self.buttons_function.button_to_draft.hide()
self.buttons_function.button_to_quote.show()
def button_to_quote_pressed(self):
sale_id = self.sale_id
self.Sale.button_method('quote', [sale_id])
self.Sale.write([sale_id], {'reservation': True})
self.store.set({'state': 'quotation'})
self.buttons_function.button_to_quote.hide()
self.buttons_function.button_to_draft.show()
def create_gui(self):
panels = QHBoxLayout()
panels.setSpacing(0)
panels.setContentsMargins(0, 0, 0, 0)
panel_left = QWidget()
panel_left.setObjectName('panel_left')
vbox_left = QVBoxLayout()
vbox_left.setSpacing(0)
vbox_left.setContentsMargins(0, 0, 0, 0)
if self.screen_size == 'small':
max_size = 620
else:
max_size = 1300
panel_left.setMaximumWidth(max_size)
panel_left.setLayout(vbox_left)
panel_left.setContentsMargins(0, 0, 0, 0)
self.panel_right = QVBoxLayout()
self.panel_right.setSpacing(4)
self.panel_right.setObjectName('panel_right')
self.panel_right.setContentsMargins(3, 0, 3, 0)
left_head = QHBoxLayout()
left_head.setSpacing(4)
left_invoice = QHBoxLayout()
left_invoice.setSpacing(4)
board = QVBoxLayout()
board.setSpacing(0)
# CREATE START FRONT
self.start_front = QWidget()
self.start_front.setObjectName('start_front')
start_front = StartButtons(self)
self.start_front.setLayout(start_front)
# CREATE ORDER FRONT
self.order_front = QWidget()
self.order_front.setObjectName('order_front')
vbox_order_front = QVBoxLayout()
vbox_order_front.setSpacing(4)
vbox_order_front.addLayout(left_head)
vbox_order_front.addLayout(left_invoice)
self.order_front.setLayout(vbox_order_front)
# CONFIGURE BOARD
board.addWidget(self.start_front, 0)
self.WidgetShop = QWidget()
self.WidgetShop.setObjectName('label_shop')
vbox_label = QVBoxLayout()
vbox_label.setAlignment(Qt.AlignBottom)
LabelShop = QLabel(self.WidgetShop)
LabelShop.setObjectName('LabelShop')
LabelShop.setText(self.shop['name'])
LabelShop.setStyleSheet(
"""
font: 30px; color: grey;
font-weight: bold;
min-height: 60px;
max-height: 90px""")
vbox_label.addWidget(LabelShop)
self.WidgetShop.setLayout(vbox_label)
board.addWidget(self.WidgetShop, 2)
board.addWidget(self.order_front, 0)
# LEFT HEAD COMPONENTS
start_front = CustomButton(
self,
id='button_start',
size=self.screen_size,
icon=get_icon('start_front'),
title='INICIO',
method='see_start_front',
name_style='start',
)
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.addWidget(start_front, 0)
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)
if self.shop.get('pos_authorization'):
invoice_type.append(('P', ('POS')))
if self.shop.get('electronic_authorization'):
invoice_type.append(('1', ('VENTA ELECTRONICA')))
if self.shop.get('manual_authorization'):
invoice_type.append(('M', ('MANUAL')))
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, 1)
self.field_invoice_number = QLabel('')
self.field_invoice_number.setObjectName('field_invoice_number')
left_invoice.addWidget(self.field_invoice_number, 1)
label_date = QLabel('FECHA:')
label_date.setObjectName('label_sale_date')
self.field_sale_date = QLabel('')
self.field_sale_date.setObjectName('field_sale_date')
left_invoice.addWidget(label_date)
left_invoice.addWidget(self.field_sale_date, 0)
# LEFT TABLE COMPONENTS
self.buttons_function = ButtonsFunction(self)
if self.environment == 'restaurant':
KIND = KIND_REST
else:
KIND = KIND_RETAIL
screen = self.screen_size
label_color = self.label_color
info_fields = [
('party', {
'name': 'CLIENTE',
'readonly': True,
'placeholder': False,
'size': screen,
'color': label_color
}),
('kind', {
'name': 'CLASE',
'placeholder': False,
'type': 'selection',
'on_change': 'action_kind_selection_changed',
'values': KIND,
'size': screen,
'color': label_color
}),
('salesman', {
'name': 'VENDEDOR',
'readonly': True,
'placeholder': False,
'size': screen,
'color': label_color
}),
('number', {
'name': 'No ORDEN',
'placeholder': False,
'readonly': True,
'size': screen,
'color': label_color
}),
('payment_term', {
'name': 'FORMA DE PAGO',
'readonly': True,
'invisible': False,
'placeholder': False,
'size': screen,
'color': label_color
}),
('delivery_party', {
'name': 'DOMICILIARIO',
'readonly': True,
'placeholder': False,
'size': screen,
'color': label_color
})
]
info_fields_append = info_fields.append
if self.environment != 'restaurant' and self._config.get('use_price_list'):
info_fields_append(
('list_price', {
'name': 'LISTA DE PRECIOS',
'placeholder': False,
'type': 'selection',
'values': self.get_price_lists(),
'default': self.shop['price_list.'],
'size': screen,
'color': label_color
}))
self.sources = self.Source.find([])
info_fields_append(
('source', {
'name': 'CANAL',
'readonly': True,
'placeholder': False,
'size': screen,
'color': label_color
}))
if self._config['show_position_pos']:
info_fields_append(('position', {
'name': 'POSICION',
'readonly': True,
'placeholder': False,
'size': screen,
'color': label_color
}))
if self._commission_activated and self._config['show_agent_pos']:
info_fields_append(('agent', {
'name': 'AGENTE',
'placeholder': False,
'readonly': True,
'size': screen,
'color': label_color
}))
_cols = 2
self.field_delivery_charge = None
if self._config['show_delivery_charge']:
info_fields_append(
('delivery_charge', {
'name': 'PAGO DOMICILIO',
'placeholder': False,
'type': 'selection',
'on_change': 'action_delivery_charge_selection_changed',
'values': [
('', ''),
('customer', 'CLIENTE'),
('company', 'COMPAÑIA'),
],
'size': screen,
'color': label_color
}))
self.field_table_assigned = None
if self._sale_pos_restaurant:
info_fields_append(
('consumer', {
'name': 'CONSUMIDOR',
'placeholder': False,
'size': screen,
'color': label_color,
'readonly': True,
}))
PAYMENT_METHODS = [
('', ''),
('cash', 'EFECTIVO'),
('terminal', 'DATAFONO'),
('all_paid', 'TODO PAGO'),
('partial_paid', 'PAGO PARCIAL'),
]
info_fields_append(
('payment_method', {
'name': 'MEDIO DE PAGO',
'placeholder': False,
'type': 'selection',
'on_change': 'action_payment_method_selection_changed',
'values': PAYMENT_METHODS,
'size': screen,
'color': label_color
}))
info_fields_append(
('table_assigned', {
'name': 'MESA',
'placeholder': False,
'size': screen,
'color': label_color,
'readonly': True,
}))
info_fields_append(
('order_status', {
'name': 'ESTADO',
'placeholder': False,
'size': screen,
'color': label_color,
'translate': True,
'readonly': True,
}))
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.environment == 'restaurant':
invisible = True
_fields_amounts = [
('untaxed_amount', {
'name': 'SUBTOTAL',
'readonly': True,
'type': 'money',
'size': screen,
'color': label_color,
'invisible': invisible,
}),
('tax_amount', {
'name': 'IMPUESTOS',
'readonly': True,
'type': 'money',
'size': screen,
'color': label_color,
'invisible': invisible,
}),
('discount', {
'name': 'DESCUENTOS',
'readonly': True,
'type': 'money',
'size': screen,
'color': label_color,
'invisible': invisible,
}),
('total_amount', {
'name': 'TOTAL',
'readonly': True,
'type': 'money',
'size': screen,
'color': 'black',
'font_size': 'big',
}),
('paid_amount', {
'name': 'PAGADO',
'readonly': True,
'type': 'money',
'size': screen,
'color': label_color,
}),
('change', {
'name': 'PENDIENTE',
'readonly': True,
'type': 'money',
'size': screen,
'color': self.label_color_2 or 'orange'
})
]
_fields_amounts_insert = _fields_amounts.insert
if self.environment == 'restaurant':
_fields_amounts_insert(3, ('tip_amount', {
'name': 'VR. PROPINA',
'type': 'money',
'readonly': True,
'size': screen,
'color': label_color,
}))
_fields_amounts_insert(4, ('delivery_amount', {
'name': 'VR. DOMIC',
'type': 'money',
'readonly': True,
'size': screen,
'color': label_color,
}))
_fields_amounts_insert(6, ('net_amount', {
'name': 'NETO TOTAL',
'readonly': True,
'type': 'money',
'size': screen,
'color': 'black',
'font_size': 'big',
}))
fields_amounts = OrderedDict(_fields_amounts)
self.grid_amounts = GridForm(self, fields_amounts, col=1)
self.buttons_stacked = ButtonsStacked(self)
self.table_payment = TableView(
'table_payment', self.table_payment_lines, [250, STRETCH])
self.grid_info = GridForm(
self,
OrderedDict(info_fields),
col=_cols,
size=screen
)
vbox_order_front.addWidget(self.table_sale_lines, 1)
vbox_left.addLayout(board, 0)
if self.environment == 'restaurant':
self.panel_right_box = QWidget()
time3 = time.time()
# values = self.get_product_by_categories_dash()
values = self.product_categories
print(time.time() - time3, 'load products categories dash')
self.menu_dash = MenuDash(self, values, 'on_selected_item')
print('final dash')
self.order_front.hide()
vbox_order_front.addLayout(self.buttons_function, 0)
vbox_order_front.addLayout(self.grid_info, 0)
self.panel_right.addWidget(self.menu_dash, 1)
_panel_right_box = QVBoxLayout()
_panel_right_box.addLayout(self.grid_amounts, 0)
_panel_right_box.addWidget(self.buttons_stacked, 0)
self.panel_right_box.setLayout(_panel_right_box)
self.panel_right.addWidget(self.panel_right_box)
else:
vbox_left.addLayout(self.grid_info, 1)
self.panel_right.addLayout(self.grid_amounts, 1)
self.panel_right.addLayout(self.buttons_function, 1)
self.panel_right.addWidget(self.buttons_stacked, 0)
self.panel_right.addWidget(self.table_payment)
panels.addWidget(panel_left, 1)
panels.addLayout(self.panel_right, 0)
widget = QWidget()
widget.setLayout(panels)
self.setCentralWidget(widget)
self.order_number = QLabel('')
self.order_number.setObjectName('label_number_notification')
self.order_number.setAlignment(Qt.AlignCenter | Qt.AlignVCenter)
def show_right_panel(self, show):
if self.environment == 'restaurant':
if show:
self.panel_right_box.show()
else:
self.panel_right_box.hide()
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 ('*', '-', '/'):
self._sale_line = self.table_sale_lines.get_selected_clicked()
if self._sale_line and self._sale_line.get('type') \
and self._state == 'add' \
and self.model_sale_lines.rowCount() > 0:
self._current_line_id = self._sale_line['id']
if self._sign == '*':
self._process_quantity(self._amount_text)
else:
error = not (self._process_price(self._amount_text))
sale_line, = self.SaleLine.find([
('id', '=', self._sale_line['id'])
])
self.model_sale_lines.update_record(sale_line)
elif self._state in ['add', 'cancel', 'accept']:
self.clear_right_panel()
self.add_product(code=self._input_text)
elif self._state == 'payment':
is_paid = self._process_pay(self.field_amount.get_value())
if not is_paid:
self.set_state('payment')
self.clear_input_text()
self.clear_amount_text()
return
if self.environment == 'restaurant':
self.set_state('finished')
else:
self.set_state('add')
else:
logging.warning('Unknown command/text')
self._clear_context(error)
def see_start_front(self):
self.change_view_to('start_front')
def change_view_to(self, to_view):
self.field_salesman_code_ask.setText('')
if to_view == 'start_front':
self.print_no_commanded()
self.salesman = {}
self.order_front.hide()
self.start_front.show()
if hasattr(self, 'WidgetShop'):
self.WidgetShop.show()
self.set_state('disabled')
self.buttons_stacked.hide()
self.buttons_function.hide_buttons()
self._sale = {}
else:
self.start_front.hide()
if hasattr(self, 'WidgetShop'):
self.WidgetShop.hide()
self.order_front.show()
self.buttons_stacked.show()
self.buttons_function.show_buttons()
if self.environment == 'retail' and self.type_pos_user in ('order', 'salesman'):
self.buttons_function.button_change_salesman.hide()
def action_read_weight(self):
print('si hace action_read_weight')
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', 'checkout', 'payment') 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._sale_line['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)
args = {
'id': self._sale_line['id'],
'quantity': quantity,
}
# if self._config.get('use_price_list'):
# args['use_price_list'] = True
ctx = None
if hasattr(self, 'field_list_price') and self._config.get('use_price_list'):
ctx = {'price_list': self.field_list_price.get_id()}
rec = self.SaleLine.faster_set_quantity(args, ctx=ctx)
except Exception as e:
print(e)
return self.message_bar.set('quantity_not_valid')
self.message_bar.set('system_ready')
self.model_sale_lines.update_record(rec)
self.set_amounts()
def _process_price(self, text, line=None):
discount_valid = True
eval_value = text.replace(',', '')
value = float(eval_value)
if not line:
line_id = self._current_line_id
else:
line_id = line['id']
if not self.allow_discount_handle:
discount_valid = False
else:
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.SaleLine.find([
('id', '=', line_id)
])
price_w_tax = float(str(round(sale_line['product.']['sale_price_taxed'], 0)))
if price_w_tax <= value:
discount_valid = self.set_unit_price(value, discount=False)
else:
eval_value = price_w_tax - value
discount_valid = self.set_discount(eval_value, type_='fixed')
if not discount_valid:
self.message_bar.set('discount_not_valid')
return False
self.message_bar.set('system_ready')
self.set_amounts()
return True
def _process_pay(self, text):
if not self.validate_done_sale():
return
cash_received = Decimal(text)
if self._commission_activated and self.environment == 'retail':
if not self.journal:
self.journal = self.default_journal
if self.journal['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
res = self.add_payment(cash_received)
if not res:
return
if res.get('msg') == 'statement_closed':
self.dialog('statement_closed')
return False
if res['residual_amount'] <= 0:
self.grid_amounts.label_change.setText('CAMBIO')
self._done_sale()
return True
self.set_amounts()
# for j in self._journals:
# if j['id'] == self.default_journal['id']:
self.journal = self.default_journal
if res['msg'] == 'missing_money':
self.message_bar.set('enter_payment', self.default_journal['name'])
return False
if res['change'] < ZERO:
self.message_bar.set('enter_payment', self.default_journal['name'])
residual_amount = res['residual_amount']
self.store.set({
'residual_amount': residual_amount,
'change': res['change'],
})
# if residual_amount <= 0:
# self.grid_amounts.label_change.setText('CAMBIO')
# 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.store.sale_id:
res = self.Sale.get_discount_total({
'sale_id': self.store.sale_id
})
self.store.set({'discount': 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:
self.ctx['exception'] = True
res = self.Sale.faster_post_invoice({
'sale_id': self.sale_to_post_id,
})
except Exception:
self.dialog('invoice_done_failed')
return
if res['result'] is not True:
self.dialog('invoice_done_failed', res['result'])
return False
payment_term = self.store.get('payment_term')
# Type payment 2 is credit
if payment_term.get('payment_type') == '2':
return True
self.Sale.reconcile_invoice({'sale_id': self.sale_to_post_id})
def _done_sale(self, is_credit=False):
# We need create this temp variable called sale_to_post_id for this
# thread, because self.store.sale_id is deleted inmediately,
# so post invoice and reconcile it will fail if reference to sale_id
# is missing or None
self.sale_to_post_id = self.store.sale_id
table_assigned = self.store.get('table_assigned')
if table_assigned:
self.RestTables.write(
[table_assigned['id']], {
'state': 'available',
'sale': None,
}
)
self.do_invoice.start()
try:
if self.print_receipt == 'automatic':
_copies = self.shop['invoice_copies']
if self.environment == 'restaurant':
for n in range(_copies):
self.action_print_sale()
else:
if not is_credit:
self.print_invoice(copies=_copies, open_box=True)
except Exception as e:
print('Error', e)
logging.error(sys.exc_info()[0])
if self.sale_automatic and self.type_pos_user not in ('cashier', 'order'):
self.create_new_sale()
else:
self.change_view_to('start_front')
self.message_bar.set('system_ready')
return True
def print_invoice(self, sale_id=None, type_doc='invoice', open_box=False, copies=1):
if not sale_id:
sale_id = self.store.sale_id
args = {
'sale_id': sale_id,
'type_doc': type_doc,
}
data = self.Sale.get_data(args)
if not data:
return
for i in range(copies):
msg = self.receipt_sale.print_sale(data, type_doc, open_box)
if isinstance(msg, dict) and msg.get('error'):
self.dialog('print_error', extra_message=msg['error'])
break
# def button_duplicate_sale(self):
# if self.sale_customer_selected:
# res = self.Sale.duplicate_sale({
# 'sale_id': self.sale_customer_selected,
# })
# self.load_sale(res['sale_id'])
# self.dialog_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_historic.reset()
sales = self.Sale.find(dom, fields=_SALE_HISTORIC, limit=10)
for record in sales:
self.model_sale_historic.add_record(record)
self.dialog_historic_sales.exec_()
def check_all_commanded(self):
return all([
line.get('order_sended') for line in self.model_sale_lines._data
])
def print_no_commanded(self):
if self.environment != 'restaurant':
return True
all_commmanded = self.check_all_commanded()
if not all_commmanded:
res = self.action_send_order(init_view=False)
if res:
return True
return True
def button_accept_pressed(self):
sale = self.store.store
environment = self.environment
sale_id = self.sale_id
if not sale_id or not self.model_sale_lines.rowCount() > 0:
return
limit_uvt = self._config.get('uvt_pos')
# sale, = self.Sale.find([['id', '=', sale_id]], fields=['invoice_type', 'total_amount'])
if sale['invoice_type'] == 'P' and limit_uvt and sale['untaxed_amount'] > limit_uvt:
return self.dialog('base_uvt_pos')
if environment == 'restaurant':
if self.print_order:
self.print_no_commanded()
if not sale.get('consumer') and sale['kind'] == 'delivery':
self.action_consumer()
return
elif environment == 'retail':
if not self._check_quantity():
return
self.Sale.button_method('quote', [sale_id])
self.load_sale(sale_id)
self.set_state('accept')
def button_checkout_pressed(self):
if not self.check_salesman():
return
if not self.validate_payment_term():
return
if self.environment == 'restaurant':
# if not self.print_no_commanded():
# return
res = self.check_delivery_party()
if not res:
return
self.store.update({'order_status': 'dispatched'})
sale_id = self.sale_id
self.ctx['exception'] = True
res = self.Sale.faster_process({'sale_id': sale_id})
if isinstance(res, list):
res, msg, exception = res
if msg:
self.message_bar.set(msg)
return
if exception:
self.dialog('invoice_validated_failed', extra_message=exception)
return
else:
self.dialog('process_invoice_failed', extra_message=res['error'])
return
self.set_amounts(res)
# self.field_number.setText(self._sale['number'])
self.field_invoice_number.setText(res['invoice_number'])
self.field_amount.setText('')
self.set_state('payment')
if self.type_pos_user == 'salesman':
self.print_invoice(sale_id)
if self.sale_automatic:
self.create_new_sale()
else:
self.message_bar.set('enter_payment', self.default_journal['name'])
self.setFocus()
def check_delivery_party(self):
kind = self.store.get('kind')
delivery_party = self.store.get('delivery_party')
if kind == 'delivery' and not delivery_party:
self.dialog('missing_delivery_party')
return False
return True
def action_reservations(self):
logging.info('Buscando reservas.....')
def action_tables(self):
if self.environment == 'restaurant':
self.dialog_manage_tables.open_tables()
self.dialog_manage_tables.exec_()
def action_release_table(self, table_id):
dialog = self.dialog('release_table', response=True)
response = dialog.exec_()
if response == DIALOG_REPLY_NO:
return
to_drop = {'state': 'available', 'sale': None}
self.RestTables.write([table_id], to_drop)
return to_drop
def action_change_salesman(self):
salesman = self.action_salesman_code()
if salesman == 0:
self.dialog_salesman_code.hide()
return
if not salesman:
return self.dialog('error_salesman_wrong')
else:
self.salesman = salesman
self.Sale.write([self.sale_id], {'salesman': salesman['id']})
self.store.update({'salesman': salesman})
def action_tip(self):
if self._state in ['finished']:
return
value = self.field_tip_amount_ask.text()
if not value and self._config['tip_rate']:
total_amount = int(self._get_total_amount())
value = int((self._config['tip_rate'] / 100) * total_amount)
self.field_tip_amount_ask.setText(str(value))
result = self.dialog_tip_amount.exec_()
if result == 0:
self.field_tip_amount_invoice.setChecked(False)
return
value = self.field_tip_amount_ask.text()
value = int(value) if value else 0
tip_invoice = self.field_tip_amount_invoice.isChecked()
self.field_tip_amount_invoice.setChecked(False)
if tip_invoice:
tip_product = self._config.get('tip_product.')
self.add_product(record=tip_product, list_price=value)
res = self.store.update({'tip_amount': 0})
self.set_amounts(res)
else:
res = self.store.update({'tip_amount': value})
self.set_amounts(res)
def action_delivery(self):
if self._state in ['finished']:
return
result = self.dialog_delivery_amount.exec_()
if result == 0:
self.field_delivery_amount_invoice.setChecked(False)
return
value = self.field_delivery_amount_ask.text()
value = int(value)
delivery_invoice = self.field_delivery_amount_invoice.isChecked()
self.field_delivery_amount_invoice.setChecked(False)
if delivery_invoice:
delivery_product = self._config.get('delivery_product.')
self.add_product(record=delivery_product, list_price=value)
res = self.store.update({'delivery_amount': 0})
self.set_amounts(res)
else:
res = self.store.update({'delivery_amount': value})
self.set_amounts(res)
def action_salesman_code(self):
if self._state in ['checkout']:
return
result = self.dialog_salesman_code.exec_()
code = self.field_salesman_code_ask.text()
if result == 0:
# Cancelled the action
return result
if not code:
return
salesman_list = self.Employee.find([
('code', '=', code)
])
if salesman_list:
return salesman_list[0]
def action_delivery_party(self):
self.dialog_delivery_party.exec_()
def action_delivery_party_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_reports(self):
self.dialog_reports.exec_()
def dialog_money_count_accepted(self):
return True
def action_expenses(self):
if self.set_expenses():
self.dialog_expenses.exec()
def action_open_statement(self):
self.dialog_money_count.exec('open')
def open_statement_accepted(self, value):
res = self.Sale.faster_open_statement({
'device': self.device['id'],
'total_money': Decimal(value),
})
if res['result']:
self.dialog('statement_created')
self.dialog_money_count.clear()
self.set_expenses()
def action_close_statement(self, values):
salesman = self.action_salesman_code()
self.field_salesman_code_ask.setText('')
self.salesman_statement = salesman
if not salesman:
return self.dialog('error_salesman_wrong')
self.dialog_money_count.exec('close')
def close_statement_accepted(self, values):
if self.salesman_statement:
salesman = self.salesman_statement
send_mail = self.field_send_mail.isChecked()
self.field_send_mail.setChecked(False)
res = self.Sale.faster_close_statement({
'device': self.device['id'],
'data': values,
'salesman': salesman['id'],
'send_mail': send_mail,
})
# FIXME: Check if account statement is closed previously.
if res and res['result']:
self.dialog('statement_finish')
else:
# self.dialog('statement_previously_closed')
pass
self.dialog_expenses.clear()
self.data_expenses = None
self.dialog_money_count.clear()
def action_split_sale(self):
if self.table_sale_lines.is_active_selection():
res = self.dialog_split_sale.ask()
if res == DIALOG_REPLY_YES:
selectedItems = self.table_sale_lines.get_selected_rows()
if selectedItems:
self.new_sale_from_lines(selectedItems)
self.table_sale_lines.delete_selected()
self.table_sale_lines.clear_selected()
self.table_sale_lines.active_selection(False)
self.message_bar.set('system_ready')
else:
self.table_sale_lines.clear_selected()
self.message_bar.set('select_products_to_split')
self.table_sale_lines.active_selection(True)
def new_sale_from_lines(self, lines):
_sale = self.store.store
lines_ids = [line['id'] for line in lines]
consumer_id = None
source_id = None
turn = None
if _sale['consumer']:
consumer_id = _sale['consumer']['id']
if _sale.get('source'):
source_id = _sale['source']['id']
if _sale.get('turn'):
turn = _sale['turn']
shipment_address = _sale['shipment_address']
if isinstance(shipment_address, dict):
shipment_address_id = shipment_address['id']
else:
shipment_address_id = shipment_address
to_create = {
'shop': self.shop['id'],
'invoice_type': 'P',
'company': self.company,
'party': _sale['party']['id'],
'sale_device': self.device['id'],
'payment_method': 'cash',
'payment_term': self.default_payment_term['id'],
'kind': _sale['kind'],
'salesman': _sale['salesman']['id'],
'shipment_address': shipment_address_id,
'invoice_address': shipment_address_id,
'turn': turn,
'sale_date': _sale['sale_date'],
'consumer': consumer_id,
'source': source_id,
'state': 'draft',
'comment': _sale['comment'],
}
sale = self.Sale.new_sale(to_create)
self.SaleLine.write(lines_ids, {'sale': sale['id']})
number = self.Sale.set_sale_number({'sale_id': sale['id']})
self.dialog_split_sale.info(number)
self.set_amounts()
def action_table_discount(self):
self.dialog_auth_discount.exec_()
def action_tax(self):
self.dialog_tax.exec_()
def button_payment_pressed(self):
if self._state not in ('checkout', 'payment'):
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'] * RATE_CREDIT_LIMIT) < (
party['credit_amount'] + self._get_total_amount()):
self.dialog('credit_limit_capacity')
return True
def validate_payment_term(self, payment_term=None):
if not payment_term:
payment_term = self.store.get('payment_term')
payment_type = payment_term.get('payment_type')
# Type payment 2 is credit
if payment_type != '2':
return True
party, = self.Party.find([('id', '=', self.party_id)])
if self._credit_limit_activated:
if self.party_id == self.default_party['id']:
self.dialog('customer_not_credit')
return False
elif not party['credit_limit_amount']:
self.dialog('customer_not_credit')
return False
self._credit_amount = self.Sale.get_credit_amount_party(
{'party_id': self.party_id})
self._credit_limit_amount = party['credit_limit_amount']
if 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_delivery_charge_selection_changed(self, index):
val = self.field_delivery_charge.get_id()
sale_id = self.store.store.get('id')
if val and sale_id:
self.store.update({'delivery_charge': val})
# self.Sale.write([sale_id], {'delivery_charge': val})
def action_kind_selection_changed(self, index):
val = self.field_kind.get_id()
self.store.update({'kind': val})
def action_payment_method_selection_changed(self, index):
val = self.field_payment_method.get_id()
self.store.update({'payment_method': val})
def action_invoice_type_selection_changed(self, index):
val = self.field_invoice_type.get_id()
sale_id = self.store.store.get('id')
if val and sale_id:
args = {'invoice_type': val}
if val == 'M':
args['invoice_method'] = 'manual'
self.Sale.write([sale_id], args)
self.label_input.setFocus()
def action_tax_selection_changed(self):
args = {'id': self.store.sale_id, 'tax_id': self.field_tax_id}
res = self.Sale.add_tax(args)
self.set_amounts(res)
def action_consumer(self):
consumer = self.store.get('consumer')
if consumer and consumer.get('id'):
self.dialog_consumer.fill(consumer)
else:
self.dialog_consumer.clear()
self.dialog_consumer.show()
def action_info_product(self):
self.dialog_info_product.show()
def action_collection(self):
self.dialog_collection.show()
def action_add_advance(self):
if self.type_pos_user not in ('cashier', 'frontend_admin'):
self.dialog('user_without_permission')
return
sale = self.store.store
sale_id = sale['id']
if sale.get('state') == 'draft':
self.Sale.button_method('quote', [sale_id])
# self.Sale.quote({'id': self._sale['id']})
self.load_sale(sale_id)
if sale.get('state') == 'quotation':
self.dialog_payment.exec_()
flag = True
# dialog_advance.addAction
while flag:
self.dialog_advance.clean()
res = self.dialog_advance.exec_()
amount = self.field_amount_ask.text()
reservation = self.field_reservation_ask.isChecked()
if res == 0 or (res == 1 and amount.isdigit()):
flag = False
if res == 1:
journal = self.journal
voucher_number = None
if journal.get('require_voucher'):
res = self.dialog_voucher.exec_()
if res == 0:
self.dialog_voucher.close()
return
voucher_number = self.field_voucher_ask.text()
if voucher_number is None or voucher_number == '':
return self.add_payment(amount)
self.field_voucher_ask.setText('')
res = self.Sale.faster_add_payment({
'sale_id': sale_id,
'journal_id': journal['id'],
'cash_received': to_numeric(float(amount)),
'voucher_number': voucher_number,
'extra_paid': False,
}, ctx={'advance': True, 'reservation': reservation})
if res.get('msg') not in ('missing_money', 'ok'):
self.dialog(res['msg'])
return
residual_amount = res.get('residual_amount')
change = res.get('change')
paid_amount = res.pop('paid_amount')
self.store.set({
'residual_amount': residual_amount,
'change': change,
'paid_amount': paid_amount,
})
if res['id']:
self.table_payment_lines.add_record(res)
def action_print_sale(self):
number = self.field_invoice_number.text()
type_doc = 'invoice'
if not number:
type_doc = 'order'
number = self.field_number.text()
if number:
self.field_invoice_number_ask.setText(number)
self.field_type_ask.set_from_id(type_doc)
res = self.dialog_print_invoice.exec_()
if res == DIALOG_REPLY_NO:
return
number = self.field_invoice_number_ask.text()
resend_dian = self.field_resend_dian_ask.isChecked()
printer_id = self.field_printer_ask.get_id()
type_doc = self.field_type_ask.get_id()
sale_id = None
sale = self.store.store
if number:
if type_doc in ['order', 'delivery', 'quotation']:
if number != sale.get('number'):
sale_id = sale['id']
else:
sales = self.Sale.find([('number', '=', number)], fields=['id'])
sale = sales[0]
if sales:
sale_id = sales[0]['id']
else:
args = {'number': number}
sale = self.Sale.get_sale_from_invoice(args)
if sale:
sale_id = sale['id']
else:
sale_id = self.sale_id
if not sale_id:
self.dialog('sale_number_not_found')
if resend_dian:
self.field_resend_dian_ask.setChecked(False)
res = self.Sale.resend_invoice({'id': sale_id, 'number': number})
if not res:
self.dialog('fail_send_invoice')
if printer_id == '1':
self.print_invoice(sale_id, type_doc=type_doc)
else:
self.print_odt_report(sale, type_doc)
def print_odt_report(self, sale, type_doc):
if type_doc == 'order':
model = 'sale.sale'
sale_id = sale['id']
action_id = self._action_report_sale[-1]['id']
data = {
'model': model,
'action_id': action_id,
}
ctx = {'date_format': '%d/%m/%Y'}
ctx.update(self.ctx)
report = Report(ctx)
values = {
'report_name': model,
'args': data,
'record': sale_id,
'records': [sale_id],
}
result = report.get(values)
report.open(result)
elif type_doc == 'invoice':
if not sale.get('invoices'):
return
invoice_id = sale['invoices'][0]
model = 'account.invoice'
action_id = self._action_report_invoice['id']
if sale['invoice_type'] == '1':
action_id = self._action_report_invoice_e['id']
model = 'electronic_invoice_co.invoice_face'
data = {
'model': model,
'action_id': action_id,
}
ctx = {'date_format': u'%d/%m/%Y'}
ctx.update(self.ctx)
report = Report(ctx)
values = {
'report_name': model,
'args': data,
'record': invoice_id,
'records': [invoice_id],
}
result = report.get(values)
report.open(result)
def action_comment(self):
self.dialog_comment.exec_()
comment = self.field_comment.toPlainText()
self.store.update({'comment': comment})
def action_position(self):
self.field_position_ask.setText(self.store.get('position'))
self.dialog_position.exec_()
position = self.field_position_ask.text()
self.store.update({'position': position})
def action_agent(self):
self.dialog_agent.exec_()
res = self.field_commission_ask.text()
commission = float(res or 0)
self.field_agent_id = self.field_agent_ask.get_id()
if self.field_agent_id:
self.Sale.write([self.sale_id], {
'agent': self.field_agent_id,
# 'commission': int(commission),
})
self.message_bar.set('system_ready')
comm_string = str('[' + str(commission) + ']'
+ ' ') + (str(self.field_agent_ask.text()))
self.field_agent.setText(comm_string)
def _set_commission_amount(self, untaxed_amount, commission):
untaxed_amount = int(untaxed_amount)
commission = int(commission if commission else 0)
total = ((untaxed_amount * commission) / 100)
self.field_commission_amount.setText(str(total))
def action_party(self):
if self._state in ['checkout']:
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', 'checkout', 'payment'):
return
if self.environment == 'restaurant' and self.type_pos_user not in (
'cashier', 'frontend_admin'):
self.dialog('user_without_permission')
return
self.dialog_global_discount.exec_()
discount = self.field_global_discount_ask.text()
if discount and discount.isdigit():
rec = {
'type_discount': 'percentage',
'discount': discount,
'name': 'DESCUENTO GLOBAL'
}
self.validate_discount(rec)
def validate_discount(self, rec, line=False):
if self.model_sale_lines.rowCount() == 0:
return
type_ = rec['type_discount']
discount = int(rec['discount'])
line_id = None
if line:
line_id = line.get('id')
self._current_line_id = line_id
if type_ == 'fixed' and line_id:
sale_line, = self.SaleLine.find([
('id', '=', line_id)
])
price_w_tax = sale_line['product.']['sale_price_taxed']
price = Decimal(price_w_tax) - (discount / Decimal(line['quantity']))
if price < 0:
price = 0
res = self.set_unit_price(price)
else:
lines = []
if line_id:
lines.append(line_id)
else:
# If type is percentage will apply to all lines
lines = [ln['id'] for ln in self.model_sale_lines._data]
if lines:
res = self.set_discount(discount, lines, type_)
if not res:
self.message_bar.set('discount_not_valid')
return False
def _print_reversion(self, sale_id, sale_line_id):
try:
args = {
'sale_id': sale_id,
'sale_line_id': sale_line_id,
}
orders, sale_number = self.Sale.get_reversion(args)
self.receipt_order.print_orders(orders.values(), reversion=True)
except Exception as e:
print(self.print_order, 'validar variable en opcion print_order=True en config_pos.ini')
logging.error('Printing order reversion fail!')
def _print_order(self, sale_id, reversion=False):
result = False
try:
args = {
'sale_id': sale_id,
'repeat': False,
}
if self.environment == 'restaurant' and self.tasks_station:
data_station = self.Sale.method_instance('get_data_for_stations', sale_id)
receipt = Receipt(context={}, environment='restaurant')
receipt.print_tasks(data_station)
orders, sale_number = self.Sale.get_order2print(args)
for line in self.model_sale_lines._data:
line['order_sended'] = ''
self.model_sale_lines.update_record(line)
result = self.receipt_order.print_orders(orders.values(), reversion)
if result:
self.Sale.mark_commanded({'sale_id': sale_id, 'lines_ids': result})
except Exception as e:
print(e, 'error')
traceback.print_exc()
logging.error('Printing order fail!')
return result
def action_delivery_report(self):
_parties = {dp['id']: dp['rec_name'] for dp in self.delivery_parties}
default = self.delivery_parties[0]
add_fields = ('delivery_party', {
'name': 'DOMICILIARIO',
'type': 'selection',
'values': _parties.items(),
'default': default,
})
args, report_name = self.dialog_reports.open_wizard(
'delivery_report', add_fields, open_print=False
)
domain = [
('sale_date', '=', args['date']),
('delivery_party', '=', int(args['delivery_party'])),
('shop', '=', self.shop['id']),
('state', '=', 'processing'),
('payment_term.payment_type', '=', '1'),
]
fields = ['number', 'invoice_number', 'total_amount', 'consumer.rec_name']
sales = self.Sale.find(domain, fields=fields)
data = {
'name': _parties[int(args['delivery_party'])],
'sale_date': str(args['date']),
'sales': [],
}
total = 0
for sale in sales:
total_amount = sale['total_amount']
consumer_address = sale['consumer.']['rec_name'].split('| ')
data['sales'].append({
'order': sale['number'],
'number': sale['invoice_number'],
'consumer_address': consumer_address[2],
'total': total_amount,
})
total += total_amount
data['total'] = total
self.receipt_sale.print_delivery_report(data)
def action_send_order(self, sale_id=None, reversion=False, init_view=True):
sale = self.store.store
sale_id = sale_id or sale['id']
if self.model_sale_lines.rowCount() == 0 or not sale_id:
return
res = self.dialog_order.exec_()
if res == DIALOG_REPLY_NO:
return False
result = None
if not sale.get('number'):
result_set = self.Sale.set_sale_number({'sale_id': sale_id})
self.store.set({'number': result_set})
self.order_number.setText(result_set)
if self.environment == 'restaurant':
if self.print_order:
result = self.print_command(sale)
else:
result = self.print_dispatch(sale)
if result:
self.dialog('order_successfully', widgets=[self.order_number])
else:
self.dialog('order_failed')
if init_view:
self.change_view_to('start_front')
return True
def get_header_sale(self, sale):
order = {
'sale_number': sale['number'],
'number': sale.get('invoice_number'),
'turn': sale.get('turn'),
'position': sale['position'],
'party': sale['party']['name'],
'kind': sale.get('kind'),
'delivery_amount': sale.get('delivery_amount'),
'salesman': sale['salesman']['rec_name'] if sale.get('salesman') else '',
'comment': sale['comment'],
'payment_term': sale['payment_term']['rec_name'] if sale.get('payment_term') else '',
'delivery_charge': sale.get('delivery_charge'),
'total_amount': sale['total_amount'],
'shop': self.shop['name'],
'consumer': sale.get('consumer'),
'table_assigned': sale['table_assigned']['name'] if sale.get('table_assigned') else ''
}
return order
def print_dispatch(self, sale):
order = self.get_header_sale(sale)
lines = self.model_sale_lines._data
order['lines'] = [
{'name': ln['product.']['name'],
'quantity': str(ln['quantity']),
'sale_price_taxed': '',
'amount_w_tax': ln['amount_w_tax'],
'note': ln['note']}
for ln in lines]
order['lines_ids']: [ln['id'] for ln in lines]
result = self.receipt_order.print_orders([order])
return result
def print_command(self, sale, line_reversion=None):
order = self.get_header_sale(sale)
reversion = False if not line_reversion else True
lines = [line_reversion] if reversion else self.model_sale_lines._data
orders = {}
lines_ids = []
for ln in lines:
if not reversion and ln['order_sended'] in (True, ''):
continue
pd_id = ln['product.']['id']
try:
printers, cat_id = self.products_printers.get(str(pd_id))
cat_id = str(cat_id[0])
except Exception:
traceback.print_exc()
printers, cat_id = None, None
if printers:
for printer_id in printers:
printer = self.printers_shop.get(str(printer_id))
if printer_id not in orders.keys():
orders[printer_id] = {
**order,
**printer,
'lines_ids': []
}
if printer.get('categories'):
orders[printer_id]['lines'] = {**printer['lines']}
orders[printer_id]['categories'] = {**printer['categories']}
else:
orders[printer_id]['lines'] = []
value_line = {
'name': ln['product.']['name'],
'quantity': str(ln['quantity']),
'sale_price_taxed': '',
'amount_w_tax': ln['amount_w_tax'],
'note': ln['note']
}
if isinstance(orders[printer_id]['lines'], list):
orders[printer_id]['lines'].append(value_line)
orders[printer_id]['lines_ids'].append(ln['id'])
else:
try:
key_id = orders[printer_id]['categories'][cat_id]
except Exception:
if 'others' not in orders[printer_id]['lines'].keys():
orders[printer_id]['lines']['others'] = {'name': 'OTROS', 'lines':[]}
key_id = 'others'
try:
orders[printer_id]['lines'][key_id]['lines'].append(value_line)
orders[printer_id]['lines_ids'].append(ln['id'])
except Exception:
orders[printer_id]['lines'][key_id]['lines'] = [value_line]
orders[printer_id]['lines_ids'].append(ln['id'])
else:
lines_ids.append(ln['id'])
if self.environment == 'restaurant' and self.tasks_station:
data_station = self.Sale.method_instance('get_data_for_stations', self.sale_id)
receipt = Receipt(context={}, environment='restaurant')
receipt.print_tasks(data_station)
result = self.receipt_order.print_orders(orders.values(), reversion)
lines_sended = result + lines_ids
if not reversion and lines_sended:
for line in lines:
if line['id'] in lines_sended:
line['order_sended'] = ''
self.model_sale_lines.update_record(line)
self.Sale.mark_commanded({'sale_id': self.sale_id, 'lines_ids': lines_sended})
return result
def action_source(self):
if self._state != 'checkout' and self.sources:
self.dialog_source.exec_()
def action_payment_term(self):
if self._state == 'payment' or self.type_pos_user == 'salesman':
self.dialog_payment_term.exec_()
def action_new_sale(self):
if self._state in ['checkout']:
return
if not self.sale_id:
return
if self._ask_new_sale():
self.create_new_sale()
def numpad_price_clicked(self):
code = self.label_input.text()
product = self._search_product(code)
if not product:
return
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):
sale = self.store.store
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 sale['id']:
return
if self._state in ('checkout', 'payment') and not self.user_can_delete:
return self.dialog('not_permission_delete_sale')
if self.type_pos_user in ('order', 'salesman') and \
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.Sale.cancel_sale({'id': sale['id']})
self.field_password_for_cancel_ask.setText('')
self.set_state('cancel')
self.clear_right_panel()
if self.sale_automatic:
self.create_new_sale()
def action_search_product(self):
if self._state in ('checkout', 'disabled'):
return
self.dialog_search_products.show()
def action_start_delivery(self):
self.create_new_sale(kind='delivery')
def action_start_catering(self):
self.create_new_sale(kind='catering')
def action_start_table(self):
self.create_new_sale(kind='to_table')
def action_start_take_away(self):
self.create_new_sale(kind='take_away')
def action_historic_sales(self):
if not hasattr(self, '_sale_historic'):
self._sale_historic = []
if not self._sale_historic:
delta = str(datetime.now() - timedelta(2))
dom = [
('create_date', '>=', delta),
('state', 'in', ['processing', 'done']),
('shop', '=', self.shop['id']),
]
sales = self.Sale.find(dom, fields=_SALE_HISTORIC,
order=[('sale_date', 'DESC')])
self._sale_historic = sales
for record in sales:
self.model_sale_historic.add_record(record)
self.dialog_historic_sales.exec_()
def action_search_sale(self):
if hasattr(self, 'activate_scale_sync') and self.activate_scale_sync:
files = read_files_scale()
for file in files:
self.create_new_sale_from_scale(file)
self.dialog_search_sales.show()
self.search_sales_by_domain()
self.field_invoice_type.set_enabled(True)
def search_sales_by_domain(self, _type='cash'):
if _type == 'reservation':
delta = timedelta(days=30)
elif _type == 'quotation':
delta = timedelta(days=8)
else:
delta = timedelta(days=1)
delta_date = str(datetime.now() - delta)
shop_id = self.shop['id']
if self.type_pos_user == 'cashier':
dom = [['OR',
[
('create_date', '>=', delta_date),
('state', 'in', ['quotation', 'confirmed']),
('shop', '=', shop_id),
], [
('state', '=', 'processing'),
('invoice.state', '=', 'draft'),
('invoice.type', '=', 'out'),
('shop', '=', shop_id),
]]]
elif self.type_pos_user in ('order', 'salesman'):
dom = [
('shop', '=', shop_id),
('create_date', '>=', delta_date),
]
if _type != 'quotation':
dom.append(('state', '=', 'draft'))
elif self.type_pos_user == 'frontend':
dom = [
('state', 'in', ['draft', 'quotation',
'confirmed', 'processing']),
('sale_device', '=', self.device['id']),
('shop', '=', shop_id),
('create_date', '>=', delta_date),
]
else:
dom = [
('state', 'in', ['draft', 'quotation',
'confirmed', 'processing']),
('shop', '=', shop_id),
('create_date', '>=', delta_date),
]
if _type == 'cash':
dom.extend([
[
'OR',
('payment_term', '=', self.default_payment_term['id']),
('payment_term', '=', None),
],
('reservation', '!=', True)
])
elif _type == 'credit':
dom.extend([
['reservation', '!=', True],
['payment_term', '!=', self.default_payment_term['id']]
])
elif _type == 'reservation':
dom.append(('reservation', '=', True))
else:
self.dialog_search_sales.set_from_values([])
return
dom.append(('state', '=', 'quotation'))
# dom.append(['payment_method', '!=', 'all_paid'])
time1 = time.time()
fields = self.dialog_search_sales.fields_names
sales = self.Sale.find(dom, fields=fields, order=[('id', 'DESC')])
self.dialog_search_sales.set_from_values(sales)
if self.environment == 'retail':
dom_draft = [
('create_date', '>=', delta_date),
('state', '=', 'draft'),
('invoice_number', '!=', None),
]
sales_draft = self.Sale.search_count(dom_draft)
self.dialog_search_sales.set_counter_control(sales_draft)
print(time.time() - time1, 'search sales')
def on_search_sale_by_number(self):
target = self.dialog_search_sales.filter_field.text()
if not target:
return
dom = [
('state', '=', 'quotation'),
('number', '=', target)
]
fields = self.dialog_search_sales.fields_names
sales = self.Sale.find(dom, fields=fields, order=[('id', 'DESC')])
self.dialog_search_sales.set_from_values(sales)
def on_selected_sale(self):
sale_id = self.dialog_search_sales.get_id()
if not sale_id:
return
if self.environment != 'restaurant' and self.type_pos_user in ['order', 'salesman']:
sale = self.Sale.find([('id', '=', sale_id)], fields=['salesman.code'])
salesman = self.action_salesman_code()
self.field_salesman_code_ask.setText('')
if not salesman:
return self.dialog('error_salesman_incorrect')
elif salesman['code'] != sale[0]['salesman.']['code']:
return self.dialog('error_salesman_incorrect')
self.load_sale(sale_id)
self.setFocus()
self.label_input.setFocus()
self.change_view_to('order_front')
def on_selected_party(self, party_id=None):
if self._state in ('payment'):
self.message_bar.set('sale_closed')
return
if not party_id:
self.label_input.setFocus()
party_id = self.dialog_search_parties.get_id()
if not party_id:
self.label_input.setFocus()
return
party, = self.Party.find([('id', '=', party_id)])
address_id = ''
if party.get('addresses.'):
address_id = party['addresses.'][0]['id']
invoice_type = 'P'
if party['invoice_type']:
invoice_type = party['invoice_type']
values = {
'party': {'id': party_id, 'name': party['name']},
'invoice_address': address_id,
'shipment_address': address_id,
'invoice_type': invoice_type,
}
payment_term = party.get('customer_payment_term.')
if payment_term:
values['payment_term'] = payment_term
self.field_invoice_type.set_from_id(invoice_type)
self.store.update(values)
self.party_id = party_id
if self._credit_limit_activated:
if not self._check_credit_capacity(party):
return
self.message_bar.set('system_ready')
self.setFocus()
self.label_input.setFocus()
def load_sale(self, sale_id):
# loads only draft sales
self.clear_data()
self.clear_left_panel()
self.clear_right_panel()
self.store.clear()
self._clear_invoice_number = False
fields_sale_line = ['lines.' + f for f in self.SaleLine.fields if f != 'sale']
fields = self.Sale.fields + fields_sale_line
if self._commission_activated and self.environment == 'retail':
fields.extend(['agent.rec_name', 'commission'])
sale, = self.Sale.find([('id', '=', sale_id)], fields=fields)
else:
sale, = self.Sale.find([('id', '=', sale_id)], fields=fields)
self.store.set_sale(sale['id'])
# See option only get invoice_number not all invoice data
sale['invoice'] = sale['invoice_number']
self.store.set(sale)
self.sale_id = sale['id']
self.table_payment_lines.reset()
# self._set_sale_date()
self.journal = {}
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'])
self.field_change.zero()
lines = sale.get('lines.', [])
for line in lines:
self.add_sale_line(line)
if sale.get('payments'):
for payment in sale['payments']:
# FIXME
#self.table_payment_lines.record_add(payment)
pass
self.party_id = sale['party.']['id']
self.set_amounts(sale)
self.set_amount_received()
self.field_amount.setText('')
self.message_bar.set('system_ready')
if self.type_pos_user in ('cashier', 'order', 'salesman'):
self.table_sale_lines.setEnabled(True)
if self.environment == 'restaurant':
if sale.get('table_assigned.'):
self.field_table_assigned.setText(
sale['table_assigned.']['name'] or '')
consumer = sale.get('consumer.', None)
if consumer:
self.field_consumer.setText(consumer['name'] or '')
self._consumer, = self.Consumer.find([
('id', '=', consumer['id']),
])
self.table_sale_lines.setEnabled(True)
self.menu_dash.setDisabled(False)
else:
if self._commission_activated:
if self.field_agent and sale.get('agent.'):
commission = sale.get('commission')
sale['agent.']['rec_name'] = '[' + str(commission) + ']' + ' ' + sale['agent.']['rec_name']
self.store.set({'agent': sale['agent.']})
self.field_agent_id = sale['agent.']['id']
self.field_agent_ask.setText(sale['agent.']['rec_name'])
self.field_commission_ask.setText(str(commission))
self._set_commission_amount(sale['untaxed_amount'], commission)
if self.type_pos_user in ('order', 'salesman'):
if sale['state'] == 'draft':
self.buttons_function.button_to_quote.show()
elif sale['state'] == 'quotation':
self.buttons_function.button_to_draft.show()
# else:
# self.buttons_function.button_change_salesman.show()
if sale['state'] in ('draft', 'quotation'):
self.set_state('add')
elif sale['state'] in ('confirmed', 'processing', 'done'):
self.table_sale_lines.setEnabled(False)
if self.environment == 'restaurant':
self.menu_dash.setDisabled(False)
self.set_state('payment')
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):
pass
# def set_amounts(self, res=None):
# if not res:
# res = self.Sale.get_amounts({'sale_id': self.sale_id})
# self.store.set(res)
def set_amounts(self, res=None):
if not res:
if self._state == 'add':
sale = self.store.store
lines = self.model_sale_lines._data
res = {
'untaxed_amount': 0,
'tax_amount': 0,
'total_amount': 0,
'net_amount': 0,
'residual_amount': None,
'paid_amount': None,
'change': None,
}
for ln in lines:
res['untaxed_amount'] += ln['amount']
res['total_amount'] += ln['amount_w_tax']
tip_amount = sale.get('tip_amount', 0) or 0
delivery_amount = sale.get('delivery_amount', 0) or 0
res['tax_amount'] = res['total_amount'] - res['untaxed_amount']
res['net_amount'] = res['total_amount'] + tip_amount + delivery_amount
else:
res = self.Sale.get_amounts({'sale_id': self.sale_id})
self.store.set(res)
def _get_products_by_category_dash(self, child, find):
child['items'] = self.Product.find([
('code', '!=', None),
('template.salable', '=', True),
('template.categories', '=', child['id']),
], fields=['id', 'name', 'code', 'categories', 'rec_name', 'list_price'],
order=[('template.name', 'ASC')])
# return records
def get_product_by_categories_dash(self):
values = self.Sale.get_product_by_categories(
{
'categories': self.allow_categories_ids
})
return values
def action_square_box_report(self):
values, report_name = self.dialog_reports.open_wizard('square_box_report', open_print=False)
if not values.get('turn'):
return
shop, company = self.shop['id'], self.company
statements = self.Statement.find([
('turn', '=', values['turn']),
('state', '=', 'draft'),
('date', '=', values['date']),
('company', '=', company),
('sale_device.shop', '=', shop)
], fields=['id'])
if statements:
return
values['company'] = company
values['shop'] = shop
self.dialog_reports.open_report(report_name, values)
def action_terminal_journal_report(self):
pass
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 on_selected_product(self):
if self.dialog_search_products.current_row:
self._current_line_id = None
self.add_product(record=self.dialog_search_products.current_row)
def on_selected_product_info(self):
if self.dialog_search_product_info.current_row:
self._current_line_id = None
record = self.dialog_search_product_info.current_row
self.dialog_info_product.search(record)
self.dialog_info_product.show()
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)
], fields=['image'])
if not products:
return
product = products[0]
image = Image(name='product_icon')
if not product['image']:
return
image_64_decode = base64.b64decode(product['image'])
image_ = QtGui.QImage()
image_.loadFromData(image_64_decode)
image.set_image(image_, kind='qimage')
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})
products = self.Product.find([
('code', '=', code),
('template.salable', '=', True),
], fields=['template.positions.warehouse.name', 'template.positions.position.name'])
if products and products[0]['template.']['positions.']:
warehouses = {w[0]: w + [''] for w in res}
for p in products[0]['template.']['positions.']:
warehouses[p['warehouse.']['name']][2] = p['position.']['name']
res = warehouses.values()
self.dialog_product_stock.update_values(res)
self.dialog_product_stock.show()
def on_selected_item_combo(self, record):
list_price = 0
qty_text = self.dialog_combo_product.label_qty_add.text()
try:
qty = int(qty_text) + 1
except:
qty = 1
self.dialog_combo_product.label_qty_add.setText(str(qty))
self.on_selected_item(record, list_price)
def on_selected_item_mix(self, record):
line = self.dialog_product_edit.get()
self._current_line_id = line['id']
# list_price = line['product'].get('list_price')
try:
code = line['product.']['code']
except:
code = line['product']['code']
product = self.Product.find([('code', '=', code)], fields=["list_price"])
list_price = product[0].get('list_price')
code_r = record['code']
product_r = self.Product.find([('code', '=', code_r)], fields=["template.sale_price_w_tax", "list_price"])
list_price_r = product_r[0].get('list_price')
if list_price < list_price_r:
# Update price in selected_line
self.set_unit_price(product_r[0]['template.']['sale_price_w_tax'], discount=False)
list_price = None
self.on_selected_item(record, list_price)
self.dialog_combine_product.close()
self.dialog_product_edit.close()
def on_selected_item(self, record, list_price=None, notes=None):
if not record:
return
self.add_product(record=record, list_price=list_price, notes=notes)
def on_selected_discount(self, discount):
self.dialog_auth_discount.close()
self.dialog_control_panel.close()
self.dialog_fixed_discount.close()
# If dialog edit product is active just it will
# apply discount to one product
_line = self.dialog_product_edit.get() or False
if self._state == 'payment':
dialog = self.dialog('cant_add_discount')
dialog.exec_()
return
if discount:
self.validate_discount(discount, _line)
self.dialog_product_edit.close()
def on_selected_delivery_party(self, delivery_party_id):
if delivery_party_id:
self.store.update({
'delivery_party': delivery_party_id,
})
self.dialog_delivery_party.close()
def on_selected_payment_term(self, record):
if record:
# payment_type = 2 is credit according electronic_invoice_co
payment_type = record['payment_type']
if payment_type == '2' and self.type_pos_user != 'salesman':
if not self.validate_payment_term(record):
return
self.store.update({'payment_term': record})
self._done_sale(is_credit=True)
else:
if not self.validate_payment_term(record):
return
self.store.update({'payment_term': record})
self.dialog_payment_term.close()
def on_selected_source(self, source):
self.dialog_source.close()
data = {'source': source}
# if source.get('party'):
# party = source.get('party')
# self.on_selected_party(party['id'])
# else:
# self.on_selected_party(self.default_party['id'])
self.store.update(data)
def on_selected_payment(self, journal):
self.journal = journal
self.setFocus()
if journal.get('id'):
self.field_journal_id = journal['id']
self.dialog_payment.close()
self.message_bar.set('enter_payment', journal['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)),
('barcode', '=', '%{:}%'.format(tw)),
]
if self.environment == 'retail':
clause.append(('template.reference', 'ilike', '%{:}%'.format(tw)))
domain.append(clause)
if self.shop.get('product_categories'):
categories_id = self.allow_categories_ids
domain.append([
'OR',
('template.categories', 'in', categories_id),
('template.account_category', 'in', categories_id),
])
if not domain:
return
ctx = self.stock_context
if not self.stock_context:
ctx = {'price_list': self.shop['price_list.']['id']}
else:
ctx['price_list'] = self.shop['price_list.']['id']
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, 'tip_amount': 0, 'delivery_amount': 0}
self.party_name = None
self._sale_line = {}
self._total_amount = {}
self._sale_lines_taxes = {}
self.field_journal_id = self.default_journal['id']
self.field_voucher_ask.setText('')
self._consumer = None
def clear_left_panel(self):
self.field_party.setText('')
self.field_salesman.setText('')
self.field_salesman_id = None
self.field_invoice_type.set_from_id(invoice_type[0][0])
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_sale_date.setText('')
self.field_global_discount_ask.setText('')
self.field_bono_discount_manual.setText('')
self.field_amount.setText('')
self.field_number.setText('')
self.current_comment = ''
if hasattr(self, 'field_consumer'):
self.field_consumer.setText('')
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'):
self.field_comment.document().clear()
if self._commission_activated and self.field_agent:
self.field_agent.setText('')
self.field_agent_ask.setText('')
self.field_commission_ask.setText('')
self.field_commission_ask.setReadOnly(True)
self.field_commission_amount.setText('')
self.field_commission_amount.setReadOnly(True)
self.model_sale_lines.reset()
self.clear_input_text()
self.clear_amount_text()
def clear_right_panel(self):
self.field_untaxed_amount.zero()
self.field_tax_amount.zero()
self.field_total_amount.zero()
self.field_change.zero()
self.field_paid_amount.zero()
self.field_discount.zero()
self.table_payment_lines.reset()
if self.environment == 'restaurant':
self.field_tip_amount.zero()
self.field_delivery_amount.zero()
self.field_net_amount.zero()
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 create_new_sale_from_scale(self, file):
to_create = {
'shop': self.shop['id'],
'invoice_type': invoice_type[0][0],
'company': self.company,
'party': self.default_party['id'],
'sale_device': self.device['id'],
'payment_method': 'cash',
'payment_term': self.default_payment_term['id'],
'kind': 'take_away',
'reference': file['ticket'],
'turn': 1,
}
_sale = self.Sale.new_sale(to_create)
def _add_products():
for _line in file['lines']:
_code = _line['product']
dom = [('code', '=', _code)]
products = self.Product.find(
dom,
fields=['id', 'name', 'code', 'description']
)
if not products:
print('Producto no encontrado ...', _code)
return
else:
product = products[0]
qty = Decimal(int(_line['weight']) / 1000)
print('sale qty...', qty, product)
data = {
'sale_id': _sale['id'],
'product_id': product['id'],
'qty': qty
}
res = self.Sale.faster_add_product(data)
print('res ....producto creado ...', res)
_add_products()
def create_new_sale(self, kind='take_away'):
if self.type_pos_user == 'cashier':
self.message_bar.set('not_sale')
return
if self.salesman_required and not self.salesman:
salesman = self.action_salesman_code()
if salesman == 0:
self.dialog_salesman_code.hide()
return
if not salesman:
return self.dialog('error_salesman_wrong')
else:
self.salesman = salesman
if self.environment == 'restaurant' and self._sale_pos_restaurant:
self.dialog_consumer.clear()
self._clear_invoice_number = True
last_invoice_number = self.store.get('invoice_number')
self.store.clear(
exception=['change', 'total_amount', 'net_amount', 'paid_amount']
)
self.journal = {}
self._consumer = {}
self.set_state('add')
self.input_text_changed('')
self.amount_text_changed('0')
self.clear_sign()
change = self.field_change.text()
self.clear_right_panel()
self.field_change.setText(change.replace(',', ''))
self.show_right_panel(True)
self.payment_ctx = {}
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')
to_create = {
'shop': self.shop['id'],
'invoice_type': invoice_type[0][0],
'company': self.company,
'party': self.default_party['id'],
'sale_device': self.device['id'],
'payment_method': 'cash',
'payment_term': self.default_payment_term['id'],
'kind': kind,
'turn': 1,
}
if self.salesman:
to_create.update({'salesman': self.salesman['id']})
if self.environment != 'restaurant' and self._config.get('use_price_list'):
self.field_list_price.set_from_id(self.shop['price_list.']['id'])
sale = self.Sale.new_sale(to_create)
self.store.set(sale)
self.sale_id = sale['id']
# FIXME ADD MORE
self.store.set({'invoice_number': last_invoice_number})
self.party_id = self.default_party['id']
if self.sale_id and self.field_delivery_charge:
if self.field_delivery_charge:
self.field_delivery_charge.set_enabled(True)
self.change_view_to('order_front')
if self.environment == 'restaurant':
self.menu_dash.setDisabled(False)
if kind == 'delivery':
self.action_consumer()
elif kind == 'take_away':
self.action_position()
elif kind == 'to_table':
self.action_tables()
else:
self.set_state('add')
if self.type_pos_user in ('order', 'salesman'):
self.buttons_function.button_to_quote.show()
self.buttons_function.button_to_draft.hide()
if self.environment == 'restaurant':
self.action_source()
self.label_input.setFocus()
# 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_sale_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,
fields=[
'name', 'code', 'categories', 'description',
'id', 'image', 'image_icon', 'list_price',
'quantity', 'rec_name', 'template',
'extra_tax', 'template.sale_price_w_tax',
'template.default_uom', 'write_date'])
if not products or len(products) > 1:
self.message_bar.set('product_not_found')
return False
product = products[0]
return product
def _check_quantity(self):
if self.type_pos_user == 'cashier':
return True
for line in self.model_sale_lines._data:
product_id = None
if line.get('product'):
if isinstance(line['product'], dict):
product_id = line['product']['id']
else:
product_id = line['product']
elif line.get('product.'):
product_id = line['product.']['id']
if product_id:
product, = self.Product.find(
[('id', '=', product_id)],
fields=['id', 'name', 'code', 'rec_name', 'quantity'],
ctx=self.stock_context)
quantity = Decimal(str(round(float(line['quantity']), 5)))
if 'quantity' in product and not self._check_stock_quantity(
product, quantity):
return False
return True
def check_salesman(self):
salesman = self.store.get('salesman')
if self.salesman_required and not salesman:
dialog = self.dialog('missing_salesman')
dialog.exec_()
return False
return True
def add_product(self, record=None, code=None, list_price=None, notes=None):
if self._state == 'disabled':
return
sale_id = self.sale_id
if self._state in ('payment'):
self.message_bar.set('sale_closed')
return
if self._clear_invoice_number:
self.field_invoice_number.setText('')
self._clear_invoice_number = False
self.grid_amounts.label_change.setText('PENDIENTE')
product_id = record['id'] if record else None
if not product_id and code:
# REMOVE ME THIS OPTION IS FOR BARCODE
product = self._search_product(code)
if product:
product_id = product['id']
if not product_id:
self._state = 'warning'
return
data = {
'sale_id': sale_id,
'product_id': product_id,
'qty': 1,
}
if notes:
pass
if list_price is not None:
data['list_price'] = list_price
if hasattr(self, 'field_list_price'):
data['price_list'] = self.field_list_price.get_id()
res = self.Sale.faster_add_product(data)
res['sale'] = sale_id
self._sale_line = res
self._current_line_id = res['id']
self.add_sale_line(res)
self.product_id = product_id
self.message_bar.set('system_ready')
self.set_amounts()
self.set_state('add')
if record:
self._sale_line['product'] = record
if record.get('products_mix') and len(record.get('products_mix')) > 0:
self.action_combo(record['code'])
def _check_stock_quantity(self, product, request_qty=None):
if not isinstance(product['quantity'], (str, int, float, Decimal)):
return True
qty_ = round(Decimal(product['quantity']), 2)
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.get('product.'), dict):
# if not record.get('product.name'):
# record['product.name'] = record['product.']['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 in ('checkout', 'payment'):
return
self.dialog_product_edit.set_line(line)
def sale_form_selected(self, data):
self.dialog_sale_form.start(data)
def clear_delivery_party(self):
self._delivery_party_selected = None
self.row_delivery_party.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_party_active.setChecked(True)
def delivery_party_selected(self, data):
self._delivery_party_selected = data['id']
self.row_delivery_party.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_party_active.setChecked(data['active'])
self.dialog_delivery_party_selected.show()
self.row_delivery_party_active.setFocus()
def set_expenses(self):
statements = self.Statement.find([
('state', '=', 'draft'),
('sale_device', '=', self.device['id']),
('journal.kind', '=', 'cash'),
])
if statements:
self.statement_cash = statements[0]
expenses = self.Expenses.find([
('statement', '=', self.statement_cash['id']),
], fields=['reference', 'invoice_number', 'description', 'amount'])
self.data_expenses = expenses
return True
else:
self.statement_cash = None
self.dialog('statement_closed')
def setup_sale_line(self):
product_code = {
'name': 'product.code',
'align': alignRight,
'description': 'COD',
'width': 80
}
product = {
'name': 'product.name',
'align': alignLeft,
'description': 'NOMBRE',
'width': STRETCH
}
description = {
'name': 'description',
'align': alignLeft,
'description': 'DESCRIPCION',
'width': 180
}
uom = {
'name': 'unit.symbol',
'align': alignHCenter,
'description': 'UNID.',
'width': 50
}
qty = {
'name': 'quantity',
'format': '{:3,.%sf}',
'align': alignRight,
'description': 'CANT.',
'digits': ('unit.symbol', CONVERSION_DIGITS),
'width': 80
}
discount = {
'name': 'discount_rate',
'format': '{0:.0%}',
'align': alignRight,
'description': 'DESC.',
'width': 50
}
amount = {
'name': 'amount_w_tax',
'format': '{:,d}',
'align': alignRight,
'description': 'SUBTOTAL',
'width': 100
}
note = {
'name': 'note',
'align': alignLeft,
'description': 'NOTA',
'invisible': True,
'width': 100
}
unit_price = {
'name': 'unit_price_w_tax',
'format': '{:5,.2f}',
'align': alignLeft,
'description': 'PRECIO',
'invisible': True,
'width': 100
}
qty_fraction = {
'name': 'qty_fraction',
'align': alignHCenter,
'description': 'FRAC',
'width': 50
}
commanded = {
'name': 'order_sended',
'align': alignHCenter,
'description': 'COMAN.',
'width': 70
}
self.fields_sale_line = [product, uom, qty, discount,
amount, note, unit_price]
if self.environment == '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)
if self.environment == 'restaurant':
self.fields_sale_line.insert(4, commanded)
self.model_sale_lines_simple = TableModel(
'sale.line', [product, qty, unit_price, amount, note]
)
self.model_sale_lines = TableModel('sale.line', self.fields_sale_line)
def setup_sale_consumer(self):
number = {
'name': 'number',
'align': alignCenter,
'description': 'ORDEN',
'width': 130
}
invoice_number = {
'name': 'invoice_number',
'align': alignCenter,
'description': 'FACTURA',
'width': 140
}
consumer = {
'name': 'consumer.name',
'align': alignLeft,
'description': 'CONSUMIDOR',
'width': 330
}
party = {
'name': 'party.name',
'align': alignLeft,
'description': 'CLIENTE',
'width': 330
}
sale_date = {
'name': 'sale_date',
'align': alignCenter,
'description': 'FECHA',
'width': 170
}
total_amount_cache = {
'name': 'total_amount_cache',
'align': alignRight,
'format': '{:5,.1f}',
'description': 'VALOR TOTAL',
'width': 180
}
self.fields_sales_query = [
number, invoice_number, sale_date, consumer, party, total_amount_cache
]
self.model_sale_historic = TableModel(
'sale.sale', self.fields_sales_query)
def setup_delivery_party(self):
party = {
'name': 'rec_name',
'align': alignLeft,
'description': 'CLIENTE',
'width': 380
}
id_number = {
'name': 'party.id_number',
'align': alignLeft,
'description': 'NUM. ID',
'width': 200
}
phone = {
'name': 'party.phone',
'align': alignLeft,
'description': 'TELEFONO',
'width': 200
}
number_plate = {
'name': 'number_plate',
'align': alignCenter,
'description': 'PLACA',
'width': 200
}
type_vehicle = {
'name': 'type_vehicle',
'align': alignCenter,
'description': 'TIPO DE VEHICULO',
'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)
self.delivery_parties = [
d for d in self.model_delivery_party._data if d['active']]
def setup_payment(self):
pay_fields = [{
'name': 'statement',
'align': alignLeft,
'description': 'DIARIO',
}, {
'name': 'amount',
'align': alignRight,
'format': '{:5,.1f}',
'description': 'VALOR',
}, {
'name': 'voucher',
'align': alignCenter,
'description': 'VOUCHER',
}]
self.table_payment_lines = TableModel(
'account.statement.line', pay_fields)
def on_change_line_selected(self, key):
self.table_sale_lines.moved_selection(key)
def action_discount_line(self, record):
if self.environment == 'restaurant' and self.type_pos_user not in (
'cashier', 'frontend_admin'):
self.dialog('user_without_permission')
return
self.dialog_fixed_discount.exec_()
if self.environment == 'restaurant':
self.dialog_combine_product.close()
def action_discount_bono_line(self, record):
if self.environment == 'restaurant' and self.type_pos_user not in (
'cashier', 'frontend_admin'):
self.dialog('user_without_permission')
return
self.field_bono_discount_manual.setText('')
self.dialog_fixed_discount_manual.exec_()
if self.environment == 'restaurant':
self.dialog_combine_product.close()
discount = self.field_bono_discount_manual.text()
_line = self.dialog_product_edit.get() or False
if discount and discount.isdigit() and _line:
rec = {
'type_discount': 'fixed',
'discount': discount,
'name': 'BONO ABIERTO',
}
self.validate_discount(rec, _line)
self.dialog_product_edit.close()
def action_combo(self, code=None):
if not code:
line = self.dialog_product_edit.get()
try:
code = line['product.']['code']
except Exception:
code = line['product']['code']
product = self.Product.find(
[('code', '=', code)],
fields=[
"code", "description", "extra_tax", "name", "sale_uom",
"quantity", "sale_price_w_tax", "template.account_category",
"template.sale_price_w_tax", "template.rec_name",
'products_mix.code', 'products_mix.name',
"quantity_mix_required"
])
products_mix = product[0].get('products_mix.')
if not products_mix:
return
qty_min_req = product[0].get('quantity_mix_required')
if qty_min_req:
self.dialog_combo_product.label_qty_min_req.setText(str(qty_min_req))
self.dialog_combo_product.set_buttons(products_mix)
self.dialog_combo_product.exec_()
self.dialog_combo_product.close()
def action_combine_line(self):
line = self.dialog_product_edit.get()
try:
code = line['product.']['code']
except Exception:
code = line['product']['code']
product = self.Product.find([('code', '=', code)])
categories = product[0].get('categories')
if not categories:
return
mixables = self.Product.find([
('categories', 'in', categories),
], fields=['id', 'name', 'code', 'categories', 'rec_name', 'list_price'])
self.dialog_combine_product.set_buttons(mixables)
self.dialog_combine_product.exec_()
self.dialog_combine_product.filter_field.clear()
self.dialog_combine_product.close()
def action_print_count_money(self, data):
try:
self.receipt_sale.print_count_money(data)
except Exception:
pass
def action_delete_line(self):
"""Delete Product """
if self.model_sale_lines.rowCount() <= 0 or self._state == 'checkout':
return
sale = self.store.store
self.table_sale_lines.setFocus()
line = self.table_sale_lines.get_selected_clicked()
state = sale.get('state')
note = None
if self.environment == 'restaurant':
order_sended = line.get('order_sended') and line['order_sended'] in (True, '')
not_delete = self._config.get('no_remove_commanded', False)
if order_sended and (not self.user_can_delete or not_delete):
return self.dialog('user_without_permission')
if order_sended:
res = self.dialog_delete_product.exec_()
if res == 0:
self.dialog_delete_product.close()
return
note = self.field_note_ask.toPlainText()
if note is None or note == '':
return self.dialog('missing_note_for_delete_product')
self.field_note_ask.clear()
elif self.environment == 'retail' and state and state != 'draft':
return self.dialog('dont_delete_product')
# if not removed_item:
removed_item = self.table_sale_lines.delete_item(ignore_focus=True)
if self.environment == 'restaurant' and self.print_order and order_sended:
self.print_command(sale, removed_item)
if note:
self.SaleLine.write([removed_item['id']], {'note': note})
prd_code = removed_item['product.']['code']
if prd_code and self._config['tip_product.']['code'] == prd_code:
self.Sale.write([self.sale_id], {'tip': None})
self.SaleLine.delete([removed_item['id']])
self.set_amounts()
self._current_line_id = None
self.setFocus()
self.label_input.setFocus()
self.dialog_product_edit.close()
def set_discount(self, eval_value, lines_ids=[], type_=None):
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
response = self.SaleLine.faster_set_discount({
'line_ids': target_lines,
'value': value,
'type': type_
})
if response.get('records'):
for rec in response['records']:
self.model_sale_lines.update_record(rec)
res = True
self.set_amounts()
return res
def set_unit_price(self, value, discount=True):
rec = self.SaleLine.set_faster_unit_price({
'id': self._current_line_id,
'value': value,
'discount': discount,
})
if rec:
self.model_sale_lines.update_record(rec)
self.set_amounts()
return True
return False
def add_payment(self, amount):
voucher_number = None
if not self.journal:
journal = self.default_journal
else:
journal = self.journal
if journal.get('require_voucher'):
res = self.dialog_voucher.exec_()
if res == 0:
self.dialog_voucher.close()
return
voucher_number = self.field_voucher_ask.text()
if voucher_number is None or voucher_number == '':
return self.add_payment(amount)
self.field_voucher_ask.setText('')
# FIXME add extra_paid bool
res = self.Sale.faster_add_payment({
'sale_id': self.sale_id,
'journal_id': journal['id'],
'cash_received': to_numeric(amount),
'voucher_number': voucher_number,
'extra_paid': False,
}, ctx={'advance': False, 'reservation': False})
if res.get("msg") and res.get('msg') not in ('missing_money', 'ok') or res.get('error'):
if res.get("msg"):
self.dialog(res['msg'])
return
residual_amount = res.get('residual_amount')
change = res.get('change')
paid_amount = res.pop('paid_amount')
self.store.set({
'residual_amount': residual_amount,
'change': change,
'paid_amount': paid_amount,
})
if res['id']:
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_special, *self.show_keys]
self.keys_input.extend([Qt.Key_Return, Qt.Key_Plus])
# self.keys_input.extend(self.keys_special)
# self.keys_input.extend(self.show_keys)
# self.keys_input.extend(self.keys_numbers)
def set_state(self, state='add'):
self._state = state
state_info = STATES[state]
self._re = state_info['re']
if not self.type_pos_user == 'order':
stacked = self.buttons_stacked.stacked
if not stacked.currentWidget():
return
if state_info['button']:
stacked.setCurrentWidget(
getattr(self.buttons_stacked, state_info['button'])
)
stacked.currentWidget().setVisible(True)
else:
stacked.currentWidget().setVisible(False)
if state == 'payment':
self.table_sale_lines.setDisabled(True)
self.field_invoice_type.set_enabled(False)
if self.environment == 'restaurant':
self.menu_dash.setDisabled(True)
def key_pressed(self, text):
if self._state == 'disabled':
return
if not self._sign and self._state not in ('checkout', 'payment'):
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 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
if self._state in ['payment', 'checkout']:
return
self.sign_text_changed(value)
def key_backspace_pressed(self):
if self._sign or self._state in ('checkout', 'payment'):
self._amount_text = self._amount_text[:-1]
self.amount_text_changed()
self.label_input.setFocus()
else:
self._input_text = self._input_text[:-1]
self.input_text_changed()
def set_text(self, text):
if not self._state == 'checkout':
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):
key = event.key()
self._keyStates[key] = True
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 in ('checkout', 'payment') and key not in self.keys_numbers:
return
self.key_pressed(event.text())
elif key in self.keys_special:
if self._state in ['checkout', 'payment'] 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 ('checkout', 'payment'):
self._clear_context()
# self.close()
elif key == Qt.Key_F1:
if self.type_pos_user in ('cashier', 'frontend', 'frontend_admin'):
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 ['checkout', 'accept']:
self.button_checkout_pressed()
elif key == Qt.Key_F2:
self.action_search_product()
elif key == Qt.Key_F3:
self.button_payment_pressed()
elif key == Qt.Key_F4:
self.action_party()
elif key == Qt.Key_F5:
self.action_global_discount()
elif key == Qt.Key_F6:
self.action_send_order()
elif key == Qt.Key_F8:
self.action_payment_term()
elif key == Qt.Key_F10:
# self.action_check_product_price()
pass
elif key == Qt.Key_F12:
# self.action_cancel()
pass
elif key == Qt.Key_Home:
pass
elif key == Qt.Key_Down or key == Qt.Key_Up:
self.on_change_line_selected(key)
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 ['checkout', 'payment']:
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
def get_product_fraction_prices(self, product_id, sale_id, qty):
price = self.Sale.get_product_prices({
'ids': [product_id],
'quantity': float(qty),
'sale_id': sale_id,
})
return price
def dialog_product_edit_accepted(self, data, line):
if not data:
return
_record = None
update_base_price = self.dialog_product_edit.checkbox_base.isChecked()
if update_base_price:
data['update_base_price'] = update_base_price
if data:
_record = self.edit_line_sale(data, line)
if _record:
self.model_sale_lines.update_record(_record)
self.set_amounts()
self.dialog_product_edit.clear()
def edit_line_sale(self, data, line):
current_unit_price = float(str(round(line['unit_price_w_tax'], 0)))
sale_line, = self.SaleLine.find([
('id', '=', line['id'])
])
base_price = float(str(round(sale_line['product.']['sale_price_taxed'], 0)))
previous_qty = Decimal(line['quantity'])
self._current_line_id = line['id']
to_write = {}
for k, v in data.items():
if k in ['description', 'note']:
to_write[k] = v
elif k == 'unit_price' and float(v) != current_unit_price:
if float(v) > base_price:
self.set_unit_price(v, discount=False)
else:
self._sign = '/'
value = current_unit_price - float(v)
self.set_discount(value, type_='fixed')
self._sign = None
elif k == 'quantity' and v != previous_qty:
if data.get('qty_fraction'):
qty = to_float(float(v), 5)
else:
qty = to_float(v, 2)
qty_data = {
'id': data['id'],
'quantity': qty
}
# if self._config.get('use_price_list'):
# qty_data['use_price_list'] = True
ctx = None
if hasattr(self, 'field_list_price') and self._config.get('use_price_list'):
ctx = {'price_list': self.field_list_price.get_id()}
self.SaleLine.faster_set_quantity(qty_data, ctx=ctx)
elif k == 'update_base_price':
self.set_unit_price(base_price, discount=False)
self.SaleLine.write([data['id']], to_write)
_record, = self.SaleLine.find([
('id', '=', data['id'])
])
return _record
def save_consumer(self, consumer):
if consumer is None:
return
consumer = {k: str(v).splitlines()[0] for k, v in consumer.items()
if k != 'notes' and v != ''}
if consumer.get('id'):
res = self.Consumer.write([consumer['id']], consumer)
else:
res = self.Sale.create_consumer(consumer)
if res:
self.store.update({'consumer': res})
if res.get('party'):
self.on_selected_party(res['party'])
def dialog_delivery_party_accepted(self):
if not self.state_delivery_party:
return
if hasattr(self, '_delivery_party_selected') and self._delivery_party_selected:
res = self.Sale.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.Sale.create_delivery_party({
'data': self.state_delivery_party,
'shop_id': self.ctx['shop']
})
self.model_delivery_party.add_record(res)
self.delivery_parties = [
d for d in self.model_delivery_party._data if d['active']]
self.dialog_delivery_party = DialogDeliveryParty(self).get()
def update_delivery_party(self, field=''):
state_delivery = self.state_delivery_party
field_text = ['party', 'phone', 'id_number', 'number_plate']
if field in field_text:
state_delivery[field] = self.row_delivery_party.text()
elif field == 'active':
state_delivery['active'] = self.row_delivery_party_active.isChecked()
elif field == 'type_vehicle':
state_delivery['type_vehicle'] = self.row_type_vehicle.get_id()
def search_consumer(self, phone):
if phone:
consumers = self.Consumer.find([('phone', '=', phone)])
if consumers:
self._consumer = consumers[0]
self.dialog_consumer.fill(self._consumer)
self.store.update({'consumer': self._consumer})
return True
def action_assign_table(self, table_id, table_name):
current_table = self.store.get('table_assigned')
to_drop = {}
if current_table:
# We must drop current assigned table to sale
to_drop = {
'state': 'available',
'sale': None,
}
if isinstance(current_table, int):
self.RestTables.write([current_table], to_drop)
to_drop['id'] = current_table
elif isinstance(current_table, dict):
self.RestTables.write([current_table['id']], to_drop)
to_drop['id'] = current_table['id']
vals = {
'table_assigned': {
'id': table_id,
'name': table_name,
}
}
self.store.update(vals)
to_add = {
'state': 'occupied',
'sale': self.store.sale_id,
}
self.RestTables.write([table_id], to_add)
return {'to_add': to_add, 'to_drop': to_drop}
def action_see_table(self, table_id):
table_, = self.RestTables.find([
('id', '=', table_id),
], limit=1)
sale = table_['sale']
if sale:
self.change_view_to('order_front')
self.load_sale(sale)
self.dialog_manage_tables.hide()