mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
Code refactoring of module account_payment_line_cancel
This commit is contained in:
@@ -1,29 +0,0 @@
|
||||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
|
||||
:target: https://www.gnu.org/licenses/agpl
|
||||
:alt: License: AGPL-3
|
||||
|
||||
======================
|
||||
Cancel account payment
|
||||
======================
|
||||
|
||||
This module allow you to automaticly cancel en remove a payment from a payment
|
||||
order when the payment need to be cancel (for exemple from a when a payment is
|
||||
rejected). The module allow you to free invoice and automaticly put
|
||||
the invoice in a new payment order.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://odoo-community.org/logo.png>`_.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Marco Monzione
|
||||
* Emanuel Cino
|
||||
|
||||
Do not contact contributors directly about support or help with
|
||||
technical issues.
|
||||
@@ -1,2 +0,0 @@
|
||||
|
||||
from . import models
|
||||
@@ -1,21 +0,0 @@
|
||||
{
|
||||
'name': 'Account payment cancel',
|
||||
'version': '10.0.1.0.0',
|
||||
'license': 'AGPL-3',
|
||||
'author': 'Monzione Marco',
|
||||
'website': 'https://www.compassion.ch',
|
||||
'category': 'Banking addons',
|
||||
'depends': [
|
||||
'account_payment_order',
|
||||
],
|
||||
'data': [
|
||||
'views/invoice_view.xml',
|
||||
],
|
||||
'demo': [
|
||||
'res/test_data.yml',
|
||||
],
|
||||
'test': [
|
||||
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
from . import account_payment_cancel
|
||||
from . import invoice_free_wizard
|
||||
from . import invoice
|
||||
@@ -1,130 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2017 Compassion CH (http://www.compassion.ch)
|
||||
# Releasing children from poverty in Jesus' name
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
#
|
||||
# The licence is in the file __manifest__.py
|
||||
#
|
||||
##############################################################################
|
||||
from odoo import models
|
||||
|
||||
|
||||
class AccountCancelPayment(models.AbstractModel):
|
||||
_name = 'account.payment.cancel'
|
||||
|
||||
def cancel_payment(self, payment_order, bank_payment_line,
|
||||
data_supp):
|
||||
"""
|
||||
This method proceed to call private methods to remove payment
|
||||
from payment order
|
||||
|
||||
:param payment_order: The order where to remove the payment
|
||||
:param bank_payment_line: The payment to remove
|
||||
:param data_supp: additional information used in the message
|
||||
:return: -
|
||||
"""
|
||||
|
||||
if payment_order and bank_payment_line:
|
||||
# Retrieve all account_payment_line associated to the
|
||||
# current bank_payment_line
|
||||
account_payment_lines = self.env['account.payment.line'].search(
|
||||
[('bank_line_id', '=', bank_payment_line.id)])
|
||||
|
||||
if account_payment_lines:
|
||||
self._process_invoice_remove(
|
||||
account_payment_lines, payment_order, data_supp)
|
||||
|
||||
def _process_invoice_remove(self, account_payment_lines, payment_order,
|
||||
data_supp):
|
||||
"""
|
||||
This method unreconcile the given move lines and remove the payment.
|
||||
|
||||
:param account_payment_lines: The payment to remove
|
||||
:param payment_order: The payment order where to remove the payment
|
||||
:param data_supp: additional information used in the message
|
||||
:return: -
|
||||
"""
|
||||
|
||||
# Retrieve all account_move_line(s) associated with the previous
|
||||
# account_payment_line(s).
|
||||
all_account_move_lines = account_payment_lines.mapped(
|
||||
'move_line_id')
|
||||
|
||||
full_reconcile_id = all_account_move_lines.mapped(
|
||||
'full_reconcile_id').id
|
||||
|
||||
if full_reconcile_id:
|
||||
# Retrieve the counterpart of the previous move_line(s).
|
||||
# The counter parth should always be unique.
|
||||
account_move_line_counterpart = self.env['account.move.line'].\
|
||||
search([('full_reconcile_id', '=', full_reconcile_id),
|
||||
('id', 'not in', all_account_move_lines.ids)], limit=1)
|
||||
|
||||
# Keep only the move lines that belong to the desired payment order
|
||||
filtered_move_lines_counterpart = account_move_line_counterpart.\
|
||||
filtered(lambda l: l.move_id.payment_order_id == payment_order)
|
||||
|
||||
account_move_counterpart = filtered_move_lines_counterpart.move_id
|
||||
# All the move lines with the same reconcile id are unreconciled.
|
||||
filtered_move_lines_counterpart.remove_move_reconcile()
|
||||
|
||||
# unpost and delete the move from the journal.
|
||||
account_move_counterpart.button_cancel()
|
||||
account_move_counterpart.unlink()
|
||||
|
||||
# Delete the payment line in the payment order.
|
||||
account_payment_lines.unlink()
|
||||
|
||||
# Search if there is something left in the payment order.
|
||||
account_payment_line = self.env['account.payment.line'].search(
|
||||
[('order_id', '=', payment_order.id)])
|
||||
if len(account_payment_line) == 0 and \
|
||||
payment_order.state == 'uploaded':
|
||||
|
||||
payment_order.action_done_cancel()
|
||||
|
||||
# Add the message to the invoice and to the payment order
|
||||
self._post_message(data_supp, all_account_move_lines, payment_order)
|
||||
|
||||
def _post_message(self, data_supp, account_move_lines, payment_order):
|
||||
"""
|
||||
This method is used to post message on the invoices that have been
|
||||
deleted from the payment order and it post a message too on the
|
||||
payment order for each deleted invoice.
|
||||
|
||||
:param data_supp: additional information used in the message
|
||||
:param account_move_lines: used to find the invoices where to post
|
||||
the message
|
||||
:param payment_order: used to post the message for each
|
||||
deleted invoices
|
||||
:return: -
|
||||
"""
|
||||
for account_move_line in account_move_lines:
|
||||
|
||||
# Create a link to the invoice that was removed
|
||||
url = '<a href="web#id={}&view_type=form&model=' \
|
||||
'account.invoice">{}</a>'. \
|
||||
format(account_move_line.invoice_id.id,
|
||||
account_move_line.invoice_id.move_name)
|
||||
|
||||
if data_supp and 'add_tl_inf' in data_supp:
|
||||
|
||||
# Add a message to the invoice
|
||||
account_move_line.invoice_id.message_post(
|
||||
"The invoice has been removed from the payment "
|
||||
"order because: " +
|
||||
data_supp['add_tl_inf'])
|
||||
# Add a message to the payment order
|
||||
payment_order.message_post(
|
||||
url + " has been removed because : " + data_supp[
|
||||
'add_tl_inf'])
|
||||
else:
|
||||
# Add a message to the invoice
|
||||
account_move_line.invoice_id.message_post(
|
||||
"The invoice has been removed from the payment order,"
|
||||
" no reason given.")
|
||||
# Add a message to the payment order
|
||||
payment_order.message_post(
|
||||
url + " has been removed no reason given.")
|
||||
@@ -1,49 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2017 Compassion CH (http://www.compassion.ch)
|
||||
# Releasing children from poverty in Jesus' name
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
#
|
||||
# The licence is in the file __manifest__.py
|
||||
#
|
||||
##############################################################################
|
||||
from odoo import models, api, _, exceptions
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
|
||||
''' Inherit invoice to add invoice freeing functionality. It's about
|
||||
moving related payment line in a new cancelled payment order. This
|
||||
way, the invoice (properly, invoice's move lines) can be used again
|
||||
in another payment order.
|
||||
'''
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
@api.multi
|
||||
def cancel_payment_lines(self):
|
||||
''' This function simply finds related payment lines and move them
|
||||
in a new payment order.
|
||||
'''
|
||||
mov_line_obj = self.env['account.move.line']
|
||||
pay_line_obj = self.env['account.payment.line']
|
||||
account_payment_cancel = self.env['account.payment.cancel']
|
||||
|
||||
move_ids = self.mapped('move_id.id')
|
||||
|
||||
move_line_ids = mov_line_obj.search([('move_id', 'in', move_ids)]).ids
|
||||
payment_lines = pay_line_obj.search([
|
||||
('move_line_id', 'in', move_line_ids)
|
||||
])
|
||||
|
||||
if not payment_lines:
|
||||
raise exceptions.UserError(_('No payment line found !'))
|
||||
|
||||
bank_payment_lines = payment_lines.mapped('bank_line_id')
|
||||
# It should have only one payment order
|
||||
old_pay_order = payment_lines.mapped('order_id')
|
||||
|
||||
for bank_payment_line in bank_payment_lines:
|
||||
account_payment_cancel.cancel_payment(old_pay_order,
|
||||
bank_payment_line,
|
||||
None)
|
||||
@@ -1,48 +0,0 @@
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
# Create partner
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
-
|
||||
!record {model: res.partner, id: customer, view: False}:
|
||||
name: "pain000 test"
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
# Create bank
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
-
|
||||
!record {model: res.bank, id: bank_post, view: False}:
|
||||
name: 'Postfinance AG'
|
||||
bic: 'POFICHBEXXX'
|
||||
street: 'Postfinance'
|
||||
zip: '3030'
|
||||
city: 'Bern'
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
# Create partner bank
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
-
|
||||
!record {model: res.partner.bank, id: company_bank_post, view: False}:
|
||||
acc_type: 'postal'
|
||||
acc_number: '25-9778-2'
|
||||
partner_id: base.main_partner #YourCompany
|
||||
bank_id: bank_post
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
# Create journal
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
-
|
||||
!record {model: account.journal, id: lsv_account_journal, view: False}:
|
||||
name: '2017/1013'
|
||||
type: bank
|
||||
bank_account_id: company_bank_post
|
||||
update_posted: True
|
||||
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
# Create payment mode
|
||||
#-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
|
||||
-
|
||||
!record {model: account.payment.mode, id: dd_pay_mode, view: False}:
|
||||
bank_account_link: variable
|
||||
name: 'DD'
|
||||
active: True
|
||||
payment_method_id: !ref {model: account.payment.method,search: "[('id','=','2')]"}
|
||||
payment_order_ok: True
|
||||
transfer_journal_id: !ref {model: account.journal, search: "[('type','=','bank')]"}
|
||||
transfer_account_id: !ref {model: account.account, search: "[('code','=','1090')]"}
|
||||
offsetting_account: 'transfer_account'
|
||||
@@ -1 +0,0 @@
|
||||
from . import test_payment_cancel
|
||||
@@ -1,127 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2017 Compassion CH (http://www.compassion.ch)
|
||||
# Releasing children from poverty in Jesus' name
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
#
|
||||
# The licence is in the file __manifest__.py
|
||||
#
|
||||
##############################################################################
|
||||
from odoo.tests import TransactionCase
|
||||
from odoo import fields
|
||||
|
||||
|
||||
class TestPaymentCancel(TransactionCase):
|
||||
|
||||
def setUp(self):
|
||||
super(TestPaymentCancel, self).setUp()
|
||||
|
||||
self.invoice_name = 'test invoice pain000'
|
||||
self.invoice_line_name = 'test invoice line pain000'
|
||||
self.order_name = '2017/1013'
|
||||
self.journal_name = '2017/1013'
|
||||
self.payment_line_name = 'Ltest'
|
||||
|
||||
# Create invoice
|
||||
self.invoice = self.env['account.invoice'].create({
|
||||
'company_id': self.env['res.company'].search([
|
||||
('name', '=', 'YourCompany')
|
||||
]).id,
|
||||
'move_name': self.invoice_name,
|
||||
'journal_id': self.env['account.journal'].search([
|
||||
('name', '=', self.journal_name)
|
||||
]).id,
|
||||
'currency_id': self.env['res.currency'].search([
|
||||
('name', '=', 'CHF')
|
||||
]).id,
|
||||
'account_id': self.env['account.account'].search([
|
||||
('code', '=', '1100')
|
||||
]).id,
|
||||
'type': 'in_invoice',
|
||||
'partner_id': self.env['res.partner'].search([
|
||||
('name', '=', 'pain000 test')
|
||||
]).id,
|
||||
'date_invoice': fields.Datetime.now(),
|
||||
'partner_bank_id': self.env['res.partner.bank'].search([
|
||||
('acc_number', '=', '25-9778-2')
|
||||
]).id,
|
||||
'payment_mode_id': self.env['account.payment.mode'].search([
|
||||
('name', '=', 'DD')
|
||||
]).id
|
||||
})
|
||||
|
||||
# Add an invoice line to our invoice.
|
||||
self.env['account.invoice.line'].create({
|
||||
'account_id': self.env['account.account'].search([
|
||||
('code', '=', '3200')
|
||||
]).id,
|
||||
'name': 'test invoice line pain000',
|
||||
'price_unit': 600.0,
|
||||
'quantity': 1.0,
|
||||
'product_id': self.env['product.product'].search([
|
||||
('default_code', '=', 'E-COM09')
|
||||
]).id,
|
||||
'invoice_id': self.invoice.id
|
||||
|
||||
})
|
||||
|
||||
# Validate the invoice
|
||||
self.invoice.action_invoice_open()
|
||||
|
||||
# Create a payment order
|
||||
action = self.invoice.create_account_payment_line()
|
||||
payment_order_id = action['res_id']
|
||||
|
||||
payment_order = self.env['account.payment.order'].search(
|
||||
[('id', '=', payment_order_id)])
|
||||
|
||||
partner_bank = self.env['account.journal'].search(
|
||||
[('name', '=', self.journal_name)])
|
||||
|
||||
bank = self.env['account.journal'].search([('name', '=', 'Bank')])
|
||||
|
||||
payment_order.name = self.order_name
|
||||
bank.update_posted = True
|
||||
|
||||
payment_order.journal_id = partner_bank.id
|
||||
# Confirm payment order
|
||||
payment_order.draft2open()
|
||||
# Generate payment file
|
||||
payment_order.open2generated()
|
||||
# File successfully uploaded
|
||||
payment_order.generated2uploaded()
|
||||
|
||||
payment_line = self.env['bank.payment.line'].search(
|
||||
[('order_id', '=', payment_order.id)])
|
||||
|
||||
payment_line.name = self.payment_line_name
|
||||
|
||||
def test_free_invoice(self):
|
||||
self._invoice_free()
|
||||
|
||||
# Test if the move related to the invoice are deleted after the invoice
|
||||
# is freed.
|
||||
payment_order = self.env['account.payment.order'].search(
|
||||
[('name', '=', self.order_name)])
|
||||
account_move = self.env['account.move'].search(
|
||||
[('payment_order_id', '=', payment_order.id)])
|
||||
|
||||
self.assertFalse(account_move)
|
||||
|
||||
# Test if the order is in cancel state.
|
||||
payment_order = self.env['account.payment.order'].search(
|
||||
[('name', '=', self.order_name)])
|
||||
|
||||
self.assertEqual(payment_order.state, 'cancel')
|
||||
|
||||
# Test if the invoice is in open state.
|
||||
self.assertEqual(self.invoice.state, 'open')
|
||||
|
||||
def _invoice_free(self):
|
||||
invoice = self.env['account.invoice'].search(
|
||||
[('move_name', '=', self.invoice_name)])
|
||||
|
||||
free_wizard = self.env['account.invoice.free'].with_context(
|
||||
active_ids=invoice.ids).create({})
|
||||
free_wizard.invoice_free()
|
||||
80
account_payment_line_cancel/README.rst
Normal file
80
account_payment_line_cancel/README.rst
Normal file
@@ -0,0 +1,80 @@
|
||||
.. image:: https://img.shields.io/badge/license-AGPL--3-blue.png
|
||||
:target: https://www.gnu.org/licenses/agpl
|
||||
:alt: License: AGPL-3
|
||||
|
||||
===================
|
||||
Cancel payment line
|
||||
===================
|
||||
|
||||
This module allows you to cancel and remove a payment line from a payment order when the payment needs to be cancelled (for example when a payment is rejected).
|
||||
The module allows you as well to free an invoice that was imported in a payment order and automatically cancel the payment lines that were generated.
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
To use this module, you need to:
|
||||
|
||||
#. Go in payment orders
|
||||
#. Click on the cancel button of a payment line, to remove it from the payment order.
|
||||
|
||||
Or :
|
||||
|
||||
#. Go to invoices
|
||||
#. Select a few invoices from the tree view
|
||||
#. Use the "Free invoices" entry to remove selected invoices from payment orders
|
||||
|
||||
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
|
||||
:alt: Try me on Runbot
|
||||
:target: https://runbot.odoo-community.org/runbot/173/10.0
|
||||
|
||||
Known issues / Roadmap
|
||||
======================
|
||||
|
||||
* Nothing
|
||||
|
||||
Bug Tracker
|
||||
===========
|
||||
|
||||
Bugs are tracked on `GitHub Issues
|
||||
<https://github.com/OCA/bank-payment/issues>`_. In case of trouble, please
|
||||
check there if your issue has already been reported. If you spotted it first,
|
||||
help us smash it by providing detailed and welcomed feedback.
|
||||
|
||||
Credits
|
||||
=======
|
||||
|
||||
Images
|
||||
------
|
||||
|
||||
* Odoo Community Association: `Icon <https://odoo-community.org/logo.png>`_.
|
||||
|
||||
Contributors
|
||||
------------
|
||||
|
||||
* Marco Monzione <marco.mon@windowslive.com>
|
||||
* Emanuel Cino <ecino@compassion.ch>
|
||||
* Cyril Sester <cyril.sester@outlook.com>
|
||||
|
||||
Do not contact contributors directly about support or help with technical issues.
|
||||
|
||||
Funders
|
||||
-------
|
||||
|
||||
The development of this module has been financially supported by:
|
||||
|
||||
* Compassion Switzerland
|
||||
|
||||
Maintainer
|
||||
----------
|
||||
|
||||
.. image:: https://odoo-community.org/logo.png
|
||||
:alt: Odoo Community Association
|
||||
:target: https://odoo-community.org
|
||||
|
||||
This module is maintained by the OCA.
|
||||
|
||||
OCA, or the Odoo Community Association, is a nonprofit organization whose
|
||||
mission is to support the collaborative development of Odoo features and
|
||||
promote its widespread use.
|
||||
|
||||
To contribute to this module, please visit https://odoo-community.org.
|
||||
4
account_payment_line_cancel/__init__.py
Normal file
4
account_payment_line_cancel/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from . import models
|
||||
from . import wizards
|
||||
21
account_payment_line_cancel/__manifest__.py
Normal file
21
account_payment_line_cancel/__manifest__.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017-2018 Compassion CH (http://www.compassion.ch)
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
{
|
||||
'name': 'Account payment line cancel',
|
||||
'version': '10.0.1.0.0',
|
||||
'license': 'AGPL-3',
|
||||
'author': 'Odoo Community Association (OCA)',
|
||||
'website': 'https://github.com/OCA/bank-payment',
|
||||
'category': 'Banking addons',
|
||||
'depends': [
|
||||
'account_payment_order',
|
||||
'account_cancel',
|
||||
],
|
||||
'data': [
|
||||
'views/invoice_view.xml',
|
||||
'views/payment_line_view.xml',
|
||||
],
|
||||
'installable': True,
|
||||
}
|
||||
4
account_payment_line_cancel/models/__init__.py
Normal file
4
account_payment_line_cancel/models/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from . import account_payment_line
|
||||
from . import invoice
|
||||
98
account_payment_line_cancel/models/account_payment_line.py
Normal file
98
account_payment_line_cancel/models/account_payment_line.py
Normal file
@@ -0,0 +1,98 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Compassion CH (http://www.compassion.ch)
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
|
||||
from odoo import models, fields, _
|
||||
|
||||
|
||||
class AccountCancelPayment(models.Model):
|
||||
_inherit = 'account.payment.line'
|
||||
|
||||
cancel_reason = fields.Char()
|
||||
|
||||
def cancel_line(self):
|
||||
"""
|
||||
This method proceed to call private methods to remove payment lines
|
||||
from payment order
|
||||
|
||||
A cancel reason can be put in the cancel_reason field.
|
||||
|
||||
:return: True
|
||||
"""
|
||||
# Retrieve all account_move_line(s) associated with the previous
|
||||
# account_payment_line(s).
|
||||
all_account_move_lines = self.mapped('move_line_id')
|
||||
# Add the message to the invoice and to the payment order
|
||||
self._post_cancel_message()
|
||||
# Unreconcile transfer journal entries
|
||||
full_reconcile_ids = all_account_move_lines.mapped(
|
||||
'full_reconcile_id.id')
|
||||
payment_orders = self.mapped('order_id')
|
||||
if full_reconcile_ids:
|
||||
# Retrieve the counterparts of the previous move_lines.
|
||||
# The counter part should always be unique.
|
||||
account_move_line_counterpart = self.env[
|
||||
'account.move.line'].search([
|
||||
('full_reconcile_id', 'in', full_reconcile_ids),
|
||||
('id', 'not in', all_account_move_lines.ids),
|
||||
('move_id.payment_order_id', 'in', payment_orders.ids)])
|
||||
|
||||
# All the move lines with the same reconcile id are unreconciled.
|
||||
account_move_line_counterpart.remove_move_reconcile()
|
||||
# unpost and delete the moves from the journal.
|
||||
account_move_counterpart = account_move_line_counterpart.mapped(
|
||||
'move_id')
|
||||
account_move_counterpart.button_cancel()
|
||||
account_move_counterpart.unlink()
|
||||
|
||||
# Remove bank.payment.line
|
||||
self.mapped('bank_line_id').with_context(force_unlink=True).unlink()
|
||||
|
||||
# Search if there is something left in the payment order.
|
||||
for payment_order in payment_orders:
|
||||
other_lines = self.search_count([
|
||||
('order_id', '=', payment_order.id),
|
||||
('id', 'not in', self.ids)
|
||||
])
|
||||
if not other_lines:
|
||||
payment_order.action_done_cancel()
|
||||
|
||||
# Delete the payment line in the payment order.
|
||||
res = self.unlink()
|
||||
# Force recomputation of total
|
||||
payment_orders._compute_total()
|
||||
if self.env.context.get('cancel_line_from_payment_order'):
|
||||
# to see that the state of the order has changed, we need
|
||||
# to update the whole view. Do that only if necessary.
|
||||
res = {
|
||||
'type': 'ir.actions.client',
|
||||
'tag': 'reload',
|
||||
}
|
||||
return res
|
||||
|
||||
def _post_cancel_message(self):
|
||||
"""
|
||||
This method is used to post message on the invoices that have been
|
||||
deleted from the payment order and it post a message too on the
|
||||
payment order for each deleted invoice.
|
||||
"""
|
||||
for payment_line in self:
|
||||
cancel_reason = payment_line.cancel_reason or _(
|
||||
u"no reason given.")
|
||||
# Create a link to the invoice that was removed
|
||||
invoice = payment_line.move_line_id.invoice_id
|
||||
order = payment_line.order_id
|
||||
invoice_url = u'<a href="web#id={}&view_type=form&model=' \
|
||||
u'account.invoice">{}</a>'.format(invoice.id,
|
||||
invoice.move_name)
|
||||
payment_order_url = u'<a href="web#id={}&view_type=form&model=' \
|
||||
u'account.payment.order">{}</a>'.format(order.id, order.name)
|
||||
# Add a message to the invoice
|
||||
invoice.message_post(
|
||||
_(u"The invoice has been removed from ") + u"{}, {}"
|
||||
.format(payment_order_url, cancel_reason)
|
||||
)
|
||||
# Add a message to the payment order
|
||||
payment_line.order_id.message_post(
|
||||
invoice_url + _(u" has been removed, ") + cancel_reason)
|
||||
31
account_payment_line_cancel/models/invoice.py
Normal file
31
account_payment_line_cancel/models/invoice.py
Normal file
@@ -0,0 +1,31 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Compassion CH (http://www.compassion.ch)
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import models, api, _, exceptions
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
|
||||
""" Inherit invoice to add invoice freeing functionality. It's about
|
||||
cancelling related payment line. This
|
||||
way, the invoice (properly, invoice's move lines) can be used again
|
||||
in another payment order.
|
||||
"""
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
@api.multi
|
||||
def cancel_payment_lines(self):
|
||||
""" This function simply finds related payment lines and cancel them.
|
||||
"""
|
||||
mov_line_obj = self.env['account.move.line']
|
||||
pay_line_obj = self.env['account.payment.line']
|
||||
move_ids = self.mapped('move_id.id')
|
||||
move_line_ids = mov_line_obj.search([('move_id', 'in', move_ids)]).ids
|
||||
payment_lines = pay_line_obj.search([
|
||||
('move_line_id', 'in', move_line_ids)
|
||||
])
|
||||
if not payment_lines:
|
||||
raise exceptions.UserError(_('No payment line found !'))
|
||||
|
||||
payment_lines.cancel_line()
|
||||
3
account_payment_line_cancel/tests/__init__.py
Normal file
3
account_payment_line_cancel/tests/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from . import test_payment_cancel
|
||||
75
account_payment_line_cancel/tests/test_payment_cancel.py
Normal file
75
account_payment_line_cancel/tests/test_payment_cancel.py
Normal file
@@ -0,0 +1,75 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2017 Compassion CH (http://www.compassion.ch)
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import tools, fields
|
||||
from odoo.tests import TransactionCase
|
||||
from odoo.modules.module import get_resource_path
|
||||
|
||||
|
||||
class TestPaymentCancel(TransactionCase):
|
||||
def _load(self, module, *args):
|
||||
tools.convert_file(
|
||||
self.cr, 'account_asset',
|
||||
get_resource_path(module, *args),
|
||||
{}, 'init', False, 'test', self.registry._assertion_report)
|
||||
|
||||
def test_free_invoice(self):
|
||||
self._load('account', 'test', 'account_minimal_test.xml')
|
||||
journal = self.env['account.journal'].search([
|
||||
('code', '=', 'TEXJ')], limit=1)
|
||||
account = self.env['account.account'].search([
|
||||
('code', '=', 'X1012')], limit=1)
|
||||
product = self.env.ref('product.product_product_24')
|
||||
account_line = self.env['account.account'].search([
|
||||
('code', '=', 'X2020')], limit=1)
|
||||
|
||||
# Create invoice
|
||||
invoice = self.env['account.invoice'].create({
|
||||
'journal_id': journal.id,
|
||||
'currency_id': self.env.ref('base.USD').id,
|
||||
'account_id': account.id,
|
||||
'type': 'in_invoice',
|
||||
'partner_id': self.env.ref('base.res_partner_address_31').id,
|
||||
'date_invoice': fields.Datetime.now(),
|
||||
'invoice_line_ids': [(0, 0, {
|
||||
'product_id': product.id,
|
||||
'name': product.name,
|
||||
'account_id': account_line.id,
|
||||
'quantity': 1,
|
||||
'price_unit': product.standard_price
|
||||
})]
|
||||
})
|
||||
invoice.action_invoice_open()
|
||||
payorder_id = invoice.create_account_payment_line().get('res_id')
|
||||
payment_order = self.env['account.payment.order'].browse(payorder_id)
|
||||
payment_order.journal_id = journal
|
||||
|
||||
# Confirm payment order
|
||||
payment_order.draft2open()
|
||||
# Generate payment file
|
||||
payment_order.open2generated()
|
||||
# File successfully uploaded
|
||||
payment_order.generated2uploaded()
|
||||
|
||||
# The invoice should be paid
|
||||
self.assertEquals(invoice.state, 'paid')
|
||||
|
||||
# Make journal cancellable
|
||||
journal.update_posted = True
|
||||
wizard = self.env['account.invoice.free'].with_context(
|
||||
active_ids=invoice.ids).create({})
|
||||
wizard.invoice_free()
|
||||
|
||||
# Test if the move related to the invoice are deleted after the invoice
|
||||
# is freed.
|
||||
account_move = self.env['account.move'].search(
|
||||
[('payment_order_id', '=', payment_order.id)])
|
||||
|
||||
self.assertFalse(account_move)
|
||||
|
||||
# Test if the order is in cancel state.
|
||||
self.assertEqual(payment_order.state, 'cancel')
|
||||
|
||||
# Test if the invoice is in open state.
|
||||
self.assertEqual(invoice.state, 'open')
|
||||
13
account_payment_line_cancel/views/payment_line_view.xml
Normal file
13
account_payment_line_cancel/views/payment_line_view.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<record id="account_payment_line_cancel_view" model="ir.ui.view">
|
||||
<field name="name">account.payment.line.cancel.tree</field>
|
||||
<field name="model">account.payment.line</field>
|
||||
<field name="inherit_id" ref="account_payment_order.account_payment_line_tree"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="payment_type" position="after">
|
||||
<button name="cancel_line" type="object" icon="fa-undo" context="{'cancel_line_from_payment_order': 1}"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
3
account_payment_line_cancel/wizards/__init__.py
Normal file
3
account_payment_line_cancel/wizards/__init__.py
Normal file
@@ -0,0 +1,3 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from . import invoice_free_wizard
|
||||
@@ -1,13 +1,7 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2017 Compassion CH (http://www.compassion.ch)
|
||||
# Releasing children from poverty in Jesus' name
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
#
|
||||
# The licence is in the file __manifest__.py
|
||||
#
|
||||
##############################################################################
|
||||
# Copyright 2017 Compassion CH (http://www.compassion.ch)
|
||||
# @author: Marco Monzione <marco.mon@windowslive.com>, Emanuel Cino
|
||||
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
|
||||
from odoo import models, api
|
||||
|
||||
|
||||
@@ -17,6 +11,7 @@ class AccountInvoiceFree(models.TransientModel):
|
||||
payment order.
|
||||
'''
|
||||
_name = 'account.invoice.free'
|
||||
_description = 'Free invoice wizard'
|
||||
|
||||
@api.multi
|
||||
def invoice_free(self):
|
||||
@@ -158,11 +158,12 @@ class BankPaymentLine(models.Model):
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
for line in self:
|
||||
order_state = line.order_id.state
|
||||
if order_state == 'uploaded':
|
||||
raise UserError(_(
|
||||
'Cannot delete a payment order line whose payment order is'
|
||||
' in state \'%s\'. You need to cancel it first.')
|
||||
% order_state)
|
||||
if not self.env.context.get('force_unlink'):
|
||||
for line in self:
|
||||
order_state = line.order_id.state
|
||||
if order_state == 'uploaded':
|
||||
raise UserError(_(
|
||||
'Cannot delete a payment order line whose payment '
|
||||
'order is in state \'%s\'. You need to cancel it '
|
||||
'first.') % order_state)
|
||||
return super(BankPaymentLine, self).unlink()
|
||||
|
||||
Reference in New Issue
Block a user