[IMP] mrp: add new bom and cost report
Add a new report in order to display: - bom structure and cost - bom structure - bom cost The user is allowed to print multiple bom reports at once. Print the report dynamically depending what have been unfolded. In html the rendering is lazy, when unfolding the client make a request to the server in order to get the html that will be append to the DOM. For the pdf the kwargs unfolded could be used in order to render everything at once. With the help of: Dhaval Suthar <dut@odoo.com> Jigar Patel <jat@openerp.com> task-36741
This commit is contained in:
committed by
Simon Lejeune
parent
5a22b79af5
commit
9201f92a4f
@@ -37,8 +37,10 @@
|
||||
'views/mrp_templates.xml',
|
||||
'views/stock_scrap_views.xml',
|
||||
'report/mrp_report_views_main.xml',
|
||||
'report/mrp_report_bom_structure.xml',
|
||||
'report/mrp_production_templates.xml',
|
||||
],
|
||||
'qweb': ['static/src/xml/mrp.xml'],
|
||||
'demo': [
|
||||
'data/mrp_demo.xml',
|
||||
],
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from . import mrp_report_bom_structure
|
||||
@@ -0,0 +1,220 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import json
|
||||
|
||||
from odoo import api, models, _
|
||||
from odoo.tools import float_round
|
||||
|
||||
class ReportBomStructure(models.AbstractModel):
|
||||
_name = 'report.mrp.report_bom_structure'
|
||||
|
||||
@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')
|
||||
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=int(data.get('quantity')), child_bom_ids=json.loads(data.get('childs')))
|
||||
else:
|
||||
doc = self._get_pdf_line(bom_id, product_id=product_variant_id, unfolded=True)
|
||||
doc['report_type'] = 'pdf'
|
||||
doc['report_structure'] = data and data.get('report_type') or 'all'
|
||||
docs.append(doc)
|
||||
return {
|
||||
'doc_ids': docids,
|
||||
'doc_model': 'mrp.bom',
|
||||
'docs': docs,
|
||||
}
|
||||
|
||||
@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'
|
||||
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)
|
||||
return self.env.ref('mrp.report_mrp_bom_line').render({'data': lines})
|
||||
|
||||
@api.model
|
||||
def get_operations(self, bom_id=False, qty=0, level=0):
|
||||
bom = self.env['mrp.bom'].browse(bom_id)
|
||||
lines = self._get_operation_line(bom.routing_id, qty, level)
|
||||
values = {
|
||||
'bom_id': bom_id,
|
||||
'currency': self.env.user.company_id.currency_id,
|
||||
'operations': lines,
|
||||
}
|
||||
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):
|
||||
lines = {}
|
||||
bom = self.env['mrp.bom'].browse(bom_id)
|
||||
bom_quantity = searchQty or bom.product_qty
|
||||
bom_product_variants = {}
|
||||
bom_uom_name = ''
|
||||
|
||||
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)
|
||||
|
||||
return {
|
||||
'lines': lines,
|
||||
'variants': bom_product_variants,
|
||||
'bom_uom_name': bom_uom_name,
|
||||
'bom_qty': bom_quantity,
|
||||
'is_variant_applied': self.env.user.user_has_groups('product.group_product_variant') and len(bom_product_variants) > 1,
|
||||
'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):
|
||||
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:
|
||||
product = bom.product_id or bom.product_tmpl_id.product_variant_id
|
||||
operations = self._get_operation_line(bom.routing_id, (bom_quantity / bom.product_qty), 0)
|
||||
lines = {
|
||||
'bom': bom,
|
||||
'bom_qty': bom_quantity,
|
||||
'bom_prod_name': product.display_name,
|
||||
'currency': self.env.user.company_id.currency_id,
|
||||
'product': product,
|
||||
'price': 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]),
|
||||
'operations_time': sum([op['duration_expected'] for op in operations])
|
||||
}
|
||||
components, total = self._get_bom_lines(bom, bom_quantity, product, line_id, level)
|
||||
lines['components'] = components
|
||||
lines['total'] += total
|
||||
return lines
|
||||
|
||||
def _get_bom_lines(self, bom, bom_quantity, product, line_id, level):
|
||||
components = []
|
||||
total = 0
|
||||
for line in bom.bom_line_ids:
|
||||
line_quantity = (bom_quantity) * line.product_qty
|
||||
if line._skip_bom_line(product):
|
||||
continue
|
||||
price = line.product_id.uom_id._compute_price(line.product_id.standard_price, line.product_uom_id) * line_quantity
|
||||
if line.child_bom_id:
|
||||
factor = line.product_uom_id._compute_quantity(line_quantity, line.child_bom_id.product_uom_id) * line.child_bom_id.product_qty
|
||||
total = self._get_price(line.child_bom_id, factor)
|
||||
else:
|
||||
total = price
|
||||
components.append({
|
||||
'prod_id': line.product_id.id,
|
||||
'prod_name': line.product_id.display_name,
|
||||
'prod_qty': line_quantity,
|
||||
'prod_uom': line.product_uom_id.name,
|
||||
'prod_cost': price,
|
||||
'parent_id': bom.id,
|
||||
'line_id': line.id,
|
||||
'level': level or 0,
|
||||
'total': total,
|
||||
'child_bom': line.child_bom_id.id,
|
||||
'phantom_bom': line.child_bom_id and line.child_bom_id.type == 'phantom' or False,
|
||||
})
|
||||
total += total
|
||||
return components, total
|
||||
|
||||
def _get_operation_line(self, routing, qty, level):
|
||||
operations = []
|
||||
total = 0.0
|
||||
for operation in routing.operation_ids:
|
||||
duration_expected = qty * operation.time_cycle + operation.workcenter_id.time_stop + operation.workcenter_id.time_start
|
||||
total = ((duration_expected / 60.0) * operation.workcenter_id.costs_hour)
|
||||
operations.append({
|
||||
'level': level or 0,
|
||||
'operation': operation,
|
||||
'name': operation.name + ' - ' + operation.workcenter_id.name,
|
||||
'duration_expected': duration_expected,
|
||||
'total': float_round(total, precision_rounding=self.env.user.company_id.currency_id.rounding),
|
||||
})
|
||||
return operations
|
||||
|
||||
def _get_price(self, bom, factor):
|
||||
price = 0
|
||||
if bom.routing_id:
|
||||
operations = self._get_operation_line(bom.routing_id, factor, 0)
|
||||
price += sum([op['total'] for op in operations])
|
||||
|
||||
for line in bom.bom_line_ids:
|
||||
if line.child_bom_id:
|
||||
qty = line.product_uom_id._compute_quantity(line.product_qty * factor, line.child_bom_id.product_uom_id)
|
||||
sub_price = self._get_price(line.child_bom_id, qty)
|
||||
price += sub_price * qty
|
||||
else:
|
||||
prod_qty = line.product_qty * factor
|
||||
price += (line.product_id.uom_id._compute_price(line.product_id.standard_price, line.product_uom_id) * prod_qty)
|
||||
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, line_qty=qty)
|
||||
|
||||
def get_sub_lines(bom, product_id, line_qty, line_id, level):
|
||||
data = self._get_bom(bom_id=bom.id, product_id=product_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'],
|
||||
})
|
||||
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, line.product_qty * data['bom_qty'], line, level + 1))
|
||||
if data['operations']:
|
||||
lines.append({
|
||||
'name': _('Operations'),
|
||||
'type': 'operation',
|
||||
'quantity': data['operations_time'],
|
||||
'uom': _('minutes'),
|
||||
'bom_cost': data['operations_cost'],
|
||||
'level': level,
|
||||
})
|
||||
for operation in data['operations']:
|
||||
if unfolded or 'operation-' + str(bom.id) in child_bom_ids:
|
||||
lines.append({
|
||||
'name': operation['name'],
|
||||
'type': 'operation',
|
||||
'quantity': operation['duration_expected'],
|
||||
'uom': _('minutes'),
|
||||
'bom_cost': operation['total'],
|
||||
'level': level + 1,
|
||||
})
|
||||
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
|
||||
return data
|
||||
@@ -0,0 +1,191 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<odoo>
|
||||
<template id="report_mrp_bom">
|
||||
<t t-raw="'<base href=%s>' % base_url"/>
|
||||
<div class="container o_mrp_bom_report_page">
|
||||
<t t-if="data['components'] or data['lines']">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<h1>BoM Structure & Cost</h1>
|
||||
<h3>
|
||||
<a t-if="data['report_type'] == 'html'" t-att-data-res-id="data['product'].id" t-att-data-model="data['product']._name" class="o_mrp_bom_action">
|
||||
<t t-esc="data['bom_prod_name']"/>
|
||||
</a>
|
||||
<t t-else="" t-esc="data['bom_prod_name']"/>
|
||||
</h3>
|
||||
<h6 t-if="data['bom'].code">Reference: <t t-esc="data['bom'].code"/></h6>
|
||||
</div>
|
||||
</div>
|
||||
<t t-set="currency" t-value="data['currency']"/>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="mt16">
|
||||
<table width="100%" class="o_mrp_bom_expandable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Product</th>
|
||||
<th>Quantity</th>
|
||||
<th groups="uom.group_uom">Unit of Measure</th>
|
||||
<th t-if="data['report_structure'] != 'bom_cost'" class="o_mrp_prod_cost" title="This is the cost defined on the product.">Product Cost</th>
|
||||
<th t-if="data['report_structure'] != 'bom_structure'" class="o_mrp_bom_cost" title="This is the cost based on the BoM of the product. It is computed by summing the costs of the components and operations needed to build the product.">BoM Cost</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<span><a t-if="data['report_type'] == 'html'" t-att-data-res-id="data['product'].id" t-att-data-model="'product.product'" class="o_mrp_bom_action"><t t-esc="data['bom_prod_name']"/></a><t t-else="" t-esc="data['bom_prod_name']"/></span>
|
||||
</td>
|
||||
<td><span><t t-esc="data['bom_qty']" t-options='{"widget": "float", "decimal_precision": "Product Unit of Measure"}'/></span></td>
|
||||
<td groups="uom.group_uom"><span><t t-esc="data['bom'].product_uom_id.name"/></span></td>
|
||||
<td t-if="data['report_structure'] != 'bom_cost'" class="o_mrp_prod_cost">
|
||||
<span><t t-esc="data['price']" t-options='{"widget": "monetary", "display_currency": currency}'/></span>
|
||||
</td>
|
||||
<td t-if="data['report_structure'] != 'bom_structure'" class="o_mrp_bom_cost">
|
||||
<span><t t-esc="data['total']" t-options='{"widget": "monetary", "display_currency": currency}'/></span>
|
||||
</td>
|
||||
</tr>
|
||||
<t t-if="data['report_type'] == 'html'" t-call="mrp.report_mrp_bom_line"/>
|
||||
<t t-if="data['report_type'] == 'pdf'" t-call="mrp.report_mrp_bom_pdf_line"/>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td><span><strong>Unit Cost</strong></span></td>
|
||||
<td groups="uom.group_uom"></td>
|
||||
<td t-if="data['report_structure'] != 'bom_cost'" class="o_mrp_prod_cost">
|
||||
<span><t t-esc="data['price']/data['bom_qty']" t-options='{"widget": "monetary", "display_currency": currency}'/></span>
|
||||
</td>
|
||||
<td t-if="data['report_structure'] != 'bom_structure'" class="o_mrp_bom_cost">
|
||||
<span><t t-esc="data['total']/data['bom_qty']" t-options='{"widget": "monetary", "display_currency": currency}'/></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
<t t-else="">
|
||||
<h1 class="text-center">No data available.</h1>
|
||||
</t>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="report_mrp_bom_line">
|
||||
<t t-set="currency" t-value="data['currency']"/>
|
||||
<t t-foreach="data['components']" t-as="l">
|
||||
<t t-set="space_td" t-value="'margin-left: '+ str(l['level'] * 20) + 'px;'"/>
|
||||
<tr class="o_mrp_bom_report_line" t-att-data-id="l['child_bom']" t-att-parent_id="l['parent_id']" t-att-data-line="l['line_id']" t-att-data-product_id="l['prod_id']" t-att-data-qty="l['prod_qty']" t-att-data-level="l['level']">
|
||||
<td>
|
||||
<span t-att-style="space_td"/>
|
||||
<t t-if="l['child_bom']">
|
||||
<span t-att-data-function="'get_bom'" class="o_mrp_bom_unfoldable fa fa-fw fa-caret-right"/>
|
||||
</t>
|
||||
<span t-att-class="None if l['child_bom'] else 'o_mrp_bom_no_fold'">
|
||||
<a t-att-data-res-id="l['prod_id']" t-att-data-model="'product.product'" class="o_mrp_bom_action"><t t-esc="l['prod_name']"/></a>
|
||||
</span>
|
||||
<t t-if="l['phantom_bom']">
|
||||
<span class="fa fa-dropbox" title="This is a BoM of type Kit!"/>
|
||||
</t>
|
||||
</td>
|
||||
<td><span><t t-esc="l['prod_qty']" t-options='{"widget": "float", "decimal_precision": "Product Unit of Measure"}'/></span></td>
|
||||
<td groups="uom.group_uom"><span><t t-esc="l['prod_uom']"/></span></td>
|
||||
<td class="o_mrp_prod_cost">
|
||||
<span t-esc="l['prod_cost']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
<td class="o_mrp_bom_cost">
|
||||
<span t-esc="l['total']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
<t t-if="data['operations']">
|
||||
<t t-set="space_td" t-value="'margin-left: '+ str(data['level'] * 20) + 'px;'"/>
|
||||
<tr class="o_mrp_bom_report_line o_mrp_bom_cost" t-att-data-id="'operation-' + str(data['bom'].id)" t-att-data-bom-id="data['bom'].id" t-att-parent_id="data['bom'].id" t-att-data-qty="data['bom_qty']" t-att-data-level="data['level']">
|
||||
<td>
|
||||
<span t-att-style="space_td"/>
|
||||
<span class="o_mrp_bom_unfoldable fa fa-fw fa-caret-right" t-att-data-function="'get_operations'"/>
|
||||
Operations
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="data['operations_time']" t-options='{"widget": "float_time"}'/>
|
||||
</td>
|
||||
<td groups="uom.group_uom"><span>Minutes</span></td>
|
||||
<td class="o_mrp_prod_cost">
|
||||
</td>
|
||||
<td class="o_mrp_bom_cost">
|
||||
<span t-esc="data['operations_cost']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_mrp_operation_line">
|
||||
<t t-set="currency" t-value="data['currency']"/>
|
||||
<t t-foreach="data['operations']" t-as="op">
|
||||
<t t-set="space_td" t-value="'margin-left: '+ str(op['level'] * 20) + 'px;'"/>
|
||||
<tr class="o_mrp_bom_report_line o_mrp_bom_cost" t-att-parent_id="'operation-' + str(data['bom_id'])">
|
||||
<td>
|
||||
<span t-att-style="space_td"/>
|
||||
<a t-att-data-res-id="op['operation'].id" t-att-data-model="'mrp.routing.workcenter'" class="o_mrp_bom_action"><t t-esc="op['name']"/></a>
|
||||
</td>
|
||||
<td>
|
||||
<span t-esc="op['duration_expected']" t-options='{"widget": "float_time"}'/>
|
||||
</td>
|
||||
<td groups="uom.group_uom"><span>Minutes</span></td>
|
||||
<td class="o_mrp_prod_cost"></td>
|
||||
<td class="o_mrp_bom_cost">
|
||||
<span t-esc="op['total']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_mrp_bom_pdf">
|
||||
<t t-raw="'<base href=%s>' % base_url"/>
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="mrp.report_mrp_bom"/>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_mrp_bom_pdf_line">
|
||||
<t t-set="currency" t-value="data['currency']"/>
|
||||
<t t-foreach="data['lines']" t-as="l">
|
||||
<t t-set="space_td" t-value="'margin-left: '+ str(l['level'] * 20) + 'px;'"/>
|
||||
<tr t-if="data['report_structure'] != 'bom_structure' or l['type'] != 'operation'">
|
||||
<td>
|
||||
<span t-att-style="space_td"/>
|
||||
<span><t t-esc="l['name']"/></span>
|
||||
</td>
|
||||
<td>
|
||||
<span>
|
||||
<t t-if="l['type'] == 'operation'" t-esc="l['quantity']" t-options='{"widget": "float_time"}'/>
|
||||
<t t-if="l['type'] == 'bom'" t-esc="l['quantity']" t-options='{"widget": "float", "decimal_precision": "Product Unit of Measure"}'/>
|
||||
</span>
|
||||
</td>
|
||||
<td groups="uom.group_uom"><span><t t-esc="l['uom']"/></span></td>
|
||||
<td t-if="data['report_structure'] != 'bom_cost'" class="o_mrp_prod_cost">
|
||||
<span t-if="'prod_cost' in l" t-esc="l['prod_cost']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
<td t-if="data['report_structure'] != 'bom_structure'" class="o_mrp_bom_cost">
|
||||
<span t-esc="l['bom_cost']" t-options='{"widget": "monetary", "display_currency": currency}'/>
|
||||
</td>
|
||||
</tr>
|
||||
</t>
|
||||
</template>
|
||||
|
||||
<template id="report_bom_structure">
|
||||
<t t-set="data_report_landscape" t-value="True"/>
|
||||
<t t-call="web.html_container">
|
||||
<t t-call="web.basic_layout">
|
||||
<t t-foreach="docs" t-as="data">
|
||||
<div class="page">
|
||||
<t t-raw="'<base href=%s>' % base_url"/>
|
||||
<t t-call-assets="mrp.assets_common" t-js="False"/>
|
||||
<t t-call="mrp.report_mrp_bom"/>
|
||||
</div>
|
||||
<p style="page-break-before:always;"> </p>
|
||||
</t>
|
||||
</t>
|
||||
</t>
|
||||
</template>
|
||||
</odoo>
|
||||
@@ -10,6 +10,14 @@
|
||||
report_type="qweb-pdf"
|
||||
print_report_name="'Production Order - %s' % object.name"
|
||||
/>
|
||||
<report
|
||||
string="BoM Structure"
|
||||
id="action_report_bom_structure"
|
||||
model="mrp.bom"
|
||||
name="mrp.report_bom_structure"
|
||||
file="mrp.report_bom_structure"
|
||||
report_type="qweb-pdf"
|
||||
print_report_name="'Bom Structure - %s' % object.display_name"
|
||||
/>
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
odoo.define('mrp.mrp_bom_report', function (require) {
|
||||
'use strict';
|
||||
|
||||
var core = require('web.core');
|
||||
var framework = require('web.framework');
|
||||
var stock_report_generic = require('stock.stock_report_generic');
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
var MrpBomReport = stock_report_generic.extend({
|
||||
events: {
|
||||
'click .o_mrp_bom_unfoldable': '_onClickUnfold',
|
||||
'click .o_mrp_bom_foldable': '_onClickFold',
|
||||
'click .o_mrp_bom_action': '_onClickAction',
|
||||
},
|
||||
get_html: function() {
|
||||
var self = this;
|
||||
var args = [
|
||||
this.given_context.active_id,
|
||||
this.given_context.searchQty || 1,
|
||||
this.given_context.searchVariant,
|
||||
];
|
||||
return this._rpc({
|
||||
model: 'report.mrp.report_bom_structure',
|
||||
method: 'get_html',
|
||||
args: args
|
||||
})
|
||||
.then(function (result) {
|
||||
self.data = result;
|
||||
});
|
||||
},
|
||||
set_html: function() {
|
||||
var self = this;
|
||||
return this._super().then(function () {
|
||||
self.$el.html(self.data.lines);
|
||||
self.renderSearch();
|
||||
self.update_cp();
|
||||
});
|
||||
},
|
||||
render_html: function(event, $el, result){
|
||||
$el.after(result);
|
||||
$(event.currentTarget).toggleClass('o_mrp_bom_foldable o_mrp_bom_unfoldable fa-caret-right fa-caret-down');
|
||||
this._reload_report_type();
|
||||
},
|
||||
get_bom: function(event) {
|
||||
var self = this;
|
||||
var $parent = $(event.currentTarget).closest('tr');
|
||||
var activeID = $parent.data('id');
|
||||
var productID = $parent.data('product_id');
|
||||
var lineID = $parent.data('line');
|
||||
var qty = $parent.data('qty');
|
||||
var level = $parent.data('level') || 0;
|
||||
return this._rpc({
|
||||
model: 'report.mrp.report_bom_structure',
|
||||
method: 'get_bom',
|
||||
args: [
|
||||
activeID,
|
||||
productID,
|
||||
parseFloat(qty),
|
||||
lineID,
|
||||
level + 1
|
||||
]
|
||||
})
|
||||
.then(function (result) {
|
||||
self.render_html(event, $parent, result);
|
||||
});
|
||||
},
|
||||
get_operations: function(event) {
|
||||
var self = this;
|
||||
var $parent = $(event.currentTarget).closest('tr');
|
||||
var activeID = $parent.data('bom-id');
|
||||
var qty = $parent.data('qty');
|
||||
var level = $parent.data('level') || 0;
|
||||
return this._rpc({
|
||||
model: 'report.mrp.report_bom_structure',
|
||||
method: 'get_operations',
|
||||
args: [
|
||||
activeID,
|
||||
parseFloat(qty),
|
||||
level + 1
|
||||
]
|
||||
})
|
||||
.then(function (result) {
|
||||
self.render_html(event, $parent, result);
|
||||
});
|
||||
},
|
||||
update_cp: function () {
|
||||
var status = {
|
||||
cp_content: {
|
||||
$buttons: this.$buttonPrint,
|
||||
$searchview_buttons: this.$searchView
|
||||
},
|
||||
};
|
||||
return this.update_control_panel(status);
|
||||
},
|
||||
renderSearch: function () {
|
||||
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.$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));
|
||||
},
|
||||
_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;
|
||||
if (! $(ev.currentTarget).hasClass('o_mrp_bom_print_unfolded')) {
|
||||
reportname += '&quantity=' + (this.given_context.searchQty || 1) +
|
||||
'&childs=' + JSON.stringify(childBomIDs) +
|
||||
'&report_type=' + this.given_context.report_type;
|
||||
}
|
||||
if (this.given_context.searchVariant) {
|
||||
reportname += '&variant=' + this.given_context.searchVariant;
|
||||
}
|
||||
var action = {
|
||||
'type': 'ir.actions.report',
|
||||
'report_type': 'qweb-pdf',
|
||||
'report_name': reportname,
|
||||
'report_file': 'mrp.report_bom_structure',
|
||||
};
|
||||
return this.do_action(action).then(function (){
|
||||
framework.unblockUI();
|
||||
});
|
||||
},
|
||||
_onChangeQty: function (ev) {
|
||||
var qty = $(ev.currentTarget).val().trim();
|
||||
if (qty) {
|
||||
this.given_context.searchQty = parseFloat(qty);
|
||||
this._reload();
|
||||
}
|
||||
},
|
||||
_onChangeType: function (ev) {
|
||||
var report_type = $("option:selected", $(ev.currentTarget)).data('type');
|
||||
this.given_context.report_type = report_type;
|
||||
this._reload_report_type();
|
||||
},
|
||||
_onChangeVariants: function (ev) {
|
||||
this.given_context.searchVariant = $(ev.currentTarget).val();
|
||||
this._reload();
|
||||
},
|
||||
_onClickUnfold: function (ev) {
|
||||
var redirect_function = $(ev.currentTarget).data('function');
|
||||
this[redirect_function](ev);
|
||||
},
|
||||
_onClickFold: function (ev) {
|
||||
this._removeLines($(ev.currentTarget).closest('tr'));
|
||||
$(ev.currentTarget).toggleClass('o_mrp_bom_foldable o_mrp_bom_unfoldable fa-caret-right fa-caret-down');
|
||||
},
|
||||
_onClickAction: function (ev) {
|
||||
return this.do_action({
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: $(ev.currentTarget).data('model'),
|
||||
res_id: $(ev.currentTarget).data('res-id'),
|
||||
views: [[false, 'form']],
|
||||
target: 'current'
|
||||
});
|
||||
},
|
||||
_reload: function () {
|
||||
var self = this;
|
||||
return this.get_html().then(function () {
|
||||
self.$el.html(self.data.lines);
|
||||
self._reload_report_type();
|
||||
});
|
||||
},
|
||||
_reload_report_type: function () {
|
||||
this.$('.o_mrp_bom_cost.o_hidden, .o_mrp_prod_cost.o_hidden').toggleClass('o_hidden');
|
||||
if (this.given_context.report_type === 'bom_structure') {
|
||||
this.$('.o_mrp_bom_cost').toggleClass('o_hidden');
|
||||
}
|
||||
if (this.given_context.report_type === 'bom_cost') {
|
||||
this.$('.o_mrp_prod_cost').toggleClass('o_hidden');
|
||||
}
|
||||
},
|
||||
_removeLines: function ($el) {
|
||||
var self = this;
|
||||
var activeID = $el.data('id');
|
||||
_.each(this.$('tr[parent_id='+ activeID +']'), function (parent) {
|
||||
var $parent = self.$(parent);
|
||||
var $el = self.$('tr[parent_id='+ $parent.data('id') +']');
|
||||
if ($el.length) {
|
||||
self._removeLines($parent);
|
||||
}
|
||||
$parent.remove();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
core.action_registry.add('mrp_bom_report', MrpBomReport);
|
||||
return MrpBomReport;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,40 @@
|
||||
.o_mrp_bom_report_page {
|
||||
background-color: @odoo-view-background-color;
|
||||
margin: 16px auto;
|
||||
padding: 24px 16px;
|
||||
.o_mrp_bom_action {
|
||||
cursor: pointer;
|
||||
}
|
||||
// Manage expand icon
|
||||
table.o_mrp_bom_expandable {
|
||||
thead tr {
|
||||
border-width: 2px;
|
||||
border-top-style: groove;
|
||||
border-bottom-style: groove;
|
||||
}
|
||||
tbody tr {
|
||||
border-width: 1px;
|
||||
border-top-style: solid;
|
||||
border-bottom-style: groove;
|
||||
.o_mrp_bom_unfoldable, .o_mrp_bom_foldable {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
thead tr th {
|
||||
padding-left: 10px;
|
||||
}
|
||||
tbody, tfoot {
|
||||
& > tr > td ~ td > span {
|
||||
margin-left: 10px;
|
||||
}
|
||||
tr {
|
||||
.o_mrp_bom_no_fold {
|
||||
margin-left: 18px;
|
||||
}
|
||||
.o_mrp_bom_unfoldable, .o_mrp_bom_foldable {
|
||||
margin-left: -2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates>
|
||||
<t t-name="mrp.button">
|
||||
<button type="button" class="btn btn-primary btn-sm o_mrp_bom_print">print</button>
|
||||
<button type="button" class="btn btn-primary btn-sm ml8 o_mrp_bom_print_unfolded">print unfolded</button>
|
||||
</t>
|
||||
|
||||
<form class="form-inline" t-name="mrp.report_bom_search">
|
||||
<div class="form-group col-md-4">
|
||||
<label>Quantity:</label>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<input type="number" t-att-value="bom_qty" min="1" class="o_input o_mrp_bom_report_qty"/>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<t t-if="is_uom_applied" t-esc="bom_uom_name"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="is_variant_applied" class="form-group col-md-4">
|
||||
<label>Variant:</label>
|
||||
<select class="o_input o_mrp_bom_report_variants">
|
||||
<option t-foreach="variants" t-as="variant" t-att-value="variant">
|
||||
<t t-esc="variants[variant]"/>
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group col-md-4">
|
||||
<label>Report:</label>
|
||||
<select class="o_input o_mrp_bom_report_type">
|
||||
<option t-att-data-type="'all'">BoM Structure & Cost</option>
|
||||
<option t-att-data-type="'bom_structure'">BoM Structure</option>
|
||||
<option t-att-data-type="'bom_cost'">BoM Cost</option>
|
||||
</select>
|
||||
</div>
|
||||
</form>
|
||||
</templates>
|
||||
@@ -2,6 +2,12 @@
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Bill of Materials -->
|
||||
<record id="action_report_mrp_bom" model="ir.actions.client">
|
||||
<field name="name">Cost & Structure</field>
|
||||
<field name="tag">mrp_bom_report</field>
|
||||
<field name="context" eval="{'model': 'report.mrp.report_bom_structure'}" />
|
||||
</record>
|
||||
|
||||
<record id="mrp_bom_form_view" model="ir.ui.view">
|
||||
<field name="name">mrp.bom.form</field>
|
||||
<field name="model">mrp.bom</field>
|
||||
@@ -9,6 +15,8 @@
|
||||
<form string="Bill of Material">
|
||||
<sheet>
|
||||
<div class="oe_button_box" name="button_box">
|
||||
<button name="%(action_report_mrp_bom)d" type="action"
|
||||
class="oe_stat_button" icon="fa-bars" string="Cost & Structure" attrs="{'invisible': [('bom_line_ids', '=', [])]}"/>
|
||||
<button name="toggle_active" type="object"
|
||||
class="oe_stat_button" icon="fa-archive">
|
||||
<field name="active" widget="boolean_button"
|
||||
|
||||
@@ -4,9 +4,16 @@
|
||||
<xpath expr="." position="inside">
|
||||
<script type="text/javascript" src="/mrp/static/src/js/mrp.js"></script>
|
||||
<link rel="stylesheet" type="text/less" href="/mrp/static/src/less/mrp.less"/>
|
||||
<script type="text/javascript" src="/mrp/static/src/js/mrp_bom_report.js"></script>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="assets_common" name="mrp bom common assets" inherit_id="web.assets_common">
|
||||
<xpath expr="." position="inside">
|
||||
<link rel="stylesheet" href="/mrp/static/src/less/mrp_bom_report.less" />
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="qunit_suite" inherit_id="web.qunit_suite">
|
||||
<xpath expr="//script[last()]" position="after">
|
||||
<script type="text/javascript" src="/mrp/static/tests/mrp_tests.js"/>
|
||||
|
||||
Reference in New Issue
Block a user