[IMP] : black, isort, prettier

This commit is contained in:
Jordi Ballester Alomar
2020-10-28 06:17:14 +01:00
committed by Lois Rilo
parent b9bb4016f1
commit ba251eb05a
5 changed files with 156 additions and 150 deletions

View File

@@ -4,21 +4,15 @@
{
"name": "Stock Pull List",
"summary": "The pull list checks the stock situation and calculates "
"needed quantities.",
"needed quantities.",
"version": "12.0.1.0.1",
"license": "LGPL-3",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"author": "ForgeFlow, "
"Odoo Community Association (OCA)",
"author": "ForgeFlow, " "Odoo Community Association (OCA)",
"maintainers": ["LoisRForgeFlow"],
"development_status": "Alpha",
"category": "Warehouse Management",
"depends": [
"stock",
"stock_available_unreserved",
],
"data": [
"wizards/stock_pull_list_wizard.xml",
],
"depends": ["stock", "stock_available_unreserved",],
"data": ["wizards/stock_pull_list_wizard.xml",],
"installable": True,
}

View File

@@ -1,14 +1,13 @@
# Copyright 2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
from odoo.tests.common import TransactionCase
from odoo import fields
from datetime import timedelta as td
from odoo import fields
from odoo.tests.common import TransactionCase
class TestPullListCommon(TransactionCase):
def setUp(self):
super().setUp()
self.wh_obj = self.env["stock.warehouse"]
@@ -20,21 +19,17 @@ class TestPullListCommon(TransactionCase):
self.warehouse = self.env.ref("stock.warehouse0")
self.customer_loc = self.env.ref("stock.stock_location_customers")
self.warehouse_2 = self.wh_obj.create({
"code": "WH-T",
"name": "Warehouse Test",
})
self.product_a = self.env["product.product"].create({
"name": "test product A",
"default_code": "TEST-A",
"type": "product",
})
self.warehouse_2 = self.wh_obj.create(
{"code": "WH-T", "name": "Warehouse Test",}
)
self.product_a = self.env["product.product"].create(
{"name": "test product A", "default_code": "TEST-A", "type": "product",}
)
route_vals = {
"name": "WH2 -> WH",
}
self.transfer_route = self.env["stock.location.route"].create(
route_vals)
self.transfer_route = self.env["stock.location.route"].create(route_vals)
rule_vals = {
"location_id": self.warehouse.lot_stock_id.id,
"location_src_id": self.warehouse_2.lot_stock_id.id,
@@ -59,21 +54,28 @@ class TestPullListCommon(TransactionCase):
self.create_picking_out_a(self.date_3, 70)
def create_picking_out_a(self, date_move, qty):
picking = self.picking_obj.create({
"picking_type_id": self.ref("stock.picking_type_out"),
"location_id": self.warehouse.lot_stock_id.id,
"location_dest_id": self.customer_loc.id,
"move_lines": [
(0, 0, {
"name": "Test move",
"product_id": self.product_a.id,
"date_expected": date_move,
"date": date_move,
"product_uom": self.product_a.uom_id.id,
"product_uom_qty": qty,
"location_id": self.warehouse.lot_stock_id.id,
"location_dest_id": self.customer_loc.id,
})]
})
picking = self.picking_obj.create(
{
"picking_type_id": self.ref("stock.picking_type_out"),
"location_id": self.warehouse.lot_stock_id.id,
"location_dest_id": self.customer_loc.id,
"move_lines": [
(
0,
0,
{
"name": "Test move",
"product_id": self.product_a.id,
"date_expected": date_move,
"date": date_move,
"product_uom": self.product_a.uom_id.id,
"product_uom_qty": qty,
"location_id": self.warehouse.lot_stock_id.id,
"location_dest_id": self.customer_loc.id,
},
)
],
}
)
picking.action_confirm()
return picking

View File

@@ -5,29 +5,24 @@ from .common import TestPullListCommon
class TestStockPullList(TestPullListCommon):
def test_01_default_options(self):
self._generate_moves()
wiz = self.wiz_obj.create({})
wiz.action_prepare()
lines = wiz.line_ids.filtered(lambda l: l.product_id == self.product_a)
self.assertEqual(len(lines), 2)
line_1 = lines.filtered(
lambda l: l.date_expected == self.yesterday.date())
line_1 = lines.filtered(lambda l: l.date_expected == self.yesterday.date())
self.assertEqual(line_1.raw_demand_qty, 50)
self.assertEqual(line_1.needed_qty, 50)
self.assertEqual(line_1.stock_rule_id, self.transfer_rule)
line_2 = lines.filtered(
lambda l: l.date_expected == self.date_3.date())
line_2 = lines.filtered(lambda l: l.date_expected == self.date_3.date())
self.assertEqual(line_2.raw_demand_qty, 70)
self.assertEqual(line_2.needed_qty, 70)
def test_02_consolidate(self):
self._generate_moves()
wiz = self.wiz_obj.create({
"consolidate_by_product": True,
})
wiz = self.wiz_obj.create({"consolidate_by_product": True,})
wiz.action_prepare()
line = wiz.line_ids.filtered(lambda l: l.product_id == self.product_a)
self.assertEqual(len(line), 1)

View File

@@ -1,11 +1,11 @@
# Copyright 2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
import itertools
from odoo import _, api, fields, models
from odoo.exceptions import UserError
import itertools
class PullListWizard(models.TransientModel):
_name = "stock.pull.list.wizard"
@@ -16,20 +16,15 @@ class PullListWizard(models.TransientModel):
res = super().default_get(fields)
company = self.env.user.company_id
wh = self.env["stock.warehouse"].search(
[("company_id", "=", company.id)], limit=1)
res.update({
"warehouse_id": wh.id,
"location_id": wh.lot_stock_id.id,
})
[("company_id", "=", company.id)], limit=1
)
res.update(
{"warehouse_id": wh.id, "location_id": wh.lot_stock_id.id,}
)
return res
location_id = fields.Many2one(
comodel_name="stock.location",
required=True,
)
warehouse_id = fields.Many2one(
comodel_name="stock.warehouse",
)
location_id = fields.Many2one(comodel_name="stock.location", required=True,)
warehouse_id = fields.Many2one(comodel_name="stock.warehouse",)
line_ids = fields.One2many(
comodel_name="stock.pull.list.wizard.line",
inverse_name="wizard_id",
@@ -38,17 +33,14 @@ class PullListWizard(models.TransientModel):
# Step 1 - filtering options.
exclude_reserved = fields.Boolean()
location_dest_id = fields.Many2one(
string="Destination Location",
comodel_name="stock.location",
string="Destination Location", comodel_name="stock.location",
)
date_to = fields.Date()
consolidate_by_product = fields.Boolean(
help="All needs for each product will be grouped in one line, "
"disregarding date.",
)
procurement_group_ids = fields.Many2many(
comodel_name="procurement.group"
"disregarding date.",
)
procurement_group_ids = fields.Many2many(comodel_name="procurement.group")
# Step 2 - filtering options.
select_all = fields.Boolean(default=True)
rule_action = fields.Selection(
@@ -56,7 +48,7 @@ class PullListWizard(models.TransientModel):
)
available_in_source_location = fields.Boolean(
help="Select only rules with enough available stock in source "
"location. Applies for rules with a source location.",
"location. Applies for rules with a source location.",
)
# Step 2 - grouping options.
max_lines = fields.Integer()
@@ -112,8 +104,9 @@ class PullListWizard(models.TransientModel):
def _get_available_qty(self, product, location):
product_obj = self.env["product.product"]
product_l = product_obj.with_context(
{"location": location.id}).browse(product.id)
product_l = product_obj.with_context({"location": location.id}).browse(
product.id
)
if self.exclude_reserved:
return product_l.qty_available_not_res
return product_l.qty_available
@@ -125,23 +118,24 @@ class PullListWizard(models.TransientModel):
"company_id": self.env.user.company_id,
}
stock_rule_id = self.env["procurement.group"]._get_rule(
product_id, location_id, values)
product_id, location_id, values
)
return stock_rule_id
def action_prepare(self):
domain = self._get_moves_demand_domain()
# `read_group` is not possible here because of the date format the
# method returns.
demand_moves = self.env["stock.move"].search(
domain, order="date_expected asc")
demand_moves = self.env["stock.move"].search(domain, order="date_expected asc")
demand_dict = {}
force_date = fields.Date.today() if self.consolidate_by_product \
else False
force_date = fields.Date.today() if self.consolidate_by_product else False
for demand in demand_moves:
key = (
demand.product_id, demand.location_id,
demand.product_id,
demand.location_id,
fields.Date.to_date(demand.date_expected)
if not force_date else force_date,
if not force_date
else force_date,
)
prev = demand_dict.setdefault(key, 0.0)
# TODO: when exclude_reserved is selected, handle partially avail.
@@ -149,20 +143,24 @@ class PullListWizard(models.TransientModel):
domain = self._get_moves_incoming_domain()
incoming_moves = self.env["stock.move"].search(
domain, order="date_expected asc")
domain, order="date_expected asc"
)
incoming_dict = {}
for supply in incoming_moves:
move_for_date = demand_moves.filtered(
lambda m: m.product_id == supply.product_id and
m.date_expected >= supply.date_expected)
lambda m: m.product_id == supply.product_id
and m.date_expected >= supply.date_expected
)
if move_for_date:
date_selected = move_for_date[0].date_expected \
if not force_date else force_date
date_selected = (
move_for_date[0].date_expected if not force_date else force_date
)
else:
# Supply is later than last demand -> ignore it.
continue
key = (
supply.product_id, supply.location_dest_id,
supply.product_id,
supply.location_dest_id,
fields.Date.to_date(date_selected),
)
prev = incoming_dict.setdefault(key, 0.0)
@@ -173,17 +171,17 @@ class PullListWizard(models.TransientModel):
qty_assigned = {}
for key, demand_qty in demand_dict.items():
supply_qty = incoming_dict.get(key, 0.0)
lines.append((0, 0, self._prepare_line_values(
key, demand_qty, supply_qty)))
self.update({
"line_ids": lines,
})
lines.append((0, 0, self._prepare_line_values(key, demand_qty, supply_qty)))
self.update(
{"line_ids": lines,}
)
res = self._act_window_pull_list_step_2()
return res
def _act_window_pull_list_step_2(self):
view_id = self.env.ref(
"stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2").id
"stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2"
).id
res = {
"name": _("Pull List"),
"src_model": "stock.pull.list.wizard",
@@ -202,8 +200,9 @@ class PullListWizard(models.TransientModel):
if self.select_all:
line.selected = True
continue
rule_invalid = self.rule_action and \
self.rule_action != line.stock_rule_id.action
rule_invalid = (
self.rule_action and self.rule_action != line.stock_rule_id.action
)
if self.available_in_source_location:
available = line._is_available_in_source_location()
else:
@@ -254,8 +253,7 @@ class PullListWizard(models.TransientModel):
# User requesting the procurement is passed by context to be able to
# update final MO, PO or trasfer with that information.
# TODO: migration to v13: requested_uid is not needed.
pg_obj = self.env["procurement.group"].with_context(
requested_uid=self.env.user)
pg_obj = self.env["procurement.group"].with_context(requested_uid=self.env.user)
grouping_keys = self._get_procurement_group_keys()
fields = self._get_fields_for_keys()
for gk in grouping_keys:
@@ -275,8 +273,7 @@ class PullListWizard(models.TransientModel):
group = pg_obj.create(self._prepare_proc_group_values())
proc_groups.append(group.id)
values = self._prepare_procurement_values(
line.date_expected, group)
values = self._prepare_procurement_values(line.date_expected, group)
try:
pg_obj.run(
line.product_id,
@@ -285,7 +282,7 @@ class PullListWizard(models.TransientModel):
line.location_id,
"Pull List %s" % self.id,
"Pull List %s" % self.id,
values
values,
)
except UserError as error:
errors.append(error.name)
@@ -307,28 +304,21 @@ class PullListWizardLine(models.TransientModel):
_name = "stock.pull.list.wizard.line"
_description = "Stock Pull List Wizard Line"
wizard_id = fields.Many2one(
comodel_name="stock.pull.list.wizard",
)
product_id = fields.Many2one(
comodel_name="product.product",
)
location_id = fields.Many2one(
comodel_name="stock.location",
)
wizard_id = fields.Many2one(comodel_name="stock.pull.list.wizard",)
product_id = fields.Many2one(comodel_name="product.product",)
location_id = fields.Many2one(comodel_name="stock.location",)
date_expected = fields.Date()
available_qty = fields.Float()
incoming_qty = fields.Float()
raw_demand_qty = fields.Float()
needed_qty = fields.Float()
stock_rule_id = fields.Many2one(
comodel_name="stock.rule",
)
stock_rule_id = fields.Many2one(comodel_name="stock.rule",)
selected = fields.Boolean(default=True)
def _is_available_in_source_location(self):
if not self.stock_rule_id.location_src_id:
return False
qty_avail = self.wizard_id._get_available_qty(
self.product_id, self.stock_rule_id.location_src_id)
self.product_id, self.stock_rule_id.location_src_id
)
return qty_avail > self.needed_qty

