From 4b2286bd5b06aec1147f327c781d5aecd825fe86 Mon Sep 17 00:00:00 2001 From: Ethan Vincent Date: Tue, 30 Jan 2024 12:59:32 +0100 Subject: [PATCH] [FIX] sale{,_expense}: bad conversion distr. keys **Current behavior:** Trying to post journal entries for an expense report sheet contianing an expense that has: A) an associated analytic distribution with both a Project and Department, and B) a customer to be re-invoiced (requires the expense to be for a product which permits this operation), attempting to post the journal entries for the approved expense sheet will raise a ValueError. --- **Expected behavior:** The entries get posted to their respective accounting journals. --- Steps to reproduce: 0. Install the Project, Accounting, Sales, Expense apps and enable analytic accounting in the db settings 1. Create a new expense with: A) an analytic distribution which has both available fields filled out, and B) a category that permits a customer to be re-invoiced (then enter a customer) 2. Create the report, submit it to the manager, approve it, then attempt to post the journal entries 3. This should cause the traceback, if the process was blocked try again after deleting any tax rules in the expense --- Cause of the issue: The analytic_distribution field in the hr.expense module is stored as a dictionary. During the process of recording the journal entries, the keys are converted from a string to integers. However, when multiple keys are in the dict, the conversion is set up improperly and fails. --- Fix: Modify the conversion sequence so that multiple dict keys are accounted for. --- opw-3663082 closes odoo/odoo#147985 Signed-off-by: John Laterre (jol) --- addons/sale/models/account_move_line.py | 7 ++- .../sale_expense/tests/test_sale_expense.py | 54 ++++++++++++++++++- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/addons/sale/models/account_move_line.py b/addons/sale/models/account_move_line.py index 3c45dc1ffbc..3020463ee2d 100644 --- a/addons/sale/models/account_move_line.py +++ b/addons/sale/models/account_move_line.py @@ -155,12 +155,15 @@ class AccountMoveLine(models.Model): for move_line in self: if move_line.analytic_distribution: distribution_json = move_line.analytic_distribution - sale_order = self.env['sale.order'].search([('analytic_account_id', 'in', list(int(account_id) for account_id in distribution_json.keys())), + account_ids = [int(account_id) for key in distribution_json.keys() for account_id in key.split(',')] + + sale_order = self.env['sale.order'].search([('analytic_account_id', 'in', account_ids), ('state', '=', 'sale')], order='create_date ASC', limit=1) if sale_order: mapping[move_line.id] = sale_order else: - sale_order = self.env['sale.order'].search([('analytic_account_id', 'in', list(int(account_id) for account_id in distribution_json.keys()))], order='create_date ASC', limit=1) + sale_order = self.env['sale.order'].search([('analytic_account_id', 'in', account_ids)], + order='create_date ASC', limit=1) mapping[move_line.id] = sale_order # map of AAL index with the SO on which it needs to be reinvoiced. Maybe be None if no SO found diff --git a/addons/sale_expense/tests/test_sale_expense.py b/addons/sale_expense/tests/test_sale_expense.py index 084ba03f1c1..4081e7baef6 100644 --- a/addons/sale_expense/tests/test_sale_expense.py +++ b/addons/sale_expense/tests/test_sale_expense.py @@ -1,5 +1,5 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import Command +from odoo import Command, fields from odoo.addons.hr_expense.tests.common import TestExpenseCommon from odoo.addons.sale.tests.common import TestSaleCommon from odoo.tests import tagged @@ -97,3 +97,55 @@ class TestSaleExpense(TestExpenseCommon, TestSaleCommon): # both expenses should be invoiced inv = so._create_invoices() self.assertEqual(inv.amount_untaxed, 621.5 + (prod_exp_2.list_price * 100.0), 'Sale Expense: invoicing of expense is wrong') + + def test_expense_multi_id_analytic_distribution(self): + """ + Test conversion of analytic_distribution dict into account numbers when a hr.expense.sheet containing + a hr.expense with an analytic_distribution having 2+ account ids + """ + expensed_product = self.env['product.product'].create({ + 'name': 'test product', + 'can_be_expensed': True, + 'detailed_type': 'service', + 'invoice_policy': 'order', + 'standard_price': 100, + 'expense_policy': 'cost', + }) + + sale_order = self.env['sale.order'].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({'product_id': self.product_b.id})], + }) + sale_order.action_confirm() + sale_order._create_invoices() + + analytic_account_3 = self.env['account.analytic.account'].create({ + 'name': 'analytic_account_3', + 'plan_id': self.analytic_plan.id, + }) + + expense_sheet = self.env['hr.expense.sheet'].create({ + 'name': 'Test Expense Report', + 'employee_id': self.expense_employee.id, + 'company_id': self.company_data['company'].id, + 'expense_line_ids': [Command.create({ + 'employee_id': self.expense_employee.id, + 'product_id': expensed_product.id, + 'quantity': 1000.00, + 'date': fields.Date.today(), + 'company_id': self.company_data['company'].id, + 'currency_id': self.company_data['currency'].id, + 'analytic_distribution': { + f'{self.analytic_account_1.id},{self.analytic_account_2.id}': 60, + f'{analytic_account_3.id}': 40, + }, + 'sale_order_id': sale_order.id, + })], + }) + expense_sheet.action_submit_sheet() + expense_sheet.action_approve_expense_sheets() + expense_sheet.action_sheet_move_create() + + self.assertTrue(self.env['account.move'].search([('expense_sheet_id', '=', expense_sheet.id)], limit=1))