Merge PR #1098 into 14.0

Signed-off-by LoisRForgeFlow
This commit is contained in:
OCA-git-bot
2021-03-18 14:46:12 +00:00
19 changed files with 1453 additions and 0 deletions

View File

@@ -0,0 +1 @@
../../../../stock_pull_list

View File

@@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)

107
stock_pull_list/README.rst Normal file
View File

@@ -0,0 +1,107 @@
===============
Stock Pull List
===============
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Alpha-red.png
:target: https://odoo-community.org/page/development-status
:alt: Alpha
.. |badge2| image:: https://img.shields.io/badge/licence-LGPL--3-blue.png
:target: http://www.gnu.org/licenses/lgpl-3.0-standalone.html
:alt: License: LGPL-3
.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github
:target: https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_pull_list
:alt: OCA/stock-logistics-warehouse
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
:target: https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_pull_list
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
:target: https://runbot.odoo-community.org/runbot/153/13.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
The pull list checks the stock situation at the given location and calculates
the shortfall quantities (quantity needed to cover all needs) for products.
Procurements can be created for these shortfall quantities.
.. IMPORTANT::
This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
`More details on development status <https://odoo-community.org/page/development-status>`_
**Table of contents**
.. contents::
:local:
Usage
=====
To use the module follow the next steps:
#. Go to *Inventory > Operations > Generate Pull List*.
#. Select the location to get the pull list from. Add some filtering if needed.
#. Click on Prepare. You will now see the pull list with all the needs.
#. Adjust grouping options as needed. This will generate different procurement
groups.
#. Click on *Procure*.
Known issues / Roadmap
======================
* In wizard, when `exclude_reserved` is selected, handle partially available moves.
* Use sequence numbering for procurement groups made from pull list.
* Return a pull list summary at the end.
Bug Tracker
===========
Bugs are tracked on `GitHub Issues <https://github.com/OCA/stock-logistics-warehouse/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_pull_list%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
Do not contact contributors directly about support or help with technical issues.
Credits
=======
Authors
~~~~~~~
* ForgeFlow
Contributors
~~~~~~~~~~~~
* Lois Rilo <lois.rilo@forgeflow.com>
Maintainers
~~~~~~~~~~~
This module is maintained by the OCA.
.. image:: https://odoo-community.org/logo.png
:alt: Odoo Community Association
:target: https://odoo-community.org
OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
.. |maintainer-LoisRForgeFlow| image:: https://github.com/LoisRForgeFlow.png?size=40px
:target: https://github.com/LoisRForgeFlow
:alt: LoisRForgeFlow
Current `maintainer <https://odoo-community.org/page/maintainer-role>`__:
|maintainer-LoisRForgeFlow|
This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_pull_list>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1 @@
from . import wizards

View File

@@ -0,0 +1,21 @@
# Copyright 2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
{
"name": "Stock Pull List",
"summary": "The pull list checks the stock situation and calculates "
"needed quantities.",
"version": "14.0.1.0.0",
"license": "LGPL-3",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"author": "ForgeFlow, " "Odoo Community Association (OCA)",
"maintainers": ["LoisRForgeFlow"],
"development_status": "Alpha",
"category": "Warehouse Management",
"depends": ["stock", "stock_free_quantity"],
"data": [
"wizards/stock_pull_list_wizard.xml",
"security/ir.model.access.csv",
],
"installable": True,
}

View File

