Skip to content

Commit

Permalink
[MIG] stock_orderpoint_generator: Migration to 13.0
Browse files Browse the repository at this point in the history
  • Loading branch information
victoralmau committed Nov 16, 2020
1 parent 14e9ff2 commit 6e73a28
Show file tree
Hide file tree
Showing 8 changed files with 68 additions and 98 deletions.
2 changes: 1 addition & 1 deletion stock_orderpoint_generator/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{
"name": "Order point generator",
"summary": "Mass configuration of stock order points",
"version": "12.0.1.0.0",
"version": "13.0.1.0.0",
"author": "Camptocamp, " "Tecnativa, " "Odoo Community Association (OCA)",
"category": "Warehouse",
"license": "AGPL-3",
Expand Down
9 changes: 5 additions & 4 deletions stock_orderpoint_generator/models/orderpoint_template.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2012-2016 Camptocamp SA
# Copyright 2019 Tecnativa
# Copyright 2019 David Vidal - Tecnativa
# Copyright 2020 Víctor Martínez - Tecnativa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).


Expand Down Expand Up @@ -157,6 +158,7 @@ def _create_instances(self, product_ids):
to_date=record.auto_max_date_end,
criteria=record.auto_max_qty_criteria,
)
vals_list = []
for data in record.copy_data():
for discard_field in self._template_fields_to_discard():
data.pop(discard_field)
Expand All @@ -167,17 +169,16 @@ def _create_instances(self, product_ids):
vals["product_min_qty"] = stock_min_qty.get(product_id.id, 0)
if record.auto_max_qty:
vals["product_max_qty"] = stock_max_qty.get(product_id.id, 0)
orderpoint_model.create(vals)
vals_list.append(vals)
orderpoint_model.create(vals_list)

@api.multi
def create_orderpoints(self, products):
""" Create orderpoint for *products* based on these templates.
:type products: recordset of products
"""
self._disable_old_instances(products)
self._create_instances(products)

@api.multi
def create_auto_orderpoints(self):
for template in self:
if not template.auto_generate:
Expand Down
83 changes: 38 additions & 45 deletions stock_orderpoint_generator/models/product.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2017 Camptocamp SA
# Copyright 2019 Tecnativa
# Copyright 2019 David Vidal - Tecnativa
# Copyright 2020 Víctor Martínez - Tecnativa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html)

from collections import OrderedDict
Expand All @@ -26,66 +27,32 @@ def create(self, vals):
record.auto_orderpoint_template_ids.create_orderpoints(record)
return record

@api.multi
def write(self, vals):
result = super().write(vals)
if vals.get("auto_orderpoint_template_ids"):
orderpoint_templates = self.mapped("auto_orderpoint_template_ids")
orderpoint_templates.create_orderpoints(self)
return result

def _compute_historic_quantities_dict(
self, location_id=False, from_date=False, to_date=False
):
"""Returns a dict of products with a dict of historic moves as for
a list of historic stock values resulting from those moves. If
a location_id is passed, we can restrict it to such location"""
location = location_id and location_id.id
domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self.with_context(
location=location
)._get_domain_locations()
if not to_date:
to_date = fields.Datetime.now()
domain_move_in = domain_move_out = [
("product_id", "in", self.ids),
("state", "=", "done"),
] + domain_move_in_loc
domain_move_out = [
("product_id", "in", self.ids),
("state", "=", "done"),
] + domain_move_out_loc
if from_date:
domain_move_in += [("date", ">=", from_date)]
domain_move_out += [("date", ">=", from_date)]
domain_move_in += [("date", "<=", to_date)]
domain_move_out += [("date", "<=", to_date)]
move_obj = self.env["stock.move"]
# Positive moves
moves_in = move_obj.search_read(
domain_move_in, ["product_id", "product_qty", "date"], order="date asc"
)
# We'll convert to negative these quantities to operate with them
# to obtain the stock snapshot in every moment
moves_out = move_obj.search_read(
domain_move_out, ["product_id", "product_qty", "date"], order="date asc"
)
for move in moves_out:
move["product_qty"] *= -1
# Merge both results and group them by product id as key
moves = moves_in + moves_out
def _get_stock_move_domain(self, domain_move, to_date):
domain = [("product_id", "in", self.ids), ("state", "=", "done")] + domain_move
domain += [("date", "<=", to_date)]
return domain

