Purpose of this commit is to give description more "business oriented" because those descriptions appears in Odoo Studio which is supposed to be used by end users, not only by developers. Related Task ID : 37311
236 lines
9.8 KiB
Python
236 lines
9.8 KiB
Python
# -*- 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'
|
|
_description = 'Traceability Report'
|
|
|
|
@api.model
|
|
def _get_move_lines(self, move_lines):
|
|
lines_seen = move_lines
|
|
lines_todo = list(move_lines)
|
|
while lines_todo:
|
|
move_line = lines_todo.pop(0)
|
|
# if MTO
|
|
if move_line.move_id.move_orig_ids:
|
|
lines = move_line.move_id.move_orig_ids.mapped('move_line_ids').filtered(
|
|
lambda m: m.lot_id == move_line.lot_id and m.state == 'done'
|
|
) - lines_seen
|
|
# if MTS
|
|
elif move_line.location_id.usage == 'internal':
|
|
lines = 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', 'not in', lines_seen.ids),
|
|
('date', '<=', move_line.date),
|
|
('state', '=', 'done')
|
|
])
|
|
else:
|
|
continue
|
|
lines_todo += list(lines)
|
|
lines_seen |= lines
|
|
return lines_seen - move_lines
|
|
|
|
@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 = self.env['stock.move.line']
|
|
move_line = self.env['stock.move.line']
|
|
if rec_id and model == 'stock.production.lot':
|
|
lines = move_line.search([
|
|
('lot_id', '=', context.get('lot_name') or rec_id),
|
|
('state', '=', 'done'),
|
|
])
|
|
elif rec_id and model == 'stock.move.line' and context.get('lot_name'):
|
|
record = self.env[model].browse(rec_id)
|
|
dummy, is_used = self._get_linked_move_lines(record)
|
|
if is_used:
|
|
lines = is_used
|
|
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:
|
|
lines = record.move_finished_ids.mapped('move_line_ids').filtered(lambda m: 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_reference(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
|
|
|
|
@api.model
|
|
def _quantity_to_str(self, from_uom, to_uom, qty):
|
|
""" workaround to apply the float rounding logic of t-esc on data prepared server side """
|
|
qty = from_uom._compute_quantity(qty, to_uom, rounding_method='HALF-UP')
|
|
return self.env['ir.qweb.field.float'].value_to_html(qty, {'decimal_precision': 'Product Unit of Measure'})
|
|
|
|
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)
|
|
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',
|
|
'product_id': move_line.product_id.display_name,
|
|
'product_qty_uom': "%s %s" % (self._quantity_to_str(move_line.product_uom_id, move_line.product_id.uom_id, move_line.qty_done), move_line.product_id.uom_id.name),
|
|
'lot_name': move_line.lot_id.name,
|
|
'lot_id': move_line.lot_id.id,
|
|
'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
|
|
|
|
@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'],
|
|
'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),
|
|
'columns': [data.get('reference_id', False),
|
|
data.get('product_id', False),
|
|
data.get('date', 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
|
|
|
|
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, move_lines=[], **kw):
|
|
final_vals = []
|
|
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:
|
|
# Traceability in case of consumed in.
|
|
lines = self._get_move_lines(move_line)
|
|
for line in lines:
|
|
unfoldable = False
|
|
if line.consume_line_ids or ( line.lot_id and self._get_move_lines(line) and model != "stock.production.lot"):
|
|
unfoldable = True
|
|
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=[]):
|
|
lines = []
|
|
for line in line_data:
|
|
model = self.env[line['model_name']].browse(line['model_id'])
|
|
unfoldable = False
|
|
if line.get('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=[]):
|
|
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()
|