Skip to content

Commit da54977

Browse files
committed
[IMP] pos_product_quick_info: The following improvements have been made:
- The sales description is displayed in the information pop-up. - The locations within the warehouse where the product is available are displayed. - Variants with the same attribute are grouped together to prevent duplicate attribute errors.
1 parent f594a77 commit da54977

File tree

9 files changed

+245
-5
lines changed

9 files changed

+245
-5
lines changed

pos_product_quick_info/__manifest__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"pos_product_quick_info/static/src/css/pos.css",
1515
"pos_product_quick_info/static/src/js/Screens/ProductScreen/ProductItem.js",
1616
"pos_product_quick_info/static/src/xml/Screens/ProductScreen/ProductItem.xml",
17+
"pos_product_quick_info/static/src/xml/Popups/ProductInfoPopup.xml",
1718
],
1819
},
1920
"installable": True,

pos_product_quick_info/i18n/es.po

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ msgstr "Ajustes"
2323

2424
#. module: pos_product_quick_info
2525
#: model:ir.model.fields,field_description:pos_product_quick_info.field_pos_config__display_quick_product_info
26+
#: model:ir.model.fields,field_description:pos_product_quick_info.field_product_product__display_quick_product_info
2627
#: model:ir.model.fields,field_description:pos_product_quick_info.field_res_config_settings__pos_display_quick_product_info
2728
msgid "Display Quick Product Info"
2829
msgstr "Mostrar información de producto"
@@ -32,14 +33,54 @@ msgstr "Mostrar información de producto"
3233
msgid "Display product info by one click"
3334
msgstr "Mostrar información de producto en un clic"
3435

36+
#. module: pos_product_quick_info
37+
#: model:ir.model.fields,field_description:pos_product_quick_info.field_pos_config__display_product_locations
38+
#: model:ir.model.fields,field_description:pos_product_quick_info.field_res_config_settings__pos_display_product_locations
39+
msgid "Display product locations in product info"
40+
msgstr "Mostrar la ubicación de los productos en la información del producto"
41+
3542
#. module: pos_product_quick_info
3643
#. odoo-javascript
3744
#: code:addons/pos_product_quick_info/static/src/xml/Screens/ProductScreen/ProductItem.xml:0
45+
#: code:addons/pos_product_quick_info/static/src/xml/Screens/ProductScreen/ProductItem.xml:0
3846
#, python-format
3947
msgid "Info"
4048
msgstr "Información"
4149

50+
#. module: pos_product_quick_info
51+
#. odoo-javascript
52+
#: code:addons/pos_product_quick_info/static/src/xml/Popups/ProductInfoPopup.xml:0
53+
#, python-format
54+
msgid "Locations:"
55+
msgstr "Ubicaciones:"
56+
4257
#. module: pos_product_quick_info
4358
#: model:ir.model,name:pos_product_quick_info.model_pos_config
4459
msgid "Point of Sale Configuration"
45-
msgstr "Configuración del punto de venta"
60+
msgstr "Configuración del Punto de Venta"
61+
62+
#. module: pos_product_quick_info
63+
#: model:ir.model,name:pos_product_quick_info.model_product_product
64+
msgid "Product Variant"
65+
msgstr "Variante de producto"
66+
67+
#. module: pos_product_quick_info
68+
#: model_terms:ir.ui.view,arch_db:pos_product_quick_info.res_config_settings_view_form
69+
msgid "Show locations per warehouse in product info popup"
70+
msgstr ""
71+
"Mostrar ubicaciones por almacén en la ventana emergente de información del "
72+
"producto"
73+
74+
#. module: pos_product_quick_info
75+
#. odoo-javascript
76+
#: code:addons/pos_product_quick_info/static/src/xml/Popups/ProductInfoPopup.xml:0
77+
#, python-format
78+
msgid "available,"
79+
msgstr "disponible,"
80+
81+
#. module: pos_product_quick_info
82+
#. odoo-javascript
83+
#: code:addons/pos_product_quick_info/static/src/xml/Popups/ProductInfoPopup.xml:0
84+
#, python-format
85+
msgid "forecasted"
86+
msgstr "previsto"
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
from . import product_product
12
from . import pos_config
23
from . import res_config_settings

pos_product_quick_info/models/pos_config.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,7 @@ class PosConfig(models.Model):
55
_inherit = "pos.config"
66

