mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
Remove storno, which seems specific to one particular country.
This commit is contained in:
committed by
Pedro M. Baeza
parent
212f5f992e
commit
adedae3bc2
@@ -37,45 +37,6 @@ class PaymentLine(models.Model):
|
||||
|
||||
msg = fields.Char('Message', required=False, readonly=True, default='')
|
||||
date_done = fields.Date('Date Confirmed', select=True, readonly=True)
|
||||
"""
|
||||
Hooks for processing direct debit orders, such as implemented in
|
||||
account_direct_debit module.
|
||||
"""
|
||||
@api.multi
|
||||
def get_storno_account_id(self, amount, currency_id):
|
||||
"""
|
||||
Hook for verifying a match of the payment line with the amount.
|
||||
Return the account associated with the storno.
|
||||
Used in account_banking interactive mode
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (signed) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:return: an account if there is a full match, False otherwise
|
||||
:rtype: database id of an account.account resource.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
@api.multi
|
||||
def debit_storno(self, amount, currency_id, storno_retry=True):
|
||||
"""
|
||||
Hook for handling a canceled item of a direct debit order.
|
||||
Presumably called from a bank statement import routine.
|
||||
|
||||
Decide on the direction that the invoice's workflow needs to take.
|
||||
You may optionally return an incomplete reconcile for the caller
|
||||
to reconcile the now void payment.
|
||||
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (negative) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:param boolean storno_retry: whether the storno is considered fatal \
|
||||
or not.
|
||||
:return: an incomplete reconcile for the caller to fill
|
||||
:rtype: database id of an account.move.reconcile resource.
|
||||
"""
|
||||
|
||||
return False
|
||||
|
||||
|
||||
class BankPaymentLine(models.Model):
|
||||
@@ -137,10 +98,3 @@ class BankPaymentLine(models.Model):
|
||||
lines_to_rec += payment_line.move_line_id
|
||||
|
||||
lines_to_rec.reconcile_partial(type='auto')
|
||||
|
||||
# If a bank transaction of a storno was first confirmed
|
||||
# and now canceled (the invoice is now in state 'debit_denied'
|
||||
# if torec_move_line.invoice:
|
||||
# workflow.trg_validate(
|
||||
# self.env.uid, 'account.invoice', torec_move_line.invoice.id,
|
||||
# 'undo_debit_denied', self.env.cr)
|
||||
|
||||
@@ -8,11 +8,8 @@ This module adds support for direct debit orders, analogous to payment orders.
|
||||
A new entry in the Accounting/Payment menu allow you to create a direct debit
|
||||
order that helps you to select any customer invoices for you to collect.
|
||||
|
||||
This module explicitely implements direct debit orders as applicable
|
||||
in the Netherlands. Debit orders are advanced in total by the bank.
|
||||
Amounts that cannot be debited or are canceled by account owners are
|
||||
credited afterwards. Such a creditation is called a storno. This style of
|
||||
direct debit order may not apply to your country.
|
||||
Debit orders are advanced in total by the bank. Amounts that cannot be
|
||||
debited or are canceled by account owners are credited afterwards.
|
||||
|
||||
Installation
|
||||
============
|
||||
@@ -25,12 +22,12 @@ This module is part of the OCA/bank-payment suite.
|
||||
Configuration
|
||||
=============
|
||||
|
||||
Please refer to module "Account Banking SEPA Direct Debit"
|
||||
Please refer to module *Account Banking SEPA Direct Debit*
|
||||
|
||||
Usage
|
||||
=====
|
||||
|
||||
Please refer to module "Account Banking SEPA Direct Debit"
|
||||
Please refer to module *Account Banking SEPA Direct Debit*
|
||||
|
||||
|
||||
For further information, please visit:
|
||||
@@ -58,7 +55,7 @@ Contributors
|
||||
|
||||
* Stefan Rijnhart
|
||||
* Pedro M. Baeza
|
||||
* Alexis de Lattre
|
||||
* Alexis de Lattre <alexis.delattre@akretion.com>
|
||||
* Danimar Ribeiro
|
||||
* Stéphane Bidoul <stephane.bidoul@acsone.eu>
|
||||
* Alexandre Fayolle
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
'views/account_invoice.xml',
|
||||
'views/payment_mode.xml',
|
||||
'views/payment_mode_type.xml',
|
||||
'workflow/account_invoice.xml',
|
||||
'data/account_payment_term.xml',
|
||||
'data/payment_mode_type.xml'
|
||||
],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from . import account_payment
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from . import payment_line
|
||||
from . import account_move_line
|
||||
from . import account_invoice
|
||||
|
||||
@@ -1,165 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
# All Rights Reserved
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
"""
|
||||
This module adds support for Direct debit orders as applicable
|
||||
in the Netherlands. Debit orders are advanced in total by the bank.
|
||||
Amounts that cannot be debited or are canceled by account owners are
|
||||
credited afterwards. Such a creditation is called a storno.
|
||||
|
||||
Invoice workflow:
|
||||
|
||||
1 the sale leads to
|
||||
1300 Debtors 100
|
||||
8000 Sales 100
|
||||
|
||||
Balance:
|
||||
Debtors 2000 |
|
||||
Sales | 2000
|
||||
|
||||
2 an external booking takes place
|
||||
1100 Bank 100
|
||||
1300 Debtors 100
|
||||
This booking is reconciled with [1]
|
||||
The invoice gets set to state 'paid', and 'reconciled' = True
|
||||
|
||||
Balance:
|
||||
Debtors 1900 |
|
||||
Bank 100 |
|
||||
Sales | 2000
|
||||
|
||||
This module implements the following diversion:
|
||||
|
||||
2a the invoice is included in a direct debit order. When the order is
|
||||
confirmed, a move is created per invoice:
|
||||
|
||||
2000 Transfer account 100 |
|
||||
1300 Debtors | 100
|
||||
Reconciliation takes place between 1 and 2a.
|
||||
The invoice gets set to state 'paid', and 'reconciled' = True
|
||||
|
||||
Balance:
|
||||
Debtors 0 |
|
||||
Transfer account 2000 |
|
||||
Bank 0 |
|
||||
Sales | 2000
|
||||
|
||||
3a the direct debit order is booked on the bank account
|
||||
|
||||
Balance:
|
||||
1100 Bank 2000 |
|
||||
2000 Transfer account | 2000
|
||||
Reconciliation takes place between 3a and 2a
|
||||
|
||||
Balance:
|
||||
Debtors 0 |
|
||||
Transfer account 0 |
|
||||
Bank 2000 |
|
||||
Sales | 2000
|
||||
|
||||
4 a storno from invoice [1] triggers a new booking on the bank account
|
||||
1300 Debtors 100 |
|
||||
1100 Bank | 100
|
||||
|
||||
Balance:
|
||||
Debtors 100 |
|
||||
Transfer account 0 |
|
||||
Bank 1900 |
|
||||
Sales | 2000
|
||||
|
||||
The reconciliation of 2a is undone. The booking of 2a is reconciled
|
||||
with the booking of 4 instead.
|
||||
The payment line attribute 'storno' is set to True and the invoice
|
||||
state is no longer 'paid'.
|
||||
|
||||
Two cases need to be distinguisted:
|
||||
1) If the storno is a manual storno from the partner, the invoice is set to
|
||||
state 'debit_denied', with 'reconciled' = False
|
||||
This module implements this option by allowing the bank module to call
|
||||
|
||||
netsvc.LocalService("workflow").trg_validate(
|
||||
uid, 'account.invoice', ids, 'debit_denied', cr)
|
||||
|
||||
2) If the storno is an error generated by the bank (assumingly non-fatal),
|
||||
the invoice is reopened for the next debit run. This is a call to
|
||||
existing
|
||||
|
||||
netsvc.LocalService("workflow").trg_validate(
|
||||
uid, 'account.invoice', ids, 'open_test', cr)
|
||||
|
||||
Should also be adding a log entry on the invoice for tracing purposes
|
||||
|
||||
self._log_event(cr, uid, ids, -1.0, 'Debit denied')
|
||||
|
||||
If not for that funny comment
|
||||
"#TODO: implement messages system" in account/invoice.py
|
||||
|
||||
Repeating non-fatal fatal errors need to be dealt with manually by checking
|
||||
open invoices with a matured invoice- or due date.
|
||||
"""
|
||||
|
||||
from openerp.osv import orm
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class AccountInvoice(orm.Model):
|
||||
_inherit = "account.invoice"
|
||||
|
||||
def _register_hook(self, cr):
|
||||
"""
|
||||
Adding a state to the hardcoded state list of the inherited
|
||||
model. The alternative is duplicating the field definition
|
||||
in columns but only one module can do that!
|
||||
|
||||
Maybe apply a similar trick when overriding the buttons' 'states'
|
||||
attributes in the form view, manipulating the xml in fields_view_get().
|
||||
"""
|
||||
self._columns['state'].selection.append(
|
||||
('debit_denied', 'Debit denied'))
|
||||
return super(AccountInvoice, self)._register_hook(cr)
|
||||
|
||||
def action_debit_denied(self, cr, uid, ids, context=None):
|
||||
for invoice_id in ids:
|
||||
if self.test_paid(cr, uid, [invoice_id], context):
|
||||
number = self.read(
|
||||
cr, uid, invoice_id, ['number'], context=context)['number']
|
||||
raise orm.except_orm(
|
||||
_('Error !'),
|
||||
_("You cannot set invoice '%s' to state 'debit "
|
||||
"denied', as it is still reconciled.") % number)
|
||||
self.write(cr, uid, ids, {'state': 'debit_denied'}, context=context)
|
||||
for inv_id, name in self.name_get(cr, uid, ids, context=context):
|
||||
message = _("Invoice '%s': direct debit is denied.") % name
|
||||
self.log(cr, uid, inv_id, message)
|
||||
return True
|
||||
|
||||
def test_undo_debit_denied(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Called from the workflow. Used to unset paid state on
|
||||
invoices that were paid with bank transfers which are being cancelled
|
||||
"""
|
||||
for invoice in self.read(cr, uid, ids, ['reconciled'], context):
|
||||
if not invoice['reconciled']:
|
||||
return False
|
||||
return True
|
||||
@@ -1,19 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp.osv import orm
|
||||
|
||||
|
||||
class PaymentOrder(orm.Model):
|
||||
_inherit = 'payment.order'
|
||||
|
||||
def test_undo_done(self, cr, uid, ids, context=None):
|
||||
"""Called from the workflow. Used to unset done state on
|
||||
payment orders that were reconciled with bank transfers
|
||||
which are being cancelled
|
||||
"""
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
if order.payment_order_type == 'debit':
|
||||
for line in order.line_ids:
|
||||
if line.storno:
|
||||
return False
|
||||
return super(PaymentOrder, self).test_undo_done(
|
||||
cr, uid, ids, context=context)
|
||||
@@ -1,134 +1,10 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from openerp import api, exceptions, models, fields, _, workflow
|
||||
from openerp import models, fields
|
||||
|
||||
|
||||
class PaymentLine(models.Model):
|
||||
_inherit = 'payment.line'
|
||||
|
||||
@api.multi
|
||||
def debit_storno(self, amount, currency, storno_retry=True):
|
||||
"""The processing of a storno is triggered by a debit
|
||||
transfer on one of the company's bank accounts.
|
||||
This method offers to re-reconcile the original debit
|
||||
payment. For this purpose, we have registered that
|
||||
payment move on the payment line.
|
||||
|
||||
Return the (now incomplete) reconcile id. The caller MUST
|
||||
re-reconcile this reconcile with the bank transfer and
|
||||
re-open the associated invoice.
|
||||
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (signed) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:param boolean storno_retry: when True, attempt to reopen the invoice,
|
||||
set the invoice to 'Debit denied' otherwise.
|
||||
:return: an incomplete reconcile for the caller to fill
|
||||
:rtype: database id of an account.move.reconcile resource.
|
||||
"""
|
||||
self.ensure_one()
|
||||
reconcile_obj = self.env['account.move.reconcile']
|
||||
line = self
|
||||
reconcile = reconcile_obj.browse([])
|
||||
if (line.transit_move_line_id and not line.storno and
|
||||
self.env['res.currency'].is_zero(
|
||||
currency, (
|
||||
(line.transit_move_line_id.credit or 0.0) -
|
||||
(line.transit_move_line_id.debit or 0.0) + amount))):
|
||||
# Two different cases, full and partial
|
||||
# Both cases differ subtly in the procedure to follow
|
||||
# Needs refractoring, but why is this not in the OpenERP API?
|
||||
# Actually, given the nature of a direct debit order and storno,
|
||||
# we should not need to take partial into account on the side of
|
||||
# the transit_move_line.
|
||||
if line.transit_move_line_id.reconcile_partial_id:
|
||||
reconcile_partial = \
|
||||
line.transit_move_line_id.reconcile_partial_id
|
||||
reconcile = line.transit_move_line_id.reconcile_id
|
||||
if len(reconcile.line_partial_ids) == 2:
|
||||
# reuse the simple reconcile for the storno transfer
|
||||
reconcile_partial.write({
|
||||
'line_id': [(6, 0, line.transit_move_line_id.id)],
|
||||
'line_partial_ids': [(6, 0, [])]
|
||||
})
|
||||
else:
|
||||
# split up the original reconcile in a partial one
|
||||
# and a new one for reconciling the storno transfer
|
||||
reconcile_partial.write({
|
||||
'line_partial_ids': [(3, line.transit_move_line_id.id)]
|
||||
})
|
||||
reconcile = reconcile_obj.create({
|
||||
'type': 'auto',
|
||||
'line_id': [(6, 0, line.transit_move_line_id.id)]
|
||||
})
|
||||
elif line.transit_move_line_id.reconcile_id:
|
||||
reconcile = line.transit_move_line_id.reconcile_id
|
||||
if len(line.transit_move_line_id.reconcile_id.line_id) == 2:
|
||||
# reuse the simple reconcile for the storno transfer
|
||||
reconcile.write({
|
||||
'line_id': [(6, 0, [line.transit_move_line_id.id])],
|
||||
})
|
||||
else:
|
||||
# split up the original reconcile in a partial one
|
||||
# and a new one for reconciling the storno transfer
|
||||
reconcile = line.transit_move_line_id.reconcile_id
|
||||
partial_ids = [x.id for x in reconcile.line_id
|
||||
if x.id != line.transit_move_line_id.id]
|
||||
reconcile.write({
|
||||
'line_partial_ids': [(6, 0, partial_ids)],
|
||||
'line_id': [(6, 0, [])],
|
||||
})
|
||||
reconcile = reconcile_obj.create({
|
||||
'type': 'auto',
|
||||
'line_id': [(6, 0, line.transit_move_line_id.id)]
|
||||
})
|
||||
# mark the payment line for storno processed
|
||||
if reconcile:
|
||||
self.write({'storno': True})
|
||||
# put forth the invoice workflow
|
||||
if line.move_line_id.invoice:
|
||||
activity = (storno_retry and 'open_test' or
|
||||
'invoice_debit_denied')
|
||||
workflow.trg_validate(
|
||||
self.env.uid, 'account.invoice',
|
||||
line.move_line_id.invoice.id, activity, self.env.cr)
|
||||
return reconcile.id
|
||||
|
||||
@api.multi
|
||||
def get_storno_account_id(self, amount, currency):
|
||||
"""Check the match of the arguments, and return the account associated
|
||||
with the storno.
|
||||
Used in account_banking interactive mode
|
||||
|
||||
:param payment_line_id: the single payment line id
|
||||
:param amount: the (signed) amount debited from the bank account
|
||||
:param currency: the bank account's currency *browse object*
|
||||
:return: an account if there is a full match, False otherwise
|
||||
:rtype: database id of an account.account resource.
|
||||
"""
|
||||
self.ensure_one()
|
||||
account_id = False
|
||||
if (self.transit_move_line_id and not self.storno and
|
||||
self.env['res.currency'].is_zero(
|
||||
currency, (
|
||||
(self.transit_move_line_id.credit or 0.0) -
|
||||
(self.transit_move_line_id.debit or 0.0) + amount))):
|
||||
account_id = self.transit_move_line_id.account_id.id
|
||||
return account_id
|
||||
|
||||
@api.one
|
||||
def debit_reconcile(self):
|
||||
"""Raise if a payment line is passed for which storno is True."""
|
||||
if self.storno:
|
||||
raise exceptions.except_orm(
|
||||
_('Can not reconcile'),
|
||||
_('Cancelation of payment line \'%s\' has already been '
|
||||
'processed') % self.name)
|
||||
return super(PaymentLine, self).debit_reconcile()
|
||||
|
||||
storno = fields.Boolean(
|
||||
'Storno', readonly=True,
|
||||
help="If this is true, the debit order has been canceled by the bank "
|
||||
"or by the customer")
|
||||
# The original string is "Destination Bank Account"...
|
||||
# but in direct debit this field is the *Source* Bank Account !
|
||||
bank_id = fields.Many2one(string='Partner Bank Account')
|
||||
|
||||
@@ -47,17 +47,5 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_payment_line_tree" model="ir.ui.view">
|
||||
<field name="name">Payment Lines</field>
|
||||
<field name="model">payment.line</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_line_tree"/>
|
||||
<field eval="4" name="priority"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="name" position="after">
|
||||
<field name="storno"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record id="act_debit_denied" model="workflow.activity">
|
||||
<field name="wkf_id" ref="account.wkf"/>
|
||||
<field name="name">debit_denied</field>
|
||||
<field name="action">action_debit_denied()</field>
|
||||
<field name="kind">function</field>
|
||||
</record>
|
||||
<record id="paid_to_debit_denied" model="workflow.transition">
|
||||
<!--
|
||||
Set an invoice to state debit denied, either manually
|
||||
or by confirming a bank statement line that constitutes
|
||||
a fatal storno
|
||||
-->
|
||||
<field name="act_from" ref="account.act_paid"/>
|
||||
<field name="act_to" ref="act_debit_denied"/>
|
||||
<field name="signal">invoice_debit_denied</field>
|
||||
</record>
|
||||
<record id="open_test_to_debit_denied" model="workflow.transition">
|
||||
<!--
|
||||
A storno leads to unreconciling the move line, which
|
||||
reopens the invoice. We need to allow a transition from
|
||||
this state to the debit denied state if the storno is fatal.
|
||||
-->
|
||||
<field name="act_from" ref="account.act_open_test"/>
|
||||
<field name="act_to" ref="act_debit_denied"/>
|
||||
<field name="signal">invoice_debit_denied</field>
|
||||
</record>
|
||||
<record id="debit_denied_to_paid" model="workflow.transition">
|
||||
<!--
|
||||
Cancel a bank statement line that constitutes a fatal
|
||||
storno
|
||||
-->
|
||||
<field name="act_from" ref="act_debit_denied"/>
|
||||
<field name="act_to" ref="account.act_paid"/>
|
||||
<field name="condition">test_undo_debit_denied()</field>
|
||||
<field name="signal">undo_debit_denied</field>
|
||||
</record>
|
||||
<record id="debit_denied_to_open" model="workflow.transition">
|
||||
<!--
|
||||
Allow the user to manually reset a debit denied status
|
||||
on a paid invoice (but only after manually unreconciling
|
||||
the invoice)
|
||||
-->
|
||||
<field name="act_from" ref="act_debit_denied"/>
|
||||
<field name="act_to" ref="account.act_open_test"/>
|
||||
<field name="signal">open_test</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user