From 6170b1c2f07a66f6f2dcec0043e0b8ea1e113ced Mon Sep 17 00:00:00 2001 From: Ankita Raval Date: Tue, 12 Feb 2019 15:20:49 +0530 Subject: [PATCH] [IMP] mrp: Improve CSV/EXCEL export of BoM Structure & Cost Report This commit is Related to Task ID 1875706 --- addons/mrp/__init__.py | 1 + addons/mrp/controller/__init__.py | 1 + addons/mrp/controller/main.py | 103 +++++++++++ addons/mrp/report/mrp_report_bom_structure.py | 172 ++++++++++++------ .../mrp/report/mrp_report_bom_structure.xml | 64 ++++--- addons/mrp/static/src/js/mrp_bom_report.js | 44 ++++- addons/mrp/static/src/xml/mrp.xml | 3 +- 7 files changed, 284 insertions(+), 104 deletions(-) create mode 100644 addons/mrp/controller/__init__.py create mode 100644 addons/mrp/controller/main.py diff --git a/addons/mrp/__init__.py b/addons/mrp/__init__.py index 5879cc956c992..d17fb626e282b 100644 --- a/addons/mrp/__init__.py +++ b/addons/mrp/__init__.py @@ -4,6 +4,7 @@ from . import models from . import wizard from . import report +from . import controller from odoo import api, SUPERUSER_ID diff --git a/addons/mrp/controller/__init__.py b/addons/mrp/controller/__init__.py new file mode 100644 index 0000000000000..12a7e529b6741 --- /dev/null +++ b/addons/mrp/controller/__init__.py @@ -0,0 +1 @@ +from . import main diff --git a/addons/mrp/controller/main.py b/addons/mrp/controller/main.py new file mode 100644 index 0000000000000..179d89eedec46 --- /dev/null +++ b/addons/mrp/controller/main.py @@ -0,0 +1,103 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + +import io +import xlsxwriter + +from odoo import http +from odoo.http import content_disposition, request + + +class BomReportController(http.Controller): + @http.route('/bom_report_xslx/', type='http') + def get_report_xlsx(self, bom_id, bom_quantity, variant, report_name='all', **kw): + response = request.make_response( + None, + headers=[ + ('Content-Type', 'application/vnd.ms-excel'), + ('Content-Disposition', content_disposition('bom_structure_sheet.xlsx')) + ] + ) + report_obj = request.env['report.mrp.report_bom_structure'] + data, header, columns, report_name = report_obj._get_report_xslx_values(bom_id, bom_quantity, variant, report_name) + self.prepare_xlsx_sheet(data, header, columns, report_name, response) + return response + + def prepare_xlsx_sheet(self, data, header, columns, report_name, response): + output = io.BytesIO() + workbook = xlsxwriter.Workbook(output, {'in_memory': True}) + sheet = workbook.add_worksheet(report_name) + + default_col1_style = workbook.add_format({'font_name': 'Arial', 'font_size': 12, 'font_color': '#666666', 'indent': 2}) + default_style = workbook.add_format({'font_name': 'Arial', 'font_size': 12, 'font_color': '#666666'}) + title_style = workbook.add_format({'font_name': 'Arial', 'bold': True, 'bottom': 2}) + level_0_style = workbook.add_format({'font_name': 'Arial', 'bold': True, 'font_size': 13, 'bottom': 6, 'font_color': '#666666'}) + level_1_col1_style = workbook.add_format({'font_name': 'Arial', 'bold': True, 'font_size': 13, 'bottom': 1, 'font_color': '#666666', 'indent': 1}) + level_1_style = workbook.add_format({'font_name': 'Arial', 'bold': True, 'font_size': 13, 'bottom': 1, 'font_color': '#666666'}) + level_2_col1_style = workbook.add_format({'font_name': 'Arial', 'bold': True, 'font_size': 12, 'font_color': '#666666', 'indent': 2}) + level_2_style = workbook.add_format({'font_name': 'Arial', 'bold': True, 'font_size': 12, 'font_color': '#666666'}) + level_3_col1_style = workbook.add_format({'font_name': 'Arial', 'font_size': 12, 'font_color': '#666666', 'indent': 3}) + level_3_style = workbook.add_format({'font_name': 'Arial', 'font_size': 12, 'font_color': '#666666'}) + + #Set the first column width to 50 + sheet.set_column(0, 0, 50) + sheet.set_column(1, 1, 25) + sheet.set_column(4, 4, 10) + y_offset = 1 + x = 0 + + docs = data['docs'][0] + lines = docs['lines'] + + #header + for column_x in range(0, len(header)): + header_label = header[column_x].get('name', '').replace('
', ' ').replace(' ', ' ') + sheet.write(y_offset, column_x, header_label, title_style) + + y_offset += 1 + for bom_line_x in range(0, len(columns)): + sheet.write(y_offset, bom_line_x, docs.get(columns[bom_line_x], ''), level_0_style) + + y_offset += 1 + for y in range(0, len(lines)): + level = lines[y].get('level') + if level == 0: + y_offset += 1 + style = level_0_style + col1_style = style + elif level == 1: + style = level_1_style + col1_style = level_1_col1_style + elif level == 2: + style = level_2_style + col1_style = level_2_col1_style + elif level == 3: + style = level_3_style + col1_style = level_3_col1_style + else: + style = default_style + col1_style = default_col1_style + + for x in range(0, len(columns)): + sheet.write(y+y_offset, x, lines[y].get(columns[x], ''), x > 0 and style or col1_style) + + quantity_index = columns.index('quantity') + sheet.set_column(y_offset + len(lines), quantity_index, 8) + sheet.write(y_offset + len(lines), quantity_index, 'Unit Cost', level_1_style) + + if docs.get('report_name') == 'bom_structure': + price_index = columns.index('prod_cost') + sheet.write(y_offset + len(lines), price_index, docs.get('prod_cost'), level_1_style) + elif docs.get('report_name') == 'bom_cost': + total_index = columns.index('total') + sheet.write(y_offset + len(lines), total_index, docs.get('total'), level_1_style) + else: + price_index = columns.index('prod_cost') + total_index = columns.index('total') + sheet.write(y_offset + len(lines), price_index, docs.get('prod_cost'), level_1_style) + sheet.write(y_offset + len(lines), total_index, docs.get('total'), level_1_style) + + workbook.close() + output.seek(0) + response.stream.write(output.read()) + output.close() diff --git a/addons/mrp/report/mrp_report_bom_structure.py b/addons/mrp/report/mrp_report_bom_structure.py index b95c1bcf658b7..f5f1345057025 100644 --- a/addons/mrp/report/mrp_report_bom_structure.py +++ b/addons/mrp/report/mrp_report_bom_structure.py @@ -5,33 +5,43 @@ from odoo import api, models, _ from odoo.tools import float_round + class ReportBomStructure(models.AbstractModel): _name = 'report.mrp.report_bom_structure' _description = 'BOM Structure Report' + def _get_report_xslx_values(self, bom_id, bom_qty, variant, report_name='all'): + data = self._get_report_values([bom_id], {'report_name': report_name, 'quantity': bom_qty, 'variant': variant}) + header = self.get_header(report_name) + columns = self.get_column_key(report_name) + bom_report_name = self._get_report_name() + return data, header, columns, bom_report_name + @api.model def _get_report_values(self, docids, data=None): docs = [] for bom_id in docids: bom = self.env['mrp.bom'].browse(bom_id) - variant = data and data.get('variant') + report_name = data.get('report_name') or 'all' + variant = int(data.get('variant')) candidates = variant and self.env['product.product'].browse(variant) or bom.product_tmpl_id.product_variant_ids for product_variant_id in candidates: if data and data.get('childs'): - doc = self._get_pdf_line(bom_id, product_id=product_variant_id, qty=float(data.get('quantity')), child_bom_ids=json.loads(data.get('childs'))) + doc = self._get_pdf_line(bom_id, product_id=product_variant_id, qty=float(data.get('quantity')), child_bom_ids=json.loads(data.get('childs')), report_name=report_name, report_type='pdf') else: - doc = self._get_pdf_line(bom_id, product_id=product_variant_id, unfolded=True) + doc = self._get_pdf_line(bom_id, product_id=product_variant_id, qty=float(data.get('quantity')), report_name=report_name, report_type='pdf') doc['report_type'] = 'pdf' - doc['report_structure'] = data and data.get('report_type') or 'all' + doc['report_name'] = report_name docs.append(doc) if not candidates: if data and data.get('childs'): - doc = self._get_pdf_line(bom_id, qty=float(data.get('quantity')), child_bom_ids=json.loads(data.get('childs'))) + doc = self._get_pdf_line(bom_id, qty=float(data.get('quantity')), child_bom_ids=json.loads(data.get('childs')), report_name=report_name, report_type='pdf') else: - doc = self._get_pdf_line(bom_id, unfolded=True) + doc = self._get_pdf_line(bom_id, qty=float(data.get('quantity')), report_name=report_name, report_type='pdf') doc['report_type'] = 'pdf' - doc['report_structure'] = data and data.get('report_type') or 'all' + doc['report_name'] = report_name docs.append(doc) + doc['header'] = self.get_header(report_name) return { 'doc_ids': docids, 'doc_model': 'mrp.bom', @@ -39,17 +49,20 @@ def _get_report_values(self, docids, data=None): } @api.model - def get_html(self, bom_id=False, searchQty=1, searchVariant=False): - res = self._get_report_data(bom_id=bom_id, searchQty=searchQty, searchVariant=searchVariant) - res['lines']['report_type'] = 'html' - res['lines']['report_structure'] = 'all' + def get_html(self, bom_id=False, searchQty=1, searchVariant=False, report_name='all', report_type='html'): + searchVariant = self.env['product.product'].browse(searchVariant) + self._get_report_values([bom_id], {'report_name': report_name, 'quantity': searchQty, 'variant': searchVariant}) + res = self._get_report_data(bom_id=bom_id, searchQty=searchQty, report_name=False, searchVariant=searchVariant) + res['lines']['report_type'] = report_type + res['lines']['report_name'] = report_name res['lines']['has_attachments'] = res['lines']['attachments'] or any(component['attachments'] for component in res['lines']['components']) + res['lines']['header'] = self.get_header(report_name) res['lines'] = self.env.ref('mrp.report_mrp_bom').render({'data': res['lines']}) return res @api.model - def get_bom(self, bom_id=False, product_id=False, line_qty=False, line_id=False, level=False): - lines = self._get_bom(bom_id=bom_id, product_id=product_id, line_qty=line_qty, line_id=line_id, level=level) + def get_bom(self, bom_id=False, product_id=False, line_qty=False, line_id=False, level=False, report_name=False): + lines = self._get_bom(bom_id=bom_id, product=product_id, line_qty=line_qty, line_id=line_id, level=level, report_name=report_name) return self.env.ref('mrp.report_mrp_bom_line').render({'data': lines}) @api.model @@ -60,11 +73,12 @@ def get_operations(self, bom_id=False, qty=0, level=0): 'bom_id': bom_id, 'currency': self.env.user.company_id.currency_id, 'operations': lines, + 'report_name': 'all', } return self.env.ref('mrp.report_mrp_operation_line').render({'data': values}) @api.model - def _get_report_data(self, bom_id, searchQty=0, searchVariant=False): + def _get_report_data(self, bom_id, searchQty=0, searchVariant=False, report_name=False): lines = {} bom = self.env['mrp.bom'].browse(bom_id) bom_quantity = searchQty or bom.product_qty @@ -74,12 +88,11 @@ def _get_report_data(self, bom_id, searchQty=0, searchVariant=False): if bom: bom_uom_name = bom.product_uom_id.name - # Get variants used for search - if not bom.product_id: - for variant in bom.product_tmpl_id.product_variant_ids: - bom_product_variants[variant.id] = variant.display_name - - lines = self._get_bom(bom_id, product_id=searchVariant, line_qty=bom_quantity, level=1) + # Get variants used for search + if not bom.product_id: + for variant in bom.product_tmpl_id.product_variant_ids: + bom_product_variants[variant.id] = variant.display_name + lines = self._get_bom(bom_id=bom_id, product=searchVariant, line_qty=bom_quantity, level=1) return { 'lines': lines, 'variants': bom_product_variants, @@ -89,37 +102,40 @@ def _get_report_data(self, bom_id, searchQty=0, searchVariant=False): 'is_uom_applied': self.env.user.user_has_groups('uom.group_uom') } - def _get_bom(self, bom_id=False, product_id=False, line_qty=False, line_id=False, level=False): + def _get_bom(self, bom_id=False, product=False, line_qty=False, line_id=False, level=False, report_name='all'): bom = self.env['mrp.bom'].browse(bom_id) bom_quantity = line_qty if line_id: current_line = self.env['mrp.bom.line'].browse(int(line_id)) bom_quantity = current_line.product_uom_id._compute_quantity(line_qty, bom.product_uom_id) # Display bom components for current selected product variant - if product_id: - product = self.env['product.product'].browse(int(product_id)) - else: + if not product: product = bom.product_id or bom.product_tmpl_id.product_variant_id + if product: + product_id = int(product) + product = self.env['product.product'].browse(product_id) attachments = self.env['mrp.document'].search(['|', '&', ('res_model', '=', 'product.product'), - ('res_id', '=', product.id), '&', ('res_model', '=', 'product.template'), ('res_id', '=', product.product_tmpl_id.id)]) + ('res_id', '=', product.id), '&', ('res_model', '=', 'product.template'), + ('res_id', '=', product.product_tmpl_id.id)]) else: product = bom.product_tmpl_id attachments = self.env['mrp.document'].search([('res_model', '=', 'product.template'), ('res_id', '=', product.id)]) operations = self._get_operation_line(bom.routing_id, float_round(bom_quantity / bom.product_qty, precision_rounding=1, rounding_method='UP'), 0) lines = { 'bom': bom, - 'bom_qty': bom_quantity, - 'bom_prod_name': product.display_name, + 'quantity': bom_quantity, + 'name': product.display_name, 'currency': self.env.user.company_id.currency_id, 'product': product, 'code': bom and bom.display_name or '', - 'price': product.uom_id._compute_price(product.standard_price, bom.product_uom_id) * bom_quantity, + 'prod_cost': product.uom_id._compute_price(product.standard_price, bom.product_uom_id) * bom_quantity, 'total': sum([op['total'] for op in operations]), 'level': level or 0, 'operations': operations, 'operations_cost': sum([op['total'] for op in operations]), 'attachments': attachments, + 'report_name': report_name, 'operations_time': sum([op['duration_expected'] for op in operations]) } components, total = self._get_bom_lines(bom, bom_quantity, product, line_id, level) @@ -143,9 +159,9 @@ def _get_bom_lines(self, bom, bom_quantity, product, line_id, level): sub_total = self.env.user.company_id.currency_id.round(sub_total) components.append({ 'prod_id': line.product_id.id, - 'prod_name': line.product_id.display_name, + 'name': line.product_id.display_name, 'code': line.child_bom_id and line.child_bom_id.display_name or '', - 'prod_qty': line_quantity, + 'quantity': line_quantity, 'prod_uom': line.product_uom_id.name, 'prod_cost': self.env.user.company_id.currency_id.round(price), 'parent_id': bom.id, @@ -156,7 +172,6 @@ def _get_bom_lines(self, bom, bom_quantity, product, line_id, level): 'phantom_bom': line.child_bom_id and line.child_bom_id.type == 'phantom' or False, 'attachments': self.env['mrp.document'].search(['|', '&', ('res_model', '=', 'product.product'), ('res_id', '=', line.product_id.id), '&', ('res_model', '=', 'product.template'), ('res_id', '=', line.product_id.product_tmpl_id.id)]), - }) total += sub_total return components, total @@ -203,35 +218,36 @@ def _get_price(self, bom, factor, product): price += self.env.user.company_id.currency_id.round(not_rounded_price) return price - def _get_pdf_line(self, bom_id, product_id=False, qty=1, child_bom_ids=[], unfolded=False): - - data = self._get_bom(bom_id=bom_id, product_id=product_id.id, line_qty=qty) + def _get_pdf_line(self, bom_id, product_id=False, qty=1, child_bom_ids=[], unfolded=False, report_name='all', report_type='pdf'): + data = self._get_bom(bom_id=bom_id, product=product_id, line_qty=qty) + bom = self.env['mrp.bom'].browse(bom_id) + product = product_id or bom.product_id or bom.product_tmpl_id.product_variant_id + pdf_lines = self.get_sub_lines(bom, product, qty, False, 1, child_bom_ids, unfolded, report_name, report_type) + data['components'] = [] + data['lines'] = pdf_lines + return data - def get_sub_lines(bom, product_id, line_qty, line_id, level): - data = self._get_bom(bom_id=bom.id, product_id=product_id.id, line_qty=line_qty, line_id=line_id, level=level) - bom_lines = data['components'] - lines = [] - for bom_line in bom_lines: - lines.append({ - 'name': bom_line['prod_name'], - 'type': 'bom', - 'quantity': bom_line['prod_qty'], - 'uom': bom_line['prod_uom'], - 'prod_cost': bom_line['prod_cost'], - 'bom_cost': bom_line['total'], - 'level': bom_line['level'], - 'code': bom_line['code'] - }) - if bom_line['child_bom'] and (unfolded or bom_line['child_bom'] in child_bom_ids): - line = self.env['mrp.bom.line'].browse(bom_line['line_id']) - lines += (get_sub_lines(line.child_bom_id, line.product_id, bom_line['prod_qty'], line, level + 1)) + def get_sub_lines(self, bom, product_id, line_qty, line_id, level, child_bom_ids=[], unfolded=False, report_name='all', report_type='pdf'): + data = self._get_bom(bom_id=bom.id, product=product_id, line_qty=line_qty, line_id=line_id, level=level) + bom_lines = data['components'] + lines = [] + bom_key = self.get_column_key_xlsx(report_name) + for bom_line in bom_lines: + dict_data = {} + for x in range(0, len(bom_key)): + dict_data.update({bom_key[x]: bom_line.get(bom_key[x])}) + lines.append(dict_data) + if bom_line['child_bom'] and (unfolded or bom_line['child_bom'] in child_bom_ids): + line = self.env['mrp.bom.line'].browse(bom_line['line_id']) + lines += (self.get_sub_lines(line.child_bom_id, line.product_id, bom_line['quantity'], line, level + 1, child_bom_ids, unfolded, report_name, report_type)) + if report_name != "bom_structure": if data['operations']: lines.append({ 'name': _('Operations'), 'type': 'operation', 'quantity': data['operations_time'], 'uom': _('minutes'), - 'bom_cost': data['operations_cost'], + 'total': data['operations_cost'], 'level': level, }) for operation in data['operations']: @@ -241,14 +257,50 @@ def get_sub_lines(bom, product_id, line_qty, line_id, level): 'type': 'operation', 'quantity': operation['duration_expected'], 'uom': _('minutes'), - 'bom_cost': operation['total'], + 'total': operation['total'], 'level': level + 1, }) - return lines + return lines - bom = self.env['mrp.bom'].browse(bom_id) - product = product_id or bom.product_id or bom.product_tmpl_id.product_variant_id - pdf_lines = get_sub_lines(bom, product, qty, False, 1) - data['components'] = [] - data['lines'] = pdf_lines + #TO BE OVERWRITTEN + def _get_report_name(self): + return _('General Report') + + def get_header(self, report_name): + data = [] + if report_name in ['all' or 'undefined']: + data += [ + {'name': 'Product'}, + {'name': 'BoM'}, + {'name': 'Quantity'}, + {'name': 'Product Cost'}, + {'name': 'BoM Cost'} + ] + elif report_name == 'bom_structure': + data += [ + {'name': 'Product'}, + {'name': 'BoM'}, + {'name': 'Quantity'}, + {'name': 'Product Cost'} + ] + elif report_name == 'bom_cost': + data += [ + {'name': 'Product'}, + {'name': 'BoM'}, + {'name': 'Quantity'}, + {'name': 'BoM Cost'} + ] + return data + + def get_column_key_xlsx(self, report_name): + data = ['name', 'quantity', 'prod_uom', 'prod_cost', 'total', 'level', 'code', 'type'] + return data + + def get_column_key(self, report_name): + if(report_name == "bom_structure"): + data = ['name', 'code', 'quantity', 'prod_cost'] + elif(report_name == "bom_cost"): + data = ['name', 'code', 'quantity', 'total'] + else: + data = ['name', 'code', 'quantity', 'prod_cost', 'total'] return data diff --git a/addons/mrp/report/mrp_report_bom_structure.xml b/addons/mrp/report/mrp_report_bom_structure.xml index 5b9c889a4e4fa..3f64363b46de3 100644 --- a/addons/mrp/report/mrp_report_bom_structure.xml +++ b/addons/mrp/report/mrp_report_bom_structure.xml @@ -8,9 +8,9 @@

