Merge PR #1023 into 14.0

Signed-off-by pedrobaeza
This commit is contained in:
OCA-git-bot
2020-12-04 09:44:29 +00:00
28 changed files with 2418 additions and 0 deletions

View File

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

View File

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

View File

@@ -0,0 +1,119 @@
===================
Move Stock Location
===================
.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! This file is generated by oca-gen-addon-readme !!
!! changes will be overwritten. !!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
.. |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/14.0/stock_move_location
: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-14-0/stock-logistics-warehouse-14-0-stock_move_location
: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/14.0
:alt: Try me on Runbot
|badge1| |badge2| |badge3| |badge4| |badge5|
This module allows to move entire location of products from one place to
another and move only selected quantities.
**Table of contents**
.. contents::
:local:
Usage
=====
* A new menu item Stock > Move from location... opens a wizard
where 2 locations can be specified.
* Select origin and destination locations and press "IMMEDIATE TRANSFER" or "PLANNED TRANSFER"
* Press `ADD ALL` button to add all products available
* Those lines can be edited. Move quantity can't be more than a max available quantity
* Move doesn't care about the reservations and will move stuff anyway
* If during your operation with the wizard the real quantity will change
it will move only the available quantity at the button press
* Products will be moved and a form view of picking that did that will show up
* If "PLANNED TRANSFER" is used - the picking won't be validated automatically
If you want to transfer a full quant:
* Go to `Inventory > Master Data > Products` and click "On hand" smart button
or `Inventory > Reporting > Inventory`, the quants view will be
opened.
* Select the quantities which you want move to another location
If you go to the Inventory Dashboard you can see the button "Move from location"
in each of the picking types (only applicable to internal transfers). Press it
and you will be directed to the wizard.
Known issues / Roadmap
======================
Change the current implementation (suggested by Denis Roussel from ACSONE):
* A new parameter on stock picking types : 'Product Change Location' (with a little help).
* With this, go to the dashboard, create a picking with that type.
* Add a button on the picking form which is visible with that type that fill in the picking as now
* Nice to have: add a magic button on locations that with context creates a new picking of that type with the origin location already filled in.
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_move_location%0Aversion:%2014.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
~~~~~~~
* Julius Network Solutions
Contributors
~~~~~~~~~~~~
* Mathieu Vatel <mathieu@julius.fr>
* Mykhailo Panarin <m.panarin@mobilunity.com>
* Joan Sisquella <joan.sisquella@forgeflow.com>
* Jordi Ballester Alomar <jordi.ballester@forgeflow.com>
* Lois Rilo <lois.rilo@forgeflow.com>
* Héctor Villarreal <hector.villarreal@forgeflow.com>
* Tecnativa <tecnativa.com>
* Sergio Teruel
* João Marques
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.
This module is part of the `OCA/stock-logistics-warehouse <https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_move_location>`_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

View File

@@ -0,0 +1,7 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from . import wizard
from . import models
from .init_hook import enable_multi_locations

View File

@@ -0,0 +1,23 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# Copyright 2020 Tecnativa - João Marques
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
{
"name": "Move Stock Location",
"version": "14.0.1.0.0",
"author": "Julius Network Solutions, Odoo Community Association (OCA)",
"summary": "This module allows to move all stock "
"in a stock location to an other one.",
"website": "https://github.com/OCA/stock-logistics-warehouse",
"license": "AGPL-3",
"depends": ["stock"],
"category": "Stock",
"data": [
"data/stock_quant_view.xml",
"security/ir.model.access.csv",
"views/stock_picking_type_views.xml",
"wizard/stock_move_location.xml",
],
"post_init_hook": "enable_multi_locations",
}

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- To move quants directly from qants views -->
<record id="wiz_stock_quant_location_action" model="ir.actions.act_window">
<field name="name">Move to location...</field>
<field name="res_model">wiz.stock.move.location</field>
<field name="binding_model_id" ref="stock.model_stock_quant" />
<field name="view_mode">form</field>
<field name="context" eval="{'origin_location_disable': True}" />
<field name="target">new</field>
<field name="groups_id" eval="[(4, ref('stock.group_stock_user'))]" />
</record>
</odoo>

View File

