diff --git a/account_document_reversal/__manifest__.py b/account_document_reversal/__manifest__.py
index 6d50c6ed4..13f236b1d 100644
--- a/account_document_reversal/__manifest__.py
+++ b/account_document_reversal/__manifest__.py
@@ -2,22 +2,17 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
{
- 'name': 'Account Document Reversal',
- 'summary': 'Create reversed journal entries when cancel document',
- 'version': '12.0.1.0.0',
- 'author': 'Ecosoft,'
- 'Eficent,'
- 'Odoo Community Association (OCA)',
- 'website': 'https://github.com/OCA/account-financial-tools',
- 'category': 'Accounting & Finance',
- 'depends': ['account_cancel'],
- 'data': [
- 'wizard/reverse_account_document_wizard.xml',
- 'views/account_view.xml',
- ],
- 'license': 'AGPL-3',
- 'installable': True,
- 'application': False,
- 'development_status': 'beta',
- 'maintainers': ['kittiu'],
+ "name": "Account Document Reversal",
+ "summary": "Create reversed journal entries when cancel document",
+ "version": "12.0.1.0.0",
+ "author": "Ecosoft," "Eficent," "Odoo Community Association (OCA)",
+ "website": "https://github.com/OCA/account-financial-tools",
+ "category": "Accounting & Finance",
+ "depends": ["account_cancel"],
+ "data": ["wizard/reverse_account_document_wizard.xml", "views/account_view.xml",],
+ "license": "AGPL-3",
+ "installable": True,
+ "application": False,
+ "development_status": "beta",
+ "maintainers": ["kittiu"],
}
diff --git a/account_document_reversal/models/account.py b/account_document_reversal/models/account.py
index 59c0b0a48..b7afc1094 100644
--- a/account_document_reversal/models/account.py
+++ b/account_document_reversal/models/account.py
@@ -1,35 +1,38 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import models, fields, api
+from odoo import api, fields, models
class AccountJournal(models.Model):
- _inherit = 'account.journal'
+ _inherit = "account.journal"
cancel_method = fields.Selection(
- [('normal', 'Normal (delete journal entries if exists)'),
- ('reversal', 'Reversal (create reversed journal entries)')],
- string='Cancel Method',
- default='normal',
+ [
+ ("normal", "Normal (delete journal entries if exists)"),
+ ("reversal", "Reversal (create reversed journal entries)"),
+ ],
+ string="Cancel Method",
+ default="normal",
required=True,
)
is_cancel_reversal = fields.Boolean(
- string='Use Cancel Reversal',
- compute='_compute_is_cancel_reversal',
+ string="Use Cancel Reversal",
+ compute="_compute_is_cancel_reversal",
help="True, when journal allow cancel entries with method is reversal",
)
use_different_journal = fields.Boolean(
- string='Use different journal for reversal',
+ string="Use different journal for reversal",
help="If checked, reversal wizard will show field Reversal Journal",
)
reversal_journal_id = fields.Many2one(
- 'account.journal',
- string='Default Reversal Journal',
+ "account.journal",
+ string="Default Reversal Journal",
help="Journal in this field will show in reversal wizard as default",
)
@api.multi
def _compute_is_cancel_reversal(self):
for rec in self:
- rec.is_cancel_reversal = \
- rec.update_posted and rec.cancel_method == 'reversal'
+ rec.is_cancel_reversal = (
+ rec.update_posted and rec.cancel_method == "reversal"
+ )
diff --git a/account_document_reversal/models/account_bank_statement.py b/account_document_reversal/models/account_bank_statement.py
index caaff3a95..07f2dafbe 100644
--- a/account_document_reversal/models/account_bank_statement.py
+++ b/account_document_reversal/models/account_bank_statement.py
@@ -1,23 +1,23 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import api, models, _
+from odoo import _, api, models
from odoo.exceptions import UserError
class AccountPayment(models.Model):
- _name = 'account.bank.statement.line'
- _inherit = ['account.bank.statement.line', 'account.document.reversal']
+ _name = "account.bank.statement.line"
+ _inherit = ["account.bank.statement.line", "account.document.reversal"]
@api.multi
def button_cancel_reconciliation(self):
""" If cancel method is to reverse, use document reversal wizard """
- cancel_reversal = all(self.mapped('journal_entry_ids.move_id.'
- 'journal_id.is_cancel_reversal'))
- states = self.mapped('statement_id.state')
+ cancel_reversal = all(
+ self.mapped("journal_entry_ids.move_id." "journal_id.is_cancel_reversal")
+ )
+ states = self.mapped("statement_id.state")
if cancel_reversal:
- if not all(st == 'open' for st in states):
- raise UserError(
- _('Only new bank statement can be cancelled'))
+ if not all(st == "open" for st in states):
+ raise UserError(_("Only new bank statement can be cancelled"))
return self.reverse_document_wizard()
return super().button_cancel_reconciliation()
@@ -25,41 +25,42 @@ class AccountPayment(models.Model):
def action_document_reversal(self, date=None, journal_id=None):
""" Reverse all moves related to this statement + delete payment """
# This part is from button_cancel_reconciliation()
- aml_to_unbind = self.env['account.move.line']
- aml_to_cancel = self.env['account.move.line']
- payment_to_unreconcile = self.env['account.payment']
- payment_to_cancel = self.env['account.payment']
+ aml_to_unbind = self.env["account.move.line"]
+ aml_to_cancel = self.env["account.move.line"]
+ payment_to_unreconcile = self.env["account.payment"]
+ payment_to_cancel = self.env["account.payment"]
for st_line in self:
aml_to_unbind |= st_line.journal_entry_ids
for line in st_line.journal_entry_ids:
payment_to_unreconcile |= line.payment_id
- if st_line.move_name and \
- line.payment_id.payment_reference == st_line.move_name:
+ if (
+ st_line.move_name
+ and line.payment_id.payment_reference == st_line.move_name
+ ):
# there can be several moves linked to a statement line but
# maximum one created by the line itself
aml_to_cancel |= line
payment_to_cancel |= line.payment_id
aml_to_unbind = aml_to_unbind - aml_to_cancel
if aml_to_unbind:
- aml_to_unbind.write({'statement_line_id': False})
+ aml_to_unbind.write({"statement_line_id": False})
payment_to_unreconcile = payment_to_unreconcile - payment_to_cancel
if payment_to_unreconcile:
payment_to_unreconcile.unreconcile()
# --
# Set all moves to unreconciled
- aml_to_cancel.filtered(lambda x:
- x.account_id.reconcile).remove_move_reconcile()
- moves = aml_to_cancel.mapped('move_id')
+ aml_to_cancel.filtered(lambda x: x.account_id.reconcile).remove_move_reconcile()
+ moves = aml_to_cancel.mapped("move_id")
# Important to remove relation with move.line before reverse
- aml_to_cancel.write({'payment_id': False,
- 'statement_id': False,
- 'statement_line_id': False})
+ aml_to_cancel.write(
+ {"payment_id": False, "statement_id": False, "statement_line_id": False}
+ )
# Create reverse entries
moves.reverse_moves(date, journal_id)
# Delete related payments
if payment_to_cancel:
payment_to_cancel.unlink()
# Unlink from statement line
- self.write({'move_name': False})
+ self.write({"move_name": False})
return True
diff --git a/account_document_reversal/models/account_document_reversal.py b/account_document_reversal/models/account_document_reversal.py
index 4e80c7423..44507cb39 100644
--- a/account_document_reversal/models/account_document_reversal.py
+++ b/account_document_reversal/models/account_document_reversal.py
@@ -1,17 +1,18 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import models, api
+from odoo import api, models
class AccountDocumentReversal(models.AbstractModel):
- _name = 'account.document.reversal'
- _description = 'Abstract Module for Document Reversal'
+ _name = "account.document.reversal"
+ _description = "Abstract Module for Document Reversal"
@api.model
def reverse_document_wizard(self):
""" Return Wizard to Cancel Document """
- action = self.env.ref('account_document_reversal.'
- 'action_view_reverse_account_document')
+ action = self.env.ref(
+ "account_document_reversal." "action_view_reverse_account_document"
+ )
vals = action.read()[0]
return vals
diff --git a/account_document_reversal/models/account_invoice.py b/account_document_reversal/models/account_invoice.py
index 6e7e96341..36c1c2f62 100644
--- a/account_document_reversal/models/account_invoice.py
+++ b/account_document_reversal/models/account_invoice.py
@@ -1,12 +1,12 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import api, models, _
-from odoo.exceptions import ValidationError, UserError
+from odoo import _, api, models
+from odoo.exceptions import UserError, ValidationError
class AccountInvoice(models.Model):
- _name = 'account.invoice'
- _inherit = ['account.invoice', 'account.document.reversal']
+ _name = "account.invoice"
+ _inherit = ["account.invoice", "account.document.reversal"]
@api.multi
def action_invoice_cancel(self):
@@ -14,16 +14,20 @@ class AccountInvoice(models.Model):
* Draft invoice, fall back to standard invoice cancel
* Non draft, must be fully open (not even partial reconciled) to cancel
"""
- cancel_reversal = all(self.mapped('journal_id.is_cancel_reversal'))
- states = self.mapped('state')
- if cancel_reversal and 'draft' not in states:
- if not all(st == 'open' for st in states) or \
- (self.mapped('move_id.line_ids.matched_debit_ids') |
- self.mapped('move_id.line_ids.matched_credit_ids')):
+ cancel_reversal = all(self.mapped("journal_id.is_cancel_reversal"))
+ states = self.mapped("state")
+ if cancel_reversal and "draft" not in states:
+ if not all(st == "open" for st in states) or (
+ self.mapped("move_id.line_ids.matched_debit_ids")
+ | self.mapped("move_id.line_ids.matched_credit_ids")
+ ):
raise UserError(
- _('Only fully unpaid invoice can be cancelled.\n'
- 'To cancel this invoice, make sure all payment(s) '
- 'are also cancelled.'))
+ _(
+ "Only fully unpaid invoice can be cancelled.\n"
+ "To cancel this invoice, make sure all payment(s) "
+ "are also cancelled."
+ )
+ )
return self.reverse_document_wizard()
return super().action_invoice_cancel()
@@ -31,22 +35,24 @@ class AccountInvoice(models.Model):
def action_document_reversal(self, date=None, journal_id=None):
""" Reverse all moves related to this invoice + set state to cancel """
# Check document state
- if 'cancel' in self.mapped('state'):
- raise ValidationError(
- _('You are trying to cancel the cancelled document'))
- MoveLine = self.env['account.move.line']
- move_lines = MoveLine.search([('invoice_id', 'in', self.ids)])
- moves = move_lines.mapped('move_id')
+ if "cancel" in self.mapped("state"):
+ raise ValidationError(_("You are trying to cancel the cancelled document"))
+ MoveLine = self.env["account.move.line"]
+ move_lines = MoveLine.search([("invoice_id", "in", self.ids)])
+ moves = move_lines.mapped("move_id")
# Set all moves to unreconciled
- move_lines.filtered(lambda x:
- x.account_id.reconcile).remove_move_reconcile()
+ move_lines.filtered(lambda x: x.account_id.reconcile).remove_move_reconcile()
# Important to remove relation with move.line before reverse
- move_lines.write({'invoice_id': False})
+ move_lines.write({"invoice_id": False})
# Create reverse entries
moves.reverse_moves(date, journal_id)
# Set state cancelled and unlink with account.move
- self.write({'move_id': False,
- 'move_name': False,
- 'reference': False,
- 'state': 'cancel'})
+ self.write(
+ {
+ "move_id": False,
+ "move_name": False,
+ "reference": False,
+ "state": "cancel",
+ }
+ )
return True
diff --git a/account_document_reversal/models/account_move.py b/account_document_reversal/models/account_move.py
index 29fee46e8..8eb1732df 100644
--- a/account_document_reversal/models/account_move.py
+++ b/account_document_reversal/models/account_move.py
@@ -1,24 +1,26 @@
# Copyright 2019 Eficent Business and IT Consulting Services, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import api, models, fields, _
+from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
class AccountMove(models.Model):
- _name = 'account.move'
- _inherit = ['account.move', 'account.document.reversal']
+ _name = "account.move"
+ _inherit = ["account.move", "account.document.reversal"]
is_cancel_reversal = fields.Boolean(
- string='Use Cancel Reversal',
- related='journal_id.is_cancel_reversal',
+ string="Use Cancel Reversal", related="journal_id.is_cancel_reversal",
)
@api.multi
def button_cancel(self):
""" Do not allow using this button for cancel with reversal """
- cancel_reversal = any(self.mapped('is_cancel_reversal'))
+ cancel_reversal = any(self.mapped("is_cancel_reversal"))
if cancel_reversal:
raise ValidationError(
- _('This action is not allowed for cancel with reversal.\n'
- 'Please use Reverse Entry.'))
+ _(
+ "This action is not allowed for cancel with reversal.\n"
+ "Please use Reverse Entry."
+ )
+ )
return super().button_cancel()
diff --git a/account_document_reversal/models/account_payment.py b/account_document_reversal/models/account_payment.py
index bc8d86a45..c9adde6de 100644
--- a/account_document_reversal/models/account_payment.py
+++ b/account_document_reversal/models/account_payment.py
@@ -1,20 +1,21 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import api, models, _
+from odoo import _, api, models
from odoo.exceptions import ValidationError
class AccountPayment(models.Model):
- _name = 'account.payment'
- _inherit = ['account.payment', 'account.document.reversal']
+ _name = "account.payment"
+ _inherit = ["account.payment", "account.document.reversal"]
@api.multi
def cancel(self):
""" If cancel method is to reverse, use document reversal wizard """
cancel_reversal = all(
- self.mapped('move_line_ids.move_id.journal_id.is_cancel_reversal'))
- states = self.mapped('state')
- if cancel_reversal and 'draft' not in states:
+ self.mapped("move_line_ids.move_id.journal_id.is_cancel_reversal")
+ )
+ states = self.mapped("state")
+ if cancel_reversal and "draft" not in states:
return self.reverse_document_wizard()
return super().cancel()
@@ -22,19 +23,16 @@ class AccountPayment(models.Model):
def action_document_reversal(self, date=None, journal_id=None):
""" Reverse all moves related to this payment + set state to cancel """
# Check document state
- if 'cancelled' in self.mapped('state'):
- raise ValidationError(
- _('You are trying to cancel the cancelled document'))
- move_lines = self.mapped('move_line_ids')
- moves = move_lines.mapped('move_id')
+ if "cancelled" in self.mapped("state"):
+ raise ValidationError(_("You are trying to cancel the cancelled document"))
+ move_lines = self.mapped("move_line_ids")
+ moves = move_lines.mapped("move_id")
# Set all moves to unreconciled
- move_lines.filtered(lambda x:
- x.account_id.reconcile).remove_move_reconcile()
+ move_lines.filtered(lambda x: x.account_id.reconcile).remove_move_reconcile()
# Important to remove relation with move.line before reverse
- move_lines.write({'payment_id': False})
+ move_lines.write({"payment_id": False})
# Create reverse entries
moves.reverse_moves(date, journal_id)
# Set state cancelled and unlink with account.move
- self.write({'move_name': False,
- 'state': 'cancelled'})
+ self.write({"move_name": False, "state": "cancelled"})
return True
diff --git a/account_document_reversal/tests/test_invoice_reversal.py b/account_document_reversal/tests/test_invoice_reversal.py
index 16fd181d4..25ee36627 100644
--- a/account_document_reversal/tests/test_invoice_reversal.py
+++ b/account_document_reversal/tests/test_invoice_reversal.py
@@ -1,50 +1,54 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo.tests.common import SavepointCase, Form
+from odoo.tests.common import Form, SavepointCase
class TestInvoiceReversal(SavepointCase):
-
@classmethod
def setUpClass(cls):
super(TestInvoiceReversal, cls).setUpClass()
- cls.partner = cls.env['res.partner'].create({'name': 'Test'})
- cls.account_type_receivable = cls.env['account.account.type'].create({
- 'name': 'Test Receivable',
- 'type': 'receivable',
- })
- cls.account_type_regular = cls.env['account.account.type'].create({
- 'name': 'Test Regular',
- 'type': 'other',
- })
- cls.account_receivable = cls.env['account.account'].create({
- 'name': 'Test Receivable',
- 'code': 'TEST_AR',
- 'user_type_id': cls.account_type_receivable.id,
- 'reconcile': True,
- })
- cls.account_income = cls.env['account.account'].create({
- 'name': 'Test Income',
- 'code': 'TEST_IN',
- 'user_type_id': cls.account_type_regular.id,
- 'reconcile': False,
- })
- cls.sale_journal = cls.env['account.journal'].\
- search([('type', '=', 'sale')])[0]
- cls.invoice = cls.env['account.invoice'].create({
- 'name': "Test Customer Invoice",
- 'journal_id': cls.sale_journal.id,
- 'partner_id': cls.partner.id,
- 'account_id': cls.account_receivable.id,
- })
- cls.invoice_line = cls.env['account.invoice.line']
- cls.invoice_line1 = cls.invoice_line.create({
- 'invoice_id': cls.invoice.id,
- 'name': 'Line 1',
- 'price_unit': 200.0,
- 'account_id': cls.account_income.id,
- 'quantity': 1,
- })
+ cls.partner = cls.env["res.partner"].create({"name": "Test"})
+ cls.account_type_receivable = cls.env["account.account.type"].create(
+ {"name": "Test Receivable", "type": "receivable",}
+ )
+ cls.account_type_regular = cls.env["account.account.type"].create(
+ {"name": "Test Regular", "type": "other",}
+ )
+ cls.account_receivable = cls.env["account.account"].create(
+ {
+ "name": "Test Receivable",
+ "code": "TEST_AR",
+ "user_type_id": cls.account_type_receivable.id,
+ "reconcile": True,
+ }
+ )
+ cls.account_income = cls.env["account.account"].create(
+ {
+ "name": "Test Income",
+ "code": "TEST_IN",
+ "user_type_id": cls.account_type_regular.id,
+ "reconcile": False,
+ }
+ )
+ cls.sale_journal = cls.env["account.journal"].search([("type", "=", "sale")])[0]
+ cls.invoice = cls.env["account.invoice"].create(
+ {
+ "name": "Test Customer Invoice",
+ "journal_id": cls.sale_journal.id,
+ "partner_id": cls.partner.id,
+ "account_id": cls.account_receivable.id,
+ }
+ )
+ cls.invoice_line = cls.env["account.invoice.line"]
+ cls.invoice_line1 = cls.invoice_line.create(
+ {
+ "invoice_id": cls.invoice.id,
+ "name": "Line 1",
+ "price_unit": 200.0,
+ "account_id": cls.account_income.id,
+ "quantity": 1,
+ }
+ )
def test_journal_invoice_cancel_reversal(self):
""" Tests cancel with reversal, end result must follow,
@@ -52,30 +56,35 @@ class TestInvoiceReversal(SavepointCase):
- Status is changed to cancel
"""
# Test journal
- self.sale_journal.write({'update_posted': True,
- 'cancel_method': 'normal', })
+ self.sale_journal.write(
+ {"update_posted": True, "cancel_method": "normal",}
+ )
self.assertFalse(self.sale_journal.is_cancel_reversal)
- self.sale_journal.write({'update_posted': True,
- 'cancel_method': 'reversal',
- 'use_different_journal': True, })
+ self.sale_journal.write(
+ {
+ "update_posted": True,
+ "cancel_method": "reversal",
+ "use_different_journal": True,
+ }
+ )
# Open invoice
self.invoice.action_invoice_open()
move = self.invoice.move_id
# Click Cancel will open reverse document wizard
res = self.invoice.action_invoice_cancel()
- self.assertEqual(res['res_model'], 'reverse.account.document')
+ self.assertEqual(res["res_model"], "reverse.account.document")
# Cancel invoice
- ctx = {'active_model': 'account.invoice',
- 'active_ids': [self.invoice.id]}
- f = Form(self.env[res['res_model']].with_context(ctx))
+ ctx = {"active_model": "account.invoice", "active_ids": [self.invoice.id]}
+ f = Form(self.env[res["res_model"]].with_context(ctx))
cancel_wizard = f.save()
cancel_wizard.action_cancel()
reversed_move = move.reverse_entry_id
- move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id')
- reversed_move_reconcile = \
- reversed_move.mapped('line_ids').mapped('full_reconcile_id')
+ move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
+ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
+ "full_reconcile_id"
+ )
# Check
self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile)
self.assertEqual(move_reconcile, reversed_move_reconcile)
- self.assertEqual(self.invoice.state, 'cancel')
+ self.assertEqual(self.invoice.state, "cancel")
diff --git a/account_document_reversal/tests/test_payment_reversal.py b/account_document_reversal/tests/test_payment_reversal.py
index f37dd9bba..cc2d42edb 100644
--- a/account_document_reversal/tests/test_payment_reversal.py
+++ b/account_document_reversal/tests/test_payment_reversal.py
@@ -1,83 +1,89 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
import time
-from odoo.tests.common import SavepointCase, Form
+
from odoo.exceptions import UserError
+from odoo.tests.common import Form, SavepointCase
class TestPaymentReversal(SavepointCase):
-
@classmethod
def setUpClass(cls):
super(TestPaymentReversal, cls).setUpClass()
# Models
- cls.acc_bank_stmt_model = cls.env['account.bank.statement']
- cls.acc_bank_stmt_line_model = cls.env['account.bank.statement.line']
- cls.partner = cls.env['res.partner'].create({'name': 'Test'})
- cls.account_account_type_model = cls.env['account.account.type']
- cls.account_account_model = cls.env['account.account']
- cls.account_journal_model = cls.env['account.journal']
- cls.account_invoice_model = cls.env['account.invoice']
- cls.account_move_line_model = cls.env['account.move.line']
- cls.invoice_line_model = cls.env['account.invoice.line']
+ cls.acc_bank_stmt_model = cls.env["account.bank.statement"]
+ cls.acc_bank_stmt_line_model = cls.env["account.bank.statement.line"]
+ cls.partner = cls.env["res.partner"].create({"name": "Test"})
+ cls.account_account_type_model = cls.env["account.account.type"]
+ cls.account_account_model = cls.env["account.account"]
+ cls.account_journal_model = cls.env["account.journal"]
+ cls.account_invoice_model = cls.env["account.invoice"]
+ cls.account_move_line_model = cls.env["account.move.line"]
+ cls.invoice_line_model = cls.env["account.invoice.line"]
# Records
- cls.account_type_bank = cls.account_account_type_model.create({
- 'name': 'Test Bank',
- 'type': 'liquidity',
- })
- cls.account_type_receivable = cls.account_account_type_model.create({
- 'name': 'Test Receivable',
- 'type': 'receivable',
- })
- cls.account_type_regular = cls.account_account_type_model.create({
- 'name': 'Test Regular',
- 'type': 'other',
- })
- cls.account_bank = cls.account_account_model.create({
- 'name': 'Test Bank',
- 'code': 'TEST_BANK',
- 'user_type_id': cls.account_type_bank.id,
- 'reconcile': False,
- })
- cls.account_receivable = cls.account_account_model.create({
- 'name': 'Test Receivable',
- 'code': 'TEST_AR',
- 'user_type_id': cls.account_type_receivable.id,
- 'reconcile': True,
- })
- cls.account_income = cls.account_account_model.create({
- 'name': 'Test Income',
- 'code': 'TEST_IN',
- 'user_type_id': cls.account_type_regular.id,
- 'reconcile': False,
- })
- cls.account_expense = cls.account_account_model.create({
- 'name': 'Test Expense',
- 'code': 'TEST_EX',
- 'user_type_id': cls.account_type_regular.id,
- 'reconcile': False,
- })
- cls.bank_journal = cls.account_journal_model.create({
- 'name': 'Test Bank',
- 'code': 'TBK',
- 'type': 'bank'
- })
- cls.sale_journal = cls.account_journal_model.\
- search([('type', '=', 'sale')])[0]
- cls.invoice = cls.account_invoice_model.create({
- 'name': "Test Customer Invoice",
- 'journal_id': cls.sale_journal.id,
- 'partner_id': cls.partner.id,
- 'account_id': cls.account_receivable.id,
- })
+ cls.account_type_bank = cls.account_account_type_model.create(
+ {"name": "Test Bank", "type": "liquidity",}
+ )
+ cls.account_type_receivable = cls.account_account_type_model.create(
+ {"name": "Test Receivable", "type": "receivable",}
+ )
+ cls.account_type_regular = cls.account_account_type_model.create(
+ {"name": "Test Regular", "type": "other",}
+ )
+ cls.account_bank = cls.account_account_model.create(
+ {
+ "name": "Test Bank",
+ "code": "TEST_BANK",
+ "user_type_id": cls.account_type_bank.id,
+ "reconcile": False,
+ }
+ )
+ cls.account_receivable = cls.account_account_model.create(
+ {
+ "name": "Test Receivable",
+ "code": "TEST_AR",
+ "user_type_id": cls.account_type_receivable.id,
+ "reconcile": True,
+ }
+ )
+ cls.account_income = cls.account_account_model.create(
+ {
+ "name": "Test Income",
+ "code": "TEST_IN",
+ "user_type_id": cls.account_type_regular.id,
+ "reconcile": False,
+ }
+ )
+ cls.account_expense = cls.account_account_model.create(
+ {
+ "name": "Test Expense",
+ "code": "TEST_EX",
+ "user_type_id": cls.account_type_regular.id,
+ "reconcile": False,
+ }
+ )
+ cls.bank_journal = cls.account_journal_model.create(
+ {"name": "Test Bank", "code": "TBK", "type": "bank"}
+ )
+ cls.sale_journal = cls.account_journal_model.search([("type", "=", "sale")])[0]
+ cls.invoice = cls.account_invoice_model.create(
+ {
+ "name": "Test Customer Invoice",
+ "journal_id": cls.sale_journal.id,
+ "partner_id": cls.partner.id,
+ "account_id": cls.account_receivable.id,
+ }
+ )
- cls.invoice_line1 = cls.invoice_line_model.create({
- 'invoice_id': cls.invoice.id,
- 'name': 'Line 1',
- 'price_unit': 200.0,
- 'account_id': cls.account_income.id,
- 'quantity': 1,
- })
+ cls.invoice_line1 = cls.invoice_line_model.create(
+ {
+ "invoice_id": cls.invoice.id,
+ "name": "Line 1",
+ "price_unit": 200.0,
+ "account_id": cls.account_income.id,
+ "quantity": 1,
+ }
+ )
def test_payment_cancel_normal(self):
""" Tests that, if I don't use cancel reversal,
@@ -85,16 +91,16 @@ class TestPaymentReversal(SavepointCase):
- account move are removed completely
"""
# Test journal with normal cancel
- self.bank_journal.write({'update_posted': True,
- 'cancel_method': 'normal'})
+ self.bank_journal.write({"update_posted": True, "cancel_method": "normal"})
# Open invoice
self.invoice.action_invoice_open()
# Pay invoice
self.invoice.pay_and_reconcile(self.bank_journal, 200.0)
payment = self.invoice.payment_ids[0]
payment.cancel()
- move_lines = self.env['account.move.line'].\
- search([('payment_id', '=', payment.id)])
+ move_lines = self.env["account.move.line"].search(
+ [("payment_id", "=", payment.id)]
+ )
# All account moves are removed completely
self.assertFalse(move_lines)
@@ -106,36 +112,39 @@ class TestPaymentReversal(SavepointCase):
- The invoice is not reconciled with the payment anymore
"""
# Test journal
- self.bank_journal.write({'update_posted': True,
- 'cancel_method': 'reversal'})
+ self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
# Open invoice
self.invoice.action_invoice_open()
# Pay invoice
self.invoice.pay_and_reconcile(self.bank_journal, 200.0)
payment = self.invoice.payment_ids[0]
- move = self.env['account.move.line'].search(
- [('payment_id', '=', payment.id)], limit=1).move_id
+ move = (
+ self.env["account.move.line"]
+ .search([("payment_id", "=", payment.id)], limit=1)
+ .move_id
+ )
res = payment.cancel()
# Cancel payment
- ctx = {'active_model': 'account.payment',
- 'active_ids': [payment.id]}
- f = Form(self.env[res['res_model']].with_context(ctx))
- self.assertEqual(res['res_model'], 'reverse.account.document')
+ ctx = {"active_model": "account.payment", "active_ids": [payment.id]}
+ f = Form(self.env[res["res_model"]].with_context(ctx))
+ self.assertEqual(res["res_model"], "reverse.account.document")
cancel_wizard = f.save()
cancel_wizard.action_cancel()
- payment_moves = self.env['account.move.line'].search(
- [('payment_id', '=', payment.id)])
+ payment_moves = self.env["account.move.line"].search(
+ [("payment_id", "=", payment.id)]
+ )
self.assertFalse(payment_moves)
reversed_move = move.reverse_entry_id
- move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id')
- reversed_move_reconcile = \
- reversed_move.mapped('line_ids').mapped('full_reconcile_id')
+ move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
+ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
+ "full_reconcile_id"
+ )
# Check
self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile)
self.assertEqual(move_reconcile, reversed_move_reconcile)
- self.assertEqual(payment.state, 'cancelled')
- self.assertEqual(self.invoice.state, 'open')
+ self.assertEqual(payment.state, "cancelled")
+ self.assertEqual(self.invoice.state, "open")
def test_bank_statement_cancel_normal(self):
""" Tests that, if I don't use cancel reversal,
@@ -144,41 +153,50 @@ class TestPaymentReversal(SavepointCase):
- account move are removed completely
"""
# Test journal with normal cancel
- self.bank_journal.write({'update_posted': True,
- 'cancel_method': 'normal'})
+ self.bank_journal.write({"update_posted": True, "cancel_method": "normal"})
# Open invoice
self.invoice.action_invoice_open()
- bank_stmt = self.acc_bank_stmt_model.create({
- 'journal_id': self.bank_journal.id,
- 'date': time.strftime('%Y') + '-07-15',
- 'name': 'payment' + self.invoice.name
- })
+ bank_stmt = self.acc_bank_stmt_model.create(
+ {
+ "journal_id": self.bank_journal.id,
+ "date": time.strftime("%Y") + "-07-15",
+ "name": "payment" + self.invoice.name,
+ }
+ )
bank_stmt_line = self.acc_bank_stmt_line_model.create(
- {'name': 'payment',
- 'statement_id': bank_stmt.id,
- 'partner_id': self.partner.id,
- 'amount': 200,
- 'date': time.strftime('%Y') + '-07-15', })
+ {
+ "name": "payment",
+ "statement_id": bank_stmt.id,
+ "partner_id": self.partner.id,
+ "amount": 200,
+ "date": time.strftime("%Y") + "-07-15",
+ }
+ )
line_id = self.account_move_line_model
# reconcile the payment with the invoice
for l in self.invoice.move_id.line_ids:
if l.account_id.id == self.account_receivable.id:
line_id = l
break
- bank_stmt_line.process_reconciliation(counterpart_aml_dicts=[{
- 'move_line': line_id,
- 'account_id': self.account_income.id,
- 'debit': 0.0,
- 'credit': 200.0,
- 'name': 'test_reconciliation',
- }])
+ bank_stmt_line.process_reconciliation(
+ counterpart_aml_dicts=[
+ {
+ "move_line": line_id,
+ "account_id": self.account_income.id,
+ "debit": 0.0,
+ "credit": 200.0,
+ "name": "test_reconciliation",
+ }
+ ]
+ )
self.assertTrue(bank_stmt_line.journal_entry_ids)
original_move_lines = bank_stmt_line.journal_entry_ids
- self.assertTrue(original_move_lines.mapped('statement_id'))
+ self.assertTrue(original_move_lines.mapped("statement_id"))
# Cancel the statement line
bank_stmt_line.button_cancel_reconciliation()
- move_lines = self.env['account.move.line'].\
- search([('statement_id', '=', bank_stmt.id)])
+ move_lines = self.env["account.move.line"].search(
+ [("statement_id", "=", bank_stmt.id)]
+ )
# All account moves are removed completely
self.assertFalse(move_lines)
@@ -191,58 +209,69 @@ class TestPaymentReversal(SavepointCase):
- The line in the statement is ready to reconcile again
"""
# Test journal
- self.bank_journal.write({'update_posted': True,
- 'cancel_method': 'reversal'})
+ self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
# Open invoice
self.invoice.action_invoice_open()
- bank_stmt = self.acc_bank_stmt_model.create({
- 'journal_id': self.bank_journal.id,
- 'date': time.strftime('%Y') + '-07-15',
- 'name': 'payment' + self.invoice.name
- })
+ bank_stmt = self.acc_bank_stmt_model.create(
+ {
+ "journal_id": self.bank_journal.id,
+ "date": time.strftime("%Y") + "-07-15",
+ "name": "payment" + self.invoice.name,
+ }
+ )
bank_stmt_line = self.acc_bank_stmt_line_model.create(
- {'name': 'payment',
- 'statement_id': bank_stmt.id,
- 'partner_id': self.partner.id,
- 'amount': 200,
- 'date': time.strftime('%Y') + '-07-15', })
+ {
+ "name": "payment",
+ "statement_id": bank_stmt.id,
+ "partner_id": self.partner.id,
+ "amount": 200,
+ "date": time.strftime("%Y") + "-07-15",
+ }
+ )
line_id = self.account_move_line_model
# reconcile the payment with the invoice
for l in self.invoice.move_id.line_ids:
if l.account_id.id == self.account_receivable.id:
line_id = l
break
- bank_stmt_line.process_reconciliation(counterpart_aml_dicts=[{
- 'move_line': line_id,
- 'account_id': self.account_income.id,
- 'debit': 0.0,
- 'credit': 200.0,
- 'name': 'test_reconciliation',
- }])
+ bank_stmt_line.process_reconciliation(
+ counterpart_aml_dicts=[
+ {
+ "move_line": line_id,
+ "account_id": self.account_income.id,
+ "debit": 0.0,
+ "credit": 200.0,
+ "name": "test_reconciliation",
+ }
+ ]
+ )
self.assertTrue(bank_stmt_line.journal_entry_ids)
original_move_lines = bank_stmt_line.journal_entry_ids
- original_payment_id = original_move_lines.mapped('payment_id').id
- self.assertTrue(original_move_lines.mapped('statement_id'))
+ original_payment_id = original_move_lines.mapped("payment_id").id
+ self.assertTrue(original_move_lines.mapped("statement_id"))
# Cancel the statement line
res = bank_stmt_line.button_cancel_reconciliation()
- ctx = {'active_model': 'account.bank.statement.line',
- 'active_ids': [bank_stmt_line.id]}
- f = Form(self.env[res['res_model']].with_context(ctx))
- self.assertEqual(res['res_model'], 'reverse.account.document')
+ ctx = {
+ "active_model": "account.bank.statement.line",
+ "active_ids": [bank_stmt_line.id],
+ }
+ f = Form(self.env[res["res_model"]].with_context(ctx))
+ self.assertEqual(res["res_model"], "reverse.account.document")
cancel_wizard = f.save()
cancel_wizard.action_cancel()
self.assertFalse(bank_stmt_line.journal_entry_ids)
- payment = self.env['account.payment'].search(
- [('id', '=', original_payment_id)],
- limit=1)
+ payment = self.env["account.payment"].search(
+ [("id", "=", original_payment_id)], limit=1
+ )
self.assertFalse(payment)
- self.assertFalse(original_move_lines.mapped('statement_id'))
+ self.assertFalse(original_move_lines.mapped("statement_id"))
move = original_move_lines[0].move_id
reversed_move = move.reverse_entry_id
- move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id')
- reversed_move_reconcile = \
- reversed_move.mapped('line_ids').mapped('full_reconcile_id')
+ move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
+ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
+ "full_reconcile_id"
+ )
# Check
self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile)
@@ -257,51 +286,62 @@ class TestPaymentReversal(SavepointCase):
- The line in the statement is ready to reconcile again
"""
# Test journal
- self.bank_journal.write({'update_posted': True,
- 'cancel_method': 'reversal'})
+ self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
# Create a bank statement
- bank_stmt = self.acc_bank_stmt_model.create({
- 'journal_id': self.bank_journal.id,
- 'date': time.strftime('%Y') + '-07-15',
- 'name': 'payment' + self.invoice.name
- })
+ bank_stmt = self.acc_bank_stmt_model.create(
+ {
+ "journal_id": self.bank_journal.id,
+ "date": time.strftime("%Y") + "-07-15",
+ "name": "payment" + self.invoice.name,
+ }
+ )
bank_stmt_line = self.acc_bank_stmt_line_model.create(
- {'name': 'payment',
- 'statement_id': bank_stmt.id,
- 'partner_id': self.partner.id,
- 'amount': 200,
- 'date': time.strftime('%Y') + '-07-15', })
+ {
+ "name": "payment",
+ "statement_id": bank_stmt.id,
+ "partner_id": self.partner.id,
+ "amount": 200,
+ "date": time.strftime("%Y") + "-07-15",
+ }
+ )
line_id = self.account_move_line_model
- bank_stmt_line.process_reconciliation(new_aml_dicts=[{
- 'move_line': line_id,
- 'account_id': self.account_expense.id,
- 'debit': 200.0,
- 'name': 'test_expense_reconciliation',
- }])
+ bank_stmt_line.process_reconciliation(
+ new_aml_dicts=[
+ {
+ "move_line": line_id,
+ "account_id": self.account_expense.id,
+ "debit": 200.0,
+ "name": "test_expense_reconciliation",
+ }
+ ]
+ )
self.assertTrue(bank_stmt_line.journal_entry_ids)
original_move_lines = bank_stmt_line.journal_entry_ids
- original_payment_id = original_move_lines.mapped('payment_id').id
- self.assertTrue(original_move_lines.mapped('statement_id'))
+ original_payment_id = original_move_lines.mapped("payment_id").id
+ self.assertTrue(original_move_lines.mapped("statement_id"))
# Cancel the statement line
res = bank_stmt_line.button_cancel_reconciliation()
- ctx = {'active_model': 'account.bank.statement.line',
- 'active_ids': [bank_stmt_line.id]}
- f = Form(self.env[res['res_model']].with_context(ctx))
- self.assertEqual(res['res_model'], 'reverse.account.document')
+ ctx = {
+ "active_model": "account.bank.statement.line",
+ "active_ids": [bank_stmt_line.id],
+ }
+ f = Form(self.env[res["res_model"]].with_context(ctx))
+ self.assertEqual(res["res_model"], "reverse.account.document")
cancel_wizard = f.save()
cancel_wizard.action_cancel()
self.assertFalse(bank_stmt_line.journal_entry_ids)
- payment = self.env['account.payment'].search(
- [('id', '=', original_payment_id)],
- limit=1)
+ payment = self.env["account.payment"].search(
+ [("id", "=", original_payment_id)], limit=1
+ )
self.assertFalse(payment)
- self.assertFalse(original_move_lines.mapped('statement_id'))
+ self.assertFalse(original_move_lines.mapped("statement_id"))
move = original_move_lines[0].move_id
reversed_move = move.reverse_entry_id
- move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id')
- reversed_move_reconcile = \
- reversed_move.mapped('line_ids').mapped('full_reconcile_id')
+ move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
+ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
+ "full_reconcile_id"
+ )
# Check
self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile)
@@ -313,31 +353,39 @@ class TestPaymentReversal(SavepointCase):
- UserError will show
"""
# Test journal
- self.bank_journal.write({'update_posted': True,
- 'cancel_method': 'reversal'})
+ self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
# Create a bank statement
- bank_stmt = self.acc_bank_stmt_model.create({
- 'journal_id': self.bank_journal.id,
- 'date': time.strftime('%Y') + '-07-15',
- 'name': 'payment' + self.invoice.name
- })
+ bank_stmt = self.acc_bank_stmt_model.create(
+ {
+ "journal_id": self.bank_journal.id,
+ "date": time.strftime("%Y") + "-07-15",
+ "name": "payment" + self.invoice.name,
+ }
+ )
bank_stmt_line = self.acc_bank_stmt_line_model.create(
- {'name': 'payment',
- 'statement_id': bank_stmt.id,
- 'partner_id': self.partner.id,
- 'amount': 200,
- 'date': time.strftime('%Y') + '-07-15', })
+ {
+ "name": "payment",
+ "statement_id": bank_stmt.id,
+ "partner_id": self.partner.id,
+ "amount": 200,
+ "date": time.strftime("%Y") + "-07-15",
+ }
+ )
line_id = self.account_move_line_model
- bank_stmt_line.process_reconciliation(new_aml_dicts=[{
- 'move_line': line_id,
- 'account_id': self.account_expense.id,
- 'debit': 200.0,
- 'name': 'test_expense_reconciliation',
- }])
+ bank_stmt_line.process_reconciliation(
+ new_aml_dicts=[
+ {
+ "move_line": line_id,
+ "account_id": self.account_expense.id,
+ "debit": 200.0,
+ "name": "test_expense_reconciliation",
+ }
+ ]
+ )
bank_stmt.balance_end_real = 200.00
bank_stmt.check_confirm_bank()
- self.assertEqual(bank_stmt.state, 'confirm')
+ self.assertEqual(bank_stmt.state, "confirm")
with self.assertRaises(UserError):
bank_stmt_line.button_cancel_reconciliation()
diff --git a/account_document_reversal/views/account_view.xml b/account_document_reversal/views/account_view.xml
index 9ef29e4db..0a0cb4c15 100644
--- a/account_document_reversal/views/account_view.xml
+++ b/account_document_reversal/views/account_view.xml
@@ -1,34 +1,49 @@
-
+
-
account.journal.form
account.journal
-
+
-
-
-
+
+
+
-
account.move.form.inherit
account.move
-
+
-
+
-
diff --git a/account_document_reversal/wizard/reverse_account_document.py b/account_document_reversal/wizard/reverse_account_document.py
index 2d46a106f..4e578ecc3 100644
--- a/account_document_reversal/wizard/reverse_account_document.py
+++ b/account_document_reversal/wizard/reverse_account_document.py
@@ -1,48 +1,47 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
-from odoo import models, fields, api
+from odoo import api, fields, models
class ReverseAccountDocument(models.TransientModel):
"""
Document reversal wizard, it cancel by reverse document journal entries
"""
- _name = 'reverse.account.document'
- _description = 'Account Document Reversal'
+
+ _name = "reverse.account.document"
+ _description = "Account Document Reversal"
date = fields.Date(
- string='Reversal date',
- default=fields.Date.context_today,
- required=True,
+ string="Reversal date", default=fields.Date.context_today, required=True,
)
use_different_journal = fields.Boolean(
- string='Use different journal for reversal',
- help="Checked, if the journal of underlineing document is checked."
+ string="Use different journal for reversal",
+ help="Checked, if the journal of underlineing document is checked.",
)
journal_id = fields.Many2one(
- 'account.journal',
- string='Reversal Journal',
- help='If empty, uses the journal of the journal entry to be reversed.',
+ "account.journal",
+ string="Reversal Journal",
+ help="If empty, uses the journal of the journal entry to be reversed.",
)
@api.model
def default_get(self, default_fields):
- model = self._context.get('active_model')
- active_ids = self._context.get('active_ids')
+ model = self._context.get("active_model")
+ active_ids = self._context.get("active_ids")
documents = self.env[model].browse(active_ids)
res = super().default_get(default_fields)
if documents:
- if 'journal_id' in documents[0]:
+ if "journal_id" in documents[0]:
journal = documents[0].journal_id
if journal.use_different_journal:
- res['use_different_journal'] = True
- res['journal_id'] = journal.reversal_journal_id.id
+ res["use_different_journal"] = True
+ res["journal_id"] = journal.reversal_journal_id.id
return res
@api.multi
def action_cancel(self):
- model = self._context.get('active_model')
- active_ids = self._context.get('active_ids')
+ model = self._context.get("active_model")
+ active_ids = self._context.get("active_ids")
documents = self.env[model].browse(active_ids)
documents.action_document_reversal(self.date, self.journal_id)
- return {'type': 'ir.actions.act_window_close'}
+ return {"type": "ir.actions.act_window_close"}
diff --git a/account_document_reversal/wizard/reverse_account_document_wizard.xml b/account_document_reversal/wizard/reverse_account_document_wizard.xml
index 7bb904063..4e0000ec2 100644
--- a/account_document_reversal/wizard/reverse_account_document_wizard.xml
+++ b/account_document_reversal/wizard/reverse_account_document_wizard.xml
@@ -1,4 +1,4 @@
-
+
@@ -8,27 +8,38 @@
+
-
Document Cancel
reverse.account.document
form
tree,form
-
+
new