[MERGE] stock, mrp, repair: traceability report improvements
- remove the upstream/downstream logic, always display the most recent move at the top and the least one at the end - color in move in green, out move in red - better integration with repair and mo - when trying to drill down an untracked component, don't display all the possibilities - few usability fixes overall Closes #19737 task 36338
This commit is contained in:
@@ -238,6 +238,7 @@ class StockMove(models.Model):
|
||||
'move_id': self.id,
|
||||
'product_id': self.product_id.id,
|
||||
'location_id': location_id.id if location_id else self.location_id.id,
|
||||
'production_id': self.raw_material_production_id.id,
|
||||
'location_dest_id': self.location_dest_id.id,
|
||||
'product_uom_qty': 0,
|
||||
'product_uom_id': self.product_uom.id,
|
||||
|
||||
@@ -4,8 +4,8 @@ class MrpStockReport(models.TransientModel):
|
||||
_inherit = 'stock.traceability.report'
|
||||
|
||||
@api.model
|
||||
def get_links(self, move_line):
|
||||
res_model, res_id, ref = super(MrpStockReport, self).get_links(move_line)
|
||||
def _get_reference(self, move_line):
|
||||
res_model, res_id, ref = super(MrpStockReport, self)._get_reference(move_line)
|
||||
if move_line.production_id:
|
||||
res_model = 'mrp.production'
|
||||
res_id = move_line.production_id.id
|
||||
@@ -19,3 +19,12 @@ class MrpStockReport(models.TransientModel):
|
||||
res_id = move_line.move_id.consume_unbuild_id.id
|
||||
ref = move_line.move_id.consume_unbuild_id.name
|
||||
return res_model, res_id, ref
|
||||
|
||||
@api.model
|
||||
def _get_linked_move_lines(self, move_line):
|
||||
move_lines, is_used = super(MrpStockReport, self)._get_linked_move_lines(move_line)
|
||||
if not move_lines:
|
||||
move_lines = (move_line.move_id.consume_unbuild_id and move_line.produce_line_ids) or (move_line.move_id.production_id and move_line.consume_line_ids)
|
||||
if not is_used:
|
||||
is_used = (move_line.move_id.unbuild_id and move_line.consume_line_ids) or (move_line.move_id.raw_material_production_id and move_line.produce_line_ids)
|
||||
return move_lines, is_used
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
<field name="post_visible" invisible="1"/>
|
||||
<field name="unreserve_visible" invisible="1"/>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(stock.action_stock_report)d" icon="fa-arrow-up" class="oe_stat_button" string="Traceability Report" type="action" states="done" groups="stock.group_production_lot"/>
|
||||
<button name="%(action_mrp_workorder_production_specific)d" type="action" attrs="{'invisible': [('workorder_count', '=', 0)]}" class="oe_stat_button" icon="fa-play-circle-o">
|
||||
<div class="o_field_widget o_stat_info">
|
||||
<span class="o_stat_value"><field name="workorder_done_count" widget="statinfo" nolabel="1"/> / <field name="workorder_count" widget="statinfo" nolabel="1"/></span>
|
||||
|
||||
@@ -88,11 +88,11 @@
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_mrp_traceability"
|
||||
name="Traceability: Lots"
|
||||
parent="menu_mrp_reporting"
|
||||
name="Lots/Serial Numbers"
|
||||
parent="menu_mrp_bom"
|
||||
action="stock.action_production_lot_form"
|
||||
groups="stock.group_production_lot"
|
||||
sequence="10"/>
|
||||
sequence="15"/>
|
||||
|
||||
<menuitem id="menu_mrp_scrap"
|
||||
name="Scrap Orders"
|
||||
|
||||
@@ -4,10 +4,19 @@ class MrpStockReport(models.TransientModel):
|
||||
_inherit = 'stock.traceability.report'
|
||||
|
||||
@api.model
|
||||
def get_links(self, move_line):
|
||||
res_model, res_id, ref = super(MrpStockReport, self).get_links(move_line)
|
||||
def _get_reference(self, move_line):
|
||||
res_model, res_id, ref = super(MrpStockReport, self)._get_reference(move_line)
|
||||
if move_line.move_id.repair_id:
|
||||
res_model = 'repair.order'
|
||||
res_id = move_line.move_id.repair_id.id
|
||||
ref = move_line.move_id.repair_id.name
|
||||
return res_model, res_id, ref
|
||||
|
||||
@api.model
|
||||
def _get_linked_move_lines(self, move_line):
|
||||
move_lines, is_used = super(MrpStockReport, self)._get_linked_move_lines(move_line)
|
||||
if not move_lines:
|
||||
move_lines = move_line.move_id.repair_id and move_line.consume_line_ids
|
||||
if not is_used:
|
||||
is_used = move_line.move_id.repair_id and move_line.produce_line_ids
|
||||
return move_lines, is_used
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<odoo>
|
||||
|
||||
<record id="action_stock_report" model="ir.actions.client">
|
||||
<field name="name">Upstream Traceability</field>
|
||||
<field name="name">Traceability Report</field>
|
||||
<field name="tag">stock_report_generic</field>
|
||||
<field name="context" eval="{'url': '/stock/output_format/stock/active_id', 'model': 'stock.traceability.report'}" />
|
||||
</record>
|
||||
|
||||
@@ -20,7 +20,7 @@ class MrpStockReport(models.TransientModel):
|
||||
_name = 'stock.traceability.report'
|
||||
|
||||
@api.model
|
||||
def get_move_lines_upstream(self, move_lines):
|
||||
def _get_move_lines(self, move_lines):
|
||||
res = self.env['stock.move.line']
|
||||
for move_line in move_lines:
|
||||
# if MTO
|
||||
@@ -29,105 +29,50 @@ class MrpStockReport(models.TransientModel):
|
||||
lambda m: m.lot_id.id == move_line.lot_id.id)
|
||||
# if MTS
|
||||
else:
|
||||
if move_line.location_id.usage == 'internal':
|
||||
if move_line:
|
||||
res |= self.env['stock.move.line'].search([
|
||||
('product_id', '=', move_line.product_id.id),
|
||||
('lot_id', '=', move_line.lot_id.id),
|
||||
('location_dest_id', '=', move_line.location_id.id),
|
||||
('id', '!=', move_line.id),
|
||||
('date', '<', move_line.date),
|
||||
])
|
||||
if res:
|
||||
res |= self.get_move_lines_upstream(res)
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def get_move_lines_downstream(self, move_lines):
|
||||
res = self.env['stock.move.line']
|
||||
for move_line in move_lines:
|
||||
# if MTO
|
||||
if move_line.move_id.move_dest_ids:
|
||||
res |= move_line.move_id.move_dest_ids.mapped('move_line_ids').filtered(
|
||||
lambda m: m.lot_id.id == move_line.lot_id.id)
|
||||
# if MTS
|
||||
else:
|
||||
if move_line.location_dest_id.usage == 'internal':
|
||||
res |= self.env['stock.move.line'].search([
|
||||
('product_id', '=', move_line.product_id.id),
|
||||
('lot_id', '=', move_line.lot_id.id),
|
||||
('location_id', '=', move_line.location_dest_id.id),
|
||||
('id', '!=', move_line.id),
|
||||
('date', '>', move_line.date),
|
||||
])
|
||||
if res:
|
||||
res |= self.get_move_lines_downstream(res)
|
||||
res |= self._get_move_lines(res)
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def get_lines(self, line_id=None, **kw):
|
||||
context = dict(self.env.context)
|
||||
stream = context.get('ttype')
|
||||
model = False
|
||||
model_id = False
|
||||
level = 1
|
||||
parent_quant = False
|
||||
if kw:
|
||||
level = kw['level']
|
||||
model = kw['model_name']
|
||||
model_id = kw['model_id']
|
||||
stream = kw['stream']
|
||||
parent_quant = kw['parent_quant']
|
||||
res = []
|
||||
if context.get('active_id') and not context.get('model') or context.get('model') == 'stock.production.lot':
|
||||
if stream == "downstream":
|
||||
move_ids = self.env['stock.move.line'].search([
|
||||
('lot_id', '=', context.get('active_id')),
|
||||
('location_id.usage', '!=', 'internal'),
|
||||
('state', '=', 'done'),
|
||||
('move_id.returned_move_ids', '=', False),
|
||||
])
|
||||
res += self._lines(line_id, model_id=model_id, model='stock.move.line', level=level, parent_quant=parent_quant,
|
||||
stream=stream, obj_ids=move_ids)
|
||||
quant_ids = self.env['stock.quant'].search([
|
||||
('lot_id', '=', context.get('active_id')),
|
||||
('quantity', '<', 0),
|
||||
('location_id.usage', '=', 'internal'),
|
||||
])
|
||||
res += self._lines(line_id, model_id=model_id, model='stock.quant', level=level,
|
||||
parent_quant=parent_quant, stream=stream, obj_ids=quant_ids)
|
||||
model = kw and kw['model_name'] or context.get('model')
|
||||
rec_id = kw and kw['model_id'] or context.get('active_id')
|
||||
level = kw and kw['level'] or 1
|
||||
lines = []
|
||||
move_line = self.env['stock.move.line']
|
||||
if rec_id and model in ('stock.production.lot', 'stock.move.line'):
|
||||
lines = move_line.search([
|
||||
('lot_id', '=', context.get('lot_name') or rec_id),
|
||||
('state', '=', 'done'),
|
||||
('move_id.returned_move_ids', '=', False),
|
||||
])
|
||||
if model == 'stock.move.line':
|
||||
for line in lines:
|
||||
dummy, is_used = self._get_linked_move_lines(line)
|
||||
if is_used:
|
||||
move_line |= is_used
|
||||
lines = move_line
|
||||
elif rec_id and model in ('stock.picking', 'mrp.production'):
|
||||
record = self.env[model].browse(rec_id)
|
||||
if model == 'stock.picking':
|
||||
lines = record.move_lines.mapped('move_line_ids').filtered(lambda m: m.lot_id and m.state == 'done')
|
||||
else:
|
||||
move_ids = self.env['stock.move.line'].search([
|
||||
('lot_id', '=', context.get('active_id')),
|
||||
('location_dest_id.usage', '!=', 'internal'),
|
||||
('state', '=', 'done'),
|
||||
('move_id.returned_move_ids', '=', False),
|
||||
])
|
||||
res += self._lines(line_id, model_id=model_id, model='stock.move.line', level=level, parent_quant=parent_quant,
|
||||
stream=stream, obj_ids=move_ids)
|
||||
quant_ids = self.env['stock.quant'].search([
|
||||
('lot_id', '=', context.get('active_id')),
|
||||
('quantity', '>', 0),
|
||||
('location_id.usage', '=', 'internal'),
|
||||
])
|
||||
res += self._lines(line_id, model_id=model_id, model='stock.quant', level=level,
|
||||
parent_quant=parent_quant, stream=stream, obj_ids=quant_ids)
|
||||
elif context.get('active_id') and context.get('model') == 'stock.picking':
|
||||
move_ids = self.env['stock.picking'].browse(context['active_id']).move_lines.mapped('move_line_ids').filtered(lambda m: m.lot_id and m.state == 'done')
|
||||
res = self._lines(line_id, model_id=model_id, model='stock.move.line', level=level, parent_quant=parent_quant, stream=stream, obj_ids=move_ids)
|
||||
elif context.get('active_id') and context.get('model') == 'stock.move.line':
|
||||
move_line_ids = self.env['stock.move.line'].browse(context.get('active_id'))
|
||||
res = self._lines(line_id, model_id=context.get('active_id'), model=context.get('model'), level=level, parent_quant=parent_quant, stream=stream, obj_ids=move_line_ids)
|
||||
else:
|
||||
res = self._lines(line_id, model_id=model_id, model=model, level=level, parent_quant=parent_quant, stream=stream)
|
||||
reverse_sort = True
|
||||
if stream == "downstream":
|
||||
reverse_sort = False
|
||||
final_vals = sorted(res, key=lambda v: v['date'], reverse=reverse_sort)
|
||||
lines = self.final_vals_to_lines(final_vals, level)
|
||||
lines = record.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.lot_id and m.state == 'done')
|
||||
move_line_vals = self._lines(line_id, model_id=rec_id, model=model, level=level, move_lines=lines)
|
||||
final_vals = sorted(move_line_vals, key=lambda v: v['date'], reverse=True)
|
||||
lines = self._final_vals_to_lines(final_vals, level)
|
||||
return lines
|
||||
|
||||
@api.model
|
||||
def get_links(self, move_line):
|
||||
def _get_reference(self, move_line):
|
||||
res_model = ''
|
||||
ref = ''
|
||||
res_id = False
|
||||
@@ -145,181 +90,101 @@ class MrpStockReport(models.TransientModel):
|
||||
ref = move_line.move_id.scrap_ids[0].name
|
||||
return res_model, res_id, ref
|
||||
|
||||
def make_dict_move(self, level, parent_id, move_line, stream=False, unfoldable=False):
|
||||
res_model, res_id, ref = self.get_links(move_line)
|
||||
def _get_usage(self, move_line):
|
||||
usage = ''
|
||||
if (move_line.location_id.usage == 'internal') and (move_line.location_dest_id.usage == 'internal'):
|
||||
usage = 'internal'
|
||||
elif (move_line.location_id.usage != 'internal') and (move_line.location_dest_id.usage == 'internal'):
|
||||
usage = 'in'
|
||||
else:
|
||||
usage = 'out'
|
||||
return usage
|
||||
|
||||
def _make_dict_move(self, level, parent_id, move_line, unfoldable=False):
|
||||
res_model, res_id, ref = self._get_reference(move_line)
|
||||
dummy, is_used = self._get_linked_move_lines(move_line)
|
||||
unfoldable = False if not move_line.lot_id else unfoldable
|
||||
data = [{
|
||||
'level': level,
|
||||
'unfoldable': unfoldable,
|
||||
'date': move_line.move_id.date,
|
||||
'parent_id': parent_id,
|
||||
'is_used': bool(is_used),
|
||||
'usage': self._get_usage(move_line),
|
||||
'model_id': move_line.id,
|
||||
'model':'stock.move.line',
|
||||
'model': 'stock.move.line',
|
||||
'product_id': move_line.product_id.display_name,
|
||||
'lot_name': move_line.lot_id.name,
|
||||
'lot_id': move_line.lot_id.id,
|
||||
'product_qty_uom': str(move_line.product_uom_id._compute_quantity(move_line.qty_done, move_line.product_id.uom_id, rounding_method='HALF-UP')) + ' ' + move_line.product_id.uom_id.name,
|
||||
'location': move_line.location_id.name + ' -> ' + move_line.location_dest_id.name,
|
||||
'location_source': move_line.location_id.name if not unfoldable or level == 1 else False,
|
||||
'location_destination': move_line.location_dest_id.name if not unfoldable or level == 1 else False,
|
||||
'reference_id': ref,
|
||||
'res_id': res_id,
|
||||
'stream': stream,
|
||||
'res_model': res_model}]
|
||||
return data
|
||||
|
||||
def make_dict_head(self, level, parent_id, model=False, stream=False, move_line=False):
|
||||
data = []
|
||||
if model == 'stock.move.line':
|
||||
data = [{
|
||||
'level': level,
|
||||
'unfoldable': True,
|
||||
'date': move_line.move_id.date,
|
||||
'model_id': move_line.id,
|
||||
'parent_id': parent_id,
|
||||
'model': model or 'stock.move.line',
|
||||
'product_id': move_line.product_id.display_name,
|
||||
'lot_id': move_line.lot_id.name,
|
||||
'product_qty_uom': str(move_line.product_uom_id._compute_quantity(move_line.qty_done, move_line.product_id.uom_id, rounding_method='HALF-UP')) + ' ' + move_line.product_id.uom_id.name,
|
||||
'location': move_line.location_dest_id.name,
|
||||
'stream': stream,
|
||||
'reference_id': False}]
|
||||
elif model == 'stock.quant':
|
||||
data = [{
|
||||
'level': level,
|
||||
'unfoldable': True,
|
||||
'date': move_line.write_date,
|
||||
'model_id': move_line.id,
|
||||
'parent_id': parent_id,
|
||||
'model': model or 'stock.quant',
|
||||
'product_id': move_line.product_id.display_name,
|
||||
'lot_id': move_line.lot_id.name,
|
||||
'product_qty_uom': str(move_line.quantity) + ' ' + move_line.product_id.uom_id.name,
|
||||
'location': move_line.location_id.name,
|
||||
'stream': stream,
|
||||
'reference_id': False}]
|
||||
return data
|
||||
|
||||
@api.model
|
||||
def upstream_traceability(self, level, stream=False, line_id=False, model=False, model_obj=False, parent_quant=False):
|
||||
final_vals =[]
|
||||
if model == 'stock.move.line':
|
||||
moves = self.get_move_lines_upstream(model_obj)
|
||||
elif model == 'stock.quant':
|
||||
moves = self.env['stock.move.line'].search([
|
||||
('location_dest_id', '=', model_obj.location_id.id),
|
||||
('lot_id', '=', model_obj.lot_id.id),
|
||||
('date', '<=', model_obj.write_date),
|
||||
('state', '=', 'done'),
|
||||
])
|
||||
moves |= self.get_move_lines_upstream(moves)
|
||||
for move in moves:
|
||||
unfoldable = False
|
||||
if move.consume_line_ids:
|
||||
unfoldable = True
|
||||
final_vals += self.make_dict_move(level, stream=stream, parent_id=line_id, move_line=move, unfoldable=unfoldable)
|
||||
return final_vals
|
||||
|
||||
@api.model
|
||||
def downstream_traceability(self, level, stream=False, line_id=False, model=False, model_obj=False, parent_quant=False):
|
||||
final_vals = []
|
||||
if model == 'stock.move.line':
|
||||
moves = self.get_move_lines_downstream(model_obj)
|
||||
elif model == 'stock.quant':
|
||||
moves = self.env['stock.move.line'].search([
|
||||
('location_id', '=', model_obj.location_id.id),
|
||||
('lot_id', '=', model_obj.lot_id.id),
|
||||
('date', '>=', model_obj.write_date),
|
||||
('state', '=', 'done'),
|
||||
])
|
||||
moves |= self.get_move_lines_downstream(moves)
|
||||
for move in moves:
|
||||
unfoldable = False
|
||||
if move.produce_line_ids:
|
||||
unfoldable = True
|
||||
final_vals += self.make_dict_move(level, stream=stream, parent_id=line_id, move_line=move, unfoldable=unfoldable)
|
||||
return final_vals
|
||||
|
||||
@api.model
|
||||
def final_vals_to_lines(self, final_vals, level):
|
||||
def _final_vals_to_lines(self, final_vals, level):
|
||||
lines = []
|
||||
for data in final_vals:
|
||||
lines.append({
|
||||
'id': autoIncrement(),
|
||||
'model': data['model'],
|
||||
'model_id': data['model_id'],
|
||||
'stream': data['stream'] or 'upstream',
|
||||
'parent_id': data['parent_id'],
|
||||
'parent_quant': data.get('parent_quant', False),
|
||||
'type': 'line',
|
||||
'usage': data.get('usage', False),
|
||||
'is_used': data.get('is_used', False),
|
||||
'lot_name': data.get('lot_name', False),
|
||||
'lot_id': data.get('lot_id', False),
|
||||
'reference': data.get('reference_id', False),
|
||||
'res_id': data.get('res_id', False),
|
||||
'res_model': data.get('res_model', False),
|
||||
'name': _(data.get('lot_id', False)),
|
||||
'columns': [data.get('reference_id', False) or data.get('product_id', False),
|
||||
data.get('lot_id', False),
|
||||
'columns': [data.get('reference_id', False),
|
||||
data.get('product_id', False),
|
||||
data.get('date', False),
|
||||
data.get('product_qty_uom', 0),
|
||||
data.get('location', False)],
|
||||
data.get('lot_name', False),
|
||||
data.get('location_source', False),
|
||||
data.get('location_destination', False),
|
||||
data.get('product_qty_uom', 0)],
|
||||
'level': level,
|
||||
'unfoldable': data['unfoldable'],
|
||||
})
|
||||
return lines
|
||||
|
||||
@api.model
|
||||
def _lines(self, line_id=None, model_id=False, model=False, level=0, parent_quant=False, stream=False, obj_ids=[], **kw):
|
||||
final_vals = []
|
||||
if model and line_id:
|
||||
model_obj = self.env[model].browse(model_id)
|
||||
if stream == "downstream":
|
||||
final_vals += self.downstream_traceability(level, stream='downstream', line_id=line_id, model=model, model_obj=model_obj, parent_quant=parent_quant)
|
||||
if model == 'stock.move.line':
|
||||
if model_obj.produce_line_ids:
|
||||
final_vals += self.get_produced_or_consumed_vals(model_obj.produce_line_ids, level, model=model, stream=stream, parent_id=line_id)
|
||||
else:
|
||||
final_vals = self.make_dict_move(level, stream=stream, parent_id=line_id,move_line=model_obj) + final_vals
|
||||
else:
|
||||
final_vals += self.upstream_traceability(level, stream='upstream', line_id=line_id, model=model, model_obj=model_obj, parent_quant=parent_quant)
|
||||
if model == 'stock.move.line':
|
||||
if model_obj.consume_line_ids:
|
||||
final_vals += self.get_produced_or_consumed_vals(model_obj.consume_line_ids, level, model=model, stream=stream, parent_id=line_id)
|
||||
else:
|
||||
final_vals = self.make_dict_move(level, stream=stream, parent_id=line_id, move_line=model_obj) + final_vals
|
||||
else:
|
||||
for move_line in obj_ids:
|
||||
final_vals += self.make_dict_head(level, stream=stream, parent_id=line_id, model=model or 'stock.pack.operation', move_line=move_line)
|
||||
return final_vals
|
||||
def _get_linked_move_lines(self, move_line):
|
||||
""" This method will return the consumed line or produced line for this operation."""
|
||||
return False, False
|
||||
|
||||
@api.model
|
||||
def get_produced_or_consumed_vals(self, move_lines, level, model, stream, parent_id):
|
||||
def _lines(self, line_id=None, model_id=False, model=False, level=0, move_lines=[], **kw):
|
||||
final_vals = []
|
||||
for line in move_lines:
|
||||
final_vals += self.make_dict_head(level, model=model, stream=stream, parent_id=parent_id, move_line=line)
|
||||
lines = move_lines or []
|
||||
if model and line_id:
|
||||
move_line = self.env[model].browse(model_id)
|
||||
move_lines, is_used = self._get_linked_move_lines(move_line)
|
||||
if move_lines:
|
||||
lines = move_lines
|
||||
else:
|
||||
if is_used:
|
||||
# Traceability in case of consumed in.
|
||||
move_line |= self._get_move_lines(move_line)
|
||||
for line in move_line:
|
||||
final_vals += self._make_dict_move(level, parent_id=line_id, move_line=line)
|
||||
for line in lines:
|
||||
unfoldable = bool(line.produce_line_ids or line.consume_line_ids)
|
||||
final_vals += self._make_dict_move(level, parent_id=line_id, move_line=line, unfoldable=unfoldable)
|
||||
return final_vals
|
||||
|
||||
def get_pdf_lines(self, line_data=[]):
|
||||
final_vals = []
|
||||
lines = []
|
||||
for line in line_data:
|
||||
model = self.env[line['model_name']].browse(line['model_id'])
|
||||
unfoldable = False
|
||||
if line.get('unfoldable'):
|
||||
final_vals += self.make_dict_head(line['level'], model=line['model_name'], parent_id=line['id'], move_line=model)
|
||||
else:
|
||||
if line['model_name'] == 'stock.move.line':
|
||||
final_vals += self.make_dict_move(line['level'], parent_id=line['id'], move_line=model)
|
||||
for data in final_vals:
|
||||
lines.append({
|
||||
'id': autoIncrement(),
|
||||
'model': data['model'],
|
||||
'model_id': data['model_id'],
|
||||
'parent_id': data['parent_id'],
|
||||
'stream': "%s" % (data['stream']),
|
||||
'type': 'line',
|
||||
'name': _(data.get('lot_id')),
|
||||
'columns': [data.get('reference_id') or data.get('product_id'),
|
||||
data.get('lot_id'),
|
||||
data.get('date'),
|
||||
data.get('product_qty_uom', 0),
|
||||
data.get('location')],
|
||||
'level': data['level'],
|
||||
'unfoldable': data['unfoldable'],
|
||||
})
|
||||
|
||||
unfoldable = True
|
||||
final_vals = self._make_dict_move(line['level'], parent_id=line['id'], move_line=model, unfoldable=unfoldable)
|
||||
lines.append(self._final_vals_to_lines(final_vals, line['level'])[0])
|
||||
return lines
|
||||
|
||||
def get_pdf(self, line_data=[]):
|
||||
|
||||
@@ -23,6 +23,8 @@ var stock_report_generic = Widget.extend(ControlPanelMixin, {
|
||||
this.given_context.active_id = action.context.active_id || action.params.active_id;
|
||||
this.given_context.model = action.context.active_model || false;
|
||||
this.given_context.ttype = action.context.ttype || false;
|
||||
this.given_context.auto_unfold = action.context.auto_unfold || false;
|
||||
this.given_context.lot_name = action.context.lot_name || false;
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
willStart: function() {
|
||||
@@ -37,8 +39,11 @@ var stock_report_generic = Widget.extend(ControlPanelMixin, {
|
||||
}
|
||||
def.then(function () {
|
||||
self.report_widget.$el.html(self.html);
|
||||
if(self.given_context['ttype'] == 'downstream'){
|
||||
self.report_widget.$el.find('.o_report_heading').html('<h1>Downstream Traceability</h1>');
|
||||
self.report_widget.$el.find('.o_report_heading').html('<h1>Traceability Report</h1>')
|
||||
if (self.given_context.auto_unfold) {
|
||||
_.each(self.$el.find('.fa-caret-right'), function (line) {
|
||||
self.report_widget.autounfold(line, self.given_context.lot_name);
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@@ -14,6 +14,7 @@ var ReportWidget = Widget.extend({
|
||||
'click span.o_stock_reports_unfoldable': 'unfold',
|
||||
'click .o_stock_reports_web_action': 'boundLink',
|
||||
'click .o_stock_reports_stream': 'updownStream',
|
||||
'click .o_stock_report_lot_action': 'actionOpenLot'
|
||||
},
|
||||
init: function(parent) {
|
||||
this._super.apply(this, arguments);
|
||||
@@ -31,13 +32,22 @@ var ReportWidget = Widget.extend({
|
||||
target: 'current'
|
||||
});
|
||||
},
|
||||
updownStream: function(e) {
|
||||
var stream = $(e.target).parent().data('stream');
|
||||
actionOpenLot: function(e) {
|
||||
var $el = $(e.target).parents('tr');
|
||||
var string = "Upstream Traceability"
|
||||
if (stream == 'downstream') {
|
||||
string = "Downstream Traceability"
|
||||
}
|
||||
this.do_action({
|
||||
type: 'ir.actions.client',
|
||||
tag: 'stock_report_generic',
|
||||
name: $el.data('lot_name'),
|
||||
context: {
|
||||
active_id : $el.data('lot_id'),
|
||||
active_model : 'stock.production.lot',
|
||||
url: '/stock/output_format/stock/active_id'
|
||||
},
|
||||
});
|
||||
},
|
||||
updownStream: function(e) {
|
||||
var $el = $(e.target).parents('tr');
|
||||
var string = "Traceability Report";
|
||||
this.do_action({
|
||||
type: "ir.actions.client",
|
||||
tag: 'stock_report_generic',
|
||||
@@ -45,7 +55,8 @@ var ReportWidget = Widget.extend({
|
||||
context: {
|
||||
active_id : $el.data('model_id'),
|
||||
active_model : $el.data('model'),
|
||||
ttype: stream || false,
|
||||
auto_unfold: true,
|
||||
lot_name: $el.data('lot_name') !== undefined && $el.data('lot_name').toString(),
|
||||
url: '/stock/output_format/stock/active_id'
|
||||
},
|
||||
});
|
||||
@@ -76,16 +87,15 @@ var ReportWidget = Widget.extend({
|
||||
$(e.target).parents('tr').find('span.o_stock_reports_foldable').replaceWith(QWeb.render("unfoldable", {lineId: active_id}));
|
||||
$(e.target).parents('tr').toggleClass('o_stock_reports_unfolded');
|
||||
},
|
||||
unfold: function(e) {
|
||||
autounfold: function(target, lot_name) {
|
||||
var self = this;
|
||||
var $CurretElement;
|
||||
$CurretElement = $(e.target).parents('tr').find('td.o_stock_reports_unfoldable');
|
||||
$CurretElement = $(target).parents('tr').find('td.o_stock_reports_unfoldable');
|
||||
var active_id = $CurretElement.data('id');
|
||||
var active_model_name = $CurretElement.data('model');
|
||||
var active_model_id = $CurretElement.data('model_id');
|
||||
var row_level = $CurretElement.data('level');
|
||||
var stream = $CurretElement.data('stream');
|
||||
var parent_quant = $CurretElement.data('parent_quant');
|
||||
var $cursor = $(e.target).parents('tr');
|
||||
var $cursor = $(target).parents('tr');
|
||||
this._rpc({
|
||||
model: 'stock.traceability.report',
|
||||
method: 'get_lines',
|
||||
@@ -93,21 +103,24 @@ var ReportWidget = Widget.extend({
|
||||
kwargs: {
|
||||
'model_id': active_model_id,
|
||||
'model_name': active_model_name,
|
||||
'stream': stream || 'upstream',
|
||||
'parent_quant': parseInt(parent_quant) || false,
|
||||
'level': parseInt(row_level) + 30 || 1
|
||||
},
|
||||
})
|
||||
.then(function (lines) {// After loading the line
|
||||
var line;
|
||||
for (line in lines) { // Render each line
|
||||
$cursor.after(QWeb.render("report_mrp_line", {l: lines[line]}));
|
||||
_.each(lines, function (line) { // Render each line
|
||||
$cursor.after(QWeb.render("report_mrp_line", {l: line}));
|
||||
$cursor = $cursor.next();
|
||||
}
|
||||
if ($cursor && line.unfoldable && line.lot_name == lot_name) {
|
||||
self.autounfold($cursor.find(".fa-caret-right"), lot_name);
|
||||
}
|
||||
});
|
||||
});
|
||||
$CurretElement.attr('class', 'o_stock_reports_foldable ' + active_id); // Change the class, and rendering of the unfolded line
|
||||
$(e.target).parents('tr').find('span.o_stock_reports_unfoldable').replaceWith(QWeb.render("foldable", {lineId: active_id}));
|
||||
$(e.target).parents('tr').toggleClass('o_stock_reports_unfolded');
|
||||
$(target).parents('tr').find('span.o_stock_reports_unfoldable').replaceWith(QWeb.render("foldable", {lineId: active_id}));
|
||||
$(target).parents('tr').toggleClass('o_stock_reports_unfolded');
|
||||
},
|
||||
unfold: function(e) {
|
||||
this.autounfold($(e.target));
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
.o_stock_reports_lines(@border-width: 5px, @color: inherit, @font-weight: inherit, @border-top-style: initial, @border-bottom-style: initial) {
|
||||
.o_stock_reports_lines(@border-width: 5px, @font-weight: inherit, @border-top-style: initial, @border-bottom-style: initial) {
|
||||
border-width: @border-width;
|
||||
border-left-style: hidden;
|
||||
border-right-style: hidden;
|
||||
color: @color;
|
||||
font-weight: @font-weight;
|
||||
border-top-style: @border-top-style;
|
||||
border-bottom-style: @border-bottom-style;
|
||||
@@ -15,15 +14,22 @@
|
||||
}
|
||||
}
|
||||
|
||||
.o_main_content {
|
||||
.o_stock_reports_page {
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
.o_stock_reports_page {
|
||||
@media (max-width: @screen-md-min) {
|
||||
overflow-x:scroll;
|
||||
}
|
||||
background-color: @odoo-view-background-color;
|
||||
color: @odoo-main-text-color;
|
||||
&.o_stock_reports_no_print {
|
||||
padding-bottom: 125px;
|
||||
margin: @odoo-horizontal-padding auto;
|
||||
.o-webclient-padding(@top: @odoo-sheet-vpadding, @bottom: @odoo-sheet-vpadding);
|
||||
.o_stock_reports_level0 {
|
||||
.o_stock_reports_lines(@border-width: 1px, @color: @odoo-brand-primary, @font-weight: bold, @border-top-style: solid, @border-bottom-style: groove);
|
||||
.o_stock_reports_lines(@border-width: 1px, @font-weight: normal, @border-top-style: solid, @border-bottom-style: groove);
|
||||
}
|
||||
.o_stock_reports_table {
|
||||
white-space: nowrap;
|
||||
@@ -31,6 +37,7 @@
|
||||
margin-top: 30px;
|
||||
}
|
||||
.o_report_line_header {
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.o_report_header {
|
||||
@@ -43,6 +50,12 @@
|
||||
.o_stock_reports_unfolded {
|
||||
display: inline-block;
|
||||
}
|
||||
.o_stock_reports_nofoldable {
|
||||
margin-left: 17px;
|
||||
}
|
||||
a.o_stock_report_lot_action {
|
||||
cursor: pointer;
|
||||
}
|
||||
table > tbody > tr > td ~ td > span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
@@ -13,57 +13,41 @@
|
||||
<t t-name="report_mrp_line">
|
||||
<t t-set="trclass" t-value="'o_stock_reports_default_style'"/>
|
||||
<t t-if="l.model == 'stock.move.line'"><t t-set="trclass" t-value="'o_stock_reports_level0'"/></t>
|
||||
<t t-if="l.model == 'stock.quant'"><t t-set="trclass" t-value="'o_stock_reports_level0'"/></t>
|
||||
<t t-set="space_td" t-value="'margin-left: '+ l.level + 'px;'"/>
|
||||
<t t-set="domainClass" t-value="'o_stock_reports_domain_line_0'"/>
|
||||
<t t-if="l.unfoldable == false">
|
||||
<t t-set="spanclass" t-value="'o_stock_reports_nofoldable'" />
|
||||
<t t-set="domainClass" t-value="'o_stock_reports_domain_line_1'"/>
|
||||
</t>
|
||||
|
||||
<tr t-att-data-type="l.type" t-att-data-unfold="l.unfoldable" t-att-data-parent_id="l.parent_id" t-att-data-id="l.id" t-att-data-stream="'l.stream'" t-att-data-model_id="l.model_id" t-att-data-model="l.model" t-att-class="trclass">
|
||||
<t t-set="span_color" t-value="'color: #666666;'"/>
|
||||
<t t-if="l.usage == 'in'"><t t-set="span_color" t-value="'color: #3c763d;'"/></t>
|
||||
<t t-if="l.usage == 'out'"><t t-set="span_color" t-value="'color: #a94442;'"/></t>
|
||||
|
||||
<tr t-att-data-unfold="l.unfoldable" t-att-data-parent_id="l.parent_id" t-att-data-id="l.id" t-att-data-model_id="l.model_id" t-att-data-model="l.model" t-att-class="trclass" t-att-data-lot_name="l.lot_name" t-att-data-lot_id="l.lot_id">
|
||||
<t t-if="l.unfoldable == true"><t t-set="tdclass" t-value="'o_stock_reports_unfoldable'" /></t>
|
||||
<t t-set="column" t-value="0" />
|
||||
<t t-foreach="l.columns" t-as="c">
|
||||
<t t-set="column" t-value="column + 1" />
|
||||
<td style="white-space: nowrap;" t-att-data-id="l.id" t-att-data-stream="l.stream" t-att-data-model="l.model" t-att-data-model_id="l.model_id" t-att-class="tdclass" t-att-data-level="l.level" t-att-data-parent_quant="l.parent_quant">
|
||||
<td style="white-space: nowrap;" t-att-data-id="l.id" t-att-data-model="l.model" t-att-data-model_id="l.model_id" t-att-class="tdclass" t-att-data-level="l.level" t-att-data-lot_name="l.lot_name">
|
||||
<t t-if="column == 1">
|
||||
<span t-att-style="space_td" t-att-class="domainClass"></span>
|
||||
<t t-if="l.unfoldable">
|
||||
<span class="o_stock_reports_unfoldable o_stock_reports_caret_icon"><i class="fa fa-fw fa-caret-right"></i></span>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="l.res_model == 'mrp.production' and l.reference == c">
|
||||
<span t-if="c">
|
||||
<t t-if="l.reference == c">
|
||||
<span t-if="c" t-att-class="spanclass">
|
||||
<a t-att-data-active-id="l.res_id" t-att-data-res-model="l.res_model" class="o_stock_reports_web_action"><t t-raw="c" /></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="l.res_model == 'mrp.unbuild' and l.reference == c">
|
||||
<span t-if="c">
|
||||
<a t-att-data-active-id="l.res_id" t-att-data-res-model="l.res_model" class="o_stock_reports_web_action"><t t-raw="c" /></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="l.res_model == 'repair.order' and l.reference == c">
|
||||
<span t-if="c">
|
||||
<a t-att-data-active-id="l.res_id" t-att-data-res-model="l.res_model" class="o_stock_reports_web_action"><t t-raw="c" /></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="l.res_model == 'stock.scrap' and l.reference == c">
|
||||
<span t-if="c">
|
||||
<a t-att-data-active-id="l.res_id" t-att-data-res-model="l.res_model" class="o_stock_reports_web_action"><t t-raw="c" /></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="l.res_model == 'stock.inventory' and l.reference == c">
|
||||
<span t-if="c">
|
||||
<a t-att-data-active-id="l.res_id" t-att-data-res-model="l.res_model" class="o_stock_reports_web_action"><t t-raw="c" /></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="l.res_model == 'stock.picking' and l.reference == c">
|
||||
<span t-if="c">
|
||||
<a t-att-data-active-id="l.res_id" t-att-data-res-model="l.res_model" class="o_stock_reports_web_action"><t t-raw="c" /></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="l.reference != c">
|
||||
<t t-if="typeof c == 'string' || typeof c == 'number'"><span>
|
||||
<t t-elif="l.lot_name == c and l.lot_name != false">
|
||||
<span>
|
||||
<a class="o_stock_report_lot_action"><t t-raw="c"/></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-if="l.reference != c and l.lot_name != c">
|
||||
<t t-if="typeof c == 'string' || typeof c == 'number'"><span t-att-style="span_color">
|
||||
<t t-raw="c"/>
|
||||
</span></t>
|
||||
<t t-if="typeof c != 'string' & typeof c != 'number'"><span t-att-style="c[1]">
|
||||
@@ -72,12 +56,6 @@
|
||||
</t>
|
||||
</td>
|
||||
</t>
|
||||
<td>
|
||||
<t t-if="l.model == 'stock.move.line'">
|
||||
<span t-if="l.stream == 'downstream'" class="o_stock_reports_stream text-muted" data-stream="upstream" title="Upstream Traceability"><i class="fa fa-arrow-up"></i></span>
|
||||
<span t-if="l.stream == 'upstream'" class="o_stock_reports_stream" data-stream="downstream" title="Downstream Traceability"><i class="fa fa-arrow-down"></i></span>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
|
||||
|
||||
@@ -5,28 +5,46 @@
|
||||
<t t-foreach="lines" t-as="a">
|
||||
<t t-set="trclass" t-value="'o_stock_reports_default_style'"/>
|
||||
<t t-if="a.get('model') == 'stock.move.line'"><t t-set="trclass" t-value="'o_stock_reports_level0'"/></t>
|
||||
<t t-if="a.get('model') == 'stock.quant'"><t t-set="trclass" t-value="'o_stock_reports_level0'"/></t>
|
||||
<t t-set="domainClass" t-value="'o_stock_reports_domain_line'"/>
|
||||
<t t-if="a['unfoldable'] == false">
|
||||
<t t-set="domainClass" t-value="'o_stock_reports_domain_line_1'"/>
|
||||
</t>
|
||||
<t t-set="column" t-value="0" />
|
||||
<tr t-att-data-type="a['type']" t-att-data-stream="a['stream']" t-att-data-unfold="a['unfoldable']" t-att-data-parent_id="a['parent_id']" t-att-data-id="a['id']" t-att-data-model_id="a['model_id']" t-att-data-model="a['model']" t-att-class="trclass">
|
||||
<tr t-att-data-unfold="a['unfoldable']" t-att-data-parent_id="a['parent_id']" t-att-data-id="a['id']" t-att-data-model_id="a['model_id']" t-att-data-model="a['model']" t-att-data-lot_name="a['lot_name']" t-att-data-lot_id="a['lot_id']" t-att-class="trclass">
|
||||
<t t-set="tdclass" t-value="''" />
|
||||
<t t-set="spanclass" t-value="''" />
|
||||
<t t-set="span_color" t-value="'color: #666666;'"/>
|
||||
<t t-if="a['usage'] == 'in'"><t t-set="span_color" t-value="'color: #3c763d;'"/></t>
|
||||
<t t-if="a['usage'] == 'out'"><t t-set="span_color" t-value="'color: #a94442;'"/></t>
|
||||
<t t-if="a.get('unfoldable')"><t t-set="tdclass" t-value="'o_stock_reports_unfoldable'" /></t>
|
||||
<t t-if="not a.get('unfoldable')"><t t-set="spanclass" t-value="'o_stock_reports_nofoldable'" /></t>
|
||||
<t t-set="column" t-value="0" />
|
||||
<t t-foreach="a['columns']" t-as="c">
|
||||
<t t-set="column" t-value="column + 1" />
|
||||
<td t-att-data-id="a.get('id')" t-att-data-model="a['model']" t-att-data-model_id="a['model_id']" t-att-data-stream="a['stream']" t-att-class="tdclass" t-att-data-level="a['level']" t-att-data-parent_quant="a['parent_quant']">
|
||||
<td t-att-data-id="a.get('id')" t-att-data-model="a['model']" t-att-data-model_id="a['model_id']" t-att-class="tdclass" t-att-data-level="a['level']">
|
||||
<t t-if="column == 1">
|
||||
<span t-att-data-active-id="a['id']" t-att-class="a['type'] + ' ' + domainClass"></span>
|
||||
<t t-if="a.get('unfoldable')">
|
||||
<t t-if="a.get('unfoldable') and not a.get('is_used')">
|
||||
<span t-att-data-id="a['id']" class="o_stock_reports_unfoldable o_stock_reports_caret_icon"><i class="fa fa-fw fa-caret-right"></i></span>
|
||||
</t>
|
||||
<t t-if="a.get('is_used')">
|
||||
<span class="o_stock_reports_stream" title="Traceability Report"><i class="fa fa-fw fa-level-up fa-rotate-270"></i></span>
|
||||
</t>
|
||||
</t>
|
||||
<t t-if="a['reference'] == c">
|
||||
<span t-if="c" t-att-class="spanclass">
|
||||
<a t-att-data-active-id="a['res_id']" t-att-data-res-model="a['res_model']" class="o_stock_reports_web_action"><t t-raw="c" /></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-elif="a['lot_name'] == c">
|
||||
<span>
|
||||
<a class="o_stock_report_lot_action"><t t-raw="c"/></a>
|
||||
</span>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<span t-att-style="span_color">
|
||||
<t t-raw="c"/>
|
||||
</span>
|
||||
</t>
|
||||
<span>
|
||||
<t t-raw="c" />
|
||||
</span>
|
||||
</td>
|
||||
</t>
|
||||
<td>
|
||||
@@ -37,7 +55,7 @@
|
||||
|
||||
<template id="report_stock_inventory">
|
||||
<div class="container o_stock_reports_page o_stock_reports_no_print">
|
||||
<span t-if="lines" class="o_report_heading text-left"><h1>Upstream Traceability</h1></span>
|
||||
<span t-if="lines" class="o_report_heading text-left"><h1>Traceability Report</h1></span>
|
||||
<span><t t-esc="context"/></span>
|
||||
<div class="o_stock_reports_table">
|
||||
<t t-if="not lines">
|
||||
@@ -47,12 +65,13 @@
|
||||
<table width='100%' class="table-responsive">
|
||||
<thead>
|
||||
<tr class="o_report_header">
|
||||
<th class="o_report_line_header">PRODUCT</th>
|
||||
<th class="o_report_line_header">LOT/SERIAL NUMBER</th>
|
||||
<th class="o_report_line_header">DATE</th>
|
||||
<th class="o_report_line_header">QUANTITY</th>
|
||||
<th class="o_report_line_header">LOCATION</th>
|
||||
<th></th>
|
||||
<th class="o_report_line_header">Reference</th>
|
||||
<th class="o_report_line_header">Product</th>
|
||||
<th class="o_report_line_header">Date</th>
|
||||
<th class="o_report_line_header">Lot/Serial #</th>
|
||||
<th class="o_report_line_header">From</th>
|
||||
<th class="o_report_line_header">To</th>
|
||||
<th class="o_report_line_header">Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -85,11 +104,13 @@
|
||||
<table width='100%' class="o_stock_reports_table">
|
||||
<thead>
|
||||
<tr class="o_report_header">
|
||||
<th class="o_report_line_header">PRODUCT</th>
|
||||
<th class="o_report_line_header">LOT/SERIAL NUMBER</th>
|
||||
<th class="o_report_line_header">DATE</th>
|
||||
<th class="o_report_line_header">QUANTITY</th>
|
||||
<th class="o_report_line_header">LOCATION</th>
|
||||
<th class="o_report_line_header">Reference</th>
|
||||
<th class="o_report_line_header">Product</th>
|
||||
<th class="o_report_line_header">Date</th>
|
||||
<th class="o_report_line_header">Lot/Serial #</th>
|
||||
<th class="o_report_line_header">From</th>
|
||||
<th class="o_report_line_header">To</th>
|
||||
<th class="o_report_line_header">Quantity</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@@ -98,9 +119,8 @@
|
||||
<t t-set="space_td" t-value="'margin-left: '+ str(a['level']) + 'px;'"/>
|
||||
<t t-set="trclass" t-value="'o_stock_reports_default_style'"/>
|
||||
<t t-if="a.get('model') == 'stock.move.line'"><t t-set="trclass" t-value="'o_stock_reports_level0'"/></t>
|
||||
<t t-if="a.get('model') == 'stock.quant'"><t t-set="trclass" t-value="'o_stock_reports_level0'"/></t>
|
||||
<t t-set="column" t-value="0" />
|
||||
<tr t-att-data-type="a['type']" t-att-data-stream="a['stream']" t-att-data-id="a['id']" t-att-data-model="a['model']" t-att-class="trclass">
|
||||
<tr t-att-data-id="a['id']" t-att-data-model="a['model']" t-att-class="trclass">
|
||||
<t t-set="tdclass" t-value="''" />
|
||||
<t t-if="a.get('unfoldable')"><t t-set="tdclass" t-value="'o_stock_reports_unfoldable'" /></t>
|
||||
<t t-set="column" t-value="0" />
|
||||
|
||||
@@ -231,7 +231,7 @@
|
||||
<button name="action_see_packages" string="Packages" type="object"
|
||||
class="oe_stat_button" icon="fa-cubes"
|
||||
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"/>
|
||||
<button name="%(action_stock_report)d" icon="fa-arrow-up" class="oe_stat_button" string="Traceability Report" type="action" states="done" groups="stock.group_production_lot"/>
|
||||
</div>
|
||||
<h1 class="hidden-xs">
|
||||
<field name="name" attrs="{'invisible': [('name','=','/')]}" readonly="1"/>
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(lot_open_quants)d" icon="fa-arrows" class="oe_stat_button" string="Locate" type="action"/>
|
||||
<button name="%(action_stock_report)d" icon="fa-arrow-up" class="oe_stat_button" string="Up/Down Traceability" type="action"/>
|
||||
<button name="%(action_stock_report)d" icon="fa-arrow-up" class="oe_stat_button" string="Traceability Report" type="action"/>
|
||||
</div>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
|
||||
Reference in New Issue
Block a user