@@ -0,0 +1,264 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_move_location
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"PO-Revision-Date: 2019-10-25 14:33+0000\n"
"Last-Translator: Stefan Wild <stefan.wild@sewisoft.de>\n"
"Language-Team: none\n"
"Language: de\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 3.8\n"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__apply_putaway_strategy
msgid "Apply putaway strategy"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Apply putaway strategy for moving products"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Cancel"
msgstr "Abbrechen"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__picking_id
msgid "Connected Picking"
msgstr "Verbundene Bewegung"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__create_uid
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__create_uid
msgid "Created by"
msgstr "Erstellt von"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__create_date
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__create_date
msgid "Created on"
msgstr "Erstellt am"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__custom
msgid "Custom line"
msgstr "Angepasste Zeile"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__destination_location_id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__destination_location_id
msgid "Destination Location"
msgstr "Ziellagerort"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__destination_location_disable
#, fuzzy
msgid "Destination Location Disable"
msgstr "Ziellagerort"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__display_name
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__display_name
msgid "Display Name"
msgstr "Anzeigename"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__edit_locations
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
#, fuzzy
msgid "Edit Locations"
msgstr "Quelllagerort"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__id
msgid "ID"
msgstr "ID"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Immediate Transfer"
msgstr "Sofortige Lieferung"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Inventory Details"
msgstr "Bestand Details"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location____last_update
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line____last_update
msgid "Last Modified on"
msgstr "Zuletzt geändert am"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__write_uid
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__write_uid
msgid "Last Updated by"
msgstr "Zuletzt geändert von"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__write_date
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__write_date
msgid "Last Updated on"
msgstr "Zuletzt geändert am"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__lot_id
msgid "Lot/Serial Number"
msgstr "Lot/Seriennummer"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__max_quantity
msgid "Maximum available quantity"
msgstr "Maximal verfügbare Menge"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__stock_move_location_line_ids
msgid "Move Location lines"
msgstr "Lagerort Buchungszeilen"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.stock_picking_type_kanban
msgid "Move On Hand"
msgstr ""
#. module: stock_move_location
#: model:ir.actions.act_window,name:stock_move_location.wiz_stock_move_location_action
#: model:ir.ui.menu,name:stock_move_location.menuitem_move_location
msgid "Move from location..."
msgstr "Von Lagerort bewegen..."
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__move_location_wizard_id
msgid "Move location Wizard"
msgstr "Umlagerungsassistent"
#. module: stock_move_location
#: code:addons/stock_move_location/wizard/stock_move_location_line.py:0
#, python-format
msgid "Move quantity can not exceed max quantity or be negative"
msgstr "Die Menge darf nicht die Bestandsmenge überschreiten oder negativ sein"
#. module: stock_move_location
#: model:ir.actions.act_window,name:stock_move_location.wiz_stock_quant_location_action
msgid "Move to location..."
msgstr "Zu Lagerort bewegen..."
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__origin_location_id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__origin_location_id
msgid "Origin Location"
msgstr "Quelllagerort"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__origin_location_disable
#, fuzzy
msgid "Origin Location Disable"
msgstr "Quelllagerort"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_stock_move__location_move
msgid "Part of move location"
msgstr "Teil der Lagerortbewegung"
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_stock_picking_type
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__picking_type_id
msgid "Picking Type"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Planned Transfer"
msgstr "Geplante Bewegung"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__product_id
msgid "Product"
msgstr "Produkt"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__product_uom_id
msgid "Product Unit of Measure"
msgstr "Produkt Maßeinheit"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__move_quantity
msgid "Quantity to move"
msgstr "Anzahl"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__reserved_quantity
msgid "Reserved quantity"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_stock_picking_type__show_move_onhand
msgid "Show Move On hand stock"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_stock_picking_type__show_move_onhand
msgid ""
"Show a button 'Move On Hand' in the Inventory Dashboard to initiate the "
"process to move the products in stock at the origin location."
msgstr ""
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_stock_move
msgid "Stock Move"
msgstr "Lagerbewegung"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "UoM"
msgstr "Einheit"
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_stock_move__location_move
msgid "Whether this move is a part of stock_location moves"
msgstr "Ist diese Bewegung Teil der Lagerortbewegungen"
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_wiz_stock_move_location
#, fuzzy
msgid "Wizard move location"
msgstr "Teil der Lagerortbewegung"
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_wiz_stock_move_location_line
#, fuzzy
msgid "Wizard move location line"
msgstr "Lagerort Buchungszeilen"
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_wiz_stock_move_location__destination_location_disable
msgid "technical field to disable the edition of destination location."
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_wiz_stock_move_location__origin_location_disable
msgid "technical field to disable the edition of origin location."
msgstr ""
#~ msgid "Add all"
#~ msgstr "Alle hinzufügen"
#~ msgid "Clear all"
#~ msgstr "Alle entfernen"
#~ msgid "wiz.stock.move.location"
#~ msgstr "wiz.stock.move.location"
#~ msgid "wiz.stock.move.location.line"
#~ msgstr "wiz.stock.move.location.line"

View File

@@ -0,0 +1,250 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_move_location
#
msgid ""
msgstr ""
"Project-Id-Version: Odoo Server 11.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2020-11-02 20:30+0000\n"
"PO-Revision-Date: 2020-11-02 21:37+0100\n"
"Last-Translator: Sergio Teruel <sergio.teruel@tecnativa.com>\n"
"Language-Team: none\n"
"Language: es\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=n != 1;\n"
"X-Generator: Poedit 2.3\n"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__apply_putaway_strategy
msgid "Apply putaway strategy"
msgstr "Aplicar estrategia de traslado"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Apply putaway strategy for moving products"
msgstr "Aplicar estrategia de traslado para los movimientos de producto"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Cancel"
msgstr "Cancelar"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__picking_id
msgid "Connected Picking"
msgstr "Operación relacionada"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__create_uid
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__create_uid
msgid "Created by"
msgstr "Creado por"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__create_date
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__create_date
msgid "Created on"
msgstr "Creado el"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__custom
msgid "Custom line"
msgstr "Línea personalizada"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__destination_location_id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__destination_location_id
msgid "Destination Location"
msgstr "Ubicación de destino"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__destination_location_disable
msgid "Destination Location Disable"
msgstr "Ubicación de destino desactivada"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__display_name
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__display_name
msgid "Display Name"
msgstr "Nombre mostrado"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__edit_locations
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Edit Locations"
msgstr "(editar)"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__id
msgid "ID"
msgstr "ID"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Immediate Transfer"
msgstr "Transferencia Inmediata"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Inventory Details"
msgstr "Detalles de Inventario"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location____last_update
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line____last_update
msgid "Last Modified on"
msgstr "Última modificación en"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__write_uid
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__write_uid
msgid "Last Updated by"
msgstr "Última actualización por"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__write_date
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__write_date
msgid "Last Updated on"
msgstr "Última actualización el"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__lot_id
msgid "Lot/Serial Number"
msgstr "Lote/Número de serie"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__max_quantity
msgid "Maximum available quantity"
msgstr "Cantidad máxima disponible"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__stock_move_location_line_ids
msgid "Move Location lines"
msgstr "Líneas de movimiento de Ubicación"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.stock_picking_type_kanban
msgid "Move On Hand"
msgstr "Disponible"
#. module: stock_move_location
#: model:ir.actions.act_window,name:stock_move_location.wiz_stock_move_location_action
#: model:ir.ui.menu,name:stock_move_location.menuitem_move_location
msgid "Move from location..."
msgstr "Mover desde ubicación..."
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__move_location_wizard_id
msgid "Move location Wizard"
msgstr "Asistente para mover desde ubicación"
#. module: stock_move_location
#: code:addons/stock_move_location/wizard/stock_move_location_line.py:0
#, python-format
msgid "Move quantity can not exceed max quantity or be negative"
msgstr "La cantidad movida no puede superar la cantidad máxima o ser negativo"
#. module: stock_move_location
#: model:ir.actions.act_window,name:stock_move_location.wiz_stock_quant_location_action
msgid "Move to location..."
msgstr "Mover a ubicación..."
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__origin_location_id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__origin_location_id
msgid "Origin Location"
msgstr "Ubicación de origen"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__origin_location_disable
msgid "Origin Location Disable"
msgstr "Ubicación origen desactivada"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_stock_move__location_move
msgid "Part of move location"
msgstr "Parte de un movimiento entre ubicaciones"
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_stock_picking_type
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__picking_type_id
msgid "Picking Type"
msgstr "Tipo de operación"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Planned Transfer"
msgstr "Transferencia Planificada"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__product_id
msgid "Product"
msgstr "Producto"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__product_uom_id
msgid "Product Unit of Measure"
msgstr "Unidad de medida"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__move_quantity
msgid "Quantity to move"
msgstr "Cantidad a mover"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__reserved_quantity
msgid "Reserved quantity"
msgstr "Cantidad Reservada"
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_stock_picking_type__show_move_onhand
msgid "Show Move On hand stock"
msgstr "Mostrar cantidad disponible"
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_stock_picking_type__show_move_onhand
msgid ""
"Show a button 'Move On Hand' in the Inventory Dashboard to initiate the "
"process to move the products in stock at the origin location."
msgstr ""
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_stock_move
msgid "Stock Move"
msgstr "Movimiento de existencias"
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "UoM"
msgstr "UdM"
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_stock_move__location_move
msgid "Whether this move is a part of stock_location moves"
msgstr "Si este movimiento es parte de movimiento de ubicaciones"
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_wiz_stock_move_location
#, fuzzy
msgid "Wizard move location"
msgstr "Asistente para mover desde ubicación"
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_wiz_stock_move_location_line
#, fuzzy
msgid "Wizard move location line"
msgstr "Asistente para mover desde ubicación"
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_wiz_stock_move_location__destination_location_disable
msgid "technical field to disable the edition of destination location."
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_wiz_stock_move_location__origin_location_disable
msgid "technical field to disable the edition of origin location."
msgstr ""

