[IMP] point_of_sale: Add new settings form per POS

Purpose: Replace POS APP settings with a clean POS Form settings,
         one per POS

    - add state button for (active/inactive) archive/unarchive and remove boolean field.
    - remove 'Reprint Receipt' field from pos_reprint module and related code.
    - remove 'Multi-currencies' field and related code.
    - put 'Company' field under the 'Taxes' block and 'Sales Channel' under the 'Pricing' block.
    - add 'save' buttons to install needed modules manually.
    - removed constrains and set default fiscal value on fiscal position ids even if that not selected.
    - change the skip Receipt Screen label to a more appropriate one.
    - Tax help only appears when Tax-included is selected.

    * [IMP] point_of_sale: Removing multiple prices prod

    Purpose: The choice of the method for "Multiple price per product" was
    not appropriate for the Point Of Sale. Only the choice of the Pricelist
    make sense. So, only the Pricelist can be choosen in the POS config.

    * [IMP] point_of_sale, pos_discount, pos_restaurant:
            Generic module install

    Purpose: Before this improvement, some fields were used in pos.config to
             automatically install some modules.
             This fix tries to mimic the ResConfigSettings transient so that
             when a field starts with 'module_', it's automaticaly
             installed.
This commit is contained in:
Mansi Gandhi
2017-08-07 14:24:45 +02:00
committed by Christophe Monniez
parent bbcebcc123
commit 80e89c7a11
28 changed files with 877 additions and 471 deletions
-1
View File
@@ -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',
@@ -24,6 +24,11 @@
<field name="tag">reload</field>
<field name="params" eval="{'menu_id': ref('menu_point_root')}"/>
</record>
<record id="product_product_tip" model="product.product">
<field name="name">Tips</field>
</record>
<record model="pos.config" id="pos_config_main">
<field name="name">Main</field>
<field name="picking_type_id" ref="picking_type_posout"/>
-1
View File
@@ -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
+152 -45
View File
@@ -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):
@@ -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)
+2 -2
View File
@@ -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();
@@ -20,7 +20,29 @@
</record>
<menuitem id="menu_action_account_journal_form_open"
action="action_account_journal_form"
parent="point_of_sale.pos_config_menu_configuration"
sequence="30"/>
action="action_account_journal_form"
parent="point_of_sale.menu_point_config_product"
groups="base.group_no_one"
sequence="30"/>
<record id="view_account_journal_search_inherit_point_of_sale" model="ir.ui.view">
<field name="name">account.journal.filter</field>
<field name="model">account.journal</field>
<field name="inherit_id" ref="account.view_account_journal_search" />
<field name="arch" type="xml">
<field name="name" position="after">
<filter name="available_in_pos" string="Available for Point of Sale" domain="[('journal_user', '=', True)]"/>
</field>
</field>
</record>
<record id="account_journal_action_point_of_sale" model="ir.actions.act_window">
<field name="name">POS Journals</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.journal</field>
<field name="view_mode">tree,form,kanban</field>
<field name="search_view_id" ref="view_account_journal_search_inherit_point_of_sale"/>
<field name="domain">[('journal_user', '=', True ), ('type', 'in', ['bank', 'cash'])]</field>
<field name="context">{'search_default_available_in_pos': 1, 'default_type': 'cash'}</field>
</record>
</odoo>
@@ -28,9 +28,4 @@
sequence="100"
groups="group_pos_manager"/>
<menuitem id="pos_config_menu_configuration"
name="Point of Sale"
parent="menu_point_config_product"
sequence="10"/>
</odoo>
@@ -1,170 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="pos_config_settings_view_form" model="ir.ui.view">
<field name="name">pos.config.settings.form</field>
<field name="model">pos.config.settings</field>
<field name="arch" type="xml">
<form class="oe_form_configuration">
<header>
<button string="Save" type="object" name="execute" class="oe_highlight"/>
<button string="Cancel" type="object" name="cancel" class="oe_link"/>
</header>
<sheet>
<h2>Point of Sale</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" title="Restaurants benefit from additional features. Bill Printing allows you to print a receipt before the order is paid. Bill Splitting allows you to split an order into different orders. Kitchen Order Printing allows you to print order updates to kitchen or bar printers.">
<div class="o_setting_left_pane">
<field name="module_pos_restaurant"/>
</div>
<div class="o_setting_right_pane">
<label string="Restaurant"/>
<div class="text-muted">
Add table management, tips, bill printing &amp; split, etc.
</div>
</div>
</div>
</div>
<h2>Pricing</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" title="Allows the cashier to quickly give a percentage sale discount for all the sales order to a customer.">
<div class="o_setting_left_pane">
<field name="module_pos_discount"/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_discount"/>
<div class="text-muted">
Allow global discounts on orders
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" title="Allows you to define a loyalty program in the point of sale, where the customers earn loyalty points and get rewards.">
<div class="o_setting_left_pane">
<field name="module_pos_loyalty" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_loyalty"/>
<div class="text-muted">
Give customer rewards, free samples, etc.
</div>
<div class="content-group">
<div class="text-warning mt16 mb4" id="msg_loyalty_program" attrs="{'invisible': [('module_pos_loyalty', '=', False)]}">
Save this page and come back here to set up this feature.
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" title="There are two ways to manage pricelists: 1) Multiple prices per product (e.g. quantity, shop-specific) : must be set in the Sales tab of the product detail form. 2) Price computed from formulas (discounts, margins, rounding) : must be set in the pricelist form.">
<div class="o_setting_left_pane">
<field name="use_pos_sale_price"/>
</div>
<div class="o_setting_right_pane">
<label for="pos_pricelist_setting"/>
<div class="text-muted">
Set shop-specific prices, seasonal discounts, etc.
</div>
<div class="content-group" attrs="{'invisible': [('use_pos_sale_price','=',False)]}">
<div class="mt16">
<field name="group_product_pricelist" invisible="1"/>
<field name="group_sale_pricelist" invisible="1"/>
<field name="group_pricelist_item" invisible="1"/>
<field name="pos_pricelist_setting" widget="radio" class="o_light_label"/>
</div>
<div>
<button name="%(product.product_pricelist_action2)d" icon="fa-arrow-right" type="action"
string="Pricelists" attrs="{'invisible': [('pos_pricelist_setting', '=', False)]}" class="btn-link"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_multi_currency"/>
</div>
<div class="o_setting_right_pane">
<label for="group_multi_currency"/>
<div class="text-muted">
Sell in several currencies
</div>
<div class="content-group" attrs="{'invisible': [('group_multi_currency','=',False)]}">
<div class="mt16">
<button name="%(base.action_currency_all_form)d" icon="fa-arrow-right" type="action" string="Activate Other Currencies" class="btn-link"/>
</div>
<div>
<button name="%(account.action_account_config)d" icon="fa-arrow-right" type="action" string="Advanced Currencies Setup" class="btn-link"/>
</div>
</div>
</div>
</div>
</div>
<h2>Product Catalog</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="group_product_variant"/>
</div>
<div class="o_setting_right_pane">
<label for="group_product_variant"/>
<div class="text-muted">
Set product attributes (e.g. color, size) to sell variants
</div>
<div class="content-group mt16" attrs="{'invisible': [('group_product_variant','=',False)]}">
<button name="%(product.attribute_action)d" icon="fa-arrow-right" type="action" string="Attributes" class="btn-link"/>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_pos_data_drinks"/>
</div>
<div class="o_setting_right_pane">
<label string="Drinks" for="module_pos_data_drinks"/>
<div class="text-muted">
Get a predefined set of common drinks
</div>
</div>
</div>
</div>
<h2>Payments</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" title="The transactions are processed by MercuryPay.">
<div class="o_setting_left_pane">
<field name="module_pos_mercury"/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_mercury"/>
<div class="text-muted">
Accept payments with an integrated card reader
</div>
<div class="content-group">
<div class="text-warning mt16 mb4" id="msg_pos_mercury" attrs="{'invisible': [('module_pos_mercury', '=', False)]}">
Save this page and come back here to set up this feature.
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_pos_reprint"/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_reprint"/>
<div class="text-muted">
Allow cashier to reprint receipts
</div>
</div>
</div>
</div>
</sheet>
</form>
</field>
</record>
<record id="action_pos_configuration" model="ir.actions.act_window">
<field name="name">Configure Point of Sale</field>
<field name="res_model">pos.config.settings</field>
<field name="view_mode">form</field>
<field name="target">inline</field>
</record>
<menuitem id="menu_pos_global_settings" name="Settings"
parent="menu_point_config_product" sequence="1" action="action_pos_configuration" groups="base.group_system"/>
</odoo>
+474 -85
View File
@@ -1,87 +1,480 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_pos_config_form" model="ir.ui.view">
<!--Account cashbox line-->
<record id="account_cashbox_line_view_tree" model="ir.ui.view">
<field name="name">account.cashbox.line.tree</field>
<field name="model">account.cashbox.line</field>
<field name="arch" type="xml">
<tree string="Cashbox balance" editable="top">
<field name="coin_value"/>
<field name="number"/>
<field name="subtotal"/>
</tree>
</field>
</record>
<record id="account_cashbox_line_action" model="ir.actions.act_window">
<field name="name">Opening/Closing Values</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.cashbox.line</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="account_cashbox_line_view_tree"/>
</record>
<record id="pos_config_view_form" model="ir.ui.view">
<field name="name">pos.config.form.view</field>
<field name="model">pos.config</field>
<field name="arch" type="xml">
<form string="Point of Sale Configuration">
<sheet>
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
<group name="pos_config_main_options" col="4">
<field name="picking_type_id" widget="selection" groups="stock.group_stock_multi_locations" />
<field name="stock_location_id" groups="stock.group_stock_multi_locations"/>
<field name="company_id" groups="base.group_multi_company"/>
<field name="pricelist_id" groups="product.group_sale_pricelist"/>
<field name="journal_id" widget="selection"/>
<field name="invoice_journal_id" widget="selection"/>
<field name="group_by" groups="account.group_account_user"/>
<field name="barcode_nomenclature_id" />
<field name="sequence_id" readonly="1" groups="base.group_no_one"/>
<field name="fiscal_position_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="default_fiscal_position_id" options="{'no_create': True}"/>
<field name="active"/>
<field name="currency_id" invisible="1"/>
</group>
<separator string="Available Payment Methods" colspan="4"/>
<field name="journal_ids" colspan="4" nolabel="1" context="{'default_journal_user': 1}">
<tree string="Journals">
<field name="code" />
<field name="name" />
<field name="type" />
</tree>
</field>
<group name="features" string="Features" >
<group>
<field name="iface_vkeyboard" />
<field name="iface_invoicing" />
<field name="iface_precompute_cash" />
<field name="iface_start_categ_id" />
<field name="tip_product_id" />
<field name="iface_tax_included" />
<field name="restrict_price_control" />
</group>
<group>
<field name="iface_big_scrollbars" />
<field name="iface_display_categ_images" />
</group>
</group>
<group string="Hardware Proxy / PosBox" >
<field name="proxy_ip" />
<field name="iface_scan_via_proxy" string="Barcode Scanner"/>
<field name="iface_electronic_scale" />
<field name="iface_cashdrawer" />
<field name="iface_print_via_proxy" string="Receipt Printer"/>
<field name="iface_customer_facing_display" />
</group>
<group attrs="{'invisible' : [('iface_customer_facing_display', '=', False)]}" colspan="4">
<field name="customer_facing_display_html"
widget="html_frame"
nolabel="1"
options="{'editor_url': '/point_of_sale/field/customer_facing_display_template'}"/>
</group>
<group name="receipt" string="Receipt">
<group>
<field name="iface_print_auto"/>
</group>
<group>
<field name="iface_print_skip_screen" attrs="{'invisible' : [('iface_print_auto', '=', False)]}"/>
</group>
<field name="receipt_header" string="Header" placeholder="A custom receipt header message"/>
<field name="receipt_footer" string="Footer" placeholder="A custom receipt footer message"/>
</group>
<group string="Cash Control">
<field name="cash_control"/>
<field name="default_cashbox_lines_ids" attrs="{'invisible': [('cash_control', '=', False)]}">
<tree string="Cashbox" editable="bottom">
<field name="coin_value"/>
<field name="number"/>
<field name="subtotal"/>
</tree>
</field>
</group>
<field name="currency_id" invisible="1"/>
<field name="is_installed_account_accountant" invisible="1"/>
<div class="oe_button_box" name="button_box">
<button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
<field name="active" widget="boolean_button" options='{"terminology": "archive"}'/>
</button>
</div>
<div class="oe_title" id="title">
<label for="name" class="oe_edit_only"/>
<h1><field name="name"/></h1>
</div>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_pos_restaurant"/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_restaurant"/>
<button string="Save" type="object" name="execute" class="oe_highlight" id="btn_use_pos_restaurant" attrs="{'invisible': [('module_pos_restaurant','=',False)]}" />
</div>
</div>
</div>
<div class="row mt16" id="company" groups="base.group_multi_company">
<label for="company_id" class="col-md-2"/>
<field name="company_id"/>
</div>
<h2 name="order">Order Interface</h2>
<div class="row mt16 o_settings_container">
<div id="category_reference" class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="iface_display_categ_images"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_display_categ_images" string="Category Pictures"/>
<div class="text-muted">
Display pictures of product categories
</div>
<div class="content-group mt16" attrs="{'invisible': [('iface_display_categ_images', '=', False)]}">
<button name="%(product_pos_category_action)d" icon="fa-arrow-right" type="action" string="Categories" class="btn-link"/>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" >
<div class="o_setting_left_pane">
<field name="start_category"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_start_categ_id"/>
<div class="text-muted">
Start selling from a default product category
</div>
<div class="content-group mt16" attrs="{'invisible': [('start_category', '=', False)]}">
<field name="iface_start_categ_id"/>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" title="Don’t turn this option on if you take orders on smartphones or tablets. Such devices already benefit from a native keyboard.">
<div class="o_setting_left_pane">
<field name="iface_vkeyboard"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_vkeyboard"/>
<div class="text-muted">
Use a virtual keyboard for touchscreens
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" >
<div class="o_setting_left_pane">
<field name="iface_big_scrollbars"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_big_scrollbars"/>
<div class="text-muted">
Improve navigation for imprecise industrial touchscreens
</div>
</div>
</div>
</div>
<h2>PosBox / Hardware Proxy</h2>
<div class="row mt16 o_settings_container" id="posbox_reference">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="is_posbox"/>
</div>
<div class="o_setting_right_pane">
<label for="is_posbox"/>
<div class="text-muted">
Use an integrated hardware setup like <a href="https://www.odoo.com/documentation/user/online/point_of_sale/overview/setup.html" target="_blank">PosBox</a>
</div>
<div class="content-group" attrs="{'invisible' : [('is_posbox', '=', False)]}">
<div class="row mt16">
<label string="IP Address" for="proxy_ip" class="col-md-3 o_light_label"/>
<field name="proxy_ip"/>
</div>
<div class="row">
<label string="Barcode Scanner" for="iface_scan_via_proxy" class="col-md-3 o_light_label"/>
<field name="iface_scan_via_proxy"/>
</div>
<div class="row">
<label string="Electronic Scale" for="iface_electronic_scale" class="col-md-3 o_light_label"/>
<field name="iface_electronic_scale"/>
</div>
<div class="row">
<label string="Cashdrawer" for="iface_cashdrawer" class="col-md-3 o_light_label"/>
<field name="iface_cashdrawer"/>
</div>
<div class="row">
<label string="Receipt Printer" for="iface_print_via_proxy" class="col-md-3 o_light_label"/>
<field name="iface_print_via_proxy"/>
</div>
<div class="row">
<label string="Customer Display" for="iface_customer_facing_display" class="col-md-3 o_light_label"/>
<field name="iface_customer_facing_display"/>
</div>
</div>
</div>
</div>
<div id="barcode_scanner" class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="barcode_scanner"/>
</div>
<div class="o_setting_right_pane">
<label for="barcode_nomenclature_id" string="Barcode Scanner"/>
<div class="text-muted">
Set barcodes to scan products, customer cards, etc.
</div>
<div class="content-group row mt16" attrs="{'invisible': [('barcode_scanner', '=', False)]}">
<label for="barcode_nomenclature_id" string="Barcode Nomenclature" class="col-md-3 o_light_label"/>
<field name="barcode_nomenclature_id" attrs="{'required': [('barcode_scanner', '=', True)]}"/>
</div>
</div>
</div>
</div>
<h2>Taxes</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" title="Choose a specific tax regime at the order depending on the kind of customer (tax exempt, onsite vs. takeaway, etc.).">
<div class="o_setting_left_pane">
<field name="tax_regime_selection"/>
</div>
<div class="o_setting_right_pane">
<label for="fiscal_position_ids" string="Fiscal Position per Order"/>
<div class="text-muted">
Choose among several tax regimes when processing an order
</div>
<div class="content-group" attrs="{'invisible': [('tax_regime_selection', '=', False)]}">
<div class="mt16">
<field name="fiscal_position_ids" widget="many2many_tags" options="{'no_create': True}"/>
</div>
<div>
<button name="%(account.action_account_fiscal_position_form)d" icon="fa-arrow-right" type="action" string="Fiscal Positions" class="btn-link"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="tax_regime"/>
</div>
<div class="o_setting_right_pane">
<label for="default_fiscal_position_id" string="Fiscal Position"/>
<div class="text-muted">
Use a default specific tax regime
</div>
<div class="content-group" attrs="{'invisible': [('tax_regime', '=', False)]}">
<div class="mt16">
<field name="default_fiscal_position_id" attrs="{'required': [('tax_regime', '=', True)]}"/>
</div>
</div>
</div>
</div>
</div>
<h2>Pricing</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" id="product_prices">
<div class="o_setting_right_pane">
<label for="iface_tax_included" string="Product Prices"/>
<div class="text-muted">
Product prices on receipts
</div>
<div class="content-group">
<div class="mt16">
<field name="iface_tax_included" class="o_light_label" widget="radio"/>
</div>
<a attrs="{'invisible': [('iface_tax_included', '!=', 'total')]}" href="https://www.odoo.com/documentation/user/10.0/accounting/others/taxes/tax_included.html" target="_blank" class="oe-link"><i class="fa fa-fw fa-arrow-right"/>How to manage tax-included prices</a>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" >
<div class="o_setting_left_pane">
<field name="module_pos_discount"/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_discount"/>
<div class="text-muted">
Allow global discounts on orders
</div>
<div class="content-group " attrs="{'invisible':[('module_pos_discount','=',False)]}">
<div class="mt16" id="btn_use_pos_discount">
<button string="Save" type="object" name="execute" class="oe_highlight"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" id="pricelist_setting" title="Choose a pricelist for the Point Of Sale">
<div class="o_setting_left_pane">
<field name="use_pricelist"/>
</div>
<div class="o_setting_right_pane">
<label for="use_pricelist" string="Pricelist"/>
<div class="text-muted">
Set shop-specific prices, seasonal discounts, etc.
</div>
<div class="content-group mt16" attrs="{'invisible': [('use_pricelist','=',False)], 'required' : [('use_pricelist', '=', True)]}">
<div class="mt16">
<field name="pricelist_id"/>
</div>
<div>
<button name="%(product.product_pricelist_action2)d" icon="fa-arrow-right" type="action" string="Pricelists" class="btn-link"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_pos_loyalty" widget="upgrade_boolean"/>
</div>
<div class="o_setting_right_pane" title="Loyalty program to use for this point of sale. ">
<label for="module_pos_loyalty"/>
<div class="text-muted" id="loyalty_program">
Give customer rewards, free samples, etc.
</div>
<div class="content-group" attrs="{'invisible': [('module_pos_loyalty', '=', False)]}">
<div class="mt16" id="btn_use_pos_loyalty">
<button string="Save" type="object" name="execute" class="oe_highlight"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" title="Only users with Manager access rights for PoS app can modify the product prices on orders.">
<div class="o_setting_left_pane">
<field name="restrict_price_control"/>
</div>
<div class="o_setting_right_pane">
<label for="restrict_price_control" string="Price Control"/>
<div class="text-muted">
Restrict price modification to managers
</div>
</div>
</div>
</div>
<h2>Payments</h2>
<div class="row mt16 o_settings_container">
<div class="col-xs-12 col-md-6 o_setting_box" id="payment_methods">
<div class="o_setting_right_pane">
<label string="Payment Methods"/>
<div class="text-muted">
Payment methods available
</div>
<div class="content-group mt16">
<field name="journal_ids" colspan="4" nolabel="1" context="{'default_journal_user': 1, 'default_type': 'cash'}" widget="many2many_tags"/>
</div>
<div>
<button name="%(account_journal_action_point_of_sale)d" icon="fa-arrow-right" type="action" string="Payment Methods" class="btn-link"/>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="module_pos_mercury"/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_mercury" string="Integrated Card Reader"/>
<div class="text-muted">
Accept payments with an integrated card reader
</div>
<div class="content-group" attrs="{'invisible': [('module_pos_mercury', '=', False)]}">
<div class="mt16" id="btn_use_pos_mercury">
<button string="Save" type="object" name="execute" class="oe_highlight"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="cash_control"/>
</div>
<div class="o_setting_right_pane">
<label for="cash_control"/>
<div class="text-muted">
Control cash box at opening and closing
</div>
<div class="content-group mt16" attrs="{'invisible': [('cash_control', '=', False)]}">
<div class="row mt16">
<label string="Default Opening" for="proxy_ip" class="col-md-3 o_light_label" />
<field name="default_cashbox_lines_ids" widget="many2many_tags" context="{'show_coin_and_value': True}"/>
</div>
<div>
<button type="action" name="%(account_cashbox_line_action)d" string="Opening/Closing Values" class="btn-link" icon="fa-arrow-right"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="iface_precompute_cash"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_precompute_cash"/>
<div class="text-muted">
Prefill amount paid with the exact due amount
</div>
</div>
</div>
</div>
<h2>Bills &amp; Receipts</h2>
<div class="row mt16 o_settings_container" id="receipt">
<div id="order_reference" class="col-xs-12 col-md-6 o_setting_box" groups="base.group_no_one">
<div class="o_setting_right_pane">
<label string="Order Reference"/>
<div class="text-muted">
Generation of your order references
</div>
<div class="content-group mt16">
<field name="sequence_id" readonly="1"/>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_left_pane">
<field name="is_header_or_footer"/>
</div>
<div class="o_setting_right_pane">
<label for="is_header_or_footer"/>
<div class="text-muted">
Add a custom message to header and footer
</div>
<div class="content-group mt16" attrs="{'invisible' : [('is_header_or_footer', '=', False)]}">
<div>
<label string="Header" for="receipt_header" class="col-md-2 o_light_label"/>
<field name="receipt_header" placeholder="e.g. Company Address, Website"/>
</div>
<div>
<label string="Footer" for="receipt_footer" class="col-md-2 o_light_label"/>
<field name="receipt_footer" placeholder="e.g. Return Policy, Thanks for shopping with us!"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" id="auto_printing" attrs="{'invisible': [('is_posbox', '=', False)]}">
<div class="o_setting_left_pane">
<field name="iface_print_auto"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_print_auto"/>
<div class="text-muted">
Print receipts automatically once the payment registered
</div>
<div class="content-group mt16" attrs="{'invisible' : [('iface_print_auto', '=', False)]}">
<div>
<field name="iface_print_skip_screen" class="oe_inline"/><span class="oe_inline"><b>Skip Preview Screen</b></span>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('iface_print_via_proxy','=',False)]}">
<div class="o_setting_left_pane">
<field name='module_pos_reprint'/>
</div>
<div class="o_setting_right_pane">
<label for="module_pos_reprint"/>
<div class="text-muted">
Allow cashier to reprint receipts
</div>
<div class="content-group" attrs="{'invisible': [('module_pos_reprint', '=', False)]}">
<div class="mt16" id="btn_use_pos_reprint">
<button string="Save" type="object" name="execute" class="oe_highlight"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" id="iface_invoicing">
<div class="o_setting_left_pane">
<field name="iface_invoicing"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_invoicing"/>
<div class="text-muted" id="invoice_journal">
Print invoices on customer request
</div>
<div class="content-group mt16" attrs="{'invisible': [('iface_invoicing', '=', False)]}">
<div class="row mt16">
<label string="Invoice Journal" for="invoice_journal_id" class="col-md-3 o_light_label"/>
<field name="invoice_journal_id"/>
</div>
</div>
</div>
</div>
</div>
<h2>Inventory</h2>
<div class="row mt16 o_settings_container" id="inventory_location">
<div class="col-xs-12 col-md-6 o_setting_box" title="Operation types show up in the Inventory dashboard.">
<div class="o_setting_right_pane">
<label for="picking_type_id" string="Operation Type"/>
<div class="text-muted">
Operation type used to record product pickings
</div>
<div class="content-group mt16">
<field name="picking_type_id"/>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" groups="stock.group_stock_multi_locations">
<div class="o_setting_right_pane">
<label for="stock_location_id"/>
<div class="text-muted">
Stock location used for the inventory
</div>
<div>
<field name="stock_location_id"/>
</div>
</div>
</div>
</div>
<h2 attrs="{'invisible': [('is_installed_account_accountant', '=', False)]}">Accounting</h2>
<div class="row mt16 o_settings_container" attrs="{'invisible': [('is_installed_account_accountant', '=', False)]}">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_right_pane">
<label string="Journal Entries"/>
<div class="text-muted">
Configuration for journal entries of PoS orders
</div>
<div class="content-group">
<div class="row mt16" title="Whenever you close a session, one entry is generated in the following accounting journal for all the orders not invoiced. Invoices are recorded in accounting separately.">
<label string="Sales Journal" for="journal_id" class="col-md-3 o_light_label" widget="selection"/>
<field name="journal_id"/>
</div>
<div class="row" groups="account.group_account_user" title="Get one journal item per product rather than one journal item per receipt line. This works for any anonymous order. If the customer is set on the order, one journal item is created for each receipt line. This option is recommended for an easy review of your journal entries when managing lots of orders.">
<label string="Group Journal Items" for="group_by" class="col-md-3 o_light_label"/>
<field name="group_by"/>
</div>
</div>
</div>
</div>
</div>
</sheet>
</form>
@@ -129,9 +522,11 @@
<menuitem
id="menu_pos_config_pos"
parent="point_of_sale.pos_config_menu_configuration"
name="Point of Sale"
parent="menu_point_config_product"
sequence="0"
action="action_pos_config_pos"
groups="group_pos_manager" sequence="5"/>
groups="group_pos_manager"/>
<record id="action_pos_config_kanban" model="ir.actions.act_window">
<field name="name">Point of Sale</field>
@@ -149,12 +544,6 @@
</p>
</field>
</record>
<!-- Barcode Nomenclatures -->
<menuitem id="menu_pos_barcode_nomenclature_all"
parent="point_of_sale.pos_config_menu_configuration"
action="barcodes.action_barcode_nomenclature_form"
sequence="40"/>
<menuitem id="menu_pos_dashboard" action="action_pos_config_kanban" parent="menu_point_root" name="Dashboard" sequence="1"/>
</odoo>
+3 -3
View File
@@ -1,10 +1,10 @@
<?xml version="1.0"?>
<odoo>
<data>
<record model="ir.ui.view" id="view_pos_config_form">
<field name="name">pos.config.form.view</field>
<record id="pos_config_view_form_inherit_pos_cache" model="ir.ui.view">
<field name="name">pos.config.form.inherit.pos.cache</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.view_pos_config_form" />
<field name="inherit_id" ref="point_of_sale.pos_config_view_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='currency_id']" position="after">
<field name='oldest_cache_time'/>
+14 -2
View File
@@ -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
@@ -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;
},
});
@@ -1,18 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record model="ir.ui.view" id="view_pos_config_form">
<field name="name">pos.config.form.inherit</field>
<record id="pos_config_view_form_inherit_pos_discount" model="ir.ui.view">
<field name="name">pos.config.form.inherit.pos.discount</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.view_pos_config_form" />
<field name="inherit_id" ref="point_of_sale.pos_config_view_form" />
<field name="arch" type="xml">
<xpath expr="//group[@name='receipt']" position="after">
<group string="Discounts">
<field name='iface_discount' />
<label string="" colspan="2" />
<field name='discount_product_id' attrs="{'required': [('iface_discount', '=', True)]}" />
<field name='discount_pc' />
</group>
</xpath>
<div id="btn_use_pos_discount" position="replace">
<div class="row mt16">
<label string="Discount Product" for="discount_product_id" class="col-md-3 o_light_label"/>
<field name="discount_product_id" attrs="{'required':[('module_pos_discount','=',True)]}"/>
</div>
<div class="row">
<label string="Discount %" for="discount_pc" class="col-md-3 o_light_label"/>
<field name="discount_pc"/>
</div>
</div>
</field>
</record>
</odoo>
@@ -1,13 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="pos_config_settings_view_form_inherit_pos_mercury" model="ir.ui.view">
<field name="name">pos.config.settings.form.inherit.mercury</field>
<field name="model">pos.config.settings</field>
<field name="inherit_id" ref="point_of_sale.pos_config_settings_view_form"/>
<record id="pos_config_view_form_inherit_pos_mercury" model="ir.ui.view">
<field name="name">pos.config.form.inherit.mercury</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<div id="msg_pos_mercury" position="replace">
<div class="mt16" attrs="{'invisible': [('module_pos_mercury', '=', False)]}">
<button name="%(pos_mercury.action_configuration_form)d" icon="fa-arrow-right" type="action" string="Mercury Configuration" class="btn-link"/>
<div id="btn_use_pos_mercury" position="replace">
<div class="mt16">
<button name="%(pos_mercury.action_configuration_form)d" icon="fa-arrow-right" type="action" string="Vantiv Accounts" class="btn-link"/>
</div>
<div>
<a class="oe_link" href="https://www.odoo.com/page/point-of-sale-hardware#part_8" target="_blank"><i class="fa fa-fw fa-arrow-right"/>Buy a card reader</a>
</div>
</div>
</field>
@@ -104,6 +104,6 @@
</field>
</record>
<menuitem parent="point_of_sale.pos_config_menu_configuration" action="pos_mercury.action_configuration_form" id="menu_pos_pos_mercury_config" sequence="35"/>
<menuitem parent="point_of_sale.menu_point_config_product" action="pos_mercury.action_configuration_form" id="menu_pos_pos_mercury_config" groups="base.group_no_one" sequence="35"/>
</data>
</odoo>
-1
View File
@@ -1,3 +1,2 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+1 -1
View File
@@ -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',
-4
View File
@@ -1,4 +0,0 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import pos_config
-10
View File
@@ -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.")
+2 -2
View File
@@ -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;
},
});
+8 -10
View File
@@ -1,13 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_pos_config_form" model="ir.ui.view" >
<field name="name">pos.config.form.view</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.view_pos_config_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='iface_print_auto']" position="after">
<field string="Reprint Receipt" name='iface_reprint'/>
</xpath>
</field>
</record>
<record id="pos_config_view_form_inherit_pos_reprint" model="ir.ui.view">
<field name="name">pos.config.form.inherit.reprint</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<div id="btn_use_pos_reprint" position="replace"/>
</field>
</record>
</odoo>
+1 -1
View File
@@ -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': [
+35 -5
View File
@@ -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)]
+106 -12
View File
@@ -1,20 +1,114 @@
<?xml version="1.0"?>
<odoo>
<record id="view_pos_config_form" model="ir.ui.view">
<field name="name">pos.config.form.view.inherit</field>
<record id="pos_config_view_form_inherit_restaurant" model="ir.ui.view">
<field name="name">pos.config.form.inherit.restaurant</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.view_pos_config_form"></field>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<sheet position='inside'>
<group string="Bar &amp; Restaurant" >
<field name="iface_splitbill" />
<field name="iface_printbill" />
<field name="iface_orderline_notes" />
<field name="floor_ids" />
<field name="printer_ids" />
</group>
</sheet>
<button id="btn_use_pos_restaurant" position="replace"/>
<div id="iface_invoicing" position="before">
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('module_pos_restaurant', '=', False)]}">
<div class="o_setting_left_pane">
<field name="iface_printbill"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_printbill"/>
<span class="fa fa-lg fa-cutlery" title="For bars and restaurants"/>
<div class="text-muted">
Allow to print bill before payment
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('module_pos_restaurant', '=', False)]}">
<div class="o_setting_left_pane">
<field name="iface_splitbill"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_splitbill"/>
<span class="fa fa-lg fa-cutlery" title="For bars and restaurants"/>
<div class="text-muted">
Split total or order lines
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" title="This product is used as reference on customer receipts." attrs="{'invisible': [('module_pos_restaurant', '=', False)]}">
<div class="o_setting_left_pane">
<field name="iface_tipproduct"/>
</div>
<div class="o_setting_right_pane">
<label for="tip_product_id" string="Tips"/>
<span class="fa fa-lg fa-cutlery" title="For bars and restaurants"/>
<div class="text-muted">
Accept customer tips or convert their change to a tip
</div>
<div class="content-group" attrs="{'invisible': [('iface_tipproduct', '=', False)]}">
<div class="mt16">
<label string="Tip Product" for="tip_product_id" class="o_light_label"/>
<field name="tip_product_id"/>
</div>
</div>
</div>
</div>
</div>
<div id="barcode_scanner" position="after">
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('module_pos_restaurant', '=', False)]}">
<div class="o_setting_left_pane">
<field name="is_order_printer"/>
</div>
<div class="o_setting_right_pane">
<label for="is_order_printer"/>
<span class="fa fa-lg fa-cutlery" title="For bars and restaurants"/>
<div class="text-muted">
Print orders at the kitchen, at the bar, etc.
</div>
<div class="content-group" attrs="{'invisible': [('is_order_printer', '=', False)]}">
<div class="mt16">
<label string="Printers" for="printer_ids" class="o_light_label"/>
<field name="printer_ids" widget="many2many_tags"/>
</div>
<div>
<button name="%(pos_restaurant.action_restaurant_printer_form)d" icon="fa-arrow-right" type="action" string="Printers" class="btn-link"/>
</div>
</div>
</div>
</div>
</div>
<div id="category_reference" position="before">
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('module_pos_restaurant', '=', False)]}">
<div class="o_setting_left_pane">
<field name="is_table_management"/>
</div>
<div class="o_setting_right_pane">
<label for="is_table_management"/>
<span class="fa fa-lg fa-cutlery" title="For bars and restaurants"/>
<div class="text-muted">
Manage table orders
</div>
<div class="content-group" attrs="{'invisible': [('is_table_management','=',False)]}">
<div class="mt16">
<label string="Floors" for="floor_ids" class="o_light_label"/>
<field name="floor_ids" widget="many2many_tags"/>
</div>
<div>
<button name="%(pos_restaurant.action_restaurant_floor_form)d" icon="fa-arrow-right" type="action" string="Floors" class="btn-link"/>
</div>
</div>
</div>
</div>
<div class="col-xs-12 col-md-6 o_setting_box" attrs="{'invisible': [('module_pos_restaurant', '=', False)]}">
<div class="o_setting_left_pane">
<field name="iface_orderline_notes"/>
</div>
<div class="o_setting_right_pane">
<label for="iface_orderline_notes"/>
<span class="fa fa-lg fa-cutlery" title="For bars and restaurants"/>
<div class="text-muted">
Add notes to orderlines
</div>
</div>
</div>
</div>
</field>
</record>
@@ -94,10 +94,10 @@
</record>
<menuitem id="menu_restaurant_floor_all"
parent="point_of_sale.pos_config_menu_configuration"
action="action_restaurant_floor_form"
sequence="10"
groups="point_of_sale.group_pos_manager"/>
parent="point_of_sale.menu_point_config_product"
action="action_restaurant_floor_form"
sequence="10"
groups="base.group_no_one"/>
<!-- RESTAURANT PRINTERS -->
@@ -149,9 +149,9 @@
</record>
<menuitem id="menu_restaurant_printer_all"
parent="point_of_sale.pos_config_menu_configuration"
action="action_restaurant_printer_form"
sequence="15"
groups="point_of_sale.group_pos_manager"/>
parent="point_of_sale.menu_point_config_product"
action="action_restaurant_printer_form"
sequence="15"
groups="base.group_no_one"/>
</odoo>
+13 -8
View File
@@ -1,16 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="view_pos_config_form_inherit_pos_sale" model="ir.ui.view">
<field name="name">pos.config.form</field>
<record id="pos_config_view_form_pos_sale" model="ir.ui.view">
<field name="name">pos.config.form.pos.sale</field>
<field name="model">pos.config</field>
<field name="inherit_id" ref="point_of_sale.view_pos_config_form"/>
<field name="inherit_id" ref="point_of_sale.pos_config_view_form"/>
<field name="arch" type="xml">
<data>
<xpath expr="//group[@name='pos_config_main_options']" position="inside">
<field name="crm_team_id" options="{'no_create': True}" groups="base.group_no_one"/>
</xpath>
</data>
<div id="company" position="after">
<div class="row mt16 o_settings_container" id="crm_team_id" groups="base.group_no_one">
<div class="col-xs-12 col-md-6 o_setting_box">
<div class="o_setting_right_pane">
<label for="crm_team_id" class="col-md-3"/>
<field name="crm_team_id"/>
</div>
</div>
</div>
</div>
</field>
</record>
@@ -647,6 +647,9 @@ label.o_light_label, .o_light_label label {
button.btn-link {
padding: 0;
}
a.oe-link {
font-size: 12px;
}
}
}
}