mirror of
https://github.com/OCA/stock-logistics-warehouse.git
synced 2025-01-21 14:27:28 +02:00
[IMP] stock_move_location: black, isort
This commit is contained in:
committed by
Alex Cuellar
parent
e3131c7325
commit
50ddacfe53
@@ -5,19 +5,16 @@
|
||||
{
|
||||
"name": "Move Stock Location",
|
||||
"version": "12.0.1.2.0",
|
||||
"author": "Julius Network Solutions, "
|
||||
"Odoo Community Association (OCA)",
|
||||
"author": "Julius Network Solutions, " "Odoo Community Association (OCA)",
|
||||
"summary": "This module allows to move all stock "
|
||||
"in a stock location to an other one.",
|
||||
"in a stock location to an other one.",
|
||||
"website": "https://github.com/OCA/stock-logistics-warehouse",
|
||||
'license': 'AGPL-3',
|
||||
"depends": [
|
||||
"stock",
|
||||
],
|
||||
"license": "AGPL-3",
|
||||
"depends": ["stock"],
|
||||
"category": "Stock",
|
||||
"data": [
|
||||
'data/stock_quant_view.xml',
|
||||
'views/stock_picking_type_views.xml',
|
||||
'wizard/stock_move_location.xml',
|
||||
"data/stock_quant_view.xml",
|
||||
"views/stock_picking_type_views.xml",
|
||||
"wizard/stock_move_location.xml",
|
||||
],
|
||||
}
|
||||
|
||||
@@ -7,19 +7,20 @@ class StockPickingType(models.Model):
|
||||
_inherit = "stock.picking.type"
|
||||
|
||||
show_move_onhand = fields.Boolean(
|
||||
string='Show Move On hand stock',
|
||||
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.")
|
||||
"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,
|
||||
"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
|
||||
|
||||
@@ -19,4 +19,4 @@ 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.
|
||||
and you will be directed to the wizard.
|
||||
|
||||
@@ -6,7 +6,6 @@ from odoo.tests import common
|
||||
|
||||
|
||||
class TestsCommon(common.SavepointCase):
|
||||
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super().setUpClass()
|
||||
@@ -17,83 +16,65 @@ class TestsCommon(common.SavepointCase):
|
||||
cls.quant_obj = cls.env["stock.quant"]
|
||||
|
||||
# Enable multi-locations:
|
||||
wizard = cls.env['res.config.settings'].create({
|
||||
'group_stock_multi_locations': True,
|
||||
})
|
||||
wizard = cls.env["res.config.settings"].create(
|
||||
{"group_stock_multi_locations": True}
|
||||
)
|
||||
wizard.execute()
|
||||
|
||||
cls.internal_loc_1 = cls.location_obj.create({
|
||||
"name": "INT_1",
|
||||
"usage": "internal",
|
||||
"active": True,
|
||||
})
|
||||
cls.internal_loc_2 = cls.location_obj.create({
|
||||
"name": "INT_2",
|
||||
"usage": "internal",
|
||||
"active": True,
|
||||
})
|
||||
cls.uom_unit = cls.env.ref('uom.product_uom_unit')
|
||||
cls.product_no_lots = product_obj.create({
|
||||
"name": "Pineapple",
|
||||
"type": "product",
|
||||
"tracking": "none",
|
||||
'category_id': cls.env.ref('product.product_category_all').id,
|
||||
})
|
||||
cls.product_lots = product_obj.create({
|
||||
"name": "Pineapple",
|
||||
"type": "product",
|
||||
"tracking": "lot",
|
||||
'category_id': cls.env.ref('product.product_category_all').id,
|
||||
})
|
||||
cls.lot1 = cls.env['stock.production.lot'].create({
|
||||
'product_id': cls.product_lots.id,
|
||||
})
|
||||
cls.lot2 = cls.env['stock.production.lot'].create({
|
||||
'product_id': cls.product_lots.id,
|
||||
})
|
||||
cls.lot3 = cls.env['stock.production.lot'].create({
|
||||
'product_id': cls.product_lots.id,
|
||||
})
|
||||
cls.internal_loc_1 = cls.location_obj.create(
|
||||
{"name": "INT_1", "usage": "internal", "active": True}
|
||||
)
|
||||
cls.internal_loc_2 = cls.location_obj.create(
|
||||
{"name": "INT_2", "usage": "internal", "active": True}
|
||||
)
|
||||
cls.uom_unit = cls.env.ref("uom.product_uom_unit")
|
||||
cls.product_no_lots = product_obj.create(
|
||||
{
|
||||
"name": "Pineapple",
|
||||
"type": "product",
|
||||
"tracking": "none",
|
||||
"category_id": cls.env.ref("product.product_category_all").id,
|
||||
}
|
||||
)
|
||||
cls.product_lots = product_obj.create(
|
||||
{
|
||||
"name": "Pineapple",
|
||||
"type": "product",
|
||||
"tracking": "lot",
|
||||
"category_id": cls.env.ref("product.product_category_all").id,
|
||||
}
|
||||
)
|
||||
cls.lot1 = cls.env["stock.production.lot"].create(
|
||||
{"product_id": cls.product_lots.id}
|
||||
)
|
||||
cls.lot2 = cls.env["stock.production.lot"].create(
|
||||
{"product_id": cls.product_lots.id}
|
||||
)
|
||||
cls.lot3 = cls.env["stock.production.lot"].create(
|
||||
{"product_id": cls.product_lots.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_no_lots,
|
||||
self.internal_loc_1,
|
||||
123,
|
||||
self.product_lots, self.internal_loc_1, 1, lot_id=self.lot1
|
||||
)
|
||||
self.set_product_amount(
|
||||
self.product_lots,
|
||||
self.internal_loc_1,
|
||||
1,
|
||||
lot_id=self.lot1,
|
||||
self.product_lots, self.internal_loc_1, 1, lot_id=self.lot2
|
||||
)
|
||||
self.set_product_amount(
|
||||
self.product_lots,
|
||||
self.internal_loc_1,
|
||||
1,
|
||||
lot_id=self.lot2,
|
||||
)
|
||||
self.set_product_amount(
|
||||
self.product_lots,
|
||||
self.internal_loc_1,
|
||||
1,
|
||||
lot_id=self.lot3,
|
||||
self.product_lots, self.internal_loc_1, 1, 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,
|
||||
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,
|
||||
self.env["stock.quant"]._get_available_quantity(
|
||||
product, location, lot_id=lot_id
|
||||
),
|
||||
amount,
|
||||
)
|
||||
|
||||
@@ -2,51 +2,37 @@
|
||||
# Copyright 2018 Camptocamp SA
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||
|
||||
from .test_common import TestsCommon
|
||||
from odoo.exceptions import ValidationError
|
||||
|
||||
from .test_common import TestsCommon
|
||||
|
||||
|
||||
class TestMoveLocation(TestsCommon):
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.setup_product_amounts()
|
||||
|
||||
def _create_wizard(self, origin_location, destination_location):
|
||||
return self.wizard_obj.create({
|
||||
"origin_location_id": origin_location.id,
|
||||
"destination_location_id": destination_location.id,
|
||||
})
|
||||
return self.wizard_obj.create(
|
||||
{
|
||||
"origin_location_id": origin_location.id,
|
||||
"destination_location_id": destination_location.id,
|
||||
}
|
||||
)
|
||||
|
||||
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,
|
||||
)
|
||||
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."""
|
||||
@@ -61,27 +47,16 @@ class TestMoveLocation(TestsCommon):
|
||||
wizard.onchange_origin_location()
|
||||
# reserve some quants
|
||||
self.quant_obj._update_reserved_quantity(
|
||||
self.product_no_lots,
|
||||
self.internal_loc_1,
|
||||
50,
|
||||
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,
|
||||
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,
|
||||
)
|
||||
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."""
|
||||
@@ -91,7 +66,8 @@ class TestMoveLocation(TestsCommon):
|
||||
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')
|
||||
"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)
|
||||
@@ -100,33 +76,33 @@ class TestMoveLocation(TestsCommon):
|
||||
"""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 = wizard.with_context({"planned": True})
|
||||
wizard.action_move_location()
|
||||
picking = wizard.picking_id
|
||||
self.assertEqual(picking.state, 'assigned')
|
||||
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],
|
||||
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_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,
|
||||
})
|
||||
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.destination_location_id = self.internal_loc_1
|
||||
wizard._onchange_destination_location_id()
|
||||
self.assertEqual(
|
||||
lines.mapped('destination_location_id'), self.internal_loc_1)
|
||||
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)
|
||||
|
||||
@@ -9,56 +9,60 @@ from odoo.fields import first
|
||||
|
||||
class StockMoveLocationWizard(models.TransientModel):
|
||||
_name = "wiz.stock.move.location"
|
||||
_description = 'Wizard move location'
|
||||
_description = "Wizard move location"
|
||||
|
||||
@api.multi
|
||||
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
|
||||
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."
|
||||
help="technical field to disable the edition of origin location.",
|
||||
)
|
||||
origin_location_id = fields.Many2one(
|
||||
string='Origin Location',
|
||||
comodel_name='stock.location',
|
||||
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."
|
||||
help="technical field to disable the edition of destination location.",
|
||||
)
|
||||
destination_location_id = fields.Many2one(
|
||||
string='Destination Location',
|
||||
comodel_name='stock.location',
|
||||
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",
|
||||
string="Move Location lines", comodel_name="wiz.stock.move.location.line"
|
||||
)
|
||||
picking_type_id = fields.Many2one(
|
||||
comodel_name='stock.picking.type',
|
||||
default=_get_default_picking_type_id,
|
||||
comodel_name="stock.picking.type", default=_get_default_picking_type_id
|
||||
)
|
||||
picking_id = fields.Many2one(
|
||||
string="Connected Picking",
|
||||
comodel_name="stock.picking",
|
||||
string="Connected Picking", comodel_name="stock.picking"
|
||||
)
|
||||
edit_locations = fields.Boolean(string='Edit Locations',
|
||||
default=True)
|
||||
edit_locations = fields.Boolean(string="Edit Locations", default=True)
|
||||
|
||||
@api.depends('edit_locations')
|
||||
@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)
|
||||
"origin_location_disable", False
|
||||
)
|
||||
if not rec.edit_locations:
|
||||
rec.origin_location_disable = True
|
||||
rec.destination_location_disable = True
|
||||
@@ -66,29 +70,37 @@ class StockMoveLocationWizard(models.TransientModel):
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super().default_get(fields)
|
||||
if self.env.context.get('active_model', False) != 'stock.quant':
|
||||
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,
|
||||
'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
|
||||
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["origin_location_id"] = first(quants).location_id.id
|
||||
return res
|
||||
|
||||
@api.onchange('origin_location_id')
|
||||
@api.onchange("origin_location_id")
|
||||
def _onchange_origin_location_id(self):
|
||||
if not self.env.context.get('origin_location_disable', False):
|
||||
if not self.env.context.get("origin_location_disable", False):
|
||||
self._clear_lines()
|
||||
|
||||
@api.onchange('destination_location_id')
|
||||
@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
|
||||
@@ -97,22 +109,23 @@ class StockMoveLocationWizard(models.TransientModel):
|
||||
self.stock_move_location_line_ids = False
|
||||
|
||||
def _get_locations_domain(self):
|
||||
return [('usage', '=', 'internal')]
|
||||
return [("usage", "=", "internal")]
|
||||
|
||||
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,
|
||||
})
|
||||
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,
|
||||
}
|
||||
)
|
||||
|
||||
@api.multi
|
||||
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(),
|
||||
line.product_id.id, self.env["wiz.stock.move.location.line"].browse()
|
||||
)
|
||||
lines_grouped[line.product_id.id] |= line
|
||||
return lines_grouped
|
||||
@@ -148,9 +161,7 @@ class StockMoveLocationWizard(models.TransientModel):
|
||||
@api.multi
|
||||
def _create_move(self, picking, lines):
|
||||
self.ensure_one()
|
||||
move = self.env["stock.move"].create(
|
||||
self._get_move_values(picking, lines),
|
||||
)
|
||||
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)
|
||||
@@ -172,18 +183,14 @@ class StockMoveLocationWizard(models.TransientModel):
|
||||
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,
|
||||
})
|
||||
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.id
|
||||
company = self.env['res.company']._company_default_get(
|
||||
'stock.inventory',
|
||||
)
|
||||
company = self.env["res.company"]._company_default_get("stock.inventory")
|
||||
# Using sql as search_group doesn't support aggregation functions
|
||||
# leading to overhead in queries to DB
|
||||
query = """
|
||||
@@ -197,44 +204,46 @@ class StockMoveLocationWizard(models.TransientModel):
|
||||
return self.env.cr.dictfetchall()
|
||||
|
||||
def _get_stock_move_location_lines_values(self):
|
||||
product_obj = self.env['product.product']
|
||||
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.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("sum"),
|
||||
'max_quantity': group.get("sum"),
|
||||
'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,
|
||||
})
|
||||
location_dest_id = (
|
||||
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("sum"),
|
||||
"max_quantity": group.get("sum"),
|
||||
"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')
|
||||
@api.onchange("origin_location_id")
|
||||
def onchange_origin_location(self):
|
||||
lines = []
|
||||
if self.origin_location_id:
|
||||
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:
|
||||
if line_val.get("max_quantity") <= 0:
|
||||
continue
|
||||
line = line_model.create(line_val)
|
||||
line.max_quantity = line.get_max_quantity()
|
||||
lines.append(line)
|
||||
# self.stock_move_location_line_ids = [(4, line.id)]
|
||||
self.update({'stock_move_location_line_ids': [
|
||||
(6, 0, [line.id for line in lines])]})
|
||||
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",
|
||||
}
|
||||
return {"type": "ir.action.do_nothing"}
|
||||
|
||||
@@ -3,99 +3,88 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.addons import decimal_precision as dp
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.tools import float_compare
|
||||
|
||||
from odoo.addons import decimal_precision as dp
|
||||
|
||||
|
||||
class StockMoveLocationWizardLine(models.TransientModel):
|
||||
_name = "wiz.stock.move.location.line"
|
||||
_description = 'Wizard move location line'
|
||||
_description = "Wizard move location line"
|
||||
|
||||
product_id = fields.Many2one(
|
||||
string="Product",
|
||||
comodel_name="product.product",
|
||||
required=True,
|
||||
string="Product", comodel_name="product.product", required=True
|
||||
)
|
||||
origin_location_id = fields.Many2one(
|
||||
string='Origin Location',
|
||||
comodel_name='stock.location',
|
||||
string="Origin Location", comodel_name="stock.location"
|
||||
)
|
||||
destination_location_id = fields.Many2one(
|
||||
string='Destination Location',
|
||||
comodel_name='stock.location',
|
||||
string="Destination Location", comodel_name="stock.location"
|
||||
)
|
||||
product_uom_id = fields.Many2one(
|
||||
string='Product Unit of Measure',
|
||||
comodel_name='uom.uom',
|
||||
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)]"
|
||||
string="Lot/Serial Number",
|
||||
comodel_name="stock.production.lot",
|
||||
domain="[('product_id','=',product_id)]",
|
||||
)
|
||||
move_quantity = fields.Float(
|
||||
string="Quantity to move",
|
||||
digits=dp.get_precision('Product Unit of Measure'),
|
||||
string="Quantity to move", digits=dp.get_precision("Product Unit of Measure")
|
||||
)
|
||||
max_quantity = fields.Float(
|
||||
string="Maximum available quantity",
|
||||
digits=dp.get_precision('Product Unit of Measure'),
|
||||
)
|
||||
custom = fields.Boolean(
|
||||
string="Custom line",
|
||||
default=True,
|
||||
digits=dp.get_precision("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)
|
||||
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"
|
||||
))
|
||||
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),
|
||||
("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))
|
||||
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']
|
||||
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:
|
||||
if not self.env.context.get("planned") and values.get("qty_done") <= 0:
|
||||
continue
|
||||
self.env["stock.move.line"].create(
|
||||
values
|
||||
)
|
||||
self.env["stock.move.line"].create(values)
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def _get_move_line_values(self, picking, move):
|
||||
self.ensure_one()
|
||||
location_dest_id = self.destination_location_id.get_putaway_strategy(
|
||||
self.product_id).id or self.destination_location_id.id
|
||||
location_dest_id = (
|
||||
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,
|
||||
@@ -121,22 +110,23 @@ class StockMoveLocationWizardLine(models.TransientModel):
|
||||
# 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),
|
||||
("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))
|
||||
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']
|
||||
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
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user