View File

@@ -0,0 +1,244 @@
# Translation of Odoo Server.
# This file contains the translation of the following modules:
# * stock_move_location
#
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_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__apply_putaway_strategy
msgid "Apply putaway strategy"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Apply putaway strategy for moving products"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Cancel"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__picking_id
msgid "Connected Picking"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__create_uid
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__create_uid
msgid "Created by"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__create_date
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__create_date
msgid "Created on"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__custom
msgid "Custom line"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__destination_location_id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__destination_location_id
msgid "Destination Location"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__destination_location_disable
msgid "Destination Location Disable"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__display_name
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__display_name
msgid "Display Name"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__edit_locations
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Edit Locations"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__id
msgid "ID"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Immediate Transfer"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Inventory Details"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location____last_update
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line____last_update
msgid "Last Modified on"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__write_uid
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__write_uid
msgid "Last Updated by"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__write_date
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__write_date
msgid "Last Updated on"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__lot_id
msgid "Lot/Serial Number"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__max_quantity
msgid "Maximum available quantity"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__stock_move_location_line_ids
msgid "Move Location lines"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.stock_picking_type_kanban
msgid "Move On Hand"
msgstr ""
#. module: stock_move_location
#: model:ir.actions.act_window,name:stock_move_location.wiz_stock_move_location_action
#: model:ir.ui.menu,name:stock_move_location.menuitem_move_location
msgid "Move from location..."
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__move_location_wizard_id
msgid "Move location Wizard"
msgstr ""
#. module: stock_move_location
#: code:addons/stock_move_location/wizard/stock_move_location_line.py:0
#, python-format
msgid "Move quantity can not exceed max quantity or be negative"
msgstr ""
#. module: stock_move_location
#: model:ir.actions.act_window,name:stock_move_location.wiz_stock_quant_location_action
msgid "Move to location..."
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__origin_location_id
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__origin_location_id
msgid "Origin Location"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__origin_location_disable
msgid "Origin Location Disable"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_stock_move__location_move
msgid "Part of move location"
msgstr ""
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_stock_picking_type
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location__picking_type_id
msgid "Picking Type"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "Planned Transfer"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__product_id
msgid "Product"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__product_uom_id
msgid "Product Unit of Measure"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__move_quantity
msgid "Quantity to move"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_wiz_stock_move_location_line__reserved_quantity
msgid "Reserved quantity"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,field_description:stock_move_location.field_stock_picking_type__show_move_onhand
msgid "Show Move On hand stock"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_stock_picking_type__show_move_onhand
msgid ""
"Show a button 'Move On Hand' in the Inventory Dashboard to initiate the "
"process to move the products in stock at the origin location."
msgstr ""
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_stock_move
msgid "Stock Move"
msgstr ""
#. module: stock_move_location
#: model_terms:ir.ui.view,arch_db:stock_move_location.view_wiz_stock_move_location_form_stock_move_location
msgid "UoM"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_stock_move__location_move
msgid "Whether this move is a part of stock_location moves"
msgstr ""
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_wiz_stock_move_location
msgid "Wizard move location"
msgstr ""
#. module: stock_move_location
#: model:ir.model,name:stock_move_location.model_wiz_stock_move_location_line
msgid "Wizard move location line"
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_wiz_stock_move_location__destination_location_disable
msgid "technical field to disable the edition of destination location."
msgstr ""
#. module: stock_move_location
#: model:ir.model.fields,help:stock_move_location.field_wiz_stock_move_location__origin_location_disable
msgid "technical field to disable the edition of origin location."
msgstr ""

View File

