[IMP] account: default taxes for multi-company product creation
Before: When a new product was created in a multi-company setting, if the company field is empty, the product is available for all companies. The default sale and purchase tax of the company was set on the product. The problem is that only the default taxes of the currently active company was set on the product, so viewing the product in other companies showed an empty field for the tax. Now: - When creating a new product with the company field empty, the default taxes of the other companies are set on the product as well. - Tax display_name now shows company name if in a multi-company environment and more than one company is selected to make it easier to know which tax belongs to which company. task-3375286 closes odoo/odoo#127196 Related: odoo/enterprise#45075 Signed-off-by: William André (wan) <wan@odoo.com>
This commit is contained in:
@@ -866,17 +866,13 @@ class AccountMoveLine(models.Model):
|
||||
company_domain = self.env['account.tax']._check_company_domain(self.move_id.company_id)
|
||||
if self.move_id.is_sale_document(include_receipts=True):
|
||||
# Out invoice.
|
||||
if self.product_id.taxes_id:
|
||||
tax_ids = self.product_id.taxes_id.filtered_domain(company_domain)
|
||||
else:
|
||||
tax_ids = self.account_id.tax_ids.filtered(lambda tax: tax.type_tax_use == 'sale')
|
||||
filtered_taxes_id = self.product_id.taxes_id.filtered_domain(company_domain)
|
||||
tax_ids = filtered_taxes_id or self.account_id.tax_ids.filtered(lambda tax: tax.type_tax_use == 'sale')
|
||||
|
||||
elif self.move_id.is_purchase_document(include_receipts=True):
|
||||
# In invoice.
|
||||
if self.product_id.supplier_taxes_id:
|
||||
tax_ids = self.product_id.supplier_taxes_id.filtered_domain(company_domain)
|
||||
else:
|
||||
tax_ids = self.account_id.tax_ids.filtered(lambda tax: tax.type_tax_use == 'purchase')
|
||||
filtered_supplier_taxes_id = self.product_id.supplier_taxes_id.filtered_domain(company_domain)
|
||||
tax_ids = filtered_supplier_taxes_id or self.account_id.tax_ids.filtered(lambda tax: tax.type_tax_use == 'purchase')
|
||||
|
||||
else:
|
||||
tax_ids = self.account_id.tax_ids
|
||||
|
||||
@@ -381,6 +381,8 @@ class AccountTax(models.Model):
|
||||
name += ' (%s)' % type_tax_use.get(record.type_tax_use)
|
||||
if record.tax_scope:
|
||||
name += ' (%s)' % tax_scope.get(record.tax_scope)
|
||||
if len(self.env.companies) > 1 and self.env.context.get('params', {}).get('model') == 'product.template':
|
||||
name += ' (%s)' % record.company_id.display_name
|
||||
record.display_name = name
|
||||
|
||||
@api.onchange('amount')
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo import api, fields, models, _
|
||||
from odoo import api, Command, fields, models, _
|
||||
from odoo.exceptions import ValidationError
|
||||
from odoo.osv import expression
|
||||
from odoo.tools import format_amount
|
||||
@@ -27,12 +27,12 @@ class ProductTemplate(models.Model):
|
||||
|
||||
taxes_id = fields.Many2many('account.tax', 'product_taxes_rel', 'prod_id', 'tax_id', help="Default taxes used when selling the product.", string='Customer Taxes',
|
||||
domain=[('type_tax_use', '=', 'sale')],
|
||||
default=lambda self: self.env.company.account_sale_tax_id or self.env.company.root_id.account_sale_tax_id,
|
||||
default=lambda self: self.env.companies.account_sale_tax_id or self.env.companies.root_id.account_sale_tax_id,
|
||||
)
|
||||
tax_string = fields.Char(compute='_compute_tax_string')
|
||||
supplier_taxes_id = fields.Many2many('account.tax', 'product_supplier_taxes_rel', 'prod_id', 'tax_id', string='Vendor Taxes', help='Default taxes used when buying the product.',
|
||||
domain=[('type_tax_use', '=', 'purchase')],
|
||||
default=lambda self: self.env.company.account_purchase_tax_id or self.env.company.root_id.account_purchase_tax_id,
|
||||
default=lambda self: self.env.companies.account_purchase_tax_id or self.env.companies.root_id.account_purchase_tax_id,
|
||||
)
|
||||
property_account_income_id = fields.Many2one('account.account', company_dependent=True,
|
||||
string="Income Account",
|
||||
@@ -118,6 +118,27 @@ class ProductTemplate(models.Model):
|
||||
"If you want to change its Unit of Measure, please archive this product and create a new one."
|
||||
))
|
||||
|
||||
@api.model_create_multi
|
||||
def create(self, vals_list):
|
||||
products = super().create(vals_list)
|
||||
# If no company was set for the product, the product will be available for all companies and therefore should
|
||||
# have the default taxes of the other companies as well. sudo() is used since we're going to need to fetch all
|
||||
# the other companies default taxes which the user may not have access to.
|
||||
other_companies = self.env['res.company'].sudo().search([('id', 'not in', self.env.companies.ids)])
|
||||
if not other_companies:
|
||||
return products
|
||||
|
||||
default_customer_tax_ids = other_companies.filtered('account_sale_tax_id').account_sale_tax_id.ids
|
||||
default_supplier_tax_ids = other_companies.filtered('account_purchase_tax_id').account_purchase_tax_id.ids
|
||||
|
||||
products_without_company = products.filtered(lambda p: not p.company_id).sudo()
|
||||
products_without_company.taxes_id = [Command.link(tax) for tax in default_customer_tax_ids]
|
||||
products_without_company.supplier_taxes_id = [Command.link(tax) for tax in default_supplier_tax_ids]
|
||||
|
||||
products_without_company.invalidate_recordset(['taxes_id', 'supplier_taxes_id'])
|
||||
products.invalidate_recordset(['taxes_id', 'supplier_taxes_id'])
|
||||
return products
|
||||
|
||||
|
||||
class ProductProduct(models.Model):
|
||||
_inherit = "product.product"
|
||||
|
||||
@@ -42,3 +42,4 @@ from . import test_multivat
|
||||
from . import test_account_partner
|
||||
from . import test_setup_wizard
|
||||
from . import test_structured_reference
|
||||
from . import test_product
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from odoo.addons.account.tests.common import AccountTestInvoicingCommon
|
||||
from odoo.tests import tagged
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestProduct(AccountTestInvoicingCommon):
|
||||
|
||||
def test_multi_company_product_tax(self):
|
||||
""" Ensure default taxes are set for all companies on products with no company set. """
|
||||
product_without_company = self.env['product.template'].with_context(allowed_company_ids=self.env.company.ids).create({
|
||||
'name': 'Product Without a Company',
|
||||
})
|
||||
product_with_company = self.env['product.template'].with_context(allowed_company_ids=self.env.company.ids).create({
|
||||
'name': 'Product With a Company',
|
||||
'company_id': self.company_data['company'].id,
|
||||
})
|
||||
companies = self.env['res.company'].sudo().search([])
|
||||
# Product should have all the default taxes of the other companies.
|
||||
self.assertRecordValues(product_without_company.sudo(), [{
|
||||
'taxes_id': companies.account_sale_tax_id.ids,
|
||||
'supplier_taxes_id': companies.account_purchase_tax_id.ids,
|
||||
}])
|
||||
# Product should have only the default tax of the company it belongs to.
|
||||
self.assertRecordValues(product_with_company.sudo(), [{
|
||||
'taxes_id': self.company_data['company'].account_sale_tax_id.ids,
|
||||
'supplier_taxes_id': self.company_data['company'].account_purchase_tax_id.ids,
|
||||
}])
|
||||
@@ -110,7 +110,16 @@ class Partner(models.Model):
|
||||
'move_type': 'out_invoice',
|
||||
'partner_id': partner.id,
|
||||
'invoice_line_ids': [
|
||||
(0, None, {'product_id': product.id, 'quantity': 1, 'price_unit': amount, 'tax_ids': [(6, 0, product.taxes_id.ids)]})
|
||||
(
|
||||
0,
|
||||
None,
|
||||
{
|
||||
'product_id': product.id,
|
||||
'quantity': 1,
|
||||
'price_unit': amount,
|
||||
'tax_ids': [(6, 0, product.taxes_id.filtered_domain(self.env['account.tax']._check_company_domain(self.env.company)).ids)]
|
||||
}
|
||||
)
|
||||
]
|
||||
})
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ class TestPoSCommon(ValuationReconciliationTestCommon):
|
||||
|
||||
def create_order_line(product, quantity, discount=0.0):
|
||||
price_unit = self.pricelist._get_product_price(product, quantity)
|
||||
tax_ids = fiscal_position.map_tax(product.taxes_id)
|
||||
tax_ids = fiscal_position.map_tax(product.taxes_id.filtered_domain(self.env['account.tax']._check_company_domain(self.env.company)))
|
||||
price_unit_after_discount = price_unit * (1 - discount / 100.0)
|
||||
tax_values = (
|
||||
tax_ids.compute_all(price_unit_after_discount, self.currency, quantity)
|
||||
|
||||
@@ -81,7 +81,7 @@ class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestPr
|
||||
)
|
||||
self.assertDictEqual(
|
||||
expense_profitability['costs'],
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': -280.0 - expense_foreign.untaxed_amount * 0.2, 'to_bill': 0.0},
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-280.0 - expense_foreign.untaxed_amount * 0.2), 'to_bill': 0.0},
|
||||
)
|
||||
|
||||
expense_sheet.action_sheet_move_create()
|
||||
@@ -109,7 +109,7 @@ class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestPr
|
||||
)
|
||||
self.assertDictEqual(
|
||||
expense_profitability['costs'],
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': -280.0 - expense_foreign.untaxed_amount * 0.2, 'to_bill': 0.0},
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-280.0 - expense_foreign.untaxed_amount * 0.2), 'to_bill': 0.0},
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
@@ -135,7 +135,7 @@ class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestPr
|
||||
)
|
||||
self.assertDictEqual(
|
||||
expense_profitability['costs'],
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': -280.0 - expense_foreign.untaxed_amount * 0.2, 'to_bill': 0.0},
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-280.0 - expense_foreign.untaxed_amount * 0.2), 'to_bill': 0.0},
|
||||
)
|
||||
|
||||
self.assertDictEqual(
|
||||
@@ -183,7 +183,7 @@ class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestPr
|
||||
)
|
||||
self.assertDictEqual(
|
||||
expense_profitability['costs'],
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': -280.0 - expense_foreign.untaxed_amount * 0.2, 'to_bill': 0.0},
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-280.0 - expense_foreign.untaxed_amount * 0.2), 'to_bill': 0.0},
|
||||
)
|
||||
|
||||
expense_sheet._do_refuse('Test Cancel Expense')
|
||||
@@ -194,7 +194,7 @@ class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestPr
|
||||
)
|
||||
self.assertDictEqual(
|
||||
expense_profitability.get('costs', {}),
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': -expense_foreign.untaxed_amount * 0.2, 'to_bill': 0.0},
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-expense_foreign.untaxed_amount * 0.2), 'to_bill': 0.0},
|
||||
)
|
||||
|
||||
invoice = self.env['sale.advance.payment.inv'].with_context({
|
||||
@@ -227,7 +227,7 @@ class TestProjectSaleExpenseProfitability(TestProjectProfitabilityCommon, TestPr
|
||||
)
|
||||
self.assertDictEqual(
|
||||
expense_profitability['costs'],
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': -expense_foreign.untaxed_amount * 0.2, 'to_bill': 0.0},
|
||||
{'id': 'expenses', 'sequence': expense_sequence, 'billed': expense.currency_id.round(-expense_foreign.untaxed_amount * 0.2), 'to_bill': 0.0},
|
||||
)
|
||||
|
||||
expense_sheet_foreign._do_refuse('Test Cancel Expense')
|
||||
|
||||
@@ -316,6 +316,7 @@ class TestValuationReconciliation(ValuationReconciliationTestCommon):
|
||||
'amount': 33.3333,
|
||||
'company_id': self.company_data['company'].id,
|
||||
'cash_basis_transition_account_id': cash_basis_transfer_account.id,
|
||||
'type_tax_use': 'purchase',
|
||||
'tax_exigibility': 'on_payment',
|
||||
'invoice_repartition_line_ids': [
|
||||
(0, 0, {
|
||||
|
||||
Reference in New Issue
Block a user