Remove storno, which seems specific to one particular country.

This commit is contained in:
Alexis de Lattre
2015-09-24 10:00:45 +02:00
committed by Pedro M. Baeza
parent 212f5f992e
commit adedae3bc2
9 changed files with 8 additions and 429 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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'
],

View File

@@ -1,4 +1,4 @@
from . import account_payment
# -*- coding: utf-8 -*-
from . import payment_line
from . import account_move_line
from . import account_invoice

View File

@@ -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

View File

@@ -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)

View File

@@ -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')

View File

@@ -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>

View File

@@ -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>