[IMP] account_document_reversal: black, isort

This commit is contained in:
kittiu
2020-03-26 11:38:12 +07:00
committed by OCA Transbot
parent b78efe0593
commit 0cbb026770
12 changed files with 481 additions and 393 deletions

View File

@@ -2,22 +2,17 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
{ {
'name': 'Account Document Reversal', "name": "Account Document Reversal",
'summary': 'Create reversed journal entries when cancel document', "summary": "Create reversed journal entries when cancel document",
'version': '12.0.1.0.0', "version": "12.0.1.0.0",
'author': 'Ecosoft,' "author": "Ecosoft," "Eficent," "Odoo Community Association (OCA)",
'Eficent,' "website": "https://github.com/OCA/account-financial-tools",
'Odoo Community Association (OCA)', "category": "Accounting & Finance",
'website': 'https://github.com/OCA/account-financial-tools', "depends": ["account_cancel"],
'category': 'Accounting & Finance', "data": ["wizard/reverse_account_document_wizard.xml", "views/account_view.xml",],
'depends': ['account_cancel'], "license": "AGPL-3",
'data': [ "installable": True,
'wizard/reverse_account_document_wizard.xml', "application": False,
'views/account_view.xml', "development_status": "beta",
], "maintainers": ["kittiu"],
'license': 'AGPL-3',
'installable': True,
'application': False,
'development_status': 'beta',
'maintainers': ['kittiu'],
} }

View File

@@ -1,35 +1,38 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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): class AccountJournal(models.Model):
_inherit = 'account.journal' _inherit = "account.journal"
cancel_method = fields.Selection( cancel_method = fields.Selection(
[('normal', 'Normal (delete journal entries if exists)'), [
('reversal', 'Reversal (create reversed journal entries)')], ("normal", "Normal (delete journal entries if exists)"),
string='Cancel Method', ("reversal", "Reversal (create reversed journal entries)"),
default='normal', ],
string="Cancel Method",
default="normal",
required=True, required=True,
) )
is_cancel_reversal = fields.Boolean( is_cancel_reversal = fields.Boolean(
string='Use Cancel Reversal', string="Use Cancel Reversal",
compute='_compute_is_cancel_reversal', compute="_compute_is_cancel_reversal",
help="True, when journal allow cancel entries with method is reversal", help="True, when journal allow cancel entries with method is reversal",
) )
use_different_journal = fields.Boolean( 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", help="If checked, reversal wizard will show field Reversal Journal",
) )
reversal_journal_id = fields.Many2one( reversal_journal_id = fields.Many2one(
'account.journal', "account.journal",
string='Default Reversal Journal', string="Default Reversal Journal",
help="Journal in this field will show in reversal wizard as default", help="Journal in this field will show in reversal wizard as default",
) )
@api.multi @api.multi
def _compute_is_cancel_reversal(self): def _compute_is_cancel_reversal(self):
for rec in self: for rec in self:
rec.is_cancel_reversal = \ rec.is_cancel_reversal = (
rec.update_posted and rec.cancel_method == 'reversal' rec.update_posted and rec.cancel_method == "reversal"
)

View File

@@ -1,23 +1,23 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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 from odoo.exceptions import UserError
class AccountPayment(models.Model): class AccountPayment(models.Model):
_name = 'account.bank.statement.line' _name = "account.bank.statement.line"
_inherit = ['account.bank.statement.line', 'account.document.reversal'] _inherit = ["account.bank.statement.line", "account.document.reversal"]
@api.multi @api.multi
def button_cancel_reconciliation(self): def button_cancel_reconciliation(self):
""" If cancel method is to reverse, use document reversal wizard """ """ If cancel method is to reverse, use document reversal wizard """
cancel_reversal = all(self.mapped('journal_entry_ids.move_id.' cancel_reversal = all(
'journal_id.is_cancel_reversal')) self.mapped("journal_entry_ids.move_id." "journal_id.is_cancel_reversal")
states = self.mapped('statement_id.state') )
states = self.mapped("statement_id.state")
if cancel_reversal: if cancel_reversal:
if not all(st == 'open' for st in states): if not all(st == "open" for st in states):
raise UserError( raise UserError(_("Only new bank statement can be cancelled"))
_('Only new bank statement can be cancelled'))
return self.reverse_document_wizard() return self.reverse_document_wizard()
return super().button_cancel_reconciliation() return super().button_cancel_reconciliation()
@@ -25,41 +25,42 @@ class AccountPayment(models.Model):
def action_document_reversal(self, date=None, journal_id=None): def action_document_reversal(self, date=None, journal_id=None):
""" Reverse all moves related to this statement + delete payment """ """ Reverse all moves related to this statement + delete payment """
# This part is from button_cancel_reconciliation() # This part is from button_cancel_reconciliation()
aml_to_unbind = self.env['account.move.line'] aml_to_unbind = self.env["account.move.line"]
aml_to_cancel = self.env['account.move.line'] aml_to_cancel = self.env["account.move.line"]
payment_to_unreconcile = self.env['account.payment'] payment_to_unreconcile = self.env["account.payment"]
payment_to_cancel = self.env['account.payment'] payment_to_cancel = self.env["account.payment"]
for st_line in self: for st_line in self:
aml_to_unbind |= st_line.journal_entry_ids aml_to_unbind |= st_line.journal_entry_ids
for line in st_line.journal_entry_ids: for line in st_line.journal_entry_ids:
payment_to_unreconcile |= line.payment_id payment_to_unreconcile |= line.payment_id
if st_line.move_name and \ if (
line.payment_id.payment_reference == st_line.move_name: 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 # there can be several moves linked to a statement line but
# maximum one created by the line itself # maximum one created by the line itself
aml_to_cancel |= line aml_to_cancel |= line
payment_to_cancel |= line.payment_id payment_to_cancel |= line.payment_id
aml_to_unbind = aml_to_unbind - aml_to_cancel aml_to_unbind = aml_to_unbind - aml_to_cancel
if aml_to_unbind: 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 payment_to_unreconcile = payment_to_unreconcile - payment_to_cancel
if payment_to_unreconcile: if payment_to_unreconcile:
payment_to_unreconcile.unreconcile() payment_to_unreconcile.unreconcile()
# -- # --
# Set all moves to unreconciled # Set all moves to unreconciled
aml_to_cancel.filtered(lambda x: aml_to_cancel.filtered(lambda x: x.account_id.reconcile).remove_move_reconcile()
x.account_id.reconcile).remove_move_reconcile() moves = aml_to_cancel.mapped("move_id")
moves = aml_to_cancel.mapped('move_id')
# Important to remove relation with move.line before reverse # Important to remove relation with move.line before reverse
aml_to_cancel.write({'payment_id': False, aml_to_cancel.write(
'statement_id': False, {"payment_id": False, "statement_id": False, "statement_line_id": False}
'statement_line_id': False}) )
# Create reverse entries # Create reverse entries
moves.reverse_moves(date, journal_id) moves.reverse_moves(date, journal_id)
# Delete related payments # Delete related payments
if payment_to_cancel: if payment_to_cancel:
payment_to_cancel.unlink() payment_to_cancel.unlink()
# Unlink from statement line # Unlink from statement line
self.write({'move_name': False}) self.write({"move_name": False})
return True return True

View File

@@ -1,17 +1,18 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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): class AccountDocumentReversal(models.AbstractModel):
_name = 'account.document.reversal' _name = "account.document.reversal"
_description = 'Abstract Module for Document Reversal' _description = "Abstract Module for Document Reversal"
@api.model @api.model
def reverse_document_wizard(self): def reverse_document_wizard(self):
""" Return Wizard to Cancel Document """ """ Return Wizard to Cancel Document """
action = self.env.ref('account_document_reversal.' action = self.env.ref(
'action_view_reverse_account_document') "account_document_reversal." "action_view_reverse_account_document"
)
vals = action.read()[0] vals = action.read()[0]
return vals return vals

