mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[MIG] stock_orderpoint_generator: Migration to 13.0
This commit is contained in:
committed by
sergiocorato
parent
8a23ede668
commit
1b95696248
@@ -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",
|
||||
|
||||
@@ -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).
|
||||
|
||||
|
||||
@@ -157,6 +158,7 @@ class OrderpointTemplate(models.Model):
|
||||
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)
|
||||
@@ -167,9 +169,9 @@ class OrderpointTemplate(models.Model):
|
||||
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
|
||||
@@ -177,7 +179,6 @@ class OrderpointTemplate(models.Model):
|
||||
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:
|
||||
|
||||
@@ -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
|
||||
@@ -26,7 +27,6 @@ class ProductProduct(models.Model):
|
||||
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"):
|
||||
@@ -34,45 +34,16 @@ class ProductProduct(models.Model):
|
||||
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
|
||||
def _get_stock_move_domain(self, domain_move=False, from_date=False, to_date=False):
|
||||
domain = [("product_id", "in", self.ids), ("state", "=", "done")] + domain_move
|
||||
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
|
||||
domain += [("date", ">=", from_date)]
|
||||
domain += [("date", "<=", to_date)]
|
||||
return domain
|
||||
|
||||
def _set_product_moves_dict(
|
||||
self, moves=False, location=False, from_date=False, to_date=False
|
||||
):
|
||||
# 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
|
||||
@@ -85,7 +56,7 @@ class ProductProduct(models.Model):
|
||||
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
|
||||
@@ -121,3 +92,37 @@ class ProductProduct(models.Model):
|
||||
v["stock"] for k, v in product_moves.items()
|
||||
]
|
||||
return product_moves_dict
|
||||
|
||||
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 = self._get_stock_move_domain(
|
||||
domain_move_in_loc, from_date, to_date
|
||||
)
|
||||
domain_move_out = self._get_stock_move_domain(
|
||||
domain_move_out_loc, from_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
|
||||
return self._set_product_moves_dict(moves, location, from_date, to_date)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -14,17 +15,17 @@ class TestOrderpointGenerator(SavepointCase):
|
||||
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,
|
||||
@@ -325,39 +326,25 @@ class TestOrderpointGenerator(SavepointCase):
|
||||
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(
|
||||
@@ -370,19 +357,13 @@ class TestOrderpointGenerator(SavepointCase):
|
||||
)
|
||||
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(
|
||||
@@ -395,9 +376,7 @@ class TestOrderpointGenerator(SavepointCase):
|
||||
)
|
||||
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):
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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"]
|
||||
@@ -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()
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user