@@ -0,0 +1,10 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
from odoo import SUPERUSER_ID, api
def enable_multi_locations(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
ResConfig = env["res.config.settings"]
default_values = ResConfig.default_get(list(ResConfig.fields_get()))
default_values.update({"group_stock_multi_locations": True})
ResConfig.create(default_values).execute()

View File

@@ -0,0 +1,5 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from . import stock_move
from . import stock_picking_type

View File

@@ -0,0 +1,19 @@
# Copyright 2019 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import api, fields, models
class StockMove(models.Model):
_inherit = "stock.move"
location_move = fields.Boolean(
string="Part of move location",
help="Whether this move is a part of stock_location moves",
)
@api.depends("location_move")
def _compute_show_details_visible(self):
super()._compute_show_details_visible()
for move in self.filtered(lambda x: x.location_move):
move.show_details_visible = True

View File

@@ -0,0 +1,26 @@
# Copyright 2019 Sergio Teruel <sergio.teruel@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import fields, models
class StockPickingType(models.Model):
_inherit = "stock.picking.type"
show_move_onhand = fields.Boolean(
string="Show Move On hand stock",
help="Show a button 'Move On Hand' in the Inventory Dashboard "
"to initiate the process to move the products in stock "
"at the origin location.",
)
def action_move_location(self):
action = self.env.ref(
"stock_move_location.wiz_stock_move_location_action"
).read()[0]
action["context"] = {
"default_origin_location_id": self.default_location_src_id.id,
"default_destination_location_id": self.default_location_dest_id.id,
"default_picking_type_id": self.id,
"default_edit_locations": False,
}
return action

View File

@@ -0,0 +1,10 @@
* Mathieu Vatel <mathieu@julius.fr>
* Mykhailo Panarin <m.panarin@mobilunity.com>
* Joan Sisquella <joan.sisquella@forgeflow.com>
* Jordi Ballester Alomar <jordi.ballester@forgeflow.com>
* Lois Rilo <lois.rilo@forgeflow.com>
* Héctor Villarreal <hector.villarreal@forgeflow.com>
* Tecnativa <tecnativa.com>
* Sergio Teruel
* João Marques

View File

@@ -0,0 +1,2 @@
This module allows to move entire location of products from one place to
another and move only selected quantities.

View File

@@ -0,0 +1,6 @@
Change the current implementation (suggested by Denis Roussel from ACSONE):
* A new parameter on stock picking types : 'Product Change Location' (with a little help).
* With this, go to the dashboard, create a picking with that type.
* Add a button on the picking form which is visible with that type that fill in the picking as now
* Nice to have: add a magic button on locations that with context creates a new picking of that type with the origin location already filled in.

View File

@@ -0,0 +1,22 @@
* A new menu item Stock > Move from location... opens a wizard
where 2 locations can be specified.
* Select origin and destination locations and press "IMMEDIATE TRANSFER" or "PLANNED TRANSFER"
* Press `ADD ALL` button to add all products available
* Those lines can be edited. Move quantity can't be more than a max available quantity
* Move doesn't care about the reservations and will move stuff anyway
* If during your operation with the wizard the real quantity will change
it will move only the available quantity at the button press
* Products will be moved and a form view of picking that did that will show up
* If "PLANNED TRANSFER" is used - the picking won't be validated automatically
If you want to transfer a full quant:
* Go to `Inventory > Master Data > Products` and click "On hand" smart button
or `Inventory > Reporting > Inventory`, the quants view will be
opened.
* Select the quantities which you want move to another location
If you go to the Inventory Dashboard you can see the button "Move from location"
in each of the picking types (only applicable to internal transfers). Press it
and you will be directed to the wizard.

View File

@@ -0,0 +1,3 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_wiz_stock_move_location,access wiz.stock.move.location,model_wiz_stock_move_location,stock.group_stock_user,1,1,1,1
access_wiz_stock_move_location_line,access wiz.stock.move.location.line,model_wiz_stock_move_location_line,stock.group_stock_user,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_move_location access wiz.stock.move.location model_wiz_stock_move_location stock.group_stock_user 1 1 1 1
3 access_wiz_stock_move_location_line access wiz.stock.move.location.line model_wiz_stock_move_location_line stock.group_stock_user 1 1 1 1

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

View File

@@ -0,0 +1,467 @@
<?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>Move Stock Location</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="move-stock-location">
<h1 class="title">Move Stock Location</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="Beta" src="https://img.shields.io/badge/maturity-Beta-yellow.png" /></a> <a class="reference external" 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" href="https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_move_location"><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-14-0/stock-logistics-warehouse-14-0-stock_move_location"><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/14.0"><img alt="Try me on Runbot" src="https://img.shields.io/badge/runbot-Try%20me-875A7B.png" /></a></p>
<p>This module allows to move entire location of products from one place to
another and move only selected 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="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>
<ul class="simple">
<li>A new menu item Stock &gt; Move from location… opens a wizard
where 2 locations can be specified.</li>
<li>Select origin and destination locations and press “IMMEDIATE TRANSFER” or “PLANNED TRANSFER”</li>
<li>Press <cite>ADD ALL</cite> button to add all products available</li>
<li>Those lines can be edited. Move quantity cant be more than a max available quantity</li>
<li>Move doesnt care about the reservations and will move stuff anyway</li>
<li>If during your operation with the wizard the real quantity will change
it will move only the available quantity at the button press</li>
<li>Products will be moved and a form view of picking that did that will show up</li>
<li>If “PLANNED TRANSFER” is used - the picking wont be validated automatically</li>
</ul>
<p>If you want to transfer a full quant:</p>
<ul class="simple">
<li>Go to <cite>Inventory &gt; Master Data &gt; Products</cite> and click “On hand” smart button
or <cite>Inventory &gt; Reporting &gt; Inventory</cite>, the quants view will be
opened.</li>
<li>Select the quantities which you want move to another location</li>
</ul>
<p>If you go to the Inventory Dashboard you can see the button “Move from location”
in each of the picking types (only applicable to internal transfers). Press it
and you will be directed to the wizard.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id2">Known issues / Roadmap</a></h1>
<p>Change the current implementation (suggested by Denis Roussel from ACSONE):</p>
<ul class="simple">
<li>A new parameter on stock picking types : Product Change Location (with a little help).</li>
<li>With this, go to the dashboard, create a picking with that type.</li>
<li>Add a button on the picking form which is visible with that type that fill in the picking as now</li>
<li>Nice to have: add a magic button on locations that with context creates a new picking of that type with the origin location already filled in.</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_move_location%0Aversion:%2014.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>Julius Network Solutions</li>
</ul>
</div>
<div class="section" id="contributors">
<h2><a class="toc-backref" href="#id6">Contributors</a></h2>
<ul class="simple">
<li>Mathieu Vatel &lt;<a class="reference external" href="mailto:mathieu&#64;julius.fr">mathieu&#64;julius.fr</a>&gt;</li>
<li>Mykhailo Panarin &lt;<a class="reference external" href="mailto:m.panarin&#64;mobilunity.com">m.panarin&#64;mobilunity.com</a>&gt;</li>
<li>Joan Sisquella &lt;<a class="reference external" href="mailto:joan.sisquella&#64;forgeflow.com">joan.sisquella&#64;forgeflow.com</a>&gt;</li>
<li>Jordi Ballester Alomar &lt;<a class="reference external" href="mailto:jordi.ballester&#64;forgeflow.com">jordi.ballester&#64;forgeflow.com</a>&gt;</li>
<li>Lois Rilo &lt;<a class="reference external" href="mailto:lois.rilo&#64;forgeflow.com">lois.rilo&#64;forgeflow.com</a>&gt;</li>
<li>Héctor Villarreal &lt;<a class="reference external" href="mailto:hector.villarreal&#64;forgeflow.com">hector.villarreal&#64;forgeflow.com</a>&gt;</li>
<li>Tecnativa &lt;tecnativa.com&gt;<ul>
<li>Sergio Teruel</li>
<li>João Marques</li>
</ul>
</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>This module is part of the <a class="reference external" href="https://github.com/OCA/stock-logistics-warehouse/tree/14.0/stock_move_location">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,6 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from . import test_common
from . import test_move_location

View File

@@ -0,0 +1,115 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.tests import common
class TestsCommon(common.SavepointCase):
@classmethod
def setUpClass(cls):
super().setUpClass()
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
cls.location_obj = cls.env["stock.location"]
product_obj = cls.env["product.product"]
cls.wizard_obj = cls.env["wiz.stock.move.location"]
cls.quant_obj = cls.env["stock.quant"]
cls.company = cls.env.ref("base.main_company")
cls.internal_loc_1 = cls.location_obj.create(
{
"name": "INT_1",
"usage": "internal",
"active": True,
"company_id": cls.company.id,
}
)
cls.internal_loc_2 = cls.location_obj.create(
{
"name": "INT_2",
"usage": "internal",
"active": True,
"company_id": cls.company.id,
}
)
cls.internal_loc_2_shelf = cls.location_obj.create(
{
"name": "Shelf",
"usage": "internal",
"active": True,
"company_id": cls.company.id,
"location_id": cls.internal_loc_2.id,
}
)
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
cls.product_no_lots = product_obj.create(
{"name": "Pineapple", "type": "product", "tracking": "none"}
)
cls.product_lots = product_obj.create(
{"name": "Apple", "type": "product", "tracking": "lot"}
)
cls.lot1 = cls.env["stock.production.lot"].create(
{
"name": "lot1",
"product_id": cls.product_lots.id,
"company_id": cls.company.id,
}
)
cls.lot2 = cls.env["stock.production.lot"].create(
{
"name": "lot2",
"product_id": cls.product_lots.id,
"company_id": cls.company.id,
}
)
cls.lot3 = cls.env["stock.production.lot"].create(
{
"name": "lot3",
"product_id": cls.product_lots.id,
"company_id": cls.company.id,
}
)
def setup_product_amounts(self):
self.set_product_amount(self.product_no_lots, self.internal_loc_1, 123)
self.set_product_amount(
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot1
)
self.set_product_amount(
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot2
)
self.set_product_amount(
self.product_lots, self.internal_loc_1, 1.0, lot_id=self.lot3
)
def set_product_amount(self, product, location, amount, lot_id=None):
self.env["stock.quant"]._update_available_quantity(
product, location, amount, lot_id=lot_id
)
def check_product_amount(self, product, location, amount, lot_id=None):
self.assertEqual(
self.env["stock.quant"]._get_available_quantity(
product, location, lot_id=lot_id
),
amount,
)
def _create_wizard(self, origin_location, destination_location):
move_location_wizard = self.env["wiz.stock.move.location"]
return move_location_wizard.create(
{
"origin_location_id": origin_location.id,
"destination_location_id": destination_location.id,
}
)
def _create_putaway_for_product(self, product, loc_in, loc_out):
putaway = self.env["stock.putaway.rule"].create(
{
"product_id": product.id,
"location_in_id": loc_in.id,
"location_out_id": loc_out.id,
}
)
loc_in.write({"putaway_rule_ids": [(4, putaway.id, 0)]})

View File

@@ -0,0 +1,167 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo.exceptions import ValidationError
from .test_common import TestsCommon
class TestMoveLocation(TestsCommon):
def setUp(self):
super().setUp()
self.setup_product_amounts()
def test_move_location_wizard(self):
"""Test a simple move."""
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
wizard.onchange_origin_location()
wizard.action_move_location()
self.check_product_amount(self.product_no_lots, self.internal_loc_1, 0)
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot1)
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot2)
self.check_product_amount(self.product_lots, self.internal_loc_1, 0, self.lot3)
self.check_product_amount(self.product_no_lots, self.internal_loc_2, 123)
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot1)
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot2)
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot3)
def test_move_location_wizard_amount(self):
"""Can't move more than exists."""
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
wizard.onchange_origin_location()
with self.assertRaises(ValidationError):
wizard.stock_move_location_line_ids[0].move_quantity += 1
def test_move_location_wizard_ignore_reserved(self):
"""Can't move more than exists."""
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
wizard.onchange_origin_location()
# reserve some quants
self.quant_obj._update_reserved_quantity(
self.product_no_lots, self.internal_loc_1, 50
)
self.quant_obj._update_reserved_quantity(
self.product_lots, self.internal_loc_1, 1, lot_id=self.lot1
)
# doesn't care about reservations, everything is moved
wizard.action_move_location()
self.check_product_amount(self.product_no_lots, self.internal_loc_1, 0)
self.check_product_amount(self.product_no_lots, self.internal_loc_2, 123)
self.check_product_amount(self.product_lots, self.internal_loc_2, 1, self.lot1)
def test_wizard_clear_lines(self):
"""Test lines getting cleared properly."""
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
wizard.onchange_origin_location()
self.assertEqual(len(wizard.stock_move_location_line_ids), 4)
wizard._onchange_destination_location_id()
self.assertEqual(len(wizard.stock_move_location_line_ids), 4)
dest_location_line = wizard.stock_move_location_line_ids.mapped(
"destination_location_id"
)
self.assertEqual(dest_location_line, wizard.destination_location_id)
wizard._onchange_origin_location_id()
self.assertEqual(len(wizard.stock_move_location_line_ids), 0)
def test_planned_transfer(self):
"""Test planned transfer."""
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
wizard.onchange_origin_location()
wizard = wizard.with_context({"planned": True})
wizard.action_move_location()
picking = wizard.picking_id
self.assertEqual(picking.state, "assigned")
self.assertEqual(len(picking.move_line_ids), 4)
self.assertEqual(
sorted(picking.move_line_ids.mapped("product_uom_qty")), [1, 1, 1, 123]
)
def test_quant_transfer(self):
"""Test quants transfer."""
quants = self.product_lots.stock_quant_ids
wizard = self.wizard_obj.with_context(
active_model="stock.quant",
active_ids=quants.ids,
origin_location_disable=True,
).create(
{
"origin_location_id": quants[:1].location_id.id,
"destination_location_id": self.internal_loc_2.id,
}
)
lines = wizard.stock_move_location_line_ids
self.assertEqual(len(lines), 3)
wizard.onchange_origin_location()
self.assertEqual(len(lines), 3)
wizard.destination_location_id = self.internal_loc_1
wizard._onchange_destination_location_id()
self.assertEqual(lines.mapped("destination_location_id"), self.internal_loc_1)
wizard.origin_location_id = self.internal_loc_2
wizard._onchange_destination_location_id()
self.assertEqual(len(lines), 3)
def test_readonly_location_computation(self):
"""Test that origin_location_disable and destination_location_disable
are computed correctly."""
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
# locations are editable.
self.assertFalse(wizard.origin_location_disable)
self.assertFalse(wizard.destination_location_disable)
# Disable edit mode:
wizard.edit_locations = False
self.assertTrue(wizard.origin_location_disable)
self.assertTrue(wizard.destination_location_disable)
def test_picking_type_action_dummy(self):
"""Test that no error is raised from actions."""
pick_type = self.env.ref("stock.picking_type_internal")
pick_type.action_move_location()
def test_wizard_with_putaway_strategy(self):
"""Test that Putaway strategies are being applied."""
self._create_putaway_for_product(
self.product_no_lots, self.internal_loc_2, self.internal_loc_2_shelf
)
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2)
wizard.apply_putaway_strategy = True
wizard.onchange_origin_location()
putaway_line = wizard.stock_move_location_line_ids.filtered(
lambda p: p.product_id == self.product_no_lots
)[0]
self.assertEqual(
putaway_line.destination_location_id, self.internal_loc_2_shelf
)
def test_inmediate_transfer_reserved_quantity(self):
"""
Unreserve quantities in old location and reserve the same items on
new location
"""
# Create some quants
self.set_product_amount(
self.product_lots, self.internal_loc_1, 100, lot_id=self.lot1
)
# Reserve some quantities
stock_move = self.env["stock.move"].create(
{
"name": "Move for test",
"product_id": self.product_lots.id,
"product_uom_qty": 20.0,
"product_uom": self.product_lots.uom_id.id,
"location_id": self.internal_loc_1.id,
"location_dest_id": self.internal_loc_2.id,
}
)
stock_move._action_confirm()
stock_move._action_assign()
# Move all quantities to other location
wizard = self._create_wizard(self.internal_loc_1, self.internal_loc_2_shelf)
wizard.onchange_origin_location()
wizard.action_move_location()
# The old reserved quantities must be in new location after confirm wizard
self.assertEqual(len(stock_move.move_line_ids), 1)
self.assertEqual(stock_move.move_line_ids.product_uom_qty, 20.0)
self.assertEqual(
stock_move.move_line_ids.location_id, self.internal_loc_2_shelf
)

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record model="ir.ui.view" id="view_picking_type_form">
<field name="name">Operation Types</field>
<field name="model">stock.picking.type</field>
<field name="inherit_id" ref="stock.view_picking_type_form" />
<field name="arch" type="xml">
<field name="show_operations" position="after">
<field
name="show_move_onhand"
attrs='{"invisible": [("code", "not in", ["internal"])]}'
/>
</field>
</field>
</record>
<record id="stock_picking_type_kanban" model="ir.ui.view">
<field name="model">stock.picking.type</field>
<field name="inherit_id" ref="stock.stock_picking_type_kanban" />
<field name="arch" type="xml">
<field name="code" position="after">
<field name="show_move_onhand" />
</field>
<xpath expr="//div[hasclass('o_kanban_primary_left')]" position="inside">
<div t-if="record.show_move_onhand.raw_value">
<button
name="action_move_location"
type="object"
class="btn btn-info"
style="margin-top: 5px;"
>
Move On Hand
</button>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1,6 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from . import stock_move_location
from . import stock_move_location_line

