# -*- coding: utf-8 -*- """ Tests for l10n_ru_contract_account — invoice field validation against contract. Validates: Requirements 15.1, 15.2, 15.3, 15.4 """ from odoo.tests.common import TransactionCase from odoo.exceptions import ValidationError # --------------------------------------------------------------------------- # Helpers # --------------------------------------------------------------------------- def _get_or_create_partner(env, parent=None): """Return or create a company partner, optionally with a parent.""" partner = env['res.partner'].create({ 'name': 'Test Partner CA', 'is_company': True, 'parent_id': parent.id if parent else False, }) return partner def _get_or_create_accounts(env): """Return a receivable and payable account for the current company.""" receivable = env['account.account'].search([ ('account_type', '=', 'asset_receivable'), ('company_ids', 'in', env.company.id), ], limit=1) payable = env['account.account'].search([ ('account_type', '=', 'liability_payable'), ('company_ids', 'in', env.company.id), ], limit=1) return receivable, payable def _get_or_create_journal(env, name='Test Sales Journal CA'): """Return or create a sales journal.""" journal = env['account.journal'].search([ ('type', '=', 'sale'), ('company_id', '=', env.company.id), ], limit=1) if not journal: journal = env['account.journal'].create({ 'name': name, 'type': 'sale', 'code': 'TSCA', 'company_id': env.company.id, }) return journal def _get_or_create_payment_term(env): """Return or create a payment term.""" term = env['account.payment.term'].search([], limit=1) if not term: term = env['account.payment.term'].create({'name': 'Immediate CA'}) return term def _make_profile(env, journal, payment_term, receivable, payable): """Create a contract.profile with all required account fields.""" return env['contract.profile'].create({ 'name': 'Test Profile CA', 'journal_id': journal.id, 'payment_term_id': payment_term.id, 'receivable_account_id': receivable.id, 'payable_account_id': payable.id, 'max_receivable_id': 100000.0, }) def _make_contract(env, partner, profile): """Create a minimal partner.contract.customer.""" return env['partner.contract.customer'].create({ 'date_start': '2024-01-01', 'date_end': '2024-12-31', 'type': 'customer', 'profile_id': profile.id, 'partner_id': partner.id, 'partner_type': 'company', 'company_id': env.company.id, }) def _make_invoice(env, partner, journal, payment_term, contract, receivable_account): """Create a draft out_invoice linked to a contract.""" product = env['product.product'].search([], limit=1) income_account = env['account.account'].search([ ('account_type', '=', 'income'), ('company_ids', 'in', env.company.id), ], limit=1) if not income_account: income_account = receivable_account # fallback move = env['account.move'].create({ 'move_type': 'out_invoice', 'partner_id': partner.id, 'journal_id': journal.id, 'invoice_payment_term_id': payment_term.id, 'mt_contract_id': contract.id, 'invoice_line_ids': [(0, 0, { 'name': 'Test line', 'quantity': 1.0, 'price_unit': 100.0, 'account_id': income_account.id, })], }) return move # --------------------------------------------------------------------------- # TestContractAccountMove # --------------------------------------------------------------------------- class TestContractAccountMove(TransactionCase): """ Tests for invoice field validation against contract profile. Validates: Requirements 15.1, 15.2, 15.3, 15.4 """ def setUp(self): super().setUp() self.receivable, self.payable = _get_or_create_accounts(self.env) self.journal = _get_or_create_journal(self.env) self.payment_term = _get_or_create_payment_term(self.env) self.partner = _get_or_create_partner(self.env) self.profile = _make_profile( self.env, self.journal, self.payment_term, self.receivable, self.payable, ) self.contract = _make_contract(self.env, self.partner, self.profile) # ------------------------------------------------------------------ # Requirement 15.1 — journal mismatch raises ValidationError on post # ------------------------------------------------------------------ def test_invoice_posted_journal_mismatch_raises(self): """ Req 15.1 — posting an invoice whose journal differs from the contract's profile journal raises ValidationError. """ other_journal = self.env['account.journal'].create({ 'name': 'Other Journal CA', 'type': 'sale', 'code': 'OJCA', 'company_id': self.env.company.id, }) invoice = _make_invoice( self.env, self.partner, other_journal, self.payment_term, self.contract, self.receivable, ) with self.assertRaises(ValidationError): invoice.action_post() # ------------------------------------------------------------------ # Requirement 15.2 — payment term mismatch raises ValidationError on post # ------------------------------------------------------------------ def test_invoice_posted_payment_term_mismatch_raises(self): """ Req 15.2 — posting an invoice whose payment term differs from the contract's profile payment term raises ValidationError. """ other_term = self.env['account.payment.term'].create({ 'name': 'Other Term CA', }) invoice = _make_invoice( self.env, self.partner, self.journal, other_term, self.contract, self.receivable, ) with self.assertRaises(ValidationError): invoice.action_post() # ------------------------------------------------------------------ # Requirement 15.3 — matching fields post without errors # ------------------------------------------------------------------ def test_invoice_posted_matching_contract_ok(self): """ Req 15.3 — posting an invoice whose journal and payment term match the contract profile succeeds without errors. """ invoice = _make_invoice( self.env, self.partner, self.journal, self.payment_term, self.contract, self.receivable, ) # Should not raise invoice.action_post() self.assertEqual(invoice.state, 'posted', "Invoice should be in posted state after action_post") # ------------------------------------------------------------------ # Requirement 15.4 — sec_partner_id computed field # ------------------------------------------------------------------ def test_compute_sec_partner_id_with_parent(self): """ Req 15.4 — sec_partner_id equals partner_id.parent_id when parent exists. """ parent = _get_or_create_partner(self.env) child = _get_or_create_partner(self.env, parent=parent) invoice = self.env['account.move'].create({ 'move_type': 'out_invoice', 'partner_id': child.id, 'journal_id': self.journal.id, }) self.assertEqual( invoice.sec_partner_id, parent, "sec_partner_id should be partner_id.parent_id when parent exists", ) def test_compute_sec_partner_id_without_parent(self): """ Req 15.4 — sec_partner_id equals partner_id when partner has no parent. """ invoice = self.env['account.move'].create({ 'move_type': 'out_invoice', 'partner_id': self.partner.id, 'journal_id': self.journal.id, }) self.assertEqual( invoice.sec_partner_id, self.partner, "sec_partner_id should equal partner_id when no parent exists", )