[IMP] {stock_}account: margin and inventory measures in invoice analysis
In order to better analyze profitability, we added an extra measure to the Invoice Analysis report to show the "Margin" on every invoice line based on the product cost price. In order to have a simplified inventory valuation without fully using the Inventory app, we also added an "Inventory Value" measure that also uses the product cost price to show the change in inventory value based on incoming and outgoing accounting documents. An extra filter "Inventory Valuation" was added as well to show the "Inventory Value" values per storable product and per month. task-3708415 closes odoo/odoo#151805 Signed-off-by: Cedric Snauwaert <csn@odoo.com>
This commit is contained in:
@@ -47,6 +47,8 @@ class AccountInvoiceReport(models.Model):
|
||||
price_subtotal = fields.Float(string='Untaxed Total', readonly=True)
|
||||
price_total = fields.Float(string='Total in Currency', readonly=True)
|
||||
price_average = fields.Float(string='Average Price', readonly=True, group_operator="avg")
|
||||
price_margin = fields.Float(string='Margin', readonly=True)
|
||||
inventory_value = fields.Float(string='Inventory Value', readonly=True)
|
||||
currency_id = fields.Many2one('res.currency', string='Currency', readonly=True)
|
||||
|
||||
_depends = {
|
||||
@@ -59,7 +61,7 @@ class AccountInvoiceReport(models.Model):
|
||||
'move_id', 'product_id', 'product_uom_id', 'account_id',
|
||||
'journal_id', 'company_id', 'currency_id', 'partner_id',
|
||||
],
|
||||
'product.product': ['product_tmpl_id'],
|
||||
'product.product': ['product_tmpl_id', 'standard_price'],
|
||||
'product.template': ['categ_id'],
|
||||
'uom.uom': ['category_id', 'factor', 'name', 'uom_type'],
|
||||
'res.currency.rate': ['currency_id', 'name'],
|
||||
@@ -104,6 +106,13 @@ class AccountInvoiceReport(models.Model):
|
||||
-- convert to template uom
|
||||
* (NULLIF(COALESCE(uom_line.factor, 1), 0.0) / NULLIF(COALESCE(uom_template.factor, 1), 0.0)),
|
||||
0.0) * currency_table.rate AS price_average,
|
||||
CASE
|
||||
WHEN move.move_type NOT IN ('out_invoice', 'out_receipt') THEN 0.0
|
||||
ELSE -line.balance * currency_table.rate - (line.quantity / NULLIF(COALESCE(uom_line.factor, 1) / COALESCE(uom_template.factor, 1), 0.0)) * product_standard_price.value_float
|
||||
END
|
||||
AS price_margin,
|
||||
line.quantity / NULLIF(COALESCE(uom_line.factor, 1) / COALESCE(uom_template.factor, 1), 0.0) * (CASE WHEN move.move_type IN ('out_invoice','in_refund','out_receipt') THEN -1 ELSE 1 END)
|
||||
* product_standard_price.value_float AS inventory_value,
|
||||
COALESCE(partner.country_id, commercial_partner.country_id) AS country_id,
|
||||
line.currency_id AS currency_id
|
||||
'''
|
||||
@@ -120,6 +129,9 @@ class AccountInvoiceReport(models.Model):
|
||||
LEFT JOIN uom_uom uom_template ON uom_template.id = template.uom_id
|
||||
INNER JOIN account_move move ON move.id = line.move_id
|
||||
LEFT JOIN res_partner commercial_partner ON commercial_partner.id = move.commercial_partner_id
|
||||
LEFT JOIN ir_property product_standard_price
|
||||
ON product_standard_price.res_id = CONCAT('product.product,', product.id)
|
||||
AND product_standard_price.name = 'standard_price'
|
||||
JOIN {currency_table} ON currency_table.company_id = line.company_id
|
||||
'''.format(
|
||||
currency_table=self.env['res.currency']._get_query_currency_table(self.env.companies.ids, fields.Date.today())
|
||||
|
||||
@@ -43,6 +43,8 @@
|
||||
<field name="quantity" optional="hide" sum="Total"/>
|
||||
<field name="price_subtotal" optional="show" sum="Total"/>
|
||||
<field name="price_total" optional="show" sum="Total"/>
|
||||
<field name="price_margin" optional="hide"/>
|
||||
<field name="inventory_value" optional="hide" sum="Total"/>
|
||||
<field name="state" optional="hide"/>
|
||||
<field name="payment_state" optional="hide"/>
|
||||
<field name="move_type" optional="hide"/>
|
||||
|
||||
@@ -102,18 +102,21 @@ class TestAccountInvoiceReport(AccountTestInvoicingCommon):
|
||||
'price_average': vals[0],
|
||||
'price_subtotal': vals[1],
|
||||
'quantity': vals[2],
|
||||
'price_margin': vals[3],
|
||||
'inventory_value': vals[4],
|
||||
} for vals in expected_values_list]
|
||||
|
||||
self.assertRecordValues(reports, expected_values_dict)
|
||||
|
||||
def test_invoice_report_multiple_types(self):
|
||||
self.assertInvoiceReportValues([
|
||||
#price_average price_subtotal quantity
|
||||
[2000, 2000, 1],
|
||||
[1000, 1000, 1],
|
||||
[250, 750, 3],
|
||||
[6, 6, 1],
|
||||
[20, -20, -1],
|
||||
[20, -20, -1],
|
||||
[600, -600, -1],
|
||||
# pylint: disable=bad-whitespace
|
||||
# price_average, price_subtotal, quantity, price_margin, inventory_value
|
||||
[ 2000, 2000, 1, 1200, -800], # price_unit = 6000, currency.rate = 3.0
|
||||
[ 1000, 1000, 1, 200, -800], # price_unit = 3000, currency.rate = 3.0
|
||||
[ 250, 750, 3, -1650, -2400], # price_unit = 750, currency.rate = 2.0
|
||||
[ 6, 6, 1, 0, -800], # price_unit = 12, currency.rate = 2.0
|
||||
[ 20, -20, -1, 0, 800], # price_unit = 60, currency.rate = 3.0
|
||||
[ 20, -20, -1, 0, 800], # price_unit = 60, currency.rate = 3.0
|
||||
[ 600, -600, -1, 0, 800], # price_unit = 1200, currency.rate = 2.0
|
||||
])
|
||||
|
||||
@@ -36,6 +36,7 @@ Dashboard / Reports for Warehouse Management includes:
|
||||
'wizard/stock_request_count.xml',
|
||||
'wizard/stock_valuation_layer_revaluation_views.xml',
|
||||
'wizard/stock_quantity_history.xml',
|
||||
'report/account_invoice_report_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
'auto_install': True,
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="filter_invoice_inventory_valuation" model="ir.filters">
|
||||
<field name="name">Inventory Valuation</field>
|
||||
<field name="model_id">account.invoice.report</field>
|
||||
<field name="domain">[('product_id.type', '=', 'product')]</field>
|
||||
<field name="user_id" eval="False"/>
|
||||
<field name="context">{'group_by': ['product_id'], 'pivot_column_groupby': ['invoice_date:month'], 'pivot_measures': ['inventory_value'], 'graph_measure': 'inventory_value'}</field>
|
||||
</record>
|
||||
</odoo>
|
||||
Reference in New Issue
Block a user