View File

@@ -0,0 +1,297 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# Copyright 2019 Sergio Teruel - Tecnativa <sergio.teruel@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import api, fields, models
from odoo.fields import first
class StockMoveLocationWizard(models.TransientModel):
_name = "wiz.stock.move.location"
_description = "Wizard move location"
def _get_default_picking_type_id(self):
company_id = self.env.context.get("company_id") or self.env.user.company_id.id
return (
self.env["stock.picking.type"]
.search(
[
("code", "=", "internal"),
("warehouse_id.company_id", "=", company_id),
],
limit=1,
)
.id
)
origin_location_disable = fields.Boolean(
compute="_compute_readonly_locations",
help="technical field to disable the edition of origin location.",
)
origin_location_id = fields.Many2one(
string="Origin Location",
comodel_name="stock.location",
required=True,
domain=lambda self: self._get_locations_domain(),
)
destination_location_disable = fields.Boolean(
compute="_compute_readonly_locations",
help="technical field to disable the edition of destination location.",
)
destination_location_id = fields.Many2one(
string="Destination Location",
comodel_name="stock.location",
required=True,
domain=lambda self: self._get_locations_domain(),
)
stock_move_location_line_ids = fields.Many2many(
string="Move Location lines",
comodel_name="wiz.stock.move.location.line",
column1="move_location_wiz_id",
column2="move_location_line_wiz_id",
)
picking_type_id = fields.Many2one(
comodel_name="stock.picking.type", default=_get_default_picking_type_id
)
picking_id = fields.Many2one(
string="Connected Picking", comodel_name="stock.picking"
)
edit_locations = fields.Boolean(string="Edit Locations", default=True)
apply_putaway_strategy = fields.Boolean(string="Apply putaway strategy")
@api.depends("edit_locations")
def _compute_readonly_locations(self):
for rec in self:
rec.origin_location_disable = self.env.context.get(
"origin_location_disable", False
)
rec.destination_location_disable = self.env.context.get(
"destination_location_disable", False
)
if not rec.edit_locations:
rec.origin_location_disable = True
rec.destination_location_disable = True
@api.model
def default_get(self, fields):
res = super().default_get(fields)
if self.env.context.get("active_model", False) != "stock.quant":
return res
# Load data directly from quants
quants = self.env["stock.quant"].browse(
self.env.context.get("active_ids", False)
)
res["stock_move_location_line_ids"] = [
(
0,
0,
{
"product_id": quant.product_id.id,
"move_quantity": quant.quantity,
"max_quantity": quant.quantity,
"reserved_quantity": quant.reserved_quantity,
"origin_location_id": quant.location_id.id,
"lot_id": quant.lot_id.id,
"product_uom_id": quant.product_uom_id.id,
"custom": False,
},
)
for quant in quants
]
res["origin_location_id"] = first(quants).location_id.id
return res
@api.onchange("origin_location_id")
def _onchange_origin_location_id(self):
if not self.env.context.get("origin_location_disable", False):
self._clear_lines()
@api.onchange("destination_location_id")
def _onchange_destination_location_id(self):
for line in self.stock_move_location_line_ids:
line.destination_location_id = self.destination_location_id
def _clear_lines(self):
self.stock_move_location_line_ids = False
def _get_locations_domain(self):
return [
"|",
("company_id", "=", self.env.user.company_id.id),
("company_id", "=", False),
]
def _create_picking(self):
return self.env["stock.picking"].create(
{
"picking_type_id": self.picking_type_id.id,
"location_id": self.origin_location_id.id,
"location_dest_id": self.destination_location_id.id,
}
)
def group_lines(self):
lines_grouped = {}
for line in self.stock_move_location_line_ids:
lines_grouped.setdefault(
line.product_id.id, self.env["wiz.stock.move.location.line"].browse()
)
lines_grouped[line.product_id.id] |= line
return lines_grouped
def _create_moves(self, picking):
self.ensure_one()
groups = self.group_lines()
moves = self.env["stock.move"]
for lines in groups.values():
move = self._create_move(picking, lines)
moves |= move
return moves
def _get_move_values(self, picking, lines):
# locations are same for the products
location_from_id = lines[0].origin_location_id.id
location_to_id = lines[0].destination_location_id.id
product = lines[0].product_id
product_uom_id = lines[0].product_uom_id.id
qty = sum([x.move_quantity for x in lines])
return {
"name": product.display_name,
"location_id": location_from_id,
"location_dest_id": location_to_id,
"product_id": product.id,
"product_uom": product_uom_id,
"product_uom_qty": qty,
"picking_id": picking.id,
"location_move": True,
}
def _create_move(self, picking, lines):
self.ensure_one()
move = self.env["stock.move"].create(self._get_move_values(picking, lines))
if not self.env.context.get("planned"):
for line in lines:
line.create_move_lines(picking, move)
return move
def _unreserve_moves(self):
"""
Try to unreserve moves that they has reserved quantity before user
moves products from a location to other one and change move origin
location to the new location to assign later.
:return moves unreserved
"""
moves_to_reassign = self.env["stock.move"]
lines_to_ckeck_reverve = self.stock_move_location_line_ids.filtered(
lambda l: (
l.move_quantity > l.max_quantity - l.reserved_quantity
and not l.origin_location_id.should_bypass_reservation()
)
)
for line in lines_to_ckeck_reverve:
move_lines = self.env["stock.move.line"].search(
[
("state", "=", "assigned"),
("product_id", "=", line.product_id.id),
("location_id", "=", line.origin_location_id.id),
("lot_id", "=", line.lot_id.id),
("product_uom_qty", ">", 0.0),
]
)
moves_to_unreserve = move_lines.mapped("move_id")
# Unreserve in old location
moves_to_unreserve._do_unreserve()
# Change location in move with the new one
moves_to_unreserve.write({"location_id": line.destination_location_id.id})
moves_to_reassign |= moves_to_unreserve
return moves_to_reassign
def action_move_location(self):
self.ensure_one()
picking = self._create_picking()
self._create_moves(picking)
if not self.env.context.get("planned"):
moves_to_reassign = self._unreserve_moves()
picking.button_validate()
moves_to_reassign._action_assign()
else:
picking.action_confirm()
picking.action_assign()
self.picking_id = picking
return self._get_picking_action(picking.id)
def _get_picking_action(self, pickinig_id):
action = self.env.ref("stock.action_picking_tree_all").read()[0]
form_view = self.env.ref("stock.view_picking_form").id
action.update(
{"view_mode": "form", "views": [(form_view, "form")], "res_id": pickinig_id}
)
return action
def _get_group_quants(self):
location_id = self.origin_location_id
# Using sql as search_group doesn't support aggregation functions
# leading to overhead in queries to DB
query = """
SELECT product_id, lot_id, SUM(quantity) AS quantity,
SUM(reserved_quantity) AS reserved_quantity
FROM stock_quant
WHERE location_id = %s
GROUP BY product_id, lot_id
"""
self.env.cr.execute(query, (location_id.id,))
return self.env.cr.dictfetchall()
def _get_stock_move_location_lines_values(self):
product_obj = self.env["product.product"]
product_data = []
for group in self._get_group_quants():
product = product_obj.browse(group.get("product_id")).exists()
# Apply the putaway strategy
location_dest_id = (
self.apply_putaway_strategy
and self.destination_location_id._get_putaway_strategy(product).id
or self.destination_location_id.id
)
product_data.append(
{
"product_id": product.id,
"move_quantity": group.get("quantity"),
"max_quantity": group.get("quantity"),
"reserved_quantity": group.get("reserved_quantity"),
"origin_location_id": self.origin_location_id.id,
"destination_location_id": location_dest_id,
# cursor returns None instead of False
"lot_id": group.get("lot_id") or False,
"product_uom_id": product.uom_id.id,
"custom": False,
}
)
return product_data
@api.onchange("origin_location_id")
def onchange_origin_location(self):
# Get origin_location_disable context key to prevent load all origin
# location products when user opens the wizard from stock quants to
# move it to other location.
if (
not self.env.context.get("origin_location_disable")
and self.origin_location_id
):
lines = []
line_model = self.env["wiz.stock.move.location.line"]
for line_val in self._get_stock_move_location_lines_values():
if line_val.get("max_quantity") <= 0:
continue
line = line_model.create(line_val)
line.max_quantity = line.get_max_quantity()
line.reserved_quantity = line.reserved_quantity
lines.append(line)
self.update(
{"stock_move_location_line_ids": [(6, 0, [line.id for line in lines])]}
)
def clear_lines(self):
self._clear_lines()
return {"type": "ir.action.do_nothing"}

