201 lines
7.9 KiB
Python
201 lines
7.9 KiB
Python
"""
|
|
Tests for Template_Engine (account_move_templates).
|
|
|
|
Validates: Requirements 3.1, 3.2, 3.3, 3.4, 3.5
|
|
"""
|
|
from odoo.exceptions import ValidationError
|
|
from odoo.tests.common import TransactionCase
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Helpers
|
|
# ---------------------------------------------------------------------------
|
|
|
|
def _get_account(env, account_type='asset_current'):
|
|
"""Return an existing account of the given type, or create one."""
|
|
account = env['account.account'].search(
|
|
[('account_type', '=', account_type)], limit=1
|
|
)
|
|
if not account:
|
|
account = env['account.account'].create({
|
|
'name': f'Test Account ({account_type})',
|
|
'code': f'TST{account_type[:4].upper()}',
|
|
'account_type': account_type,
|
|
})
|
|
return account
|
|
|
|
|
|
def _make_template(env, name='Test Template', lines=None):
|
|
"""Create an AccountMoveTemplate with the given lines.
|
|
|
|
lines: list of dicts with keys: account_id, move_type, percent, line_type
|
|
"""
|
|
vals = {'name': name}
|
|
if lines is not None:
|
|
vals['line_ids'] = [(0, 0, line) for line in lines]
|
|
return env['account.move.template'].create(vals)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TestMoveTemplateValidation
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestMoveTemplateValidation(TransactionCase):
|
|
"""Validates: Requirements 3.1, 3.2, 3.3, 3.4"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.account_debit = _get_account(self.env, 'asset_current')
|
|
self.account_credit = _get_account(self.env, 'liability_current')
|
|
|
|
def _line(self, move_type, percent, account=None):
|
|
if account is None:
|
|
account = self.account_debit if move_type == 'debit' else self.account_credit
|
|
return {
|
|
'account_id': account.id,
|
|
'move_type': move_type,
|
|
'percent': percent,
|
|
'line_type': 'product',
|
|
}
|
|
|
|
def test_no_lines_raises_validation_error(self):
|
|
"""Req 3.1 — template without lines raises ValidationError."""
|
|
with self.assertRaises(ValidationError):
|
|
_make_template(self.env, name='Empty Template', lines=[])
|
|
|
|
def test_imbalanced_percentages_raises_validation_error(self):
|
|
"""Req 3.2 — debit sum != credit sum raises ValidationError."""
|
|
with self.assertRaises(ValidationError):
|
|
_make_template(self.env, name='Imbalanced', lines=[
|
|
self._line('debit', 60.0),
|
|
self._line('credit', 40.0),
|
|
])
|
|
|
|
def test_balanced_percentages_saves_without_error(self):
|
|
"""Req 3.3 — debit sum == credit sum saves without error."""
|
|
template = _make_template(self.env, name='Balanced', lines=[
|
|
self._line('debit', 100.0),
|
|
self._line('credit', 100.0),
|
|
])
|
|
self.assertTrue(template.id, "Template should be saved with a valid ID")
|
|
|
|
def test_percent_below_minimum_raises_validation_error(self):
|
|
"""Req 3.4 — percent < 0.01 raises ValidationError."""
|
|
with self.assertRaises(ValidationError):
|
|
_make_template(self.env, name='Low Percent', lines=[
|
|
self._line('debit', 0.001),
|
|
self._line('credit', 0.001),
|
|
])
|
|
|
|
def test_percent_above_maximum_raises_validation_error(self):
|
|
"""Req 3.4 — percent > 100.0 raises ValidationError."""
|
|
with self.assertRaises(ValidationError):
|
|
_make_template(self.env, name='High Percent', lines=[
|
|
self._line('debit', 101.0),
|
|
self._line('credit', 101.0),
|
|
])
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# TestMoveTemplateWizard
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestMoveTemplateWizard(TransactionCase):
|
|
"""Validates: Requirement 3.5"""
|
|
|
|
def setUp(self):
|
|
super().setUp()
|
|
self.account_debit = _get_account(self.env, 'asset_current')
|
|
self.account_credit = _get_account(self.env, 'liability_current')
|
|
|
|
# Create a balanced template: 100% debit / 100% credit
|
|
self.template = self.env['account.move.template'].create({
|
|
'name': 'Wizard Test Template',
|
|
'line_ids': [
|
|
(0, 0, {
|
|
'account_id': self.account_debit.id,
|
|
'move_type': 'debit',
|
|
'percent': 100.0,
|
|
'line_type': 'product',
|
|
}),
|
|
(0, 0, {
|
|
'account_id': self.account_credit.id,
|
|
'move_type': 'credit',
|
|
'percent': 100.0,
|
|
'line_type': 'product',
|
|
}),
|
|
],
|
|
})
|
|
|
|
# Create a source document that uses the mixin (account.move itself
|
|
# does not use the mixin, so we create a minimal move to act as source)
|
|
self.source_move = self.env['account.move'].create({
|
|
'move_type': 'entry',
|
|
'state': 'draft',
|
|
'line_ids': [
|
|
(0, 0, {
|
|
'account_id': self.account_debit.id,
|
|
'debit': 0.0,
|
|
'credit': 0.0,
|
|
'name': 'Source line',
|
|
}),
|
|
],
|
|
})
|
|
|
|
def test_apply_template_creates_draft_move_with_correct_distribution(self):
|
|
"""Req 3.5 — applying template via wizard creates a draft account.move
|
|
with correct account distribution."""
|
|
base_amount = 1000.0
|
|
|
|
wizard = self.env['account.move.template.wizard'].create({
|
|
'res_model': 'account.move',
|
|
'res_id': self.source_move.id,
|
|
'template_id': self.template.id,
|
|
'amount': base_amount,
|
|
})
|
|
|
|
# Trigger onchange to populate draft lines
|
|
wizard._onchange_compute_draft_lines()
|
|
|
|
# Verify draft lines were computed
|
|
self.assertEqual(len(wizard.draft_line_ids), 2,
|
|
"Wizard should have 2 draft lines (one debit, one credit)")
|
|
|
|
debit_line = wizard.draft_line_ids.filtered(lambda l: l.move_type == 'debit')
|
|
credit_line = wizard.draft_line_ids.filtered(lambda l: l.move_type == 'credit')
|
|
|
|
self.assertAlmostEqual(debit_line.amount, base_amount,
|
|
msg="Debit line amount should equal base_amount * 100%")
|
|
self.assertAlmostEqual(credit_line.amount, base_amount,
|
|
msg="Credit line amount should equal base_amount * 100%")
|
|
|
|
# Post the wizard — creates the account.move
|
|
wizard.action_post()
|
|
|
|
# Reload source move and check linked moves
|
|
self.source_move.invalidate_recordset()
|
|
linked_moves = self.source_move.move_ids
|
|
|
|
self.assertTrue(linked_moves, "A new account.move should be linked to the source")
|
|
|
|
new_move = linked_moves[-1]
|
|
self.assertEqual(new_move.state, 'draft',
|
|
"Created move should be in draft state")
|
|
|
|
move_lines = new_move.line_ids
|
|
debit_move_line = move_lines.filtered(lambda l: l.debit > 0)
|
|
credit_move_line = move_lines.filtered(lambda l: l.credit > 0)
|
|
|
|
self.assertTrue(debit_move_line, "Move should have a debit line")
|
|
self.assertTrue(credit_move_line, "Move should have a credit line")
|
|
|
|
self.assertAlmostEqual(debit_move_line[0].debit, base_amount,
|
|
msg="Debit amount on move line should match base_amount")
|
|
self.assertAlmostEqual(credit_move_line[0].credit, base_amount,
|
|
msg="Credit amount on move line should match base_amount")
|
|
|
|
self.assertEqual(debit_move_line[0].account_id, self.account_debit,
|
|
"Debit line should use the debit account from template")
|
|
self.assertEqual(credit_move_line[0].account_id, self.account_credit,
|
|
"Credit line should use the credit account from template")
|