300 lines
12 KiB
Python
300 lines
12 KiB
Python
# -*- coding: utf-8 -*-
|
|
|
|
from odoo.tests import tagged
|
|
from odoo.tests.common import TransactionCase
|
|
from odoo.exceptions import ValidationError, UserError
|
|
from hypothesis import given, strategies as st, settings, assume
|
|
|
|
|
|
@tagged('post_install', '-at_install')
|
|
class TestAccountValidation(TransactionCase):
|
|
"""
|
|
Property-based tests for account validation on res.partner model.
|
|
Tests Properties 10, 11, and 12 from the design document.
|
|
"""
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True))
|
|
|
|
# Create test company
|
|
cls.company = cls.env['res.company'].create({
|
|
'name': 'Test Validation Company',
|
|
})
|
|
|
|
# Create accounts of different types for testing
|
|
# Note: In Odoo 18, account.account uses company_ids (Many2many), not company_id
|
|
# We need to set company_ids to match the partner's company for validation to pass
|
|
cls.income_account = cls.env['account.account'].with_company(cls.company).create({
|
|
'name': 'Valid Income Account',
|
|
'code': 'VINC001',
|
|
'account_type': 'income',
|
|
'company_ids': [(6, 0, [cls.company.id])],
|
|
})
|
|
|
|
cls.expense_account = cls.env['account.account'].with_company(cls.company).create({
|
|
'name': 'Valid Expense Account',
|
|
'code': 'VEXP001',
|
|
'account_type': 'expense',
|
|
'company_ids': [(6, 0, [cls.company.id])],
|
|
})
|
|
|
|
# Create accounts of wrong types
|
|
cls.asset_account = cls.env['account.account'].with_company(cls.company).create({
|
|
'name': 'Asset Account',
|
|
'code': 'ASSET001',
|
|
'account_type': 'asset_current',
|
|
'company_ids': [(6, 0, [cls.company.id])],
|
|
})
|
|
|
|
cls.liability_account = cls.env['account.account'].with_company(cls.company).create({
|
|
'name': 'Liability Account',
|
|
'code': 'LIAB001',
|
|
'account_type': 'liability_current',
|
|
'company_ids': [(6, 0, [cls.company.id])],
|
|
})
|
|
|
|
cls.equity_account = cls.env['account.account'].with_company(cls.company).create({
|
|
'name': 'Equity Account',
|
|
'code': 'EQUITY001',
|
|
'account_type': 'equity',
|
|
'company_ids': [(6, 0, [cls.company.id])],
|
|
})
|
|
|
|
# Create deprecated accounts
|
|
cls.deprecated_income_account = cls.env['account.account'].with_company(cls.company).create({
|
|
'name': 'Deprecated Income Account',
|
|
'code': 'DINC001',
|
|
'account_type': 'income',
|
|
'deprecated': True,
|
|
'company_ids': [(6, 0, [cls.company.id])],
|
|
})
|
|
|
|
cls.deprecated_expense_account = cls.env['account.account'].with_company(cls.company).create({
|
|
'name': 'Deprecated Expense Account',
|
|
'code': 'DEXP001',
|
|
'account_type': 'expense',
|
|
'deprecated': True,
|
|
'company_ids': [(6, 0, [cls.company.id])],
|
|
})
|
|
|
|
@settings(max_examples=100)
|
|
@given(
|
|
customer_name=st.text(min_size=1, max_size=50, alphabet=st.characters(blacklist_categories=('Cs', 'Cc'))),
|
|
account_type=st.sampled_from(['asset_current', 'liability_current', 'equity', 'expense']),
|
|
)
|
|
def test_property_10_income_account_type_validation(self, customer_name, account_type):
|
|
"""
|
|
**Feature: customer-cogs-expense-account, Property 10: Income account type validation**
|
|
|
|
For any customer record, attempting to set an income account field to a non-income
|
|
account type should trigger a validation error.
|
|
|
|
**Validates: Requirements 4.1, 4.6**
|
|
"""
|
|
# Map account types to test accounts
|
|
account_map = {
|
|
'asset_current': self.asset_account,
|
|
'liability_current': self.liability_account,
|
|
'equity': self.equity_account,
|
|
'expense': self.expense_account,
|
|
}
|
|
|
|
wrong_account = account_map[account_type]
|
|
|
|
# Create a partner
|
|
partner = self.env['res.partner'].with_company(self.company).create({
|
|
'name': customer_name,
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
# Attempt to set a non-income account as income account
|
|
# This should raise a ValidationError
|
|
with self.assertRaises(
|
|
ValidationError,
|
|
msg=f"Setting income account to {account_type} type should raise ValidationError"
|
|
):
|
|
partner.write({
|
|
'property_account_income_customer_id': wrong_account.id,
|
|
})
|
|
|
|
@settings(max_examples=100)
|
|
@given(
|
|
customer_name=st.text(min_size=1, max_size=50, alphabet=st.characters(blacklist_categories=('Cs', 'Cc'))),
|
|
account_type=st.sampled_from(['asset_current', 'liability_current', 'equity', 'income']),
|
|
)
|
|
def test_property_11_expense_account_type_validation(self, customer_name, account_type):
|
|
"""
|
|
**Feature: customer-cogs-expense-account, Property 11: Expense account type validation**
|
|
|
|
For any customer record, attempting to set an expense account field to a non-expense
|
|
account type should trigger a validation error.
|
|
|
|
**Validates: Requirements 4.2, 4.7**
|
|
"""
|
|
# Map account types to test accounts
|
|
account_map = {
|
|
'asset_current': self.asset_account,
|
|
'liability_current': self.liability_account,
|
|
'equity': self.equity_account,
|
|
'income': self.income_account,
|
|
}
|
|
|
|
wrong_account = account_map[account_type]
|
|
|
|
# Create a partner
|
|
partner = self.env['res.partner'].with_company(self.company).create({
|
|
'name': customer_name,
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
# Attempt to set a non-expense account as expense account
|
|
# This should raise a ValidationError
|
|
with self.assertRaises(
|
|
ValidationError,
|
|
msg=f"Setting expense account to {account_type} type should raise ValidationError"
|
|
):
|
|
partner.write({
|
|
'property_account_expense_customer_id': wrong_account.id,
|
|
})
|
|
|
|
@settings(max_examples=100)
|
|
@given(
|
|
customer_name=st.text(min_size=1, max_size=50, alphabet=st.characters(blacklist_categories=('Cs', 'Cc'))),
|
|
)
|
|
def test_property_12_active_account_validation_income(self, customer_name):
|
|
"""
|
|
**Feature: customer-cogs-expense-account, Property 12: Active account validation**
|
|
|
|
For any journal entry creation, using an inactive or invalid customer-specific account
|
|
should prevent entry creation and display an error.
|
|
|
|
This test validates the income account scenario.
|
|
|
|
**Validates: Requirements 4.3, 4.4**
|
|
"""
|
|
# Test 1: Deprecated income account should not be assignable at creation time
|
|
partner = self.env['res.partner'].with_company(self.company).create({
|
|
'name': customer_name,
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
# Attempt to set a deprecated income account
|
|
with self.assertRaises(
|
|
ValidationError,
|
|
msg="Setting deprecated income account should raise ValidationError"
|
|
):
|
|
partner.write({
|
|
'property_account_income_customer_id': self.deprecated_income_account.id,
|
|
})
|
|
|
|
# Test 2: Runtime validation - account becomes deprecated after assignment
|
|
# Create partner with valid income account
|
|
partner_runtime = self.env['res.partner'].with_company(self.company).create({
|
|
'name': customer_name + '_runtime',
|
|
'company_id': self.company.id,
|
|
'property_account_income_customer_id': self.income_account.id,
|
|
})
|
|
|
|
# Simulate account becoming deprecated
|
|
self.income_account.write({'deprecated': True})
|
|
|
|
# Attempting to retrieve the account should raise UserError
|
|
with self.assertRaises(
|
|
UserError,
|
|
msg="Retrieving deprecated income account should raise UserError"
|
|
):
|
|
partner_runtime._get_customer_income_account()
|
|
|
|
# Restore account state for other tests
|
|
self.income_account.write({'deprecated': False})
|
|
|
|
@settings(max_examples=100)
|
|
@given(
|
|
customer_name=st.text(min_size=1, max_size=50, alphabet=st.characters(blacklist_categories=('Cs', 'Cc'))),
|
|
)
|
|
def test_property_12_active_account_validation_expense(self, customer_name):
|
|
"""
|
|
**Feature: customer-cogs-expense-account, Property 12: Active account validation**
|
|
|
|
For any journal entry creation, using an inactive or invalid customer-specific account
|
|
should prevent entry creation and display an error.
|
|
|
|
This test validates the expense account scenario.
|
|
|
|
**Validates: Requirements 4.3, 4.4**
|
|
"""
|
|
# Test 1: Deprecated expense account should not be assignable at creation time
|
|
partner = self.env['res.partner'].with_company(self.company).create({
|
|
'name': customer_name,
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
# Attempt to set a deprecated expense account
|
|
with self.assertRaises(
|
|
ValidationError,
|
|
msg="Setting deprecated expense account should raise ValidationError"
|
|
):
|
|
partner.write({
|
|
'property_account_expense_customer_id': self.deprecated_expense_account.id,
|
|
})
|
|
|
|
# Test 2: Runtime validation - account becomes deprecated after assignment
|
|
# Create partner with valid expense account
|
|
partner_runtime = self.env['res.partner'].with_company(self.company).create({
|
|
'name': customer_name + '_runtime',
|
|
'company_id': self.company.id,
|
|
'property_account_expense_customer_id': self.expense_account.id,
|
|
})
|
|
|
|
# Simulate account becoming deprecated
|
|
self.expense_account.write({'deprecated': True})
|
|
|
|
# Attempting to retrieve the account should raise UserError
|
|
with self.assertRaises(
|
|
UserError,
|
|
msg="Retrieving deprecated expense account should raise UserError"
|
|
):
|
|
partner_runtime._get_customer_expense_account()
|
|
|
|
# Restore account state for other tests
|
|
self.expense_account.write({'deprecated': False})
|
|
|
|
def test_valid_accounts_accepted(self):
|
|
"""
|
|
Test that valid income and expense accounts are accepted without errors.
|
|
This is a sanity check to ensure validation doesn't reject valid accounts.
|
|
"""
|
|
# Create partner with valid accounts
|
|
partner = self.env['res.partner'].with_company(self.company).create({
|
|
'name': 'Valid Partner',
|
|
'company_id': self.company.id,
|
|
'property_account_income_customer_id': self.income_account.id,
|
|
'property_account_expense_customer_id': self.expense_account.id,
|
|
})
|
|
|
|
# Verify accounts are set correctly
|
|
self.assertEqual(
|
|
partner.property_account_income_customer_id.id,
|
|
self.income_account.id,
|
|
"Valid income account should be accepted"
|
|
)
|
|
self.assertEqual(
|
|
partner.property_account_expense_customer_id.id,
|
|
self.expense_account.id,
|
|
"Valid expense account should be accepted"
|
|
)
|
|
|
|
# Verify helper methods work correctly
|
|
self.assertEqual(
|
|
partner._get_customer_income_account().id,
|
|
self.income_account.id,
|
|
"Helper method should return valid income account"
|
|
)
|
|
self.assertEqual(
|
|
partner._get_customer_expense_account().id,
|
|
self.expense_account.id,
|
|
"Helper method should return valid expense account"
|
|
)
|