prevent user from creating lots from the wizard

This commit is contained in:
mpanarin
2019-01-16 14:30:41 +02:00
committed by João Marques
parent 3546b770a4
commit a3cda8db38
6 changed files with 157 additions and 25 deletions

View File

@@ -3,3 +3,4 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from . import wizard
from . import models

View File

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

View File

@@ -0,0 +1,20 @@
# 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="Wether 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:
if move.location_move:
move.show_details_visible = True

View File

@@ -2,6 +2,8 @@
# Copyright 2018 Camptocamp SA
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
from itertools import groupby
from odoo import api, fields, models
@@ -34,6 +36,19 @@ class StockMoveLocationWizard(models.TransientModel):
def _onchange_locations(self):
self._clear_lines()
@api.onchange("stock_move_location_line_ids")
def _onchange_stock_move_location_line_ids(self):
lines_to_update = self.stock_move_location_line_ids.filtered(
lambda x: x.custom is True and
not all([x.origin_location_id, x.destination_location_id])
)
lines_to_update.update({
"origin_location_id": self.origin_location_id,
"destination_location_id": self.destination_location_id,
})
# for an easier extension of this function
return lines_to_update
def _clear_lines(self):
origin = self.origin_location_id
destination = self.destination_location_id
@@ -54,8 +69,55 @@ class StockMoveLocationWizard(models.TransientModel):
'location_dest_id': self.destination_location_id.id,
})
@api.multi
def group_lines(self):
sorted_lines = sorted(
self.stock_move_location_line_ids,
key=lambda x: x.product_id,
)
groups = groupby(sorted_lines, key=lambda x: x.product_id)
groups_dict = {}
for prod, lines in groups:
groups_dict[prod.id] = list(lines)
return groups_dict
@api.multi
def _create_moves(self, picking):
return self.stock_move_location_line_ids.create_move_lines(picking)
self.ensure_one()
groups = self.group_lines()
moves = self.env["stock.move"]
for group, lines in groups.items():
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_id = lines[0].product_id.id
product_uom_id = lines[0].product_uom_id.id
qty = sum([x.move_quantity for x in lines])
return {
"name": "test",
"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,
}
@api.multi
def _create_move(self, picking, lines):
self.ensure_one()
move = self.env["stock.move"].create(
self._get_move_values(picking, lines),
)
for line in lines:
line.create_move_lines(picking, move)
return move
@api.multi
def action_move_location(self):
@@ -63,6 +125,8 @@ class StockMoveLocationWizard(models.TransientModel):
picking = self._create_picking()
self._create_moves(picking)
if not self.env.context.get("planned"):
picking.action_confirm()
picking.action_assign()
picking.button_validate()
self.picking_id = picking
return self._get_picking_action(picking.id)
@@ -111,6 +175,7 @@ class StockMoveLocationWizard(models.TransientModel):
'lot_id': group.get("lot_id") or False,
'product_uom_id': product.uom_id.id,
'move_location_wizard_id': self.id,
'custom': False,
})
return product_data
@@ -118,7 +183,9 @@ class StockMoveLocationWizard(models.TransientModel):
self.ensure_one()
if not self.stock_move_location_line_ids:
for line_val in self._get_stock_move_location_lines_values():
self.env["wiz.stock.move.location.line"].create(line_val).id
if line_val.get('max_quantity') <= 0:
continue
self.env["wiz.stock.move.location.line"].create(line_val)
return {
"type": "ir.actions.do_nothing",
}

View File

@@ -16,15 +16,16 @@
<button name="clear_lines" string="Clear all" type="object" class="btn-primary"/>
</group>
<group name="lines">
<field name="stock_move_location_line_ids" nolabel="1">
<field name="stock_move_location_line_ids" nolabel="1" >
<tree string="Inventory Details" editable="bottom" decoration-info="move_quantity != max_quantity" decoration-danger="(move_quantity &lt; 0) or (move_quantity > max_quantity)">
<field name="product_id" domain="[('type','=','product')]"/>
<field name="product_uom_id" string="UoM" groups="product.group_uom"/>
<field name="origin_location_id"/>
<field name="destination_location_id"/>
<field name="lot_id" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}" groups="stock.group_production_lot"/>
<field name="origin_location_id" readonly="1" />
<field name="destination_location_id" readonly="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="max_quantity"/>
<field name="custom" invisible="1" />
<field name="max_quantity" attrs="{'readonly': [('custom', '!=', True)]}" />
</tree>
</field>
</group>
@@ -48,7 +49,7 @@
<menuitem
id="menuitem_move_location"
string="Move from location..."
parent="stock.menu_stock_root"
parent="stock.menu_stock_warehouse_mgmt"
action="wiz_stock_move_location_action"
sequence="99"/>

