diff --git a/account_bank_statement_import_auto_reconcile/__manifest__.py b/account_bank_statement_import_auto_reconcile/__manifest__.py index 7d8a7bf..b16a3f6 100644 --- a/account_bank_statement_import_auto_reconcile/__manifest__.py +++ b/account_bank_statement_import_auto_reconcile/__manifest__.py @@ -13,9 +13,6 @@ 'account_bank_statement_import', 'base_domain_operator', ], - "demo": [ - "demo/account_bank_statement_import_auto_reconcile_rule.xml", - ], "data": [ "views/account_bank_statement_import_reapply_rules.xml", "views/account_bank_statement.xml", diff --git a/account_bank_statement_import_auto_reconcile/demo/account_bank_statement_import_auto_reconcile_rule.xml b/account_bank_statement_import_auto_reconcile/demo/account_bank_statement_import_auto_reconcile_rule.xml deleted file mode 100644 index 46c7976..0000000 --- a/account_bank_statement_import_auto_reconcile/demo/account_bank_statement_import_auto_reconcile_rule.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - account.bank.statement.import.auto.reconcile.exact.amount - - - - - - - - diff --git a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import.py b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import.py index eb63cb1..6dca184 100644 --- a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import.py +++ b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import.py @@ -19,8 +19,8 @@ class AccountBankStatementImport(models.TransientModel): return statement_ids, notifications statements = self.env['account.bank.statement'].browse(statement_ids) for statement in statements.filtered( - lambda x: x.journal_id. - statement_import_auto_reconcile_rule_ids): + 'journal_id.statement_import_auto_reconcile_rule_ids' + ): reconcile_rules = statement.journal_id\ .statement_import_auto_reconcile_rule_ids.get_rules() auto_reconciled_ids = [] diff --git a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile.py b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile.py index e4a536d..7caf137 100644 --- a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile.py +++ b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile.py @@ -20,7 +20,7 @@ class AccountBankStatementImportAutoReconcile(models.AbstractModel): def _digits(self): try: return self.__digits - except: + except AttributeError: self.__digits = self.env['decimal.precision'].precision_get( 'Account' ) @@ -52,16 +52,13 @@ class AccountBankStatementImportAutoReconcile(models.AbstractModel): :param statement_line: The account.bank.statement.line to reconcile. :param move_line_id: The id of the account.move.line to reconcile. """ - acc_move_line = self.env['account.move.line'] - acc_move = self.env['account.move'] - move = acc_move.create(statement_line._prepare_reconciliation_move( - statement_line.ref)) - move_line_dict = statement_line._prepare_reconciliation_move_line( - move, - -acc_move_line.browse(move_line_id).balance, - ) - move_line = acc_move_line.with_context( - check_move_validity=False).create(move_line_dict) + move_line = self.env['account.move.line'].browse(move_line_id) + return statement_line.process_reconciliation(counterpart_aml_dicts=[{ + 'name': statement_line.name, + 'debit': move_line.credit, + 'credit': move_line.debit, + 'move_line': move_line, + }]) @api.multi def reconcile(self, statement_line): diff --git a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile_rule.py b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile_rule.py index 7ff3ebf..3ba03af 100644 --- a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile_rule.py +++ b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_auto_reconcile_rule.py @@ -11,6 +11,7 @@ class AccountBankStatementImportAutoReconcileRule(models.Model): _name = 'account.bank.statement.import.auto.reconcile.rule' _description = 'Automatic reconciliation rule' + display_name = fields.Char(string='Rule') rule_type = fields.Selection('_sel_rule_type', required=True) journal_id = fields.Many2one('account.journal', 'Journal', required=True) options = fields.Serialized('Options') diff --git a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_reapply_rules.py b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_reapply_rules.py index fc410f6..7e46a94 100644 --- a/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_reapply_rules.py +++ b/account_bank_statement_import_auto_reconcile/models/account_bank_statement_import_reapply_rules.py @@ -23,13 +23,12 @@ class AccountBankStatementImportReapplyRules(models.TransientModel): 'journal!' )) - self.write({'journal_id': journal.id}) reconcile_rules = journal.statement_import_auto_reconcile_rule_ids\ .get_rules() for line in self.env['account.bank.statement.line'].search([ ('statement_id', 'in', statements.ids), - ('journal_entry_id', '=', False), + ('journal_entry_ids', '=', False), ]): for rule in reconcile_rules: if rule.reconcile(line): diff --git a/account_bank_statement_import_auto_reconcile/tests/test_account_bank_statement_import_auto_reconcile.py b/account_bank_statement_import_auto_reconcile/tests/test_account_bank_statement_import_auto_reconcile.py index d04cd84..9706e48 100644 --- a/account_bank_statement_import_auto_reconcile/tests/test_account_bank_statement_import_auto_reconcile.py +++ b/account_bank_statement_import_auto_reconcile/tests/test_account_bank_statement_import_auto_reconcile.py @@ -4,35 +4,43 @@ import base64 from datetime import timedelta from odoo import fields -from odoo.tests.common import TransactionCase +from odoo.addons.account.tests.account_test_classes import AccountingTestCase from odoo.addons.account_bank_statement_import\ .account_bank_statement_import import AccountBankStatementImport -class TestAccountBankStatementImportAutoReconcile(TransactionCase): - - post_install = True - at_install = False - +class TestAccountBankStatementImportAutoReconcile(AccountingTestCase): def setUp(self): super(TestAccountBankStatementImportAutoReconcile, self).setUp() # we don't really have something to import, so we patch the # import routine to return what we want for our tests self.original_parse_file = AccountBankStatementImport._parse_file AccountBankStatementImport._parse_file = self._parse_file - invoice_account = self.env['account.account'].search([ - ('user_type_id', '=', self.env.ref( - 'account.data_account_type_receivable').id)], - limit=1, - ) + self.bank_account = self.env['res.partner.bank'].create({ + 'acc_number': '42424242', + 'partner_id': self.env.ref('base.main_partner').id, + }) + partner = self.env.ref('base.res_partner_2') self.invoice = self.env['account.invoice'].create({ - 'partner_id': self.env.ref('base.res_partner_2').id, - 'account_id': invoice_account.id, - 'type': 'in_invoice', + 'partner_id': partner.id, + 'account_id': partner.property_account_receivable_id.id, + 'type': 'out_invoice', + }) + self.invoice.with_context( + journal_id=self.invoice.journal_id.id + ).write({ + 'invoice_line_ids': [(0, 0, { + 'name': '/', + 'price_unit': 42, + })], + }) + self.invoice.action_invoice_open() + self.journal = self.env['account.journal'].create({ + 'name': 'Journal for automatic reconciliations', + 'code': 'BNKAU', + 'type': 'bank', + 'bank_account_id': self.bank_account.id, }) - self.rule = self.env.ref( - 'account_bank_statement_import_auto_reconcile.rule_amount_exact' - ) def tearDown(self): super(TestAccountBankStatementImportAutoReconcile, self).tearDown() @@ -40,34 +48,58 @@ class TestAccountBankStatementImportAutoReconcile(TransactionCase): def _parse_file(self, data): date = self.invoice.date_invoice - return [ - { - 'currency_code': self.invoice.company_id.currency_id.name, - 'account_number': False, + return ( + self.invoice.company_id.currency_id.name, + self.bank_account.acc_number, + [{ 'name': 'Auto reconcile test', 'date': fields.Date.to_string( fields.Date.from_string(date) + timedelta(days=5) ), - 'transactions': [ - { - 'name': self.invoice.number, - 'date': fields.Date.to_string( - fields.Date.from_string(date) + timedelta(days=5) - ), - 'amount': self.invoice.residual, - 'unique_import_id': '42', - 'account_number': - self.invoice.partner_id.bank_ids[:1].acc_number, - }, - ], - }, - ] + 'transactions': [{ + 'name': self.invoice.number, + 'date': fields.Date.to_string( + fields.Date.from_string(date) + timedelta(days=5) + ), + 'amount': self.invoice.residual, + 'unique_import_id': '42', + 'account_number': + self.invoice.partner_id.bank_ids[:1].acc_number, + }], + }], + ) - def test_account_bank_statement_import_auto_reconcile(self): + def test_account_bank_statement_import_auto_reconcile_odoo(self): + self.env[ + 'account.bank.statement.import.auto.reconcile.rule' + ].create({ + 'journal_id': self.journal.id, + 'rule_type': 'account.bank.statement.import.auto.reconcile.odoo', + }) + self.env['account.bank.statement.import'].create({ + 'data_file': base64.b64encode('hello world'), + 'auto_reconcile': True, + }).import_file() + # should find our move line + self.assertEqual(self.invoice.state, 'paid') + + def test_account_bank_statement_import_auto_reconcile_exact_amount(self): + rule = self.env[ + 'account.bank.statement.import.auto.reconcile.rule' + ].create({ + 'journal_id': self.journal.id, + 'rule_type': + 'account.bank.statement.import.auto.reconcile.exact.amount', + 'match_st_name': True, + 'match_st_ref': True, + 'match_move_name': True, + 'match_move_ref': True, + 'match_line_name': True, + 'match_line_ref': True, + }) # first, we do an import with auto reconciliation turned off action = self.env['account.bank.statement.import'].create({ 'data_file': base64.b64encode('hello world'), - 'journal_id': self.env.ref('account.bank_journal').id, 'auto_reconcile': False, }).import_file() # nothing should have happened @@ -77,10 +109,9 @@ class TestAccountBankStatementImportAutoReconcile(TransactionCase): ).unlink() # now we do matching, but manipulate the matching to work on the # ref field only which is empty in our example - self.rule.write({'match_st_name': False}) + rule.write({'match_st_name': False}) action = self.env['account.bank.statement.import'].create({ 'data_file': base64.b64encode('hello world'), - 'journal_id': self.env.ref('account.bank_journal').id, 'auto_reconcile': True, }).import_file() # nothing should have happened @@ -91,24 +122,45 @@ class TestAccountBankStatementImportAutoReconcile(TransactionCase): # for exact amount matching, our first transaction should be matched # to the invoice's move line, marking the invoice as paid, # provided we allow matching by name - self.rule.write({'match_st_name': True}) + rule.write({'match_st_name': True}) action = self.env['account.bank.statement.import'].create({ 'data_file': base64.b64encode('hello world'), - 'journal_id': self.env.ref('account.bank_journal').id, 'auto_reconcile': True, }).import_file() + # with the changed rules, we should have found a match + self.assertEqual(self.invoice.state, 'paid') + # undo reconciliations, reapply rules on the statement + statement = self.env['account.bank.statement'].browse( + action['context']['statement_ids'] + ) + statement.mapped( + 'line_ids.journal_entry_ids.line_ids' + ).remove_move_reconcile() + self.assertEqual(self.invoice.state, 'open') + statement.mapped('line_ids.journal_entry_ids').write({ + 'statement_line_id': False, + }) + # fiddle a bit with the options + rule.write({'case_sensitive': True}) + self.env['account.bank.statement.import.reapply.rules'].with_context( + active_ids=statement.ids, + ).create({}).action_reapply_rules() + # we should have found the match from before self.assertEqual(self.invoice.state, 'paid') def test_rule_options(self): - self.rule.unlink() rule = self.env[ 'account.bank.statement.import.auto.reconcile.rule' ].create({ - 'journal_id': self.env.ref('account.bank_journal').id, + 'journal_id': self.journal.id, 'rule_type': 'account.bank.statement.import.auto.reconcile.exact.amount', 'match_st_name': False, }) + # be sure our options are patched into the view + fields_view = rule.fields_view_get(view_type='form') + self.assertIn('match_st_name', fields_view['arch']) + # check option values rules = rule.get_rules() # explicitly written self.assertFalse(rules.match_st_name) diff --git a/account_bank_statement_import_auto_reconcile/views/account_bank_statement_import.xml b/account_bank_statement_import_auto_reconcile/views/account_bank_statement_import.xml index e6b3421..d8b40c4 100644 --- a/account_bank_statement_import_auto_reconcile/views/account_bank_statement_import.xml +++ b/account_bank_statement_import_auto_reconcile/views/account_bank_statement_import.xml @@ -5,8 +5,10 @@ - - diff --git a/account_bank_statement_import_auto_reconcile/views/account_journal.xml b/account_bank_statement_import_auto_reconcile/views/account_journal.xml index ae5dd08..a7ee22e 100644 --- a/account_bank_statement_import_auto_reconcile/views/account_journal.xml +++ b/account_bank_statement_import_auto_reconcile/views/account_journal.xml @@ -4,9 +4,26 @@ account.journal - - - + + + + + + + + + account.journal + + + + + + +