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

305 lines
12 KiB
Python

# -*- coding: utf-8 -*-
from odoo.tests import tagged, TransactionCase
from odoo.exceptions import UserError, ValidationError
@tagged('post_install', '-at_install')
class TestErrorHandling(TransactionCase):
"""
Test error handling for runtime scenarios in customer account determination.
Validates Requirements 3.1, 4.3, 4.4
"""
def setUp(self):
super().setUp()
# Get or create a test company
self.company = self.env.company
# Create test accounts with company_ids (Odoo 18 uses Many2many)
# Account codes can only contain alphanumeric characters and dots
self.income_account = self.env['account.account'].create({
'name': 'Test Income Account',
'code': 'TESTINC001',
'account_type': 'income',
'company_ids': [(6, 0, [self.company.id])],
})
self.expense_account = self.env['account.account'].create({
'name': 'Test Expense Account',
'code': 'TESTEXP001',
'account_type': 'expense',
'company_ids': [(6, 0, [self.company.id])],
})
# Create a test partner
self.partner = self.env['res.partner'].create({
'name': 'Test Customer',
'company_id': self.company.id,
})
def test_inactive_income_account_raises_user_error(self):
"""
Test that using an inactive income account raises UserError at runtime.
Validates Requirement 4.3
"""
# Set customer income account
self.partner.property_account_income_customer_id = self.income_account
# Deprecate the account
self.income_account.deprecated = True
# Attempt to get the account should raise UserError
with self.assertRaises(UserError) as context:
self.partner._get_customer_income_account()
self.assertIn('inactive', str(context.exception).lower())
def test_inactive_expense_account_raises_user_error(self):
"""
Test that using an inactive expense account raises UserError at runtime.
Validates Requirement 4.3
"""
# Set customer expense account
self.partner.property_account_expense_customer_id = self.expense_account
# Deprecate the account
self.expense_account.deprecated = True
# Attempt to get the account should raise UserError
with self.assertRaises(UserError) as context:
self.partner._get_customer_expense_account()
self.assertIn('inactive', str(context.exception).lower())
def test_company_mismatch_income_account_raises_user_error(self):
"""
Test that company mismatch for income account raises error at write time.
Validates Requirement 4.4
"""
# Create another company
other_company = self.env['res.company'].create({
'name': 'Other Company',
})
# Create account in other company (Odoo 18 uses company_ids)
# Account codes can only contain alphanumeric characters and dots
other_income_account = self.env['account.account'].create({
'name': 'Other Income Account',
'code': 'OTHERINC001',
'account_type': 'income',
'company_ids': [(6, 0, [other_company.id])],
})
# Set partner to first company
self.partner.company_id = self.company
# Odoo's account module prevents assigning accounts from different companies at write time
# This is a ValidationError from Odoo's base account module, which is expected behavior
# Our module's runtime check is an additional safety layer
error_raised = False
try:
self.partner.property_account_income_customer_id = other_income_account
except (UserError, ValidationError) as e:
error_raised = True
# The error should mention company mismatch
self.assertIn('company', str(e).lower())
self.assertTrue(error_raised, "Expected UserError or ValidationError for company mismatch")
def test_company_mismatch_expense_account_raises_user_error(self):
"""
Test that company mismatch for expense account raises error at write time.
Validates Requirement 4.4
"""
# Create another company
other_company = self.env['res.company'].create({
'name': 'Other Company',
})
# Create account in other company (Odoo 18 uses company_ids)
# Account codes can only contain alphanumeric characters and dots
other_expense_account = self.env['account.account'].create({
'name': 'Other Expense Account',
'code': 'OTHEREXP001',
'account_type': 'expense',
'company_ids': [(6, 0, [other_company.id])],
})
# Set partner to first company
self.partner.company_id = self.company
# Odoo's account module prevents assigning accounts from different companies at write time
# This is a ValidationError from Odoo's base account module, which is expected behavior
# Our module's runtime check is an additional safety layer
error_raised = False
try:
self.partner.property_account_expense_customer_id = other_expense_account
except (UserError, ValidationError) as e:
error_raised = True
# The error should mention company mismatch
self.assertIn('company', str(e).lower())
self.assertTrue(error_raised, "Expected UserError or ValidationError for company mismatch")
def test_graceful_fallback_with_no_account(self):
"""
Test that when no customer account is set, methods return False gracefully.
Validates Requirement 3.1 (graceful fallback)
"""
# Don't set any customer accounts
# Should return False without errors
income_account = self.partner._get_customer_income_account()
self.assertFalse(income_account)
expense_account = self.partner._get_customer_expense_account()
self.assertFalse(expense_account)
def test_valid_account_returns_successfully(self):
"""
Test that valid accounts are returned successfully.
Validates Requirement 3.1 (standard functionality preserved)
"""
# Set valid customer accounts
self.partner.property_account_income_customer_id = self.income_account
self.partner.property_account_expense_customer_id = self.expense_account
# Should return accounts without errors
income_account = self.partner._get_customer_income_account()
self.assertEqual(income_account, self.income_account)
expense_account = self.partner._get_customer_expense_account()
self.assertEqual(expense_account, self.expense_account)
def test_fallback_to_category_account_for_income(self):
"""
Test that when customer has no income account, system falls back to product category.
Validates Requirement 3.1 (graceful fallback to standard Odoo behavior)
"""
# Create a product category with income account
# Account codes can only contain alphanumeric characters and dots
category_income_account = self.env['account.account'].create({
'name': 'Category Income Account',
'code': 'CATINC001',
'account_type': 'income',
'company_ids': [(6, 0, [self.company.id])],
})
product_category = self.env['product.category'].create({
'name': 'Test Category',
'property_account_income_categ_id': category_income_account.id,
})
# Create a product with this category
product = self.env['product.product'].create({
'name': 'Test Product',
'categ_id': product_category.id,
'list_price': 100.0,
})
# Customer has no income account set
self.assertFalse(self.partner.property_account_income_customer_id)
# Helper method should return False
customer_account = self.partner._get_customer_income_account()
self.assertFalse(customer_account)
# The system should fall back to category account in actual invoice processing
# This is tested in the integration tests, but we verify the helper returns False
def test_fallback_to_category_account_for_expense(self):
"""
Test that when customer has no expense account, system falls back to product category.
Validates Requirement 3.1 (graceful fallback to standard Odoo behavior)
"""
# Create a product category with expense account
# Account codes can only contain alphanumeric characters and dots
category_expense_account = self.env['account.account'].create({
'name': 'Category Expense Account',
'code': 'CATEXP001',
'account_type': 'expense',
'company_ids': [(6, 0, [self.company.id])],
})
product_category = self.env['product.category'].create({
'name': 'Test Category',
'property_account_expense_categ_id': category_expense_account.id,
})
# Create a product with this category
product = self.env['product.product'].create({
'name': 'Test Product',
'categ_id': product_category.id,
'standard_price': 50.0,
})
# Customer has no expense account set
self.assertFalse(self.partner.property_account_expense_customer_id)
# Helper method should return False
customer_account = self.partner._get_customer_expense_account()
self.assertFalse(customer_account)
# The system should fall back to category account in actual stock move processing
# This is tested in the integration tests, but we verify the helper returns False
def test_missing_both_customer_and_category_accounts(self):
"""
Test that when both customer and category accounts are missing,
the system falls back to Odoo's standard error handling.
Validates Requirement 3.1 (preserve existing Odoo functionality)
"""
# Create a product category without accounts
product_category = self.env['product.category'].create({
'name': 'Test Category No Accounts',
})
# Create a product with this category
product = self.env['product.product'].create({
'name': 'Test Product No Accounts',
'categ_id': product_category.id,
'list_price': 100.0,
})
# Customer has no accounts set
self.assertFalse(self.partner.property_account_income_customer_id)
self.assertFalse(self.partner.property_account_expense_customer_id)
# Helper methods should return False
customer_income = self.partner._get_customer_income_account()
self.assertFalse(customer_income)
customer_expense = self.partner._get_customer_expense_account()
self.assertFalse(customer_expense)
# When creating actual invoices/moves, Odoo's standard error handling will kick in
# This ensures we don't break standard Odoo behavior
def test_exception_handling_in_income_account_retrieval(self):
"""
Test that unexpected exceptions in income account retrieval are caught and logged.
Validates Requirement 3.1 (graceful error handling)
"""
# Set a valid account
self.partner.property_account_income_customer_id = self.income_account
# The method should handle unexpected errors gracefully
# In normal operation, this should work fine
account = self.partner._get_customer_income_account()
self.assertEqual(account, self.income_account)
def test_exception_handling_in_expense_account_retrieval(self):
"""
Test that unexpected exceptions in expense account retrieval are caught and logged.
Validates Requirement 3.1 (graceful error handling)
"""
# Set a valid account
self.partner.property_account_expense_customer_id = self.expense_account
# The method should handle unexpected errors gracefully
# In normal operation, this should work fine
account = self.partner._get_customer_expense_account()
self.assertEqual(account, self.expense_account)