[IMP] point_of_sale: allow to choose loaded categs

Before this commit, it was not possible to effectively limit the available
products when a company had several POS with different product offers.

This commit allows limiting the available product categories for a single POS.

Now, a company can serve different POS with different product offerings.

Anecdotal Note: This was especially annoying as the POS only shows 100 products.
If those happened to be the products not served by a signle POS, the only work
around was using the search bar (on a tablet?!?)

closes odoo/odoo#28393
This commit is contained in:
David Arnold
2018-11-26 10:23:07 +00:00
committed by Gert Pellin
parent 9215a40768
commit 30e4d89a62
4 changed files with 64 additions and 20 deletions
+19
View File
@@ -63,6 +63,13 @@ class PosConfig(models.Model):
def _compute_default_customer_html(self):
return self.env['ir.qweb'].render('point_of_sale.customer_facing_display_html')
@api.depends('limit_categories', 'iface_available_categ_ids')
def _compute_iface_start_categ_domain_ids(self):
if self.limit_categories:
self.iface_start_categ_domain_ids = self.iface_available_categ_ids
else:
self.iface_start_categ_domain_ids = self.env['pos.category'].search([])
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(string="Is the Full Accounting Installed",
compute="_compute_is_installed_account_accountant")
@@ -103,6 +110,10 @@ class PosConfig(models.Model):
iface_tax_included = fields.Selection([('subtotal', 'Tax-Excluded Price'), ('total', 'Tax-Included Price')], string="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_available_categ_ids = fields.Many2many('pos.category', string='Available Category Tree',
help='The point of sale will only display products which are within one of the selected category trees. If no category is specified, all available products will be shown')
iface_start_categ_domain_ids = fields.Many2many('pos.category', compute='_compute_iface_start_categ_domain_ids', store=False,
help='Technical field used as a domain for iface_start_categ_id.')
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',
@@ -154,6 +165,7 @@ class PosConfig(models.Model):
tax_regime = fields.Boolean("Tax Regime")
tax_regime_selection = fields.Boolean("Tax Regime Selection value")
start_category = fields.Boolean("Set Start Category")
limit_categories = fields.Boolean("Restrict Available Product Categories")
module_account = fields.Boolean(string='Invoicing', help='Enables invoice generation from the Point of Sale.')
module_pos_restaurant = fields.Boolean("Is a Bar/Restaurant")
module_pos_discount = fields.Boolean("Global Discounts")
@@ -307,6 +319,13 @@ class PosConfig(models.Model):
if not self.start_category:
self.iface_start_categ_id = False
@api.onchange('limit_categories', 'iface_available_categ_ids', 'iface_start_categ_id')
def _onchange_limit_categories(self):
if not self.limit_categories:
self.iface_available_categ_ids = False
elif self.iface_start_categ_id not in self.iface_available_categ_ids:
self.iface_start_categ_id = False
@api.onchange('is_header_or_footer')
def _onchange_header_footer(self):
if not self.is_header_or_footer:
+13 -12
View File
@@ -94,19 +94,20 @@ var PosDB = core.Class.extend({
name : 'Root',
};
}
for(var i=0, len = categories.length; i < len; i++){
this.category_by_id[categories[i].id] = categories[i];
}
len = categories.length;
for(i=0; i < len; i++){
var cat = categories[i];
var parent_id = cat.parent_id[0] || this.root_category_id;
this.category_parent[cat.id] = cat.parent_id[0];
if(!this.category_childs[parent_id]){
this.category_childs[parent_id] = [];
categories.forEach(function(cat){
self.category_by_id[cat.id] = cat;
});
categories.forEach(function(cat){
var parent_id = cat.parent_id[0];
if(!(parent_id && self.category_by_id[parent_id])){
parent_id = self.root_category_id;
}
this.category_childs[parent_id].push(cat.id);
}
self.category_parent[cat.id] = parent_id;
if(!self.category_childs[parent_id]){
self.category_childs[parent_id] = [];
}
self.category_childs[parent_id].push(cat.id);
});
function make_ancestors(cat_id, ancestors){
self.category_ancestors[cat_id] = ancestors;
+16 -7
View File
@@ -355,7 +355,9 @@ exports.PosModel = Backbone.Model.extend({
},{
model: 'pos.category',
fields: ['id', 'name', 'parent_id', 'child_id'],
domain: null,
domain: function(self) {
return self.config.limit_categories ? [['id', 'in', self.config.iface_available_categ_ids]] : [];
},
loaded: function(self, categories){
self.db.add_categories(categories);
},
@@ -365,7 +367,13 @@ exports.PosModel = Backbone.Model.extend({
'barcode', 'default_code', 'to_weight', 'uom_id', 'description_sale', 'description',
'product_tmpl_id','tracking'],
order: _.map(['sequence','default_code','name'], function (name) { return {name: name}; }),
domain: [['sale_ok','=',true],['available_in_pos','=',true]],
domain: function(self){
var domain = [['sale_ok','=',true],['available_in_pos','=',true]];
if (self.config.limit_categories) {
domain.push(['pos_categ_id', 'in', self.config.iface_available_categ_ids]);
}
return domain;
},
context: function(self){ return { display_default_code: false }; },
loaded: function(self, products){
var using_company_currency = self.config.currency_id[0] === self.company.currency_id[0];
@@ -1326,7 +1334,12 @@ exports.Orderline = Backbone.Model.extend({
this.pos = options.pos;
this.order = options.order;
if (options.json) {
this.init_from_JSON(options.json);
try {
this.init_from_JSON(options.json);
} catch(error) {
console.error('ERROR: attempting to recover product ID', json.product_id,
'not available in the point of sale. Correct the product or clean the browser cache.');
}
return;
}
this.product = options.product;
@@ -1347,10 +1360,6 @@ exports.Orderline = Backbone.Model.extend({
},
init_from_JSON: function(json) {
this.product = this.pos.db.get_product_by_id(json.product_id);
if (!this.product) {
console.error('ERROR: attempting to recover product ID', json.product_id,
'not available in the point of sale. Correct the product or clean the browser cache.');
}
this.set_product_lot(this.product);
this.price = json.price_unit;
this.set_discount(json.discount);
+16 -1
View File
@@ -73,6 +73,7 @@
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" >
<field name="iface_start_categ_domain_ids" invisible="1"/>
<div class="o_setting_left_pane">
<field name="start_category"/>
</div>
@@ -82,7 +83,21 @@
Start selling from a default product category
</div>
<div class="content-group mt16" attrs="{'invisible': [('start_category', '=', False)]}">
<field name="iface_start_categ_id"/>
<field name="iface_start_categ_id" domain="[('id', 'in', iface_start_categ_domain_ids)]"/>
</div>
</div>
</div>
<div class="col-12 col-lg-6 o_setting_box" >
<div class="o_setting_left_pane">
<field name="limit_categories"/>
</div>
<div class="o_setting_right_pane">
<label for="limit_categories"/>
<div class="text-muted">
Available Product Categories
</div>
<div class="content-group mt16" attrs="{'invisible': [('limit_categories', '=', False)]}">
<field name="iface_available_categ_ids" widget="many2many_tags"/>
</div>
</div>
</div>