diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py
index 7d785e00358..e0a813c71bb 100644
--- a/addons/stock/models/stock_move.py
+++ b/addons/stock/models/stock_move.py
@@ -50,7 +50,7 @@ class StockMove(models.Model):
digits=0, store=True,
help='Quantity in the default UoM of the product')
product_uom_qty = fields.Float(
- 'Quantity',
+ 'Initial Demand',
digits=dp.get_precision('Product Unit of Measure'),
default=1.0, required=True, states={'done': [('readonly', True)]},
help="This is the quantity of products from an inventory "
@@ -121,10 +121,6 @@ class StockMove(models.Model):
"its current stock) to gather products. If we want to chain moves and have this one to wait for the previous,"
"this second option should be chosen.")
scrapped = fields.Boolean('Scrapped', related='location_dest_id.scrap_location', readonly=True, store=True)
- remaining_qty = fields.Float(
- 'Remaining Quantity', compute='_get_remaining_qty',
- digits=0, states={'done': [('readonly', True)]},
- help="Remaining Quantity in default UoM according to operations matched with this move")
procurement_id = fields.Many2one('procurement.order', 'Procurement')
group_id = fields.Many2one('procurement.group', 'Procurement Group', default=_default_group_id)
rule_id = fields.Many2one('procurement.rule', 'Procurement Rule', ondelete='restrict', help='The procurement rule that created this stock move')
@@ -135,10 +131,12 @@ class StockMove(models.Model):
picking_type_id = fields.Many2one('stock.picking.type', 'Operation Type')
inventory_id = fields.Many2one('stock.inventory', 'Inventory')
pack_operation_ids = fields.One2many('stock.pack.operation', 'move_id')
+ pack_operation_nosuggest_ids = fields.One2many('stock.pack.operation', 'move_id', domain=[('product_qty', '=', 0.0)])
origin_returned_move_id = fields.Many2one('stock.move', 'Origin return move', copy=False, help='Move that created the return move')
returned_move_ids = fields.One2many('stock.move', 'origin_returned_move_id', 'All returned moves', help='Optional: all returned moves created from this move')
reserved_availability = fields.Float(
'Quantity Reserved', compute='_compute_reserved_availability',
+ digits=dp.get_precision('Product Unit of Measure'),
readonly=True, help='Quantity that has already been reserved for this move')
availability = fields.Float(
'Forecasted Quantity', compute='_compute_product_availability',
@@ -151,7 +149,63 @@ class StockMove(models.Model):
route_ids = fields.Many2many('stock.location.route', 'stock_location_route_move', 'move_id', 'route_id', 'Destination route', help="Preferred route to be followed by the procurement order")
warehouse_id = fields.Many2one('stock.warehouse', 'Warehouse', help="Technical field depicting the warehouse to consider for the route selection on the next procurement (if any).")
has_tracking = fields.Selection(related='product_id.tracking', string='Product with Tracking')
- quantity_done = fields.Float('Quantity', compute='_qty_done_compute', digits=dp.get_precision('Product Unit of Measure'))
+ quantity_done = fields.Float('Quantity Done', compute='_quantity_done_compute', digits=dp.get_precision('Product Unit of Measure'), inverse='_quantity_done_set',
+ states={'done': [('readonly', True)]})
+ show_operations = fields.Boolean(related='picking_id.picking_type_id.show_operations')
+ show_details_visible = fields.Boolean('Details Visible', compute='_compute_show_details_visible')
+ show_reserved_availability = fields.Boolean('From Supplier', compute='_compute_show_reserved_availability')
+ picking_code = fields.Selection(related='picking_id.picking_type_id.code', readonly=True)
+ product_type = fields.Selection(related='product_id.type', readonly=True)
+ additional = fields.Boolean("Whether the move was added after the picking's confirmation", default=False)
+ is_editable = fields.Boolean('Is editable when done', compute='_compute_is_editable')
+ is_initial_demand_editable = fields.Boolean('Is initial demand editable', compute='_compute_is_initial_demand_editable')
+
+ @api.multi
+ @api.depends('has_tracking', 'pack_operation_ids', 'location_id', 'location_dest_id', 'is_editable')
+ def _compute_show_details_visible(self):
+ """ According to this field, the button that calls `action_show_details` will be displayed
+ to work on a move from its picking form view, or not.
+ """
+ for move in self:
+ if not move.product_id:
+ move.show_details_visible = False
+ continue
+
+ if move.is_editable:
+ move.show_details_visible = True
+ continue
+
+ multi_locations_enabled = False
+ if self.user_has_groups('stock.group_stock_multi_locations'):
+ multi_locations_enabled = move.location_id.child_ids or move.location_dest_id.child_ids
+
+ if move.picking_id.picking_type_id.show_operations is False\
+ and move.state not in ['cancel', 'draft', 'confirmed']\
+ and (multi_locations_enabled or move.has_tracking != 'none' or len(move.pack_operation_ids) > 1):
+ move.show_details_visible = True
+ else:
+ move.show_details_visible = False
+
+ @api.multi
+ def _compute_show_reserved_availability(self):
+ """ This field is only of use in an attrs in the picking view, in order to hide the
+ "available" column if the move is coming from a supplier.
+ """
+ for move in self:
+ move.show_reserved_availability = not move.location_id.usage == 'supplier'
+
+ @api.multi
+ def _compute_is_editable(self):
+ """ This field is only of use in an attrs in the picking view, in order to show
+ the button to edit move when they're done.
+ """
+ for move in self:
+ move.is_editable = move.state == 'done' and self.user_has_groups('stock.group_stock_manager')
+
+ @api.multi
+ def _compute_is_initial_demand_editable(self):
+ for move in self:
+ move.is_initial_demand_editable = move.state not in ['done', 'cancel'] and self.user_has_groups('stock.group_stock_manager')
@api.one
@api.depends('product_id', 'product_uom', 'product_uom_qty')
@@ -194,11 +248,6 @@ class StockMove(models.Model):
detect errors. """
raise UserError(_('The requested operation cannot be processed because of a programming error setting the `product_qty` field instead of the `product_uom_qty`.'))
- @api.one
- def _get_remaining_qty(self):
- # TODO: sle jco
- pass
-
@api.one
@api.depends('pack_operation_ids.product_qty')
@@ -258,6 +307,20 @@ class StockMove(models.Model):
if not self._cr.fetchone():
self._cr.execute('CREATE INDEX stock_move_product_location_index ON stock_move (product_id, location_id, location_dest_id, company_id, state)')
+ @api.model
+ def default_get(self, fields_list):
+ # We override the default_get to make stock moves created after the picking was confirmed
+ # directly as available (like a force_assign). This allows to create extra move lines in
+ # the fp view.
+ defaults = super(StockMove, self).default_get(fields_list)
+ if self.env.context.get('default_picking_id'):
+ picking_id = self.env['stock.picking'].browse(self.env.context['default_picking_id'])
+ if picking_id.state not in ['draft', 'confirmed']:
+ defaults['state'] = 'assigned'
+ defaults['product_uom_qty'] = 0.0
+ defaults['additional'] = True
+ return defaults
+
@api.multi
def name_get(self):
res = []
@@ -387,10 +450,6 @@ class StockMove(models.Model):
forgot some of them, use this tool instead. """
return self.filtered(lambda move: move.state not in ('done', 'cancel'))
-
- # Main actions
- # ------------------------------------------------------------
-
@api.multi
def do_unreserve(self):
if any(move.state in ('done', 'cancel') for move in self):
@@ -443,7 +502,8 @@ class StockMove(models.Model):
product = self.product_id.with_context(lang=self.partner_id.lang or self.env.user.lang)
self.name = product.partner_ref
self.product_uom = product.uom_id.id
- self.product_uom_qty = 1.0
+ if self.product_uom_qty:
+ self.product_uom_qty = 1.0
return {'domain': {'product_uom': [('category_id', '=', product.uom_id.category_id.id)]}}
@api.onchange('date')
@@ -945,4 +1005,3 @@ class StockMove(models.Model):
'target': 'new',
'res_id': self.id}
show_picking = action_show_picking
-
diff --git a/addons/stock/models/stock_pack_operation.py b/addons/stock/models/stock_pack_operation.py
index e5fd2a866e0..40dedc233ce 100644
--- a/addons/stock/models/stock_pack_operation.py
+++ b/addons/stock/models/stock_pack_operation.py
@@ -10,7 +10,7 @@ from odoo.tools.float_utils import float_round, float_compare, float_is_zero
class PackOperation(models.Model):
- _name = "stock.pack.operation"
+ _name = "stock.pack.operation" #TODO: change to stock.move.operation
_description = "Packing Operation"
_order = "result_package_id desc, id"
@@ -30,11 +30,12 @@ class PackOperation(models.Model):
ordered_qty = fields.Float('Ordered Quantity', digits=dp.get_precision('Product Unit of Measure'))
qty_done = fields.Float('Done', default=0.0, digits=dp.get_precision('Product Unit of Measure'), copy=False)
package_id = fields.Many2one('stock.quant.package', 'Source Package')
+ lot_id = fields.Many2one('stock.production.lot', 'Lot')
+ lot_name = fields.Char('Lot/Serial Number')
result_package_id = fields.Many2one(
'stock.quant.package', 'Destination Package',
ondelete='cascade', required=False,
help="If set, the operations are packed into this package")
- lot_id = fields.Many2one('stock.production.lot', 'Lot')
date = fields.Datetime('Date', default=fields.Datetime.now(), required=True)
owner_id = fields.Many2one('res.partner', 'Owner', help="Owner of the quants")
location_id = fields.Many2one('stock.location', 'From', required=True)
@@ -54,9 +55,10 @@ class PackOperation(models.Model):
operation.to_loc = '%s%s' % (operation_sudo.location_dest_id.name, operation_sudo.result_package_id.name or '')
@api.one
+ @api.depends('picking_id.picking_type_id', 'product_id.tracking')
def _compute_lots_visible(self):
- if self.picking_id.picking_type_id and self.product_id.tracking != 'none': # TDE FIXME: not sure correctly migrated
- picking = self.picking_id
+ picking = self.picking_id
+ if picking.picking_type_id and self.product_id.tracking != 'none': # TDE FIXME: not sure correctly migrated
self.lots_visible = picking.picking_type_id.use_existing_lots or picking.picking_type_id.use_create_lots
else:
self.lots_visible = self.product_id.tracking != 'none'
diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py
index de261ea45d6..b848d05976c 100644
--- a/addons/stock/models/stock_picking.py
+++ b/addons/stock/models/stock_picking.py
@@ -40,6 +40,10 @@ class PickingType(models.Model):
use_existing_lots = fields.Boolean(
'Use Existing Lots/Serial Numbers', default=True,
help="If this is checked, you will be able to choose the Lots/Serial Numbers. You can also decide to not put lots in this operation type. This means it will create stock with no lot or not put a restriction on the lot taken. ")
+ show_operations = fields.Boolean(
+ 'Show Operations', default=False)
+ show_reserved = fields.Boolean(
+ 'Show Reserved', default=True)
# Statistics for the kanban view
last_done_picking = fields.Char('Last 10 Done Pickings', compute='_compute_last_done_picking')
@@ -263,7 +267,7 @@ class Picking(models.Model):
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
pack_operation_ids = fields.One2many(
- 'stock.pack.operation', 'picking_id', 'Related Packing Operations',
+ 'stock.pack.operation', 'picking_id', 'Operations',
states={'done': [('readonly', True)], 'cancel': [('readonly', True)]})
pack_operation_exist = fields.Boolean(
@@ -296,7 +300,6 @@ class Picking(models.Model):
- one of the move is assigned or partially available: partially available
- otherwise in waiting or confirmed state
'''
- # FIXME: goes in 3 times at each write?
if not self.move_lines:
self.state = 'draft'
elif any(move.state == 'draft' for move in self.move_lines): # TDE FIXME: should be all ?
@@ -514,7 +517,6 @@ class Picking(models.Model):
# 'picking_id': pick.id
# }) # Might change first element
# # Link existing moves or add moves when no one is related
-
for ops in pick.pack_operation_ids.filtered(lambda x: not x.move_id):
# Search move with this product
moves = pick.move_lines.filtered(lambda x: x.product_id == ops.product_id)
diff --git a/addons/stock/views/stock_move_views.xml b/addons/stock/views/stock_move_views.xml
index 426cf829172..6ddb7de4ffb 100644
--- a/addons/stock/views/stock_move_views.xml
+++ b/addons/stock/views/stock_move_views.xml
@@ -131,12 +131,94 @@
-
+
+ stock.move.operations.form
+ stock.move
+ 1000
+
+
+
+
+
+
+ stock.move.operations.nosuggest.form
+ stock.move
+ 1000
+ primary
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
stock.move.formstock.move
@@ -183,6 +265,9 @@
+
+
+
@@ -227,6 +312,9 @@
+
+
+
diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml
index 727927af368..e94bec27cad 100644
--- a/addons/stock/views/stock_picking_views.xml
+++ b/addons/stock/views/stock_picking_views.xml
@@ -46,6 +46,8 @@
+
+
@@ -209,12 +211,13 @@
-
-
-
-
-
+
+
+
+
+
+