View File

@@ -1,12 +1,12 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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, UserError from odoo.exceptions import UserError, ValidationError
class AccountInvoice(models.Model): class AccountInvoice(models.Model):
_name = 'account.invoice' _name = "account.invoice"
_inherit = ['account.invoice', 'account.document.reversal'] _inherit = ["account.invoice", "account.document.reversal"]
@api.multi @api.multi
def action_invoice_cancel(self): def action_invoice_cancel(self):
@@ -14,16 +14,20 @@ class AccountInvoice(models.Model):
* Draft invoice, fall back to standard invoice cancel * Draft invoice, fall back to standard invoice cancel
* Non draft, must be fully open (not even partial reconciled) to cancel * Non draft, must be fully open (not even partial reconciled) to cancel
""" """
cancel_reversal = all(self.mapped('journal_id.is_cancel_reversal')) cancel_reversal = all(self.mapped("journal_id.is_cancel_reversal"))
states = self.mapped('state') states = self.mapped("state")
if cancel_reversal and 'draft' not in states: if cancel_reversal and "draft" not in states:
if not all(st == 'open' for st in states) or \ 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_debit_ids")
self.mapped('move_id.line_ids.matched_credit_ids')): | self.mapped("move_id.line_ids.matched_credit_ids")
):
raise UserError( raise UserError(
_('Only fully unpaid invoice can be cancelled.\n' _(
'To cancel this invoice, make sure all payment(s) ' "Only fully unpaid invoice can be cancelled.\n"
'are also cancelled.')) "To cancel this invoice, make sure all payment(s) "
"are also cancelled."
)
)
return self.reverse_document_wizard() return self.reverse_document_wizard()
return super().action_invoice_cancel() return super().action_invoice_cancel()
@@ -31,22 +35,24 @@ class AccountInvoice(models.Model):
def action_document_reversal(self, date=None, journal_id=None): def action_document_reversal(self, date=None, journal_id=None):
""" Reverse all moves related to this invoice + set state to cancel """ """ Reverse all moves related to this invoice + set state to cancel """
# Check document state # Check document state
if 'cancel' in self.mapped('state'): if "cancel" in self.mapped("state"):
raise ValidationError( raise ValidationError(_("You are trying to cancel the cancelled document"))
_('You are trying to cancel the cancelled document')) MoveLine = self.env["account.move.line"]
MoveLine = self.env['account.move.line'] move_lines = MoveLine.search([("invoice_id", "in", self.ids)])
move_lines = MoveLine.search([('invoice_id', 'in', self.ids)]) moves = move_lines.mapped("move_id")
moves = move_lines.mapped('move_id')
# Set all moves to unreconciled # Set all moves to unreconciled
move_lines.filtered(lambda x: move_lines.filtered(lambda x: x.account_id.reconcile).remove_move_reconcile()
x.account_id.reconcile).remove_move_reconcile()
# Important to remove relation with move.line before reverse # Important to remove relation with move.line before reverse
move_lines.write({'invoice_id': False}) move_lines.write({"invoice_id": False})
# Create reverse entries # Create reverse entries
moves.reverse_moves(date, journal_id) moves.reverse_moves(date, journal_id)
# Set state cancelled and unlink with account.move # Set state cancelled and unlink with account.move
self.write({'move_id': False, self.write(
'move_name': False, {
'reference': False, "move_id": False,
'state': 'cancel'}) "move_name": False,
"reference": False,
"state": "cancel",
}
)
return True return True

View File

