Merge PR #1338 into 14.0

Signed-off-by LoisRForgeFlow
This commit is contained in:
OCA-git-bot
2022-01-07 11:39:29 +00:00
12 changed files with 264 additions and 29 deletions

View File

@@ -59,6 +59,16 @@ 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.
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.
Known issues / Roadmap
======================
@@ -86,6 +96,8 @@ Authors
~~~~~~~
* Julius Network Solutions
* BCIM
* Camptocamp
Contributors
~~~~~~~~~~~~
@@ -100,6 +112,8 @@ Contributors
* Sergio Teruel
* João Marques
* Jacques-Etienne Baudoux <je@bcim.be>
* Iryna Vyshnevska <i.vyshnevska@mobilunity.com>
Maintainers
~~~~~~~~~~~

View File

@@ -1,7 +1,3 @@
# 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

@@ -1,12 +1,12 @@
# Copyright (C) 2011 Julius Network Solutions SARL <contact@julius.fr>
# Copyright 2018 Camptocamp SA
# Copyright 2020 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.1",
"author": "Julius Network Solutions, Odoo Community Association (OCA)",
"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.",
"website": "https://github.com/OCA/stock-logistics-warehouse",
@@ -17,6 +17,7 @@
"data/stock_quant_view.xml",
"security/ir.model.access.csv",
"views/stock_picking_type_views.xml",
"views/stock_picking.xml",
"wizard/stock_move_location.xml",
],
"post_init_hook": "enable_multi_locations",

View File

@@ -3,3 +3,4 @@
from . import stock_move
from . import stock_picking_type
from . import stock_picking

View File

@@ -0,0 +1,51 @@
# 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),
]
)

View File

@@ -8,3 +8,5 @@
* Sergio Teruel
* João Marques
* Jacques-Etienne Baudoux <je@bcim.be>
* Iryna Vyshnevska <i.vyshnevska@mobilunity.com>

View File

@@ -20,3 +20,13 @@ 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.
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.

View File

@@ -3,7 +3,7 @@
<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/" />
<meta name="generator" content="Docutils: http://docutils.sourceforge.net/" />
<title>Move Stock Location</title>
<style type="text/css">
@@ -408,6 +408,14 @@ opened.</li>
<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>
<p>If you want transfer everything from stock.location</p>
<p>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 wont be listed in the
operations.
Use barcode interface to scan a location and create an empty picking. Then use
the fill with stock button.</p>
</div>
<div class="section" id="known-issues-roadmap">
<h1><a class="toc-backref" href="#id2">Known issues / Roadmap</a></h1>
@@ -433,6 +441,8 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<h2><a class="toc-backref" href="#id5">Authors</a></h2>
<ul class="simple">
<li>Julius Network Solutions</li>
<li>BCIM</li>
<li>Camptocamp</li>
</ul>
</div>
<div class="section" id="contributors">
@@ -449,6 +459,8 @@ If you spotted it first, help us smashing it by providing a detailed and welcome
<li>João Marques</li>
</ul>
</li>
<li>Jacques-Etienne Baudoux &lt;<a class="reference external" href="mailto:je&#64;bcim.be">je&#64;bcim.be</a>&gt;</li>
<li>Iryna Vyshnevska &lt;<a class="reference external" href="mailto:i.vyshnevska&#64;mobilunity.com">i.vyshnevska&#64;mobilunity.com</a>&gt;</li>
</ul>
</div>
<div class="section" id="maintainers">

View File

@@ -1,6 +1,3 @@
# 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
from . import test_stock_fillwithstock

View File

@@ -0,0 +1,90 @@
# 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,
)

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" ?>
<odoo>
<record id="view_picking_form" model="ir.ui.view">
<field name="name">stock.picking.form.fillwithstock</field>
<field name="model">stock.picking</field>
<field name="inherit_id" ref="stock.view_picking_form" />
<field name="arch" type="xml">
<button name="action_confirm" position="before">
<button
name="button_fillwithstock"
states="draft"
string="Fill with stock"
type="object"
class="oe_highlight"
groups="stock.group_stock_user"
/>
</button>
</field>
</record>
</odoo>

View File

@@ -3,6 +3,8 @@
# Copyright 2019 Sergio Teruel - Tecnativa <sergio.teruel@tecnativa.com>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from itertools import groupby
from odoo import api, fields, models
from odoo.fields import first
@@ -82,26 +84,60 @@ class StockMoveLocationWizard(models.TransientModel):
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["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,
"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
]
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,
"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,
},
)
)
return res
@api.onchange("origin_location_id")
def _onchange_origin_location_id(self):
if not self.env.context.get("origin_location_disable", False):
@@ -209,7 +245,10 @@ class StockMoveLocationWizard(models.TransientModel):
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"):
moves_to_reassign = self._unreserve_moves()