mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[IMP] : black, isort, prettier
This commit is contained in:
committed by
Lois Rilo
parent
b9bb4016f1
commit
ba251eb05a
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user