@@ -1,24 +1,26 @@
# Copyright 2019 Eficent Business and IT Consulting Services, S.L. # Copyright 2019 Eficent Business and IT Consulting Services, S.L.
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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 from odoo.exceptions import ValidationError
class AccountMove(models.Model): class AccountMove(models.Model):
_name = 'account.move' _name = "account.move"
_inherit = ['account.move', 'account.document.reversal'] _inherit = ["account.move", "account.document.reversal"]
is_cancel_reversal = fields.Boolean( is_cancel_reversal = fields.Boolean(
string='Use Cancel Reversal', string="Use Cancel Reversal", related="journal_id.is_cancel_reversal",
related='journal_id.is_cancel_reversal',
) )
@api.multi @api.multi
def button_cancel(self): def button_cancel(self):
""" Do not allow using this button for cancel with reversal """ """ 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: if cancel_reversal:
raise ValidationError( 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() return super().button_cancel()

View File

@@ -1,20 +1,21 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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 from odoo.exceptions import ValidationError
class AccountPayment(models.Model): class AccountPayment(models.Model):
_name = 'account.payment' _name = "account.payment"
_inherit = ['account.payment', 'account.document.reversal'] _inherit = ["account.payment", "account.document.reversal"]
@api.multi @api.multi
def cancel(self): def cancel(self):
""" If cancel method is to reverse, use document reversal wizard """ """ If cancel method is to reverse, use document reversal wizard """
cancel_reversal = all( cancel_reversal = all(
self.mapped('move_line_ids.move_id.journal_id.is_cancel_reversal')) self.mapped("move_line_ids.move_id.journal_id.is_cancel_reversal")
states = self.mapped('state') )
if cancel_reversal and 'draft' not in states: states = self.mapped("state")
if cancel_reversal and "draft" not in states:
return self.reverse_document_wizard() return self.reverse_document_wizard()
return super().cancel() return super().cancel()
@@ -22,19 +23,16 @@ class AccountPayment(models.Model):
def action_document_reversal(self, date=None, journal_id=None): def action_document_reversal(self, date=None, journal_id=None):
""" Reverse all moves related to this payment + set state to cancel """ """ Reverse all moves related to this payment + set state to cancel """
# Check document state # Check document state
if 'cancelled' in self.mapped('state'): if "cancelled" in self.mapped("state"):
raise ValidationError( raise ValidationError(_("You are trying to cancel the cancelled document"))
_('You are trying to cancel the cancelled document')) move_lines = self.mapped("move_line_ids")
move_lines = self.mapped('move_line_ids') moves = move_lines.mapped("move_id")
moves = move_lines.mapped('move_id')
# Set all moves to unreconciled # Set all moves to unreconciled
move_lines.filtered(lambda x: move_lines.filtered(lambda x: x.account_id.reconcile).remove_move_reconcile()
x.account_id.reconcile).remove_move_reconcile()
# Important to remove relation with move.line before reverse # Important to remove relation with move.line before reverse
move_lines.write({'payment_id': False}) move_lines.write({"payment_id": False})
# Create reverse entries # Create reverse entries
moves.reverse_moves(date, journal_id) moves.reverse_moves(date, journal_id)
# Set state cancelled and unlink with account.move # Set state cancelled and unlink with account.move
self.write({'move_name': False, self.write({"move_name": False, "state": "cancelled"})
'state': 'cancelled'})
return True return True

View File

@@ -1,50 +1,54 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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): class TestInvoiceReversal(SavepointCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super(TestInvoiceReversal, cls).setUpClass() super(TestInvoiceReversal, cls).setUpClass()
cls.partner = cls.env['res.partner'].create({'name': 'Test'}) cls.partner = cls.env["res.partner"].create({"name": "Test"})
cls.account_type_receivable = cls.env['account.account.type'].create({ cls.account_type_receivable = cls.env["account.account.type"].create(
'name': 'Test Receivable', {"name": "Test Receivable", "type": "receivable",}
'type': 'receivable', )
}) cls.account_type_regular = cls.env["account.account.type"].create(
cls.account_type_regular = cls.env['account.account.type'].create({ {"name": "Test Regular", "type": "other",}
'name': 'Test Regular', )
'type': 'other', cls.account_receivable = cls.env["account.account"].create(
}) {
cls.account_receivable = cls.env['account.account'].create({ "name": "Test Receivable",
'name': 'Test Receivable', "code": "TEST_AR",
'code': 'TEST_AR', "user_type_id": cls.account_type_receivable.id,
'user_type_id': cls.account_type_receivable.id, "reconcile": True,
'reconcile': True, }
}) )
cls.account_income = cls.env['account.account'].create({ cls.account_income = cls.env["account.account"].create(
'name': 'Test Income', {
'code': 'TEST_IN', "name": "Test Income",
'user_type_id': cls.account_type_regular.id, "code": "TEST_IN",
'reconcile': False, "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({ cls.sale_journal = cls.env["account.journal"].search([("type", "=", "sale")])[0]
'name': "Test Customer Invoice", cls.invoice = cls.env["account.invoice"].create(
'journal_id': cls.sale_journal.id, {
'partner_id': cls.partner.id, "name": "Test Customer Invoice",
'account_id': cls.account_receivable.id, "journal_id": cls.sale_journal.id,
}) "partner_id": cls.partner.id,
cls.invoice_line = cls.env['account.invoice.line'] "account_id": cls.account_receivable.id,
cls.invoice_line1 = cls.invoice_line.create({ }
'invoice_id': cls.invoice.id, )
'name': 'Line 1', cls.invoice_line = cls.env["account.invoice.line"]
'price_unit': 200.0, cls.invoice_line1 = cls.invoice_line.create(
'account_id': cls.account_income.id, {
'quantity': 1, "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): def test_journal_invoice_cancel_reversal(self):
""" Tests cancel with reversal, end result must follow, """ Tests cancel with reversal, end result must follow,
@@ -52,30 +56,35 @@ class TestInvoiceReversal(SavepointCase):
- Status is changed to cancel - Status is changed to cancel
""" """
# Test journal # Test journal
self.sale_journal.write({'update_posted': True, self.sale_journal.write(
'cancel_method': 'normal', }) {"update_posted": True, "cancel_method": "normal",}
)
self.assertFalse(self.sale_journal.is_cancel_reversal) self.assertFalse(self.sale_journal.is_cancel_reversal)
self.sale_journal.write({'update_posted': True, self.sale_journal.write(
'cancel_method': 'reversal', {
'use_different_journal': True, }) "update_posted": True,
"cancel_method": "reversal",
"use_different_journal": True,
}
)
# Open invoice # Open invoice
self.invoice.action_invoice_open() self.invoice.action_invoice_open()
move = self.invoice.move_id move = self.invoice.move_id
# Click Cancel will open reverse document wizard # Click Cancel will open reverse document wizard
res = self.invoice.action_invoice_cancel() res = self.invoice.action_invoice_cancel()
self.assertEqual(res['res_model'], 'reverse.account.document') self.assertEqual(res["res_model"], "reverse.account.document")
# Cancel invoice # Cancel invoice
ctx = {'active_model': 'account.invoice', ctx = {"active_model": "account.invoice", "active_ids": [self.invoice.id]}
'active_ids': [self.invoice.id]} f = Form(self.env[res["res_model"]].with_context(ctx))
f = Form(self.env[res['res_model']].with_context(ctx))
cancel_wizard = f.save() cancel_wizard = f.save()
cancel_wizard.action_cancel() cancel_wizard.action_cancel()
reversed_move = move.reverse_entry_id reversed_move = move.reverse_entry_id
move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id') move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
reversed_move_reconcile = \ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
reversed_move.mapped('line_ids').mapped('full_reconcile_id') "full_reconcile_id"
)
# Check # Check
self.assertTrue(move_reconcile) self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile) self.assertTrue(reversed_move_reconcile)
self.assertEqual(move_reconcile, reversed_move_reconcile) self.assertEqual(move_reconcile, reversed_move_reconcile)
self.assertEqual(self.invoice.state, 'cancel') self.assertEqual(self.invoice.state, "cancel")

View File

@@ -1,83 +1,89 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html)
import time import time
from odoo.tests.common import SavepointCase, Form
from odoo.exceptions import UserError from odoo.exceptions import UserError
from odoo.tests.common import Form, SavepointCase
class TestPaymentReversal(SavepointCase): class TestPaymentReversal(SavepointCase):
@classmethod @classmethod
def setUpClass(cls): def setUpClass(cls):
super(TestPaymentReversal, cls).setUpClass() super(TestPaymentReversal, cls).setUpClass()
# Models # Models
cls.acc_bank_stmt_model = cls.env['account.bank.statement'] cls.acc_bank_stmt_model = cls.env["account.bank.statement"]
cls.acc_bank_stmt_line_model = cls.env['account.bank.statement.line'] cls.acc_bank_stmt_line_model = cls.env["account.bank.statement.line"]
cls.partner = cls.env['res.partner'].create({'name': 'Test'}) cls.partner = cls.env["res.partner"].create({"name": "Test"})
cls.account_account_type_model = cls.env['account.account.type'] cls.account_account_type_model = cls.env["account.account.type"]
cls.account_account_model = cls.env['account.account'] cls.account_account_model = cls.env["account.account"]
cls.account_journal_model = cls.env['account.journal'] cls.account_journal_model = cls.env["account.journal"]
cls.account_invoice_model = cls.env['account.invoice'] cls.account_invoice_model = cls.env["account.invoice"]
cls.account_move_line_model = cls.env['account.move.line'] cls.account_move_line_model = cls.env["account.move.line"]
cls.invoice_line_model = cls.env['account.invoice.line'] cls.invoice_line_model = cls.env["account.invoice.line"]
# Records # Records
cls.account_type_bank = cls.account_account_type_model.create({ cls.account_type_bank = cls.account_account_type_model.create(
'name': 'Test Bank', {"name": "Test Bank", "type": "liquidity",}
'type': 'liquidity', )
}) cls.account_type_receivable = cls.account_account_type_model.create(
cls.account_type_receivable = cls.account_account_type_model.create({ {"name": "Test Receivable", "type": "receivable",}
'name': 'Test Receivable', )
'type': 'receivable', cls.account_type_regular = cls.account_account_type_model.create(
}) {"name": "Test Regular", "type": "other",}
cls.account_type_regular = cls.account_account_type_model.create({ )
'name': 'Test Regular', cls.account_bank = cls.account_account_model.create(
'type': 'other', {
}) "name": "Test Bank",
cls.account_bank = cls.account_account_model.create({ "code": "TEST_BANK",
'name': 'Test Bank', "user_type_id": cls.account_type_bank.id,
'code': 'TEST_BANK', "reconcile": False,
'user_type_id': cls.account_type_bank.id, }
'reconcile': False, )
}) cls.account_receivable = cls.account_account_model.create(
cls.account_receivable = cls.account_account_model.create({ {
'name': 'Test Receivable', "name": "Test Receivable",
'code': 'TEST_AR', "code": "TEST_AR",
'user_type_id': cls.account_type_receivable.id, "user_type_id": cls.account_type_receivable.id,
'reconcile': True, "reconcile": True,
}) }
cls.account_income = cls.account_account_model.create({ )
'name': 'Test Income', cls.account_income = cls.account_account_model.create(
'code': 'TEST_IN', {
'user_type_id': cls.account_type_regular.id, "name": "Test Income",
'reconcile': False, "code": "TEST_IN",
}) "user_type_id": cls.account_type_regular.id,
cls.account_expense = cls.account_account_model.create({ "reconcile": False,
'name': 'Test Expense', }
'code': 'TEST_EX', )
'user_type_id': cls.account_type_regular.id, cls.account_expense = cls.account_account_model.create(
'reconcile': False, {
}) "name": "Test Expense",
cls.bank_journal = cls.account_journal_model.create({ "code": "TEST_EX",
'name': 'Test Bank', "user_type_id": cls.account_type_regular.id,
'code': 'TBK', "reconcile": False,
'type': 'bank' }
}) )
cls.sale_journal = cls.account_journal_model.\ cls.bank_journal = cls.account_journal_model.create(
search([('type', '=', 'sale')])[0] {"name": "Test Bank", "code": "TBK", "type": "bank"}
cls.invoice = cls.account_invoice_model.create({ )
'name': "Test Customer Invoice", cls.sale_journal = cls.account_journal_model.search([("type", "=", "sale")])[0]
'journal_id': cls.sale_journal.id, cls.invoice = cls.account_invoice_model.create(
'partner_id': cls.partner.id, {
'account_id': cls.account_receivable.id, "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({ cls.invoice_line1 = cls.invoice_line_model.create(
'invoice_id': cls.invoice.id, {
'name': 'Line 1', "invoice_id": cls.invoice.id,
'price_unit': 200.0, "name": "Line 1",
'account_id': cls.account_income.id, "price_unit": 200.0,
'quantity': 1, "account_id": cls.account_income.id,
}) "quantity": 1,
}
)
def test_payment_cancel_normal(self): def test_payment_cancel_normal(self):
""" Tests that, if I don't use cancel reversal, """ Tests that, if I don't use cancel reversal,
@@ -85,16 +91,16 @@ class TestPaymentReversal(SavepointCase):
- account move are removed completely - account move are removed completely
""" """
# Test journal with normal cancel # Test journal with normal cancel
self.bank_journal.write({'update_posted': True, self.bank_journal.write({"update_posted": True, "cancel_method": "normal"})
'cancel_method': 'normal'})
# Open invoice # Open invoice
self.invoice.action_invoice_open() self.invoice.action_invoice_open()
# Pay invoice # Pay invoice
self.invoice.pay_and_reconcile(self.bank_journal, 200.0) self.invoice.pay_and_reconcile(self.bank_journal, 200.0)
payment = self.invoice.payment_ids[0] payment = self.invoice.payment_ids[0]
payment.cancel() payment.cancel()
move_lines = self.env['account.move.line'].\ move_lines = self.env["account.move.line"].search(
search([('payment_id', '=', payment.id)]) [("payment_id", "=", payment.id)]
)
# All account moves are removed completely # All account moves are removed completely
self.assertFalse(move_lines) self.assertFalse(move_lines)
@@ -106,36 +112,39 @@ class TestPaymentReversal(SavepointCase):
- The invoice is not reconciled with the payment anymore - The invoice is not reconciled with the payment anymore
""" """
# Test journal # Test journal
self.bank_journal.write({'update_posted': True, self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
'cancel_method': 'reversal'})
# Open invoice # Open invoice
self.invoice.action_invoice_open() self.invoice.action_invoice_open()
# Pay invoice # Pay invoice
self.invoice.pay_and_reconcile(self.bank_journal, 200.0) self.invoice.pay_and_reconcile(self.bank_journal, 200.0)
payment = self.invoice.payment_ids[0] payment = self.invoice.payment_ids[0]
move = self.env['account.move.line'].search( move = (
[('payment_id', '=', payment.id)], limit=1).move_id self.env["account.move.line"]
.search([("payment_id", "=", payment.id)], limit=1)
.move_id
)
res = payment.cancel() res = payment.cancel()
# Cancel payment # Cancel payment
ctx = {'active_model': 'account.payment', ctx = {"active_model": "account.payment", "active_ids": [payment.id]}
'active_ids': [payment.id]} f = Form(self.env[res["res_model"]].with_context(ctx))
f = Form(self.env[res['res_model']].with_context(ctx)) self.assertEqual(res["res_model"], "reverse.account.document")
self.assertEqual(res['res_model'], 'reverse.account.document')
cancel_wizard = f.save() cancel_wizard = f.save()
cancel_wizard.action_cancel() cancel_wizard.action_cancel()
payment_moves = self.env['account.move.line'].search( payment_moves = self.env["account.move.line"].search(
[('payment_id', '=', payment.id)]) [("payment_id", "=", payment.id)]
)
self.assertFalse(payment_moves) self.assertFalse(payment_moves)
reversed_move = move.reverse_entry_id reversed_move = move.reverse_entry_id
move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id') move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
reversed_move_reconcile = \ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
reversed_move.mapped('line_ids').mapped('full_reconcile_id') "full_reconcile_id"
)
# Check # Check
self.assertTrue(move_reconcile) self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile) self.assertTrue(reversed_move_reconcile)
self.assertEqual(move_reconcile, reversed_move_reconcile) self.assertEqual(move_reconcile, reversed_move_reconcile)
self.assertEqual(payment.state, 'cancelled') self.assertEqual(payment.state, "cancelled")
self.assertEqual(self.invoice.state, 'open') self.assertEqual(self.invoice.state, "open")
def test_bank_statement_cancel_normal(self): def test_bank_statement_cancel_normal(self):
""" Tests that, if I don't use cancel reversal, """ Tests that, if I don't use cancel reversal,
@@ -144,41 +153,50 @@ class TestPaymentReversal(SavepointCase):
- account move are removed completely - account move are removed completely
""" """
# Test journal with normal cancel # Test journal with normal cancel
self.bank_journal.write({'update_posted': True, self.bank_journal.write({"update_posted": True, "cancel_method": "normal"})
'cancel_method': 'normal'})
# Open invoice # Open invoice
self.invoice.action_invoice_open() self.invoice.action_invoice_open()
bank_stmt = self.acc_bank_stmt_model.create({ bank_stmt = self.acc_bank_stmt_model.create(
'journal_id': self.bank_journal.id, {
'date': time.strftime('%Y') + '-07-15', "journal_id": self.bank_journal.id,
'name': 'payment' + self.invoice.name "date": time.strftime("%Y") + "-07-15",
}) "name": "payment" + self.invoice.name,
}
)
bank_stmt_line = self.acc_bank_stmt_line_model.create( bank_stmt_line = self.acc_bank_stmt_line_model.create(
{'name': 'payment', {
'statement_id': bank_stmt.id, "name": "payment",
'partner_id': self.partner.id, "statement_id": bank_stmt.id,
'amount': 200, "partner_id": self.partner.id,
'date': time.strftime('%Y') + '-07-15', }) "amount": 200,
"date": time.strftime("%Y") + "-07-15",
}
)
line_id = self.account_move_line_model line_id = self.account_move_line_model
# reconcile the payment with the invoice # reconcile the payment with the invoice
for l in self.invoice.move_id.line_ids: for l in self.invoice.move_id.line_ids:
if l.account_id.id == self.account_receivable.id: if l.account_id.id == self.account_receivable.id:
line_id = l line_id = l
break break
bank_stmt_line.process_reconciliation(counterpart_aml_dicts=[{ bank_stmt_line.process_reconciliation(
'move_line': line_id, counterpart_aml_dicts=[
'account_id': self.account_income.id, {
'debit': 0.0, "move_line": line_id,
'credit': 200.0, "account_id": self.account_income.id,
'name': 'test_reconciliation', "debit": 0.0,
}]) "credit": 200.0,
"name": "test_reconciliation",
}
]
)
self.assertTrue(bank_stmt_line.journal_entry_ids) self.assertTrue(bank_stmt_line.journal_entry_ids)
original_move_lines = 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 # Cancel the statement line
bank_stmt_line.button_cancel_reconciliation() bank_stmt_line.button_cancel_reconciliation()
move_lines = self.env['account.move.line'].\ move_lines = self.env["account.move.line"].search(
search([('statement_id', '=', bank_stmt.id)]) [("statement_id", "=", bank_stmt.id)]
)
# All account moves are removed completely # All account moves are removed completely
self.assertFalse(move_lines) self.assertFalse(move_lines)
@@ -191,58 +209,69 @@ class TestPaymentReversal(SavepointCase):
- The line in the statement is ready to reconcile again - The line in the statement is ready to reconcile again
""" """
# Test journal # Test journal
self.bank_journal.write({'update_posted': True, self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
'cancel_method': 'reversal'})
# Open invoice # Open invoice
self.invoice.action_invoice_open() self.invoice.action_invoice_open()
bank_stmt = self.acc_bank_stmt_model.create({ bank_stmt = self.acc_bank_stmt_model.create(
'journal_id': self.bank_journal.id, {
'date': time.strftime('%Y') + '-07-15', "journal_id": self.bank_journal.id,
'name': 'payment' + self.invoice.name "date": time.strftime("%Y") + "-07-15",
}) "name": "payment" + self.invoice.name,
}
)
bank_stmt_line = self.acc_bank_stmt_line_model.create( bank_stmt_line = self.acc_bank_stmt_line_model.create(
{'name': 'payment', {
'statement_id': bank_stmt.id, "name": "payment",
'partner_id': self.partner.id, "statement_id": bank_stmt.id,
'amount': 200, "partner_id": self.partner.id,
'date': time.strftime('%Y') + '-07-15', }) "amount": 200,
"date": time.strftime("%Y") + "-07-15",
}
)
line_id = self.account_move_line_model line_id = self.account_move_line_model
# reconcile the payment with the invoice # reconcile the payment with the invoice
for l in self.invoice.move_id.line_ids: for l in self.invoice.move_id.line_ids:
if l.account_id.id == self.account_receivable.id: if l.account_id.id == self.account_receivable.id:
line_id = l line_id = l
break break
bank_stmt_line.process_reconciliation(counterpart_aml_dicts=[{ bank_stmt_line.process_reconciliation(
'move_line': line_id, counterpart_aml_dicts=[
'account_id': self.account_income.id, {
'debit': 0.0, "move_line": line_id,
'credit': 200.0, "account_id": self.account_income.id,
'name': 'test_reconciliation', "debit": 0.0,
}]) "credit": 200.0,
"name": "test_reconciliation",
}
]
)
self.assertTrue(bank_stmt_line.journal_entry_ids) self.assertTrue(bank_stmt_line.journal_entry_ids)
original_move_lines = 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 original_payment_id = original_move_lines.mapped("payment_id").id
self.assertTrue(original_move_lines.mapped('statement_id')) self.assertTrue(original_move_lines.mapped("statement_id"))
# Cancel the statement line # Cancel the statement line
res = bank_stmt_line.button_cancel_reconciliation() res = bank_stmt_line.button_cancel_reconciliation()
ctx = {'active_model': 'account.bank.statement.line', ctx = {
'active_ids': [bank_stmt_line.id]} "active_model": "account.bank.statement.line",
f = Form(self.env[res['res_model']].with_context(ctx)) "active_ids": [bank_stmt_line.id],
self.assertEqual(res['res_model'], 'reverse.account.document') }
f = Form(self.env[res["res_model"]].with_context(ctx))
self.assertEqual(res["res_model"], "reverse.account.document")
cancel_wizard = f.save() cancel_wizard = f.save()
cancel_wizard.action_cancel() cancel_wizard.action_cancel()
self.assertFalse(bank_stmt_line.journal_entry_ids) self.assertFalse(bank_stmt_line.journal_entry_ids)
payment = self.env['account.payment'].search( payment = self.env["account.payment"].search(
[('id', '=', original_payment_id)], [("id", "=", original_payment_id)], limit=1
limit=1) )
self.assertFalse(payment) 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 move = original_move_lines[0].move_id
reversed_move = move.reverse_entry_id reversed_move = move.reverse_entry_id
move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id') move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
reversed_move_reconcile = \ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
reversed_move.mapped('line_ids').mapped('full_reconcile_id') "full_reconcile_id"
)
# Check # Check
self.assertTrue(move_reconcile) self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile) self.assertTrue(reversed_move_reconcile)
@@ -257,51 +286,62 @@ class TestPaymentReversal(SavepointCase):
- The line in the statement is ready to reconcile again - The line in the statement is ready to reconcile again
""" """
# Test journal # Test journal
self.bank_journal.write({'update_posted': True, self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
'cancel_method': 'reversal'})
# Create a bank statement # Create a bank statement
bank_stmt = self.acc_bank_stmt_model.create({ bank_stmt = self.acc_bank_stmt_model.create(
'journal_id': self.bank_journal.id, {
'date': time.strftime('%Y') + '-07-15', "journal_id": self.bank_journal.id,
'name': 'payment' + self.invoice.name "date": time.strftime("%Y") + "-07-15",
}) "name": "payment" + self.invoice.name,
}
)
bank_stmt_line = self.acc_bank_stmt_line_model.create( bank_stmt_line = self.acc_bank_stmt_line_model.create(
{'name': 'payment', {
'statement_id': bank_stmt.id, "name": "payment",
'partner_id': self.partner.id, "statement_id": bank_stmt.id,
'amount': 200, "partner_id": self.partner.id,
'date': time.strftime('%Y') + '-07-15', }) "amount": 200,
"date": time.strftime("%Y") + "-07-15",
}
)
line_id = self.account_move_line_model line_id = self.account_move_line_model
bank_stmt_line.process_reconciliation(new_aml_dicts=[{ bank_stmt_line.process_reconciliation(
'move_line': line_id, new_aml_dicts=[
'account_id': self.account_expense.id, {
'debit': 200.0, "move_line": line_id,
'name': 'test_expense_reconciliation', "account_id": self.account_expense.id,
}]) "debit": 200.0,
"name": "test_expense_reconciliation",
}
]
)
self.assertTrue(bank_stmt_line.journal_entry_ids) self.assertTrue(bank_stmt_line.journal_entry_ids)
original_move_lines = 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 original_payment_id = original_move_lines.mapped("payment_id").id
self.assertTrue(original_move_lines.mapped('statement_id')) self.assertTrue(original_move_lines.mapped("statement_id"))
# Cancel the statement line # Cancel the statement line
res = bank_stmt_line.button_cancel_reconciliation() res = bank_stmt_line.button_cancel_reconciliation()
ctx = {'active_model': 'account.bank.statement.line', ctx = {
'active_ids': [bank_stmt_line.id]} "active_model": "account.bank.statement.line",
f = Form(self.env[res['res_model']].with_context(ctx)) "active_ids": [bank_stmt_line.id],
self.assertEqual(res['res_model'], 'reverse.account.document') }
f = Form(self.env[res["res_model"]].with_context(ctx))
self.assertEqual(res["res_model"], "reverse.account.document")
cancel_wizard = f.save() cancel_wizard = f.save()
cancel_wizard.action_cancel() cancel_wizard.action_cancel()
self.assertFalse(bank_stmt_line.journal_entry_ids) self.assertFalse(bank_stmt_line.journal_entry_ids)
payment = self.env['account.payment'].search( payment = self.env["account.payment"].search(
[('id', '=', original_payment_id)], [("id", "=", original_payment_id)], limit=1
limit=1) )
self.assertFalse(payment) 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 move = original_move_lines[0].move_id
reversed_move = move.reverse_entry_id reversed_move = move.reverse_entry_id
move_reconcile = move.mapped('line_ids').mapped('full_reconcile_id') move_reconcile = move.mapped("line_ids").mapped("full_reconcile_id")
reversed_move_reconcile = \ reversed_move_reconcile = reversed_move.mapped("line_ids").mapped(
reversed_move.mapped('line_ids').mapped('full_reconcile_id') "full_reconcile_id"
)
# Check # Check
self.assertTrue(move_reconcile) self.assertTrue(move_reconcile)
self.assertTrue(reversed_move_reconcile) self.assertTrue(reversed_move_reconcile)
@@ -313,31 +353,39 @@ class TestPaymentReversal(SavepointCase):
- UserError will show - UserError will show
""" """
# Test journal # Test journal
self.bank_journal.write({'update_posted': True, self.bank_journal.write({"update_posted": True, "cancel_method": "reversal"})
'cancel_method': 'reversal'})
# Create a bank statement # Create a bank statement
bank_stmt = self.acc_bank_stmt_model.create({ bank_stmt = self.acc_bank_stmt_model.create(
'journal_id': self.bank_journal.id, {
'date': time.strftime('%Y') + '-07-15', "journal_id": self.bank_journal.id,
'name': 'payment' + self.invoice.name "date": time.strftime("%Y") + "-07-15",
}) "name": "payment" + self.invoice.name,
}
)
bank_stmt_line = self.acc_bank_stmt_line_model.create( bank_stmt_line = self.acc_bank_stmt_line_model.create(
{'name': 'payment', {
'statement_id': bank_stmt.id, "name": "payment",
'partner_id': self.partner.id, "statement_id": bank_stmt.id,
'amount': 200, "partner_id": self.partner.id,
'date': time.strftime('%Y') + '-07-15', }) "amount": 200,
"date": time.strftime("%Y") + "-07-15",
}
)
line_id = self.account_move_line_model line_id = self.account_move_line_model
bank_stmt_line.process_reconciliation(new_aml_dicts=[{ bank_stmt_line.process_reconciliation(
'move_line': line_id, new_aml_dicts=[
'account_id': self.account_expense.id, {
'debit': 200.0, "move_line": line_id,
'name': 'test_expense_reconciliation', "account_id": self.account_expense.id,
}]) "debit": 200.0,
"name": "test_expense_reconciliation",
}
]
)
bank_stmt.balance_end_real = 200.00 bank_stmt.balance_end_real = 200.00
bank_stmt.check_confirm_bank() bank_stmt.check_confirm_bank()
self.assertEqual(bank_stmt.state, 'confirm') self.assertEqual(bank_stmt.state, "confirm")
with self.assertRaises(UserError): with self.assertRaises(UserError):
bank_stmt_line.button_cancel_reconciliation() bank_stmt_line.button_cancel_reconciliation()