77
display_quick_product_info = fields.Boolean(default=True)
8+
display_product_locations = fields.Boolean(
9+
string="Display product locations in product info",
10+
default=True
11+
)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
from odoo import api, fields, models, _
2+
from collections import defaultdict
3+
4+
5+
class ProductProduct(models.Model):
6+
_inherit = 'product.product'
7+
8+
def _get_variant_list(self):
9+
"""Returns a list of product variants grouped by attribute name."""
10+
self.ensure_one()
11+
12+
grouped_values = defaultdict(set)
13+
14+
for line in self.attribute_line_ids:
15+
attr_name = line.attribute_id.name
16+
for value in line.value_ids:
17+
grouped_values[attr_name].add(value.name)
18+
19+
variant_list = []
20+
for attr_name, values in grouped_values.items():
21+
variant_list.append({
22+
'name': attr_name,
23+
'values': [
24+
{
25+
'name': value_name,
26+
'search': f'{self.name} {value_name}',
27+
}
28+
for value_name in sorted(values)
29+
],
30+
})
31+
32+
return variant_list
33+
34+
def _get_warehouse_list(self):
35+
"""Returns a list of warehouses with overall quantities and details by location."""
36+
self.ensure_one()
37+
38+
product = self
39+
warehouse_obj = self.env['stock.warehouse']
40+
location_obj = self.env['stock.location']
41+
quant_obj = self.env['stock.quant']
42+
move_obj = self.env['stock.move']
43+
44+
warehouses = warehouse_obj.search([])
45+
46+
warehouse_list = []
47+
48+
for warehouse in warehouses:
49+
# Total quantities per warehouse using context
50+
available_qty = product.with_context(warehouse=warehouse.id).qty_available
51+
forecasted_qty = product.with_context(warehouse=warehouse.id).virtual_available
52+
53+
# Internal warehouse locations
54+
internal_locations = location_obj.search([
55+
('usage', '=', 'internal'),
56+
('location_id', 'child_of', warehouse.view_location_id.id),
57+
])
58+
location_ids = internal_locations.ids
59+
60+
# Stock available by location
61+
quants = quant_obj.read_group(
62+
domain=[
63+
('product_id', '=', product.id),
64+
('location_id', 'in', location_ids),
65+
],
66+
fields=['quantity', 'location_id'],
67+
groupby=['location_id'],
68+
)
69+
70+
# Future incoming and outgoing movements
71+
incoming_moves = move_obj.read_group(
72+
domain=[
73+
('product_id', '=', product.id),
74+
('location_dest_id', 'in', location_ids),
75+
('state', 'in', ['confirmed', 'waiting', 'assigned']),
76+
],
77+
fields=['product_uom_qty', 'location_dest_id'],
78+
groupby=['location_dest_id'],
79+
)
80+
81+
outgoing_moves = move_obj.read_group(
82+
domain=[
83+
('product_id', '=', product.id),
84+
('location_id', 'in', location_ids),
85+
('state', 'in', ['confirmed', 'waiting', 'assigned']),
86+
],
87+
fields=['product_uom_qty', 'location_id'],
88+
groupby=['location_id'],
89+
)
90+
91+
# Index quantities by location
92+
available_by_location = {
93+
q['location_id'][0]: q['quantity'] for q in quants
94+
}
95+
incoming_by_location = {
96+
m['location_dest_id'][0]: m['product_uom_qty'] for m in incoming_moves
97+
}
98+
outgoing_by_location = {
99+
m['location_id'][0]: m['product_uom_qty'] for m in outgoing_moves
100+
}
101+
102+
# Build list of locations
103+
location_list = []
104+
for loc in internal_locations:
105+
loc_id = loc.id
106+
available = available_by_location.get(loc_id, 0.0)
107+
incoming = incoming_by_location.get(loc_id, 0.0)
108+
outgoing = outgoing_by_location.get(loc_id, 0.0)
109+
forecasted = available + incoming - outgoing
110+
111+
# SOnly show relevant locations (if any exist)
112+
if available or incoming or outgoing:
113+
location_list.append({
114+
'name': loc.display_name,
115+
'available_quantity': available,
116+
'forecasted_quantity': forecasted,
117+
})
118+
119+
# Build warehouse result
120+
warehouse_list.append({
121+
'name': warehouse.name,
122+
'available_quantity': available_qty,
123+
'forecasted_quantity': forecasted_qty,
124+
'uom': product.uom_id.name,
125+
'locations': sorted(location_list, key=lambda x: x['name']),
126+
})
127+
128+
return warehouse_list
129+
130+
def get_product_info_pos(self, price, quantity, pos_config_id):
131+
132+
res = super().get_product_info_pos(price, quantity, pos_config_id)
133+
134+
pos_config = self.env['pos.config'].browse(pos_config_id)
135+
136+
# Display warehouses with or without locations according to configuration
137+
if pos_config.display_product_locations:
138+
res['warehouses'] = self._get_warehouse_list()
139+
140+
# Group variants with the same attribute
141+
res['variants'] = self._get_variant_list()
142+
143+
return res

