[IMP] point_of_sale: Allow products to be part of multiple pos categories

Currently, a product can only be part of one `pos_category`.
This PR changes the relationship between the `product` and the `pos_category` from a `One2many` to a `Many2many`.

closes odoo/odoo#122907

Task: 3138825
Related: odoo/enterprise#41791
Related: odoo/upgrade#4739
Signed-off-by: Joseph Caburnay (jcb) <jcb@odoo.com>
This commit is contained in:
vlst
2023-06-21 13:45:50 +02:00
parent c6405e3f3a
commit 42087c6fa1
19 changed files with 226 additions and 263 deletions
@@ -40,14 +40,14 @@
<record id="preparation_printer" model="pos.printer">
<field name="name">Preparation Printer</field>
<field name="proxy_ip">localhost</field>
<field name="product_categories_ids" eval="[(6, 0, [ref('point_of_sale.pos_category_miscellaneous')])]" />
<field name="product_categories_ids" eval="[(6, 0, [ref('point_of_sale.pos_category_miscellaneous')])]"/>
</record>
<!-- Resource: product.product -->
<record id="stock.product_cable_management_box" model="product.product">
<field name="pos_categ_id" ref="point_of_sale.pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('point_of_sale.pos_category_miscellaneous')])]"/>
</record>
<record id="wall_shelf" model="product.product">
<record id="wall_shelf" model="product.product">
<field name="available_in_pos">True</field>
<field name="list_price">1.98</field>
<field name="name">Wall Shelf Unit</field>
@@ -56,11 +56,11 @@
<field name="weight">0.01</field>
<field name="to_weight">True</field>
<field name="barcode">2100002000003</field>
<field name="taxes_id" eval='[(5,)]'/>
<field name="taxes_id" eval="[(5,)]"/>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/wall_shelf_unit.png"/>
</record>
<record id="small_shelf" model="product.product">
@@ -70,12 +70,12 @@
<field name="default_code">FURN_0008</field>
<field name="type">product</field>
<field name="weight">0.01</field>
<field name="taxes_id" eval='[(5,)]'/>
<field name="taxes_id" eval="[(5,)]"/>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="to_weight">True</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/small_shelf.png"/>
</record>
@@ -88,9 +88,9 @@
<field name="weight">0.01</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/letter_tray.png"/>
</record>
<record id="desk_organizer" model="product.product">
@@ -103,11 +103,11 @@
<field name="type">product</field>
<field name="weight">0.01</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/desk_organizer.png"/>
<field name="taxes_id" eval='[(5,)]'/> <!-- no taxes -->
<field name="taxes_id" eval="[(5,)]"/> <!-- no taxes -->
</record>
<function model="ir.model.data" name="_update_xmlids">
@@ -183,9 +183,9 @@
<field name="barcode">2301000000006</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/magnetic_board.png"/>
</record>
<record id="monitor_stand" model="product.product">
@@ -197,9 +197,9 @@
<field name="weight">0.01</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/monitor_stand.png"/>
</record>
<record id="desk_pad" model="product.product">
@@ -211,9 +211,9 @@
<field name="weight">0.01</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/desk_pad.png"/>
</record>
@@ -225,8 +225,8 @@
<field name="type">product</field>
<field name="weight">0.01</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/whiteboard.png"/>
</record>
@@ -239,9 +239,9 @@
<field name="weight">0.01</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/led_lamp.png"/>
</record>
@@ -255,9 +255,9 @@
<field name="to_weight">True</field>
<field name="barcode">2100001000004</field>
<field name="categ_id" ref="product.product_category_5"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/newspaper_stand.png"/>
</record>
@@ -269,114 +269,114 @@
<field name="default_code">CONS_0001</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_consumable"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/whiteboard_pen.png"/>
</record>
<record id="product.product_product_1" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_2" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_delivery_01" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_chairs"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_chairs')])]"/>
</record>
<record id="product.product_delivery_02" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_order_01" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_3" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_desks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_desks')])]"/>
</record>
<record id="product.product_product_4_product_template" model="product.template">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_desks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_desks')])]"/>
</record>
<record id="product.product_product_5" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_desks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_desks')])]"/>
</record>
<record id="product.product_product_6" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_7" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_8" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_desks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_desks')])]"/>
</record>
<record id="product.product_product_9" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_10" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_11" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_chairs"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_chairs')])]"/>
</record>
<record id="product.product_product_11b" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_chairs"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_chairs')])]"/>
</record>
<record id="product.product_product_12" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_chairs"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_chairs')])]"/>
</record>
<record id="product.product_product_13" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_desks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_desks')])]"/>
</record>
<record id="product.product_product_16" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_20" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_22" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_24" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_25" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.product_product_27" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.consu_delivery_03" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_desks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_desks')])]"/>
</record>
<record id="product.consu_delivery_02" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
<record id="product.consu_delivery_01" model="product.product">
<field name="available_in_pos" eval="True"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
</record>
</odoo>
@@ -17,11 +17,11 @@
<field name="weight">0.01</field>
<field name="to_weight">True</field>
<field name="barcode">2100002000003</field>
<field name="taxes_id" eval='[(5,)]'/>
<field name="taxes_id" eval="[(5,)]"/>
<field name="categ_id" ref="product.product_category_1"/>
<field name="pos_categ_id" ref="pos_category_furniture"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_furniture')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/wall_shelf_unit.png"/>
</record>
@@ -32,12 +32,12 @@
<field name="default_code">FURN_0008</field>
<field name="type">product</field>
<field name="weight">0.01</field>
<field name="taxes_id" eval='[(5,)]'/>
<field name="taxes_id" eval="[(5,)]"/>
<field name="categ_id" ref="product.product_category_1"/>
<field name="pos_categ_id" ref="pos_category_furniture"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_furniture')])]"/>
<field name="to_weight">True</field>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/small_shelf.png"/>
</record>
@@ -50,9 +50,9 @@
<field name="weight">0.01</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_1"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/monitor_stand.png"/>
</record>
@@ -66,9 +66,9 @@
<field name="type">product</field>
<field name="weight">0.01</field>
<field name="categ_id" ref="product.product_category_1"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/desk_organizer.png"/>
</record>
@@ -80,9 +80,9 @@
<field name="default_code">CONS_0001</field>
<field name="to_weight">True</field>
<field name="categ_id" ref="product.product_category_1"/>
<field name="pos_categ_id" ref="pos_category_miscellaneous"/>
<field name="uom_id" ref="uom.product_uom_unit" />
<field name="uom_po_id" ref="uom.product_uom_unit" />
<field name="pos_categ_ids" eval="[(6, 0, [ref('pos_category_miscellaneous')])]"/>
<field name="uom_id" ref="uom.product_uom_unit"/>
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="image_1920" type="base64" file="point_of_sale/static/img/whiteboard_pen.png"/>
</record>
</odoo>
+10 -8
View File
@@ -1,5 +1,8 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from typing import List, Tuple
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError, UserError
@@ -24,14 +27,13 @@ class PosCategory(models.Model):
# field to determine whether a pos.category has an image or not.
has_image = fields.Boolean(compute='_compute_has_image')
def name_get(self):
def get_names(cat):
res = []
while cat:
res.append(cat.name)
cat = cat.parent_id
return res
return [(cat.id, " / ".join(reversed(get_names(cat)))) for cat in self if cat.name]
def _get_hierarchy(self) -> List[str]:
""" Returns a list representing the hierarchy of the categories. """
self.ensure_one()
return (self.parent_id._get_hierarchy() if self.parent_id else []) + [self.name]
def name_get(self) -> List[Tuple[int, str]]:
return [(category.id, " / ".join(category._get_hierarchy())) for category in self if category.name]
@api.ondelete(at_uninstall=False)
def _unlink_except_session_open(self):
+4 -1
View File
@@ -654,7 +654,10 @@ class PosConfig(models.Model):
t.available_in_pos
AND t.sale_ok
AND (t.company_id=%(company_id)s OR t.company_id IS NULL)
AND %(available_categ_ids)s IS NULL OR t.pos_categ_id=ANY(%(available_categ_ids)s)
AND (%(available_categ_ids)s IS NULL OR EXISTS (
SELECT 1 FROM pos_category_product_template_rel
WHERE product_template_id = t.id
AND pos_category_id = ANY(%(available_categ_ids)s)))
) OR p.id=%(tip_product_id)s
ORDER BY t.priority DESC,
t.detailed_type DESC,
+2 -38
View File
@@ -1913,7 +1913,7 @@ class PosSession(models.Model):
('company_id', '=', self.config_id.company_id.id), ('company_id', '=', False)
]
if self.config_id.limit_categories and self.config_id.iface_available_categ_ids:
domain = AND([domain, [('pos_categ_id', 'in', self.config_id.iface_available_categ_ids.ids)]])
domain = AND([domain, [('pos_categ_ids', 'in', self.config_id.iface_available_categ_ids.ids)]])
if self.config_id.iface_tipproduct:
domain = OR([domain, [('id', '=', self.config_id.tip_product_id.id)]])
@@ -1921,7 +1921,7 @@ class PosSession(models.Model):
'search_params': {
'domain': domain,
'fields': [
'display_name', 'lst_price', 'standard_price', 'categ_id', 'pos_categ_id', 'taxes_id', 'barcode',
'display_name', 'lst_price', 'standard_price', 'categ_id', 'pos_categ_ids', 'taxes_id', 'barcode',
'default_code', 'to_weight', 'uom_id', 'description_sale', 'description', 'product_tmpl_id', 'tracking',
'write_date', 'available_in_pos', 'attribute_line_ids', 'active', 'image_128'
],
@@ -2049,42 +2049,6 @@ class PosSession(models.Model):
return amount
def get_total_sold_refund_per_category(self, group_by_user_id=None):
total_sold_per_user_per_category = {}
total_refund_per_user_per_category = {}
for order in self.order_ids:
if group_by_user_id:
user_id = order.user_id.id
else:
# use a user_id of 0 to keep the logic between with user group and without user group the same
user_id = 0
if user_id not in total_sold_per_user_per_category:
total_sold_per_user_per_category[user_id] = {}
total_refund_per_user_per_category[user_id] = {}
total_sold_per_category = total_sold_per_user_per_category[user_id]
total_refund_per_category = total_refund_per_user_per_category[user_id]
for line in order.lines:
key = line.product_id.pos_categ_id.name or "None"
if line.qty >= 0:
if key in total_sold_per_category:
total_sold_per_category[key] += line.price_subtotal_incl
else:
total_sold_per_category[key] = line.price_subtotal_incl
else:
if key in total_refund_per_category:
total_refund_per_category[key] += line.price_subtotal_incl
else:
total_refund_per_category[key] = line.price_subtotal_incl
if group_by_user_id or not total_sold_per_user_per_category:
return list(total_sold_per_user_per_category.items()), list(total_refund_per_user_per_category.items())
else:
return list(total_sold_per_user_per_category[0].items()), list(total_refund_per_user_per_category[0].items())
def get_pos_ui_product_pricelists_by_ids(self, pricelist_ids):
params = self._loader_params_product_pricelist()
params['search_params']['domain'] = [('id', 'in', pricelist_ids)]
+1 -1
View File
@@ -12,7 +12,7 @@ class ProductTemplate(models.Model):
available_in_pos = fields.Boolean(string='Available in POS', help='Check if you want this product to appear in the Point of Sale.', default=False)
to_weight = fields.Boolean(string='To Weigh With Scale', help="Check if the product should be weighted using the hardware scale integration.")
pos_categ_id = fields.Many2one(
pos_categ_ids = fields.Many2many(
'pos.category', string='Point of Sale Category',
help="Category used in the Point of Sale.")
@@ -272,11 +272,12 @@ class ReportSaleDetails(models.AbstractModel):
}
def _get_products_and_taxes_dict(self, line, products, taxes, currency):
key1 = line.product_id.product_tmpl_id.pos_categ_id.name
key2 = (line.product_id, line.price_unit, line.discount)
products.setdefault(key1, {})
products[key1].setdefault(key2, 0.0)
products[key1][key2] += line.qty
keys1 = line.product_id.product_tmpl_id.pos_categ_ids.mapped("name")
for key1 in keys1:
products.setdefault(key1, {})
products[key1].setdefault(key2, 0.0)
products[key1][key2] += line.qty
if line.tax_ids_after_fiscal_position:
line_taxes = line.tax_ids_after_fiscal_position.sudo().compute_all(line.price_unit * (1-(line.discount or 0.0)/100.0), currency, line.qty, product=line.product_id, partner=line.order_id.partner_id or False)
@@ -293,17 +294,20 @@ class ReportSaleDetails(models.AbstractModel):
def _get_total_and_qty_per_category(self, categories):
all_qty = 0
all_total = 0
total = lambda product: (product['quantity'] * product['price_unit']) * (100 - product['discount']) / 100
for category_dict in categories:
qty_cat = 0
total_cat = 0
for product in category_dict['products']:
qty_cat += product['quantity']
total_cat += (product['quantity'] * product['price_unit']) * (100 - product['discount']) / 100
product['total_paid'] = (product['quantity'] * product['price_unit']) * (100 - product['discount']) / 100
product['total_paid'] = total(product)
total_cat += product['total_paid']
category_dict['total'] = total_cat
category_dict['qty'] = qty_cat
all_qty += qty_cat
all_total += total_cat
# IMPROVEMENT: It would be better if the `products` are grouped by pos.order.line.id.
unique_products = list({tuple(sorted(product.items())): product for category in categories for product in category['products']}.values())
all_qty = sum([product['quantity'] for product in unique_products])
all_total = sum([total(product) for product in unique_products])
return categories, {'total': all_total, 'qty': all_qty}
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models, tools
from odoo import fields, models, tools
class PosOrderReport(models.Model):
@@ -32,7 +32,6 @@ class PosOrderReport(models.Model):
product_categ_id = fields.Many2one('product.category', string='Product Category', readonly=True)
invoiced = fields.Boolean(readonly=True)
config_id = fields.Many2one('pos.config', string='Point of Sale', readonly=True)
pos_categ_id = fields.Many2one('pos.category', string='PoS Category', readonly=True)
pricelist_id = fields.Many2one('product.pricelist', string='Pricelist', readonly=True)
session_id = fields.Many2one('pos.session', string='Session', readonly=True)
margin = fields.Float(string='Margin', readonly=True)
@@ -62,7 +61,6 @@ class PosOrderReport(models.Model):
pt.categ_id AS product_categ_id,
p.product_tmpl_id,
ps.config_id,
pt.pos_categ_id,
s.pricelist_id,
s.session_id,
s.account_move IS NOT NULL AS invoiced,
@@ -88,7 +86,7 @@ class PosOrderReport(models.Model):
s.user_id, s.company_id, s.sale_journal,
s.pricelist_id, s.account_move, s.create_date, s.session_id,
l.product_id,
pt.categ_id, pt.pos_categ_id,
pt.categ_id,
p.product_tmpl_id,
ps.config_id
"""
+65 -49
View File
@@ -133,17 +133,7 @@ export class PosDB {
}
make_ancestors(this.root_category_id, []);
}
category_contains(categ_id, product_id) {
var product = this.product_by_id[product_id];
if (product) {
var cid = product.pos_categ_id[0];
while (cid && cid !== categ_id) {
cid = this.category_parent[cid];
}
return !!cid;
}
return false;
}
/* loads a record store from the database. returns default if nothing is found */
load(store, deft) {
if (CACHE[store] !== undefined) {
@@ -193,33 +183,35 @@ export class PosDB {
}
if (product.available_in_pos) {
var search_string = unaccent(this._product_search_string(product));
var categ_id = product.pos_categ_id
? product.pos_categ_id[0]
: this.root_category_id;
const all_categ_ids = product.pos_categ_ids.length
? product.pos_categ_ids
: [this.root_category_id];
product.product_tmpl_id = product.product_tmpl_id[0];
if (!stored_categories[categ_id]) {
stored_categories[categ_id] = [];
}
stored_categories[categ_id].push(product.id);
if (this.category_search_string[categ_id] === undefined) {
this.category_search_string[categ_id] = "";
}
this.category_search_string[categ_id] += search_string;
var ancestors = this.get_category_ancestors_ids(categ_id) || [];
for (var j = 0, jlen = ancestors.length; j < jlen; j++) {
var ancestor = ancestors[j];
if (!stored_categories[ancestor]) {
stored_categories[ancestor] = [];
for (const categ_id of all_categ_ids) {
if (!stored_categories[categ_id]) {
stored_categories[categ_id] = [];
}
stored_categories[ancestor].push(product.id);
stored_categories[categ_id].push(product.id);
if (this.category_search_string[ancestor] === undefined) {
this.category_search_string[ancestor] = "";
if (this.category_search_string[categ_id] === undefined) {
this.category_search_string[categ_id] = "";
}
this.category_search_string[categ_id] += search_string;
var ancestors = this.get_category_ancestors_ids(categ_id) || [];
for (var j = 0, jlen = ancestors.length; j < jlen; j++) {
var ancestor = ancestors[j];
if (!stored_categories[ancestor]) {
stored_categories[ancestor] = [];
}
stored_categories[ancestor].push(product.id);
if (this.category_search_string[ancestor] === undefined) {
this.category_search_string[ancestor] = "";
}
this.category_search_string[ancestor] += search_string;
}
this.category_search_string[ancestor] += search_string;
}
}
this.product_by_id[product.id] = product;
@@ -408,13 +400,18 @@ export class PosDB {
}
return undefined;
}
shouldAddProduct(product, list) {
return product.active && product.available_in_pos && !list.includes(product);
}
get_product_by_category(category_id) {
var product_ids = this.product_by_category_id[category_id];
var list = [];
if (product_ids) {
for (var i = 0, len = Math.min(product_ids.length, this.limit); i < len; i++) {
const product = this.product_by_id[product_ids[i]];
if (!(product.active && product.available_in_pos)) {
if (!this.shouldAddProduct(product, list)) {
continue;
}
list.push(product);
@@ -441,7 +438,7 @@ export class PosDB {
if (r) {
var id = Number(r[1]);
const product = this.get_product_by_id(id);
if (!(product.active && product.available_in_pos)) {
if (!this.shouldAddProduct(product, results)) {
continue;
}
results.push(product);
@@ -451,22 +448,41 @@ export class PosDB {
}
return results;
}
/* from a product id, and a list of category ids, returns
* true if the product belongs to one of the provided category
/**
* returns true if the product belongs to one of the provided categories
* or one of its child categories.
* @param {number[]} category_ids
* @param {number} product_id
* @returns {boolean}
*/
is_product_in_category(category_ids, product_id) {
let cat = this.get_product_by_id(product_id).pos_categ_id[0];
while (cat) {
for (const cat_id of category_ids) {
if (cat == cat_id) {
// The == is important, ids may be strings
return true;
}
}
cat = this.get_category_parent_id(cat);
}
return false;
const product_categ_ids = this.get_product_by_id(product_id).pos_categ_ids;
return this.any_of_is_subcategory(product_categ_ids, category_ids);
}
/**
* Recursively check if any of `subcategory_ids` belongs to any of the `category_ids`
* @param {number[]} subcategory_ids
* @param {number[]} category_ids
* @returns {boolean}
*/
any_of_is_subcategory(subcategory_ids, category_ids) {
return subcategory_ids.some((subcategory_id) =>
this.is_subcategory(subcategory_id, category_ids)
);
}
/**
* Recursively check if a `subcategory_id` a child of any of the provided `category_ids`.
* @param {number} subcategory_id
* @param {number[]} category_ids
* @returns {boolean}
*/
is_subcategory(subcategory_id, category_ids) {
const check = (categ_id) => categ_id && this.is_subcategory(categ_id, category_ids);
return (
category_ids.includes(Number(subcategory_id)) ||
check(this.get_category_parent_id(subcategory_id))
);
}
/* paid orders */
@@ -1638,8 +1638,10 @@ export class Order extends PosModel {
const note = orderline.getNote();
const productKey = `${product.id} - ${orderline.get_full_product_name()} - ${note}`;
const lineKey = `${orderline.uuid} - ${note}`;
if (prepaCategoryIds.has(product.pos_categ_id[0]) || prepaCategoryIds.size === 0) {
if (
prepaCategoryIds.size === 0 ||
this.pos.db.any_of_is_subcategory(product.pos_categ_ids, [...prepaCategoryIds])
) {
const quantity = orderline.get_quantity();
const quantityDiff = oldChanges[lineKey]
? quantity - oldChanges[lineKey].quantity
@@ -1664,7 +1666,6 @@ export class Order extends PosModel {
orderline.setHasChange(false);
}
}
// Checks whether an orderline has been deleted from the order since it
// was last sent to the preparation tools. If so we add this to the changes.
for (const [lineKey, lineResume] of Object.entries(this.lastOrderPrepaChange)) {
@@ -2266,26 +2267,6 @@ export class Order extends PosModel {
return fulldetails;
}
// Returns a total only for the orderlines with products belonging to the category
get_total_for_category_with_tax(categ_id) {
var total = 0;
var self = this;
if (categ_id instanceof Array) {
for (var i = 0; i < categ_id.length; i++) {
total += this.get_total_for_category_with_tax(categ_id[i]);
}
return total;
}
this.orderlines.forEach(function (line) {
if (self.pos.db.category_contains(categ_id, line.product.id)) {
total += line.get_price_with_tax();
}
});
return total;
}
get_total_for_taxes(tax_id) {
var total = 0;
+3 -3
View File
@@ -106,7 +106,7 @@ class TestPointOfSaleHttpCommon(AccountTestInvoicingHttpCommon):
'taxes_id': False,
'weight': 0.01,
'to_weight': True,
'pos_categ_id': pos_categ_misc.id,
'pos_categ_ids': [(4, pos_categ_misc.id)],
})
cls.wall_shelf = env['product.product'].create({
'name': 'Wall Shelf Unit',
@@ -137,14 +137,14 @@ class TestPointOfSaleHttpCommon(AccountTestInvoicingHttpCommon):
'available_in_pos': True,
'list_price': 1.98,
'taxes_id': False,
'pos_categ_id': pos_categ_desks.id,
'pos_categ_ids': [(4, pos_categ_desks.id)],
})
cls.letter_tray = env['product.product'].create({
'name': 'Letter Tray',
'available_in_pos': True,
'list_price': 4.80,
'taxes_id': False,
'pos_categ_id': pos_categ_chairs.id,
'pos_categ_ids': [(4, pos_categ_chairs.id)],
})
cls.desk_organizer = env['product.product'].create({
'name': 'Desk Organizer',
+4 -4
View File
@@ -9,7 +9,7 @@
<filter name="filter_to_availabe_pos" string="Available in POS" domain="[('available_in_pos', '=', True)]"/>
</filter>
<filter name="categ_id" position="after">
<filter string="POS Product Category" name="pos_categ_id" context="{'group_by':'pos_categ_id'}"/>
<filter string="POS Product Category" name="pos_categ_ids" context="{'group_by':'pos_categ_ids'}"/>
</filter>
</field>
</record>
@@ -68,7 +68,7 @@
<group name="pos" string="Point of Sale" attrs="{'invisible': [('sale_ok','=',False)]}">
<field name="available_in_pos"/>
<field name="to_weight" attrs="{'invisible': [('available_in_pos', '=', False)]}"/>
<field name="pos_categ_id" attrs="{'invisible': [('available_in_pos', '=', False)]}" string="Category"/>
<field name="pos_categ_ids" widget="many2many_tags" groups="point_of_sale.group_pos_user" attrs="{'invisible': [('available_in_pos', '=', False)]}" string="Category"/>
</group>
</xpath>
</field>
@@ -122,7 +122,7 @@
<field name="inherit_id" ref="product.product_template_tree_view"/>
<field name="arch" type="xml">
<field name="categ_id" position="before">
<field name="pos_categ_id" optional="hide" string="POS Product Category"/>
<field name="pos_categ_ids" widget="many2many_tags" groups="point_of_sale.group_pos_user" optional="hide" string="POS Product Category"/>
</field>
</field>
</record>
@@ -133,7 +133,7 @@
<field name="inherit_id" ref="product.product_product_tree_view"/>
<field name="arch" type="xml">
<field name="categ_id" position="before">
<field name="pos_categ_id" optional="hide" string="POS Product Category"/>
<field name="pos_categ_ids" widget="many2many_tags" groups="point_of_sale.group_pos_user" optional="hide" string="POS Product Category"/>
</field>
</field>
</record>
@@ -1,4 +1,3 @@
<?xml version="1.0"?>
<odoo>
<!-- ****** Basic Restaurant Setup ***** -->
@@ -22,7 +21,7 @@
<field name="list_price">7.0</field>
<field name="name">Margherita</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-pizza.png"/>
</record>
@@ -31,7 +30,7 @@
<field name="list_price">7.0</field>
<field name="name">Funghi</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-pizza.png"/>
</record>
@@ -40,7 +39,7 @@
<field name="list_price">7.0</field>
<field name="name">Vegetarian</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-pizza.png"/>
</record>
@@ -49,7 +48,7 @@
<field name="list_price">4.5</field>
<field name="name">Pasta Bolognese</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-pasta.jpg"/>
</record>
@@ -58,7 +57,7 @@
<field name="list_price">5.5</field>
<field name="name">Pasta 4 formaggi </field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-pasta-4f.jpg"/>
</record>
@@ -67,7 +66,7 @@
<field name="list_price">7.5</field>
<field name="name">Bacon Burger</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-burger.jpg"/>
</record>
@@ -76,7 +75,7 @@
<field name="list_price">7.0</field>
<field name="name">Cheese Burger</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-cheeseburger.jpg"/>
</record>
@@ -85,7 +84,7 @@
<field name="list_price">3.0</field>
<field name="name">Chicken Curry Sandwich</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-sandwich.jpg"/>
</record>
@@ -94,7 +93,7 @@
<field name="list_price">3.0</field>
<field name="name">Spicy Tuna Sandwich</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-tuna.jpg"/>
</record>
@@ -103,7 +102,7 @@
<field name="list_price">3.9</field>
<field name="name">Mozzarella Sandwich</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-mozza.jpg"/>
</record>
@@ -112,7 +111,7 @@
<field name="list_price">3.4</field>
<field name="name">Club Sandwich</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-club.jpg"/>
</record>
@@ -121,7 +120,7 @@
<field name="list_price">12.0</field>
<field name="name">Lunch Maki 18pc</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-maki.jpg"/>
</record>
@@ -130,7 +129,7 @@
<field name="list_price">13.80</field>
<field name="name">Lunch Salmon 20pc</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-salmon.jpg"/>
</record>
@@ -139,7 +138,7 @@
<field name="list_price">14.0</field>
<field name="name">Lunch Temaki mix 3pc</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-temaki.jpg"/>
</record>
@@ -148,7 +147,7 @@
<field name="list_price">9.25</field>
<field name="name">Salmon and Avocado</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="food"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('food')])]"/>
<field name="categ_id" ref="pos_restaurant.product_category_pos_food"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-salmon-avocado.jpg"/>
</record>
@@ -159,7 +158,7 @@
<field name="list_price">2.20</field>
<field name="name">Coca-Cola</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="drinks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('drinks')])]"/>
<field name="categ_id" ref="point_of_sale.product_category_pos"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-coke.jpg"/>
</record>
@@ -169,7 +168,7 @@
<field name="list_price">2.20</field>
<field name="name">Water</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="drinks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('drinks')])]"/>
<field name="categ_id" ref="point_of_sale.product_category_pos"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-water.jpg"/>
</record>
@@ -179,7 +178,7 @@
<field name="list_price">2.20</field>
<field name="name">Minute Maid</field>
<field name="weight">0.01</field>
<field name="pos_categ_id" ref="drinks"/>
<field name="pos_categ_ids" eval="[(6, 0, [ref('drinks')])]"/>
<field name="categ_id" ref="point_of_sale.product_category_pos"/>
<field name="image_1920" type="base64" file="pos_restaurant/static/img/th-minute_maid.jpg"/>
</record>
@@ -504,5 +503,5 @@
<field name="position_v">315</field>
</record>
<function model="pos.config" name="add_cash_payment_method" />
</odoo>
<function model="pos.config" name="add_cash_payment_method"/>
</odoo>
@@ -8,9 +8,7 @@ import { nbsp } from "@web/core/utils/strings";
patch(ActionpadWidget.prototype, "point_of_sale.ActionpadWidget", {
get swapButton() {
return (
this.props.actionType === "payment" && this.pos.config.module_pos_restaurant
);
return this.props.actionType === "payment" && this.pos.config.module_pos_restaurant;
},
get currentOrder() {
return this.pos.get_order();
@@ -57,8 +55,8 @@ patch(ActionpadWidget.prototype, "point_of_sale.ActionpadWidget", {
const orderChange = this.currentOrder.getOrderChanges().orderlines;
for (const idx in orderChange) {
const orderline = orderChange[idx];
const category = this.pos.db.get_product_by_id(orderline.product_id)
.pos_categ_id[1];
const categoryId = this.pos.db.get_product_by_id(orderline.product_id).pos_categ_ids[0];
const category = this.pos.db.category_by_id[categoryId].name;
const numProd = orderline.quantity;
categories[category] = categories[category] ? categories[category] + numProd : numProd;
}
+3 -10
View File
@@ -39,13 +39,6 @@ class TestFrontend(odoo.tests.HttpCase):
'journal_id': second_cash_journal.id,
})
printer = self.env['pos.printer'].create({
'name': 'Preparation Printer',
'epson_printer_ip': '127.0.0.1',
'printer_type': 'epson_epos',
'product_categories_ids': [drinks_category.id]
})
pos_config = self.env['pos.config'].create({
'name': 'Bar',
'module_pos_restaurant': True,
@@ -144,7 +137,7 @@ class TestFrontend(odoo.tests.HttpCase):
'list_price': 2.20,
'name': 'Coca-Cola',
'weight': 0.01,
'pos_categ_id': drinks_category.id,
'pos_categ_ids': [(4, drinks_category.id)],
'categ_id': self.env.ref('point_of_sale.product_category_pos').id,
'taxes_id': [(6, 0, [])],
})
@@ -154,7 +147,7 @@ class TestFrontend(odoo.tests.HttpCase):
'list_price': 2.20,
'name': 'Water',
'weight': 0.01,
'pos_categ_id': drinks_category.id,
'pos_categ_ids': [(4, drinks_category.id)],
'categ_id': self.env.ref('point_of_sale.product_category_pos').id,
'taxes_id': [(6, 0, [])],
})
@@ -164,7 +157,7 @@ class TestFrontend(odoo.tests.HttpCase):
'list_price': 2.20,
'name': 'Minute Maid',
'weight': 0.01,
'pos_categ_id': drinks_category.id,
'pos_categ_ids': [(4, drinks_category.id)],
'categ_id': self.env.ref('point_of_sale.product_category_pos').id,
'taxes_id': [(6, 0, [])],
})
+1 -2
View File
@@ -153,11 +153,10 @@ class PosConfig(models.Model):
*(
self.limit_categories
and self.iface_available_categ_ids
and [("pos_categ_id", "in", self.iface_available_categ_ids.ids)]
and [("pos_categ_ids", "in", self.iface_available_categ_ids.ids)]
or []
),
],
order="pos_categ_id.sequence asc nulls last",
)
)
@@ -101,7 +101,7 @@ class ProductProduct(models.Model):
"name": product._get_name(),
"product_id": product.id,
"description_sale": product.description_sale,
"tag": product.pos_categ_id.name if product.pos_categ_id else "Other",
"tags": product.pos_categ_ids.mapped("name") or ["Other"],
"is_pos_groupable": product.uom_id.is_pos_groupable,
}
for product in self
@@ -10,7 +10,7 @@ export class Product extends Reactive {
name,
product_id,
description_sale,
tag,
tags,
is_pos_groupable,
},
selfOrder
@@ -27,7 +27,7 @@ export class Product extends Reactive {
this.attributes = product.attributes;
this.name = product.name;
this.description_sale = product.description_sale;
this.tag = product.tag;
this.tags = product.tags;
this.is_pos_groupable = product.is_pos_groupable;
// data
@@ -50,7 +50,7 @@ export class SelfOrder {
this.products = this.products.map((p) => {
const product = new Product(p, this);
this.tagList.add(product.tag);
this.tagList.add(...product.tags);
this.productByIds[product.id] = product;
return product;
@@ -67,7 +67,13 @@ export class SelfOrder {
);
}
this.productsGroupedByTag = groupBy(this.products, "tag");
this.productsGroupedByTag = this._getProductsGroupedByTag(this.products);
}
_getProductsGroupedByTag(products) {
const productsDuplicatedSuchThatEachHasOneTag = products.flatMap((product) =>
product.tags.map((tag) => ({ ...product, tag }))
);
return groupBy(productsDuplicatedSuchThatEachHasOneTag, "tag");
}
saveOrderToLocalStorage(orders) {