def _set_product_moves_dict(self, moves, location, to_date):
# Obtain a dict with the stock snapshot for the relative date_from
# otherwise, the first move will counted as first stock value. We
# default the compute the stock value anyway to default the value
# for products with no moves for the given period
initial_stock = {}
initial_stock = self.with_context(location=location)._compute_quantities_dict(
False, False, False, to_date=from_date or to_date
False, False, False, to_date=to_date
)
product_moves_dict = {}
for move in moves:
product_moves_dict.setdefault(move["product_id"][0], {})
product_moves_dict[move["product_id"][0]].update(
{move["date"]: {"prod_qty": move["product_qty"],}}
{move["date"]: {"prod_qty": move["product_qty"]}}
)
for product in self.with_context(prefetch_fields=False):
# If no there are no moves for a product we default the stock
Expand All @@ -107,8 +74,6 @@ def _compute_historic_quantities_dict(
product_moves = OrderedDict(sorted(product_moves.items()))
stock = False
first_item = product_moves[next(iter(product_moves))]
if from_date:
stock = prod_initial_stock.get("qty_available")
if not stock:
stock = first_item["prod_qty"]
first_item["stock"] = stock
Expand All @@ -121,3 +86,31 @@ def _compute_historic_quantities_dict(
v["stock"] for k, v in product_moves.items()
]
return product_moves_dict

def _compute_historic_quantities_dict(self, location_id, to_date):
"""Returns a dict of products with a dict of historic moves as for
a list of historic stock values resulting from those moves. If
a location_id is passed, we can restrict it to such location"""
location = location_id and location_id.id
domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self.with_context(
location=location
)._get_domain_locations()
if not to_date:
to_date = fields.Datetime.now()
domain_move_in = self._get_stock_move_domain(domain_move_in_loc, to_date)
domain_move_out = self._get_stock_move_domain(domain_move_out_loc, to_date)
move_obj = self.env["stock.move"]
# Positive moves
moves_in = move_obj.search_read(
domain_move_in, ["product_id", "product_qty", "date"], order="date asc"
)
# We'll convert to negative these quantities to operate with them
# to obtain the stock snapshot in every moment
moves_out = move_obj.search_read(
domain_move_out, ["product_id", "product_qty", "date"], order="date asc"
)
for move in moves_out:
move["product_qty"] *= -1
# Merge both results and group them by product id as key
moves = moves_in + moves_out
return self._set_product_moves_dict(moves, location, to_date)
3 changes: 2 additions & 1 deletion stock_orderpoint_generator/readme/CONTRIBUTORS.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@
* Guewen Baconnier <guewen.baconnier@camptocamp.com>
* `Tecnativa <https://www.tecnativa.com>`_:

* Vicent Cubells <vicent@vcubells.net>
* Vicent Cubells
* David Vidal
* Víctor Martínez
57 changes: 18 additions & 39 deletions stock_orderpoint_generator/tests/test_orderpoint_generator.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Copyright 2016 Cyril Gaudin (Camptocamp)
# Copyright 2019 Tecnativa
# Copyright 2019 David Vidal - Tecnativa
# Copyright 2020 Víctor Martínez - Tecnativa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models
from odoo.exceptions import UserError
Expand All @@ -14,17 +15,17 @@ def setUpClass(cls):
cls.orderpoint_model = cls.env["stock.warehouse.orderpoint"]
cls.orderpoint_template_model = cls.env["stock.warehouse.orderpoint.template"]
cls.product_model = cls.env["product.product"]
cls.p1 = cls.product_model.create({"name": "Unittest P1", "type": "product",})
cls.p2 = cls.product_model.create({"name": "Unittest P2", "type": "product",})
cls.p1 = cls.product_model.create({"name": "Unittest P1", "type": "product"})
cls.p2 = cls.product_model.create({"name": "Unittest P2", "type": "product"})
cls.wh1 = cls.env["stock.warehouse"].create(
{"name": "TEST WH1", "code": "TST1",}
{"name": "TEST WH1", "code": "TST1"}
)
location_obj = cls.env["stock.location"]
cls.supplier_loc = location_obj.create(
{"name": "Test supplier location", "usage": "supplier",}
{"name": "Test supplier location", "usage": "supplier"}
)
cls.customer_loc = location_obj.create(
{"name": "Test customer location", "usage": "customer",}
{"name": "Test customer location", "usage": "customer"}
)
cls.orderpoint_fields_dict = {
"warehouse_id": cls.wh1.id,
Expand Down Expand Up @@ -325,39 +326,25 @@ def test_auto_qty(self):
wizard = self.wizard_over_products(self.p1, self.template)
wizard.action_configure()
orderpoint_auto_dict = self.orderpoint_fields_dict.copy()
orderpoint_auto_dict.update(
{"product_min_qty": 100.0,}
)
orderpoint_auto_dict.update({"product_min_qty": 100.0})
self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict)
# Min stock for p1: 45
self.template.write(
{"auto_min_qty_criteria": "min",}
)
self.template.write({"auto_min_qty_criteria": "min"})
wizard = self.wizard_over_products(self.p1, self.template)
wizard.action_configure()
orderpoint_auto_dict.update(
{"product_min_qty": 45.0,}
)
orderpoint_auto_dict.update({"product_min_qty": 45.0})
self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict)
# Median of stock for p1: 52
self.template.write(
{"auto_min_qty_criteria": "median",}
)
self.template.write({"auto_min_qty_criteria": "median"})
wizard = self.wizard_over_products(self.p1, self.template)
wizard.action_configure()
orderpoint_auto_dict.update(
{"product_min_qty": 52.0,}
)
orderpoint_auto_dict.update({"product_min_qty": 52.0})
self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict)
# Average of stock for p1: 60.4
self.template.write(
{"auto_min_qty_criteria": "avg",}
)
self.template.write({"auto_min_qty_criteria": "avg"})
wizard = self.wizard_over_products(self.p1, self.template)
wizard.action_configure()
orderpoint_auto_dict.update(
{"product_min_qty": 60.4,}
)
orderpoint_auto_dict.update({"product_min_qty": 60.4})
self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict)
# Set auto values for min and max: 60.4 (avg) 100 (max)
self.template.write(
Expand All @@ -370,19 +357,13 @@ def test_auto_qty(self):
)
wizard = self.wizard_over_products(self.p1, self.template)
wizard.action_configure()
orderpoint_auto_dict.update(
{"product_max_qty": 100,}
)
orderpoint_auto_dict.update({"product_max_qty": 100})
self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict)
# If they have the same values, only one is computed:
self.template.write(
{"auto_min_qty_criteria": "max",}
)
self.template.write({"auto_min_qty_criteria": "max"})
wizard = self.wizard_over_products(self.p1, self.template)
wizard.action_configure()
orderpoint_auto_dict.update(
{"product_min_qty": 100,}
)
orderpoint_auto_dict.update({"product_min_qty": 100})
self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict)
# Auto min max over a shorter period
self.template.write(
Expand All @@ -395,9 +376,7 @@ def test_auto_qty(self):
)
wizard = self.wizard_over_products(self.p1, self.template)
wizard.action_configure()
orderpoint_auto_dict.update(
{"product_min_qty": 55, "product_max_qty": 50,}
)
orderpoint_auto_dict.update({"product_min_qty": 55, "product_max_qty": 50})
self.check_orderpoint(self.p1, self.template, orderpoint_auto_dict)

