diff --git a/stock_move_location/__init__.py b/stock_move_location/__init__.py index 91ba63a47..134df2743 100644 --- a/stock_move_location/__init__.py +++ b/stock_move_location/__init__.py @@ -1,6 +1,2 @@ -# Copyright (C) 2011 Julius Network Solutions SARL -# Copyright 2018 Camptocamp SA -# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) - from . import wizard from . import models diff --git a/stock_move_location/__manifest__.py b/stock_move_location/__manifest__.py index cd696448c..5660ebf8d 100644 --- a/stock_move_location/__manifest__.py +++ b/stock_move_location/__manifest__.py @@ -1,10 +1,12 @@ # Copyright (C) 2011 Julius Network Solutions SARL -# Copyright 2018 Camptocamp SA +# Copyright 2020 Camptocamp SA # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { "name": "Move Stock Location", - "version": "12.0.1.3.2", + "version": "12.0.2.0.1", "author": "Julius Network Solutions, " + "BCIM," + "Camptocamp," "Odoo Community Association (OCA)", "summary": "This module allows to move all stock " "in a stock location to an other one.", @@ -17,6 +19,7 @@ "data": [ 'data/stock_quant_view.xml', 'views/stock_picking_type_views.xml', + 'views/stock_picking.xml', 'wizard/stock_move_location.xml', ], } diff --git a/stock_move_location/i18n/fr_BE.po b/stock_move_location/i18n/fr_BE.po new file mode 100644 index 000000000..b53f5b505 --- /dev/null +++ b/stock_move_location/i18n/fr_BE.po @@ -0,0 +1,44 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * stock_picking_fillwithstock +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 9.0e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-01-22 08:26+0000\n" +"PO-Revision-Date: 2017-01-22 08:26+0000\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_picking_fillwithstock +#: model:ir.ui.view,arch_db:stock_picking_fillwithstock.view_picking_form +msgid "Fill with stock" +msgstr "Remplir avec le stock" + +#. module: stock_picking_fillwithstock +#: code:addons/stock_picking_fillwithstock/models/stock.py:35 +#, python-format +msgid "Moves lines already exsits" +msgstr "Des lignes existent déjà" + +#. module: stock_picking_fillwithstock +#: code:addons/stock_picking_fillwithstock/models/stock.py:60 +#, python-format +msgid "Nothing to move" +msgstr "Rien à déplacer" + +#. module: stock_picking_fillwithstock +#: code:addons/stock_picking_fillwithstock/models/stock.py:33 +#, python-format +msgid "Please choose a source end location" +msgstr "Veuillez choisir un emplacement source physique" + +#. module: stock_picking_fillwithstock +#: model:ir.model,name:stock_picking_fillwithstock.model_stock_picking +msgid "Transfer" +msgstr "Transfert" diff --git a/stock_move_location/i18n/stock_move_location.pot b/stock_move_location/i18n/stock_move_location.pot deleted file mode 100644 index 96a2b2131..000000000 --- a/stock_move_location/i18n/stock_move_location.pot +++ /dev/null @@ -1,238 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * stock_move_location -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server 12.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:72 -#, 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_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 "" - diff --git a/stock_move_location/models/__init__.py b/stock_move_location/models/__init__.py index 58ff9b56b..a44310781 100644 --- a/stock_move_location/models/__init__.py +++ b/stock_move_location/models/__init__.py @@ -3,3 +3,4 @@ from . import stock_move from . import stock_picking_type +from . import stock_picking diff --git a/stock_move_location/models/stock_picking.py b/stock_move_location/models/stock_picking.py new file mode 100644 index 000000000..7235230f4 --- /dev/null +++ b/stock_move_location/models/stock_picking.py @@ -0,0 +1,45 @@ +# Copyright Jacques-Etienne Baudoux 2016 Camptocamp +# Copyright Iryna Vyshnevska 2020 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import _, models +from odoo.exceptions import UserError + + +class StockPicking(models.Model): + _inherit = 'stock.picking' + + def button_fillwithstock(self): + # check source location has no children, i.e. we scanned a bin + + self.ensure_one() + self._validate_picking() + context = { + 'active_ids': self._get_movable_quants().ids, + 'active_model': 'stock.quant', + 'only_reserved_qty': True, + 'planned': True, + } + move_wizard = self.env['wiz.stock.move.location'].with_context(context).create({ + 'destination_location_id' : self.location_dest_id.id, + 'origin_location_id': self.location_id.id, + 'picking_type_id': self.picking_type_id.id, + 'picking_id': self.id, + }) + move_wizard._onchange_destination_location_id() + move_wizard.action_move_location() + return True + + def _validate_picking(self): + if self.location_id.child_ids: + raise UserError(_('Please choose a source end location')) + if self.move_lines: + raise UserError(_('Moves lines already exists')) + + def _get_movable_quants(self): + return self.env['stock.quant'].search( + [ + ('location_id', '=', self.location_id.id), + ('quantity', '>', 0.0), + ] + ) diff --git a/stock_move_location/readme/CONTRIBUTORS.rst b/stock_move_location/readme/CONTRIBUTORS.rst index 0e307c893..943767f9e 100644 --- a/stock_move_location/readme/CONTRIBUTORS.rst +++ b/stock_move_location/readme/CONTRIBUTORS.rst @@ -4,3 +4,5 @@ * Joan Sisquella * Jordi Ballester Alomar * Lois Rilo +* Jacques-Etienne Baudoux +* Iryna Vyshnevska diff --git a/stock_move_location/readme/USAGE.rst b/stock_move_location/readme/USAGE.rst index 55d8c0b71..b562610a0 100644 --- a/stock_move_location/readme/USAGE.rst +++ b/stock_move_location/readme/USAGE.rst @@ -19,4 +19,14 @@ If you want to transfer a full quant: 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. \ No newline at end of file +and you will be directed to the wizard. + +If you want transfer everything from stock.location + +On a draft picking, add a button to fill with moves lines for all products in +the source destination. This allows to create a picking to move all the content +of a location. If some quants are not available (i.e. reserved) the picking +will be in partially available state and reserved moves won't be listed in the +operations. +Use barcode interface to scan a location and create an empty picking. Then use +the fill with stock button. diff --git a/stock_move_location/tests/__init__.py b/stock_move_location/tests/__init__.py index 00527a8b0..315ff5109 100644 --- a/stock_move_location/tests/__init__.py +++ b/stock_move_location/tests/__init__.py @@ -1,6 +1,3 @@ -# Copyright (C) 2011 Julius Network Solutions SARL -# 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 +from . import test_stock_fillwithstock diff --git a/stock_move_location/tests/test_stock_fillwithstock.py b/stock_move_location/tests/test_stock_fillwithstock.py new file mode 100644 index 000000000..5546daa2b --- /dev/null +++ b/stock_move_location/tests/test_stock_fillwithstock.py @@ -0,0 +1,73 @@ +# Copyright Iryna Vyshnevska 2020 Camptocamp +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + + +import odoo.tests.common as common + + +class TestFillwithStock(common.TransactionCase): + + def setUp(self): + super(TestFillwithStock, self).setUp() + self.env = self.env(context=dict( + self.env.context, + tracking_disable=True, + )) + + self.stock_location = self.env.ref('stock.stock_location_stock') + self.pack_location = self.env.ref('stock.location_pack_zone') + + self.shelf1_location = self.env["stock.location"].create({ + "name": "Test location", + "usage": "internal", + "location_id": self.stock_location.id + }) + + self.product1 = self.env['product.product'].create({ + 'name': 'Product A', + 'type': 'product', + }) + self.product2 = self.env['product.product'].create({ + 'name': 'Product B', + 'type': 'product', + }) + + self.env['stock.quant'].create({ + 'product_id': self.product1.id, + 'location_id': self.shelf1_location.id, + 'quantity': 5.0, + 'reserved_quantity': 0.0, + }) + self.env['stock.quant'].create({ + 'product_id': self.product1.id, + 'location_id': self.shelf1_location.id, + 'quantity': 10.0, + 'reserved_quantity': 5.0, + }) + self.env['stock.quant'].create({ + 'product_id': self.product2.id, + 'location_id': self.shelf1_location.id, + 'quantity': 5.0, + 'reserved_quantity': 0.0, + }) + + def test_fillwithstock(self): + picking_stock_pack = self.env['stock.picking'].create({ + 'location_id': self.shelf1_location.id, + 'location_dest_id': self.pack_location.id, + 'picking_type_id': self.env.ref('stock.picking_type_internal').id, + }) + self.assertFalse(picking_stock_pack.move_lines) + picking_stock_pack.button_fillwithstock() + # picking filled with quants in bin + self.assertEqual(len(picking_stock_pack.move_lines), 2) + self.assertEqual( + picking_stock_pack.move_lines.filtered( + lambda m: m.product_id == self.product1).product_uom_qty, + 10.0 + ) + self.assertEqual( + picking_stock_pack.move_lines.filtered( + lambda m: m.product_id == self.product2).product_uom_qty, + 5.0 + ) diff --git a/stock_move_location/views/stock_picking.xml b/stock_move_location/views/stock_picking.xml new file mode 100644 index 000000000..052a76184 --- /dev/null +++ b/stock_move_location/views/stock_picking.xml @@ -0,0 +1,15 @@ + + + + + stock.picking.form.fillwithstock + stock.picking + + + + + + + diff --git a/stock_move_location/wizard/stock_move_location.py b/stock_move_location/wizard/stock_move_location.py index cac188648..059af54fe 100644 --- a/stock_move_location/wizard/stock_move_location.py +++ b/stock_move_location/wizard/stock_move_location.py @@ -5,6 +5,7 @@ from odoo import api, fields, models from odoo.fields import first +from itertools import groupby class StockMoveLocationWizard(models.TransientModel): @@ -77,18 +78,45 @@ class StockMoveLocationWizard(models.TransientModel): # 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, - '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['stock_move_location_line_ids'] = self._prepare_wizard_move_lines(quants) res['origin_location_id'] = first(quants).location_id.id return res + @api.model + def _prepare_wizard_move_lines(self, quants): + res = [] + exclude_reserved_qty = self.env.context.get('only_reserved_qty', False) + if not exclude_reserved_qty: + res = [(0, 0, { + 'product_id': quant.product_id.id, + 'move_quantity': quant.quantity, + 'max_quantity': quant.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] + else: + # if need move only available qty per product on location + for product, quant in groupby(quants, lambda r: r.product_id): + # we need only one quant per product + quant = list(quant)[0] + qty = quant._get_available_quantity( + quant.product_id, + quant.location_id, + ) + if qty: + res.append((0, 0, { + 'product_id': quant.product_id.id, + 'move_quantity': qty, + 'max_quantity': qty, + 'origin_location_id': quant.location_id.id, + 'lot_id': quant.lot_id.id, + 'product_uom_id': quant.product_uom_id.id, + 'custom': False, + })) + return res + @api.onchange('origin_location_id') def _onchange_origin_location_id(self): if not self.env.context.get('origin_location_disable', False): @@ -167,7 +195,10 @@ class StockMoveLocationWizard(models.TransientModel): @api.multi def action_move_location(self): self.ensure_one() - picking = self._create_picking() + if not self.picking_id: + picking = self._create_picking() + else: + picking = self.picking_id self._create_moves(picking) if not self.env.context.get("planned"): picking.button_validate()