hunk.code_window:     &.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_table {
               white-space: nowrap;
               margin-bottom: 228px;
  
hunk.labels: keep keep keep keep keep replace keep keep keep keep
hunk.after_edit:             .o_stock_reports_lines(@border-width: 1px, @font-weight: normal, @border-top-style: solid, @border-bottom-style: groove);
  
hunk.file_path: addons/stock/static/src/less/stock_traceability_report.less
hunk.type: replace
file: # -*- coding: utf-8 -*-
  # Part of Odoo. See LICENSE file for full copyright and licensing details.
  
  from odoo import api, models, _
  
  
  rec = 0
  def autoIncrement():
      global rec
      pStart = 1
      pInterval = 1
      if rec == 0:
          rec = pStart
      else:
          rec += pInterval
      return rec
  
  
  class MrpStockReport(models.TransientModel):
      _name = 'stock.traceability.report'
  
      @api.model
      def get_move_lines(self, move_lines):
          res = self.env['stock.move.line']
          for move_line in move_lines:
              # if MTO
              if move_line.move_id.move_orig_ids:
                  res |= move_line.move_id.move_orig_ids.mapped('move_line_ids').filtered(
                      lambda m: m.lot_id.id == move_line.lot_id.id)
              # if MTS
              else:
                  if move_line.location_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_dest_id', '=', move_line.location_id.id),
                          ('id', '!=', move_line.id),
                          ('date', '<', move_line.date),
                      ])
          if res:
              res |= self.get_move_lines(res)
          return res
  
      @api.model
      def get_lines(self, line_id=None, **kw):
          context = dict(self.env.context)
          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 = []
          res = []
          move_line = self.env['stock.move.line']
          if rec_id and model in ('stock.production.lot', 'stock.move.line'):
              lines = self.env['stock.move.line'].search([
                  ('lot_id', '=', context.get('lot_id') 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'):
              res = self.env[model].browse(rec_id)
              if model == 'stock.picking':
                  lines = res.move_lines.mapped('move_line_ids').filtered(lambda m: m.lot_id and m.state == 'done')
              else:
                  lines = res.move_finished_ids.mapped('move_line_ids').filtered(lambda m: m.lot_id and m.state == 'done')
          res = self._lines(line_id, model_id=rec_id, model=model, level=level, obj_ids=lines)
          final_vals = sorted(res, 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):
          res_model = ''
          ref = ''
          res_id = False
          if move_line.picking_id:
              res_model = 'stock.picking'
              res_id = move_line.picking_id.id
              ref = move_line.picking_id.name
          elif move_line.move_id.inventory_id:
              res_model = 'stock.inventory'
              res_id = move_line.move_id.inventory_id.id
              ref = 'Inv. Adj.: ' + move_line.move_id.inventory_id.name
          elif move_line.move_id.scrapped and move_line.move_id.scrap_ids:
              res_model = 'stock.scrap'
              res_id = move_line.move_id.scrap_ids[0].id
              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, unfoldable=False):
          res_model, res_id, ref = self.get_links(move_line)
          dummy, is_used = self._get_linked_move_lines(move_line)
          data = [{
              'level': level,
              'unfoldable': unfoldable,
              'date': move_line.move_id.date,
              'parent_id': parent_id,
              'is_used': bool(is_used),
              'model_id': move_line.id,
              'model':'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_source': move_line.location_id.name,
              'location_destination': move_line.location_dest_id.name,
              'reference_id': ref,
              'res_id': res_id,
              'res_model': res_model}]
          return data
  
      def make_dict_head(self, level, parent_id, model=False, move_line=False):
          res_model, res_id, ref = self.get_links(move_line)
          data = []
          if model == 'stock.move.line':
              data = [{
                  'level': level,
                  'unfoldable': False if not move_line.lot_id else 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_source': move_line.location_id.name if not move_line.lot_id else False,
                  'location_destination': move_line.location_dest_id.name if not move_line.lot_id else False,
                  'reference_id': ref,
                  'res_id': res_id,
                  'res_model': res_model}]
          return data
  
      @api.model
      def get_traceability(self, level, line_id=False, model=False, model_obj=False):
          final_vals = []
          if model == 'stock.move.line':
              moves = self.get_move_lines(model_obj)
          for move in moves:
              unfoldable = False
              if move.consume_line_ids:
                  unfoldable = True
              final_vals += self.make_dict_move(level, parent_id=line_id, move_line=move, unfoldable=unfoldable)
          return final_vals
  
      @api.model
      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'],
                  'parent_id': data['parent_id'],
                  'is_used': data.get('is_used', False),
                  'lot_id': data.get('lot_id', False),
                  'type': 'line',
                  '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),
                              data.get('product_id', False),
                              data.get('date', False),
                              data.get('lot_id', False),
                              data.get('location_source', False),
                              data.get('location_destination', False),
                              data.get('product_qty_uom', 0)],
                  'level': level,
                  'unfoldable': data['unfoldable'],
              })
          return lines
  
      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 _lines(self, line_id=None, model_id=False, model=False, level=0, obj_ids=[], **kw):
          final_vals = []
          if model and line_id:
              move_line = self.env[model].browse(model_id)
              move_lines, dummy = self._get_linked_move_lines(move_line)
              if move_lines:
                  final_vals += self.get_produced_or_consumed_vals(move_lines, level, model=model, parent_id=line_id)
              else:
                  final_vals += self.get_traceability(level, line_id=line_id, model=model, model_obj=move_line)
                  final_vals = self.make_dict_move(level, parent_id=line_id, move_line=move_line) + final_vals
          else:
              for move_line in obj_ids:
                  unfoldable = bool(move_line.produce_line_ids or move_line.consume_line_ids)
                  final_vals += self.make_dict_move(level, parent_id=line_id, move_line=move_line, unfoldable=unfoldable)
          return final_vals
  
      @api.model
      def get_produced_or_consumed_vals(self, move_lines, level, model, parent_id):
          final_vals = []
          for line in move_lines:
              final_vals += self.make_dict_head(level, model=model, parent_id=parent_id, move_line=line)
          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'])
              dummy, move_line = self._get_linked_move_lines(model)
              if line.get('unfoldable') and move_line:
                      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'],
                  'type': 'line',
                  'name': _(data.get('lot_id')),
                  'columns': [data.get('reference_id'),
                              data.get('product_id'),
                              data.get('date'),
                              data.get('lot_id'),
                              data.get('location_source', False),
                              data.get('location_destination', False),
                              data.get('product_qty_uom', 0)],
                  'level': data['level'],
                  'unfoldable': data['unfoldable'],
              })
  
          return lines
  
      def get_pdf(self, line_data=[]):
          lines = self.with_context(print_mode=True).get_pdf_lines(line_data)
          base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url')
          rcontext = {
              'mode': 'print',
              'base_url': base_url,
          }
  
          body = self.env['ir.ui.view'].render_template(
              "stock.report_stock_inventory_print",
              values=dict(rcontext, lines=lines, report=self, context=self),
          )
  
          header = self.env['ir.actions.report'].render_template("web.internal_layout", values=rcontext)
          header = self.env['ir.actions.report'].render_template("web.minimal_layout", values=dict(rcontext, subst=True, body=header))
  
          return self.env['ir.actions.report']._run_wkhtmltopdf(
              [body],
              header=header,
              landscape=True,
              specific_paperformat_args={'data-report-margin-top': 10, 'data-report-header-spacing': 10}
          )
  
      def _get_html(self):
          result = {}
          rcontext = {}
          context = dict(self.env.context)
          rcontext['lines'] = self.with_context(context).get_lines()
          result['html'] = self.env.ref('stock.report_stock_inventory').render(rcontext)
          return result
  
      @api.model
      def get_html(self, given_context=None):
          res = self.search([('create_uid', '=', self.env.uid)], limit=1)
          if not res:
              return self.create({}).with_context(given_context)._get_html()
          return res.with_context(given_context)._get_html()
  