View File

@@ -1,34 +1,49 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record id="view_account_journal_form_inherit" model="ir.ui.view"> <record id="view_account_journal_form_inherit" model="ir.ui.view">
<field name="name">account.journal.form</field> <field name="name">account.journal.form</field>
<field name="model">account.journal</field> <field name="model">account.journal</field>
<field name="inherit_id" ref="account_cancel.view_account_journal_form_inherit"/> <field
name="inherit_id"
ref="account_cancel.view_account_journal_form_inherit"
/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="update_posted" position="after"> <field name="update_posted" position="after">
<field name="cancel_method" groups="base.group_no_one" widget="radio" <field
attrs="{'invisible': [('update_posted', '=', False)]}"/> name="cancel_method"
<field name="use_different_journal" attrs="{'invisible': ['|', ('update_posted', '=', False), ('cancel_method', '!=', 'reversal')]}"/> groups="base.group_no_one"
<field name="reversal_journal_id" widget="selection" widget="radio"
attrs="{'invisible': ['|', '|', ('update_posted', '=', False), ('cancel_method', '!=', 'reversal'), ('use_different_journal', '=', False)]}"/> attrs="{'invisible': [('update_posted', '=', False)]}"
/>
<field
name="use_different_journal"
attrs="{'invisible': ['|', ('update_posted', '=', False), ('cancel_method', '!=', 'reversal')]}"
/>
<field
name="reversal_journal_id"
widget="selection"
attrs="{'invisible': ['|', '|', ('update_posted', '=', False), ('cancel_method', '!=', 'reversal'), ('use_different_journal', '=', False)]}"
/>
</field> </field>
</field> </field>
</record> </record>
<record id="view_move_form_inherit_account_cancel" model="ir.ui.view"> <record id="view_move_form_inherit_account_cancel" model="ir.ui.view">
<field name="name">account.move.form.inherit</field> <field name="name">account.move.form.inherit</field>
<field name="model">account.move</field> <field name="model">account.move</field>
<field name="inherit_id" ref="account_cancel.view_move_form_inherit_account_cancel"/> <field
name="inherit_id"
ref="account_cancel.view_move_form_inherit_account_cancel"
/>
<field name="arch" type="xml"> <field name="arch" type="xml">
<field name="state" position="before"> <field name="state" position="before">
<field name="is_cancel_reversal" invisible="1"/> <field name="is_cancel_reversal" invisible="1" />
</field> </field>
<button name="button_cancel" position="attributes"> <button name="button_cancel" position="attributes">
<attribute name="attrs">{'invisible': ['|', ('state', '!=', 'posted'), ('is_cancel_reversal', '=', True)]}</attribute> <attribute
<attribute name="states"></attribute> name="attrs"
>{'invisible': ['|', ('state', '!=', 'posted'), ('is_cancel_reversal', '=', True)]}</attribute>
<attribute name="states" />
</button> </button>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -1,48 +1,47 @@
# Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/) # Copyright 2019 Ecosoft Co., Ltd (http://ecosoft.co.th/)
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl.html) # 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): class ReverseAccountDocument(models.TransientModel):
""" """
Document reversal wizard, it cancel by reverse document journal entries 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( date = fields.Date(
string='Reversal date', string="Reversal date", default=fields.Date.context_today, required=True,
default=fields.Date.context_today,
required=True,
) )
use_different_journal = fields.Boolean( use_different_journal = fields.Boolean(
string='Use different journal for reversal', string="Use different journal for reversal",
help="Checked, if the journal of underlineing document is checked." help="Checked, if the journal of underlineing document is checked.",
) )
journal_id = fields.Many2one( journal_id = fields.Many2one(
'account.journal', "account.journal",
string='Reversal Journal', string="Reversal Journal",
help='If empty, uses the journal of the journal entry to be reversed.', help="If empty, uses the journal of the journal entry to be reversed.",
) )
@api.model @api.model
def default_get(self, default_fields): def default_get(self, default_fields):
model = self._context.get('active_model') model = self._context.get("active_model")
active_ids = self._context.get('active_ids') active_ids = self._context.get("active_ids")
documents = self.env[model].browse(active_ids) documents = self.env[model].browse(active_ids)
res = super().default_get(default_fields) res = super().default_get(default_fields)
if documents: if documents:
if 'journal_id' in documents[0]: if "journal_id" in documents[0]:
journal = documents[0].journal_id journal = documents[0].journal_id
if journal.use_different_journal: if journal.use_different_journal:
res['use_different_journal'] = True res["use_different_journal"] = True
res['journal_id'] = journal.reversal_journal_id.id res["journal_id"] = journal.reversal_journal_id.id
return res return res
@api.multi @api.multi
def action_cancel(self): def action_cancel(self):
model = self._context.get('active_model') model = self._context.get("active_model")
active_ids = self._context.get('active_ids') active_ids = self._context.get("active_ids")
documents = self.env[model].browse(active_ids) documents = self.env[model].browse(active_ids)
documents.action_document_reversal(self.date, self.journal_id) documents.action_document_reversal(self.date, self.journal_id)
return {'type': 'ir.actions.act_window_close'} return {"type": "ir.actions.act_window_close"}

