diff --git a/addons/point_of_sale/__manifest__.py b/addons/point_of_sale/__manifest__.py index 713c990330e..7689d082fc7 100644 --- a/addons/point_of_sale/__manifest__.py +++ b/addons/point_of_sale/__manifest__.py @@ -36,7 +36,6 @@ 'views/account_statement_report.xml', 'views/res_users_view.xml', 'views/res_partner_view.xml', - 'views/pos_config_settings_views.xml', 'views/report_statement.xml', 'views/report_userlabel.xml', 'views/report_saledetails.xml', diff --git a/addons/point_of_sale/data/point_of_sale_data.xml b/addons/point_of_sale/data/point_of_sale_data.xml index 0c97ed89258..13059b39a0c 100644 --- a/addons/point_of_sale/data/point_of_sale_data.xml +++ b/addons/point_of_sale/data/point_of_sale_data.xml @@ -24,6 +24,11 @@ reload + + + Tips + + Main diff --git a/addons/point_of_sale/models/__init__.py b/addons/point_of_sale/models/__init__.py index 9df58f2c5fe..c65760ad7fc 100644 --- a/addons/point_of_sale/models/__init__.py +++ b/addons/point_of_sale/models/__init__.py @@ -6,7 +6,6 @@ from . import account_journal from . import barcode_rule from . import pos_category from . import pos_config -from . import pos_config_settings from . import pos_order from . import pos_session from . import product diff --git a/addons/point_of_sale/models/pos_config.py b/addons/point_of_sale/models/pos_config.py index 05259bd89a3..65a082d9f65 100644 --- a/addons/point_of_sale/models/pos_config.py +++ b/addons/point_of_sale/models/pos_config.py @@ -3,7 +3,7 @@ import uuid from odoo import api, fields, models, _ -from odoo.exceptions import UserError +from odoo.exceptions import ValidationError class AccountCashboxLine(models.Model): @@ -11,9 +11,16 @@ class AccountCashboxLine(models.Model): default_pos_id = fields.Many2one('pos.config', string='This cashbox line is used by default when opening or closing a balance for this point of sale') + @api.multi + def name_get(self): + result = [] + for cashbox_line in self: + result.append((cashbox_line.id, "%s * %s"%(cashbox_line.coin_value, cashbox_line.number))) + return result + class AccountBankStmtCashWizard(models.Model): _inherit = 'account.bank.statement.cashbox' - + @api.model def default_get(self, fields): vals = super(AccountBankStmtCashWizard, self).default_get(fields) @@ -44,9 +51,6 @@ class PosConfig(models.Model): def _get_default_location(self): return self.env['stock.warehouse'].search([('company_id', '=', self.env.user.company_id.id)], limit=1).lot_stock_id - def _get_default_nomenclature(self): - return self.env['barcode.nomenclature'].search([], limit=1) - def _get_group_pos_manager(self): return self.env.ref('point_of_sale.group_pos_manager') @@ -56,7 +60,8 @@ class PosConfig(models.Model): def _compute_default_customer_html(self): return self.env['ir.qweb'].render('point_of_sale.customer_facing_display_html') - name = fields.Char(string='Point of Sale Name', index=True, required=True, help="An internal identification of the point of sale") + name = fields.Char(string='Point of Sale Name', index=True, required=True, help="An internal identification of the point of sale.") + is_installed_account_accountant = fields.Boolean(compute="_compute_is_installed_account_accountant") journal_ids = fields.Many2many( 'account.journal', 'pos_config_journal_rel', 'pos_config_id', 'journal_id', string='Available Payment Methods', @@ -77,37 +82,36 @@ class PosConfig(models.Model): help="Accounting journal used to create invoices.", default=_default_invoice_journal) currency_id = fields.Many2one('res.currency', compute='_compute_currency', string="Currency") - iface_cashdrawer = fields.Boolean(string='Cashdrawer', help="Automatically open the cashdrawer") - iface_payment_terminal = fields.Boolean(string='Payment Terminal', help="Enables Payment Terminal integration") - iface_electronic_scale = fields.Boolean(string='Electronic Scale', help="Enables Electronic Scale integration") - iface_vkeyboard = fields.Boolean(string='Virtual KeyBoard', help="Enables an integrated Virtual Keyboard") - iface_customer_facing_display = fields.Boolean(string='Customer Facing Display', help="Enables a remotely connected customer facing display") - iface_print_via_proxy = fields.Boolean(string='Print via Proxy', help="Bypass browser printing and prints via the hardware proxy") - iface_scan_via_proxy = fields.Boolean(string='Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner") - iface_invoicing = fields.Boolean(string='Invoicing', help='Enables invoice generation from the Point of Sale', default=True) - iface_big_scrollbars = fields.Boolean('Large Scrollbars', help='For imprecise industrial touchscreens') + iface_cashdrawer = fields.Boolean(string='Cashdrawer', help="Automatically open the cashdrawer.") + iface_payment_terminal = fields.Boolean(string='Payment Terminal', help="Enables Payment Terminal integration.") + iface_electronic_scale = fields.Boolean(string='Electronic Scale', help="Enables Electronic Scale integration.") + iface_vkeyboard = fields.Boolean(string='Virtual KeyBoard', help=u"Don’t turn this option on if you take orders on smartphones or tablets. \n Such devices already benefit from a native keyboard.") + iface_customer_facing_display = fields.Boolean(string='Customer Facing Display', help="Show checkout to customers with a remotely-connected screen.") + iface_print_via_proxy = fields.Boolean(string='Print via Proxy', help="Bypass browser printing and prints via the hardware proxy.") + iface_scan_via_proxy = fields.Boolean(string='Scan via Proxy', help="Enable barcode scanning with a remotely connected barcode scanner.") + iface_invoicing = fields.Boolean(string='Invoicing', help='Enables invoice generation from the Point of Sale.') + iface_big_scrollbars = fields.Boolean('Large Scrollbars', help='For imprecise industrial touchscreens.') iface_print_auto = fields.Boolean(string='Automatic Receipt Printing', default=False, - help='The receipt will automatically be printed at the end of each order') - iface_print_skip_screen = fields.Boolean(string='Skip Receipt Screen', default=True, + help='The receipt will automatically be printed at the end of each order.') + iface_print_skip_screen = fields.Boolean(string='Skip Preview Screen', default=True, help='The receipt screen will be skipped if the receipt can be printed automatically.') iface_precompute_cash = fields.Boolean(string='Prefill Cash Payment', - help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount') - iface_tax_included = fields.Boolean(string='Include Taxes in Prices', - help='The displayed prices will always include all taxes, even if the taxes have been setup differently') - iface_start_categ_id = fields.Many2one('pos.category', string='Start Category', - help='The point of sale will display this product category by default. If no category is specified, all available products will be shown') + help='The payment input will behave similarily to bank payment input, and will be prefilled with the exact due amount.') + iface_tax_included = fields.Selection([('subtotal', 'Tax-Excluded Prices'), ('total', 'Tax-Included Prices')], "Tax Display", default='subtotal', required=True) + iface_start_categ_id = fields.Many2one('pos.category', string='Initial Category', + help='The point of sale will display this product category by default. If no category is specified, all available products will be shown.') iface_display_categ_images = fields.Boolean(string='Display Category Pictures', help="The product categories will be displayed with pictures.") restrict_price_control = fields.Boolean(string='Restrict Price Modifications to Managers', - help="Check to box to restrict the price control to managers only on point of sale orders.") + help="Only users with Manager access rights for PoS app can modify the product prices on orders.") cash_control = fields.Boolean(string='Cash Control', help="Check the amount of the cashbox at opening and closing.") - receipt_header = fields.Text(string='Receipt Header', help="A short text that will be inserted as a header in the printed receipt") - receipt_footer = fields.Text(string='Receipt Footer', help="A short text that will be inserted as a footer in the printed receipt") + receipt_header = fields.Text(string='Receipt Header', help="A short text that will be inserted as a header in the printed receipt.") + receipt_footer = fields.Text(string='Receipt Footer', help="A short text that will be inserted as a footer in the printed receipt.") proxy_ip = fields.Char(string='IP Address', size=45, - help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty') + help='The hostname or ip address of the hardware proxy, Will be autodetected if left empty.') active = fields.Boolean(default=True) uuid = fields.Char(readonly=True, default=lambda self: str(uuid.uuid4()), - help='A globally unique identifier for this pos configuration, used to prevent conflicts in client-generated data') + help='A globally unique identifier for this pos configuration, used to prevent conflicts in client-generated data.') sequence_id = fields.Many2one('ir.sequence', string='Order IDs Sequence', readonly=True, help="This sequence is automatically created by Odoo but you can change it " "to customize the reference numbers of your orders.", copy=False) @@ -121,21 +125,39 @@ class PosConfig(models.Model): last_session_closing_date = fields.Date(compute='_compute_last_session') pos_session_username = fields.Char(compute='_compute_current_session_user') group_by = fields.Boolean(string='Group Journal Items', default=True, - help="Check this if you want to group the Journal Items by Product while closing a Session") + help="Check this if you want to group the Journal Items by Product while closing a Session.") pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', required=True, default=_default_pricelist) company_id = fields.Many2one('res.company', string='Company', required=True, default=lambda self: self.env.user.company_id) - barcode_nomenclature_id = fields.Many2one('barcode.nomenclature', string='Barcodes', required=True, default=_get_default_nomenclature, - help='Defines what kind of barcodes are available and how they are assigned to products, customers and cashiers') + barcode_nomenclature_id = fields.Many2one('barcode.nomenclature', string='Barcode Nomenclature', + help='Defines what kind of barcodes are available and how they are assigned to products, customers and cashiers.') group_pos_manager_id = fields.Many2one('res.groups', string='Point of Sale Manager Group', default=_get_group_pos_manager, - help='This field is there to pass the id of the pos manager group to the point of sale client') + help='This field is there to pass the id of the pos manager group to the point of sale client.') group_pos_user_id = fields.Many2one('res.groups', string='Point of Sale User Group', default=_get_group_pos_user, - help='This field is there to pass the id of the pos user group to the point of sale client') + help='This field is there to pass the id of the pos user group to the point of sale client.') + iface_tipproduct = fields.Boolean(string="Product tips") tip_product_id = fields.Many2one('product.product', string='Tip Product', - help="The product used to encode the customer tip. Leave empty if you do not accept tips.") - fiscal_position_ids = fields.Many2many('account.fiscal.position', string='Fiscal Positions') + help="This product is used as reference on customer receipts.") + fiscal_position_ids = fields.Many2many('account.fiscal.position', string='Fiscal Positions', help='This is useful for restaurants with onsite and take-away services that imply specific tax rates.') default_fiscal_position_id = fields.Many2one('account.fiscal.position', string='Default Fiscal Position') default_cashbox_lines_ids = fields.One2many('account.cashbox.line', 'default_pos_id', string='Default Balance') customer_facing_display_html = fields.Html(string='Customer facing display content', translate=True, default=_compute_default_customer_html) + use_pricelist = fields.Boolean("Use a pricelist.") + tax_regime = fields.Boolean("Tax Regime") + tax_regime_selection = fields.Boolean("Tax Regime Selection value") + barcode_scanner = fields.Boolean("Barcode Scanner") + start_category = fields.Boolean("Set Start Category") + module_pos_restaurant = fields.Boolean("Is a Bar/Restaurant") + module_pos_discount = fields.Boolean("Global Discounts") + module_pos_loyalty = fields.Boolean("Loyalty Program") + module_pos_mercury = fields.Boolean(string="Integrated Card Payments") + module_pos_reprint = fields.Boolean(string="Reprint Receipt") + is_posbox = fields.Boolean("PosBox") + is_header_or_footer = fields.Boolean("Header & Footer") + + def _compute_is_installed_account_accountant(self): + account_accountant = self.env['ir.module.module'].sudo().search([('name', '=', 'account_accountant'), ('state', '=', 'installed')]) + for pos_config in self: + pos_config.is_installed_account_accountant = account_accountant and account_accountant.id @api.depends('journal_id.currency_id', 'journal_id.company_id.currency_id') def _compute_currency(self): @@ -179,27 +201,22 @@ class PosConfig(models.Model): @api.constrains('company_id', 'stock_location_id') def _check_company_location(self): if self.stock_location_id.company_id and self.stock_location_id.company_id.id != self.company_id.id: - raise UserError(_("The company of the stock location is different than the one of point of sale")) + raise ValidationError(_("The company of the stock location is different than the one of point of sale")) @api.constrains('company_id', 'journal_id') def _check_company_journal(self): if self.journal_id and self.journal_id.company_id.id != self.company_id.id: - raise UserError(_("The company of the sales journal is different than the one of point of sale")) + raise ValidationError(_("The company of the sales journal is different than the one of point of sale")) @api.constrains('company_id', 'invoice_journal_id') - def _check_company_journal(self): + def _check_company_invoice_journal(self): if self.invoice_journal_id and self.invoice_journal_id.company_id.id != self.company_id.id: - raise UserError(_("The invoice journal and the point of sale must belong to the same company")) + raise ValidationError(_("The invoice journal and the point of sale must belong to the same company")) @api.constrains('company_id', 'journal_ids') def _check_company_payment(self): if self.env['account.journal'].search_count([('id', 'in', self.journal_ids.ids), ('company_id', '!=', self.company_id.id)]): - raise UserError(_("The company of a payment method is different than the one of point of sale")) - - @api.constrains('fiscal_position_ids', 'default_fiscal_position_id') - def _check_default_fiscal_position(self): - if self.default_fiscal_position_id and self.default_fiscal_position_id not in self.fiscal_position_ids: - raise UserError(_("The default fiscal position must be included in the available fiscal positions of the point of sale")) + raise ValidationError(_("The company of a payment method is different than the one of point of sale")) @api.onchange('iface_print_via_proxy') def _onchange_iface_print_via_proxy(self): @@ -210,6 +227,60 @@ class PosConfig(models.Model): if self.picking_type_id.default_location_src_id.usage == 'internal' and self.picking_type_id.default_location_dest_id.usage == 'customer': self.stock_location_id = self.picking_type_id.default_location_src_id.id + @api.onchange('use_pricelist') + def _onchange_use_pricelist(self): + """ + If the 'pricelist' box is unchecked, we reset the pricelist_id to stop + using a pricelist for this posbox. + """ + if not self.use_pricelist: + self.pricelist_id = self._default_pricelist() + + @api.onchange('iface_scan_via_proxy') + def _onchange_iface_scan_via_proxy(self): + if self.iface_scan_via_proxy: + self.barcode_scanner = True + else: + self.barcode_scanner = False + + @api.onchange('barcode_scanner') + def _onchange_barcode_scanner(self): + if self.barcode_scanner: + self.barcode_nomenclature_id = self.env['barcode.nomenclature'].search([], limit=1) + else: + self.barcode_nomenclature_id = False + + @api.onchange('is_posbox') + def _onchange_is_posbox(self): + if not self.is_posbox: + self.proxy_ip = False + self.iface_scan_via_proxy = False + self.iface_electronic_scale = False + self.iface_cashdrawer = False + self.iface_print_via_proxy = False + self.iface_customer_facing_display = False + + @api.onchange('tax_regime') + def _onchange_tax_regime(self): + if not self.tax_regime: + self.default_fiscal_position_id = False + + @api.onchange('tax_regime_selection') + def _onchange_tax_regime_selection(self): + if not self.tax_regime_selection: + self.fiscal_position_ids = [(5, 0, 0)] + + @api.onchange('start_category') + def _onchange_start_category(self): + if not self.start_category: + self.iface_start_categ_id = False + + @api.onchange('is_header_or_footer') + def _onchange_header_footer(self): + if not self.is_header_or_footer: + self.receipt_header = False + self.receipt_footer = False + @api.multi def name_get(self): result = [] @@ -235,7 +306,17 @@ class PosConfig(models.Model): val.update(name=_('POS order line %s') % values['name'], code='pos.order.line') values['sequence_line_id'] = IrSequence.create(val).id - return super(PosConfig, self).create(values) + pos_config = super(PosConfig, self).create(values) + pos_config.sudo()._check_modules_to_install() + # If you plan to add something after this, use a new environment. The one above is no longer valid after the modules install. + return pos_config + + @api.multi + def write(self, vals): + result = super(PosConfig, self).write(vals) + self.sudo()._set_fiscal_position() + self.sudo()._check_modules_to_install() + return result @api.multi def unlink(self): @@ -244,6 +325,32 @@ class PosConfig(models.Model): pos_config.sequence_line_id.unlink() return super(PosConfig, self).unlink() + def _set_fiscal_position(self): + for config in self: + if config.tax_regime and config.default_fiscal_position_id.id not in config.fiscal_position_ids.ids: + config.fiscal_position_ids = [(4, config.default_fiscal_position_id.id)] + elif not config.tax_regime_selection and not config.tax_regime and config.fiscal_position_ids.ids: + config.fiscal_position_ids = [(5, 0, 0)] + + def _check_modules_to_install(self): + module_installed = False + for pos_config in self: + for field_name in [f for f in pos_config.fields_get_keys() if f.startswith('module_')]: + module_name = field_name.split('module_')[1] + module_to_install = self.env['ir.module.module'].sudo().search([('name', '=', module_name)]) + if getattr(pos_config, field_name) and module_to_install.state not in ('installed', 'to install', 'to upgrade'): + module_to_install.button_immediate_install() + module_installed = True + # just in case we want to do something if we install a module. (like a refresh ...) + return module_installed + + def execute(self): + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + 'params': {'wait': True} + } + # Methods to open the POS @api.multi def open_ui(self): diff --git a/addons/point_of_sale/models/pos_config_settings.py b/addons/point_of_sale/models/pos_config_settings.py deleted file mode 100644 index db36ef0ddd6..00000000000 --- a/addons/point_of_sale/models/pos_config_settings.py +++ /dev/null @@ -1,72 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models - - -class PosConfigSettings(models.TransientModel): - _inherit = 'res.config.settings' - _name = 'pos.config.settings' - - group_multi_currency = fields.Boolean("Multi-Currencies", implied_group='base.group_multi_currency') - group_product_variant = fields.Boolean("Attributes & Variants", implied_group='product.group_product_variant') - group_product_pricelist = fields.Boolean("Show pricelists On Products", - implied_group='product.group_product_pricelist') - group_pricelist_item = fields.Boolean("Show pricelists to customers", - implied_group='product.group_pricelist_item') - group_sale_pricelist = fields.Boolean("Use pricelists to adapt your price per customers", - implied_group='product.group_sale_pricelist', - help="""Allows to manage different prices based on rules per category of customers. - Example: 10% for retailers, promotion of 5 EUR on this product, etc.""") - use_pos_sale_price = fields.Boolean("A single sale price per product", oldname='default_sale_price') - pos_pricelist_setting = fields.Selection([ - ('percentage', 'Multiple prices per product (e.g. quantity, shop-specific)'), - ('formula', 'Price computed from formulas (discounts, margins, rounding)') - ], string="Multiple Prices per Products", - oldname='default_pricelist_setting') - module_pos_restaurant = fields.Boolean(string="Restaurant") - module_pos_loyalty = fields.Boolean(string="Loyalty Program") - module_pos_discount = fields.Boolean(string="Global Discounts") - module_pos_mercury = fields.Boolean(string="Credit Cards") - module_pos_reprint = fields.Boolean(string="Reprint Receipt") - module_pos_data_drinks = fields.Boolean(string="Import common drinks data") - - @api.onchange('use_pos_sale_price') - def _onchange_fix_sale_price(self): - if not self.use_pos_sale_price: - self.pos_pricelist_setting = False - if self.use_pos_sale_price and not self.pos_pricelist_setting: - self.pos_pricelist_setting = 'percentage' - - @api.onchange('pos_pricelist_setting') - def _onchange_sale_price(self): - if self.pos_pricelist_setting == 'percentage': - self.update({ - 'group_product_pricelist': True, - 'group_sale_pricelist': True, - 'group_pricelist_item': False, - }) - elif self.pos_pricelist_setting == 'formula': - self.update({ - 'group_product_pricelist': False, - 'group_sale_pricelist': True, - 'group_pricelist_item': True, - }) - else: - self.update({ - 'group_product_pricelist': False, - 'group_sale_pricelist': False, - 'group_pricelist_item': False, - }) - - def get_values(self): - res = super(PosConfigSettings, self).get_values() - res.update( - use_pos_sale_price=self.env['ir.config_parameter'].sudo().get_param('pos.use_pos_sale_price'), - pos_pricelist_setting=self.env['ir.config_parameter'].sudo().get_param('pos.pos_pricelist_setting'), - ) - return res - - def set_values(self): - super(PosConfigSettings, self).set_values() - self.env['ir.config_parameter'].sudo().set_param('pos.use_pos_sale_price', self.use_pos_sale_price) - self.env['ir.config_parameter'].sudo().set_param('pos.pos_pricelist_setting', self.pos_pricelist_setting) diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index dfd4b5b9b29..9d06aeca317 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -1442,7 +1442,7 @@ exports.Orderline = Backbone.Model.extend({ return parseFloat(round_di(this.price || 0, digits).toFixed(digits)); }, get_unit_display_price: function(){ - if (this.pos.config.iface_tax_included) { + if (this.pos.config.iface_tax_included === 'total') { var quantity = this.quantity; this.quantity = 1.0; var price = this.get_all_prices().priceWithTax; @@ -1457,7 +1457,7 @@ exports.Orderline = Backbone.Model.extend({ return round_pr(this.get_unit_price() * this.get_quantity() * (1 - this.get_discount()/100), rounding); }, get_display_price: function(){ - if (this.pos.config.iface_tax_included) { + if (this.pos.config.iface_tax_included === 'total') { return this.get_price_with_tax(); } else { return this.get_base_price(); diff --git a/addons/point_of_sale/views/account_journal_view.xml b/addons/point_of_sale/views/account_journal_view.xml index a4d69f7f64a..7255a81cf0a 100644 --- a/addons/point_of_sale/views/account_journal_view.xml +++ b/addons/point_of_sale/views/account_journal_view.xml @@ -20,7 +20,29 @@ + action="action_account_journal_form" + parent="point_of_sale.menu_point_config_product" + groups="base.group_no_one" + sequence="30"/> + + account.journal.filter + account.journal + + + + + + + + + + POS Journals + ir.actions.act_window + account.journal + tree,form,kanban + + [('journal_user', '=', True ), ('type', 'in', ['bank', 'cash'])] + {'search_default_available_in_pos': 1, 'default_type': 'cash'} + + diff --git a/addons/point_of_sale/views/point_of_sale_view.xml b/addons/point_of_sale/views/point_of_sale_view.xml index 9d12f0bce7c..dd4d48c6ef8 100644 --- a/addons/point_of_sale/views/point_of_sale_view.xml +++ b/addons/point_of_sale/views/point_of_sale_view.xml @@ -28,9 +28,4 @@ sequence="100" groups="group_pos_manager"/> - - diff --git a/addons/point_of_sale/views/pos_config_settings_views.xml b/addons/point_of_sale/views/pos_config_settings_views.xml deleted file mode 100644 index 8edc687273a..00000000000 --- a/addons/point_of_sale/views/pos_config_settings_views.xml +++ /dev/null @@ -1,170 +0,0 @@ - - - - pos.config.settings.form - pos.config.settings - -
-
-
- -

