From 6ff1cfa88da28871089196bbc932f19467fa7d51 Mon Sep 17 00:00:00 2001 From: Sylvain Van Hoof Date: Fri, 2 Jun 2017 10:18:24 +0200 Subject: [PATCH] [MIG] Add some unit tests on the module account_credit_control (cherry picked from commit 7fa1b0ac883914f3cdfcaaa1a00016a35cced368) [IMP] Add some unitest and fix a bug with exceptions (cherry picked from commit c2f34ecd06fa5ba093084cf32f35c722c5866ff5) [FIX] Reindent line (cherry picked from commit b643a48f86b20ed6496f2e08366a407095d1540b) [IMP] Add some new unittests (cherry picked from commit f118e2391de3b443558d26186fe44f55a7f0b9c4) [IMP] Add encoding in each python files (cherry picked from commit 9599a88303b921c8e73174db61318f6dc9f02017) --- account_credit_control/__init__.py | 3 + account_credit_control/models/__init__.py | 3 + .../models/credit_control_policy.py | 12 +- .../models/credit_control_run.py | 2 +- account_credit_control/tests/__init__.py | 7 + .../tests/test_account_invoice.py | 111 +++++++++++++ .../tests/test_credit_control_policy.py | 89 ++++++++++ .../tests/test_credit_control_run.py | 155 ++++++++++++++++++ .../tests/test_res_partner.py | 37 +++++ .../wizard/credit_control_policy_changer.py | 1 - 10 files changed, 412 insertions(+), 8 deletions(-) create mode 100644 account_credit_control/tests/__init__.py create mode 100644 account_credit_control/tests/test_account_invoice.py create mode 100644 account_credit_control/tests/test_credit_control_policy.py create mode 100644 account_credit_control/tests/test_credit_control_run.py create mode 100644 account_credit_control/tests/test_res_partner.py diff --git a/account_credit_control/__init__.py b/account_credit_control/__init__.py index 9b4296142..7b23af6a9 100644 --- a/account_credit_control/__init__.py +++ b/account_credit_control/__init__.py @@ -1,2 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Okia SPRL (https://okia.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import models from . import wizard diff --git a/account_credit_control/models/__init__.py b/account_credit_control/models/__init__.py index 01bbe9e10..342e9b8cd 100644 --- a/account_credit_control/models/__init__.py +++ b/account_credit_control/models/__init__.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Okia SPRL (https://okia.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from . import account_account from . import account_invoice from . import credit_control_line diff --git a/account_credit_control/models/credit_control_policy.py b/account_credit_control/models/credit_control_policy.py index 2c3666d91..252712672 100644 --- a/account_credit_control/models/credit_control_policy.py +++ b/account_credit_control/models/credit_control_policy.py @@ -3,7 +3,7 @@ # Copyright 2017 Okia SPRL (https://okia.be) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). from odoo import _, api, fields, models -from odoo.exceptions import UserError +from odoo.exceptions import UserError, ValidationError class CreditControlPolicy(models.Model): @@ -241,8 +241,8 @@ class CreditControlPolicyLevel(models.Model): self.search([('policy_id', '=', policy_level.policy_id.id)], order='level asc', limit=1) if smallest_level.computation_mode == 'previous_date': - return api.ValidationError(_('The smallest level can not be ' - 'of type Previous Reminder')) + raise ValidationError(_('The smallest level can not be ' + 'of type Previous Reminder')) @api.multi def _previous_level(self): @@ -280,7 +280,7 @@ class CreditControlPolicyLevel(models.Model): return "(cr_line.date + %(delay)s)::date <= date(%(controlling_date)s)" @api.multi - def _get_sql_date_boundary_for_computation_mode(self, controlling_date): + def _get_sql_date_boundary_for_computation_mode(self): """ Return a where clauses statement for the given controlling date and computation mode of the level """ @@ -323,7 +323,7 @@ class CreditControlPolicyLevel(models.Model): " AND (mv_line.debit IS NOT NULL AND mv_line.debit != 0.0)\n") sql += " AND" _get_sql_date_part = self._get_sql_date_boundary_for_computation_mode - sql += _get_sql_date_part(controlling_date) + sql += _get_sql_date_part() data_dict = {'controlling_date': controlling_date, 'line_ids': tuple(lines.ids), 'delay': self.delay_days} @@ -363,7 +363,7 @@ class CreditControlPolicyLevel(models.Model): " AND mv_line.id in %(line_ids)s\n") sql += " AND " _get_sql_date_part = self._get_sql_date_boundary_for_computation_mode - sql += _get_sql_date_part(controlling_date) + sql += _get_sql_date_part() previous_level = self._previous_level() data_dict = {'controlling_date': controlling_date, 'line_ids': tuple(lines.ids), diff --git a/account_credit_control/models/credit_control_run.py b/account_credit_control/models/credit_control_run.py index 910525aa5..4bd4ee2e1 100644 --- a/account_credit_control/models/credit_control_run.py +++ b/account_credit_control/models/credit_control_run.py @@ -59,7 +59,7 @@ class CreditControlRun(models.Model): copy=False, ) - @api.multi + @api.model def _check_run_date(self, controlling_date): """ Ensure that there is no credit line in the future using controlling_date diff --git a/account_credit_control/tests/__init__.py b/account_credit_control/tests/__init__.py new file mode 100644 index 000000000..c13729caa --- /dev/null +++ b/account_credit_control/tests/__init__.py @@ -0,0 +1,7 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Okia SPRL (https://okia.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from . import test_credit_control_policy +from . import test_res_partner +from . import test_account_invoice +from . import test_credit_control_run diff --git a/account_credit_control/tests/test_account_invoice.py b/account_credit_control/tests/test_account_invoice.py new file mode 100644 index 000000000..9f920b4c0 --- /dev/null +++ b/account_credit_control/tests/test_account_invoice.py @@ -0,0 +1,111 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Okia SPRL (https://okia.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime +from dateutil import relativedelta + +from odoo import fields +from odoo.tests.common import TransactionCase +from odoo.exceptions import UserError + + +class TestAccountInvoice(TransactionCase): + post_install = True + at_install = False + + def test_action_cancel(self): + """ + Test the method action_cancel on invoice + We will create an old invoice, generate a control run + and check if I can unlink this invoice + :return: + """ + journal = self.env['account.invoice']._default_journal() + + account_type_rec = self.env.ref('account.data_account_type_receivable') + account = self.env['account.account'].create({ + 'code': '400001', + 'name': 'Clients (test)', + 'user_type_id': account_type_rec.id, + 'reconcile': True, + }) + + tag_operation = self.env.ref('account.account_tag_operating') + account_type_inc = self.env.ref('account.data_account_type_revenue') + analytic_account = self.env['account.account'].create({ + 'code': '701001', + 'name': 'Ventes en Belgique (test)', + 'user_type_id': account_type_inc.id, + 'reconcile': True, + 'tag_ids': [(6, 0, [tag_operation.id])] + }) + payment_term = self.env.ref('account.account_payment_term_immediate') + + product = self.env['product.product'].create({ + 'name': 'Product test' + }) + + policy = self.env.ref('account_credit_control.credit_control_3_time') + policy.write({ + 'account_ids': [(6, 0, [account.id])] + }) + + # There is a bug with Odoo ... + # The field "credit_policy_id" is considered as an "old field" and + # the field property_account_receivable_id like a "new field" + # The ORM will create the record with old field + # and update the record with new fields. + # However constrains are applied after the first creation. + partner = self.env['res.partner'].create({ + 'name': 'Partner', + 'property_account_receivable_id': account.id, + }) + partner.credit_policy_id = policy.id + + date_invoice = datetime.today() - relativedelta.relativedelta(years=1) + invoice = self.env['account.invoice'].create({ + 'partner_id': partner.id, + 'journal_id': journal.id, + 'type': 'out_invoice', + 'payment_term_id': payment_term.id, + 'date_invoice': fields.Datetime.to_string(date_invoice), + 'date_due': fields.Datetime.to_string(date_invoice), + }) + + invoice.invoice_line_ids.create({ + 'invoice_id': invoice.id, + 'product_id': product.id, + 'name': product.name, + 'account_id': analytic_account.id, + 'quantity': 5, + 'price_unit': 100, + }) + + # Validate the invoice + invoice.action_invoice_open() + + control_run = self.env['credit.control.run'].create({ + 'date': fields.Date.today(), + 'policy_ids': [(6, 0, [policy.id])] + }) + control_run.generate_credit_lines() + + self.assertTrue(len(invoice.credit_control_line_ids), 1) + control_line = invoice.credit_control_line_ids + + control_marker = self.env['credit.control.marker'] + marker_line = control_marker\ + .with_context(active_model='credit.control.line', + active_ids=[control_line.id])\ + ._get_line_ids() + + self.assertIn(control_line, marker_line) + + marker = self.env['credit.control.marker'].create({ + 'name': 'to_be_sent', + 'line_ids': [(6, 0, [control_line.id])] + }) + marker.mark_lines() + + with self.assertRaises(UserError): + invoice.unlink() diff --git a/account_credit_control/tests/test_credit_control_policy.py b/account_credit_control/tests/test_credit_control_policy.py new file mode 100644 index 000000000..c80566c06 --- /dev/null +++ b/account_credit_control/tests/test_credit_control_policy.py @@ -0,0 +1,89 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Okia SPRL (https://okia.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.exceptions import ValidationError, UserError +from odoo.tests.common import TransactionCase + + +class TestCreditControlPolicy(TransactionCase): + post_install = True + at_install = False + + def test_check_policy_against_account(self): + """ + Test the model check_policy_against_account with several case + :return: + """ + policy = self.env.ref('account_credit_control.credit_control_3_time') + + account_type = self.env.ref('account.data_account_type_receivable') + account = self.env['account.account'].create({ + 'code': '400001', + 'name': 'Test', + 'user_type_id': account_type.id, + 'reconcile': True, + }) + + # The account is not included in the policy + with self.assertRaises(UserError): + policy.check_policy_against_account(account) + + # We set the flag "do_nothing" to True + policy.write({ + 'account_ids': [(5, 0)], + 'do_nothing': True, + }) + result = policy.check_policy_against_account(account) + self.assertTrue(result) + + # We add the account in the policy + policy.write({ + 'account_ids': [(6, 0, [account.id])] + }) + result = policy.check_policy_against_account(account) + self.assertTrue(result) + + def test_check_level_mode(self): + """ + Check the method _check_level_mode on policy level + :return: + """ + level_1 = self.env.ref('account_credit_control.3_time_1') + + with self.assertRaises(ValidationError): + level_1.computation_mode = 'previous_date' + + def test_previous_level(self): + """ + Check the method _previous_level on policy level + :return: + """ + level_1 = self.env.ref('account_credit_control.3_time_1') + level_2 = self.env.ref('account_credit_control.3_time_2') + + previous_level = level_2._previous_level() + self.assertEqual(previous_level, level_1) + + def test_get_sql_date_boundary_for_computation_mode(self): + """ + Check the where clauses statement return by the method + _get_sql_date_boundary_for_computation_mode + according the computation mode + :return: + """ + level_2 = self.env.ref('account_credit_control.3_time_2') + + level_2.computation_mode = 'net_days' + where_clause = level_2._net_days_get_boundary() + result = level_2._get_sql_date_boundary_for_computation_mode() + self.assertEqual(result, where_clause) + + level_2.computation_mode = 'end_of_month' + where_clause = level_2._end_of_month_get_boundary() + result = level_2._get_sql_date_boundary_for_computation_mode() + self.assertEqual(result, where_clause) + + level_2.computation_mode = 'previous_date' + where_clause = level_2._previous_date_get_boundary() + result = level_2._get_sql_date_boundary_for_computation_mode() + self.assertEqual(result, where_clause) diff --git a/account_credit_control/tests/test_credit_control_run.py b/account_credit_control/tests/test_credit_control_run.py new file mode 100644 index 000000000..ac7c83196 --- /dev/null +++ b/account_credit_control/tests/test_credit_control_run.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Okia SPRL (https://okia.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import re +from datetime import datetime +from dateutil import relativedelta + +from odoo import fields +from odoo.tests.common import TransactionCase +from odoo.exceptions import UserError + + +class TestCreditControlRun(TransactionCase): + post_install = True + at_install = False + + def setUp(self): + super(TestCreditControlRun, self).setUp() + + journal = self.env['account.invoice']._default_journal() + + account_type_rec = self.env.ref('account.data_account_type_receivable') + account = self.env['account.account'].create({ + 'code': '400001', + 'name': 'Clients (test)', + 'user_type_id': account_type_rec.id, + 'reconcile': True, + }) + + tag_operation = self.env.ref('account.account_tag_operating') + account_type_inc = self.env.ref('account.data_account_type_revenue') + analytic_account = self.env['account.account'].create({ + 'code': '701001', + 'name': 'Ventes en Belgique (test)', + 'user_type_id': account_type_inc.id, + 'reconcile': True, + 'tag_ids': [(6, 0, [tag_operation.id])] + }) + payment_term = self.env.ref('account.account_payment_term_immediate') + + product = self.env['product.product'].create({ + 'name': 'Product test' + }) + + self.policy = \ + self.env.ref('account_credit_control.credit_control_3_time') + self.policy.write({ + 'account_ids': [(6, 0, [account.id])] + }) + + # There is a bug with Odoo ... + # The field "credit_policy_id" is considered as an "old field" and + # the field property_account_receivable_id like a "new field" + # The ORM will create the record with old field + # and update the record with new fields. + # However constrains are applied after the first creation. + partner = self.env['res.partner'].create({ + 'name': 'Partner', + 'property_account_receivable_id': account.id, + }) + partner.credit_policy_id = self.policy.id + + date_invoice = datetime.today() - relativedelta.relativedelta(years=1) + self.invoice = self.env['account.invoice'].create({ + 'partner_id': partner.id, + 'journal_id': journal.id, + 'type': 'out_invoice', + 'payment_term_id': payment_term.id, + 'date_invoice': fields.Datetime.to_string(date_invoice), + 'date_due': fields.Datetime.to_string(date_invoice), + }) + + self.invoice.invoice_line_ids.create({ + 'invoice_id': self.invoice.id, + 'product_id': product.id, + 'name': product.name, + 'account_id': analytic_account.id, + 'quantity': 5, + 'price_unit': 100, + }) + + # Validate the invoice + self.invoice.action_invoice_open() + + def test_check_run_date(self): + """ + Create a control run older than the last control run + :return: + """ + control_run = self.env['credit.control.run'].create({ + 'date': fields.Date.today(), + 'policy_ids': [(6, 0, [self.policy.id])] + }) + + with self.assertRaises(UserError): + today = datetime.today() + previous_date = today - relativedelta.relativedelta(days=15) + previous_date_str = fields.Date.to_string(previous_date) + control_run._check_run_date(previous_date_str) + + def test_generate_credit_lines(self): + """ + Test the method generate_credit_lines + :return: + """ + control_run = self.env['credit.control.run'].create({ + 'date': fields.Date.today(), + 'policy_ids': [(6, 0, [self.policy.id])] + }) + + control_run.with_context(lang='en_US').generate_credit_lines() + + self.assertTrue(len(self.invoice.credit_control_line_ids), 1) + self.assertEqual(control_run.state, 'done') + + report_regex = \ + r'

Policy "%s" has generated ' \ + r'\d+ Credit Control Lines.

' % self.policy.name + regex_result = re.match(report_regex, control_run.report) + self.assertIsNotNone(regex_result) + + def test_multi_credit_control_run(self): + """ + Generate several control run + :return: + """ + + six_months = datetime.today() - relativedelta.relativedelta(months=6) + six_months_str = fields.Date.to_string(six_months) + three_months = datetime.today() - relativedelta.relativedelta(months=2) + three_months_str = fields.Date.to_string(three_months) + + # First run + first_control_run = self.env['credit.control.run'].create({ + 'date': six_months_str, + 'policy_ids': [(6, 0, [self.policy.id])] + }) + first_control_run.with_context(lang='en_US').generate_credit_lines() + self.assertTrue(len(self.invoice.credit_control_line_ids), 1) + + # Second run + second_control_run = self.env['credit.control.run'].create({ + 'date': three_months_str, + 'policy_ids': [(6, 0, [self.policy.id])] + }) + second_control_run.with_context(lang='en_US').generate_credit_lines() + self.assertTrue(len(self.invoice.credit_control_line_ids), 2) + + # Last run + last_control_run = self.env['credit.control.run'].create({ + 'date': fields.Date.today(), + 'policy_ids': [(6, 0, [self.policy.id])] + }) + last_control_run.with_context(lang='en_US').generate_credit_lines() + self.assertTrue(len(self.invoice.credit_control_line_ids), 2) diff --git a/account_credit_control/tests/test_res_partner.py b/account_credit_control/tests/test_res_partner.py new file mode 100644 index 000000000..62399c6e4 --- /dev/null +++ b/account_credit_control/tests/test_res_partner.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 Okia SPRL (https://okia.be) +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo.tests.common import TransactionCase +from odoo.exceptions import ValidationError + + +class TestCreditControlPolicyLevel(TransactionCase): + post_install = True + at_install = False + + def test_check_credit_policy(self): + """ + Test the constrains on res.partner + First we try to assign an account and a policy with a wrong policy + (this policy doesn't contains the account of the partner). + After that we add the previous account in the policy and + retry to assign this policy and this account on the partner + :return: + """ + policy = self.env.ref('account_credit_control.credit_control_3_time') + + partner = self.env['res.partner'].create({ + 'name': 'Partner 1', + }) + account = partner.property_account_receivable_id + + with self.assertRaises(ValidationError): + partner.write({ + 'credit_policy_id': policy.id, + }) + + policy.write({ + 'account_ids': [(6, 0, [account.id])] + }) + partner.property_account_receivable_id = account.id + partner.credit_policy_id = policy.id diff --git a/account_credit_control/wizard/credit_control_policy_changer.py b/account_credit_control/wizard/credit_control_policy_changer.py index 045a221f4..76ecefb3d 100644 --- a/account_credit_control/wizard/credit_control_policy_changer.py +++ b/account_credit_control/wizard/credit_control_policy_changer.py @@ -82,7 +82,6 @@ class CreditControlPolicyChanger(models.TransientModel): @api.model def _set_invoice_policy(self, move_lines, policy): """ Force policy on invoice """ - invoice_obj = self.env['account.invoice'] invoices = move_lines.mapped('invoice_id') invoices.write({'credit_policy_id': policy.id})