pos_product_quick_info/models/res_config_settings.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,7 @@ class ResConfigSettings(models.TransientModel):
77
pos_display_quick_product_info = fields.Boolean(
88
related="pos_config_id.display_quick_product_info", readonly=False
99
)
10+
pos_display_product_locations = fields.Boolean(
11+
related="pos_config_id.display_product_locations",
12+
readonly=False
13+
)

pos_product_quick_info/static/description/index.html

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,11 @@
88

99
/*
1010
:Author: David Goodger ([email protected])
11-
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
11+
:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $
1212
:Copyright: This stylesheet has been placed in the public domain.
1313
1414
Default cascading style sheet for the HTML output of Docutils.
15+
Despite the name, some widely supported CSS2 features are used.
1516
1617
See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to
1718
customize this style sheet.
@@ -274,7 +275,7 @@
274275
margin-left: 2em ;
275276
margin-right: 2em }
276277

277-
pre.code .ln { color: grey; } /* line numbers */
278+
pre.code .ln { color: gray; } /* line numbers */
278279
pre.code, code { background-color: #eeeeee }
279280
pre.code .comment, code .comment { color: #5C6576 }
280281
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
@@ -300,7 +301,7 @@
300301
span.pre {
301302
white-space: pre }
302303

303-
span.problematic {
304+
span.problematic, pre.problematic {
304305
color: red }
305306

306307
span.section-subtitle {
@@ -423,7 +424,9 @@ <h2><a class="toc-backref" href="#toc-entry-5">Contributors</a></h2>
423424
<div class="section" id="maintainers">
424425
<h2><a class="toc-backref" href="#toc-entry-6">Maintainers</a></h2>
425426
<p>This module is maintained by the OCA.</p>
426-
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
427+
<a class="reference external image-reference" href="https://odoo-community.org">
428+
<img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" />
429+
</a>
427430
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
428431
mission is to support the collaborative development of Odoo features and
429432
promote its widespread use.</p>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<templates xml:space="preserve">
3+
<!-- Extension of the ProductInfoPopup template -->
4+
<t t-name="pos_product_quick_info.ProductInfoPopup" t-inherit="point_of_sale.ProductInfoPopup" t-inherit-mode="extension" owl="1">
5+
6+
<!-- Location: immediately after each warehouse -->
7+
<xpath expr="//div[@class='section-inventory-body']/table/t[@t-foreach='productInfo.warehouses']/tr" position="after">
8+
<!-- This code will be within the context of each warehouse. -->
9+
<t t-if="warehouse.locations">
10+
<tr>
11+
<td colspan="3" style="padding-left: 1rem; font-weight: bold;">
12+
<t t-esc="env._t('Locations:')"/>
13+
</td>
14+
</tr>
15+
<t t-foreach="warehouse.locations" t-as="location" t-key="location.name">
16+
<tr>
17+
<td style="padding-left: 2rem;"><span t-esc="location.name" class="table-name"/>:</td>
18+
<td><t t-esc="location.available_quantity"/> <t t-esc="warehouse.uom"/> available,</td>
19+
<td><t t-esc="location.forecasted_quantity"/> forecasted</td>
20+
</tr>
21+
</t>
22+
</t>
23+
</xpath>
24+
<xpath expr="//div[@class='section-product-info-title']" position="after">
25+
<div t-if="props.product.description_sale" style="text-align: justify; padding: 0.5rem 1rem;">
26+
<t t-esc="props.product.description_sale"/>
27+
</div>
28+
</xpath>
29+
30+
</t>
31+
</templates>

pos_product_quick_info/views/res_config_settings_view.xml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,18 @@
1818
</div>
1919
</div>
2020
</div>
21+
<div class="col-12 col-lg-6 o_setting_box"
22+
attrs="{'invisible': [('pos_display_quick_product_info', '=', False)]}">
23+
<div class="o_setting_left_pane">
24+
<field name="pos_display_product_locations"/>
25+
</div>
26+
<div class="o_setting_right_pane">
27+
<label for="pos_display_product_locations"/>
28+
<div class="text-muted">
29+
Show locations per warehouse in product info popup
30+
</div>
31+
</div>
32+
</div>
2133
</xpath>
2234
</field>
2335
</record>

0 commit comments

Comments
 (0)