BoM Structure & Cost

- + - +

Reference:
@@ -22,32 +22,30 @@ - - - - - - - + + + - + - - - - - @@ -87,14 +85,14 @@ - + - + - - + - - - - + + - - diff --git a/addons/mrp/static/src/js/mrp_bom_report.js b/addons/mrp/static/src/js/mrp_bom_report.js index 8a96c5d6d676f..bfdab321050c7 100644 --- a/addons/mrp/static/src/js/mrp_bom_report.js +++ b/addons/mrp/static/src/js/mrp_bom_report.js @@ -4,6 +4,8 @@ odoo.define('mrp.mrp_bom_report', function (require) { var core = require('web.core'); var framework = require('web.framework'); var stock_report_generic = require('stock.stock_report_generic'); +var session = require('web.session'); +var crash_manager = require('web.crash_manager'); var QWeb = core.qweb; var _t = core._t; @@ -20,7 +22,8 @@ var MrpBomReport = stock_report_generic.extend({ var args = [ this.given_context.active_id, this.given_context.searchQty || 1, - this.given_context.searchVariant, + parseInt(this.given_context.searchVariant), + this.given_context.report_name || 'all', ]; return this._rpc({ model: 'report.mrp.report_bom_structure', @@ -110,34 +113,55 @@ var MrpBomReport = stock_report_generic.extend({ this.$buttonPrint = $(QWeb.render('mrp.button')); this.$buttonPrint.filter('.o_mrp_bom_print').on('click', this._onClickPrint.bind(this)); this.$buttonPrint.filter('.o_mrp_bom_print_unfolded').on('click', this._onClickPrint.bind(this)); + this.$buttonPrint.filter('.o_mrp_bom_print_xlsx').on('click', this._onClickXlsx.bind(this)); this.$searchView = $(QWeb.render('mrp.report_bom_search', _.omit(this.data, 'lines'))); this.$searchView.find('.o_mrp_bom_report_qty').on('change', this._onChangeQty.bind(this)); this.$searchView.find('.o_mrp_bom_report_variants').on('change', this._onChangeVariants.bind(this)); - this.$searchView.find('.o_mrp_bom_report_type').on('change', this._onChangeType.bind(this)); + this.$searchView.find('.bom_report').on('change', this._onChangeType.bind(this)); }, _onClickPrint: function (ev) { var childBomIDs = _.map(this.$el.find('.o_mrp_bom_foldable').closest('tr'), function (el) { return $(el).data('id'); }); framework.blockUI(); - var reportname = 'mrp.report_bom_structure?docids=' + this.given_context.active_id + '&report_type=' + this.given_context.report_type; - if (! $(ev.currentTarget).hasClass('o_mrp_bom_print_unfolded')) { - reportname += '&quantity=' + (this.given_context.searchQty || 1) + + var reportname = 'mrp.report_bom_structure?docids=' + this.given_context.active_id + '&report_name=' + (this.given_context.report_type || 'pdf' || 'qweb-pdf') + '&quantity=' + (this.given_context.searchQty || 1) + '&childs=' + JSON.stringify(childBomIDs); - } - if (this.given_context.searchVariant) { - reportname += '&variant=' + this.given_context.searchVariant; - } + reportname += '&variant=' + (this.given_context.searchVariant || 1); var action = { 'type': 'ir.actions.report', 'report_type': 'qweb-pdf', - 'report_name': reportname, + 'report_name': reportname, 'report_file': 'mrp.report_bom_structure', }; return this.do_action(action).then(function (){ framework.unblockUI(); }); }, + _onClickXlsx: function (ev) { + var childBomIDs = _.map(this.$el.find('.o_mrp_bom_foldable').closest('tr'), function (el) { + return $(el).data('id'); + }); + framework.blockUI(); + var bom_quantity = this.$searchView.find('.o_mrp_bom_report_qty').val(); + var report_name = this.$searchView.find('.bom_report').children('option:selected').attr('data-type'); + var variant = this.$searchView.find('.o_mrp_bom_report_variants').val(); + var bom_id = parseInt(this.given_context.active_id); + var def = $.Deferred(); + session.get_file({ + url: '/bom_report_xslx/'+ bom_id, + data: { + bom_quantity: bom_quantity, + variant: variant || 0, + report_name: report_name, + }, + success: def.resolve.bind(def), + error: function () { + crash_manager.rpc_error.apply(crash_manager, arguments); + def.reject(); + }, + complete: framework.unblockUI, + }); + }, _onChangeQty: function (ev) { var qty = $(ev.currentTarget).val().trim(); if (qty) { diff --git a/addons/mrp/static/src/xml/mrp.xml b/addons/mrp/static/src/xml/mrp.xml index 5791fc2f9c006..403f135943492 100644 --- a/addons/mrp/static/src/xml/mrp.xml +++ b/addons/mrp/static/src/xml/mrp.xml @@ -3,6 +3,7 @@ + @@ -27,7 +28,7 @@
-
ProductBoMQuantityUnit of MeasureProduct CostBoM CostAttachments + +
- + - + + + + @@ -64,11 +62,11 @@ Unit Cost - + + - + +
- + @@ -105,12 +103,12 @@ + + @@ -124,7 +122,7 @@ -
@@ -135,9 +133,9 @@ Minutes + + @@ -159,8 +157,8 @@ Minutes + @@ -178,7 +176,7 @@ -
@@ -193,11 +191,11 @@ + - + +