diff --git a/account_statement_cancel_line/__init__.py b/account_statement_cancel_line/__init__.py new file mode 100644 index 00000000..153ef95a --- /dev/null +++ b/account_statement_cancel_line/__init__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +############################################################################### +# # +# Author: Leonardo Pistone +# Copyright 2014 Camptocamp SA +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU Affero General Public License as # +# published by the Free Software Foundation, either version 3 of the # +# License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Affero General Public License for more details. # +# # +# You should have received a copy of the GNU Affero General Public License # +# along with this program. If not, see . # +# # +############################################################################### +"""Account Statement Cancel Line.""" + +import statement # noqa +import statement_line # noqa +import wizard # noqa diff --git a/account_statement_cancel_line/__openerp__.py b/account_statement_cancel_line/__openerp__.py new file mode 100644 index 00000000..35833051 --- /dev/null +++ b/account_statement_cancel_line/__openerp__.py @@ -0,0 +1,74 @@ +# -*- coding: utf-8 -*- +############################################################################### +# # +# Author: Leonardo Pistone # +# Copyright 2014 Camptocamp SA # +# # +# Inspired by module account_banking by EduSense BV, Therp BV, Smile # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU Affero General Public License as # +# published by the Free Software Foundation, either version 3 of the # +# License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Affero General Public License for more details. # +# # +# You should have received a copy of the GNU Affero General Public License # +# along with this program. If not, see . # +# # +############################################################################### +{ + 'name': "Account Statement Cancel Line", + 'version': '0.3', + 'author': 'Camptocamp', + 'maintainer': 'Camptocamp', + 'category': 'Finance', + 'complexity': 'normal', + 'depends': [ + 'account', + 'account_statement_ext', + 'account_default_draft_move', + 'account_statement_base_completion', + ], + 'description': """ + Account Statement Cancel Line + + This module allows to cancel one line of the statement without + cancelling the whole thing. + + To do that, a state is added to the statement line. + + When the user confirms or cancels the whole statement, we keep the + previous functionality, and then we change the state in all statement + lines. We also add a warning if any lines are reconciled. If no lines + are reconciled, we show a generic warning because the operation could + take a long time. + + When the user confirms or cancels a statement line, we update the state + of the line, and if necessary we update the state of the whole + statement, too. + + If the user tries to cancel a line that is reconciled, we ask for + confirmation before proceeding. + """, + 'website': 'http://www.camptocamp.com', + 'init_xml': [], + 'update_xml': [ + 'statement_view.xml', + 'wizard/cancel_statement_view.xml', + 'wizard/cancel_statement_line_view.xml', + ], + 'demo_xml': [], + 'test': [ + 'test/cancel_line.yml', + 'test/test_confirm_last_line_balance_check.yml', + 'test/test_confirm_last_line_no_balance_check.yml', + 'test/confirm_statement_no_double_moves.yml', + ], + 'installable': True, + 'images': [], + 'license': 'AGPL-3', +} diff --git a/account_statement_cancel_line/i18n/account_statement_cancel_line.pot b/account_statement_cancel_line/i18n/account_statement_cancel_line.pot new file mode 100644 index 00000000..c6cc879f --- /dev/null +++ b/account_statement_cancel_line/i18n/account_statement_cancel_line.pot @@ -0,0 +1,97 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * account_statement_cancel_line +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-17 16:46+0000\n" +"PO-Revision-Date: 2014-02-17 16:46+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Some entries are already reconciled. Do you want to unreconcile them and proceed?" +msgstr "" + +#. module: account_statement_cancel_line +#: view:account.bank.statement:0 +msgid "Cancel transaction" +msgstr "" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_wizard_cancel_statement +msgid "Cancel Statement" +msgstr "" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_account_bank_statement +msgid "Bank Statement" +msgstr "" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_wizard_cancel_statement_line +msgid "Cancel Statement Line" +msgstr "" + +#. module: account_statement_cancel_line +#: field:account.bank.statement.line,state:0 +msgid "State" +msgstr "" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Reconciled Entries" +msgstr "" + +#. module: account_statement_cancel_line +#: selection:account.bank.statement.line,state:0 +msgid "Draft" +msgstr "" + +#. module: account_statement_cancel_line +#: selection:account.bank.statement.line,state:0 +msgid "Confirmed" +msgstr "" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Cancel" +msgstr "" + +#. module: account_statement_cancel_line +#: view:account.bank.statement:0 +msgid "Confirm transaction" +msgstr "" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Unreconcile" +msgstr "" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "or" +msgstr "" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Unreconciliation" +msgstr "" diff --git a/account_statement_cancel_line/i18n/fr.po b/account_statement_cancel_line/i18n/fr.po new file mode 100644 index 00000000..b5f2d29d --- /dev/null +++ b/account_statement_cancel_line/i18n/fr.po @@ -0,0 +1,97 @@ +# Translation of OpenERP Server. +# This file contains the translation of the following modules: +# * account_statement_cancel_line +# +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 7.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2014-02-17 15:59+0000\n" +"PO-Revision-Date: 2014-02-17 15:59+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_account_bank_statement_line +msgid "Bank Statement Line" +msgstr "Ligne de relevé bancaire" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Some entries are already reconciled. Do you want to unreconcile them and proceed?" +msgstr "Certaines écritures sont déja léttrées. Voulez-vous annuler définitivement tout les léttrages et poursuivre?" + +#. module: account_statement_cancel_line +#: view:account.bank.statement:0 +msgid "Cancel transaction" +msgstr "Annuler ligne" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_wizard_cancel_statement +msgid "Cancel Statement" +msgstr "Annuler le relevé" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_account_bank_statement +msgid "Bank Statement" +msgstr "Relevé bancaire" + +#. module: account_statement_cancel_line +#: model:ir.model,name:account_statement_cancel_line.model_wizard_cancel_statement_line +msgid "Cancel Statement Line" +msgstr "Annuler ligne de relevé" + +#. module: account_statement_cancel_line +#: field:account.bank.statement.line,state:0 +msgid "State" +msgstr "Etat" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Reconciled Entries" +msgstr "Lignes léttrées" + +#. module: account_statement_cancel_line +#: selection:account.bank.statement.line,state:0 +msgid "Draft" +msgstr "Brouillon" + +#. module: account_statement_cancel_line +#: selection:account.bank.statement.line,state:0 +msgid "Confirmed" +msgstr "Confirmée" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Cancel" +msgstr "Annuler" + +#. module: account_statement_cancel_line +#: view:account.bank.statement:0 +msgid "Confirm transaction" +msgstr "Confirmer ligne" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Unreconcile" +msgstr "Déléttrer" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "or" +msgstr "ou" + +#. module: account_statement_cancel_line +#: view:wizard.cancel.statement:0 +#: view:wizard.cancel.statement.line:0 +msgid "Unreconciliation" +msgstr "Déléttrage" diff --git a/account_statement_cancel_line/migrations/0.3/post-set-statement-line-state.py b/account_statement_cancel_line/migrations/0.3/post-set-statement-line-state.py new file mode 100644 index 00000000..1772bfae --- /dev/null +++ b/account_statement_cancel_line/migrations/0.3/post-set-statement-line-state.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +############################################################################### +# # +# Author: Leonardo Pistone # +# Copyright 2014 Camptocamp SA # +# # +# Inspired by module account_banking by EduSense BV, Therp BV, Smile # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU Affero General Public License as # +# published by the Free Software Foundation, either version 3 of the # +# License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Affero General Public License for more details. # +# # +# You should have received a copy of the GNU Affero General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +__name__ = ("account.bank.statement.line:: set new field 'state' to " + "confirmed for all statement lines belonging to confirmed " + "statements") + + +def migrate(cr, version): + """This module adds the field state to the statement line. Here we set it + correctly on existing statements. The names, and so this migration, are + intentionally kept in sync with the account_banking module.""" + cr.execute("UPDATE account_bank_statement_line as sl " + " SET state = 'confirmed'" + " FROM account_bank_statement as s " + " WHERE sl.statement_id = s.id " + " AND s.state = 'confirm' " + ) diff --git a/account_statement_cancel_line/statement.py b/account_statement_cancel_line/statement.py new file mode 100644 index 00000000..26571f9f --- /dev/null +++ b/account_statement_cancel_line/statement.py @@ -0,0 +1,118 @@ +# -*- coding: utf-8 -*- +############################################################################### +# # +# Author: Leonardo Pistone +# Copyright 2014 Camptocamp SA +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU Affero General Public License as # +# published by the Free Software Foundation, either version 3 of the # +# License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Affero General Public License for more details. # +# # +# You should have received a copy of the GNU Affero General Public License # +# along with this program. If not, see . # +# # +############################################################################### +"""Account Statement Cancel Line.""" + +from openerp.osv import orm + + +class Statement(orm.Model): + + """Bank Statement. + + Minimal changes to allow cancelling single lines and checking if there are + any lines that are already reconciled. + + """ + + _inherit = "account.bank.statement" + + def button_confirm_bank(self, cr, uid, ids, context=None): + """If all lines are draft, change their state. + Otherwise, confirm line by line to avoid duplicate moves. + Return super. + + """ + st_line_obj = self.pool['account.bank.statement.line'] + + statement_ids_fully_confirm = [] + for st in self.browse(cr, uid, ids, context=context): + if all(l.state == 'draft' for l in st.line_ids): + statement_ids_fully_confirm.append(st.id) + st_line_obj.write(cr, uid, [l.id for l in st.line_ids], { + 'state': 'confirmed' + }, context=context) + else: + st_line_obj.confirm(cr, uid, [l.id for l in st.line_ids], + context=context) + + if statement_ids_fully_confirm: + return super(Statement, self).button_confirm_bank( + cr, uid, statement_ids_fully_confirm, context) + else: + return True + + def button_cancel(self, cr, uid, ids, context=None): + """Check if there is any reconciliation. Return action.""" + st_line_obj = self.pool['account.bank.statement.line'] + for statement in self.browse(cr, uid, ids, context=context): + ctx = context.copy() + ctx['default_reconcile_warning'] = st_line_obj.has_reconciliation( + cr, + uid, + [line.id for line in statement.line_ids], + context=context) + return { + 'type': 'ir.actions.act_window', + 'res_model': 'wizard.cancel.statement', + 'view_type': 'form', + 'view_mode': 'form', + 'target': 'new', + 'context': ctx, + } + + self.do_cancel(cr, uid, ids, context=context) + + def do_cancel(self, cr, uid, ids, context=None): + """Change the state on the statement lines. Return super. + + This method is called directly when there are no reconciliations, or + from the warning wizard, if there are reconciliations. + + """ + st_line_obj = self.pool['account.bank.statement.line'] + for st_data in self.read(cr, uid, ids, ['line_ids'], context=context): + st_line_obj.write(cr, uid, st_data['line_ids'], { + 'state': 'draft' + }, context=context) + + return super(Statement, self).button_cancel( + cr, uid, ids, context) + + def confirm_statement_from_lines(self, cr, uid, ids, context=None): + """If all lines are confirmed, so is the whole statement. + + Return True if we changed anything. + + """ + need_to_update_view = False + for statement in self.browse(cr, uid, ids, context=context): + if all(line.state == 'confirmed' for line in statement.line_ids): + self.write(cr, uid, [statement.id], { + 'state': 'confirm' + }, context=context) + need_to_update_view = True + self.balance_check( + cr, + uid, + statement.id, + journal_type=statement.journal_id.type, + context=context) + return need_to_update_view diff --git a/account_statement_cancel_line/statement_line.py b/account_statement_cancel_line/statement_line.py new file mode 100644 index 00000000..486ccab9 --- /dev/null +++ b/account_statement_cancel_line/statement_line.py @@ -0,0 +1,213 @@ +# -*- coding: utf-8 -*- +############################################################################### +# # +# Author: Leonardo Pistone +# Copyright 2014 Camptocamp SA +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU Affero General Public License as # +# published by the Free Software Foundation, either version 3 of the # +# License, or (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU Affero General Public License for more details. # +# # +# You should have received a copy of the GNU Affero General Public License # +# along with this program. If not, see . # +# # +############################################################################### +"""Account Statement Cancel Line.""" + +from openerp.osv import fields, orm + +from openerp.tools.translate import _ + + +class StatementLine(orm.Model): + + """Add a state to the statement line.""" + + _inherit = "account.bank.statement.line" + + _columns = { + 'state': fields.selection( + [('draft', 'Draft'), ('confirmed', 'Confirmed')], + 'State', + readonly=True, + required=True + ), + } + + _defaults = { + 'state': 'draft', + } + + def confirm(self, cr, uid, ids, context=None): + """Confirm just one statement line, return action. + + The module account_banking does have a similar method, but at the + moment it uses a different logic (for example, it uses vouchers, where + the bank-statement-reconcile branch does not). + + """ + if context is None: + context = {} + local_ctx = context.copy() + # if account_constraints is installed, we need to tell it that moves + # are being created by a statement, which is OK. + # The module tries to prevent direct changes to the moves created by + # bank statements. + local_ctx['from_parent_object'] = True + + statement_pool = self.pool.get('account.bank.statement') + res = {} + + for st_line in self.browse(cr, uid, ids, context): + if st_line.state != 'draft': + continue + st = st_line.statement_id + curr_id = st.journal_id.company_id.currency_id.id + + st_number = st.name + st_line_number = statement_pool.get_next_st_line_number( + cr, uid, st_number, st_line, context) + + # We pass the local_ctx so that account_constraints allows us to + # work on the moves generated by the bank statement + statement_pool.create_move_from_st_line( + cr, + uid, + st_line.id, + curr_id, + st_line_number, + local_ctx) + self.write(cr, uid, st_line.id, { + 'state': 'confirmed' + }, context) + if statement_pool.confirm_statement_from_lines(cr, uid, [st.id], + context=context): + # to see that the state of the statement has changed, we need + # to update the whole view. Do that only if necessary. + res = { + 'type': 'ir.actions.client', + 'tag': 'reload', + } + + return res + + def has_reconciliation(self, cr, uid, ids, context=None): + """Check if the line has some reconciliation. Return boolean.""" + if context is None: + context = {} + + for st_line in self.browse(cr, uid, ids, context=context): + for move in st_line.move_ids: + for move_line in move.line_id: + if move_line.reconcile: + # aha! we have some reconciliation! + return True + + # no reconciliation to worry about + return False + + def button_cancel(self, cr, uid, ids, context=None): + """Check if a line is reconciled, and cancel it. Return action.""" + if context is None: + context = {} + + if self.has_reconciliation(cr, uid, ids, context=context): + # ask confirmation, we have some reconciliation already + return { + 'type': 'ir.actions.act_window', + 'res_model': 'wizard.cancel.statement.line', + 'view_type': 'form', + 'view_mode': 'form', + 'target': 'new', + 'context': context, + } + + # no reconciliation to worry about: we cancel our lines directly then + return self.cancel(cr, uid, ids, context=context) + + def cancel(self, cr, uid, ids, context=None): + """Cancel one statement line, return action. + + This is again similar to the method cancel in the account_banking + module. + + """ + if context is None: + context = {} + local_ctx = context.copy() + # if account_constraints is installed, we need to tell it that moves + # are being created by a statement, which is OK. + # The module tries to prevent direct changes to the moves created by + # bank statements. + local_ctx['from_parent_object'] = True + + move_pool = self.pool['account.move'] + statement_pool = self.pool['account.bank.statement'] + + st_line_ids = [] + + # to avoid duplicates if all lines come from the same statement + statement_ids = set() + + move_unlink_ids = [] + # harvest ids for various actions + for st_line in self.browse(cr, uid, ids, context): + if st_line.state != 'confirmed': + continue + + for move in st_line.move_ids: + # We allow for people canceling and removing + # the associated payments, which can lead to confirmed + # statement lines without an associated move + move_unlink_ids.append(move.id) + # do we need to check that? + if move.state != 'draft': + raise orm.except_orm( + _('Confirmed Journal Entry'), + _('You cannot delete a confirmed Statement Line ' + 'associated to a Journal Entry that is posted.')) + st_line_ids.append(st_line.id) + if st_line.statement_id.state != 'draft': + statement_ids.add(st_line.statement_id.id) + + move_pool.button_cancel( + cr, uid, move_unlink_ids, context=context) + + move_pool.unlink(cr, uid, move_unlink_ids, context=local_ctx) + self.write(cr, uid, st_line_ids, { + 'state': 'draft', + 'already_completed': False + }, context=context) + if statement_ids: + # if we cancel one or more lines, the statement goes back to draft, + # too + statement_pool.write(cr, uid, list(statement_ids), { + 'state': 'draft' + }, context=context) + # then we manually update the view of the statement + return { + 'type': 'ir.actions.client', + 'tag': 'reload', + } + else: + # no need to update the view then + return {} + + def unlink(self, cr, uid, ids, context=None): + """Don't allow deletion of a confirmed statement line. Return super.""" + if type(ids) is int: + ids = [ids] + for line in self.browse(cr, uid, ids, context=context): + if line.state == 'confirmed': + raise orm.except_orm( + _('Confirmed Statement Line'), + _("You cannot delete a confirmed Statement Line" + ": '%s'") % line.name) + return super(StatementLine, self).unlink( + cr, uid, ids, context=context) diff --git a/account_statement_cancel_line/statement_view.xml b/account_statement_cancel_line/statement_view.xml new file mode 100644 index 00000000..4d3abb0c --- /dev/null +++ b/account_statement_cancel_line/statement_view.xml @@ -0,0 +1,28 @@ + + + + + + account.bank.statement.form. + + account.bank.statement + + + + +