Skip to content

Commit

Permalink
Merge PR OCA#899 into 16.0
Browse files Browse the repository at this point in the history
Signed-off-by rousseldenis
  • Loading branch information
OCA-git-bot committed Oct 31, 2024
2 parents 33562ce + 2fdc6e5 commit 78addfd
Show file tree
Hide file tree
Showing 7 changed files with 313 additions and 5 deletions.
72 changes: 71 additions & 1 deletion shopfloor/actions/data_detail.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from collections import defaultdict

from odoo.tools.float_utils import float_round

from odoo.addons.component.core import Component
Expand All @@ -24,12 +26,47 @@ def locations_detail(self, record, **kw):
def _location_detail_parser(self):
return self._location_parser + [
"complete_name",
(
"quant_ids:products",
lambda record, fname: self._location_content(record),
),
(
"reserved_move_line_ids:reserved_move_lines",
lambda record, fname: self.move_lines(record[fname]),
),
]

def _location_content(self, record):
res = []
product_qties = defaultdict(lambda: defaultdict(lambda: 0))
for quant in record.quant_ids:
product_qties[quant.product_id][quant.lot_id] += quant.quantity
for product, quants_qty in product_qties.items():
val = self.product(product)
lots = []
val["lots"] = lots
qty_total = 0
for lot, qty in quants_qty.items():
qty_total += qty
if not lot:
continue
lot_val = self._location_lot(lot)
lot_val["quantity"] = qty
lots.append(lot_val)
val["quantity"] = qty_total
res.append(val)
return res

def _location_lot(self, record, **kw):
# Define a new method to not overload the base one which is used in many places
return self._jsonify(record, self._location_lot_detail_parser, **kw)

@property
def _location_lot_detail_parser(self):
return self._lot_parser + [
"removal_date",
]

@ensure_model("stock.picking")
def picking_detail(self, record, **kw):
parser = self._picking_detail_parser
Expand Down Expand Up @@ -92,7 +129,6 @@ def lots_detail(self, record, **kw):
def _lot_detail_parser(self):
return self._lot_parser + [
"removal_date",
"expiration_date:expire_date",
(
"product_id:product",
lambda record, fname: self.product_detail(record[fname]),
Expand Down Expand Up @@ -131,6 +167,10 @@ def _product_reserved_qty_subparser(self, rec, field_name):
def _product_detail_parser(self):
return self._product_parser + [
("image_128:image", self._product_image_url),
(
"stock_quant_ids:locations",
lambda record, fname: self._locations_for_product(record),
),
(
"product_tmpl_id:manufacturer",
lambda rec, fname: self._jsonify(
Expand All @@ -139,6 +179,36 @@ def _product_detail_parser(self):
),
]

def _get_product_locations(self, record):
# Retrieve all products -- maybe more than one location
product_template = record.product_tmpl_id
products = product_template.product_variant_ids
quants = self.env["stock.quant"].search(
[("product_id", "in", products.ids), ("location_id.usage", "=", "internal")]
)
return quants.location_id

def _locations_for_product(self, record):
res = []
for location in self._get_product_locations(record):
quants = location.quant_ids.filtered(lambda q: q.product_id == record)
loc = self.location(location)
loc["complete_name"] = location.complete_name
loc["quantity"] = sum(quants.mapped("quantity"))
lots = []
quants_lot_qty = defaultdict(lambda: 0)
for quant in quants:
quants_lot_qty[quant.lot_id] += quant.quantity
for lot, qty in quants_lot_qty.items():
if not lot:
continue
lot_val = self._location_lot(lot)
lot_val["quantity"] = qty
lots.append(lot_val)
loc["lots"] = lots
res.append(loc)
return res

def _product_image_url(self, record, field_name):
if not record[field_name]:
return None
Expand Down
37 changes: 37 additions & 0 deletions shopfloor/actions/schema_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ def location_detail(self):
"required": True,
},
"reserved_move_lines": self._schema_list_of(self.move_line()),
"products": self._schema_list_of(self.location_product()),
}
)
return schema

def location_product(self):
schema = self.product()
schema.update(
{
"quantity": {"type": "float", "required": True},
"lots": self._schema_list_of(self.location_lot()),
}
)
return schema

def location_lot(self):
schema = self.lot()
schema.update(
{
"removal_date": {"type": "string", "nullable": True, "required": False},
"quantity": {"type": "float", "required": True},
}
)
return schema
Expand Down Expand Up @@ -76,6 +97,22 @@ def product_detail(self):
"image": {"type": "string", "nullable": True, "required": False},
"manufacturer": self._schema_dict_of(self._simple_record()),
"suppliers": self._schema_list_of(self.product_supplierinfo()),
"locations": self._schema_list_of(self.product_location()),
}
)
return schema

