diff --git a/addons/mrp/report/mrp_report_mo_overview.py b/addons/mrp/report/mrp_report_mo_overview.py
index 27343f4c6af..5a9051ef62d 100644
--- a/addons/mrp/report/mrp_report_mo_overview.py
+++ b/addons/mrp/report/mrp_report_mo_overview.py
@@ -49,6 +49,9 @@ class ReportMoOverview(models.AbstractModel):
doc['show_receipts'] = data.get('receipts') == '1'
if doc['show_receipts']:
footer_colspan += 1
+ doc['show_unit_costs'] = data.get('unitCosts') == '1'
+ if doc['show_unit_costs']:
+ footer_colspan += 1
doc['show_mo_costs'] = data.get('moCosts') == '1'
doc['show_real_costs'] = data.get('realCosts') == '1'
doc['show_uom'] = self.env.user.user_has_groups('uom.group_uom')
@@ -76,6 +79,7 @@ class ReportMoOverview(models.AbstractModel):
initial_mo_cost, initial_real_cost = self._compute_cost_sums(components, operations)
remaining_cost_share, byproducts = self._get_byproducts_data(production, initial_mo_cost, initial_real_cost, level=1, current_index='')
summary = self._get_mo_summary(production, components, initial_mo_cost, initial_real_cost, remaining_cost_share)
+ extra_lines = self._get_report_extra_lines(summary, components, operations, production.state == 'done')
return {
'id': production.id,
'name': production.display_name,
@@ -83,18 +87,77 @@ class ReportMoOverview(models.AbstractModel):
'components': components,
'operations': operations,
'byproducts': byproducts,
- 'extras': self._get_report_extra_lines(summary),
+ 'extras': extra_lines,
+ 'cost_breakdown': self._get_cost_breakdown_data(production, extra_lines, remaining_cost_share),
}
- def _get_report_extra_lines(self, summary):
- currency = summary.get('currency', self.env.company)
+ def _get_report_extra_lines(self, summary, components, operations, production_done=False):
+ currency = summary.get('currency', self.env.company.currency_id)
unit_mo_cost = currency.round(summary.get('mo_cost', 0) / summary.get('quantity', 1))
unit_real_cost = currency.round(summary.get('real_cost', 0) / summary.get('quantity', 1))
- return {
+ extras = {
'unit_mo_cost': unit_mo_cost,
'unit_mo_cost_decorator': self._get_comparison_decorator(unit_real_cost, unit_mo_cost, currency.rounding),
'unit_real_cost': unit_real_cost,
}
+ if production_done:
+ production_qty = summary.get('quantity', 1.0)
+ extras['total_mo_cost_components'] = sum(compo.get('summary', {}).get('mo_cost', 0.0) for compo in components)
+ extras['total_real_cost_components'] = sum(compo.get('summary', {}).get('real_cost', 0.0) for compo in components)
+ extras['total_mo_cost_components_decorator'] = self._get_comparison_decorator(extras['total_real_cost_components'], extras['total_mo_cost_components'], currency.rounding)
+ extras['unit_mo_cost_components'] = extras['total_mo_cost_components'] / production_qty
+ extras['unit_real_cost_components'] = extras['total_real_cost_components'] / production_qty
+ extras['total_mo_cost_operations'] = operations.get('summary', {}).get('mo_cost', 0.0)
+ extras['total_real_cost_operations'] = operations.get('summary', {}).get('real_cost', 0.0)
+ extras['total_mo_cost_operations_decorator'] = self._get_comparison_decorator(extras['total_real_cost_operations'], extras['total_mo_cost_operations'], currency.rounding)
+ extras['unit_mo_cost_operations'] = extras['total_mo_cost_operations'] / production_qty
+ extras['unit_real_cost_operations'] = extras['total_real_cost_operations'] / production_qty
+ extras['total_mo_cost'] = extras['total_mo_cost_components'] + extras['total_mo_cost_operations']
+ extras['total_real_cost'] = extras['total_real_cost_components'] + extras['total_real_cost_operations']
+ extras['total_mo_cost_decorator'] = self._get_comparison_decorator(extras['total_real_cost'], extras['total_mo_cost'], currency.rounding)
+ return extras
+
+ def _get_cost_breakdown_data(self, production, extras, remaining_cost_share):
+ if production.state != 'done' or not production.move_byproduct_ids:
+ return []
+
+ # Prepare byproducts data
+ quantities_by_product = defaultdict(float)
+ total_cost_by_product = defaultdict(float)
+ component_cost_by_product = defaultdict(float)
+ operation_cost_by_product = defaultdict(float)
+ for bp_move in production.move_byproduct_ids:
+ # Byproducts without cost share are irrelevant in a cost breakdrown.
+ if bp_move.state == 'cancel' or float_is_zero(bp_move.cost_share, precision_digits=2):
+ continue
+ # As UoMs can vary, we use the default UoM of each product
+ quantities_by_product[bp_move.product_id] += bp_move.product_qty
+ cost_share = bp_move.cost_share / 100
+ total_cost_by_product[bp_move.product_id] += extras['total_real_cost'] * cost_share
+ component_cost_by_product[bp_move.product_id] += extras['total_real_cost_components'] * cost_share
+ operation_cost_by_product[bp_move.product_id] += extras['total_real_cost_operations'] * cost_share
+
+ # Add finished product to its own default UoM (not the production UoM)
+ breakdown_lines = [self._format_cost_breakdown_lines(0, production.product_id.display_name, production.product_id.uom_id.display_name,
+ (extras['total_real_cost_components'] * remaining_cost_share) / production.product_uom_qty,
+ (extras['total_real_cost_operations'] * remaining_cost_share) / production.product_uom_qty,
+ (extras['total_real_cost'] * remaining_cost_share) / production.product_uom_qty)]
+ for index, product in enumerate(quantities_by_product.keys()):
+ breakdown_lines.append(self._format_cost_breakdown_lines(index + 1, product.display_name, product.uom_id.display_name,
+ component_cost_by_product[product] / quantities_by_product[product],
+ operation_cost_by_product[product] / quantities_by_product[product],
+ total_cost_by_product[product] / quantities_by_product[product]))
+ return breakdown_lines
+
+ def _format_cost_breakdown_lines(self, index, product_name, uom_name, component_cost, operation_cost, total_cost):
+ return {
+ 'index': f"BR{index}",
+ 'name': product_name,
+ 'unit_avg_cost_component': component_cost,
+ 'unit_avg_cost_operation': operation_cost,
+ 'unit_avg_total_cost': total_cost,
+ 'uom_name': uom_name,
+ }
def _get_mo_summary(self, production, components, current_mo_cost, current_real_cost, remaining_cost_share):
currency = (production.company_id or self.env.company).currency_id
@@ -117,6 +180,7 @@ class ReportMoOverview(models.AbstractModel):
'quantity_on_hand': product.uom_id._compute_quantity(product.qty_available, production.product_uom_id) if product.type == 'product' else False,
'quantity_reserved': 0.0,
'receipt': self._check_planned_start(production.date_deadline, self._get_replenishment_receipt(production, components)),
+ 'unit_cost': self._get_unit_cost(production.move_finished_ids.filtered(lambda m: m.product_id == production.product_id)),
'mo_cost': currency.round(mo_cost),
'mo_cost_decorator': self._get_comparison_decorator(real_cost, mo_cost, currency.rounding),
'real_cost': currency.round(real_cost),
@@ -124,6 +188,9 @@ class ReportMoOverview(models.AbstractModel):
'currency': currency,
}
+ def _get_unit_cost(self, move):
+ return move.product_id.uom_id._compute_price(move.product_id.standard_price, move.product_uom)
+
def _format_state(self, record):
if record._name != 'mrp.production' or record.state not in ('draft', 'confirmed') or not record.move_raw_ids:
return dict(record._fields['state']._description_selection(self.env)).get(record.state)
@@ -165,6 +232,8 @@ class ReportMoOverview(models.AbstractModel):
return 'success'
def _get_operations_data(self, production, level=0, current_index=False):
+ if production.state == "done":
+ return self._get_finished_operation_data(production, level, current_index)
currency = (production.company_id or self.env.company).currency_id
operation_uom = _("Minutes")
operations = []
@@ -202,6 +271,7 @@ class ReportMoOverview(models.AbstractModel):
'quantity_decorator': self._get_comparison_decorator(workorder.duration_expected, wo_duration, 0.01),
'uom_name': operation_uom,
'production_id': production.id,
+ 'unit_cost': expected_cost / workorder.duration_expected,
'mo_cost': mo_cost,
'real_cost': real_cost,
'currency_id': currency.id,
@@ -226,6 +296,46 @@ class ReportMoOverview(models.AbstractModel):
'details': operations,
}
+ def _get_finished_operation_data(self, production, level=0, current_index=False):
+ currency = (production.company_id or self.env.company).currency_id
+ done_operation_uom = _("Hours")
+ operations = []
+ total_duration = total_duration_expected = total_cost = 0
+ for index, workorder in enumerate(production.workorder_ids):
+ hourly_cost = workorder.costs_hour or workorder.workcenter_id.costs_hour
+ duration = workorder.get_duration() / 60
+ total_duration += duration
+ total_duration_expected += workorder.duration_expected
+ operation_cost = duration * hourly_cost
+ total_cost += operation_cost
+ operations.append({
+ 'level': level,
+ 'index': f"{current_index}W{index}",
+ 'name': f"{workorder.workcenter_id.display_name}: {workorder.display_name}",
+ 'quantity': duration,
+ 'uom_name': done_operation_uom,
+ 'uom_precision': 4,
+ 'unit_cost': hourly_cost,
+ 'mo_cost': currency.round(operation_cost),
+ 'real_cost': currency.round(operation_cost),
+ 'currency_id': currency.id,
+ 'currency': currency,
+ })
+ return {
+ 'summary': {
+ 'index': f"{current_index}W",
+ 'done': True,
+ 'quantity': total_duration,
+ 'quantity_decorator': self._get_comparison_decorator(total_duration_expected, total_duration, 0.01),
+ 'mo_cost': total_cost,
+ 'real_cost': total_cost,
+ 'uom_name': done_operation_uom,
+ 'currency_id': currency.id,
+ 'currency': currency,
+ },
+ 'details': operations,
+ }
+
def _get_byproducts_data(self, production, current_mo_cost, current_real_cost, level=0, current_index=False):
currency = (production.company_id or self.env.company).currency_id
byproducts = []
@@ -249,6 +359,7 @@ class ReportMoOverview(models.AbstractModel):
'quantity': move_bp.product_uom_qty,
'uom_name': move_bp.product_uom.display_name,
'uom_precision': self._get_uom_precision(move_bp.product_uom.rounding),
+ 'unit_cost': self._get_unit_cost(move_bp),
'mo_cost': currency.round(mo_cost),
'mo_cost_decorator': self._get_comparison_decorator(real_cost, mo_cost, currency.rounding),
'real_cost': currency.round(real_cost),
@@ -328,6 +439,7 @@ class ReportMoOverview(models.AbstractModel):
'quantity_on_hand': product.uom_id._compute_quantity(product.qty_available, move_raw.product_uom) if product.type == 'product' else False,
'quantity_reserved': self._get_reserved_qty(move_raw, production.warehouse_id, replenish_data),
'receipt': self._check_planned_start(production.date_start, self._get_component_receipt(product, move_raw, production.warehouse_id, replenishments, replenish_data)),
+ 'unit_cost': self._get_unit_cost(move_raw),
'mo_cost': currency.round(replenish_mo_cost + missing_quantity_cost),
'real_cost': currency.round(self._get_component_real_cost(move_raw, move_raw.product_uom_qty)),
'currency_id': currency.id,
@@ -346,8 +458,7 @@ class ReportMoOverview(models.AbstractModel):
def _get_component_real_cost(self, move_raw, quantity):
if float_is_zero(quantity, precision_rounding=move_raw.product_uom.rounding):
return 0
- product = move_raw.product_id
- return product.standard_price * move_raw.product_uom._compute_quantity(quantity, product.uom_id)
+ return self._get_unit_cost(move_raw) * quantity
def _check_planned_start(self, mo_planned_start, receipt):
if mo_planned_start and receipt.get('date', False) and receipt['date'] > mo_planned_start:
@@ -413,8 +524,9 @@ class ReportMoOverview(models.AbstractModel):
'quantity': line_quantity,
'uom_name': move_raw.product_uom.display_name,
'uom_precision': self._get_uom_precision(forecast_line['uom_id']['rounding']),
+ 'unit_cost': self._get_unit_cost(move_raw),
'mo_cost': forecast_line.get('cost', self._get_replenishment_mo_cost(product, line_quantity, move_raw.product_uom, currency, forecast_line.get('move_in'))),
- 'real_cost': currency.round(move_raw.product_uom._compute_quantity(line_quantity, product.uom_id) * product.standard_price),
+ 'real_cost': currency.round(self._get_component_real_cost(move_raw, line_quantity)),
'currency_id': currency.id,
'currency': currency,
}
@@ -474,6 +586,7 @@ class ReportMoOverview(models.AbstractModel):
else:
to_order_line['summary']['mo_cost'] = currency.round(product.standard_price * move_raw.product_uom._compute_quantity(missing_quantity, product.uom_id))
to_order_line['summary']['receipt'] = self._format_receipt_date('unavailable')
+ to_order_line['summary']['unit_cost'] = currency.round(to_order_line['summary']['mo_cost'] / missing_quantity)
to_order_line['summary']['mo_cost_decorator'] = self._get_comparison_decorator(to_order_line['summary']['real_cost'], to_order_line['summary']['mo_cost'], currency.rounding)
replenishments.append(to_order_line)
diff --git a/addons/mrp/report/mrp_report_mo_overview.xml b/addons/mrp/report/mrp_report_mo_overview.xml
index 28e8fdc8755..92155369a74 100644
--- a/addons/mrp/report/mrp_report_mo_overview.xml
+++ b/addons/mrp/report/mrp_report_mo_overview.xml
@@ -25,6 +25,7 @@
Free to use / On Hand |
Reserved |
Receipt |
+ Unit Cost |
MO Cost |
Real Cost |
@@ -46,7 +47,83 @@
|
+
+
+ | Total Cost of Components |
+
+
+ |
+
+
+ |
+
+
+ |
+ Cost of Components per unit
+ (in )
+ |
+
+
+ |
+
+
+ |
+
+
+ | Total Cost of Operations |
+
+
+ |
+
+
+ |
+
+
+ |
+ Cost of Operations per unit
+ (in )
+ |
+
+
+ |
+
+
+ |
+
+
+ | Total Cost of Production |
+
+
+ |
+
+
+ |
+
+
+
+ Cost Breakdown of Products
+
+
+
+ | Product |
+ Avg Cost of Components per Unit |
+ Avg Cost of Operations per Unit |
+ Avg Total Cost per Unit |
+ Unit of Measure |
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+
@@ -73,6 +150,9 @@
|
+
+
+ |
|
@@ -119,13 +199,14 @@
|
-
- |
-
+
+
+
|
|
|
|
+ |
|
@@ -154,6 +235,7 @@
|
|
|
+ |
|
diff --git a/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.js b/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.js
index a05f94fe37b..0184f0192dc 100644
--- a/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.js
+++ b/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.js
@@ -21,7 +21,6 @@ export class MoOverview extends Component {
this.state = useState({
data: {},
- isDone: false,
showOptions: this.getDefaultConfig(),
});
@@ -41,12 +40,12 @@ export class MoOverview extends Component {
[this.activeId],
);
this.state.data = reportValues.data;
- this.state.isDone = ['done', 'cancel'].some(doneState => reportValues.data?.summary?.state === doneState);
- if (this.state.isDone) {
+ if (this.isProductionDone) {
// Hide Availabilities / Receipts / Status columns when the MO is done.
this.state.showOptions.availabilities = false;
this.state.showOptions.receipts = false;
this.state.showOptions.replenishments = false;
+ this.state.showOptions.unitCosts = true;
}
this.state.showOptions.uom = reportValues.context.show_uom;
this.context = reportValues.context;
@@ -92,6 +91,7 @@ export class MoOverview extends Component {
replenishments: true,
availabilities: true,
receipts: true,
+ unitCosts: false,
moCosts: true,
realCosts: true,
};
@@ -127,6 +127,10 @@ export class MoOverview extends Component {
return this.state.showOptions.receipts;
}
+ get showUnitCosts() {
+ return this.state.showOptions.unitCosts;
+ }
+
get showMoCosts() {
return this.state.showOptions.moCosts;
}
@@ -139,12 +143,21 @@ export class MoOverview extends Component {
return this.state.data?.summary?.state === "done";
}
+ get hasOperations() {
+ return this.state.data?.operations?.details?.length > 0;
+ }
+
+ get hasBreakdown() {
+ return this.state.data?.cost_breakdown?.length > 0;
+ }
+
get totalColspan() {
let colspan = 2; // Name & Quantity
if (this.showReplenishments) colspan++;
if (this.showAvailabilities) colspan += 2; // Free to use / On Hand & Reserved
if (this.showUom) colspan++;
if (this.showReceipts) colspan++;
+ if (this.showUnitCosts) colspan++;
return colspan;
}
@@ -153,6 +166,7 @@ export class MoOverview extends Component {
+ `&replenishments=${+this.state.showOptions.replenishments}`
+ `&availabilities=${+this.state.showOptions.availabilities}`
+ `&receipts=${+this.state.showOptions.receipts}`
+ + `&unitCosts=${+this.state.showOptions.unitCosts}`
+ `&moCosts=${+this.state.showOptions.moCosts}`
+ `&realCosts=${+this.state.showOptions.realCosts}`
+ `&unfoldedIds=${JSON.stringify(Array.from(this.unfoldedIds))}`;
diff --git a/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.xml b/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.xml
index 64a29168660..366d1ad3580 100644
--- a/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.xml
+++ b/addons/mrp/static/src/components/mo_overview/mrp_mo_overview.xml
@@ -4,7 +4,7 @@
-
+
@@ -24,6 +24,7 @@
| Free to use / On Hand |
Reserved |
Receipt |
+ Unit Cost |
MO Cost |
Real Cost |
@@ -42,8 +43,65 @@
|
|
+
+
+ | Total Cost of Components |
+ |
+ |
+
+
+ |
+ Cost of Components per unit
+ (in )
+ |
+ |
+ |
+
+
+ | Total Cost of Operations |
+ |
+ |
+
+
+ |
+ Cost of Operations per unit
+ (in )
+ |
+ |
+ |
+
+
+ | Total Cost of Production |
+ |
+ |
+
+
+
+
+ Cost Breakdown of Products
+
+
+
+ | Product |
+ Avg Cost of Components per Unit |
+ Avg Cost of Operations per Unit |
+ Avg Total Cost per Unit |
+ Unit of Measure |
+
+
+
+
+ |
+ |
+ |
+ |
+ |
+
+
+
+
diff --git a/addons/mrp/static/src/components/mo_overview_byproducts_block/mrp_mo_overview_byproducts_block.xml b/addons/mrp/static/src/components/mo_overview_byproducts_block/mrp_mo_overview_byproducts_block.xml
index a1c52d59f5c..987730c60d1 100644
--- a/addons/mrp/static/src/components/mo_overview_byproducts_block/mrp_mo_overview_byproducts_block.xml
+++ b/addons/mrp/static/src/components/mo_overview_byproducts_block/mrp_mo_overview_byproducts_block.xml
@@ -17,6 +17,7 @@
|
|
|
+ |
|
|
diff --git a/addons/mrp/static/src/components/mo_overview_display_filter/mrp_mo_overview_display_filter.js b/addons/mrp/static/src/components/mo_overview_display_filter/mrp_mo_overview_display_filter.js
index a41d2fe9350..dff4e2e0527 100644
--- a/addons/mrp/static/src/components/mo_overview_display_filter/mrp_mo_overview_display_filter.js
+++ b/addons/mrp/static/src/components/mo_overview_display_filter/mrp_mo_overview_display_filter.js
@@ -10,6 +10,7 @@ export const SHOW_OPTIONS = {
replenishments: Boolean,
availabilities: Boolean,
receipts: Boolean,
+ unitCosts: Boolean,
moCosts: Boolean,
realCosts: Boolean,
},
@@ -26,6 +27,7 @@ export class MoOverviewDisplayFilter extends BomOverviewDisplayFilter {
}
this.displayOptions = {
...(this.displayOptions || {}),
+ unitCosts: _t("Unit Costs"),
moCosts: _t("MO Costs"),
realCosts: _t("Real Costs"),
};
diff --git a/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.js b/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.js
index 60756986a4c..942ac3fe96e 100644
--- a/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.js
+++ b/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.js
@@ -158,6 +158,7 @@ MoOverviewLine.props = {
},
optional: true,
},
+ unit_cost: { type: Number, optional: true },
mo_cost: Number,
mo_cost_decorator: { type: [String, Boolean], optional: true },
real_cost: Number,
diff --git a/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.xml b/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.xml
index 722db97f07b..8713e590a53 100644
--- a/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.xml
+++ b/addons/mrp/static/src/components/mo_overview_line/mrp_mo_overview_line.xml
@@ -35,6 +35,7 @@
+ |
|
|
diff --git a/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.js b/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.js
index 278200fb5e1..085497fbebc 100644
--- a/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.js
+++ b/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.js
@@ -2,7 +2,7 @@
import { Component, useState } from "@odoo/owl";
import { useBus } from "@web/core/utils/hooks";
-import { formatFloatTime, formatMonetary } from "@web/views/fields/formatters";
+import { formatFloat, formatFloatTime, formatMonetary } from "@web/views/fields/formatters";
import { MoOverviewLine } from "../mo_overview_line/mrp_mo_overview_line";
import { SHOW_OPTIONS } from "../mo_overview_display_filter/mrp_mo_overview_display_filter";
@@ -57,6 +57,13 @@ export class MoOverviewOperationsBlock extends Component {
get index() {
return this.props.summary.index;
}
+
+ get totalQuantity() {
+ // Float for Hours when displaying done productions, FloatTime for Minutes otherwise.
+ return this.props.summary?.done ?
+ formatFloat(this.props.summary.quantity, { digits: [false, this.props.operations[0].uom_precision || undefined] }) :
+ formatFloatTime(this.props.summary.quantity)
+ }
}
MoOverviewOperationsBlock.components = {
@@ -77,6 +84,7 @@ MoOverviewOperationsBlock.props = {
uom_name: { type: String, optional: true },
currency_id: { type: Number, optional: true },
currency: { type: String, optional: true },
+ done: { type: Boolean, optional: true },
},
},
showOptions: SHOW_OPTIONS,
diff --git a/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.xml b/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.xml
index 2ab7d406a90..cde612b3a0e 100644
--- a/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.xml
+++ b/addons/mrp/static/src/components/mo_overview_operations_block/mrp_mo_overview_operations_block.xml
@@ -12,11 +12,12 @@
|
- |
+ |
|
|
|
|
+ |
|
|
diff --git a/addons/mrp_account/__init__.py b/addons/mrp_account/__init__.py
index 274ebd30be4..77019e2f005 100644
--- a/addons/mrp_account/__init__.py
+++ b/addons/mrp_account/__init__.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from . import models
+from . import report
def _configure_journals(env):
diff --git a/addons/mrp_account/report/__init__.py b/addons/mrp_account/report/__init__.py
new file mode 100644
index 00000000000..37b544e72ce
--- /dev/null
+++ b/addons/mrp_account/report/__init__.py
@@ -0,0 +1,4 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from . import mrp_report_mo_overview
diff --git a/addons/mrp_account/report/mrp_report_mo_overview.py b/addons/mrp_account/report/mrp_report_mo_overview.py
new file mode 100644
index 00000000000..6008f8caf4f
--- /dev/null
+++ b/addons/mrp_account/report/mrp_report_mo_overview.py
@@ -0,0 +1,16 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import models
+from odoo.tools import float_is_zero
+
+class ReportMoOverview(models.AbstractModel):
+ _inherit = 'report.mrp.report_mo_overview'
+
+ def _get_unit_cost(self, move):
+ valuation_layers = move.sudo().stock_valuation_layer_ids
+ layers_quantity = sum(valuation_layers.mapped('quantity'))
+ if valuation_layers and not float_is_zero(layers_quantity, precision_rounding=valuation_layers.uom_id.rounding):
+ unit_price = sum(valuation_layers.mapped('value')) / layers_quantity
+ return move.product_id.uom_id._compute_price(unit_price, move.product_uom)
+ return super()._get_unit_cost(move)