def test_auto_qty_multi_products(self):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -166,8 +166,7 @@
<field name="name">Reordering Rule Templates</field>
<field name="res_model">stock.warehouse.orderpoint.template</field>
<field name="type">ir.actions.act_window</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_mode">form,tree</field>
<field name="view_id" ref="view_warehouse_orderpoint_template_tree" />
<field name="search_view_id" ref="view_warehouse_orderpoint_template_search" />
<field name="help" type="html">
Expand Down
3 changes: 1 addition & 2 deletions stock_orderpoint_generator/wizard/orderpoint_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).


from odoo import _, api, fields, models
from odoo import _, fields, models
from odoo.exceptions import UserError

_template_register = ["orderpoint_template_id"]
Expand All @@ -22,7 +22,6 @@ class OrderpointGenerator(models.TransientModel):
string="Reordering Rule Templates",
)

@api.multi
def action_configure(self):
"""Action to retrieve wizard data and launch creation of items."""
self.ensure_one()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,19 +27,17 @@
<act_window
name="Reordering Rules Generator"
res_model="stock.warehouse.orderpoint.generator"
src_model="product.product"
binding_model="product.product"
view_mode="form"
target="new"
key2="client_action_multi"
id="act_create_product_conf"
/>
<act_window
name="Reordering Rules Generator"
res_model="stock.warehouse.orderpoint.generator"
src_model="product.template"
binding_model="product.template"
view_mode="form"
target="new"
key2="client_action_multi"
id="act_create_product_template_conf"
/>
</odoo>

0 comments on commit 6e73a28

Please sign in to comment.