@@ -0,0 +1,269 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_pull_list
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 13.0\n"
"Report-Msgid-Bugs-To: \n"
"Last-Translator: \n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: \n"
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
msgid ""
"All existing Stock moves moving outside of the location specified will be considered demand.\n"
" You can filter these moves in the section below."
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,help:stock_pull_list.field_stock_pull_list_wizard__consolidate_by_product
msgid ""
"All needs for each product will be grouped in one line, disregarding date."
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2
msgid "Apply Filter"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__available_in_source_location
msgid "Available In Source Location"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__available_qty
msgid "Available Qty"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2
msgid "Cancel"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__consolidate_by_product
msgid "Consolidate By Product"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__create_uid
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__create_uid
msgid "Created by"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__create_date
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__create_date
msgid "Created on"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__date_expected
msgid "Date Expected"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__date_to
msgid "Date To"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__location_dest_id
msgid "Destination Location"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__display_name
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__display_name
msgid "Display Name"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__exclude_reserved
msgid "Exclude Reserved"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2
msgid "Filter Selected"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
msgid "Filtering"
msgstr ""
#. module: stock_pull_list
#: model:ir.actions.act_window,name:stock_pull_list.action_stock_pull_list_wizard
#: model:ir.ui.menu,name:stock_pull_list.menu_stock_pull_list_wizard
msgid "Generate Pull List"
msgstr ""
#. module: stock_pull_list
#: code:addons/stock_pull_list/wizards/stock_pull_list_wizard.py:0
#, python-format
msgid "Generated Procurement Groups"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__group_by_rule
msgid "Group By Rule"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__id
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__id
msgid "ID"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__incoming_qty
msgid "Incoming Qty"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard____last_update
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line____last_update
msgid "Last Modified on"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__write_uid
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__write_uid
msgid "Last Updated by"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__write_date
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__write_date
msgid "Last Updated on"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__line_ids
msgid "Line"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__location_id
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__location_id
msgid "Location"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__max_lines
msgid "Max Lines"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__needed_qty
msgid "Needed Qty"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2
msgid "Needs"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
msgid "Prepare"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2
msgid "Procure"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__procurement_group_ids
msgid "Procurement Group"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__product_id
msgid "Product"
msgstr ""
#. module: stock_pull_list
#: code:addons/stock_pull_list/wizards/stock_pull_list_wizard.py:0
#, python-format
msgid "Pull List"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__raw_demand_qty
msgid "Raw Demand Qty"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__rule_action
msgid "Rule Action"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__select_all
msgid "Select All"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,help:stock_pull_list.field_stock_pull_list_wizard__available_in_source_location
msgid ""
"Select only rules with enough available stock in source location. Applies "
"for rules with a source location."
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__selected
msgid "Selected"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2
msgid "Split/Grouping Options"
msgstr ""
#. module: stock_pull_list
#: model:ir.model,name:stock_pull_list.model_stock_pull_list_wizard
msgid "Stock Pull List Wizard"
msgstr ""
#. module: stock_pull_list
#: model:ir.model,name:stock_pull_list.model_stock_pull_list_wizard_line
msgid "Stock Pull List Wizard Line"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__stock_rule_id
msgid "Stock Rule"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
msgid ""
"The pull list checks the stock situation at the given location and calculates\n"
" the shortfall quantities (quantity needed to cover all needs) for products."
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__warehouse_id
msgid "Warehouse"
msgstr ""
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__wizard_id
msgid "Wizard"
msgstr ""
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard_step_2
msgid "or"
msgstr ""

View File

@@ -0,0 +1 @@
* Lois Rilo <lois.rilo@forgeflow.com>

View File

@@ -0,0 +1,3 @@
The pull list checks the stock situation at the given location and calculates
the shortfall quantities (quantity needed to cover all needs) for products.
Procurements can be created for these shortfall quantities.

View File

@@ -0,0 +1,3 @@
* In wizard, when `exclude_reserved` is selected, handle partially available moves.
* Use sequence numbering for procurement groups made from pull list.
* Return a pull list summary at the end.

View File

@@ -0,0 +1,8 @@
To use the module follow the next steps:
#. Go to *Inventory > Operations > Generate Pull List*.
#. Select the location to get the pull list from. Add some filtering if needed.
#. Click on Prepare. You will now see the pull list with all the needs.
#. Adjust grouping options as needed. This will generate different procurement
groups.
#. Click on *Procure*.

View File