View File

@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<data> <data>
<record id="view_reverse_account_document" model="ir.ui.view"> <record id="view_reverse_account_document" model="ir.ui.view">
@@ -8,27 +8,38 @@
<form string="Document Cancel"> <form string="Document Cancel">
<group> <group>
<group> <group>
<field name="date"/> <field name="date" />
</group> </group>
<group> <group>
<field name="use_different_journal" invisible="1"/> <field name="use_different_journal" invisible="1" />
<field name="journal_id" attrs="{'invisible': [('use_different_journal', '=', False)]}"/> <field
name="journal_id"
attrs="{'invisible': [('use_different_journal', '=', False)]}"
/>
</group> </group>
</group> </group>
<footer> <footer>
<button string="Confirm" name="action_cancel" type="object" class="btn-primary"/> <button
<button string="Discard" class="btn-secondary" special="cancel"/> string="Confirm"
name="action_cancel"
type="object"
class="btn-primary"
/>
<button
string="Discard"
class="btn-secondary"
special="cancel"
/>
</footer> </footer>
</form> </form>
</field> </field>
</record> </record>
<record id="action_view_reverse_account_document" model="ir.actions.act_window"> <record id="action_view_reverse_account_document" model="ir.actions.act_window">
<field name="name">Document Cancel</field> <field name="name">Document Cancel</field>
<field name="res_model">reverse.account.document</field> <field name="res_model">reverse.account.document</field>
<field name="view_type">form</field> <field name="view_type">form</field>
<field name="view_mode">tree,form</field> <field name="view_mode">tree,form</field>
<field name="view_id" ref="view_reverse_account_document"/> <field name="view_id" ref="view_reverse_account_document" />
<field name="target">new</field> <field name="target">new</field>
</record> </record>
</data> </data>