customer_cogs_expense_account/tests/test_account_validation.py
2025-11-25 21:43:35 +07:00

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"
)