[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:
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
@@ -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
|
||||
|
||||
|
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user