[REF] stock, delivery: adapt packages to new implementation

This commit is contained in:
Pierre Masereel
2017-07-14 17:08:21 +02:00
committed by Simon Lejeune
parent ff91120416
commit 809605bb8f
9 changed files with 111 additions and 93 deletions
+1 -4
View File
@@ -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')
+1
View File
@@ -251,6 +251,7 @@
<field name="inherit_id" ref="stock.view_quant_package_form"/>
<field name="arch" type="xml">
<field name="company_id" position="before">
<field name="packaging_id"/>
<field name="weight"/>
<field name="shipping_weight"/>
</field>
@@ -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):
+3 -2
View File
@@ -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):
+2 -2
View File
@@ -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")
+43 -18
View File
@@ -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
+23 -40
View File
@@ -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]
+6 -1
View File
@@ -215,6 +215,7 @@
<button name="do_unreserve" string="Unreserve" groups="base.group_user" type="object" attrs="{'invisible': ['|', ('picking_type_code', '=', 'incoming'), ('state', 'not in', ('assigned', 'partially_available'))]}"/>
<field name="show_operations" invisible="1"/>
<field name="pack_operation_exist" invisible="1"/>
<field name="has_packages" invisible="1"/>
<button name="button_scrap" type="object" string="Scrap" attrs="{'invisible': ['|', '&amp;', ('picking_type_code', '!=', 'incoming'), ('state', 'in', ('draft', 'cancel', 'waiting')), '&amp;', ('picking_type_code', '=', 'incoming'), ('state', '!=', 'done')]}"/>
<field name="state" widget="statusbar" statusbar_visible="draft,confirmed,partially_available,assigned,done" />
<field name="picking_type_entire_packs" invisible="1"/>
@@ -225,6 +226,10 @@
<button name="action_see_move_scrap" string="Scraps" type="object"
class="oe_stat_button" icon="fa-arrows-v"
attrs="{'invisible': [('has_scrap_move', '=', False)]}"/>
<button name="action_see_packages" string="Packages" type="object"
class="oe_stat_button" icon="fa-arrows-v"
attrs="{'invisible': [('has_packages', '=', False)]}"/>
<button name="%(action_stock_report)d" icon="fa-arrow-up" class="oe_stat_button" string="Up/Down Traceability" type="action" states="done" groups="stock.group_production_lot"/>
</div>
<h1 class="hidden-xs">
<field name="name" attrs="{'invisible': [('name','=','/')]}" readonly="1"/>
@@ -258,7 +263,7 @@
<field name="package_id" groups="stock.group_tracking_lot" invisible="1"/>
<field name="location_id" domain="[('id', 'child_of', parent.location_id)]" invisible="1"/>
<field name="location_dest_id" domain="[('id', 'child_of', parent.location_dest_id)]" invisible="1"/>
<field name="result_package_id" readonly="1" groups="stock.group_tracking_lot" context="{'default_location_id': location_dest_id}" invisible="1"/>
<field name="result_package_id" groups="stock.group_tracking_lot" context="{'default_location_id': location_dest_id}" invisible="1"/>
<field name="lots_visible" invisible="1"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<field name="state" invisible="1"/>
+30 -23
View File
@@ -140,14 +140,13 @@
<field name="arch" type="xml">
<form string="Package">
<header>
<button name="unpack" string="Unpack" type="object" groups="base.group_no_one"/>
<button name="unpack" string="Unpack" type="object"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
<button class="oe_stat_button" name="action_view_picking"
string="Package Transfers" type="object"
widget="statinfo" icon="fa-dropbox"/>
<button name="get_content_package" type="object" string="View Contained Packages content" icon="fa-cogs" attrs="{'invisible': [('children_ids','=',[])]}"/>
</div>
<div class="oe_title">
<label for="name" string="Package Reference" class="oe_edit_only"/>
@@ -155,34 +154,22 @@
</div>
<group>
<group>
<field name="packaging_id"/>
<field name='company_id' groups="base.group_multi_company"/>
<field name='owner_id' groups="stock.group_tracking_owner"/>
</group>
<group>
<field name="location_id"/>
<field name="parent_id"/>
<field name='company_id' groups="base.group_multi_company"/>
<field name='owner_id' groups="stock.group_tracking_owner"/>
<field name="location_id"/>
</group>
</group>
<notebook>
<page string="Content">
<separator string="Bulk Content" />
<field name="quant_ids">
<tree string="Quants">
<separator string="Products" />
<field name="current_picking_id" invisible="1"/>
<field name="current_picking_move_line_ids" readonly="1" attrs="{'invisible': [('current_picking_id', '=', False)]}">
<tree>
<field name="product_id"/>
<field name="quantity"/>
<field name="location_id"/>
<field name="in_date"/>
</tree>
</field>
<separator string="Contained Packages"/>
<field name="children_ids">
<tree string="Contained Packages">
<field name="display_name"/>
<field name="packaging_id"/>
<field name="location_id"/>
<field name="qty_done" string="Quantity"/>
</tree>
</field>
<field name="quant_ids" attrs="{'invisible': [('current_picking_id', '=', True)]}"/>
</page>
</notebook>
</sheet>
@@ -204,10 +191,30 @@
</field>
</record>
<record model="ir.ui.view" id="view_quant_package_kanban">
<field name="name">stock.quant.package.kanban</field>
<field name="model">stock.quant.package</field>
<field name="arch" type="xml">
<kanban>
<templates>
<field name="name"/>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<div class="oe_kanban_vignette">
<strong><field name="name"/></strong>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<record model="ir.actions.act_window" id="action_package_view">
<field name="context">{}</field>
<field name="name">Packages</field>
<field name="res_model">stock.quant.package</field>
<field name="view_mode">kanban,tree,form</field>
<field name="help" type="html">
<p>Packages are usually created by pack operations made on transfers and can contains several different products. You can then reuse a package to move its whole content somewhere else, or to pack it into another bigger package. A package can also be unpacked, allowing the disposal of its former content as single units again.
</p>