[IMP] point_of_sale: add product combos

In this PR we introduce the ability to create product combos.

Now any product can be turned into a combo meal by defining
the different combo choices it contains.

In this PR, we add the backend logic and the backoffice views
needed for this feature. Afterwards, different modules will be
able to use the platform provided by this PR in order to integrate
`product combos` in their flows. Such modules are the `point_of_sale`
and `pos_self_order`.

closes odoo/odoo#128857

Task: 3430635
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
vlst
2023-07-26 23:15:05 +02:00
parent 5b607e5a0b
commit 669b9fcc2f
8 changed files with 91 additions and 0 deletions
+1
View File
@@ -23,6 +23,7 @@
'views/point_of_sale_view.xml',
'views/pos_order_view.xml',
'views/pos_category_view.xml',
'views/pos_combo_view.xml',
'views/product_view.xml',
'views/account_journal_view.xml',
'views/pos_payment_method_views.xml',
+2
View File
@@ -13,6 +13,8 @@ from . import pos_category
from . import pos_config
from . import pos_order
from . import pos_session
from . import pos_combo
from . import pos_combo_line
from . import product
from . import res_partner
from . import res_company
+25
View File
@@ -0,0 +1,25 @@
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
class PosCombo(models.Model):
"""
This model is used to allow the pos user to create menus.
This means that products can be grouped together and sold as a combo.
"""
_name = "pos.combo"
_description = "Product combo choices"
name = fields.Char(string="Name", required=True)
combo_line_ids = fields.One2many("pos.combo.line", "combo_id", string="Products in Combo")
num_of_products = fields.Integer("No of Products", compute="_compute_num_of_products")
@api.depends("combo_line_ids")
def _compute_num_of_products(self):
# optimization trick to count the number of products in each combo
for rec, num_of_products in self.env["pos.combo.line"]._read_group([("combo_id", "in", self.ids)], groupby=["combo_id"], aggregates=["__count"]):
rec.num_of_products = num_of_products
@api.constrains("combo_line_ids")
def _check_combo_line_ids_is_not_null(self):
if any(not rec.combo_line_ids for rec in self):
raise ValidationError(_("Please add products in combo."))
@@ -0,0 +1,11 @@
from odoo import fields, models
class PosComboLine(models.Model):
_name = "pos.combo.line"
_description = "Product Combo Items"
product_id = fields.Many2one("product.product", string="Product")
price = fields.Float("Price Extra", default=0.0)
lst_price = fields.Float("Original Price", related="product_id.lst_price")
combo_id = fields.Many2one("pos.combo")
+1
View File
@@ -15,6 +15,7 @@ class ProductTemplate(models.Model):
pos_categ_ids = fields.Many2many(
'pos.category', string='Point of Sale Category',
help="Category used in the Point of Sale.")
combo_ids = fields.Many2many('pos.combo', string='Combinations')
@api.ondelete(at_uninstall=False)
def _unlink_except_open_session(self):
@@ -47,6 +47,10 @@ access_decimal_precision_user,decimal.precision,base.model_decimal_precision,gro
access_pos_payment_user,pos.payment user,model_pos_payment,group_pos_user,1,1,1,1
access_pos_payment_method_user,pos.payment.method user,model_pos_payment_method,group_pos_user,1,0,0,0
access_pos_payment_method_manager,pos.payment.method manager,model_pos_payment_method,group_pos_manager,1,1,1,1
access_pos_combo_manager,pos.combo manager,model_pos_combo,group_pos_manager,1,1,1,1
access_pos_combo_user,pos.combo user,model_pos_combo,group_pos_user,1,0,0,0
access_pos_combo_line_manager,pos.combo.line manager,model_pos_combo_line,group_pos_manager,1,1,1,1
access_pos_combo_line_user,pos.combo.line user,model_pos_combo_line,group_pos_user,1,0,0,0
access_pos_details_wizard,access.pos.details.wizard,model_pos_details_wizard,point_of_sale.group_pos_manager,1,1,1,0
access_pos_make_payment,access.pos.make.payment,model_pos_make_payment,point_of_sale.group_pos_manager,1,1,1,0
access_pos_close_session_wizard,access.pos.close.session.wizard,model_pos_close_session_wizard,point_of_sale.group_pos_user,1,1,1,0
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
47 access_pos_payment_user pos.payment user model_pos_payment group_pos_user 1 1 1 1
48 access_pos_payment_method_user pos.payment.method user model_pos_payment_method group_pos_user 1 0 0 0
49 access_pos_payment_method_manager pos.payment.method manager model_pos_payment_method group_pos_manager 1 1 1 1
50 access_pos_combo_manager pos.combo manager model_pos_combo group_pos_manager 1 1 1 1
51 access_pos_combo_user pos.combo user model_pos_combo group_pos_user 1 0 0 0
52 access_pos_combo_line_manager pos.combo.line manager model_pos_combo_line group_pos_manager 1 1 1 1
53 access_pos_combo_line_user pos.combo.line user model_pos_combo_line group_pos_user 1 0 0 0
54 access_pos_details_wizard access.pos.details.wizard model_pos_details_wizard point_of_sale.group_pos_manager 1 1 1 0
55 access_pos_make_payment access.pos.make.payment model_pos_make_payment point_of_sale.group_pos_manager 1 1 1 0
56 access_pos_close_session_wizard access.pos.close.session.wizard model_pos_close_session_wizard point_of_sale.group_pos_user 1 1 1 0
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="point_of_sale.view_pos_combo_form" model="ir.ui.view">
<field name="name">pos.combo.form</field>
<field name="model">pos.combo</field>
<field name="arch" type="xml">
<form string="Combos">
<div class="oe_title">
<label for="name" string="Combo Name"/>
<h1>
<field class="text-break" name="name" options="{'line_breaks': False}" widget="text" placeholder="e.g. Burger Menu"/>
</h1>
</div>
<field name="combo_line_ids">
<tree editable="bottom">
<field name="product_id"/>
<field name="price"/>
<field name="lst_price"/>
</tree>
</field>
</form>
</field>
</record>
<record id="point_of_sale.view_pos_combo_tree" model="ir.ui.view">
<field name="name">pos.combo.tree</field>
<field name="model">pos.combo</field>
<field name="arch" type="xml">
<tree string="combos">
<field name="name" />
<field name="num_of_products"/>
</tree>
</field>
</record>
<record id="point_of_sale.action_pos_combo" model="ir.actions.act_window">
<field name="name">Combo Choices</field>
<field name="res_model">pos.combo</field>
<field name="view_mode">tree,form</field>
</record>
</odoo>
@@ -69,6 +69,7 @@
<field name="available_in_pos"/>
<field name="to_weight" attrs="{'invisible': [('available_in_pos', '=', False)]}"/>
<field name="pos_categ_ids" widget="many2many_tags" groups="point_of_sale.group_pos_user" attrs="{'invisible': [('available_in_pos', '=', False)]}" string="Category"/>
<field name="combo_ids" widget="many2many_tags" options="{'no_create': True}"/>
</group>
</xpath>
</field>
@@ -88,6 +89,11 @@
action="product_product_action"
groups="product.group_product_variant"
sequence="10"/>
<menuitem id="point_of_sale.menu_pos_combo"
name="Product Combos"
parent="point_of_sale.pos_config_menu_catalog"
action="point_of_sale.action_pos_combo"
sequence="15" />
<menuitem id="pos_config_menu_action_product_pricelist"
parent="point_of_sale.pos_config_menu_catalog"
action="product.product_pricelist_action2"