# -*- coding: utf-8 -*- from odoo.tests import tagged from odoo.tests.common import TransactionCase from hypothesis import given, strategies as st, settings @tagged('post_install', '-at_install') class TestCustomerAccountFields(TransactionCase): """ Property-based tests for customer account fields on res.partner model. Tests Properties 1, 2, and 3 from the design document. """ @classmethod def setUpClass(cls): super().setUpClass() cls.env = cls.env(context=dict(cls.env.context, tracking_disable=True)) # Use the default company cls.company = cls.env.company # Create income accounts for testing cls.income_account_1 = cls.env['account.account'].create({ 'name': 'Test Income Account 1', 'code': 'TINC001', 'account_type': 'income', }) cls.income_account_2 = cls.env['account.account'].create({ 'name': 'Test Income Account 2', 'code': 'TINC002', 'account_type': 'income', }) # Create expense accounts for testing cls.expense_account_1 = cls.env['account.account'].create({ 'name': 'Test Expense Account 1', 'code': 'TEXP001', 'account_type': 'expense', }) cls.expense_account_2 = cls.env['account.account'].create({ 'name': 'Test Expense Account 2', 'code': 'TEXP002', 'account_type': 'expense', }) @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_1_customer_account_field_visibility(self, customer_name): """ **Feature: customer-cogs-expense-account, Property 1: Customer account field visibility** For any customer form view in the Accounting page, the income account and expense account fields should be visible in the Accounting Entries section below the Account Payable field. **Validates: Requirements 1.1, 1.2** """ # Create a customer partner = self.env['res.partner'].with_company(self.company).create({ 'name': customer_name, 'company_id': self.company.id, }) # Verify that the fields exist on the model self.assertIn( 'property_account_income_customer_id', partner._fields, "Income account field should exist on res.partner model" ) self.assertIn( 'property_account_expense_customer_id', partner._fields, "Expense account field should exist on res.partner model" ) # Verify field properties income_field = partner._fields['property_account_income_customer_id'] expense_field = partner._fields['property_account_expense_customer_id'] # Check that fields are Many2one to account.account self.assertEqual( income_field.type, 'many2one', "Income account field should be Many2one type" ) self.assertEqual( income_field.comodel_name, 'account.account', "Income account field should reference account.account" ) self.assertEqual( expense_field.type, 'many2one', "Expense account field should be Many2one type" ) self.assertEqual( expense_field.comodel_name, 'account.account', "Expense account field should reference account.account" ) # Verify fields are company-dependent (property fields) self.assertTrue( income_field.company_dependent, "Income account field should be company-dependent" ) self.assertTrue( expense_field.company_dependent, "Expense account field should be company-dependent" ) # Verify fields are accessible (can read/write) partner.write({ 'property_account_income_customer_id': self.income_account_1.id, 'property_account_expense_customer_id': self.expense_account_1.id, }) self.assertEqual( partner.property_account_income_customer_id.id, self.income_account_1.id, "Income account field should be readable and writable" ) self.assertEqual( partner.property_account_expense_customer_id.id, self.expense_account_1.id, "Expense account field should be readable and writable" ) @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_2_customer_account_persistence(self, customer_name): """ **Feature: customer-cogs-expense-account, Property 2: Customer account persistence** For any customer record with income or expense accounts set, saving and reloading the record should preserve the account values. **Validates: Requirements 1.5, 1.6** """ # Create a customer with income and expense accounts partner = self.env['res.partner'].with_company(self.company).create({ 'name': customer_name, 'company_id': self.company.id, 'property_account_income_customer_id': self.income_account_1.id, 'property_account_expense_customer_id': self.expense_account_1.id, }) # Flush to database partner.flush_recordset() # Reload the partner from database partner_reloaded = self.env['res.partner'].browse(partner.id) # Verify accounts are preserved self.assertEqual( partner_reloaded.property_account_income_customer_id.id, self.income_account_1.id, "Income account should be preserved after save and reload" ) self.assertEqual( partner_reloaded.property_account_expense_customer_id.id, self.expense_account_1.id, "Expense account should be preserved after save and reload" ) @settings(max_examples=100) @given( customer_name=st.text(min_size=1, max_size=50, alphabet=st.characters(blacklist_categories=('Cs', 'Cc'))), has_income=st.booleans(), has_expense=st.booleans(), ) def test_property_3_empty_account_acceptance(self, customer_name, has_income, has_expense): """ **Feature: customer-cogs-expense-account, Property 3: Empty account acceptance** For any customer record, leaving income or expense account fields empty should not trigger validation errors. **Validates: Requirements 1.7, 1.8** """ # Prepare account values (may be empty) income_account_id = self.income_account_1.id if has_income else False expense_account_id = self.expense_account_1.id if has_expense else False # Create partner with potentially empty accounts partner = self.env['res.partner'].with_company(self.company).create({ 'name': customer_name, 'company_id': self.company.id, 'property_account_income_customer_id': income_account_id, 'property_account_expense_customer_id': expense_account_id, }) # Verify partner was created successfully self.assertTrue(partner.id, "Partner should be created even with empty account fields") # Verify the account values match what was set if has_income: self.assertEqual(partner.property_account_income_customer_id.id, self.income_account_1.id) else: self.assertFalse(partner.property_account_income_customer_id, "Income account should be empty") if has_expense: self.assertEqual(partner.property_account_expense_customer_id.id, self.expense_account_1.id) else: self.assertFalse(partner.property_account_expense_customer_id, "Expense account should be empty") def test_helper_methods(self): """ Test the helper methods _get_customer_income_account() and _get_customer_expense_account(). """ # Create partner with accounts partner_with_accounts = self.env['res.partner'].with_company(self.company).create({ 'name': 'Partner With Accounts', 'company_id': self.company.id, 'property_account_income_customer_id': self.income_account_1.id, 'property_account_expense_customer_id': self.expense_account_1.id, }) # Test helper methods return correct accounts self.assertEqual( partner_with_accounts._get_customer_income_account().id, self.income_account_1.id, "Helper method should return income account" ) self.assertEqual( partner_with_accounts._get_customer_expense_account().id, self.expense_account_1.id, "Helper method should return expense account" ) # Create partner without accounts partner_without_accounts = self.env['res.partner'].with_company(self.company).create({ 'name': 'Partner Without Accounts', 'company_id': self.company.id, }) # Test helper methods return False when no accounts set self.assertFalse( partner_without_accounts._get_customer_income_account(), "Helper method should return False when no income account set" ) self.assertFalse( partner_without_accounts._get_customer_expense_account(), "Helper method should return False when no expense account set" )