View File

@@ -0,0 +1,154 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<record
id="view_wiz_stock_move_location_form_stock_move_location"
model="ir.ui.view"
>
<field name="name">wiz.stock.move.location.form.stock_move_location</field>
<field name="model">wiz.stock.move.location</field>
<field name="arch" type="xml">
<form>
<sheet>
<div class="oe_button_box" name="button_box" />
<div>
<label for="edit_locations">
Edit Locations
</label>
<field name="edit_locations" widget="boolean_toggle" />
</div>
<div groups="stock.group_stock_multi_locations">
<label for="apply_putaway_strategy">
Apply putaway strategy for moving products
</label>
<field name="apply_putaway_strategy" widget="boolean_toggle" />
</div>
<group name="picking_type">
<field name="picking_type_id" />
</group>
<group name="main">
<field name="origin_location_disable" invisible="True" />
<field
name="origin_location_id"
attrs="{'readonly': [('origin_location_disable', '=', True)]}"
/>
<field name="destination_location_disable" invisible="True" />
<field
name="destination_location_id"
attrs="{'readonly': [('destination_location_disable', '=', True)]}"
/>
</group>
<group name="lines">
<field
name="stock_move_location_line_ids"
nolabel="1"
widget="one2many_list"
mode="tree,kanban"
>
<tree
string="Inventory Details"
editable="bottom"
decoration-info="move_quantity != max_quantity"
decoration-danger="(move_quantity &lt; 0) or (move_quantity > max_quantity)"
create="0"
>
<field
name="product_id"
domain="[('type','=','product')]"
/>
<field
name="product_uom_id"
string="UoM"
groups="uom.group_uom"
/>
<field
name="origin_location_id"
readonly="1"
force_save="1"
/>
<field
name="destination_location_id"
readonly="1"
force_save="1"
/>
<field
name="lot_id"
domain="[('product_id', '=', product_id)]"
context="{'default_product_id': product_id}"
groups="stock.group_production_lot"
options="{'no_create': True}"
/>
<field name="move_quantity" />
<field name="custom" invisible="1" />
<field
name="max_quantity"
attrs="{'readonly': [('custom', '!=', True)]}"
force_save="1"
/>
<field
name="reserved_quantity"
readonly="1"
force_save="1"
/>
</tree>
<kanban class="o_kanban_mobile">
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click">
<div class="o_kanban_record_body">
<field name="product_id" />
<field name="product_uom_id" />
<field
name="lot_id"
groups="stock.group_production_lot"
/>
<field name="origin_location_id" />
<field name="destination_location_id" />
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_right">
<span>
<field name="move_quantity" />
</span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</group>
<footer>
<button
name="action_move_location"
string="Immediate Transfer"
type="object"
class="btn-primary"
/>
<button
name="action_move_location"
string="Planned Transfer"
type="object"
class="btn-primary"
context="{'planned': True}"
/>
<button special="cancel" string="Cancel" class="btn-default" />
</footer>
</sheet>
</form>
</field>
</record>
<record id="wiz_stock_move_location_action" model="ir.actions.act_window">
<field name="name">Move from location...</field>
<field name="res_model">wiz.stock.move.location</field>
<field name="view_mode">form</field>
<field name="target">new</field>
<field name="context">{}</field>
</record>
<menuitem
id="menuitem_move_location"
name="Move from location..."
parent="stock.menu_stock_warehouse_mgmt"
action="wiz_stock_move_location_action"
sequence="99"
/>
</odoo>

