diff --git a/account_journal_lock_date/README.rst b/account_journal_lock_date/README.rst new file mode 100644 index 00000000..0f0d5bb5 --- /dev/null +++ b/account_journal_lock_date/README.rst @@ -0,0 +1,75 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +========================= +Account Journal Lock Date +========================= + +Lock each accounting journal independently. + +In addition to the lock dates provided by standard Odoo and +account_permanent_lock_move, provide a per journal lock date. + +Note: this module depends on account_permanent_lock_move because it +implements stricter checks than standard Odoo, such as verifying that +one cannot create draft moves before the lock date. + +Note: the journal lock date is ignored for users that are part of +the Adviser group. This rule can be adapted by overriding method +`_can_bypass_journal_lock_date` of `account.journal`. + +Usage +===== + +To use this module, you need to set + +.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas + :alt: Try me on Runbot + :target: https://runbot.odoo-community.org/runbot/{repo_id}/{branch} + +.. repo_id is available in https://github.com/OCA/maintainer-tools/blob/master/tools/repos_with_ids.txt +.. branch is "8.0" for example + +Known issues / Roadmap +====================== + +* a wizard to set the lock date on several journals could be nice to have +* the module does not check that all moves prior the lock date are posted, this could be + made as part of the wizard + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Images +------ + +* Odoo Community Association: `Icon `_. + +Contributors +------------ + +* Stéphane Bidoul + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/account_journal_lock_date/__init__.py b/account_journal_lock_date/__init__.py new file mode 100644 index 00000000..0650744f --- /dev/null +++ b/account_journal_lock_date/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/account_journal_lock_date/__manifest__.py b/account_journal_lock_date/__manifest__.py new file mode 100644 index 00000000..fe491dc7 --- /dev/null +++ b/account_journal_lock_date/__manifest__.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + 'name': 'Account Journal Lock Date', + 'summary': """ + Lock each journal independently""", + 'version': '10.0.1.0.0', + 'license': 'AGPL-3', + 'author': 'ACSONE SA/NV,Odoo Community Association (OCA)', + 'website': 'https://acsone.eu/', + 'depends': [ + 'account_permanent_lock_move', + ], + 'data': [ + 'views/account_journal.xml', + ], + 'demo': [ + ], +} diff --git a/account_journal_lock_date/exceptions.py b/account_journal_lock_date/exceptions.py new file mode 100644 index 00000000..6a17c27a --- /dev/null +++ b/account_journal_lock_date/exceptions.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.exceptions import UserError + + +class JournalLockDateError(UserError): + pass diff --git a/account_journal_lock_date/models/__init__.py b/account_journal_lock_date/models/__init__.py new file mode 100644 index 00000000..067db8c5 --- /dev/null +++ b/account_journal_lock_date/models/__init__.py @@ -0,0 +1,2 @@ +from . import account_journal +from . import account_move diff --git a/account_journal_lock_date/models/account_journal.py b/account_journal_lock_date/models/account_journal.py new file mode 100644 index 00000000..134f231b --- /dev/null +++ b/account_journal_lock_date/models/account_journal.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, fields, models + + +class AccountJournal(models.Model): + + _inherit = 'account.journal' + + journal_lock_date = fields.Date( + string="Lock date", + help="Moves cannot be entered nor modified in this " + "journal prior to the lock date, unless the user " + "has the Adviser role." + ) + + @api.model + def _can_bypass_journal_lock_date(self): + """ This method is meant to be overridden to provide + finer control on who can bypass the lock date """ + return self.env.user.has_group('account.group_account_manager') diff --git a/account_journal_lock_date/models/account_move.py b/account_journal_lock_date/models/account_move.py new file mode 100644 index 00000000..1aaaaee5 --- /dev/null +++ b/account_journal_lock_date/models/account_move.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import api, models, _ + +from ..exceptions import JournalLockDateError + + +class AccountMove(models.Model): + + _inherit = 'account.move' + + @api.multi + def _check_lock_date(self): + res = super(AccountMove, self)._check_lock_date() + if self.env['account.journal']._can_bypass_journal_lock_date(): + return res + for move in self: + lock_date = move.journal_id.journal_lock_date + if lock_date and move.date <= lock_date: + raise JournalLockDateError( + _("You cannot add/modify entries prior to and " + "inclusive of the journal lock date %s") % + (lock_date, )) + return res diff --git a/account_journal_lock_date/static/description/icon.png b/account_journal_lock_date/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/account_journal_lock_date/static/description/icon.png differ diff --git a/account_journal_lock_date/tests/__init__.py b/account_journal_lock_date/tests/__init__.py new file mode 100644 index 00000000..a82bf0bf --- /dev/null +++ b/account_journal_lock_date/tests/__init__.py @@ -0,0 +1 @@ +from . import test_journal_lock_date diff --git a/account_journal_lock_date/tests/test_journal_lock_date.py b/account_journal_lock_date/tests/test_journal_lock_date.py new file mode 100644 index 00000000..88acdbc2 --- /dev/null +++ b/account_journal_lock_date/tests/test_journal_lock_date.py @@ -0,0 +1,88 @@ +# -*- coding: utf-8 -*- +# Copyright 2017 ACSONE SA/NV +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import date, timedelta + +from odoo import fields, tools +from odoo.modules import get_module_resource +from odoo.tests import common + +from ..exceptions import JournalLockDateError + + +class TestJournalLockDate(common.TransactionCase): + + def setUp(self): + super(TestJournalLockDate, self).setUp() + tools.convert_file(self.cr, 'account', + get_module_resource('account', 'test', + 'account_minimal_test.xml'), + {}, 'init', False, 'test') + self.account_move_obj = self.env["account.move"] + self.account_move_line_obj = \ + self.env["account.move.line"] + self.company_id = self.ref('base.main_company') + self.partner = self.browse_ref("base.res_partner_12") + self.account = self.browse_ref("account.a_recv") + self.account2 = self.browse_ref("account.a_expense") + self.journal = self.browse_ref("account.bank_journal") + + def test_journal_lock_date(self): + # create a move and post it + move = self.account_move_obj.create({ + 'date': fields.Date.today(), + 'journal_id': self.journal.id, + 'line_ids': [(0, 0, { + 'account_id': self.account.id, + 'credit': 1000.0, + 'name': 'Credit line', + }), (0, 0, { + 'account_id': self.account2.id, + 'debit': 1000.0, + 'name': 'Debit line', + })] + }) + move.post() + + # lock journal + self.journal.journal_lock_date = fields.Date.today() + + # Test that the move cannot be created, written, or cancelled + with self.assertRaises(JournalLockDateError): + self.account_move_obj.create({ + 'date': fields.Date.today(), + 'journal_id': self.journal.id, + 'line_ids': [(0, 0, { + 'account_id': self.account.id, + 'credit': 1000.0, + 'name': 'Credit line', + }), (0, 0, { + 'account_id': self.account2.id, + 'debit': 1000.0, + 'name': 'Debit line', + })] + }) + + with self.assertRaises(JournalLockDateError): + move.write({'name': 'TEST'}) + + with self.assertRaises(JournalLockDateError): + move.button_cancel() + + # create a move after ther lock date and post it + tomorrow = date.today() + timedelta(days=1) + move3 = self.account_move_obj.create({ + 'date': tomorrow, + 'journal_id': self.journal.id, + 'line_ids': [(0, 0, { + 'account_id': self.account.id, + 'credit': 1000.0, + 'name': 'Credit line', + }), (0, 0, { + 'account_id': self.account2.id, + 'debit': 1000.0, + 'name': 'Debit line', + })] + }) + move3.post() diff --git a/account_journal_lock_date/views/account_journal.xml b/account_journal_lock_date/views/account_journal.xml new file mode 100644 index 00000000..ec2a4504 --- /dev/null +++ b/account_journal_lock_date/views/account_journal.xml @@ -0,0 +1,33 @@ + + + + + + + + account.journal.form (in account_journal_lock_date) + account.journal + + + + + + + + + + + account.journal.tree (in account_journal_lock_date) + account.journal + + + + + + + + + + +