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
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/account_statement_cancel_line/test/cancel_line.yml b/account_statement_cancel_line/test/cancel_line.yml
new file mode 100644
index 00000000..032ab054
--- /dev/null
+++ b/account_statement_cancel_line/test/cancel_line.yml
@@ -0,0 +1,80 @@
+-
+ In order to test the cancellation of statement lines, I need a statement
+ profile.
+-
+ !record {model: account.statement.profile, id: profile_test}:
+ name: Bank EUR Profile for automatic checks
+ journal_id: account.bank_journal
+ commission_account_id: account.a_expense
+ company_id: base.main_company
+-
+ Now I create a statement. I create statment lines separately because I need
+ to find each one by XML id
+-
+ !record {model: account.bank.statement, id: statement_test}:
+ name: My Statement
+ profile_id: profile_test
+ company_id: base.main_company
+-
+ I create a first statement line
+-
+ !record {model: account.bank.statement.line, id: statement_line_1}:
+ name: line1
+ statement_id: statement_test
+ ref: ref1
+ date: '2014-01-20'
+ amount: 100.0
+-
+ I create a second statement line
+-
+ !record {model: account.bank.statement.line, id: statement_line_2}:
+ name: line2
+ statement_id: statement_test
+ ref: ref2
+ date: '2014-01-25'
+ amount: 200.0
+-
+ I check that the state of the statement is "Draft"
+-
+ !assert {model: account.bank.statement, id: statement_test}:
+ - state == 'draft'
+-
+ I confirm the statement
+-
+ !python {model: account.bank.statement}: |
+ result = self.button_confirm_bank(cr, uid, [ref("statement_test")])
+-
+ I check that the state of the statement is "Closed"
+-
+ !assert {model: account.bank.statement, id: statement_test}:
+ - state == 'confirm'
+-
+ I check that the state of the statement line is "Confirmed"
+-
+ !assert {model: account.bank.statement.line, id: statement_line_1}:
+ - state == 'confirmed'
+-
+ I check that a move was generated
+-
+ !assert {model: account.bank.statement.line, id: statement_line_1}:
+ - move_ids
+-
+ Now I cancel a statement line
+-
+ !python {model: account.bank.statement.line}: |
+ result = self.button_cancel(cr, uid, [ref("statement_line_1")])
+-
+ I check that the state of the statement went back to "Draft"
+-
+ !assert {model: account.bank.statement, id: statement_test}:
+ - state == 'draft'
+-
+ I also check that the state of the statement line went back to "Draft"
+-
+ !assert {model: account.bank.statement.line, id: statement_line_1}:
+ - state == 'draft'
+-
+ And the move is not there anymore
+-
+ !assert {model: account.bank.statement.line, id: statement_line_1}:
+ - move_ids == []
diff --git a/account_statement_cancel_line/test/confirm_statement_no_double_moves.yml b/account_statement_cancel_line/test/confirm_statement_no_double_moves.yml
new file mode 100644
index 00000000..0fa66bb3
--- /dev/null
+++ b/account_statement_cancel_line/test/confirm_statement_no_double_moves.yml
@@ -0,0 +1,70 @@
+-
+ I want to check that if I confirm a line and then the whole statement,
+ no duplicate moves are created.
+ First, I need a statement profile.
+-
+ !record {model: account.statement.profile, id: profile_test_10}:
+ name: Another Bank EUR Profile for automatic checks
+ journal_id: account.bank_journal
+ commission_account_id: account.a_expense
+ company_id: base.main_company
+-
+ Now I create a statement. I create statment lines separately because I need
+ to find each one by XML id
+-
+ !record {model: account.bank.statement, id: statement_test_10}:
+ name: My Statement
+ profile_id: profile_test_10
+ company_id: base.main_company
+-
+ I create a first statement line
+-
+ !record {model: account.bank.statement.line, id: statement_line_11}:
+ name: line11
+ statement_id: statement_test_10
+ ref: ref11
+ date: '2014-01-20'
+ amount: 100.0
+-
+ I create a second statement line
+-
+ !record {model: account.bank.statement.line, id: statement_line_12}:
+ name: line12
+ statement_id: statement_test_10
+ ref: ref12
+ date: '2014-01-25'
+ amount: 200.0
+-
+ Now I confirm only the first statement line
+-
+ !python {model: account.bank.statement.line}: |
+ result = self.confirm(cr, uid, [ref("statement_line_11")])
+-
+ I check that the state of the statement is still "Draft"
+-
+ !assert {model: account.bank.statement, id: statement_test_10}:
+ - state == 'draft'
+-
+ I confirm the statement
+-
+ !python {model: account.bank.statement}: |
+ result = self.button_confirm_bank(cr, uid, [ref("statement_test_10")])
+-
+ I check that the state of the statement is "Closed"
+-
+ !assert {model: account.bank.statement, id: statement_test_10}:
+ - state == 'confirm'
+-
+ I check that a move was generated for the first statment line
+ and that the state is confirmed
+-
+ !assert {model: account.bank.statement.line, id: statement_line_11}:
+ - len(move_ids) == 1
+ - state == 'confirmed'
+-
+ I check that a move was generated for the second statment line
+ and that the state is confirmed
+-
+ !assert {model: account.bank.statement.line, id: statement_line_12}:
+ - len(move_ids) == 1
+ - state == 'confirmed'
diff --git a/account_statement_cancel_line/test/test_confirm_last_line_balance_check.yml b/account_statement_cancel_line/test/test_confirm_last_line_balance_check.yml
new file mode 100644
index 00000000..8afc5a81
--- /dev/null
+++ b/account_statement_cancel_line/test/test_confirm_last_line_balance_check.yml
@@ -0,0 +1,42 @@
+-
+ I want to check that the behaviour when I confirm the only line of a
+ statement that is draft.
+-
+ I start with a profile
+-
+ !record {model: account.statement.profile, id: profile_test3}:
+ name: Profile for automatic checks
+ balance_check: True
+ journal_id: account.bank_journal
+ commission_account_id: account.a_expense
+-
+ Now I create a statement.
+-
+ !record {model: account.bank.statement, id: statement_test3}:
+ name: My Statement
+ profile_id: profile_test3
+ company_id: base.main_company
+ balance_start: 100.0
+ balance_end_real: 150.0
+-
+ I create a statement line
+-
+ !record {model: account.bank.statement.line, id: statement_line_3}:
+ name: line1
+ statement_id: statement_test3
+ ref: line1
+ date: '2014-01-20'
+ amount: 10.0
+-
+ Now I confirm the statement line. That should not pass the balance check
+-
+ !python {model: account.bank.statement.line}: |
+ # i.e. assertRaises
+ from openerp.osv.osv import except_osv
+ try:
+ self.confirm(cr, uid, [ref("statement_line_3")])
+ except except_osv as exc:
+ print exc.args
+ assert u'The statement balance is incorrect' in exc.args[1], 'We got an error which is not what we expected'
+ else:
+ raise AssertionError('Balance check should have blocked this.')
diff --git a/account_statement_cancel_line/test/test_confirm_last_line_no_balance_check.yml b/account_statement_cancel_line/test/test_confirm_last_line_no_balance_check.yml
new file mode 100644
index 00000000..fb6cd3ec
--- /dev/null
+++ b/account_statement_cancel_line/test/test_confirm_last_line_no_balance_check.yml
@@ -0,0 +1,44 @@
+-
+ I want to check that the behaviour when I confirm the only line of a
+ statement that is draft.
+-
+ I start with a profile
+-
+ !record {model: account.statement.profile, id: profile_test4}:
+ name: Profile for automatic checks
+ balance_check: False
+ journal_id: account.bank_journal
+ commission_account_id: account.a_expense
+-
+ Now I create a statement.
+-
+ !record {model: account.bank.statement, id: statement_test4}:
+ name: My Statement
+ profile_id: profile_test4
+ company_id: base.main_company
+ balance_start: 100.0
+ balance_end_real: 150.0
+-
+ I create a statement line
+-
+ !record {model: account.bank.statement.line, id: statement_line_4}:
+ name: line1
+ statement_id: statement_test4
+ ref: line1
+ date: '2014-01-20'
+ amount: 10.0
+-
+ Now I confirm the statement line
+-
+ !python {model: account.bank.statement.line}: |
+ result = self.confirm(cr, uid, [ref("statement_line_4")])
+-
+ The line should be confirmed
+-
+ !assert {model: account.bank.statement.line, id: statement_line_4}:
+ - state == 'confirmed'
+-
+ And the statement should be confirmed as well
+-
+ !assert {model: account.bank.statement, id: statement_test4}:
+ - state == 'confirm'
diff --git a/account_statement_cancel_line/wizard/__init__.py b/account_statement_cancel_line/wizard/__init__.py
new file mode 100644
index 00000000..49b74d3f
--- /dev/null
+++ b/account_statement_cancel_line/wizard/__init__.py
@@ -0,0 +1,24 @@
+# -*- 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 . #
+# #
+###############################################################################
+"""Wizard for asking the confirmation when some move is reconciled."""
+
+import cancel_statement # noqa
+import cancel_statement_line # noqa
diff --git a/account_statement_cancel_line/wizard/cancel_line.py b/account_statement_cancel_line/wizard/cancel_line.py
new file mode 100644
index 00000000..c5e20af4
--- /dev/null
+++ b/account_statement_cancel_line/wizard/cancel_line.py
@@ -0,0 +1,46 @@
+# -*- 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 . #
+# #
+###############################################################################
+"""Wizard to Cancel a Statement Line."""
+
+from openerp.osv import orm
+
+
+class wizard_cancel_statement_line(orm.TransientModel):
+
+ """Wizard to Cancel a Statement Line."""
+
+ _name = "wizard.cancel.statement.line"
+ _description = "Cancel Statement Line"
+ _columns = {
+ }
+
+ def unreconcile(self, cr, uid, ids, context=None):
+ """Proceed and cancel the statement line, return Action.
+
+ This will delete the move.line and the reconciliation.
+
+ """
+ return self.pool['account.bank.statement.line'].cancel(
+ cr,
+ uid,
+ context['active_ids'],
+ context=context
+ )
diff --git a/account_statement_cancel_line/wizard/cancel_statement.py b/account_statement_cancel_line/wizard/cancel_statement.py
new file mode 100644
index 00000000..6454a127
--- /dev/null
+++ b/account_statement_cancel_line/wizard/cancel_statement.py
@@ -0,0 +1,51 @@
+# -*- 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 . #
+# #
+###############################################################################
+"""Wizard to Cancel a Statement."""
+
+from openerp.osv import orm, fields
+
+
+class wizard_cancel_statement(orm.TransientModel):
+
+ """Wizard to Cancel a Statement."""
+
+ _name = "wizard.cancel.statement"
+ _description = "Cancel Statement"
+ _columns = {
+ 'reconcile_warning': fields.boolean(
+ 'Show reconcile warning',
+ help='This is a hidden field set with a default in the context '
+ 'to choose between two different warning messages in the view.'
+ ),
+ }
+
+ def do_cancel_button(self, cr, uid, ids, context=None):
+ """Proceed and cancel the statement, return Action.
+
+ This will delete the move.line and the reconciliation.
+
+ """
+ return self.pool['account.bank.statement'].do_cancel(
+ cr,
+ uid,
+ context['active_ids'],
+ context=context
+ )
diff --git a/account_statement_cancel_line/wizard/cancel_statement_line.py b/account_statement_cancel_line/wizard/cancel_statement_line.py
new file mode 100644
index 00000000..c5e20af4
--- /dev/null
+++ b/account_statement_cancel_line/wizard/cancel_statement_line.py
@@ -0,0 +1,46 @@
+# -*- 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 . #
+# #
+###############################################################################
+"""Wizard to Cancel a Statement Line."""
+
+from openerp.osv import orm
+
+
+class wizard_cancel_statement_line(orm.TransientModel):
+
+ """Wizard to Cancel a Statement Line."""
+
+ _name = "wizard.cancel.statement.line"
+ _description = "Cancel Statement Line"
+ _columns = {
+ }
+
+ def unreconcile(self, cr, uid, ids, context=None):
+ """Proceed and cancel the statement line, return Action.
+
+ This will delete the move.line and the reconciliation.
+
+ """
+ return self.pool['account.bank.statement.line'].cancel(
+ cr,
+ uid,
+ context['active_ids'],
+ context=context
+ )
diff --git a/account_statement_cancel_line/wizard/cancel_statement_line_view.xml b/account_statement_cancel_line/wizard/cancel_statement_line_view.xml
new file mode 100644
index 00000000..522bd0ae
--- /dev/null
+++ b/account_statement_cancel_line/wizard/cancel_statement_line_view.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ view.wizard.cancel.statement.line.form
+ wizard.cancel.statement.line
+
+
+
+
+
+
+
diff --git a/account_statement_cancel_line/wizard/cancel_statement_view.xml b/account_statement_cancel_line/wizard/cancel_statement_view.xml
new file mode 100644
index 00000000..eb7564c1
--- /dev/null
+++ b/account_statement_cancel_line/wizard/cancel_statement_view.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+ view.wizard.cancel.statement.form
+ wizard.cancel.statement
+
+
+
+
+
+
+