View File

@@ -0,0 +1,139 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.tools import float_compare
class StockMoveLocationWizardLine(models.TransientModel):
_name = "wiz.stock.move.location.line"
_description = "Wizard move location line"
move_location_wizard_id = fields.Many2many(
string="Move location Wizard",
comodel_name="wiz.stock.move.location",
column1="move_location_line_wiz_id",
column2="move_location_wiz_id",
readonly=True,
)
product_id = fields.Many2one(
string="Product", comodel_name="product.product", required=True
)
origin_location_id = fields.Many2one(
string="Origin Location", comodel_name="stock.location"
)
destination_location_id = fields.Many2one(
string="Destination Location", comodel_name="stock.location"
)
product_uom_id = fields.Many2one(
string="Product Unit of Measure", comodel_name="uom.uom"
)
lot_id = fields.Many2one(
string="Lot/Serial Number",
comodel_name="stock.production.lot",
domain="[('product_id','=',product_id)]",
)
move_quantity = fields.Float(
string="Quantity to move", digits="Product Unit of Measure"
)
max_quantity = fields.Float(
string="Maximum available quantity", digits="Product Unit of Measure"
)
reserved_quantity = fields.Float(
string="Reserved quantity", digits="Product Unit of Measure"
)
custom = fields.Boolean(string="Custom line", default=True)
@staticmethod
def _compare(qty1, qty2, precision_rounding):
return float_compare(qty1, qty2, precision_rounding=precision_rounding)
@api.constrains("max_quantity", "move_quantity")
def _constraint_max_move_quantity(self):
for record in self:
rounding = record.product_uom_id.rounding
move_qty_gt_max_qty = (
self._compare(record.move_quantity, record.max_quantity, rounding) == 1
)
move_qty_lt_0 = self._compare(record.move_quantity, 0.0, rounding) == -1
if move_qty_gt_max_qty or move_qty_lt_0:
raise ValidationError(
_("Move quantity can not exceed max quantity or be negative")
)
def get_max_quantity(self):
self.product_uom_id = self.product_id.uom_id
search_args = [
("location_id", "=", self.origin_location_id.id),
("product_id", "=", self.product_id.id),
]
if self.lot_id:
search_args.append(("lot_id", "=", self.lot_id.id))
else:
search_args.append(("lot_id", "=", False))
res = self.env["stock.quant"].read_group(search_args, ["quantity"], [])
max_quantity = res[0]["quantity"]
return max_quantity
def create_move_lines(self, picking, move):
for line in self:
values = line._get_move_line_values(picking, move)
if not self.env.context.get("planned") and values.get("qty_done") <= 0:
continue
self.env["stock.move.line"].create(values)
return True
def _get_move_line_values(self, picking, move):
self.ensure_one()
location_dest_id = (
self.move_location_wizard_id.apply_putaway_strategy
and self.destination_location_id.get_putaway_strategy(self.product_id).id
or self.destination_location_id.id
)
qty_todo, qty_done = self._get_available_quantity()
return {
"product_id": self.product_id.id,
"lot_id": self.lot_id.id,
"location_id": self.origin_location_id.id,
"location_dest_id": location_dest_id,
"product_uom_qty": qty_todo,
"qty_done": qty_done,
"product_uom_id": self.product_uom_id.id,
"picking_id": picking.id,
"move_id": move.id,
}
def _get_available_quantity(self):
"""We check here if the actual amount changed in the stock.
We don't care about the reservations but we do care about not moving
more than exists."""
self.ensure_one()
if not self.product_id:
return 0
if self.env.context.get("planned"):
# for planned transfer we don't care about the amounts at all
return self.move_quantity, 0
search_args = [
("location_id", "=", self.origin_location_id.id),
("product_id", "=", self.product_id.id),
]
if self.lot_id:
search_args.append(("lot_id", "=", self.lot_id.id))
else:
search_args.append(("lot_id", "=", False))
res = self.env["stock.quant"].read_group(search_args, ["quantity"], [])
available_qty = res[0]["quantity"]
if not available_qty:
# if it is immediate transfer and product doesn't exist in that
# location -> make the transfer of 0.
return 0
rounding = self.product_uom_id.rounding
available_qty_lt_move_qty = (
self._compare(available_qty, self.move_quantity, rounding) == -1
)
if available_qty_lt_move_qty:
return available_qty
return 0, self.move_quantity