Point of Sale

-
-
-
- -
-
-
-
-
-

Pricing

-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-

Product Catalog

-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-

Payments

-
-
-
- -
-
-
-
-
-
- -
-
-
-
-
-
-
-
-
- - Configure Point of Sale - pos.config.settings - form - inline - - - -
diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml index 31c7e0526dc..9dca1ea3e8c 100644 --- a/addons/point_of_sale/views/pos_config_view.xml +++ b/addons/point_of_sale/views/pos_config_view.xml @@ -1,87 +1,480 @@ - + + + + + account.cashbox.line.tree + account.cashbox.line + + + + + + + + + + + Opening/Closing Values + ir.actions.act_window + account.cashbox.line + form + tree,form + + + + pos.config.form.view pos.config
- @@ -129,9 +522,11 @@ + groups="group_pos_manager"/> Point of Sale @@ -149,12 +544,6 @@

- - -
diff --git a/addons/pos_cache/views/pos_cache_views.xml b/addons/pos_cache/views/pos_cache_views.xml index 9b4454c45d7..553c1df1f84 100644 --- a/addons/pos_cache/views/pos_cache_views.xml +++ b/addons/pos_cache/views/pos_cache_views.xml @@ -1,10 +1,10 @@ - - pos.config.form.view + + pos.config.form.inherit.pos.cache pos.config - + diff --git a/addons/pos_discount/models/pos_config.py b/addons/pos_discount/models/pos_config.py index 6ff0c325157..0c21d482f17 100644 --- a/addons/pos_discount/models/pos_config.py +++ b/addons/pos_discount/models/pos_config.py @@ -1,12 +1,24 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models +from odoo import api, fields, models class PosConfig(models.Model): _inherit = 'pos.config' + def _get_default_discount_product(self): + return self.env.ref('point_of_sale.product_product_consumable') + iface_discount = fields.Boolean(string='Order Discounts', help='Allow the cashier to give discounts on the whole order.') discount_pc = fields.Float(string='Discount Percentage', default=10, help='The default discount percentage') - discount_product_id = fields.Many2one('product.product', string='Discount Product', domain="[('available_in_pos', '=', True)]", help='The product used to model the discount') + discount_product_id = fields.Many2one('product.product', string='Discount Product', domain="[('available_in_pos', '=', True)]", help='The product used to model the discount.', default=_get_default_discount_product) + + @api.onchange('module_pos_discount') + def _onchange_module_pos_discount(self): + if self.module_pos_discount: + self.discount_product_id = self.env['product.product'].search([('available_in_pos', '=', True)], limit=1) + self.discount_pc = 10.0 + else: + self.discount_product_id = False + self.discount_pc = 0.0 diff --git a/addons/pos_discount/static/src/js/discount.js b/addons/pos_discount/static/src/js/discount.js index 497573f5107..eadad806fd1 100644 --- a/addons/pos_discount/static/src/js/discount.js +++ b/addons/pos_discount/static/src/js/discount.js @@ -44,7 +44,7 @@ screens.define_action_button({ 'name': 'discount', 'widget': DiscountButton, 'condition': function(){ - return this.pos.config.iface_discount && this.pos.config.discount_product_id; + return this.pos.config.module_pos_discount && this.pos.config.discount_product_id; }, }); diff --git a/addons/pos_discount/views/pos_discount_views.xml b/addons/pos_discount/views/pos_discount_views.xml index d8141bdb9a0..e5e8c2a2704 100644 --- a/addons/pos_discount/views/pos_discount_views.xml +++ b/addons/pos_discount/views/pos_discount_views.xml @@ -1,18 +1,20 @@ - - pos.config.form.inherit + + pos.config.form.inherit.pos.discount pos.config - + - - - - - +
+
+
+
+
+
diff --git a/addons/pos_mercury/views/pos_config_setting_views.xml b/addons/pos_mercury/views/pos_config_setting_views.xml index 4d9677344a9..6a2331d0079 100644 --- a/addons/pos_mercury/views/pos_config_setting_views.xml +++ b/addons/pos_mercury/views/pos_config_setting_views.xml @@ -1,13 +1,16 @@ - - pos.config.settings.form.inherit.mercury - pos.config.settings - + + pos.config.form.inherit.mercury + pos.config + -
-
-
+
diff --git a/addons/pos_mercury/views/pos_mercury_views.xml b/addons/pos_mercury/views/pos_mercury_views.xml index a201a02070f..d9f668b6c0d 100644 --- a/addons/pos_mercury/views/pos_mercury_views.xml +++ b/addons/pos_mercury/views/pos_mercury_views.xml @@ -104,6 +104,6 @@
- +
diff --git a/addons/pos_reprint/__init__.py b/addons/pos_reprint/__init__.py index be9f4fab00a..67dee8c60db 100644 --- a/addons/pos_reprint/__init__.py +++ b/addons/pos_reprint/__init__.py @@ -1,3 +1,2 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import models diff --git a/addons/pos_reprint/__manifest__.py b/addons/pos_reprint/__manifest__.py index 2f79db26a87..bda15eae0a9 100644 --- a/addons/pos_reprint/__manifest__.py +++ b/addons/pos_reprint/__manifest__.py @@ -15,7 +15,7 @@ Allow cashier to reprint receipts 'depends': ['point_of_sale'], 'data': [ 'views/pos_reprint_templates.xml', - 'views/pos_config_views.xml', + 'views/pos_config_views.xml' ], 'qweb': [ 'static/src/xml/reprint.xml', diff --git a/addons/pos_reprint/models/__init__.py b/addons/pos_reprint/models/__init__.py deleted file mode 100644 index eaa81f34b8a..00000000000 --- a/addons/pos_reprint/models/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import pos_config diff --git a/addons/pos_reprint/models/pos_config.py b/addons/pos_reprint/models/pos_config.py deleted file mode 100644 index 0cbda70075a..00000000000 --- a/addons/pos_reprint/models/pos_config.py +++ /dev/null @@ -1,10 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import api, fields, models - -class PosConfig(models.Model): - _inherit = 'pos.config' - - iface_reprint = fields.Boolean( - string='Receipt Reprinting', help="This allows you to reprint a previously printed receipt.") diff --git a/addons/pos_reprint/static/src/js/reprint.js b/addons/pos_reprint/static/src/js/reprint.js index 532f45f6bbe..f133cf4a2d2 100644 --- a/addons/pos_reprint/static/src/js/reprint.js +++ b/addons/pos_reprint/static/src/js/reprint.js @@ -45,8 +45,8 @@ var ReprintButton = screens.ActionButtonWidget.extend({ screens.define_action_button({ 'name': 'reprint', 'widget': ReprintButton, - 'condition': function() { - return this.pos.config.iface_reprint; + 'condition': function(){ + return this.pos.config.iface_print_via_proxy; }, }); diff --git a/addons/pos_reprint/views/pos_config_views.xml b/addons/pos_reprint/views/pos_config_views.xml index 65c61ec3d53..d5861bf3eb4 100644 --- a/addons/pos_reprint/views/pos_config_views.xml +++ b/addons/pos_reprint/views/pos_config_views.xml @@ -1,13 +1,11 @@ - - pos.config.form.view - pos.config - - - - - - - + + pos.config.form.inherit.reprint + pos.config + + +
+ + diff --git a/addons/pos_restaurant/__manifest__.py b/addons/pos_restaurant/__manifest__.py index 1ab693367ba..9db1a3c50bc 100644 --- a/addons/pos_restaurant/__manifest__.py +++ b/addons/pos_restaurant/__manifest__.py @@ -20,9 +20,9 @@ This module adds several restaurant features to the Point of Sale: 'website': 'https://www.odoo.com/page/point-of-sale', 'data': [ 'security/ir.model.access.csv', - 'views/pos_config_views.xml', 'views/pos_order_views.xml', 'views/pos_restaurant_views.xml', + 'views/pos_config_views.xml', 'views/pos_restaurant_templates.xml', ], 'qweb': [ diff --git a/addons/pos_restaurant/models/pos_config.py b/addons/pos_restaurant/models/pos_config.py index 2a0312b3fdb..8a396f9ff01 100644 --- a/addons/pos_restaurant/models/pos_config.py +++ b/addons/pos_restaurant/models/pos_config.py @@ -1,14 +1,44 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import fields, models +from odoo import api, fields, models class PosConfig(models.Model): _inherit = 'pos.config' - iface_splitbill = fields.Boolean(string='Bill Splitting', help='Enables Bill Splitting in the Point of Sale') - iface_printbill = fields.Boolean(string='Bill Printing', help='Allows to print the Bill before payment') - iface_orderline_notes = fields.Boolean(string='Orderline Notes', help='Allow custom notes on Orderlines') - floor_ids = fields.One2many('restaurant.floor', 'pos_config_id', string='Restaurant Floors', help='The restaurant floors served by this point of sale') + iface_splitbill = fields.Boolean(string='Bill Splitting', help='Enables Bill Splitting in the Point of Sale.') + iface_printbill = fields.Boolean(string='Bill Printing', help='Allows to print the Bill before payment.') + iface_orderline_notes = fields.Boolean(string='Orderline Notes', help='Allow custom notes on Orderlines.') + floor_ids = fields.One2many('restaurant.floor', 'pos_config_id', string='Restaurant Floors', help='The restaurant floors served by this point of sale.') printer_ids = fields.Many2many('restaurant.printer', 'pos_config_printer_rel', 'config_id', 'printer_id', string='Order Printers') + is_table_management = fields.Boolean('Table Management') + is_order_printer = fields.Boolean('Order Printers') + module_pos_restaurant = fields.Boolean(default=True) + + @api.onchange('iface_tipproduct') + def _onchange_tipproduct(self): + if self.iface_tipproduct: + self.tip_product_id = self.env.ref('point_of_sale.product_product_tip', False) + else: + self.tip_product_id = False + + @api.onchange('module_pos_restaurant') + def _onchange_module_pos_restaurant(self): + if not self.module_pos_restaurant: + self.update({'iface_printbill': False, + 'iface_splitbill': False, + 'iface_tipproduct': False, + 'is_order_printer': False, + 'is_table_management': False, + 'iface_orderline_notes': False}) + + @api.onchange('is_table_management') + def _onchange_is_table_management(self): + if not self.is_table_management: + self.floor_ids = [(5, 0, 0)] + + @api.onchange('is_order_printer') + def _onchange_is_order_printer(self): + if not self.is_order_printer: + self.printer_ids = [(5, 0, 0)] diff --git a/addons/pos_restaurant/views/pos_config_views.xml b/addons/pos_restaurant/views/pos_config_views.xml index 5837e89364d..be94d8fd3ff 100644 --- a/addons/pos_restaurant/views/pos_config_views.xml +++ b/addons/pos_restaurant/views/pos_config_views.xml @@ -1,20 +1,114 @@ - - pos.config.form.view.inherit + + pos.config.form.inherit.restaurant pos.config - + - - - - - - - - - +
+ + + + +
+
+
+ +
+
+
+
+
+
+ +
+
+
+
+
diff --git a/addons/pos_restaurant/views/pos_restaurant_views.xml b/addons/pos_restaurant/views/pos_restaurant_views.xml index 7f2719f3234..fa68041285e 100644 --- a/addons/pos_restaurant/views/pos_restaurant_views.xml +++ b/addons/pos_restaurant/views/pos_restaurant_views.xml @@ -94,10 +94,10 @@ + parent="point_of_sale.menu_point_config_product" + action="action_restaurant_floor_form" + sequence="10" + groups="base.group_no_one"/> @@ -149,9 +149,9 @@ + parent="point_of_sale.menu_point_config_product" + action="action_restaurant_printer_form" + sequence="15" + groups="base.group_no_one"/>
diff --git a/addons/pos_sale/views/pos_config_views.xml b/addons/pos_sale/views/pos_config_views.xml index c4cc9d65264..caa000eacd9 100644 --- a/addons/pos_sale/views/pos_config_views.xml +++ b/addons/pos_sale/views/pos_config_views.xml @@ -1,16 +1,21 @@ - - pos.config.form + + pos.config.form.pos.sale pos.config - + - - - - - +
+
+
+
+
+
+
+
diff --git a/addons/web/static/src/less/form_view.less b/addons/web/static/src/less/form_view.less index bcea630b152..28909ed87e4 100644 --- a/addons/web/static/src/less/form_view.less +++ b/addons/web/static/src/less/form_view.less @@ -647,6 +647,9 @@ label.o_light_label, .o_light_label label { button.btn-link { padding: 0; } + a.oe-link { + font-size: 12px; + } } } }