[REF] stock: views refactoring

We adapted the views to the new stock model, and differenciate two
different views:
 - The first one is more adapted for simple operations, or when no
   suggestion of location, lot, pack is needed. We have a line with
   the initial demand of each product and you can complete the lot
   numer, location, etc in the detailed window of a move.
 - The second one is more adapted for pick and barcode operations, all
   each line is fully detailed with the system reservation, you can see
   the lot, location, etc you must scan in the list view.

You can choose the view you want to use in the picking type settings by
check the box 'Show Operations'
This commit is contained in:
Josse Colpaert
2017-07-14 17:08:21 +02:00
committed by Simon Lejeune
parent 0469d2e712
commit 5d25900cd8
5 changed files with 201 additions and 80 deletions
+76 -17
View File
@@ -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
+6 -4
View File
@@ -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'
+5 -3
View File
@@ -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)
+89 -1
View File
@@ -131,12 +131,94 @@
<field name="availability" invisible="1"/>
<field name="reserved_availability" invisible="1"/>
<field name="location_dest_id" invisible="1"/>
<field name="remaining_qty" invisible="1"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="view_stock_move_operations" model="ir.ui.view">
<field name="name">stock.move.operations.form</field>
<field name="model">stock.move</field>
<field name="priority">1000</field>
<field name="arch" type="xml">
<form string="Move Detail">
<field name="state" invisible="1"/>
<field name="location_id" invisible="1"/>
<field name="location_dest_id" invisible="1"/>
<field name="picking_id" invisible="1"/>
<field name="is_editable" invisible="1"/>
<group>
<group>
<field name="product_id" readonly="1"/>
<label for="product_uom_qty"/>
<div class="o_row">
<span><field name="product_uom_qty" readonly="1" nolabel="1"/></span>
<span><field name="product_uom" readonly="1" nolabel="1"/></span>
</div>
<label for="quantity_done"/>
<div class="o_row">
<span><field name="quantity_done" readonly="1" nolabel="1"/></span>
<span> / </span>
<span><field name="reserved_availability" nolabel="1"/></span>
<span><field name="product_uom" readonly="1" nolabel="1"/></span>
</div>
</group>
</group>
<field name="pack_operation_ids" attrs="{'readonly': ['&amp;', ('state', 'in', ('done', 'cancel')), ('is_editable', '=', False)]}" context="{'default_product_uom_id': product_uom, 'default_picking_id': picking_id, 'default_move_id': id, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
<tree editable="bottom" decoration-succes="product_uom_qty==qty_done" decoration-danger="qty_done > product_uom_qty)">
<field name="picking_id" invisible="1"/>
<field name="product_id" invisible="1"/>
<field name="location_id" invisible="not context.get('show_source_location')" domain="[('id', 'child_of', parent.location_id)])" groups="stock.group_stock_multi_locations"/>
<field name="location_dest_id" invisible="not context.get('show_destination_location')" domain="[('id', 'child_of', parent.location_dest_id)]" groups="stock.group_stock_multi_locations"/>
<field name="lot_id" invisible="not context.get('show_lots_m2o')" domain="[('product_id', '=', parent.product_id)]" groups="stock.group_production_lot" context="{'default_product_id': parent.product_id}"/>
<field name="lot_name" invisible="not context.get('show_lots_text')" groups="stock.group_production_lot"/>
<field name="package_id" invisible="not context.get('show_package')" groups="stock.group_tracking_lot"/>
<field name="result_package_id" groups="stock.group_tracking_lot"/>
<field name="product_uom_qty" readonly="1"/>
<field name="qty_done"/>
<field name="product_uom_id" options="{'no_open': True, 'no_create': True}" attrs="{'readonly': [('product_uom_qty', '!=', 0.0)]}" string="Unit of Measure" groups="product.group_uom"/>
</tree>
</field>
<footer class="oe_edit_only">
<button string="Confirm" special="save" class="oe_highlight"/>
<button string="Discard" special="cancel"/>
</footer>
</form>
</field>
</record>
<record id="view_stock_move_nosuggest_operations" model="ir.ui.view">
<field name="name">stock.move.operations.nosuggest.form</field>
<field name="model">stock.move</field>
<field name="priority">1000</field>
<field name="mode">primary</field>
<field name="inherit_id" ref="stock.view_stock_move_operations"/>
<field name="arch" type="xml">
<field name="pack_operation_ids" position="before">
<field name="quantity_done" readonly="1"/>
<field name="product_uom_qty" readonly="1"/>
</field>
<field name="pack_operation_ids" position="replace">
<field name="pack_operation_nosuggest_ids" attrs="{'readonly': [('state', 'in', ('done', 'cancel'))]}" context="{'default_picking_id': picking_id, 'default_move_id': id, 'default_product_id': product_id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
<tree editable="bottom" decoration-succes="product_uom_qty==qty_done" decoration-danger="(product_uom_qty &gt; 0) and (qty_done&gt;product_uom_qty)">
<field name="location_id" invisible="context.get('hide_source')" domain="[('id', 'child_of', parent.location_id)])" groups="stock.group_stock_multi_locations"/>
<field name="package_id" groups="stock.group_tracking_lot" invisible="context.get('from_supplier')"/>
<field name="lot_id" domain="[('product_id', '=', parent.product_id)]" invisible="context.get('show_lots_inv', True)" context="{'default_product_id': parent.product_id}"/>
<field name="lot_name" invisible="context.get('show_lots_name_inv', True)"/>
<field name="product_uom_qty" invisible="1"/>
<field name="location_dest_id" invisible="context.get('hide_dest')" domain="[('id', 'child_of', parent.location_dest_id)]" groups="stock.group_stock_multi_locations"/>
<field name="qty_done"/>
<field name="product_uom_id" options="{'no_open': True, 'no_create': True}" attrs="{'readonly': [('product_uom_qty', '!=', 0.0)]}" string="Unit of Measure" groups="product.group_uom"/>
<field name="result_package_id" groups="stock.group_tracking_lot"/>
<field name="picking_id" invisible="1"/>
<field name="product_id" invisible="1"/>
</tree>
</field>
</field>
</field>
</record>
<record id="view_move_form" model="ir.ui.view">
<field name="name">stock.move.form</field>
<field name="model">stock.move</field>
@@ -183,6 +265,9 @@
<group name="quants_grp" string="Reserved Quants" colspan="2" groups="base.group_no_one" attrs="{'invisible': [('state', '=', 'done')]}">
<field name="string_availability_info"/>
</group>
<group name="moved_quants_grp" string="Moved Quants" colspan="4" groups="base.group_no_one" attrs="{'invisible': [('state', '!=', 'done')]}">
<!--<field name="quant_ids" readonly="1" nolabel="1"/>-->
</group>
</group>
</sheet>
</form>
@@ -227,6 +312,9 @@
<group name="quants_grp" string="Reserved" attrs="{'invisible': [('state', '=', 'draft')]}">
<field name="string_availability_info"/>
</group>
<group name="moved_quants_grp" string="Moved Quants" colspan="4" groups="base.group_no_one" attrs="{'invisible': [('state', '!=', 'done')]}">
<!--<field name="quant_ids" readonly="1" nolabel="1"/>-->
</group>
</group>
</form>
</field>
+25 -55
View File
@@ -46,6 +46,8 @@
<field name="code"/>
<field attrs='{"invisible": [("code", "not in", ["incoming", "outgoing", "internal"])]}' name="return_picking_type_id"/>
<field attrs='{"invisible": [("code", "not in", ["incoming", "outgoing", "internal"])]}' name="barcode_nomenclature_id" groups="base.group_no_one"/>
<field name="show_operations"/>
<field name="show_reserved"/>
</group>
</group>
<group>
@@ -209,12 +211,13 @@
<button name="%(action_report_delivery)d" string="Print" states="done" type="action" groups="base.group_user"/>
<button name="%(act_stock_return_picking)d" string="Return" states="done" type="action" groups="base.group_user"/>
<button name="action_cancel" states="assigned,confirmed,partially_available,draft,waiting" string="Cancel" groups="base.group_user" type="object"/>
<button name="do_unreserve" string="Unreserve" groups="base.group_user" type="object" attrs="{'invisible': ['|', ('state', 'not in', ('assigned', 'partially_available')), ('location_id', 'in', ('supplier', 'inventory', 'production'))]}"/>
<!--<button name="do_prepare_partial" attrs="{'invisible': ['|', ('launch_pack_operations', '=', True), '|', ('state', 'not in', ('assigned', 'partially_available')), ('pack_operation_ids', '!=', [])]}"-->
<!--string="Reset Operations" type="object"/>-->
<!--<field name="launch_pack_operations" invisible="1"/>-->
<button name="button_scrap" type="object" string="Scrap" attrs="{'invisible': ['|', ('state', 'in', ('draft', 'cancel')), '&amp;', ('picking_type_code', '=', 'incoming'), ('state', '!=', 'done')]}"/>
<field name="picking_type_code" invisible="1"/>
<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"/>
<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"/>
</header>
<sheet>
<div class="oe_button_box" name="button_box">
@@ -236,68 +239,35 @@
<group>
<field name="min_date"/>
<field name="origin" placeholder="e.g. PO0032" class="oe_inline"/>
<label for="owner_id" groups="stock.group_tracking_owner"/>
<!--<label for="owner_id" groups="stock.group_tracking_owner"/>-->
<div groups="stock.group_tracking_owner">
<field name="owner_id"/>
<!--<field name="owner_id"/>-->
<button name="action_assign_owner" string="Assign Owner" type="object" attrs="{'invisible': ['|',('pack_operation_exist', '=', False),('state', 'not in', ('draft','assigned','confirmed'))]}"
class="oe_link oe_edit_only"/>
</div>
</group>
</group>
<notebook>
<page string="Operations" attrs="{'invisible': [('state', 'in', ('draft', 'confirmed', 'waiting'))]}">
<button name="do_prepare_partial" type="object" string="Recompute" attrs="{'invisible': [('recompute_pack_op','=', False)]}"/>
<field name="recompute_pack_op" invisible="1"/>
<field name="pack_operation_ids" options="{'reload_on_button': True}" context="{'default_picking_id': id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}" mode="tree,kanban">
<tree editable="bottom">
<page string="Detailed Operations" attrs="{'invisible': ['|', '&amp;', ('show_operations', '=', True), ('state', '=', 'draft'), ('show_operations', '=', False)]}">
<field name="pack_operation_ids" context="{'default_picking_id': id, 'default_location_id': location_id, 'default_location_dest_id': location_dest_id}">
<tree editable="bottom" decoration-muted="result_package_id" decoration-danger="qty_done&gt;product_uom_qty" decoration-success="qty_done==product_uom_qty and state!='done' and not result_package_id">
<field name="product_id" required="1"/>
<field name="product_uom_id" attrs="{'readonly': [('state', '!=', 'draft')]}" groups="product.group_uom"/>
<field name="from_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="to_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="state" invisible="1"/>
<field name="product_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="qty_done"/>
<button name="action_split_lots" string="Lot Split" type="object" icon="fa-list" groups="stock.group_production_lot"
attrs="{'invisible': ['|', ('state', 'not in', ['confirmed', 'assigned', 'waiting', 'partially_available','done'])]}"/>
<button name="show_details" string="Modify" type="object" icon="fa-pencil" groups="stock.group_stock_multi_locations"
states="confirmed,assigned,waiting,partially_available"/>
</tree>
<kanban class="o_kanban_mobile">
<field name="product_id" required="1" attrs="{'readonly': [('fresh_record', '=', False)]}"/>
<field name="fresh_record" invisible="1"/>
<field name="product_uom_id" attrs="{'readonly': [('fresh_record', '=', False)]}" groups="product.group_uom"/>
<field name="owner_id" groups="stock.group_tracking_owner"/>
<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="from_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="to_loc" groups="stock.group_stock_multi_locations,stock.group_tracking_lot"/>
<field name="result_package_id" readonly="1" 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"/>
<field name="product_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="lot_id" attrs="{'invisible': [('lots_visible', '=', False)]}" domain="[('product_id', '=', product_id)]" context="{'default_product_id': product_id}"/>
<field name="product_uom_qty" readonly="1" attrs="{'required': [('product_id', '!=', False)]}"/>
<field name="qty_done"/>
<templates>
<t t-name="kanban-box">
<div>
<div class="row">
<div class="col-xs-12">
<strong><span><t t-esc="record.product_id.value"/></span></strong>
</div>
</div>
<div class="row">
<div class="col-xs-12 text-muted">
<span><t t-esc="record.from_loc.value"/> to <t t-esc="record.to_loc.value"/></span>
</div>
</div>
<div class="row">
<div class="col-xs-12 text-muted">
<span><t t-esc="record.qty_done.value"/> of <t t-esc="record.product_qty.value"/> <t t-esc="record.product_uom_id.value"/></span>
</div>
</div>
</div>
</t>
</templates>
</kanban>
<button name="show_details" string="Modify" type="object" icon="fa-pencil" groups="stock.group_stock_multi_locations" states="confirmed,assigned,waiting,partially_available"/>
</tree>
</field>
<field name="picking_type_entire_packs" invisible="1"/>
<button class="oe_link oe_right" name="put_in_pack" type="object" string="&#8627;Put in Pack" attrs="{'invisible': [('state', 'in', ('done', 'cancel'))]}" groups="stock.group_tracking_lot"/>
</page>
<page string="Operations">
@@ -318,15 +288,15 @@
<field name="additional" invisible="1"/>
<field name="product_id" required="1" attrs="{'readonly': ['&amp;', ('state', '!=', 'draft'), ('additional', '=', False)]}"/>
<field name="product_uom_qty" string="Initial Demand" attrs="{'readonly': [('state', '!=', 'draft')]}"/>
<field name="is_initial_demand_editable" invisible="1"/>
<field name="product_uom_qty" string="Initial Demand" attrs="{'readonly': [('is_initial_demand_editable', '=', False), ('state', '!=', 'draft')]}"/>
<field name="product_uom" attrs="{'readonly': [('state', '!=', 'draft')]}" options="{'no_open': True, 'no_create': True}" string="Unit of Measure" groups="product.group_uom"/>
<field name="reserved_availability" string="Reserved"/>
<field name="quantity_done" string="Done" attrs="{'readonly': [('show_details_visible', '=', True)], 'invisible': ['|', ('state', 'in', ['draft', 'confirmed', 'waiting']), ('show_operations', '=', True)]}"/>
<button name="action_show_details" string="Register lots, packs, location" type="object" icon="fa-list" attrs="{'invisible': [('show_details_visible', '=', False)]}"/>
</tree>
</field>
<field name="pack_operation_exist" invisible="1"/>
<field name="note" placeholder="Add an internal note that will be printed on the Picking Operations sheet" class="oe_inline"/>
<button class="oe_link oe_right" name="put_in_pack" type="object" string="&#8627;Put in Pack" attrs="{'invisible': [('state', 'in', ('done', 'cancel'))]}" groups="stock.group_tracking_lot"/>
</page>
<page string="Additional Info" name="extra">
<group>
@@ -759,7 +729,7 @@
</div>
</div>
</t>
</templates>
</kanban>
</field>