From 1ae1f76f22fd28881561e73d6f0cd72cc9a7ff3b Mon Sep 17 00:00:00 2001 From: Oscar Alvarez Date: Sun, 20 Dec 2020 06:14:56 -0500 Subject: [PATCH] Refactory models --- INSTALL | 10 +- INSTALL_es | 25 +++-- app/buttonpad.py | 4 +- app/dialogs.py | 118 ++++++++++++++--------- app/mainwindow.py | 216 +++++++++++++++++++++---------------------- app/manage_tables.py | 3 +- app/models.py | 165 +++++++++++++++++++++++++++++++++ app/proxy.py | 16 +++- 8 files changed, 386 insertions(+), 171 deletions(-) create mode 100644 app/models.py diff --git a/INSTALL b/INSTALL index fd382d2..1fe5a9f 100644 --- a/INSTALL +++ b/INSTALL @@ -16,15 +16,19 @@ Prerequisites Client * python3-pil * Required, Pip packages: + pip3 install pyusb pip3 install pillow pip3 install qrcode pip3 install paramiko pip3 install pycups - pip3 install requests - pip3 install simplejson + pip3 install requests + pip3 install simplejson + pip3 install escpos + +For Windows add: + pip3 install PyQt5==5.8.0 pip3 install win32printing - pip3 install escpos Prerequisites Server Modules diff --git a/INSTALL_es b/INSTALL_es index 9e1e328..7547497 100644 --- a/INSTALL_es +++ b/INSTALL_es @@ -1,7 +1,7 @@ Instalación de Tryton POS Client Qt5 ==================================== -Esta versión solo es compatible con Tryton 4.0+, se asume que el usuario tiene +Esta versión solo es compatible con Tryton 5.0+, se asume que el usuario tiene conocimientos básicos previos sobre la instalación y configuración de Tryton, especialmente los modulos oficiales relacionados con contabilidad y ventas, no es el objeto de esta guia abordar temas de configuración básica. @@ -29,15 +29,20 @@ En Debian, Ubuntu y Derivados, se recomienda usar: $ apt install paquete Los siguientes paquetes se deben instalar usando PIP -pip3 install pyusb -pip3 install pillow -pip3 install qrcode -pip3 install paramiko -pip3 install pycups -pip3 install requests -pip3 install simplejson -pip3 install win32printing -pip3 install escpos + pip3 install pyusb + pip3 install pillow + pip3 install qrcode + pip3 install paramiko + pip3 install pycups + pip3 install requests + pip3 install simplejson + pip3 install escpos + + +Para Windows agregar: + pip install pywin32 + pip install PyQt5==5.8.0 + pip install win32printing Tener en cuenta que algunos paquetes se deben instalar con pip para python3. diff --git a/app/buttonpad.py b/app/buttonpad.py index 6e9a0f3..81cc235 100644 --- a/app/buttonpad.py +++ b/app/buttonpad.py @@ -31,7 +31,7 @@ class ButtonsFunction(QGridLayout): self.parent = parent self.set_values(values) if self.parent.enviroment == 'restaurant': - rows = 3 + rows = 4 columns = 4 name_style = 'toolbar' @@ -161,7 +161,7 @@ class ButtonsStacked(QHBoxLayout): # method='button_plus_pressed', # name_style='toolbar', # ) - #self.addWidget(self.button_plus, 0) + # self.addWidget(self.button_plus, 0) if parent.type_pos_user == 'order' or parent.type_pos_user == 'salesman': self.button_send_order = CustomButton( diff --git a/app/dialogs.py b/app/dialogs.py index 0826462..5f4239b 100644 --- a/app/dialogs.py +++ b/app/dialogs.py @@ -2,10 +2,11 @@ from operator import itemgetter from .commons.dialogs import HelpDialog, QuickDialog from PyQt5.QtCore import QRect, Qt -from PyQt5.QtWidgets import (QWidget, QLabel, QTextEdit, QVBoxLayout, - QGridLayout, QLineEdit, QScrollArea, QHBoxLayout, - QDoubleSpinBox, QDateEdit, QDesktopWidget, - QPlainTextEdit, QCheckBox) +from PyQt5.QtWidgets import ( + QWidget, QLabel, QTextEdit, QVBoxLayout, QGridLayout, QLineEdit, + QScrollArea, QHBoxLayout, QDoubleSpinBox, QDateEdit, QDesktopWidget, + QPlainTextEdit, QCheckBox +) from .constants import (alignCenter, alignLeft, FRACTIONS) from .commons.forms import FieldMoney, ComboBox from .commons.search_window import SearchWindow @@ -16,6 +17,18 @@ from app.commons.table import TableView from .commons.custom_button import CustomButton from .tools import get_icon +__all__ = [ + 'ControlPanel', 'SearchSale', 'SearchParty', 'SearchProduct', 'SaleLine', + 'Comment', 'Position', 'DialogPayment', 'DialogTaxes', 'DialogChannel', + 'DialogTableMoneyCount', 'DialogTableDeliveryParty', 'DialogDeliveryMen', + 'Help', 'DeliveryPartySelected', 'DialogPrintInvoice', 'DialogStock', + 'DialogGlobalDiscountTable', 'DialogSalesman', 'DialogAgent', + 'DialogOrder', 'DialogGlobalDiscount', 'DialogVoucher', 'DialogConsumer', + 'DialogManageTables', 'DialogTableSaleConsumer', 'SaleConsumerSelected', + 'DialogCancelInvoice', 'DialogForceAssign' +] + + TYPE_VEHICLE = [ ('', ''), ('motorcycle', 'Motorcycle'), @@ -24,6 +37,13 @@ TYPE_VEHICLE = [ ] +def get_screen(): + screen = QDesktopWidget().screenGeometry() + width = screen.width() + height = screen.height() + return width, height + + def create_vbox(parent, values, method, columns=4, dimension=None): vbox_ = QVBoxLayout() grid = QGridLayout() @@ -36,10 +56,10 @@ def create_vbox(parent, values, method, columns=4, dimension=None): scroll_area.setWidgetResizable(True) scroll_area.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOn) scroll_area.setWidget(grid_buttons) + width, height = get_screen() if dimension: - screen = QDesktopWidget().screenGeometry() - width = screen.width() / dimension[0] - height = screen.height() / dimension[0] + width = width / dimension[0] + height = height / dimension[0] scroll_area.setFixedSize(width, height) grid.addWidget(scroll_area) vbox_.addLayout(grid) @@ -54,7 +74,6 @@ class ControlPanel(QWidget): def get(self, menu_dash): string_ = self.tr('CONTROL PANEL') - screen = QDesktopWidget().screenGeometry() vbox_ = QVBoxLayout() grid = QGridLayout() scroll_area = QScrollArea() @@ -62,7 +81,8 @@ class ControlPanel(QWidget): grid.addWidget(scroll_area) vbox_.addLayout(grid) dialog = QuickDialog(self._parent, 'action', widgets=[vbox_]) - dialog.setFixedSize(screen.width()/2, screen.height()/1.2) + width, height = get_screen() + dialog.setFixedSize(width/2, height/1.2) dialog.setWindowTitle(string_) return dialog @@ -90,8 +110,8 @@ class SearchSale(QWidget): 'on_return_method': 'on_selected_sale' } return SearchWindow(self._parent, headers, None, methods, - filter_column=[1, 2, 3, 4], cols_width=widths, - title=title, fill=True) + filter_column=[1, 2, 3, 4], cols_width=widths, + title=title, fill=True) class SearchParty(QWidget): @@ -190,15 +210,15 @@ class DialogManageTables(QWidget): self._parent = parent def get(self): - if not self._parent._Tables: + if not self._parent._sale_pos_restaurant: return - tables = self._parent._Tables.find([ + tables = self._parent.RestTables.find([ ('shop', '=', self._parent.shop['id']) ]) _tables = ManageTables(self._parent, tables, self._parent.action_table_assigned) dialog = QuickDialog(self._parent, 'action', widgets=[_tables]) - screen = QDesktopWidget().screenGeometry() - dialog.setFixedSize(screen.width()/1.5, screen.height()/1.5) + width, height = get_screen() + dialog.setFixedSize(width/1.5, height/1.5) return dialog @@ -212,7 +232,6 @@ class DialogConsumer(QWidget): self._parent._consumer = None vbox_consumer = QVBoxLayout() grid = QGridLayout() - screen = QDesktopWidget().screenGeometry() label_phone = QLabel(self.tr('PHONE:')) label_phone.setObjectName('label_phone') @@ -278,7 +297,8 @@ class DialogConsumer(QWidget): vbox_consumer.addLayout(grid) dialog = QuickDialog(self._parent, 'action', widgets=[vbox_consumer]) dialog.setWindowTitle('CONSUMER') - dialog.setGeometry(0, 0, screen.width()/2.7, screen.height()/1.8) + width, height = get_screen() + dialog.setGeometry(0, 0, width/2.7, height/1.8) dialog.accepted.connect(self._parent.dialog_search_consumer_accepted) return dialog @@ -302,14 +322,16 @@ class DialogTableSaleConsumer(QWidget): def get(self): string_ = self.tr('SALES') - - col_sizes_tlines = [field['width'] for field in self._parent.fields_sale_consumer] + width, height = get_screen() + col_sizes_tlines = [ + field['width'] for field in self._parent.fields_sale_consumer + ] table = TableView('model_sale_consumer', - self._parent.model_sale_consumer, col_sizes_tlines, - method_selected_row=self._parent.sale_consumer_selected) + self._parent.model_sale_consumer, col_sizes_tlines, + method_selected_row=self._parent.sale_consumer_selected + ) - screen = QDesktopWidget().screenGeometry() - table.setFixedSize(screen.width()/1.5, screen.height()/2) + table.setFixedSize(width/1.5, height/2) vbox_ = QVBoxLayout() layout = QVBoxLayout() @@ -319,7 +341,7 @@ class DialogTableSaleConsumer(QWidget): vbox_.addLayout(layout) vbox_.addSpacing(10) dialog = QuickDialog(self._parent, 'action', widgets=[vbox_]) - dialog.setFixedSize(screen.width()/1.3, screen.height()/1.7) + dialog.setFixedSize(width/1.3, height/1.7) dialog.setWindowTitle(string_) return dialog @@ -332,7 +354,6 @@ class SaleConsumerSelected(QWidget): def get(self): vbox_ = QVBoxLayout() grid = QGridLayout() - screen = QDesktopWidget().screenGeometry() label_party = QLabel(self.tr('PARTY:')) label_party.setAlignment(alignLeft) @@ -371,8 +392,9 @@ class SaleConsumerSelected(QWidget): label_lines.setObjectName('label_lines') grid.addWidget(label_lines, 3, 1, 1, 4) col_sizes_tlines = [field['width'] for field in self._parent.fields_sale_line] - table = TableView('model_sale_lines', self._parent.model_sale_customer_lines, - col_sizes_tlines) + table = TableView('model_sale_lines', + self._parent.model_sale_customer_lines, col_sizes_tlines + ) grid.addWidget(table, 4, 1, 1, 4) label_untaxed = QLabel(self.tr('UNTAXED AMOUNT:')) @@ -404,7 +426,8 @@ class SaleConsumerSelected(QWidget): vbox_.addLayout(grid) dialog = QuickDialog(self._parent, 'action', widgets=[vbox_]) - dialog.setFixedSize(screen.width()/1.2, screen.height()/1.2) + width, height = get_screen() + dialog.setFixedSize(width/1.2, height/1.2) return dialog def get_button(self): @@ -554,7 +577,6 @@ class DialogSalesman(QWidget): newlist = sorted(self._parent.employees, key=itemgetter('rec_name')) employees_ = [[e['id'], '', e['rec_name'], ' '] for e in newlist] - screen = QDesktopWidget().screenGeometry() vbox_salesman = create_vbox( self._parent, employees_, self._parent.on_selected_salesman, @@ -563,7 +585,8 @@ class DialogSalesman(QWidget): dialog = QuickDialog(self._parent, 'action', size=(800, 500), widgets=[vbox_salesman]) dialog.setWindowTitle(string_) - dialog.setFixedSize(screen.width()/1.5, screen.height()/1.5) + width, height = get_screen() + dialog.setFixedSize(width/1.5, height/1.5) return dialog @@ -595,14 +618,13 @@ class DialogDeliveryMen(QWidget): newlist = sorted(self._parent.delivery_man, key=itemgetter('rec_name')) man_ = [[e['id'], '', e['rec_name'], e['number_plate'] or ' '] for e in newlist] - screen = QDesktopWidget().screenGeometry() vbox_salesman = create_vbox(self._parent, man_, - self._parent.on_selected_delivery_men, - dimension=[1.7, 1.8]) + self._parent.on_selected_delivery_men, dimension=[1.7, 1.8]) dialog = QuickDialog(self._parent, 'action', size=(800, 500), widgets=[vbox_salesman]) dialog.setWindowTitle(string_) - dialog.setFixedSize(screen.width()/1.5, screen.height()/1.5) + width, height = get_screen() + dialog.setFixedSize(width/1.5, height/1.5) return dialog @@ -693,11 +715,11 @@ class DialogTableDeliveryParty(QWidget): col_sizes_tlines = [field['width'] for field in self._parent.fields_delivery_party] table = TableView('model_delivery_party', - self._parent.model_delivery_party, col_sizes_tlines, - method_selected_row=self._parent.delivery_party_selected) - - screen = QDesktopWidget().screenGeometry() - table.setFixedSize(screen.width()/2.2, screen.height()/2.2) + self._parent.model_delivery_party, col_sizes_tlines, + method_selected_row=self._parent.delivery_party_selected + ) + width, height = get_screen() + table.setFixedSize(width/2.2, height/2.2) vbox_ = QVBoxLayout() grid = QGridLayout() @@ -708,7 +730,8 @@ class DialogTableDeliveryParty(QWidget): vbox_.addLayout(grid) dialog = QuickDialog(self._parent, 'action', widgets=[vbox_]) - dialog.setGeometry(0, 0, screen.width()/1.8, screen.height()/1.5) + width, height = get_screen() + dialog.setGeometry(0, 0, width/1.8, height/1.5) dialog.setWindowTitle(string_) return dialog @@ -736,8 +759,8 @@ class DialogTableMoneyCount(QWidget): table = TableView('model_money_count', self._parent.model_money_count, col_sizes_tlines, selection_edit=True) - screen = QDesktopWidget().screenGeometry() - table.setFixedSize(screen.width()/3.3, screen.height()/3) + width, height = get_screen() + table.setFixedSize(width/3.3, height/3) vbox_ = QVBoxLayout() label, field = self.get_fields() @@ -756,7 +779,8 @@ class DialogTableMoneyCount(QWidget): vbox_.addLayout(layout2) vbox_.addSpacing(20) dialog = QuickDialog(self._parent, 'action', widgets=[vbox_]) - dialog.setGeometry(0, 0, screen.width()/3, screen.height()/1.8) + width, height = get_screen() + dialog.setGeometry(0, 0, width/3, height/1.8) dialog.setWindowTitle(string_) dialog.accepted.connect(self._parent.dialog_money_count_accepted) return dialog @@ -773,6 +797,7 @@ class DialogTableMoneyCount(QWidget): self._parent.row_field_total_money.setAlignment(alignLeft) return label_amount, self._parent.row_field_total_money + class DialogTaxes(QWidget): def __init__(self, parent): super(DialogTaxes, self).__init__(parent) @@ -799,14 +824,17 @@ class DialogChannel(QWidget): def get(self): string_ = self.tr('SELECT CHANNEL') - channels = [[c['id'], '', c['rec_name'].upper(), str(c['id'])] - for c in self._parent.channels] + channels = [ + [c['id'], '', c['rec_name'].upper(), str(c['id'])] + for c in self._parent.channels + ] vbox_ = create_vbox(self._parent, channels, self._parent.on_selected_channel) dialog = QuickDialog(self._parent, 'action', widgets=[vbox_]) dialog.setWindowTitle(string_) return dialog + class DialogPaymentTerm(QWidget): def __init__(self, parent): super(DialogPaymentTerm, self).__init__(parent) diff --git a/app/mainwindow.py b/app/mainwindow.py index 11a5da1..8ed5263 100644 --- a/app/mainwindow.py +++ b/app/mainwindow.py @@ -26,7 +26,7 @@ from app.commons.forms import GridForm, ComboBox, FieldNumeric from app.commons.messages import MessageBar from app.commons.image import Image from app.commons.table import TableView -from app.commons.model import TableModel, Modules, TableModelEdit +from app.commons.model import TableModel, TableModelEdit from app.commons.frontwindow import FrontWindow from app.commons.menu_buttons import MenuDash from .proxy import FastModel @@ -173,7 +173,7 @@ class MainWindow(FrontWindow): def get_current_sale(self): if hasattr(self, '_sale') and self._sale['id']: - sales = self.ModSale.find([ + sales = self.Sale.find([ ('id', '=', self._sale['id']) ]) if not sales: @@ -197,7 +197,7 @@ class MainWindow(FrontWindow): def delete_current_sale(self): if self._sale['id']: - self.ModSale.cancel_sale(self._sale['id']) + self.Sale.cancel_sale(self._sale['id']) def resize_window_tablet_dev(self): self.resize(690, self.get_geometry()[1]) @@ -250,7 +250,7 @@ class MainWindow(FrontWindow): }) def load_modules(self): - modules = Modules(self, self.conn) + # modules = Modules(self, self.conn) self._sale_pos_restaurant = None self.Module = FastModel('ir.module', self.ctx) self.Config = FastModel('sale.configuration', self.ctx) @@ -313,23 +313,25 @@ class MainWindow(FrontWindow): ) } - _Tables = self._Tables = None + # _RestTables = None if self.enviroment == 'restaurant': self._sale_pos_restaurant = self.Module.find([ ('name', '=', 'sale_pos_frontend_rest'), ('state', '=', 'activated'), ]) - if self._sale_pos_restaurant: - self.PartyConsumer = FastModel('party.consumer', self.ctx) - _Tables = { - 'name': '_Tables', - 'model': 'sale.shop.table', - 'fields': ('name', 'shop', 'capacity', 'state') - } - _PosSale['fields'].extend(['table_assigned', 'table_assigned.name', 'table_assigned.state']) + print('self._sale_pos_restaurant', self._sale_pos_restaurant) + # if self._sale_pos_restaurant: + # self.PartyConsumer = FastModel('party.consumer', self.ctx) + # _RestTables = True + # _RestTables = { + # 'name': '_Tables', + # 'model': 'sale.shop.table', + # 'fields': ('name', 'shop', 'capacity', 'state') + # } + # _PosSale['fields'].extend(['table_assigned', 'table_assigned.name', 'table_assigned.state']) - if self._commission_activated: - _PosSale['fields'].extend(['agent', 'agent.party.name', 'commission']) + # if self._commission_activated: + # _PosSale['fields'].extend(['agent', 'agent.party.name', 'commission']) self.User = FastModel('res.user', self.ctx) self._user, = self.User.find([('login', '=', self.user)]) @@ -343,8 +345,8 @@ class MainWindow(FrontWindow): self.ctx['user'] = self._user['id'] - self.ModSale = FastModel('sale.sale', self.ctx) - self.ModSaleLine = FastModel('sale.line', self.ctx) + self.Sale = FastModel('sale.sale', self.ctx) + self.SaleLine = FastModel('sale.line', self.ctx) self.Product = FastModel('product.product', self.ctx) self.Journal = FastModel('account.statement.journal', self.ctx) self.Employee = FastModel('company.employee', self.ctx) @@ -360,13 +362,8 @@ class MainWindow(FrontWindow): if self._commission_activated: self.Agent = FastModel('commission.agent', self.ctx) self.Comission = FastModel('commission', self.ctx) - - models_to_work = [_PosSale] - - if _Tables: - models_to_work.append(_Tables) - - # modules.set_models(models_to_work) + if self._sale_pos_restaurant: + self.RestTables = FastModel('sale.shop.table', self.ctx) self.device, = self.Device.find([ ('id', '=', self._user['sale_device']['id']), @@ -453,6 +450,7 @@ class MainWindow(FrontWindow): if self._commission_activated: self.dialog_agent = DialogAgent(self).get() if self.enviroment == 'restaurant' and self._sale_pos_restaurant: + print('Heere... yesss',) self.dialog_table_sale_consumer = DialogTableSaleConsumer(self).get() self.dialog_sale_consumer_selected = SaleConsumerSelected(self).get() self.dialog_search_consumer = DialogConsumer(self).get() @@ -464,7 +462,7 @@ class MainWindow(FrontWindow): if ">" in self.printer_sale_name: self.printer_sale_name = str(str("\\") + self.printer_sale_name.replace('>', str('\\'))) - ctx_printing = self.ModSale.get_printing_context({ + ctx_printing = self.Sale.get_printing_context({ 'device_id': self.device['id'], 'user': self.user, }) @@ -506,12 +504,14 @@ class MainWindow(FrontWindow): # Return sale to draft state if not self._check_quantity(): return - self._PosSale.to_quote(self._sale['id']) + + args = {'sale_id': self._sale['id']} + self.Sale.to_quote(args) if self.model_sale_lines.rowCount() > 0: if self.check_salesman(): self.state_disabled() try: - self.ModSale.generate_shipment({ + self.Sale.generate_shipment({ 'sale_id': self._sale['id'], }) self.dialog('order_dispatched') @@ -521,7 +521,7 @@ class MainWindow(FrontWindow): def button_to_draft_pressed(self): # Return sale to draft state if hasattr(self, '_sale'): - self.ModSale.to_draft(self._sale['id']) + self.Sale.to_draft(self._sale['id']) self.state_disabled() def create_gui(self): @@ -704,19 +704,19 @@ class MainWindow(FrontWindow): # 'size': self.screen_size, # 'color': self.label_color # })) - DELIVERY_WAY = [ + KIND = [ ('', ''), ('take_away', self.tr('TAKE AWAY')), ('delivery', self.tr('DELIVERY')), - ('table', self.tr('TABLE')) + ('to_table', self.tr('TO TABLE')) ] info_fields.append( - ('delivery_way', { - 'name': self.tr('DELIVERY WAY'), + ('kind', { + 'name': self.tr('KIND'), 'placeholder': False, 'type': 'selection', - 'on_change': 'action_delivery_way_selection_changed', - 'values': DELIVERY_WAY, + 'on_change': 'action_kind_selection_changed', + 'values': KIND, 'size': self.screen_size, 'color': self.label_color })) @@ -906,7 +906,7 @@ class MainWindow(FrontWindow): return False if self._current_line_id: self._sale_line['quantity'] = float(quantity) - rec = self.ModSaleLine.faster_set_quantity(self._sale_line) + rec = self.SaleLine.faster_set_quantity(self._sale_line) except: return self.message_bar.set('quantity_not_valid') @@ -926,7 +926,7 @@ class MainWindow(FrontWindow): elif self._sign == '/': if value <= 0: return - sale_line, = self.ModSaleLine.find([ + sale_line, = self.SaleLine.find([ ('id', '=', self._current_line_id) ]) price_w_tax = sale_line['product']['sale_price_w_tax'] @@ -1027,7 +1027,7 @@ class MainWindow(FrontWindow): def set_discount_amount(self): res = 0 if self._sale['id']: - res = self.ModSale.get_discount_total({ + res = self.Sale.get_discount_total({ 'sale_id': self._sale['id'] }) self.field_discount.setText(res) @@ -1046,7 +1046,7 @@ class MainWindow(FrontWindow): def __do_invoice_thread(self): try: - res = self.ModSale.faster_post_invoice({ + res = self.Sale.faster_post_invoice({ 'sale_id': self.sale_to_post['id'] }) except: @@ -1058,7 +1058,7 @@ class MainWindow(FrontWindow): if self.sale_to_post['is_credit']: return - self.ModSale.reconcile_invoice({ + self.Sale.reconcile_invoice({ 'sale_id': self.sale_to_post['id'] }) # self.set_state('finished') @@ -1067,17 +1067,18 @@ class MainWindow(FrontWindow): self._sale['is_credit'] = is_credit self.sale_to_post = self._sale self.do_invoice.start() - if self._Tables and self._sale.get('table_assigned'): - self._Tables.write([self._sale['table_assigned']], - {'state': 'available'}) - if 1: #try: + if self.RestTables and self._sale.get('table_assigned'): + self.RestTables.write( + [self._sale['table_assigned']], {'state': 'available'} + ) + try: if self.print_receipt == 'automatic': _copies = self.device['shop']['invoice_copies'] if not is_credit: self.print_invoice(copies=_copies) if self.print_order and self.print_auto_order: self.action_print_order() - else: #except: + except: logging.error(sys.exc_info()[0]) if not is_credit and self._commission_activated: @@ -1096,13 +1097,13 @@ class MainWindow(FrontWindow): if not sale_id: sale_id = self._sale['id'] args = {'sale_id': sale_id} - data = self.ModSale.get_data(args) + data = self.Sale.get_data(args) for i in range(copies): self.receipt_sale.print_sale(data) def button_duplicate_sale(self): if self.sale_customer_selected: - res = self.ModSale.duplicate_sale({ + res = self.Sale.duplicate_sale({ 'sale_id': self.sale_customer_selected, }) self.load_sale(res['sale_id']) @@ -1123,7 +1124,7 @@ class MainWindow(FrontWindow): ('consumer', '=', self._consumer['id']), ] self.model_sale_consumer.reset() - sales = self.ModSale.find(dom, limit=10) + sales = self.Sale.find(dom, limit=10) for record in sales: self.model_sale_consumer.add_record(record) self.dialog_table_sale_consumer.exec_() @@ -1143,7 +1144,7 @@ class MainWindow(FrontWindow): if not self.validate_payment_term(): return sale_id = self._sale['id'] - res, msg = self.ModSale.faster_process({'sale_id': sale_id}) + res, msg = self.Sale.faster_process({'sale_id': sale_id}) if msg: self.message_bar.set(msg) return @@ -1179,10 +1180,10 @@ class MainWindow(FrontWindow): self.add_product(code=self._config['tip_product']['code']) self.button_plus_pressed() eval_value = int((self._config['tip_rate'] / 100) * total_amount) - self.ModSaleLine.write( + self.SaleLine.write( [self._current_line_id], {'unit_price': Decimal(eval_value)} ) - self.ModSale.write([self._sale['id']], {'tip': Decimal(eval_value)}) + self.Sale.write([self._sale['id']], {'tip': Decimal(eval_value)}) self.set_unit_price(eval_value) self.update_total_amount() @@ -1214,7 +1215,7 @@ class MainWindow(FrontWindow): def action_open_statement(self): if not self.dialog_money_count.exec_(): return - res = self.ModSale.faster_open_statement({ + res = self.Sale.faster_open_statement({ 'device': self.device['id'], 'total_money': Decimal( self.row_field_total_money.text().replace(',', '') @@ -1228,7 +1229,7 @@ class MainWindow(FrontWindow): def action_closed_statement(self): if not self.dialog_money_count.exec_(): return - res = self.ModSale.faster_close_statement({ + res = self.Sale.faster_close_statement({ 'device': self.device['id'], 'data': self.model_money_count._data, }) @@ -1268,7 +1269,7 @@ class MainWindow(FrontWindow): self.dialog('customer_not_credit') return False - self._credit_amount = self.ModSale.get_credit_amount_party({'party_id': self.party_id}) + self._credit_amount = self.Sale.get_credit_amount_party({'party_id': self.party_id}) self._credit_limit_amount = party['credit_limit_amount'] if is_credit and self._credit_limit_amount and \ @@ -1279,7 +1280,7 @@ class MainWindow(FrontWindow): def action_payment_term_selection_changed(self): is_credit = self._payment_terms[str(self.field_payment_term_id)]['is_credit'] - self.ModSale.write([self._sale['id']], {'payment_term': self.field_payment_term_id}) + self.Sale.write([self._sale['id']], {'payment_term': self.field_payment_term_id}) self.field_payment_term.setText(self._payment_terms[str(self.field_payment_term_id)]['name']) if is_credit and self.type_pos_user != 'salesman': if self.validate_credit_limit(): @@ -1301,21 +1302,21 @@ class MainWindow(FrontWindow): def action_delivery_charge_selection_changed(self, index): val = self.field_delivery_charge.get_id() if val: - self.ModSale.write([self._sale['id']], {'delivery_charge': val}) + self.Sale.write([self._sale['id']], {'delivery_charge': val}) - def action_delivery_way_selection_changed(self, index): - val = self.field_delivery_way.get_id() + def action_kind_selection_changed(self, index): + val = self.field_kind.get_id() if val: - self.ModSale.write([self._sale['id']], {'delivery_way': val}) + self.Sale.write([self._sale['id']], {'kind': val}) def action_invoice_type_selection_changed(self, index): val = self.field_invoice_type.get_id() if val and self._sale and self._sale.get('id'): - self.ModSale.write([self._sale['id']], {'invoice_type': val}) + self.Sale.write([self._sale['id']], {'invoice_type': val}) def action_tax_selection_changed(self): - res = self.ModSale.add_tax(self._sale['id'], self.field_tax_id, - self._context) + res = self.Sale.add_tax(self._sale['id'], self.field_tax_id, + self._context) self._sale.update(res) self.set_amounts() @@ -1339,11 +1340,11 @@ class MainWindow(FrontWindow): sale = {} if number: if type_doc in ['order', 'delivery']: - sales = self.ModSale.find([('number', '=', number)]) + sales = self.Sale.find([('number', '=', number)]) if sales: sale = sales[0] else: - sale = self.ModSale.get_sale_from_invoice([1], number) + sale = self.Sale.get_sale_from_invoice([1], number) if not sale: return self.message_bar.set('sale_number_not_found') sale_id = sale['id'] @@ -1374,14 +1375,14 @@ class MainWindow(FrontWindow): self.dialog_comment.exec_() comment = self.field_comment_ask.text() if comment: - self.ModSale.write([self._sale['id']], {'comment': comment}) + self.Sale.write([self._sale['id']], {'comment': comment}) def action_position(self): self.dialog_position.exec_() position = self.field_position_ask.text() if hasattr(self, 'field_position') and position: self.field_position.setText(position) - self.ModSale.write([self._sale['id']], {'position': position}) + self.Sale.write([self._sale['id']], {'position': position}) def action_agent(self): self.dialog_agent.exec_() @@ -1389,7 +1390,7 @@ class MainWindow(FrontWindow): if not res: return commission = float(res) - sale, = self.ModSale.find([ + sale, = self.Sale.find([ ('id', '=', self._sale['id']), ]) self.field_agent_id = self.field_agent_ask.get_id() @@ -1399,7 +1400,7 @@ class MainWindow(FrontWindow): ('id', '=', self.field_agent_id), ]) if commission <= agent['plan']['percentage']: - self.ModSale.write([self._sale['id']], { + self.Sale.write([self._sale['id']], { 'agent': self.field_agent_id, 'commission': int(commission), }) @@ -1441,16 +1442,15 @@ class MainWindow(FrontWindow): def _print_order(self, sale_id, kind, reversion=False): result = False - if 1: #try: + try: args = { 'sale_id': sale_id, 'reversion': reversion, 'repeat': False, } - orders = self.ModSale.get_order2print(args) - print(orders) + orders = self.Sale.get_order2print(args) result = self.receipt_order.print_orders(orders, reversion, kind) - else: #except: + except: logging.error('Printing order fail!') return result @@ -1537,7 +1537,7 @@ class MainWindow(FrontWindow): response = dialog.exec_() if response == DIALOG_REPLY_NO: return - self.ModSale.cancel_sale(self._sale['id']) + self.Sale.cancel_sale(self._sale['id']) self.field_password_for_cancel_ask.setText('') self.set_state('cancel') self.clear_right_panel() @@ -1571,7 +1571,7 @@ class MainWindow(FrontWindow): ] fields = self.dialog_search_sales.fields_names - sales = self.ModSale.find(dom, fields=fields, order=[('id', 'DESC')]) + sales = self.Sale.find(dom, fields=fields, order=[('id', 'DESC')]) self.dialog_search_sales.set_from_values(sales) if self.enviroment == 'retail': dom_draft = [ @@ -1579,7 +1579,7 @@ class MainWindow(FrontWindow): ('state', '=', 'draft'), ('invoice_number', '!=', None), ] - sales_draft = self.ModSale.find(dom_draft) + sales_draft = self.Sale.find(dom_draft) self.dialog_search_sales.set_counter_control(sales_draft) response = self.dialog_search_sales.execute() self.field_invoice_type.set_enabled(True) @@ -1618,13 +1618,13 @@ class MainWindow(FrontWindow): self.field_payment_term_id = self.default_payment_term['id'] self.field_payment_term.setText(self.default_payment_term['name']) - self.ModSale.write([self._sale['id']], values) + self.Sale.write([self._sale['id']], values) self.party_id = party_id self.field_party.setText(party['name']) if party.get('salesman'): self.field_salesman_id = party['salesman']['id'] - self.ModSale.write([self._sale['id']], {'salesman': self.field_salesman_id}) + self.Sale.write([self._sale['id']], {'salesman': self.field_salesman_id}) self.field_salesman.setText(party['salesman']['name']) # if self._credit_limit_activated: @@ -1640,7 +1640,7 @@ class MainWindow(FrontWindow): self.clear_data() self.clear_left_panel() self.clear_right_panel() - sale, = self.ModSale.find([('id', '=', sale_id)]) + sale, = self.Sale.find([('id', '=', sale_id)]) self._sale.update(sale) self.table_payment_lines.reset() self.field_order_number.setText(sale['number'] or '') @@ -1655,7 +1655,7 @@ class MainWindow(FrontWindow): self.field_salesman.setText(sale['salesman']['name'] or '') self.field_salesman_id = sale['salesman']['id'] - res = self.ModSale.get_invoice_type({'sale_id': sale_id}) + res = self.Sale.get_invoice_type({'sale_id': sale_id}) if res: self.field_invoice_type.set_from_id(res['invoice_type']) @@ -1673,8 +1673,8 @@ class MainWindow(FrontWindow): if sale.get('channel'): self.field_channel.setText(sale['channel']['rec_name'].upper()) - if sale.get('delivery_way'): - self.field_delivery_way.set_from_id(sale['delivery_way']) + if sale.get('kind'): + self.field_kind.set_from_id(sale['kind']) if sale.get('delivery_party'): self.field_delivery_men.setText(sale['delivery_party']['name'] or '') @@ -1691,7 +1691,7 @@ class MainWindow(FrontWindow): self._set_commission_amount(sale['untaxed_amount'], commission) self.line_ids = [l['id'] for l in sale.get('lines')] if sale.get('lines'): - lines = self.ModSaleLine.find([ + lines = self.SaleLine.find([ ('id', 'in', self.line_ids), ]) sale['lines'] = lines @@ -1730,7 +1730,7 @@ class MainWindow(FrontWindow): def set_amounts(self, res=None): if not res: - res = self.ModSale.get_amounts({'sale_id': self._sale['id']}) + res = self.Sale.get_amounts({'sale_id': self._sale['id']}) self._sale.update(res) residual_amount = 0 @@ -1853,8 +1853,8 @@ class MainWindow(FrontWindow): def on_selected_salesman(self, salesman_id): if salesman_id: - self.ModSale.write([self._sale['id']], {'salesman': salesman_id}) - sale, = self.ModSale.find([('id', '=', self._sale['id'])]) + self.Sale.write([self._sale['id']], {'salesman': salesman_id}) + sale, = self.Sale.find([('id', '=', self._sale['id'])]) self.field_salesman.setText(sale['salesman']['name'] or '') self.field_salesman_id = salesman_id self.dialog_salesman.close() @@ -1867,8 +1867,8 @@ class MainWindow(FrontWindow): def on_selected_delivery_men(self, delivery_men_id): if delivery_men_id: - self.ModSale.write([self._sale['id']], {'delivery_party': delivery_men_id}) - sale, = self.ModSale.find([('id', '=', self._sale['id'])]) + self.Sale.write([self._sale['id']], {'delivery_party': delivery_men_id}) + sale, = self.Sale.find([('id', '=', self._sale['id'])]) self.field_delivery_men.setText(sale['delivery_party']['name'] or '') self.field_delivery_men_id = delivery_men_id self.dialog_delivery_men.close() @@ -1885,7 +1885,7 @@ class MainWindow(FrontWindow): ('id', '=', int(channel_id)) ]) self.field_channel.setText(channel['rec_name'].upper()) - self.ModSale.write([self._sale['id']], {'channel': channel_id}) + self.Sale.write([self._sale['id']], {'channel': channel_id}) self.dialog_channel.close() def on_selected_payment(self, payment_id): @@ -2042,7 +2042,7 @@ class MainWindow(FrontWindow): self.clear_data() self.clear_left_panel() self.message_bar.set('system_ready') - self._sale = self.ModSale.new_sale({ + self._sale = self.Sale.new_sale({ 'shop': self.shop['id'], 'invoice_type': 'P', 'company': self.company['id'], @@ -2059,8 +2059,8 @@ class MainWindow(FrontWindow): }) if hasattr(self, 'field_channel'): self.field_channel.setText('') - if hasattr(self, 'field_delivery_way'): - self.field_delivery_way.set_from_id('') + if hasattr(self, 'field_kind'): + self.field_kind.set_from_id('') if hasattr(self, 'field_delivery_men'): self.field_delivery_men.setText('') @@ -2147,7 +2147,7 @@ class MainWindow(FrontWindow): 'product_id': product_id, 'qty': 1 } - res = self.ModSale.faster_add_product(data) + res = self.Sale.faster_add_product(data) self._sale_line = res self._current_line_id = res['id'] self.add_sale_line(res) @@ -2456,7 +2456,7 @@ class MainWindow(FrontWindow): """Delete product """ - self.ModSaleLine.delete([removed_item['id']]) + self.SaleLine.delete([removed_item['id']]) self.set_amounts() self.update_total_amount() # self.clear_right_panel() @@ -2469,7 +2469,7 @@ class MainWindow(FrontWindow): if removed_item and self.print_order: self.action_print_order(self._sale['id'], removed_item) if self._config['tip_product']['code'] == removed_item['product.code']: - self.ModSale.write([self._sale['id']], {'tip': None}) + self.Sale.write([self._sale['id']], {'tip': None}) def set_discount(self, eval_value, lines_ids=[]): res = False @@ -2486,7 +2486,7 @@ class MainWindow(FrontWindow): else: target_lines = lines_ids - records = self.ModSaleLine.faster_set_discount({ + records = self.SaleLine.faster_set_discount({ 'line_ids': target_lines, 'value': value }) @@ -2499,7 +2499,7 @@ class MainWindow(FrontWindow): return res def set_unit_price(self, value): - rec = self.ModSaleLine.set_faster_unit_price({ + rec = self.SaleLine.set_faster_unit_price({ 'id': self._current_line_id, 'value': value, }) @@ -2518,7 +2518,7 @@ class MainWindow(FrontWindow): if voucher_number is None or voucher_number == '': return self.add_payment(amount) - res = self.ModSale.faster_add_payment({ + res = self.Sale.faster_add_payment({ 'sale_id': self._sale['id'], 'journal_id': self.field_journal_id, 'amount': to_numeric(amount), @@ -2707,7 +2707,7 @@ class MainWindow(FrontWindow): self.row_field_qty.setValue(float(qty)) value = self.field_combobox_fraction.get_label() self.state_line['quantity'] = qty - price_ = self.ModSale.get_product_prices({ + price_ = self.Sale.get_product_prices({ 'ids': [self._sale_line['product']['id']], 'quantity': float(qty), 'sale_id': self._sale['id'], @@ -2734,23 +2734,23 @@ class MainWindow(FrontWindow): self._sign = '/' self._process_price(unit_price) self._sign = None - _record = self.ModSaleLine.write([self._current_line_id], {}) + _record = self.SaleLine.write([self._current_line_id], {}) if self.state_line.get('quantity'): quantity = self.state_line.pop('quantity') if self._process_quantity(str(quantity)): - _record = self.ModSaleLine.faster_set_quantity({ + _record = self.SaleLine.faster_set_quantity({ 'id': self._current_line_id, 'quantity': to_float(quantity, 2) }) if self.state_line.get('description'): - _record = self.ModSaleLine.write([self._current_line_id], { + _record = self.SaleLine.write([self._current_line_id], { 'description': self.state_line['description'] }) if self.state_line.get('note'): - _record = self.ModSaleLine.write([self._current_line_id], { + _record = self.SaleLine.write([self._current_line_id], { 'note': self.state_line['note'] }) if _record: @@ -2768,17 +2768,17 @@ class MainWindow(FrontWindow): if not self.state_consumer: return if self._consumer: - res = self.ModSale.update_consumer({ + res = self.Sale.update_consumer({ 'id': self._consumer['id'], 'fields': self.state_consumer, }) else: - res = self.ModSale.create_consumer({ + res = self.Sale.create_consumer({ 'fields': self.state_consumer, }) if res['msg'] == 'ok': self.consumer_id = res['consumer'] - self.ModSale.write([self._sale['id']], {'consumer': self.consumer_id}) + self.Sale.write([self._sale['id']], {'consumer': self.consumer_id}) if res.get('party'): self.on_selected_party(res['party']) @@ -2786,14 +2786,14 @@ class MainWindow(FrontWindow): if not self.state_delivery_party: return if hasattr(self, '_delivery_party_selected') and self._delivery_party_selected: - res = self.ModSale.update_delivery_party({ + 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.ModSale.create_delivery_party({ + res = self.Sale.create_delivery_party({ 'data': self.state_delivery_party, 'shop_id': self.ctx['shop'] }) @@ -2867,7 +2867,7 @@ class MainWindow(FrontWindow): self._Tables.write([id], {'state': new_state}) - self.ModSale.write([self._sale['id']], { + self.Sale.write([self._sale['id']], { 'table_assigned': table_assigned }) self.field_table_assigned.setText(name) @@ -2884,7 +2884,7 @@ class MainWindow(FrontWindow): self.button_history_customer.setVisible(False) # def print_equivalent_invoice(self, sale_id): - # sale, = self.ModSale.find([('id', '=', sale_id)]) + # sale, = self.Sale.find([('id', '=', sale_id)]) # if not sale['invoices']: # return diff --git a/app/manage_tables.py b/app/manage_tables.py index 4130543..2cc44da 100644 --- a/app/manage_tables.py +++ b/app/manage_tables.py @@ -5,7 +5,7 @@ from PyQt5.QtWidgets import QGridLayout, QPushButton DIR_SHARE = os.path.abspath( os.path.normpath(os.path.join(__file__, '..', '..', 'share'))) -__all__ = ['ManageTables'] +__all__ = ['ManageTables', 'CallButton'] STATES = { 'available': 'rgb(180, 180, 180)', @@ -57,6 +57,7 @@ class ManageTables(QGridLayout): self.buttons = {} positions = [(i, j) for i in range(rows) for j in range(columns)] for position, value in zip(positions, tables): + print('XXXX == ', position, value) button = CallButton(value, method) self.buttons[button.id] = button self.addWidget(button, *position) diff --git a/app/models.py b/app/models.py new file mode 100644 index 0000000..d8047aa --- /dev/null +++ b/app/models.py @@ -0,0 +1,165 @@ + +MODELS = { + 'ir.module': { + 'rec_name': 'name', + 'fields': [ + 'name', 'state' + ] + }, + 'company.company': { + 'rec_name': 'rec_name', + 'fields': [ + 'party', 'logo' + ], + 'binaries': ['logo'] + }, + 'res.user': { + 'rec_name': 'name', + 'fields': ['name', 'sale_device'] + }, + 'product.category': { + 'rec_name': 'name', + 'fields': [ + 'name', 'leasable', 'parent', 'childs', 'name_icon', 'accounting' + ] + }, + 'sale.sale': { + 'rec_name': 'number', + 'fields': [ + 'number', 'party', 'salesman.party.name', 'lines', 'sale_date', + 'state', 'total_amount_cache', 'salesman', 'payment_term', + 'invoices', 'payments', 'untaxed_amount', 'position', 'tax_amount', + 'total_amount', 'residual_amount', 'paid_amount', 'invoice', + 'invoices', 'delivery_charge', 'price_list', 'agent', 'commission', + 'invoice_number', 'invoice.state', 'shipment_address', + 'state_string', 'shop', 'delivery_party', 'reference', + 'channel', 'channel.rec_name', 'delivery_way', 'consumer', + 'delivery_state', 'table_assigned', + ] + }, + 'sale.line': { + 'rec_name': 'product', + 'fields': [ + 'product', 'product.template.sale_price_w_tax', 'type', + 'quantity', 'unit_price_w_tax', 'product.description', 'note', + 'description', 'qty_fraction', 'amount_w_tax', 'unit.symbol', + 'product.template.name', 'product.code', 'unit.digits', 'amount', + 'discount', 'product.sale_price_w_tax', 'product.quantity', + 'order_sended' + ] + }, + 'account.statement.journal': { + 'rec_name': 'number', + 'fields': ['name', 'require_voucher', 'kind'] + }, + 'company.employee': { + 'rec_name': 'rec_name', + 'fields': ['rec_name', 'party'] + }, + 'commission.agent': { + 'rec_name': 'rec_name', + 'fields': [ + 'active', 'party', 'party.id_number', 'rec_name', 'plan.percentage' + ] + }, + 'sale.delivery_party': { + 'rec_name': 'rec_name', + 'fields': [ + 'active', 'party', 'party.id_number', 'party.phone', 'rec_name', + 'number_plate', 'type_vehicle', + ] + }, + 'sale.discount': { + 'rec_name': 'rec_name', + 'fields': ['active', 'name', 'rec_name', 'discount'] + }, + 'sale.web_channel': { + 'rec_name': 'rec_name', + 'fields': ['id', 'rec_name'] + }, + 'party.consumer': { + 'rec_name': 'rec_name', + 'fields': ['party', 'id_number', 'rec_name', 'name', 'address', + 'phone', 'birthday', 'preferences'] + }, + 'account.invoice.payment_term': { + 'rec_name': 'name', + 'fields': ['name', 'active'] + }, + 'sale.device': { + 'rec_name': 'name', + 'fields': [ + 'name', 'shop', 'shop.company', 'shop.name', 'shop.taxes', + 'shop.party', 'journals', 'shop.product_categories', 'journal', + 'shop.payment_term', 'shop.warehouse', 'shop.discount_pos_method', + 'shop.salesman_pos_required', 'shop.electronic_authorization', + 'shop.invoice_copies', 'shop.pos_authorization', 'shop.discounts', + 'shop.computer_authorization', 'shop.manual_authorization', + 'shop.credit_note_electronic_authorization', 'shop.salesmans', + 'shop.debit_note_electronic_authorization', 'shop.delivery_man', + ] + }, + 'sale.shop': { + 'rec_name': 'name', + 'fields': ['taxes', 'product_categories', 'party', 'invoice_copies', + 'warehouse', 'payment_term', 'salesmans', 'delivery_man', + 'discounts'] + }, + 'product.product': { + 'rec_name': 'rec_name', + 'fields': [ + 'name', 'code', 'barcode', 'write_date', 'description', + 'template.sale_price_w_tax', 'template.account_category', + 'location.name', 'image', 'image_icon', 'quantity', + 'encoded_sale_price', 'location', 'locations', 'uom' + ], + 'binaries': ['image'] + }, + 'party.party': { + 'rec_name': 'name', + 'fields': [ + 'name', 'id_number', 'addresses', 'phone', 'customer_payment_term', + 'customer_payment_term.name', 'credit_limit_amount', + 'credit_amount', 'street', 'receivable', 'salesman' + ] + }, + 'ir.action.report': { + 'rec_name': 'report_name', + 'fields': ['action', 'report_name'] + }, + 'sale.configuration': { + 'rec_name': 'id', + 'fields': [ + 'tip_product', 'tip_product.code', 'show_description_pos', + 'show_position_pos', 'show_stock_pos', 'encoded_sale_price', + 'tip_rate', 'password_force_assign', 'show_agent_pos', + 'show_product_image', 'show_location_pos', 'show_order_number', + 'show_brand', 'show_delivery_charge', 'decimals_digits_quantity', + 'password_admin_pos', 'show_fractions', 'discount_pos_method', + 'new_sale_automatic', 'default_invoice_type', 'cache_products_local', + 'print_invoice_payment', + ] + }, + 'party.address': { + 'rec_name': 'name', + 'fields': [ + 'name', 'street' + ] + }, + 'sale.shop.table': { + 'rec_name': 'name', + 'fields': ['id', 'name', 'state'] + }, + 'account.tax': { + 'rec_name': 'name', + 'fields': [ + 'name' + ] + }, + 'commission': { + 'rec_name': 'rec_name', + 'fields': [ + 'name' + ] + }, +} diff --git a/app/proxy.py b/app/proxy.py index 132de6c..4b44f79 100644 --- a/app/proxy.py +++ b/app/proxy.py @@ -1,8 +1,9 @@ - import requests from datetime import date import simplejson as json +from app.models import MODELS + def encoder(obj): # FIXME: add datetime, buffer, bytes @@ -18,12 +19,21 @@ def encoder(obj): class FastModel(object): - def __init__(self, model, ctx): + def __init__(self, model, ctx, fields=None): self.model = model self.ctx = ctx api_url = ctx['params']['api_url'] db = ctx['params']['database'] self.api = '/'.join(['http:/', api_url, db]) + _model = MODELS.get(model) + self.fields = None + if fields: + self.fields = fields + elif _model.get('fields'): + self.fields = _model['fields'] + else: + print('No hay model ', model) + def __getattr__(self, name, *args): 'Return attribute value' @@ -34,6 +44,8 @@ class FastModel(object): if ctx: self.ctx.update(ctx) route = self.get_route('search') + if not fields: + fields = self.fields args_ = { 'model': self.model, 'domain': domain,