def product_location(self):
schema = self.location()
schema.update(
{
"complete_name": {
"type": "string",
"nullable": False,
"required": True,
},
"quantity": {"type": "float", "required": True},
"lots": self._schema_list_of(self.location_lot()),
}
)
return schema
Expand Down
41 changes: 41 additions & 0 deletions shopfloor/tests/test_actions_data_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Copyright 2020 Camptocamp SA (http://www.camptocamp.com)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from collections import defaultdict

from odoo.tools.float_utils import float_round

from odoo.addons.shopfloor_base.tests.common_misc import ActionsDataTestMixin
Expand Down Expand Up @@ -181,6 +183,16 @@ def _expected_package(self, record, **kw):
data.update(kw)
return data

def _expected_lot(self, record, **kw):
data = {
"id": record.id,
"name": record.name,
"ref": record.ref or None,
"expiration_date": record.expiration_date or None,
}
data.update(kw)
return data


class ActionsDataDetailCaseBase(ActionsDataCaseBase):
@classmethod
Expand All @@ -206,6 +218,16 @@ def _expected_location_detail(self, record, **kw):
"reserved_move_lines": self.data_detail.move_lines(
kw.get("move_lines", [])
),
"products": self.data_detail._location_content(record),
}
)

def _expected_location_lot(self, record, **kw):
return dict(
**self._expected_lot(record),
**{
"removal_date": record.removal_date or None,
"quantity": sum(record.quant_ids.mapped("quantity")),
}
)

Expand All @@ -215,16 +237,35 @@ def _expected_product_detail(self, record, **kw):
record.qty_available - record.free_qty,
precision_rounding=record.uom_id.rounding,
)
locations = self.data_detail._get_product_locations(record)
detail = {
"qty_available": qty_available,
"qty_reserved": qty_reserved,
}
locations_info = []
for location in locations:
quants = location.quant_ids.filtered(lambda q: q.product_id == record)
loc = self._expected_location(location)
loc["complete_name"] = location.complete_name
loc["quantity"] = sum(quants.mapped("quantity"))
loc["lots"] = []
quants_lot_qty = defaultdict(lambda: 0)
for quant in quants:
quants_lot_qty[quant.lot_id] += quant.quantity
for lot, qty in quants_lot_qty.items():
if not lot:
continue
lot_val = self._expected_location_lot(lot)
lot_val["quantity"] = qty
loc["lots"].append(lot_val)
locations_info.append(loc)
if kw.get("full"):
detail.update(
{
"image": "/web/image/product.product/{}/image_128".format(record.id)
if record.image_128
else None,
"locations": locations_info,
"manufacturer": {
"id": record.manufacturer_id.id,
"name": record.manufacturer_id.name,
Expand Down
5 changes: 3 additions & 2 deletions shopfloor/tests/test_actions_data_detail.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ def test_data_lot(self):
"name": lot.name,
"ref": "#FOO",
"expiration_date": "2020-05-31T00:00:00",
"removal_date": "2020-05-20T00:00:00",
"product": self._expected_product_detail(self.product_b, full=True),
}
# ignore time and TZ, we don't care here
self.assertEqual(data.pop("removal_date").split("T")[0], "2020-05-20")
self.assertEqual(data.pop("expire_date").split("T")[0], "2020-05-31")
self.assertEqual(data.get("removal_date", "").split("T")[0], "2020-05-20")
self.assertEqual(data.get("expiration_date", "").split("T")[0], "2020-05-31")
self.assertDictEqual(data, expected)

def test_data_package(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,48 @@ Vue.component("detail-location", {
{path: "product.qty_available", label: "Qty in stock"},
];
},
available_product_list_options() {
return {
main: true,
key_title: "display_name",
klass: "loud-labels",
fields: this.available_product_list_fields(),
};
},
available_product_list_fields() {
return [
{path: "supplier_code", label: "Vendor code", klass: "loud"},
{path: "quantity", label: "Qty in stock"},
];
},
lot_detail_options() {
return {
main: false,
key_title: "name",
fields: this.lot_detail_fields(),
klass: "loud-labels",
};
},
lot_detail_fields() {
const self = this;
return [
{path: "quantity", label: "Qty in stock"},
{
path: "expiration_date",
label: "Expiry date",
renderer: function (rec, field) {
return self.utils.display.render_field_date(rec, field);
},
},
{
path: "removal_date",
label: "Removal date",
renderer: function (rec, field) {
return self.utils.display.render_field_date(rec, field);
},
},
];
},
},
template: `
<div :class="$options._componentTag">
Expand All @@ -60,6 +102,38 @@ Vue.component("detail-location", {
/>
</div>
<div class="available_products" v-if="record.products.length">
<separator-title>Available products</separator-title>
<v-expansion-panels v-if="record.products.length > 0" flat :color="utils.colors.color_for('detail_main_card')">
<v-expansion-panel v-for="(product, index) in record.products" :key="make_component_key(['product', index])">
<v-expansion-panel-header>
<item-detail-card
v-bind="$props"
:record="product"
:key="make_component_key(['product', product.id])"
:options="available_product_list_options()"
:card_color="utils.colors.color_for('detail_main_card')"
/>
</v-expansion-panel-header>
<v-expansion-panel-content>
<item-detail-card
v-for="(lot, i) in product.lots"
:record="lot"
v-bind="$props"
:key="make_component_key(['lot', lot.id])"
:options="lot_detail_options()"
:card_color="utils.colors.color_for('screen_step_todo')"
/>
</v-expansion-panel-content>
</v-expansion-panel>
</v-expansion-panels>
<!-- list
:records="record.products"
:options="available_product_list_options()"
/-->
</div>
</div>
`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Vue.component("detail-lot", {
const self = this;
return [
{
path: "expire_date",
path: "expiration_date",
label: "Expiry date",
renderer: function (rec, field) {
return self.utils.display.render_field_date(rec, field);
Expand Down
Loading

0 comments on commit 78addfd

Please sign in to comment.