View File

@@ -5,6 +5,7 @@
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
class StockMoveLocationWizardLine(models.TransientModel):
@@ -24,12 +25,10 @@ class StockMoveLocationWizardLine(models.TransientModel):
origin_location_id = fields.Many2one(
string='Origin Location',
comodel_name='stock.location',
readonly=True,
)
destination_location_id = fields.Many2one(
string='Destination Location',
comodel_name='stock.location',
readonly=True,
)
product_uom_id = fields.Many2one(
string='Product Unit of Measure',
@@ -47,34 +46,50 @@ class StockMoveLocationWizardLine(models.TransientModel):
max_quantity = fields.Float(
string="Maximum available quantity",
digits=dp.get_precision('Product Unit of Measure'),
readonly=True,
)
custom = fields.Boolean(
string="Custom line",
default=True,
)
@api.model
def get_rounding(self):
return self.env.ref("product.decimal_product_uom").digits or 3
@api.constrains("max_quantity", "move_quantity")
def _contraints_max_move_quantity(self):
def _constraint_max_move_quantity(self):
for record in self:
if (record.move_quantity > record.max_quantity or
record.move_quantity < 0):
if (float_compare(
record.move_quantity,
record.max_quantity, self.get_rounding()) == 1 or
float_compare(record.move_quantity, 0.0,
self.get_rounding()) == -1):
raise ValidationError(_(
"Move quantity can not exceed max quantity or be negative"
))
def create_move_lines(self, picking):
def create_move_lines(self, picking, move):
for line in self:
values = line._get_move_line_values(picking, move)
if values.get("qty_done") <= 0:
continue
self.env["stock.move.line"].create(
self._get_move_line_values(line, picking)
values
)
return True
def _get_move_line_values(self, line, picking):
@api.multi
def _get_move_line_values(self, picking, move):
self.ensure_one()
return {
"product_id": line.product_id.id,
"lot_id": line.lot_id.id,
"location_id": line.origin_location_id.id,
"location_dest_id": line.destination_location_id.id,
"qty_done": line._get_available_quantity(),
"product_uom_id": line.product_uom_id.id,
"product_id": self.product_id.id,
"lot_id": self.lot_id.id,
"location_id": self.origin_location_id.id,
"location_dest_id": self.destination_location_id.id,
"qty_done": self._get_available_quantity(),
"product_uom_id": self.product_uom_id.id,
"picking_id": picking.id,
"move_id": move.id,
}
def _get_available_quantity(self):
@@ -85,10 +100,20 @@ class StockMoveLocationWizardLine(models.TransientModel):
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
# switched to sql here to improve performance and lower db queries
self.env.cr.execute(self._get_specific_quants_sql())
available_qty = self.env.cr.fetchone()[0]
if available_qty < self.move_quantity:
available_qty = self.env.cr.fetchone()
if not available_qty:
# if it is immediate transfer and product doesn't exist in that
# location -> make the transfer of 0.
return 0
available_qty = available_qty[0]
if float_compare(
available_qty,
self.move_quantity, self.get_rounding()) == -1:
return available_qty
return self.move_quantity
@@ -109,3 +134,17 @@ class StockMoveLocationWizardLine(models.TransientModel):
product=self.product_id.id,
lot=lot,
)
@api.model
def create(self, vals):
res = super().create(vals)
# update of wizard lines is extremely buggy
# so i have to handle this additionally in create
if not all([res.origin_location_id, res.destination_location_id]):
or_loc_id = res.move_location_wizard_id.origin_location_id.id
des_loc_id = res.move_location_wizard_id.destination_location_id.id
res.write({
"origin_location_id": or_loc_id,
"destination_location_id": des_loc_id,
})
return res