@@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_wiz_stock_pull_list_user,access_wiz_stock_pull_list_user,model_stock_pull_list_wizard,stock.group_stock_user,1,1,1,1
access_wiz_stock_pull_list_manager,access_wiz_stock_pull_list_manager,model_stock_pull_list_wizard,stock.group_stock_manager,1,1,1,1
access_wiz_stock_pull_list_line_user,access_wiz_stock_pull_list_line_user,model_stock_pull_list_wizard_line,stock.group_stock_user,1,1,1,1
access_wiz_stock_pull_list_line_manager,access_wiz_stock_pull_list_line_manager,model_stock_pull_list_wizard_line,stock.group_stock_manager,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_wiz_stock_pull_list_user access_wiz_stock_pull_list_user model_stock_pull_list_wizard stock.group_stock_user 1 1 1 1
3 access_wiz_stock_pull_list_manager access_wiz_stock_pull_list_manager model_stock_pull_list_wizard stock.group_stock_manager 1 1 1 1
4 access_wiz_stock_pull_list_line_user access_wiz_stock_pull_list_line_user model_stock_pull_list_wizard_line stock.group_stock_user 1 1 1 1
5 access_wiz_stock_pull_list_line_manager access_wiz_stock_pull_list_line_manager model_stock_pull_list_wizard_line stock.group_stock_manager 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,451 @@
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="generator" content="Docutils 0.15.1: http://docutils.sourceforge.net/" />
<title>Stock Pull List</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 7952 2016-07-26 18:15:59Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See http://docutils.sf.net/docs/howto/html-stylesheets.html for how to
customize this style sheet.
*/
/* used to remove borders from tables and images */
.borderless, table.borderless td, table.borderless th {
border: 0 }
table.borderless td, table.borderless th {
/* Override padding for "table.docutils td" with "! important".
The right padding separates the table cells. */
padding: 0 0.5em 0 0 ! important }
.first {
/* Override more specific margin styles with "! important". */
margin-top: 0 ! important }
.last, .with-subtitle {
margin-bottom: 0 ! important }
.hidden {
display: none }
.subscript {
vertical-align: sub;
font-size: smaller }
.superscript {
vertical-align: super;
font-size: smaller }
a.toc-backref {
text-decoration: none ;
color: black }
blockquote.epigraph {
margin: 2em 5em ; }
dl.docutils dd {
margin-bottom: 0.5em }
object[type="image/svg+xml"], object[type="application/x-shockwave-flash"] {
overflow: hidden;
}
/* Uncomment (and remove this text!) to get bold-faced definition list terms
dl.docutils dt {
font-weight: bold }
*/
div.abstract {
margin: 2em 5em }
div.abstract p.topic-title {
font-weight: bold ;
text-align: center }
div.admonition, div.attention, div.caution, div.danger, div.error,
div.hint, div.important, div.note, div.tip, div.warning {
margin: 2em ;
border: medium outset ;
padding: 1em }
div.admonition p.admonition-title, div.hint p.admonition-title,
div.important p.admonition-title, div.note p.admonition-title,
div.tip p.admonition-title {
font-weight: bold ;
font-family: sans-serif }
div.attention p.admonition-title, div.caution p.admonition-title,
div.danger p.admonition-title, div.error p.admonition-title,
div.warning p.admonition-title, .code .error {
color: red ;
font-weight: bold ;
font-family: sans-serif }
/* Uncomment (and remove this text!) to get reduced vertical space in
compound paragraphs.
div.compound .compound-first, div.compound .compound-middle {
margin-bottom: 0.5em }
div.compound .compound-last, div.compound .compound-middle {
margin-top: 0.5em }
*/
div.dedication {
margin: 2em 5em ;
text-align: center ;
font-style: italic }
div.dedication p.topic-title {
font-weight: bold ;
font-style: normal }
div.figure {
margin-left: 2em ;
margin-right: 2em }
div.footer, div.header {
clear: both;
font-size: smaller }
div.line-block {
display: block ;
margin-top: 1em ;
margin-bottom: 1em }
div.line-block div.line-block {
margin-top: 0 ;
margin-bottom: 0 ;
margin-left: 1.5em }
div.sidebar {
margin: 0 0 0.5em 1em ;
border: medium outset ;
padding: 1em ;
background-color: #ffffee ;
width: 40% ;
float: right ;
clear: right }
div.sidebar p.rubric {
font-family: sans-serif ;
font-size: medium }
div.system-messages {
margin: 5em }
div.system-messages h1 {
color: red }
div.system-message {
border: medium outset ;
padding: 1em }
div.system-message p.system-message-title {
color: red ;
font-weight: bold }
div.topic {
margin: 2em }
h1.section-subtitle, h2.section-subtitle, h3.section-subtitle,
h4.section-subtitle, h5.section-subtitle, h6.section-subtitle {
margin-top: 0.4em }
h1.title {
text-align: center }
h2.subtitle {
text-align: center }
hr.docutils {
width: 75% }
img.align-left, .figure.align-left, object.align-left, table.align-left {
clear: left ;
float: left ;
margin-right: 1em }
img.align-right, .figure.align-right, object.align-right, table.align-right {
clear: right ;
float: right ;
margin-left: 1em }
img.align-center, .figure.align-center, object.align-center {
display: block;
margin-left: auto;
margin-right: auto;
}
table.align-center {
margin-left: auto;
margin-right: auto;
}
.align-left {
text-align: left }
.align-center {
clear: both ;
text-align: center }
.align-right {
text-align: right }
/* reset inner alignment in figures */
div.align-right {
text-align: inherit }
/* div.align-center * { */
/* text-align: left } */
.align-top {
vertical-align: top }
.align-middle {
vertical-align: middle }
.align-bottom {
vertical-align: bottom }
ol.simple, ul.simple {
margin-bottom: 1em }
ol.arabic {
list-style: decimal }
ol.loweralpha {
list-style: lower-alpha }
ol.upperalpha {
list-style: upper-alpha }
ol.lowerroman {
list-style: lower-roman }
ol.upperroman {
list-style: upper-roman }
p.attribution {
text-align: right ;
margin-left: 50% }
p.caption {
font-style: italic }
p.credits {
font-style: italic ;
font-size: smaller }
p.label {
white-space: nowrap }
p.rubric {
font-weight: bold ;
font-size: larger ;
color: maroon ;
text-align: center }
p.sidebar-title {
font-family: sans-serif ;
font-weight: bold ;
font-size: larger }
p.sidebar-subtitle {
font-family: sans-serif ;
font-weight: bold }
p.topic-title {
font-weight: bold }
pre.address {
margin-bottom: 0 ;
margin-top: 0 ;
font: inherit }
pre.literal-block, pre.doctest-block, pre.math, pre.code {
margin-left: 2em ;
margin-right: 2em }
pre.code .ln { color: grey; } /* line numbers */
pre.code, code { background-color: #eeeeee }
pre.code .comment, code .comment { color: #5C6576 }
pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold }
pre.code .literal.string, code .literal.string { color: #0C5404 }
pre.code .name.builtin, code .name.builtin { color: #352B84 }
pre.code .deleted, code .deleted { background-color: #DEB0A1}
pre.code .inserted, code .inserted { background-color: #A3D289}
span.classifier {
font-family: sans-serif ;
font-style: oblique }
span.classifier-delimiter {
font-family: sans-serif ;
font-weight: bold }
span.interpreted {
font-family: sans-serif }
span.option {
white-space: nowrap }
span.pre {
white-space: pre }
span.problematic {
color: red }
span.section-subtitle {
/* font-size relative to parent (h1..h6 element) */
font-size: 80% }
table.citation {
border-left: solid 1px gray;
margin-left: 1px }
table.docinfo {
margin: 2em 4em }
table.docutils {
margin-top: 0.5em ;
margin-bottom: 0.5em }
table.footnote {
border-left: solid 1px black;
margin-left: 1px }
table.docutils td, table.docutils th,
table.docinfo td, table.docinfo th {
padding-left: 0.5em ;
padding-right: 0.5em ;
vertical-align: top }
table.docutils th.field-name, table.docinfo th.docinfo-name {
font-weight: bold ;
text-align: left ;
white-space: nowrap ;
padding-left: 0 }
/* "booktabs" style (no vertical lines) */
table.docutils.booktabs {
border: 0px;
border-top: 2px solid;
border-bottom: 2px solid;
border-collapse: collapse;
}
table.docutils.booktabs * {
border: 0px;
}
table.docutils.booktabs th {
border-bottom: thin solid;
text-align: left;
}
h1 tt.docutils, h2 tt.docutils, h3 tt.docutils,
h4 tt.docutils, h5 tt.docutils, h6 tt.docutils {
font-size: 100% }
ul.auto-toc {
list-style-type: none }
</style>
</head>
<body>
<div class="document" id="stock-pull-list">
<h1 class="title">Stock Pull List</h1>
<!-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external" href="https://odoo-community.org/page/development-status"><img alt="Alpha" src="https://img.shields.io/badge/maturity-Alpha-red.png" /></a> <a class="reference external" href="http://www.gnu.org/licenses/lgpl-3.0-standalone.html"><img alt="License: LGPL-3" src="https://img.shields.io/badge/licence-LGPL--3-blue.png" /></a> <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_pull_list"><img alt="OCA/stock-logistics-warehouse" src="https://img.shields.io/badge/github-OCA%2Fstock--logistics--warehouse-lightgray.png?logo=github" /></a> <a class="reference external" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-13-0/stock-logistics-warehouse-13-0-stock_pull_list"><img alt="Translate me on Weblate" src="https://img.shields.io/badge/weblate-Translate%20me-F47D42.png" /></a> <a class="reference external" href="https://runbot.odoo-community.org/runbot/153/13.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<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.
Procurements can be created for these shortfall quantities.</p>
<div class="admonition important">
<p class="first admonition-title">Important</p>
<p class="last">This is an alpha version, the data model and design can change at any time without warning.
Only for development or testing purpose, do not use in production.
<a class="reference external" href="https://odoo-community.org/page/development-status">More details on development status</a></p>
</div>
<p><strong>Table of contents</strong></p>
<div class="contents local topic" id="contents">
<ul class="simple">
<li><a class="reference internal" href="#usage" id="id1">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="id2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="id3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="id4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="id5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="id6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="id7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#id1">Usage</a></h1>
<p>To use the module follow the next steps:</p>
<ol class="arabic simple">
<li>Go to <em>Inventory &gt; Operations &gt; Generate Pull List</em>.</li>
<li>Select the location to get the pull list from. Add some filtering if needed.</li>
<li>Click on Prepare. You will now see the pull list with all the needs.</li>
<li>Adjust grouping options as needed. This will generate different procurement
groups.</li>
<li>Click on <em>Procure</em>.</li>
</ol>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id2">Known issues / Roadmap</a></h1>
<ul class="simple">
<li>In wizard, when <cite>exclude_reserved</cite> is selected, handle partially available moves.</li>
<li>Use sequence numbering for procurement groups made from pull list.</li>
<li>Return a pull list summary at the end.</li>
</ul>
</div>
<div class="section" id="bug-tracker">
<h1><a class="toc-backref" href="#id3">Bug Tracker</a></h1>
<p>Bugs are tracked on <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues">GitHub Issues</a>.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed
<a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_pull_list%0Aversion:%2013.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**">feedback</a>.</p>
<p>Do not contact contributors directly about support or help with technical issues.</p>
</div>
<div class="section" id="credits">
<h1><a class="toc-backref" href="#id4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>ForgeFlow</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>Lois Rilo &lt;<a class="reference external" href="mailto:lois.rilo&#64;forgeflow.com">lois.rilo&#64;forgeflow.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">
<h2><a class="toc-backref" href="#id7">Maintainers</a></h2>
<p>This module is maintained by the OCA.</p>
<a class="reference external image-reference" href="https://odoo-community.org"><img alt="Odoo Community Association" src="https://odoo-community.org/logo.png" /></a>
<p>OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.</p>
<p>Current <a class="reference external" href="https://odoo-community.org/page/maintainer-role">maintainer</a>:</p>
<p><a class="reference external" href="https://github.com/LoisRForgeFlow"><img alt="LoisRForgeFlow" src="https://github.com/LoisRForgeFlow.png?size=40px" /></a></p>
<p>This module is part of the <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/13.0/stock_pull_list">OCA/stock-logistics-warehouse</a> project on GitHub.</p>
<p>You are welcome to contribute. To learn how please visit <a class="reference external" href="https://odoo-community.org/page/Contribute">https://odoo-community.org/page/Contribute</a>.</p>
</div>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1 @@
from . import test_stock_pull_list

View File

@@ -0,0 +1,81 @@
# Copyright 2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
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"]
self.move_obj = self.env["stock.move"]
self.picking_obj = self.env["stock.picking"]
self.wiz_obj = self.env["stock.pull.list.wizard"]
self.company = self.env.ref("base.main_company")
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"}
)
route_vals = {
"name": "WH2 -> WH",
}
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,
"action": "pull_push",
"warehouse_id": self.warehouse.id,
"propagate_warehouse_id": self.warehouse_2.id,
"picking_type_id": self.env.ref("stock.picking_type_internal").id,
"name": "WH2->WH",
"route_id": self.transfer_route.id,
"delay": 1,
}
self.transfer_rule = self.env["stock.rule"].create(rule_vals)
self.product_a.route_ids = [(6, 0, self.transfer_route.ids)]
# Dates:
self.today = fields.Datetime.today()
self.yesterday = self.today - td(days=1)
self.date_3 = self.today + td(days=3)
def _generate_moves(self):
self.create_picking_out_a(self.yesterday, 50)
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,
"scheduled_date": date_move,
"move_lines": [
(
0,
0,
{
"name": "Test move",
"product_id": self.product_a.id,
"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

@@ -0,0 +1,32 @@
# Copyright 2020 ForgeFlow, S.L.
# License LGPL-3.0 or later (https://www.gnu.org/licenses/lgpl.html).
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 == 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 == 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.action_prepare()
line = wiz.line_ids.filtered(lambda l: l.product_id == self.product_a)
self.assertEqual(len(line), 1)
self.assertEqual(line.date, self.today.date())
expected = 50 + 70
self.assertEqual(line.raw_demand_qty, expected)
self.assertEqual(line.needed_qty, expected)

View File

@@ -0,0 +1 @@
from . import stock_pull_list_wizard

View File

@@ -0,0 +1,331 @@
# 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
from odoo.tools import float_compare
class PullListWizard(models.TransientModel):
_name = "stock.pull.list.wizard"
_description = "Stock Pull List Wizard"
@api.model
def default_get(self, fields):
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})
return res
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",
readonly=True,
)
# Step 1 - filtering options.
exclude_reserved = fields.Boolean()
location_dest_id = fields.Many2one(
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")
# Step 2 - filtering options.
select_all = fields.Boolean(default=True)
rule_action = fields.Selection(
selection=lambda self: self.env["stock.rule"]._fields["action"].selection,
)
available_in_source_location = fields.Boolean(
help="Select only rules with enough available stock in source "
"location. Applies for rules with a source location.",
)
# Step 2 - grouping options.
max_lines = fields.Integer()
group_by_rule = fields.Boolean()
def _get_moves_demand_domain(self):
self.ensure_one()
domain = [
("location_id", "child_of", self.location_id.id),
("state", "not in", ("draft", "done", "cancel")),
]
if self.location_dest_id:
domain.append(("location_dest_id", "=", self.location_dest_id.id))
if self.exclude_reserved:
domain.append(("state", "not in", ("assigned",)))
if self.date_to:
domain.append(("date", "<=", self.date_to))
if self.procurement_group_ids:
domain.append(("group_id", "in", self.procurement_group_ids.ids))
return domain
def _get_moves_incoming_domain(self):
self.ensure_one()
domain = [
("location_dest_id", "child_of", self.location_id.id),
("state", "not in", ("draft", "done", "cancel")),
]
if self.date_to:
domain.append(("date", "<=", self.date_to))
return domain
@api.model
def _prepare_line_values(self, key, demand_qty, supply_qty):
product, location, date = key
rule = self._get_stock_rule_id(product, location)
global qty_assigned
prev = qty_assigned.setdefault(product, 0.0)
qty_available = self._get_available_qty(product, location) - prev
need_without_stock = max(demand_qty - supply_qty, 0.0)
qty_assigned_now = min(qty_available, need_without_stock)
qty_needed = max(demand_qty - qty_available - supply_qty, 0.0)
qty_assigned[product] = prev + qty_assigned_now
return {
"product_id": product.id if product else False,
"location_id": location.id if location else False,
"date": date,
"stock_rule_id": rule.id if rule else False,
"raw_demand_qty": demand_qty,
"available_qty": qty_available,
"incoming_qty": supply_qty,
"needed_qty": qty_needed,
}
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
)
if self.exclude_reserved:
return product_l.free_qty
return product_l.qty_available
@api.model
def _get_stock_rule_id(self, product_id, location_id):
values = {
"warehouse_id": self.warehouse_id,
"company_id": self.env.user.company_id,
}
stock_rule_id = self.env["procurement.group"]._get_rule(
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 asc")
demand_dict = {}
force_date = fields.Date.today() if self.consolidate_by_product else False
for demand in demand_moves:
key = (
demand.product_id,
demand.location_id,
fields.Date.to_date(demand.date) if not force_date else force_date,
)
prev = demand_dict.setdefault(key, 0.0)
# TODO: when exclude_reserved is selected, handle partially avail.
demand_dict[key] = prev + demand.product_uom_qty
domain = self._get_moves_incoming_domain()
incoming_moves = self.env["stock.move"].search(domain, order="date 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 >= supply.date
)
if move_for_date:
date_selected = move_for_date[0].date 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,
fields.Date.to_date(date_selected),
)
prev = incoming_dict.setdefault(key, 0.0)
incoming_dict[key] = prev + supply.product_uom_qty
lines = []
global qty_assigned
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})
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
res = {
"name": _("Pull List"),
"src_model": "stock.pull.list.wizard",
"view_type": "form",
"view_mode": "form",
"view_id": view_id,
"target": "new",
"res_model": "stock.pull.list.wizard",
"res_id": self.id,
"type": "ir.actions.act_window",
}
return res
def action_update_selected(self):
for line in self.line_ids:
if self.select_all:
line.selected = True
continue
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:
available = True
if rule_invalid or not available:
line.selected = False
else:
line.selected = True
# The wizard must be reloaded in order to show the new product lines
res = self._act_window_pull_list_step_2()
return res
def _prepare_procurement_values(self, date, group):
values = {
"date_planned": date,
"warehouse_id": self.warehouse_id,
"company_id": self.env.user.company_id,
"group_id": group,
}
return values
def _get_fields_for_keys(self):
fields = []
if self.group_by_rule:
fields.append("stock_rule_id")
return fields
def _get_procurement_group_keys(self):
fields = self._get_fields_for_keys()
if not fields:
return [False]
options_list = []
for f in fields:
# Many2many only field type supported. As more needs arise, this
# can be extended
options_list.append(self.line_ids.mapped(f).ids)
return list(itertools.product(*options_list))
def _prepare_proc_group_values(self):
# TODO: use special secuence to name procurement groups of pull lists.
return {}
def action_procure(self):
self.ensure_one()
lines_obj = self.env["stock.pull.list.wizard.line"]
errors = []
proc_groups = []
pg_obj = self.env["procurement.group"]
grouping_keys = self._get_procurement_group_keys()
fields = self._get_fields_for_keys()
for gk in grouping_keys:
domain = [("wizard_id", "=", self.id), ("needed_qty", ">", 0.0)]
for i, f in enumerate(fields):
domain.append((f, "=", gk[i]))
n = 0
lines = lines_obj.search(domain)
if not lines:
continue
group = pg_obj.create(self._prepare_proc_group_values())
proc_groups.append(group.id)
procurements = []
for line in lines.filtered(lambda l: l.selected):
n += 1
if 0 < self.max_lines < n:
n = 0
group = pg_obj.create(self._prepare_proc_group_values())
proc_groups.append(group.id)
values = self._prepare_procurement_values(line.date, group)
procurements.append(
pg_obj.Procurement(
line.product_id,
line.needed_qty,
line.product_id.uom_id,
line.location_id,
"Pull List %s" % self.id,
"Pull List %s" % self.id,
self.env.user.company_id,
values,
)
)
# Run procurements
try:
pg_obj.run(procurements)
except UserError as error:
errors.append(error.name)
if errors:
raise UserError("\n".join(errors))
res = {
"name": _("Generated Procurement Groups"),
"src_model": "stock.pull.list.wizard",
"view_type": "form",
"view_mode": "tree,form",
"res_model": "procurement.group",
"type": "ir.actions.act_window",
"domain": str([("id", "in", proc_groups)]),
}
return res
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",
)
date = fields.Date()
available_qty = fields.Float(digits="Product Unit of Measure")
incoming_qty = fields.Float(digits="Product Unit of Measure")
raw_demand_qty = fields.Float(digits="Product Unit of Measure")
needed_qty = fields.Float(digits="Product Unit of Measure")
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
)
return float_compare(qty_avail, self.needed_qty, precision_digits=2) > 0

View File

@@ -0,0 +1,131 @@
<!-- 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
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}" />
</group>
<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}"
/>
</group>
<group>
<field name="exclude_reserved" />
<field name="consolidate_by_product" />
</group>
</group>
<footer>
<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>
<field name="arch" type="xml">
<form>
<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" />
<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)]}"
/>
</group>
<group>
<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" />
</group>
<group>
<field name="group_by_rule" />
</group>
</group>
<footer>
<button
name="action_procure"
string="Procure"
type="object"
class="oe_highlight"
/>
or
<button string="Cancel" class="oe_link" special="cancel" />
</footer>
</form>
</field>
</record>
<record id="action_stock_pull_list_wizard" model="ir.actions.act_window">
<field name="name">Generate Pull List</field>
<field name="res_model">stock.pull.list.wizard</field>
<field name="binding_model_id" ref="model_stock_pull_list_wizard" />
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<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>