From 809605bb8f16cf1398ff5070ec0d5bd568aef343 Mon Sep 17 00:00:00 2001 From: Pierre Masereel Date: Tue, 6 Jun 2017 14:35:40 +0200 Subject: [PATCH] [REF] stock, delivery: adapt packages to new implementation --- addons/delivery/models/stock_picking.py | 5 +- addons/delivery/views/delivery_view.xml | 1 + .../wizard/choose_delivery_package.py | 5 +- addons/stock/models/stock_move.py | 5 +- addons/stock/models/stock_pack_operation.py | 4 +- addons/stock/models/stock_picking.py | 61 ++++++++++++------ addons/stock/models/stock_quant.py | 63 +++++++------------ addons/stock/views/stock_picking_views.xml | 7 ++- addons/stock/views/stock_quant_views.xml | 53 +++++++++------- 9 files changed, 111 insertions(+), 93 deletions(-) diff --git a/addons/delivery/models/stock_picking.py b/addons/delivery/models/stock_picking.py index f1dcd4fad53..a919ec8e2c5 100644 --- a/addons/delivery/models/stock_picking.py +++ b/addons/delivery/models/stock_picking.py @@ -11,14 +11,11 @@ class StockQuantPackage(models.Model): _inherit = "stock.quant.package" @api.one - @api.depends('quant_ids', 'children_ids') + @api.depends('quant_ids') def _compute_weight(self): weight = 0 for quant in self.quant_ids: weight += quant.qty * quant.product_id.weight - for pack in self.children_ids: - pack._compute_weight() - weight += pack.weight self.weight = weight weight = fields.Float(compute='_compute_weight') diff --git a/addons/delivery/views/delivery_view.xml b/addons/delivery/views/delivery_view.xml index 806f79dd98e..83494f7ebfe 100644 --- a/addons/delivery/views/delivery_view.xml +++ b/addons/delivery/views/delivery_view.xml @@ -251,6 +251,7 @@ + diff --git a/addons/delivery/wizard/choose_delivery_package.py b/addons/delivery/wizard/choose_delivery_package.py index dd5d256f74a..28cae85a8aa 100644 --- a/addons/delivery/wizard/choose_delivery_package.py +++ b/addons/delivery/wizard/choose_delivery_package.py @@ -40,9 +40,8 @@ class ChooseDeliveryPackage(models.TransientModel): return stock_quant_package.shipping_weight else: picking_id = self.env['stock.picking'].browse(self.env.context['active_id']) - pack_operation_product_ids = [po for po in picking_id.pack_operation_product_ids if po.qty_done > 0 and not po.result_package_id] - pack_operation_pack_ids = [po for po in picking_id.pack_operation_pack_ids if po.qty_done > 0 and not po.result_package_id] - total_weight = sum([po.qty_done * po.product_id.weight for po in pack_operation_product_ids]) + sum([po.package_id.weight for po in pack_operation_pack_ids]) + pack_operation_ids = [po for po in picking_id.pack_operation_ids if po.qty_done > 0 and not po.result_package_id] + total_weight = sum([po.qty_done * po.product_id.weight for po in pack_operation_ids]) return total_weight def put_in_pack(self): diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index e0a813c71bb..83b57949fb4 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -178,10 +178,10 @@ class StockMove(models.Model): 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 - + has_package = move.pack_operation_ids.mapped('package_id') | move.pack_operation_ids.mapped('result_package_id') 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): + and (multi_locations_enabled or move.has_tracking != 'none' or len(move.pack_operation_ids) > 1 or has_package): move.show_details_visible = True else: move.show_details_visible = False @@ -790,6 +790,7 @@ class StockMove(models.Model): break if move.state != 'partially_available': move.state = 'partially_available' + self.mapped('picking_id')._check_entire_pack() @api.multi def action_cancel(self): diff --git a/addons/stock/models/stock_pack_operation.py b/addons/stock/models/stock_pack_operation.py index 40dedc233ce..c7edd707319 100644 --- a/addons/stock/models/stock_pack_operation.py +++ b/addons/stock/models/stock_pack_operation.py @@ -29,12 +29,12 @@ class PackOperation(models.Model): product_uom_qty = fields.Float('Reserved', default=0.0, digits=dp.get_precision('Product Unit of Measure'), required=True) 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') + package_id = fields.Many2one('stock.quant.package', 'Source Package', ondelete='restrict') 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, + ondelete='restrict', required=False, help="If set, the operations are packed into this package") date = fields.Datetime('Date', default=fields.Datetime.now(), required=True) owner_id = fields.Many2one('res.partner', 'Owner', help="Owner of the quants") diff --git a/addons/stock/models/stock_picking.py b/addons/stock/models/stock_picking.py index 8d7bc51e9e3..cd696d6c929 100644 --- a/addons/stock/models/stock_picking.py +++ b/addons/stock/models/stock_picking.py @@ -5,11 +5,13 @@ from collections import namedtuple import json import time +from itertools import groupby from odoo import api, fields, models, _ from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, pycompat -from odoo.tools.float_utils import float_compare +from odoo.tools.float_utils import float_compare, float_round from odoo.addons.procurement.models import procurement from odoo.exceptions import UserError +from operator import itemgetter class PickingType(models.Model): @@ -274,6 +276,10 @@ class Picking(models.Model): 'Has Pack Operations', compute='_compute_pack_operation_exist', help='Check the existence of pack operation on the picking') + has_packages = fields.Boolean( + 'Has Packages', compute='_compute_has_packages', + help='Check the existence of destination packages on move lines') + owner_id = fields.Many2one( 'res.partner', 'Owner', states={'done': [('readonly', True)], 'cancel': [('readonly', True)]}, @@ -367,6 +373,15 @@ class Picking(models.Model): def _compute_pack_operation_exist(self): self.pack_operation_exist = bool(self.pack_operation_ids) + @api.one + def _compute_has_packages(self): + has_packages = False + for pack_op in self.pack_operation_ids: + if pack_op.result_package_id: + has_packages = True + break + self.has_packages = has_packages + @api.onchange('picking_type_id', 'partner_id') def onchange_picking_type(self): if self.picking_type_id: @@ -474,6 +489,7 @@ class Picking(models.Model): if not moves: raise UserError(_('Nothing to check the availability for.')) moves.action_assign() + self._check_entire_pack() return True @api.multi @@ -731,26 +747,26 @@ class Picking(models.Model): @api.multi def _put_in_pack(self): - # TDE FIXME: reclean me - QuantPackage = self.env["stock.quant.package"] package = False for pick in self: - operations = [x for x in pick.pack_operation_ids if x.qty_done > 0 and (not x.result_package_id)] - pack_operation_ids = self.env['stock.pack.operation'] - for operation in operations: - # If we haven't done all qty in operation, we have to split into 2 operation - op = operation - if operation.qty_done < operation.product_qty: - new_operation = operation.copy({'product_qty': operation.qty_done,'qty_done': operation.qty_done}) - - operation.write({'product_qty': operation.product_qty - operation.qty_done,'qty_done': 0}) - - op = new_operation - pack_operation_ids |= op + operations = pick.pack_operation_ids.filtered(lambda o: o.qty_done > 0 and not o.result_package_id) + operation_ids = self.env['stock.pack.operation'] if operations: - pack_operation_ids.check_tracking() - package = QuantPackage.create({}) - pack_operation_ids.write({'result_package_id': package.id}) + package = self.env['stock.quant.package'].create({}) + for operation in operations: + if float_compare(operation.qty_done, operation.product_qty, precision_rounding=operation.product_uom_id.rounding) >= 0: + operation_ids |= operation + else: + quantity_left_todo = float_round( + operation.product_qty - operation.qty_done, + precision_rounding=operation.product_uom_id.rounding, + rounding_method='UP') + new_operation = operation.copy( + default={'product_uom_qty': operation.qty_done, 'qty_done': operation.qty_done}) + operation.write({'product_uom_qty': quantity_left_todo, 'qty_done': 0.0}) + operation_ids |= new_operation + + operation_ids.write({'result_package_id': package.id}) else: raise UserError(_('Please process some quantities to put in the pack first!')) return package @@ -782,3 +798,12 @@ class Picking(models.Model): scraps = self.env['stock.scrap'].search([('picking_id', '=', self.id)]) action['domain'] = [('id', 'in', scraps.ids)] return action + + @api.multi + def action_see_packages(self): + self.ensure_one() + action = self.env.ref('stock.action_package_view').read()[0] + packages = self.pack_operation_ids.mapped('result_package_id') + action['domain'] = [('id', 'in', packages.ids)] + action['context'] = {'picking_id': self.id} + return action diff --git a/addons/stock/models/stock_quant.py b/addons/stock/models/stock_quant.py index 20aea7a754d..7f5b7527e7e 100644 --- a/addons/stock/models/stock_quant.py +++ b/addons/stock/models/stock_quant.py @@ -32,7 +32,7 @@ class StockQuant(models.Model): ondelete='restrict', readonly=True) package_id = fields.Many2one( 'stock.quant.package', 'Package', - help='The package containing this quant', readonly=True) + help='The package containing this quant', readonly=True, ondelete='restrict') owner_id = fields.Many2one( 'res.partner', 'Owner', help='This is the owner of the quant', readonly=True) @@ -233,13 +233,6 @@ class QuantPackage(models.Model): 'Package Reference', copy=False, index=True, default=lambda self: self.env['ir.sequence'].next_by_code('stock.quant.package') or _('Unknown Pack')) quant_ids = fields.One2many('stock.quant', 'package_id', 'Bulk Content', readonly=True) - parent_id = fields.Many2one( - 'stock.quant.package', 'Parent Package', - ondelete='restrict', readonly=True, - help="The package containing this item") - ancestor_ids = fields.One2many('stock.quant.package', string='Ancestors', compute='_compute_ancestor_ids') - children_quant_ids = fields.One2many('stock.quant', string='All Bulk Content', compute='_compute_children_quant_ids') - children_ids = fields.One2many('stock.quant.package', 'parent_id', 'Contained Packages', readonly=True) packaging_id = fields.Many2one( 'product.packaging', 'Package Type', index=True, help="This field should be completed only if everything inside the package share the same product, otherwise it doesn't really makes sense.") @@ -252,28 +245,14 @@ class QuantPackage(models.Model): owner_id = fields.Many2one( 'res.partner', 'Owner', compute='_compute_package_info', search='_search_owner', index=True, readonly=True) + move_line_ids = fields.One2many('stock.pack.operation', 'result_package_id') + current_picking_move_line_ids = fields.One2many('stock.pack.operation', compute="_compute_current_picking_info") + current_picking_id = fields.Boolean(compute="_compute_current_picking_info") - @api.one - @api.depends('parent_id', 'children_ids') - def _compute_ancestor_ids(self): - if self.id: - self.ancestor_ids = self.env['stock.quant.package'].search(['id', 'parent_of', self.id]).ids - - @api.multi - @api.depends('parent_id', 'children_ids', 'quant_ids.package_id') - def _compute_children_quant_ids(self): - for package in self: - if package.id: - package.children_quant_ids = self.env['stock.quant'].search([('package_id', 'child_of', package.id)]).ids - - @api.depends('quant_ids.package_id', 'quant_ids.location_id', 'quant_ids.company_id', 'quant_ids.owner_id', 'ancestor_ids') + @api.depends('quant_ids.package_id', 'quant_ids.location_id', 'quant_ids.company_id', 'quant_ids.owner_id') def _compute_package_info(self): for package in self: - quants = package.children_quant_ids - if quants: - values = quants[0] - else: - values = {'location_id': False, 'company_id': self.env.user.company_id.id, 'owner_id': False} + values = {'location_id': False, 'company_id': self.env.user.company_id.id, 'owner_id': False} package.location_id = values['location_id'] package.company_id = values['company_id'] package.owner_id = values['owner_id'] @@ -286,14 +265,20 @@ class QuantPackage(models.Model): """ Forms complete name of location from parent location to child location. """ res = {} for package in self: - current = package - name = current.name - while current.parent_id: - name = '%s / %s' % (current.parent_id.name, name) - current = current.parent_id + name = package.name res[package.id] = name return res + def _compute_current_picking_info(self): + picking_id = self.env.context.get('picking_id') + if picking_id: + self.current_picking_move_line_ids = self.move_line_ids.filtered(lambda move_line: move_line.picking_id.id == picking_id) + self.current_picking_id = True + else: + self.current_picking_move_line_ids = False + self.current_picking_id = False + + def _search_location(self, operator, value): if value: packs = self.search([('quant_ids.location_id', operator, value)]) @@ -330,10 +315,7 @@ class QuantPackage(models.Model): package) ''' for pack in self: - parent = pack - while parent.parent_id: - parent = parent.parent_id - locations = parent.get_content().filtered(lambda quant: quant.qty > 0.0).mapped('location_id') + locations = pack.get_content().filtered(lambda quant: quant.qty > 0.0).mapped('location_id') if len(locations) != 1: raise UserError(_('Everything inside a package should be in the same location')) return True @@ -341,10 +323,11 @@ class QuantPackage(models.Model): @api.multi def unpack(self): for package in self: - # TDE FIXME: why superuser ? - package.mapped('quant_ids').sudo().write({'package_id': package.parent_id.id}) - package.mapped('children_ids').write({'parent_id': package.parent_id.id}) - return self.env['ir.actions.act_window'].for_xml_id('stock', 'action_package_view') + move_lines_to_remove = self.move_line_ids.filtered(lambda move_line: move_line.state != 'done') + if move_lines_to_remove: + move_lines_to_remove.write({'result_package_id': False}) + else: + package.mapped('quant_ids').write({'package_id': False}) def action_view_picking(self): action = self.env.ref('stock.action_picking_tree_all').read()[0] diff --git a/addons/stock/views/stock_picking_views.xml b/addons/stock/views/stock_picking_views.xml index e94bec27cad..5e68be3ad78 100644 --- a/addons/stock/views/stock_picking_views.xml +++ b/addons/stock/views/stock_picking_views.xml @@ -215,6 +215,7 @@