View File

@@ -1,41 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<?xml version="1.0" encoding="utf-8" ?>
<!-- Copyright 2020 ForgeFlow S.L.
License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl). -->
<odoo>
<record id="view_run_stock_pull_list_wizard_wizard" model="ir.ui.view">
<field name="name">stock.pull.list.wizard.form</field>
<field name="model">stock.pull.list.wizard</field>
<field name="arch" type="xml">
<form>
<p>The pull list checks the stock situation at the given location and calculates
<p
>The pull list checks the stock situation at the given location and calculates
the shortfall quantities (quantity needed to cover all needs) for products.</p>
<group>
<field name="warehouse_id" options="{'no_create': True}"/>
<field name="location_id" options="{'no_create': True}"/>
<field name="warehouse_id" options="{'no_create': True}" />
<field name="location_id" options="{'no_create': True}" />
</group>
<p>All existing Stock moves moving outside of the location specified will be considered demand.
<p
>All existing Stock moves moving outside of the location specified will be considered demand.
You can filter these moves in the section below.</p>
<group name="options" string="Filtering">
<group>
<field name="date_to"/>
<field name="location_dest_id" options="{'no_create': True}"/>
<field name="procurement_group_ids" widget="many2many_tags" options="{'no_create': True}"/>
<field name="date_to" />
<field name="location_dest_id" options="{'no_create': True}" />
<field
name="procurement_group_ids"
widget="many2many_tags"
options="{'no_create': True}"
/>
</group>
<group>
<field name="exclude_reserved"/>
<field name="consolidate_by_product"/>
<field name="exclude_reserved" />
<field name="consolidate_by_product" />
</group>
</group>
<footer>
<button name="action_prepare" string="Prepare" type="object" class="oe_highlight"/>
<button
name="action_prepare"
string="Prepare"
type="object"
class="oe_highlight"
/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="view_run_stock_pull_list_wizard_wizard_step_2" model="ir.ui.view">
<field name="name">stock.pull.list.wizard.form.2</field>
<field name="model">stock.pull.list.wizard</field>
@@ -44,51 +53,68 @@
<group string="Needs">
<field name="line_ids" readonly="1" nolabel="1">
<tree>
<field name="wizard_id" invisible="1"/>
<field name="product_id"/>
<field name="location_id"/>
<field name="date_expected"/>
<field name="available_qty"/>
<field name="incoming_qty"/>
<field name="raw_demand_qty"/>
<field name="needed_qty"/>
<field name="stock_rule_id"/>
<field name="selected" widget="toggle_button"/>
<field name="wizard_id" invisible="1" />
<field name="product_id" />
<field name="location_id" />
<field name="date_expected" />
<field name="available_qty" />
<field name="incoming_qty" />
<field name="raw_demand_qty" />
<field name="needed_qty" />
<field name="stock_rule_id" />
<field name="selected" widget="toggle_button" />
</tree>
</field>
</group>
<group string="Filter Selected">
<group>
<field name="consolidate_by_product" invisible="1"/>
<field name="select_all"/>
<field name="rule_action" attrs="{'invisible':[('select_all', '!=', False)]}"/>
<field name="available_in_source_location"
attrs="{'invisible':['|', ('consolidate_by_product', '!=', True), ('select_all', '!=', False)]}"/>
<field name="exclude_reserved"
attrs="{'invisible':['|', ('available_in_source_location', '!=', True), ('select_all', '!=', False)]}"/>
<field name="consolidate_by_product" invisible="1" />
<field name="select_all" />
<field
name="rule_action"
attrs="{'invisible':[('select_all', '!=', False)]}"
/>
<field
name="available_in_source_location"
attrs="{'invisible':['|', ('consolidate_by_product', '!=', True), ('select_all', '!=', False)]}"
/>
<field
name="exclude_reserved"
attrs="{'invisible':['|', ('available_in_source_location', '!=', True), ('select_all', '!=', False)]}"
/>
</group>
<group>
<button name="action_update_selected" string="Apply Filter" type="object" icon="fa-cogs"/>
<button
name="action_update_selected"
string="Apply Filter"
type="object"
icon="fa-cogs"
/>
</group>
</group>
<group name="grouping" string="Split/Grouping Options">
<group>
<field name="max_lines"/>
<field name="max_lines" />
</group>
<group>
<field name="group_by_rule"/>
<field name="group_by_rule" />
</group>
</group>
<footer>
<button name="action_procure" string="Procure" type="object" class="oe_highlight"/>
<button
name="action_procure"
string="Procure"
type="object"
class="oe_highlight"
/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<act_window name="Generate Pull List"
<act_window
name="Generate Pull List"
res_model="stock.pull.list.wizard"
src_model="stock.pull.list.wizard"
view_mode="form"
@@ -96,13 +122,12 @@
key2="client_action_multi"
id="action_stock_pull_list_wizard"
/>
<menuitem name="Generate Pull List"
<menuitem
name="Generate Pull List"
id="menu_stock_pull_list_wizard"
action="action_stock_pull_list_wizard"
parent="stock.menu_stock_warehouse_mgmt"
groups="stock.group_stock_manager"
sequence="90"
/>
</odoo>