[FIX] sale[_expense]: delivered quantity amplification

Activate analytic accounting
Configure a [TEST] product to create a project on order
Create a SO with [TEST]
Have an expense product [EXP] with reinvoice policy set
Create an expense with [EXP] and set as analytic account the one created
for the SO and another one
Complete the expense flow
Check the SO

Issue: the expense line was added correctly but the quantity delivered
is set to 2

opw-3410712

closes odoo/odoo#135479

X-original-commit: 39f573eebd7af7f026ffdcb4957a47a0f699939f
Signed-off-by: Andrea Grazioso (agr) <agr@odoo.com>
This commit is contained in:
Andrea Grazioso (agr-odoo)
2023-09-14 17:07:59 +00:00
parent fac931cb0c
commit e6b353d6df
2 changed files with 64 additions and 3 deletions
+6 -3
View File
@@ -728,7 +728,7 @@ class SaleOrderLine(models.Model):
domain = expression.AND([[('so_line', 'in', self.ids)], additional_domain])
data = self.env['account.analytic.line'].read_group(
domain,
['so_line', 'unit_amount', 'product_uom_id'], ['product_uom_id', 'so_line'], lazy=False
['so_line', 'unit_amount', 'product_uom_id', 'move_line_id:count_distinct'], ['product_uom_id', 'so_line'], lazy=False
)
# convert uom and sum all unit_amount of analytic lines to get the delivered qty of SO lines
@@ -744,10 +744,13 @@ class SaleOrderLine(models.Model):
so_line = lines_map[so_line_id]
result.setdefault(so_line_id, 0.0)
uom = product_uom_map.get(item['product_uom_id'][0])
if so_line.product_uom.category_id == uom.category_id:
qty = uom._compute_quantity(item['unit_amount'], so_line.product_uom, rounding_method='HALF-UP')
# avoid counting unit_amount twice when dealing with multiple analytic lines on the same move line
if item['move_line_id'] == 1 and item['__count'] > 1:
qty = item['unit_amount'] / item['__count']
else:
qty = item['unit_amount']
if so_line.product_uom.category_id == uom.category_id:
qty = uom._compute_quantity(qty, so_line.product_uom, rounding_method='HALF-UP')
result[so_line_id] += qty
return result
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import Command
from odoo.addons.hr_expense.tests.common import TestExpenseCommon
from odoo.addons.sale.tests.common import TestSaleCommon
from odoo.tests import tagged
@@ -115,3 +116,60 @@ class TestReInvoice(TestExpenseCommon, TestSaleCommon):
{'qty_delivered_method': 'analytic'},
{'qty_delivered_method': 'analytic'},
])
def test_expenses_reinvoice_analytic_distribution(self):
"""Test expense line with multiple analytic accounts is reinvoiced correctly"""
(self.company_data['product_order_sales_price'] + self.company_data['product_delivery_sales_price']).write({
'can_be_expensed': True,
})
# create SO line and confirm SO (with only one line)
sale_order = self.env['sale.order'].with_context(mail_notrack=True, mail_create_nolog=True).create({
'partner_id': self.partner_a.id,
'partner_invoice_id': self.partner_a.id,
'partner_shipping_id': self.partner_a.id,
'order_line': [Command.create({
'name': self.company_data['product_order_sales_price'].name,
'product_id': self.company_data['product_order_sales_price'].id,
'product_uom_qty': 2.0,
'price_unit': 1000.0,
})],
})
sale_order.action_confirm()
expense_sheet = self.env['hr.expense.sheet'].create({
'name': 'First Expense for employee',
'employee_id': self.expense_employee.id,
'journal_id': self.company_data['default_journal_purchase'].id,
'accounting_date': '2017-01-01',
'expense_line_ids': [
Command.create({
'name': 'expense_1',
'date': '2016-01-01',
'product_id': self.company_data['product_order_sales_price'].id,
'quantity': 2,
'analytic_distribution': {self.analytic_account_1.id: 50, self.analytic_account_2.id: 50},
'employee_id': self.expense_employee.id,
'sale_order_id': sale_order.id,
}),
],
})
expense_sheet.action_approve_expense_sheets()
expense_sheet.action_sheet_move_create()
self.assertRecordValues(sale_order.order_line, [
# Original SO line:
{
'qty_delivered': 0.0,
'product_uom_qty': 2.0,
'is_expense': False,
},
# Expense lines:
{
'qty_delivered': 2.0,
'product_uom_qty': 2.0,
'is_expense': True,
},
])