[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:
Dylan Kiss (dyki)
2024-02-02 10:50:52 +00:00
parent 01532c2216
commit 40745b1de9
5 changed files with 37 additions and 9 deletions
@@ -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
])
+1
View File
@@ -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>