Merge PR #2093 into 16.0

Signed-off-by JordiBForgeFlow
This commit is contained in:
OCA-git-bot
2024-07-02 08:52:12 +00:00
26 changed files with 1944 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,
)

110
stock_pull_list/README.rst Normal file
View File

@@ -0,0 +1,110 @@
===============
Stock Pull List
===============
..
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:5866967730daa2fba1551d9a481e2b57951921208c66b4ec1bacec6bd6982cb5
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
:target: https://odoo-community.org/page/development-status
:alt: Beta
.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
:target: http://www.gnu.org/licenses/agpl-3.0-standalone.html
:alt: License: AGPL-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/16.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-16-0/stock-logistics-warehouse-16-0-stock_pull_list
:alt: Translate me on Weblate
.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png
:target: https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&target_branch=16.0
:alt: Try me on Runboat
|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.
**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*.
Wizard can be also launched through a server action on multiple transfers; this is to allow user to select with ease which transfers to include in the pull list. Please note that:
#. To select a transfer to be included in pull list through server action, you first need to go to its operation type and enable field "Allow pull list server action".
#. Only transfers with the same Source Location can be selected at one time.
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 to smash it by providing a detailed and welcomed
`feedback <https://github.com/OCA/stock-logistics-warehouse/issues/new?body=module:%20stock_pull_list%0Aversion:%2016.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/16.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,2 @@
from . import wizards
from . import models

View File

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

View File

@@ -0,0 +1,11 @@
<odoo>
<record model="ir.actions.server" id="stock_generate_pull_list">
<field name="name">Generate Pull List</field>
<field name="model_id" ref="stock.model_stock_picking" />
<field name="binding_model_id" ref="stock.model_stock_picking" />
<field name="state">code</field>
<field name="code">
action = records.action_create_pull_list()
</field>
</record>
</odoo>

View File

@@ -0,0 +1,9 @@
<odoo noupdate="1">
<record id="seq_stock_pull_list" model="ir.sequence">
<field name="name">Stock Pull List</field>
<field name="code">stock.pull.list</field>
<field name="prefix">SPL/</field>
<field name="padding">6</field>
<field name="company_id" eval="False" />
</record>
</odoo>

322
stock_pull_list/i18n/it.po Normal file
View File

@@ -0,0 +1,322 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_pull_list
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2023-11-24 11:33+0000\n"
"Last-Translator: Francesco Foresti <francesco.foresti@ooops404.com>\n"
"Language-Team: none\n"
"Language: it\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: \n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Weblate 4.17\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 ""
"Tutti i movimenti di magazzino esistenti in uscita dall'ubicazione "
"specificata verranno considerati fabbisogno.\n"
" Puoi filtrare questi movimenti nella sezione qui sotto."
#. 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 ""
"Tutto il fabbisogno per ogni prodotto verrà raggruppato in una singola riga, "
"ignorando la data."
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type__allow_pull_list_server_action
msgid "Allow pull list server action"
msgstr "Permetti azione del server per lista prelevamento"
#. 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 "Applica filtro"
#. 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 "Disponibile nell'ubicazione di origine"
#. 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 "Q.tà disponibile"
#. 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 "Annulla"
#. module: stock_pull_list
#: code:addons/stock_pull_list/models/stock_picking.py:0
#, python-format
msgid "Choose transfers with same source location"
msgstr "Scegli dei trasferimenti con la stessa ubicazione di origine"
#. 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 "Consolida per prodotto"
#. 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 "Creato da"
#. 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 "Creato il"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__date
msgid "Date"
msgstr "Data"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__date_to
msgid "Date To"
msgstr "Alla data"
#. 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 "Ubicazione di destinazione"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking__display_name
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type__display_name
#: 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 "Nome visualizzato"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__exclude_reserved
msgid "Exclude Reserved"
msgstr "Escludi prenotati"
#. 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 "Filtra selezionato"
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
msgid "Filtering"
msgstr "Filtro"
#. module: stock_pull_list
#: model:ir.actions.act_window,name:stock_pull_list.action_stock_pull_list_wizard
#: model:ir.actions.server,name:stock_pull_list.stock_generate_pull_list
#: model:ir.ui.menu,name:stock_pull_list.menu_stock_pull_list_wizard
msgid "Generate Pull List"
msgstr "Genera lista prelevamento"
#. module: stock_pull_list
#: code:addons/stock_pull_list/wizards/stock_pull_list_wizard.py:0
#, python-format
msgid "Generated Procurement Groups"
msgstr "Gruppi di approvvigionamento generati"
#. 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 "Regola raggruppa per"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking__id
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type__id
#: 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 "ID"
#. 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 "Q.tà in entrata"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking____last_update
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type____last_update
#: 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 "Ultima modifica il"
#. 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 "Ultimo aggiornamento di"
#. 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 "Ultimo aggiornamento il"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__line_ids
msgid "Line"
msgstr "Riga"
#. 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 "Ubicazione"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__max_lines
msgid "Max Lines"
msgstr "Max righe"
#. 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 "Q.tà necessaria"
#. 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 "Necessario"
#. module: stock_pull_list
#: code:addons/stock_pull_list/models/stock_picking.py:0
#, python-format
msgid "Operation type of %(name)s transfer did not handle server action"
msgstr ""
"Il tipo di operazione del trasferimento %(name)s non ha gestito l'azione del "
"server"
#. module: stock_pull_list
#: model:ir.model,name:stock_pull_list.model_stock_picking_type
msgid "Picking Type"
msgstr "Tipologia prelievo"
#. module: stock_pull_list
#: model_terms:ir.ui.view,arch_db:stock_pull_list.view_run_stock_pull_list_wizard_wizard
msgid "Prepare"
msgstr "Prepara"
#. 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 "Approvvigiona"
#. 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 "Gruppo di approvvigionamento"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__product_id
msgid "Product"
msgstr "Prodotto"
#. module: stock_pull_list
#: code:addons/stock_pull_list/wizards/stock_pull_list_wizard.py:0
#, python-format
msgid "Pull List"
msgstr "Lista prelevamento"
#. 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 "Q.tà richiesta grezza"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__rule_action
msgid "Rule Action"
msgstr "Azione regola"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__select_all
msgid "Select All"
msgstr "Seleziona tutto"
#. 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 ""
"Seleziona solo regole con quantità disponibile sufficiente nell'ubicazione "
"d'origine. Si applica alle regole con un'ubicazione di origine."
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__selected
msgid "Selected"
msgstr "Selezionato"
#. 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 "Opzioni dividi/raggruppa"
#. module: stock_pull_list
#: model:ir.model,name:stock_pull_list.model_stock_pull_list_wizard
msgid "Stock Pull List Wizard"
msgstr "Procedura guidata lista di prelevamento"
#. module: stock_pull_list
#: model:ir.model,name:stock_pull_list.model_stock_pull_list_wizard_line
msgid "Stock Pull List Wizard Line"
msgstr "Riga procedura guidata lista di prelevamento"
#. 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 "Regola di giacenza"
#. 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 ""
"La lista di prelevamento verifica la situazione delle giacenze in una data "
"ubicazione e calcola\n"
" le quantità mancanti (quantità necessarie per coprire le "
"richieste) per i prodotti."
#. module: stock_pull_list
#: model:ir.model,name:stock_pull_list.model_stock_picking
msgid "Transfer"
msgstr "Trasferimento"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard__warehouse_id
msgid "Warehouse"
msgstr "Magazzino"
#. module: stock_pull_list
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_pull_list_wizard_line__wizard_id
msgid "Wizard"
msgstr "Procedura guidata"
#. 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 "o"

View File

@@ -0,0 +1,303 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_pull_list
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 14.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:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type__allow_pull_list_server_action
msgid "Allow pull list server action"
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
#: code:addons/stock_pull_list/models/stock_picking.py:0
#, python-format
msgid "Choose transfers with same source location"
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
msgid "Date"
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_picking__display_name
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type__display_name
#: 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.actions.server,name:stock_pull_list.stock_generate_pull_list
#: 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_picking__id
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type__id
#: 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_picking____last_update
#: model:ir.model.fields,field_description:stock_pull_list.field_stock_picking_type____last_update
#: 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
#: code:addons/stock_pull_list/models/stock_picking.py:0
#, python-format
msgid "Operation type of %(name)s transfer did not handle server action"
msgstr ""
#. module: stock_pull_list
#: model:ir.model,name:stock_pull_list.model_stock_picking_type
msgid "Picking Type"
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,name:stock_pull_list.model_stock_picking
msgid "Transfer"
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,2 @@
from . import stock_picking
from . import stock_picking_type

View File

@@ -0,0 +1,29 @@
# Copyright 2020-24 ForgeFlow, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import _, fields, models
from odoo.exceptions import UserError
class StockPicking(models.Model):
_inherit = "stock.picking"
def action_create_pull_list(self):
source_location = fields.first(self).location_id
for record in self:
if source_location != record.location_id:
raise UserError(_("Choose transfers with same source location"))
if not record.picking_type_id.allow_pull_list_server_action:
raise UserError(
_(
"Operation type of %(name)s transfer does not allow "
"pull list server action.",
name=record.name,
)
)
pull_wizard = self.env["stock.pull.list.wizard"].create(
{"location_id": source_location.id}
)
res = pull_wizard.action_prepare()
return res

View File

@@ -0,0 +1,10 @@
# Copyright 2020-24 ForgeFlow, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class StockPickingType(models.Model):
_inherit = "stock.picking.type"
allow_pull_list_server_action = fields.Boolean(default=False)

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,13 @@
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*.
Wizard can be also launched through a server action on multiple transfers; this is to allow user to select with ease which transfers to include in the pull list. Please note that:
#. To select a transfer to be included in pull list through server action, you first need to go to its operation type and enable field "Allow pull list server action".
#. Only transfers with the same Source Location can be selected at one time.

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 @@
<!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: https://docutils.sourceforge.io/" />
<title>Stock Pull List</title>
<style type="text/css">
/*
:Author: David Goodger (goodger@python.org)
:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $
:Copyright: This stylesheet has been placed in the public domain.
Default cascading style sheet for the HTML output of Docutils.
See https://docutils.sourceforge.io/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. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:5866967730daa2fba1551d9a481e2b57951921208c66b4ec1bacec6bd6982cb5
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
<p><a class="reference external image-reference" href="https://odoo-community.org/page/development-status"><img alt="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external image-reference" href="http://www.gnu.org/licenses/agpl-3.0-standalone.html"><img alt="License: AGPL-3" src="https://img.shields.io/badge/licence-AGPL--3-blue.png" /></a> <a class="reference external image-reference" href="https://github.com/OCA/stock-logistics-warehouse/tree/16.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 image-reference" href="https://translation.odoo-community.org/projects/stock-logistics-warehouse-16-0/stock-logistics-warehouse-16-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 image-reference" href="https://runboat.odoo-community.org/builds?repo=OCA/stock-logistics-warehouse&amp;target_branch=16.0"><img alt="Try me on Runboat" src="https://img.shields.io/badge/runboat-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>
<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="toc-entry-1">Usage</a></li>
<li><a class="reference internal" href="#known-issues-roadmap" id="toc-entry-2">Known issues / Roadmap</a></li>
<li><a class="reference internal" href="#bug-tracker" id="toc-entry-3">Bug Tracker</a></li>
<li><a class="reference internal" href="#credits" id="toc-entry-4">Credits</a><ul>
<li><a class="reference internal" href="#authors" id="toc-entry-5">Authors</a></li>
<li><a class="reference internal" href="#contributors" id="toc-entry-6">Contributors</a></li>
<li><a class="reference internal" href="#maintainers" id="toc-entry-7">Maintainers</a></li>
</ul>
</li>
</ul>
</div>
<div class="section" id="usage">
<h1><a class="toc-backref" href="#toc-entry-1">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>
<p>Wizard can be also launched through a server action on multiple transfers; this is to allow user to select with ease which transfers to include in the pull list. Please note that:</p>
<ol class="arabic simple">
<li>To select a transfer to be included in pull list through server action, you first need to go to its operation type and enable field “Allow pull list server action”.</li>
<li>Only transfers with the same Source Location can be selected at one time.</li>
</ol>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#toc-entry-2">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="#toc-entry-3">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 to smash 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:%2016.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="#toc-entry-4">Credits</a></h1>
<div class="section" id="authors">
<h2><a class="toc-backref" href="#toc-entry-5">Authors</a></h2>
<ul class="simple">
<li>ForgeFlow</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#toc-entry-6">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="#toc-entry-7">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 image-reference" 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/16.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,94 @@
# Copyright 2020 ForgeFlow, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.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.stock_change_obj = self.env["stock.change.product.qty"]
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.route"].create(route_vals)
rule_vals = {
"location_src_id": self.warehouse_2.lot_stock_id.id,
"location_dest_id": self.warehouse.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_ids": [
(
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
def _update_product_qty(self, product, quantity):
"""Update Product quantity."""
change_product_qty = self.stock_change_obj.create(
{
"product_id": product.id,
"product_tmpl_id": product.product_tmpl_id.id,
"new_quantity": quantity,
}
)
change_product_qty.change_product_qty()
return change_product_qty

View File

@@ -0,0 +1,66 @@
# Copyright 2020 ForgeFlow, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html).
from odoo.exceptions import UserError
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)
def test_03_no_needed_qty(self):
"""Tests that no line is created in the wizard if there's no
quantity needed."""
quantity = 120.00
self._update_product_qty(self.product_a, quantity)
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), 0)
def test_04_server_action(self):
"""Tests work of generate pull list server action
first without allow_pull_list_server_action flag,
after this check Raise of UserError when 2 different locations"""
self._generate_moves()
picking = self.picking_obj.search(
[("location_id", "=", self.warehouse.lot_stock_id.id)]
)
self.assertRaises(UserError, picking.action_create_pull_list)
picking.picking_type_id.update({"allow_pull_list_server_action": True})
picking.action_create_pull_list()
wizard = self.env["stock.pull.list.wizard"].search([])
lines = wizard.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)
picking[0].update({"location_id": self.customer_loc.id})
self.assertRaises(UserError, picking.action_create_pull_list)

View File

@@ -0,0 +1,12 @@
<odoo>
<record id="view_picking_type_form" model="ir.ui.view">
<field name="name">view.picking.type.form</field>
<field name="model">stock.picking.type</field>
<field name="inherit_id" ref="stock.view_picking_type_form" />
<field name="arch" type="xml">
<xpath expr="//field[@name='show_reserved']" position="after">
<field name="allow_pull_list_server_action" />
</xpath>
</field>
</record>
</odoo>

View File

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

View File

@@ -0,0 +1,334 @@
# Copyright 2020 ForgeFlow, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.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)
line_data = self._prepare_line_values(key, demand_qty, supply_qty)
if line_data["needed_qty"] > 0.0:
lines.append((0, 0, line_data))
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):
vals = {}
name = self.env["ir.sequence"].next_by_code("stock.pull.list")
if name:
vals["name"] = name
return vals
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 AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -->
<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" colspan="2">
<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>