mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
Merge pull request #65 from acsone/8.0-refactor-account_banking_payment-sbi
8.0 migration of account_banking_payment
This commit is contained in:
@@ -621,22 +621,6 @@ class account_bank_statement_line(orm.Model):
|
||||
|
||||
|
||||
class invoice(orm.Model):
|
||||
'''
|
||||
Create other reference types as well.
|
||||
|
||||
Descendant classes can extend this function to add more reference
|
||||
types, ie.
|
||||
|
||||
def _get_reference_type(self, cr, uid, context=None):
|
||||
return super(my_class, self)._get_reference_type(cr, uid,
|
||||
context=context) + [('my_ref', _('My reference')]
|
||||
|
||||
Don't forget to redefine the column "reference_type" as below or
|
||||
your method will never be triggered.
|
||||
|
||||
TODO: move 'structured' part to account_banking_payment module
|
||||
where it belongs
|
||||
'''
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
def test_undo_paid(self, cr, uid, ids, context=None):
|
||||
@@ -649,35 +633,3 @@ class invoice(orm.Model):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _get_reference_type(self, cr, uid, context=None):
|
||||
'''
|
||||
Return the list of reference types
|
||||
'''
|
||||
return [('none', _('Free Reference')),
|
||||
('structured', _('Structured Reference')),
|
||||
]
|
||||
|
||||
_columns = {
|
||||
'reference_type': fields.selection(_get_reference_type,
|
||||
'Reference Type', required=True
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
class account_move_line(orm.Model):
|
||||
_inherit = "account.move.line"
|
||||
|
||||
def get_balance(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Return the balance of any set of move lines.
|
||||
|
||||
Not to be confused with the 'balance' field on this model, which
|
||||
returns the account balance that the move line applies to.
|
||||
"""
|
||||
total = 0.0
|
||||
if not ids:
|
||||
return total
|
||||
for line in self.read(
|
||||
cr, uid, ids, ['debit', 'credit'], context=context):
|
||||
total += (line['debit'] or 0.0) - (line['credit'] or 0.0)
|
||||
return total
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
import model
|
||||
@@ -1,7 +0,0 @@
|
||||
import account_payment
|
||||
import payment_line
|
||||
import payment_mode
|
||||
import payment_order_create
|
||||
import banking_import_transaction
|
||||
import banking_transaction_wizard
|
||||
import banking_import_line
|
||||
@@ -1,346 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools.translate import _
|
||||
from openerp import netsvc
|
||||
|
||||
|
||||
class payment_order(orm.Model):
|
||||
'''
|
||||
Enable extra states for payment exports
|
||||
'''
|
||||
_inherit = 'payment.order'
|
||||
|
||||
_columns = {
|
||||
'date_scheduled': fields.date(
|
||||
'Scheduled date if fixed',
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help='Select a date if you have chosen Preferred Date to be fixed.'
|
||||
),
|
||||
'reference': fields.char(
|
||||
'Reference', size=128, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'mode': fields.many2one(
|
||||
'payment.mode', 'Payment mode', select=True, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help='Select the Payment Mode to be applied.',
|
||||
),
|
||||
'state': fields.selection([
|
||||
('draft', 'Draft'),
|
||||
('open', 'Confirmed'),
|
||||
('cancel', 'Cancelled'),
|
||||
('sent', 'Sent'),
|
||||
('rejected', 'Rejected'),
|
||||
('done', 'Done'),
|
||||
], 'State', select=True
|
||||
),
|
||||
'line_ids': fields.one2many(
|
||||
'payment.line', 'order_id', 'Payment lines',
|
||||
states={
|
||||
'open': [('readonly', True)],
|
||||
'cancel': [('readonly', True)],
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'user_id': fields.many2one(
|
||||
'res.users', 'User', required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
),
|
||||
'date_prefered': fields.selection([
|
||||
('now', 'Directly'),
|
||||
('due', 'Due date'),
|
||||
('fixed', 'Fixed date')
|
||||
], "Preferred date", change_default=True, required=True,
|
||||
states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
},
|
||||
help=("Choose an option for the Payment Order:'Fixed' stands for "
|
||||
"a date specified by you.'Directly' stands for the direct "
|
||||
"execution.'Due date' stands for the scheduled date of "
|
||||
"execution."
|
||||
)
|
||||
),
|
||||
'date_sent': fields.date('Send date', readonly=True),
|
||||
}
|
||||
|
||||
def _write_payment_lines(self, cr, uid, ids, **kwargs):
|
||||
'''
|
||||
ORM method for setting attributes of corresponding payment.line
|
||||
objects.
|
||||
Note that while this is ORM compliant, it is also very ineffecient due
|
||||
to the absence of filters on writes and hence the requirement to
|
||||
filter on the client(=OpenERP server) side.
|
||||
'''
|
||||
if not hasattr(ids, '__iter__'):
|
||||
ids = [ids]
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
line_ids = payment_line_obj.search(
|
||||
cr, uid, [
|
||||
('order_id', 'in', ids)
|
||||
])
|
||||
payment_line_obj.write(cr, uid, line_ids, kwargs)
|
||||
|
||||
def action_rejected(self, cr, uid, ids, *args):
|
||||
'''
|
||||
Set both self and payment lines to state 'rejected'.
|
||||
'''
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for id in ids:
|
||||
wf_service.trg_validate(uid, 'payment.order', id, 'rejected', cr)
|
||||
return True
|
||||
|
||||
def set_done(self, cr, uid, ids, *args):
|
||||
'''
|
||||
Extend standard transition to update children as well.
|
||||
'''
|
||||
self._write_payment_lines(
|
||||
cr, uid, ids,
|
||||
date_done=fields.date.context_today(self, cr, uid))
|
||||
return super(payment_order, self).set_done(
|
||||
cr, uid, ids, *args
|
||||
)
|
||||
|
||||
def debit_reconcile_transfer(self, cr, uid, payment_order_id,
|
||||
amount, currency, context=None):
|
||||
"""
|
||||
During import of bank statements, create the reconcile on the transfer
|
||||
account containing all the open move lines on the transfer account.
|
||||
"""
|
||||
move_line_obj = self.pool.get('account.move.line')
|
||||
order = self.browse(cr, uid, payment_order_id, context)
|
||||
line_ids = []
|
||||
reconcile_id = False
|
||||
if not order.line_ids[0].transit_move_line_id:
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
wf_service.trg_validate(
|
||||
uid, 'payment.order', payment_order_id, 'done', cr)
|
||||
return False
|
||||
for order_line in order.line_ids:
|
||||
for line in order_line.transit_move_line_id.move_id.line_id:
|
||||
if line.account_id.type == 'other' and not line.reconcile_id:
|
||||
line_ids.append(line.id)
|
||||
if self.pool.get('res.currency').is_zero(
|
||||
cr, uid, currency,
|
||||
move_line_obj.get_balance(cr, uid, line_ids) - amount):
|
||||
reconcile_id = self.pool.get('account.move.reconcile').create(
|
||||
cr, uid,
|
||||
{'type': 'auto', 'line_id': [(6, 0, line_ids)]},
|
||||
context)
|
||||
# set direct debit order to finished state
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
wf_service.trg_validate(
|
||||
uid, 'payment.order', payment_order_id, 'done', cr)
|
||||
return reconcile_id
|
||||
|
||||
def debit_unreconcile_transfer(
|
||||
self, cr, uid, payment_order_id, reconcile_id, amount, currency,
|
||||
context=None):
|
||||
"""
|
||||
Due to a cancelled bank statements import, unreconcile the move on
|
||||
the transfer account. Delegate the conditions to the workflow.
|
||||
Raise on failure for rollback.
|
||||
|
||||
Workflow appears to return False even on success so we just check
|
||||
the order's state that we know to be set to 'sent' in that case.
|
||||
"""
|
||||
self.pool.get('account.move.reconcile').unlink(
|
||||
cr, uid, [reconcile_id], context=context)
|
||||
netsvc.LocalService('workflow').trg_validate(
|
||||
uid, 'payment.order', payment_order_id, 'undo_done', cr)
|
||||
state = self.pool.get('payment.order').read(
|
||||
cr, uid, payment_order_id, ['state'], context=context)['state']
|
||||
if state != 'sent':
|
||||
raise orm.except_orm(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile payment order: "
|
||||
"Workflow will not allow it."))
|
||||
return True
|
||||
|
||||
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.
|
||||
|
||||
Test if the payment order has not been reconciled. Depends
|
||||
on the restriction that transit move lines should use an
|
||||
account of type 'other', and on the restriction of payment
|
||||
and debit orders that they only take moves on accounts
|
||||
payable/receivable.
|
||||
"""
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
for order_line in order.line_ids:
|
||||
if order_line.transit_move_line_id.move_id:
|
||||
for line in \
|
||||
order_line.transit_move_line_id.move_id.line_id:
|
||||
if (line.account_id.type == 'other' and
|
||||
line.reconcile_id):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _prepare_transfer_move(
|
||||
self, cr, uid, order, line, labels, context=None):
|
||||
vals = {
|
||||
'journal_id': order.mode.transfer_journal_id.id,
|
||||
'name': '%s %s' % (labels[order.payment_order_type],
|
||||
line.move_line_id
|
||||
and line.move_line_id.move_id.name
|
||||
or line.communication),
|
||||
'ref': '%s %s' % (order.payment_order_type[:3].upper(),
|
||||
line.move_line_id
|
||||
and line.move_line_id.move_id.name
|
||||
or line.communication),
|
||||
}
|
||||
return vals
|
||||
|
||||
def _prepare_move_line_transfer_account(
|
||||
self, cr, uid, order, line, move_id, labels, context=None):
|
||||
vals = {
|
||||
'name': _('%s for %s') % (
|
||||
labels[order.payment_order_type],
|
||||
line.move_line_id and (line.move_line_id.invoice
|
||||
and line.move_line_id.invoice.number
|
||||
or line.move_line_id.name)
|
||||
or line.communication),
|
||||
'move_id': move_id,
|
||||
'partner_id': False,
|
||||
'account_id': order.mode.transfer_account_id.id,
|
||||
'credit': (order.payment_order_type == 'payment'
|
||||
and line.amount or 0.0),
|
||||
'debit': (order.payment_order_type == 'debit'
|
||||
and line.amount or 0.0),
|
||||
'date': fields.date.context_today(
|
||||
self, cr, uid, context=context),
|
||||
}
|
||||
return vals
|
||||
|
||||
def _update_move_line_partner_account(
|
||||
self, cr, uid, order, line, vals, context=None):
|
||||
vals.update({
|
||||
'partner_id': line.partner_id.id,
|
||||
'account_id': (line.move_line_id
|
||||
and line.move_line_id.account_id.id
|
||||
or False),
|
||||
# if not line.move_line_id, the field 'account_id' must be set by
|
||||
# another module that inherit this function, like for example in
|
||||
# the module purchase_payment_order
|
||||
'credit': (order.payment_order_type == 'debit'
|
||||
and line.amount or 0.0),
|
||||
'debit': (order.payment_order_type == 'payment'
|
||||
and line.amount or 0.0),
|
||||
})
|
||||
return vals
|
||||
|
||||
def action_sent_no_move_line_hook(self, cr, uid, pay_line, context=None):
|
||||
"""This function is designed to be inherited"""
|
||||
return
|
||||
|
||||
def action_sent(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Create the moves that pay off the move lines from
|
||||
the debit order. This happens when the debit order file is
|
||||
generated.
|
||||
"""
|
||||
account_move_obj = self.pool.get('account.move')
|
||||
account_move_line_obj = self.pool.get('account.move.line')
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
labels = {
|
||||
'payment': _('Payment order'),
|
||||
'debit': _('Direct debit order'),
|
||||
}
|
||||
for order in self.browse(cr, uid, ids, context=context):
|
||||
if not order.mode.transfer_journal_id \
|
||||
or not order.mode.transfer_account_id:
|
||||
continue
|
||||
for line in order.line_ids:
|
||||
# basic checks
|
||||
if line.move_line_id and line.move_line_id.reconcile_id:
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('Move line %s has already been paid/reconciled')
|
||||
% line.move_line_id.name)
|
||||
|
||||
move_id = account_move_obj.create(
|
||||
cr, uid, self._prepare_transfer_move(
|
||||
cr, uid, order, line, labels, context=context),
|
||||
context=context)
|
||||
|
||||
# TODO: take multicurrency into account
|
||||
|
||||
# create the debit move line on the transfer account
|
||||
ml_vals = self._prepare_move_line_transfer_account(
|
||||
cr, uid, order, line, move_id, labels, context=context)
|
||||
account_move_line_obj.create(cr, uid, ml_vals, context=context)
|
||||
|
||||
# create the debit move line on the partner account
|
||||
self._update_move_line_partner_account(
|
||||
cr, uid, order, line, ml_vals, context=context)
|
||||
reconcile_move_line_id = account_move_line_obj.create(
|
||||
cr, uid, ml_vals, context=context)
|
||||
|
||||
# register the debit move line on the payment line
|
||||
# and call reconciliation on it
|
||||
payment_line_obj.write(
|
||||
cr, uid, line.id,
|
||||
{'transit_move_line_id': reconcile_move_line_id},
|
||||
context=context)
|
||||
|
||||
if line.move_line_id:
|
||||
payment_line_obj.debit_reconcile(
|
||||
cr, uid, line.id, context=context)
|
||||
else:
|
||||
self.action_sent_no_move_line_hook(
|
||||
cr, uid, line, context=context)
|
||||
account_move_obj.post(cr, uid, [move_id], context=context)
|
||||
|
||||
# State field is written by act_sent_wait
|
||||
self.write(cr, uid, ids, {
|
||||
'date_sent': fields.date.context_today(
|
||||
self, cr, uid, context=context),
|
||||
}, context=context)
|
||||
|
||||
return True
|
||||
@@ -1,410 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp import netsvc
|
||||
from openerp.tools.translate import _
|
||||
from openerp.addons.decimal_precision import decimal_precision as dp
|
||||
from openerp.addons.account_banking.parsers.models import (
|
||||
mem_bank_transaction as bt
|
||||
)
|
||||
|
||||
|
||||
class banking_import_transaction(orm.Model):
|
||||
_inherit = 'banking.import.transaction'
|
||||
|
||||
def _match_payment_order(
|
||||
self, cr, uid, trans, log, order_type='payment', context=None):
|
||||
|
||||
def equals_order_amount(payment_order, transferred_amount):
|
||||
if (not hasattr(payment_order, 'payment_order_type')
|
||||
or payment_order.payment_order_type == 'payment'):
|
||||
sign = 1
|
||||
else:
|
||||
sign = -1
|
||||
total = payment_order.total + sign * transferred_amount
|
||||
return self.pool.get('res.currency').is_zero(
|
||||
cr, uid, trans.statement_line_id.statement_id.currency, total)
|
||||
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
|
||||
order_ids = payment_order_obj.search(
|
||||
cr, uid, [('payment_order_type', '=', order_type),
|
||||
('state', '=', 'sent'),
|
||||
('date_sent', '<=', trans.execution_date),
|
||||
],
|
||||
limit=0, context=context)
|
||||
orders = payment_order_obj.browse(cr, uid, order_ids, context)
|
||||
candidates = [x for x in orders if
|
||||
equals_order_amount(x, trans.statement_line_id.amount)]
|
||||
if len(candidates) > 0:
|
||||
# retrieve the common account_id, if any
|
||||
account_id = False
|
||||
transit_move_lines = candidates[0].line_ids[0].transit_move_line_id
|
||||
if transit_move_lines:
|
||||
for line in transit_move_lines.move_id.line_id:
|
||||
if line.account_id.type == 'other':
|
||||
account_id = line.account_id.id
|
||||
break
|
||||
return dict(
|
||||
move_line_ids=False,
|
||||
match_type='payment_order',
|
||||
payment_order_ids=[x.id for x in candidates],
|
||||
account_id=account_id,
|
||||
partner_id=False,
|
||||
partner_bank_id=False,
|
||||
reference=False,
|
||||
type='general',
|
||||
)
|
||||
return False
|
||||
|
||||
def _match_storno(
|
||||
self, cr, uid, trans, log, context=None):
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
line_ids = payment_line_obj.search(
|
||||
cr, uid, [
|
||||
('order_id.payment_order_type', '=', 'debit'),
|
||||
('order_id.state', 'in', ['sent', 'done']),
|
||||
('communication', '=', trans.reference)
|
||||
], context=context)
|
||||
# stornos MUST have an exact match
|
||||
if len(line_ids) == 1:
|
||||
account_id = payment_line_obj.get_storno_account_id(
|
||||
cr, uid, line_ids[0], trans.statement_line_id.amount,
|
||||
trans.statement_id.currency, context=None)
|
||||
if account_id:
|
||||
return dict(
|
||||
account_id=account_id,
|
||||
match_type='storno',
|
||||
payment_line_id=line_ids[0],
|
||||
move_line_ids=False,
|
||||
partner_id=False,
|
||||
partner_bank_id=False,
|
||||
reference=False,
|
||||
type='customer',
|
||||
)
|
||||
# TODO log the reason why there is no result for transfers marked
|
||||
# as storno
|
||||
return False
|
||||
|
||||
def _match_payment(self, cr, uid, trans, payment_lines,
|
||||
partner_ids, bank_account_ids, log, linked_payments):
|
||||
'''
|
||||
Find the payment order belonging to this reference - if there is one
|
||||
This is the easiest part: when sending payments, the returned bank info
|
||||
should be identical to ours.
|
||||
This also means that we do not allow for multiple candidates.
|
||||
'''
|
||||
# TODO: Not sure what side effects are created when payments are done
|
||||
# for credited customer invoices, which will be matched later on too.
|
||||
|
||||
def bank_match(account, partner_bank):
|
||||
"""
|
||||
Returns whether a given account number is equivalent to a
|
||||
partner bank in the database. We simply call the search method,
|
||||
which checks bank account number, disregarding spaces.
|
||||
|
||||
:param account: string representation of a bank account number
|
||||
:param partner_bank: browse record of model res.partner.bank
|
||||
"""
|
||||
return partner_bank.id in self.pool['res.partner.bank'].search(
|
||||
cr, uid, [('acc_number', '=', account)])
|
||||
|
||||
digits = dp.get_precision('Account')(cr)[1]
|
||||
candidates = [
|
||||
line for line in payment_lines
|
||||
if (line.communication == trans.reference
|
||||
and round(line.amount, digits) == -round(
|
||||
trans.statement_line_id.amount, digits)
|
||||
and bank_match(trans.remote_account, line.bank_id))
|
||||
]
|
||||
if len(candidates) == 1:
|
||||
candidate = candidates[0]
|
||||
# Check cache to prevent multiple matching of a single payment
|
||||
if candidate.id not in linked_payments:
|
||||
linked_payments[candidate.id] = True
|
||||
move_info = self._get_move_info(
|
||||
cr, uid, [candidate.move_line_id.id])
|
||||
move_info.update({
|
||||
'match_type': 'payment',
|
||||
'payment_line_id': candidate.id,
|
||||
})
|
||||
return move_info
|
||||
|
||||
return False
|
||||
|
||||
def _confirm_storno(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Creation of the reconciliation has been delegated to
|
||||
*a* direct debit module, to allow for various direct debit styles
|
||||
"""
|
||||
payment_line_pool = self.pool.get('payment.line')
|
||||
statement_line_pool = self.pool.get('account.bank.statement.line')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_line_id:
|
||||
raise orm.except_orm(
|
||||
_("Cannot link with storno"),
|
||||
_("No direct debit order item"))
|
||||
reconcile_id = payment_line_pool.debit_storno(
|
||||
cr, uid,
|
||||
transaction.payment_line_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency,
|
||||
transaction.storno_retry,
|
||||
context=context)
|
||||
statement_line_pool.write(
|
||||
cr, uid, transaction.statement_line_id.id,
|
||||
{'reconcile_id': reconcile_id}, context=context)
|
||||
transaction.refresh()
|
||||
|
||||
def _confirm_payment_order(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Creation of the reconciliation has been delegated to
|
||||
*a* direct debit module, to allow for various direct debit styles
|
||||
"""
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
statement_line_pool = self.pool.get('account.bank.statement.line')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_order_id:
|
||||
raise orm.except_orm(
|
||||
_("Cannot reconcile"),
|
||||
_("Cannot reconcile: no direct debit order"))
|
||||
reconcile_id = payment_order_obj.debit_reconcile_transfer(
|
||||
cr, uid,
|
||||
transaction.payment_order_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency,
|
||||
context=context)
|
||||
statement_line_pool.write(
|
||||
cr, uid, transaction.statement_line_id.id,
|
||||
{'reconcile_id': reconcile_id}, context=context)
|
||||
|
||||
def _confirm_payment(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Do some housekeeping on the payment line
|
||||
then pass on to _reconcile_move
|
||||
"""
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
payment_line_obj.write(
|
||||
cr, uid, transaction.payment_line_id.id, {
|
||||
'date_done': transaction.statement_line_id.date,
|
||||
}
|
||||
)
|
||||
self._confirm_move(cr, uid, transaction_id, context=context)
|
||||
# Check if the payment order is 'done'
|
||||
order_id = transaction.payment_line_id.order_id.id
|
||||
other_lines = payment_line_obj.search(
|
||||
cr, uid, [
|
||||
('order_id', '=', order_id),
|
||||
('date_done', '=', False),
|
||||
], context=context)
|
||||
if not other_lines:
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
wf_service.trg_validate(
|
||||
uid, 'payment.order', order_id, 'done', cr)
|
||||
|
||||
def _cancel_payment(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
Do not support cancelling individual lines yet, because the workflow
|
||||
of the payment order does not support reopening.
|
||||
"""
|
||||
raise orm.except_orm(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile: this operation is not yet supported for "
|
||||
"match type 'payment'"))
|
||||
|
||||
def _cancel_payment_order(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
"""
|
||||
payment_order_obj = self.pool.get('payment.order')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_order_id:
|
||||
raise orm.except_orm(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile: no payment or direct debit order"))
|
||||
if not transaction.statement_line_id.reconcile_id:
|
||||
raise orm.except_orm(
|
||||
_("Cannot unreconcile"),
|
||||
_("Payment orders without transfer move lines cannot be "
|
||||
"unreconciled this way"))
|
||||
return payment_order_obj.debit_unreconcile_transfer(
|
||||
cr, uid, transaction.payment_order_id.id,
|
||||
transaction.statement_line_id.reconcile_id.id,
|
||||
transaction.statement_line_id.amount,
|
||||
transaction.statement_line_id.currency)
|
||||
|
||||
def _cancel_storno(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
"""
|
||||
TODO: delegate unreconciliation to the direct debit module,
|
||||
to allow for various direct debit styles
|
||||
"""
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
reconcile_obj = self.pool.get('account.move.reconcile')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
|
||||
if not transaction.payment_line_id:
|
||||
raise orm.except_orm(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("No direct debit order item"))
|
||||
if not transaction.payment_line_id.storno:
|
||||
raise orm.except_orm(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("The direct debit order item is not marked for storno"))
|
||||
|
||||
journal = transaction.statement_line_id.statement_id.journal_id
|
||||
if transaction.statement_line_id.amount >= 0:
|
||||
account_id = journal.default_credit_account_id.id
|
||||
else:
|
||||
account_id = journal.default_debit_account_id.id
|
||||
cancel_line = False
|
||||
move_lines = []
|
||||
for move in transaction.statement_line_id.move_ids:
|
||||
# There should usually be just one move, I think
|
||||
move_lines += move.line_id
|
||||
for line in move_lines:
|
||||
if line.account_id.id != account_id:
|
||||
cancel_line = line
|
||||
break
|
||||
if not cancel_line:
|
||||
raise orm.except_orm(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("Line id not found"))
|
||||
reconcile = (cancel_line.reconcile_id
|
||||
or cancel_line.reconcile_partial_id)
|
||||
lines_reconcile = reconcile.line_id or reconcile.line_partial_ids
|
||||
if len(lines_reconcile) < 3:
|
||||
# delete the full reconciliation
|
||||
reconcile_obj.unlink(cr, uid, reconcile.id, context)
|
||||
else:
|
||||
# we are left with a partial reconciliation
|
||||
reconcile_obj.write(
|
||||
cr, uid, reconcile.id,
|
||||
{'line_partial_ids':
|
||||
[(6, 0, [x.id for x in lines_reconcile
|
||||
if x.id != cancel_line.id])],
|
||||
'line_id': [(6, 0, [])],
|
||||
}, context)
|
||||
# redo the original payment line reconciliation with the invoice
|
||||
payment_line_obj.write(
|
||||
cr, uid, transaction.payment_line_id.id,
|
||||
{'storno': False}, context)
|
||||
payment_line_obj.debit_reconcile(
|
||||
cr, uid, transaction.payment_line_id.id, context)
|
||||
|
||||
_columns = {
|
||||
'payment_order_ids': fields.many2many(
|
||||
'payment.order', 'banking_transaction_payment_order_rel',
|
||||
'order_id', 'transaction_id', 'Payment orders'),
|
||||
'payment_order_id': fields.many2one(
|
||||
'payment.order', 'Payment order to reconcile'),
|
||||
'payment_line_id': fields.many2one('payment.line', 'Payment line'),
|
||||
}
|
||||
|
||||
def _get_match_multi(self, cr, uid, ids, name, args, context=None):
|
||||
if not ids:
|
||||
return {}
|
||||
res = super(banking_import_transaction, self)._get_match_multi(
|
||||
cr, uid, ids, name, args, context=context)
|
||||
for transaction in self.browse(cr, uid, ids, context):
|
||||
if transaction.match_type == 'payment_order':
|
||||
if (transaction.payment_order_ids and not
|
||||
transaction.payment_order_id):
|
||||
res[transaction.id] = True
|
||||
return res
|
||||
|
||||
def clear_and_write(self, cr, uid, ids, vals=None, context=None):
|
||||
write_vals = {
|
||||
'payment_line_id': False,
|
||||
'payment_order_id': False,
|
||||
'payment_order_ids': [(6, 0, [])],
|
||||
}
|
||||
write_vals.update(vals or {})
|
||||
return super(banking_import_transaction, self).clear_and_write(
|
||||
cr, uid, ids, vals=vals, context=context)
|
||||
|
||||
def move_info2values(self, move_info):
|
||||
vals = super(banking_import_transaction, self).move_info2values(
|
||||
move_info)
|
||||
vals['payment_line_id'] = move_info.get('payment_line_id', False)
|
||||
vals['payment_order_ids'] = [
|
||||
(6, 0, move_info.get('payment_order_ids') or [])]
|
||||
vals['payment_order_id'] = (
|
||||
move_info.get('payment_order_ids', False) and
|
||||
len(move_info['payment_order_ids']) == 1 and
|
||||
move_info['payment_order_ids'][0]
|
||||
)
|
||||
return vals
|
||||
|
||||
def hook_match_payment(self, cr, uid, transaction, log, context=None):
|
||||
"""
|
||||
Called from match() in the core module.
|
||||
Match payment batches, direct debit orders and stornos
|
||||
"""
|
||||
move_info = False
|
||||
if transaction.type == bt.PAYMENT_BATCH:
|
||||
move_info = self._match_payment_order(
|
||||
cr, uid, transaction, log,
|
||||
order_type='payment', context=context)
|
||||
elif transaction.type == bt.DIRECT_DEBIT:
|
||||
move_info = self._match_payment_order(
|
||||
cr, uid, transaction, log,
|
||||
order_type='debit', context=context)
|
||||
elif transaction.type == bt.STORNO:
|
||||
move_info = self._match_storno(
|
||||
cr, uid, transaction, log,
|
||||
context=context)
|
||||
return move_info
|
||||
|
||||
def __init__(self, pool, cr):
|
||||
"""
|
||||
Updating the function maps to handle the match types that this
|
||||
module adds.
|
||||
"""
|
||||
super(banking_import_transaction, self).__init__(pool, cr)
|
||||
|
||||
self.confirm_map.update({
|
||||
'storno': banking_import_transaction._confirm_storno,
|
||||
'payment_order': banking_import_transaction._confirm_payment_order,
|
||||
'payment': banking_import_transaction._confirm_payment,
|
||||
'payment_order_manual': (
|
||||
banking_import_transaction._confirm_payment_order),
|
||||
'payment_manual': banking_import_transaction._confirm_payment,
|
||||
})
|
||||
|
||||
self.cancel_map.update({
|
||||
'storno': banking_import_transaction._cancel_storno,
|
||||
'payment_order': banking_import_transaction._cancel_payment_order,
|
||||
'payment': banking_import_transaction._cancel_payment,
|
||||
'payment_order_manual': (
|
||||
banking_import_transaction._cancel_payment_order),
|
||||
'payment_manual': banking_import_transaction._cancel_payment,
|
||||
})
|
||||
@@ -1,122 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class banking_transaction_wizard(orm.TransientModel):
|
||||
_inherit = 'banking.transaction.wizard'
|
||||
|
||||
def write(self, cr, uid, ids, vals, context=None):
|
||||
"""
|
||||
Check for manual payment orders or lines
|
||||
"""
|
||||
if not vals or not ids:
|
||||
return True
|
||||
manual_payment_order_id = vals.pop('manual_payment_order_id', False)
|
||||
manual_payment_line_id = vals.pop('manual_payment_line_id', False)
|
||||
res = super(banking_transaction_wizard, self).write(
|
||||
cr, uid, ids, vals, context=context)
|
||||
if manual_payment_order_id or manual_payment_line_id:
|
||||
transaction_id = self.browse(
|
||||
cr, uid, ids[0],
|
||||
context=context).import_transaction_id
|
||||
write_vals = {}
|
||||
if manual_payment_order_id:
|
||||
payment_order = self.pool.get('payment.order').browse(
|
||||
cr, uid, manual_payment_order_id,
|
||||
context=context)
|
||||
if payment_order.payment_order_type == 'payment':
|
||||
sign = 1
|
||||
else:
|
||||
sign = -1
|
||||
total = (payment_order.total + sign *
|
||||
transaction_id.statement_line_id.amount)
|
||||
if not self.pool.get('res.currency').is_zero(
|
||||
cr, uid,
|
||||
transaction_id.statement_line_id.statement_id.currency,
|
||||
total):
|
||||
raise orm.except_orm(
|
||||
_('Error'),
|
||||
_('When matching a payment order, the amounts have to '
|
||||
'match exactly'))
|
||||
|
||||
if (payment_order.mode
|
||||
and payment_order.mode.transfer_account_id):
|
||||
transaction_id.statement_line_id.write({
|
||||
'account_id': (
|
||||
payment_order.mode.transfer_account_id.id),
|
||||
})
|
||||
write_vals.update(
|
||||
{'payment_order_id': manual_payment_order_id,
|
||||
'match_type': 'payment_order_manual'})
|
||||
else:
|
||||
write_vals.update(
|
||||
{'payment_line_id': manual_payment_line_id,
|
||||
'match_type': 'payment_manual'})
|
||||
self.pool.get('banking.import.transaction').clear_and_write(
|
||||
cr, uid, transaction_id.id, write_vals, context=context)
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'payment_line_id': fields.related(
|
||||
'import_transaction_id',
|
||||
'payment_line_id',
|
||||
string="Matching payment or storno",
|
||||
type='many2one',
|
||||
relation='payment.line',
|
||||
readonly=True,
|
||||
),
|
||||
'payment_order_ids': fields.related(
|
||||
'import_transaction_id',
|
||||
'payment_order_ids',
|
||||
string="Matching payment orders",
|
||||
type='many2many',
|
||||
relation='payment.order',
|
||||
),
|
||||
'payment_order_id': fields.related(
|
||||
'import_transaction_id',
|
||||
'payment_order_id',
|
||||
string="Payment order to reconcile",
|
||||
type='many2one',
|
||||
relation='payment.order',
|
||||
),
|
||||
'manual_payment_order_id': fields.many2one(
|
||||
'payment.order',
|
||||
'Match this payment order',
|
||||
domain=[
|
||||
('state', '=', 'sent'),
|
||||
],
|
||||
),
|
||||
'manual_payment_line_id': fields.many2one(
|
||||
'payment.line',
|
||||
'Match this payment line',
|
||||
domain=[
|
||||
('order_id.state', '=', 'sent'),
|
||||
('date_done', '=', False),
|
||||
],
|
||||
),
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- Make buttons on payment order sensitive for extra states,
|
||||
restore wizard functionality when making payments
|
||||
-->
|
||||
|
||||
<record id="view_banking_payment_order_form_1" model="ir.ui.view">
|
||||
<field name="name">account.payment.order.form.banking-1</field>
|
||||
<field name="inherit_id" ref="account_payment.view_payment_order_form" />
|
||||
<field name="model">payment.order</field>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//button[@string='Select Invoices to Pay']"
|
||||
position="attributes">
|
||||
<attribute name="attrs">{
|
||||
'invisible':[('state','!=','draft')]
|
||||
}</attribute>
|
||||
</xpath>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -1,43 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<record model="ir.ui.view" id="transaction_wizard">
|
||||
<field name="name">transaction.wizard</field>
|
||||
<field name="model">banking.transaction.wizard</field>
|
||||
<field name="inherit_id"
|
||||
ref="account_banking.transaction_wizard_first" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="invoice_ids" position="before">
|
||||
<field name="payment_order_ids" invisible="True"/>
|
||||
</field>
|
||||
<xpath expr="//group/separator[@string='Multiple matches']/.."
|
||||
position="after">
|
||||
<group>
|
||||
<field name='payment_line_id'
|
||||
attrs="{'invisible': [('match_type', 'not in',
|
||||
('storno', 'payment', 'payment_manual'))]}" />
|
||||
</group>
|
||||
</xpath>
|
||||
<field name="move_line_id" position="after">
|
||||
<group>
|
||||
<field name='payment_order_id'
|
||||
attrs="{'readonly': [('match_multi', '=', False)],
|
||||
'invisible': [('match_type', 'not in',
|
||||
('payment_order', 'payment_order_manual'))]}"
|
||||
domain="[('id', 'in', payment_order_ids[0][2])]" />
|
||||
</group>
|
||||
</field>
|
||||
<page string="Manual match" position="after">
|
||||
<page string="Match Payments">
|
||||
<separator string="Match this payment line" colspan="4"/>
|
||||
<field name="manual_payment_line_id"/>
|
||||
<separator string="Match this payment order" colspan="4"/>
|
||||
<field name="manual_payment_order_id"/>
|
||||
</page>
|
||||
</page>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!--
|
||||
Add the payment mode type and transfer settings
|
||||
-->
|
||||
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.form.inherit</field>
|
||||
<field name="model">payment.mode</field>
|
||||
<field name="inherit_id" ref="account_banking_payment_export.view_payment_mode_form_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<field name="type" position="after">
|
||||
<group colspan="4" col="4">
|
||||
<group colspan="2">
|
||||
<separator colspan="2"
|
||||
string="Transfer move settings" />
|
||||
<field name="transfer_account_id"
|
||||
domain="[('type', '=', 'other'),
|
||||
('reconcile', '=', True),
|
||||
('company_id', '=', company_id)]"
|
||||
context="{
|
||||
'default_type': 'other',
|
||||
'default_reconcile': True,
|
||||
'default_company_id': company_id}"
|
||||
/>
|
||||
<field name="transfer_journal_id"
|
||||
domain="[('company_id', '=', company_id)]"
|
||||
/>
|
||||
</group>
|
||||
<group colspan="2">
|
||||
<separator colspan="2"
|
||||
string="Optional filter by payment term" />
|
||||
<field name="payment_term_ids" nolabel="1" colspan="2"/>
|
||||
</group>
|
||||
</group>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -75,8 +75,6 @@ for electronic banking. It provides the following technical features:
|
||||
To enable the use of payment order to collect money for customers,
|
||||
it adds a payment_order_type (payment|debit) as a basis of direct debit support
|
||||
(this field becomes visible when account_direct_debit is installed).
|
||||
Refactoring note: this field should ideally go in account_direct_debit,
|
||||
but account_banking_payment currently depends on it.
|
||||
|
||||
Bug fixes and enhancement that should land in official addons:
|
||||
|
||||
|
||||
@@ -30,6 +30,14 @@
|
||||
<field name="country" ref="base.fr"/>
|
||||
</record>
|
||||
|
||||
<record id="bank_fortis" model="res.bank">
|
||||
<field name="name">BNP Paribas Fortis Charleroi</field>
|
||||
<field name="bic">GEBABEBB03A</field>
|
||||
<field name="city">Charleroi</field>
|
||||
<field name="country" ref="base.be"/>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="main_company_iban" model="res.partner.bank">
|
||||
<field name="acc_number">FR76 4242 4242 4242 4242 4242 424</field>
|
||||
<field name="state">iban</field>
|
||||
@@ -57,6 +65,15 @@
|
||||
<field name="bank_bic">FTNOFRP1XXX</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_2_iban" model="res.partner.bank">
|
||||
<field name="acc_number">BE96 9988 7766 5544</field>
|
||||
<field name="state">iban</field>
|
||||
<field name="bank" ref="bank_fortis"/>
|
||||
<field name="partner_id" ref="base.res_partner_2" />
|
||||
<field name="bank_name">BNP Paribas Fortis Charleroi</field>
|
||||
<field name="bank_bic">GEBABEBB03A</field>
|
||||
</record>
|
||||
|
||||
<record id="account_payment.payment_mode_1" model="payment.mode">
|
||||
<field name="type" ref="account_banking_payment_export.manual_bank_tranfer"/>
|
||||
</record>
|
||||
|
||||
@@ -3,3 +3,4 @@ from . import account_payment
|
||||
from . import payment_mode
|
||||
from . import payment_mode_type
|
||||
from . import account_move_line
|
||||
from . import account_invoice
|
||||
|
||||
35
account_banking_payment_export/models/account_invoice.py
Normal file
35
account_banking_payment_export/models/account_invoice.py
Normal file
@@ -0,0 +1,35 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2014 ACSONE SA (<http://acsone.eu>).
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import api, models, _
|
||||
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_inherit = 'account.invoice'
|
||||
|
||||
@api.model
|
||||
def _get_reference_type(self):
|
||||
rt = super(AccountInvoice, self)._get_reference_type()
|
||||
rt.append(('structured', _('Structured Reference')))
|
||||
return rt
|
||||
@@ -23,20 +23,37 @@ from openerp.osv import orm, fields
|
||||
from operator import itemgetter
|
||||
|
||||
|
||||
# All the code below aims at fixing one small issue in _to_pay_search()
|
||||
# But _to_pay_search() is the search function of the field 'amount_to_pay'
|
||||
# which is a field.function and these functions are not inheritable in OpenERP.
|
||||
# So we have to inherit the field 'amount_to_pay' and duplicate the related
|
||||
# functions
|
||||
# If the patch that I proposed in this bug report
|
||||
# https://bugs.launchpad.net/openobject-addons/+bug/1275478
|
||||
# is integrated in addons/account_payment, then we will be able to remove this
|
||||
# file. -- Alexis de Lattre
|
||||
class AccountMoveLine(orm.Model):
|
||||
_inherit = 'account.move.line'
|
||||
|
||||
def get_balance(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Return the balance of any set of move lines.
|
||||
|
||||
Not to be confused with the 'balance' field on this model, which
|
||||
returns the account balance that the move line applies to.
|
||||
"""
|
||||
total = 0.0
|
||||
if not ids:
|
||||
return total
|
||||
for line in self.read(
|
||||
cr, uid, ids, ['debit', 'credit'], context=context):
|
||||
total += (line['debit'] or 0.0) - (line['credit'] or 0.0)
|
||||
return total
|
||||
|
||||
# All the code below aims at fixing one small issue in _to_pay_search()
|
||||
# But _to_pay_search() is the search function of the field 'amount_to_pay'
|
||||
# which is a field.function and these functions are not inheritable in
|
||||
# OpenERP.
|
||||
# So we have to inherit the field 'amount_to_pay' and duplicate the related
|
||||
# functions
|
||||
# If the patch that I proposed in this bug report
|
||||
# https://bugs.launchpad.net/openobject-addons/+bug/1275478
|
||||
# is integrated in addons/account_payment, then we will be able to remove
|
||||
# this file. -- Alexis de Lattre
|
||||
|
||||
def _amount_to_pay(self, cr, uid, ids, name, arg=None, context=None):
|
||||
""" Return the amount still to pay regarding all the payemnt orders
|
||||
""" Return the amount still to pay regarding all the payment orders
|
||||
(excepting cancelled orders)"""
|
||||
if not ids:
|
||||
return {}
|
||||
|
||||
@@ -16,6 +16,10 @@
|
||||
<field name="mode" position="after">
|
||||
<field name="mode_type" invisible="1"/>
|
||||
</field>
|
||||
<xpath expr="//button[@string='Invoices']" position="attributes">
|
||||
<attribute name="attrs">{
|
||||
'invisible': [('state', '!=', 'draft')]}</attribute>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<data>
|
||||
|
||||
<!--
|
||||
Add the payment mode type and transfer settings
|
||||
Add the payment mode type settings
|
||||
-->
|
||||
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.form.inherit</field>
|
||||
|
||||
1
account_banking_payment_transfer/__init__.py
Normal file
1
account_banking_payment_transfer/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import model
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
# (C) 2014 ACSONE SA/NV (<http://acsone.eu>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
@@ -24,31 +25,27 @@
|
||||
##############################################################################
|
||||
|
||||
{
|
||||
'name': 'Account Banking - Payments',
|
||||
'version': '0.1.164',
|
||||
'name': 'Account Banking - Payments Transfer Account',
|
||||
'version': '0.2',
|
||||
'license': 'AGPL-3',
|
||||
'author': 'Banking addons community',
|
||||
'website': 'https://launchpad.net/banking-addons',
|
||||
'website': 'https://github.com/OCA/banking',
|
||||
'category': 'Banking addons',
|
||||
'depends': [
|
||||
'account_banking',
|
||||
'account_banking_payment_export',
|
||||
],
|
||||
],
|
||||
'data': [
|
||||
'view/account_payment.xml',
|
||||
'view/banking_transaction_wizard.xml',
|
||||
'view/payment_mode.xml',
|
||||
'workflow/account_payment.xml',
|
||||
],
|
||||
'description': '''
|
||||
This addon adds payment reconciliation infrastructure to the Banking Addons.
|
||||
'description': '''Payment order reconciliation infrastructure
|
||||
|
||||
* Extends payments for digital banking:
|
||||
+ Adapted workflow in payments to reflect banking operations
|
||||
+ Relies on account_payment mechanics to extend with export generators.
|
||||
- ClieOp3 (NL) payment and direct debit orders files available as
|
||||
account_banking_nl_clieop
|
||||
This module reconciles invoices as soon as the payment order
|
||||
is sent, by creating a move to a transfer account (aka suspense account).
|
||||
When the moves on the suspense account are reconciled (typically through
|
||||
the bank statement reconciliation, the payment order moves to the done
|
||||
status).
|
||||
''',
|
||||
'auto_install': True,
|
||||
'installable': False,
|
||||
'auto_install': False,
|
||||
'installable': True,
|
||||
}
|
||||
4
account_banking_payment_transfer/model/__init__.py
Normal file
4
account_banking_payment_transfer/model/__init__.py
Normal file
@@ -0,0 +1,4 @@
|
||||
from . import account_payment
|
||||
from . import payment_line
|
||||
from . import payment_mode
|
||||
from . import account_move_reconcile
|
||||
@@ -0,0 +1,45 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2014 ACSONE SA (<http://acsone.eu>).
|
||||
# Copyright (C) 2014 Akretion (www.akretion.com)
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, workflow, api
|
||||
|
||||
|
||||
class AccountMoveReconcile(models.Model):
|
||||
_inherit = 'account.move.reconcile'
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
"""
|
||||
Workflow triggers upon unreconcile. This should go into the core.
|
||||
"""
|
||||
line_ids = []
|
||||
for reconcile in self:
|
||||
for move_line in reconcile.line_id:
|
||||
line_ids.append(move_line.id)
|
||||
res = super(AccountMoveReconcile, self).unlink()
|
||||
for line_id in line_ids:
|
||||
workflow.trg_trigger(
|
||||
self._uid, 'account.move.line', line_id, self._cr)
|
||||
return res
|
||||
255
account_banking_payment_transfer/model/account_payment.py
Normal file
255
account_banking_payment_transfer/model/account_payment.py
Normal file
@@ -0,0 +1,255 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
# (C) 2014 ACSONE SA (<http://acsone.eu>).
|
||||
# (C) 2014 Akretion (www.akretion.com)
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields, api, _
|
||||
|
||||
|
||||
class PaymentOrder(models.Model):
|
||||
'''
|
||||
Enable extra states for payment exports
|
||||
'''
|
||||
_inherit = 'payment.order'
|
||||
|
||||
date_scheduled = fields.Date(states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)],
|
||||
})
|
||||
reference = fields.Char(states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)],
|
||||
})
|
||||
mode = fields.Many2one(states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)],
|
||||
})
|
||||
state = fields.Selection([
|
||||
('draft', 'Draft'),
|
||||
('open', 'Confirmed'),
|
||||
('cancel', 'Cancelled'),
|
||||
('sent', 'Sent'),
|
||||
('rejected', 'Rejected'),
|
||||
('done', 'Done'),
|
||||
], string='State')
|
||||
line_ids = fields.One2many(states={
|
||||
'open': [('readonly', True)],
|
||||
'cancel': [('readonly', True)],
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
})
|
||||
user_id = fields.Many2one(states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
})
|
||||
date_prefered = fields.Selection(states={
|
||||
'sent': [('readonly', True)],
|
||||
'rejected': [('readonly', True)],
|
||||
'done': [('readonly', True)]
|
||||
})
|
||||
date_sent = fields.Date(string='Send date', readonly=True)
|
||||
|
||||
def action_rejected(self, cr, uid, ids, context=None):
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def action_done(self):
|
||||
for line in self.line_ids:
|
||||
line.date_done = fields.Date.context_today(self)
|
||||
self.date_done = fields.Date.context_today(self)
|
||||
# state is written in workflow definition
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def _get_transfer_move_lines(self):
|
||||
"""
|
||||
Get the transfer move lines (on the transfer account).
|
||||
"""
|
||||
res = []
|
||||
for order in self:
|
||||
for order_line in order.line_ids:
|
||||
move_line = order_line.transfer_move_line_id
|
||||
if move_line:
|
||||
res.append(move_line)
|
||||
return res
|
||||
|
||||
@api.multi
|
||||
def get_transfer_move_line_ids(self, *args):
|
||||
'''Used in the workflow for trigger_expr_id'''
|
||||
return [move_line.id for move_line in self._get_transfer_move_lines()]
|
||||
|
||||
@api.multi
|
||||
def test_done(self):
|
||||
"""
|
||||
Test if all moves on the transfer account are reconciled.
|
||||
|
||||
Called from the workflow to move to the done state when
|
||||
all transfer move have been reconciled through bank statements.
|
||||
"""
|
||||
return all([move_line.reconcile_id for move_line in
|
||||
self._get_transfer_move_lines()])
|
||||
|
||||
@api.multi
|
||||
def test_undo_done(self):
|
||||
return not self.test_done()
|
||||
|
||||
@api.model
|
||||
def _prepare_transfer_move(self):
|
||||
# TODO question : can I use self.mode.xxx in an @api.model ??
|
||||
# It works, but I'm not sure we are supposed to do that !
|
||||
# I didn't want to use api.one to avoid having to
|
||||
# do self._prepare_transfer_move()[0] in action_sent
|
||||
# I prefer to just have to do self._prepare_transfer_move()
|
||||
vals = {
|
||||
'journal_id': self.mode.transfer_journal_id.id,
|
||||
'ref': '%s %s' % (
|
||||
self.payment_order_type[:3].upper(), self.reference)
|
||||
}
|
||||
return vals
|
||||
|
||||
@api.model
|
||||
def _prepare_move_line_transfer_account(
|
||||
self, amount, move, payment_lines, labels):
|
||||
if len(payment_lines) == 1:
|
||||
partner_id = payment_lines[0].partner_id.id
|
||||
name = _('%s line %s') % (
|
||||
labels[self.payment_order_type], payment_lines[0].name)
|
||||
else:
|
||||
partner_id = False
|
||||
name = '%s %s' % (
|
||||
labels[self.payment_order_type], self.reference)
|
||||
vals = {
|
||||
'name': name,
|
||||
'move_id': move.id,
|
||||
'partner_id': partner_id,
|
||||
'account_id': self.mode.transfer_account_id.id,
|
||||
'credit': (self.payment_order_type == 'payment'
|
||||
and amount or 0.0),
|
||||
'debit': (self.payment_order_type == 'debit'
|
||||
and amount or 0.0),
|
||||
}
|
||||
return vals
|
||||
|
||||
@api.model
|
||||
def _prepare_move_line_partner_account(self, line, move, labels):
|
||||
if line.move_line_id:
|
||||
account_id = line.move_line_id.account_id.id
|
||||
else:
|
||||
if self.payment_order_type == 'debit':
|
||||
account_id = line.partner_id.property_account_receivable.id
|
||||
else:
|
||||
account_id = line.partner_id.property_account_payable.id
|
||||
vals = {
|
||||
'name': _('%s line %s') % (
|
||||
labels[self.payment_order_type], line.name),
|
||||
'move_id': move.id,
|
||||
'partner_id': line.partner_id.id,
|
||||
'account_id': account_id,
|
||||
'credit': (self.payment_order_type == 'debit'
|
||||
and line.amount or 0.0),
|
||||
'debit': (self.payment_order_type == 'payment'
|
||||
and line.amount or 0.0),
|
||||
}
|
||||
return vals
|
||||
|
||||
@api.model
|
||||
def action_sent_no_move_line_hook(self, pay_line):
|
||||
"""This function is designed to be inherited"""
|
||||
return
|
||||
|
||||
@api.one
|
||||
def action_sent(self):
|
||||
"""
|
||||
Create the moves that pay off the move lines from
|
||||
the debit order. This happens when the debit order file is
|
||||
generated.
|
||||
"""
|
||||
am_obj = self.env['account.move']
|
||||
aml_obj = self.env['account.move.line']
|
||||
pl_obj = self.env['payment.line']
|
||||
labels = {
|
||||
'payment': _('Payment order'),
|
||||
'debit': _('Direct debit order'),
|
||||
}
|
||||
if self.mode.transfer_journal_id and self.mode.transfer_account_id:
|
||||
# prepare a dict "trfmoves" that can be used when
|
||||
# self.mode.transfer_move_option = date or line
|
||||
# key = unique identifier (date or True or line.id)
|
||||
# value = [pay_line1, pay_line2, ...]
|
||||
trfmoves = {}
|
||||
if self.mode.transfer_move_option == 'line':
|
||||
for line in self.line_ids:
|
||||
trfmoves[line.id] = [line]
|
||||
else:
|
||||
if self.date_prefered in ('now', 'fixed'):
|
||||
trfmoves[True] = []
|
||||
for line in self.line_ids:
|
||||
trfmoves[True].append(line)
|
||||
else: # date_prefered == due
|
||||
for line in self.line_ids:
|
||||
if line.date in trfmoves:
|
||||
trfmoves[line.date].append(line)
|
||||
else:
|
||||
trfmoves[line.date] = [line]
|
||||
|
||||
for identifier, lines in trfmoves.iteritems():
|
||||
mvals = self._prepare_transfer_move()
|
||||
move = am_obj.create(mvals)
|
||||
total_amount = 0
|
||||
for line in lines:
|
||||
# TODO: take multicurrency into account
|
||||
|
||||
# create the payment/debit counterpart move line
|
||||
# on the partner account
|
||||
partner_ml_vals = self._prepare_move_line_partner_account(
|
||||
line, move, labels)
|
||||
partner_move_line = aml_obj.create(partner_ml_vals)
|
||||
total_amount += line.amount
|
||||
|
||||
# register the payment/debit move line
|
||||
# on the payment line and call reconciliation on it
|
||||
line.write({'transit_move_line_id': partner_move_line.id})
|
||||
|
||||
if line.move_line_id:
|
||||
pl_obj.debit_reconcile(line.id)
|
||||
else:
|
||||
self.action_sent_no_move_line_hook(line)
|
||||
|
||||
# create the payment/debit move line on the transfer account
|
||||
trf_ml_vals = self._prepare_move_line_transfer_account(
|
||||
total_amount, move, lines, labels)
|
||||
aml_obj.create(trf_ml_vals)
|
||||
|
||||
# post account move
|
||||
move.post()
|
||||
|
||||
# State field is written by act_sent_wait
|
||||
self.write({'date_sent': fields.Date.context_today(self)})
|
||||
return True
|
||||
@@ -24,11 +24,11 @@
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
from openerp import netsvc
|
||||
from openerp import workflow
|
||||
from openerp.tools.translate import _
|
||||
|
||||
|
||||
class payment_line(orm.Model):
|
||||
class PaymentLine(orm.Model):
|
||||
'''
|
||||
Add some fields; make destination bank account
|
||||
mandatory, as it makes no sense to send payments into thin air.
|
||||
@@ -36,16 +36,39 @@ class payment_line(orm.Model):
|
||||
accounts.
|
||||
'''
|
||||
_inherit = 'payment.line'
|
||||
|
||||
def _get_transfer_move_line(self, cr, uid, ids, name, arg, context=None):
|
||||
res = {}
|
||||
for order_line in self.browse(cr, uid, ids, context=context):
|
||||
if order_line.transit_move_line_id:
|
||||
order_type = order_line.order_id.payment_order_type
|
||||
trf_lines = order_line.transit_move_line_id.move_id.line_id
|
||||
for move_line in trf_lines:
|
||||
if order_type == 'debit' and move_line.debit > 0:
|
||||
res[order_line.id] = move_line.id
|
||||
elif order_type == 'payment' and move_line.credit > 0:
|
||||
res[order_line.id] = move_line.id
|
||||
else:
|
||||
res[order_line.id] = False
|
||||
return res
|
||||
|
||||
_columns = {
|
||||
'msg': fields.char('Message', size=255, required=False, readonly=True),
|
||||
'date_done': fields.date(
|
||||
'Date Confirmed', select=True, readonly=True),
|
||||
'transit_move_line_id': fields.many2one(
|
||||
# this line is part of the credit side of move 2a
|
||||
# from the documentation
|
||||
'account.move.line', 'Debit move line',
|
||||
'account.move.line', 'Transfer move line',
|
||||
readonly=True,
|
||||
help="Move line through which the debit order pays the invoice",
|
||||
help="Move line through which the payment/debit order "
|
||||
"pays the invoice",
|
||||
),
|
||||
'transfer_move_line_id': fields.function(
|
||||
_get_transfer_move_line,
|
||||
type='many2one',
|
||||
relation='account.move.line',
|
||||
string='Transfer move line counterpart',
|
||||
readonly=True,
|
||||
help="Counterpart move line on the transfer account",
|
||||
),
|
||||
}
|
||||
|
||||
@@ -98,7 +121,7 @@ class payment_line(orm.Model):
|
||||
Reconcile a debit order's payment line with the the move line
|
||||
that it is based on. Called from payment_order.action_sent().
|
||||
As the amount is derived directly from the counterpart move line,
|
||||
we do not expect a write off. Take partially reconcilions into
|
||||
we do not expect a write off. Take partial reconciliations into
|
||||
account though.
|
||||
|
||||
:param payment_line_id: the single id of the canceled payment line
|
||||
@@ -160,12 +183,12 @@ class payment_line(orm.Model):
|
||||
reconcile_obj.create(
|
||||
cr, uid, vals, context=context)
|
||||
for line_id in line_ids:
|
||||
netsvc.LocalService("workflow").trg_trigger(
|
||||
workflow.trg_trigger(
|
||||
uid, 'account.move.line', line_id, cr)
|
||||
|
||||
# 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:
|
||||
netsvc.LocalService("workflow").trg_validate(
|
||||
workflow.trg_validate(
|
||||
uid, 'account.invoice', torec_move_line.invoice.id,
|
||||
'undo_debit_denied', cr)
|
||||
46
account_banking_payment_transfer/model/payment_mode.py
Normal file
46
account_banking_payment_transfer/model/payment_mode.py
Normal file
@@ -0,0 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
##############################################################################
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
# (C) 2014 Akretion (www.akretion.com)
|
||||
#
|
||||
# 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/>.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp import models, fields
|
||||
|
||||
|
||||
class PaymentMode(models.Model):
|
||||
_inherit = "payment.mode"
|
||||
|
||||
transfer_account_id = fields.Many2one(
|
||||
'account.account', string='Transfer account',
|
||||
domain=[('type', '=', 'other'), ('reconcile', '=', True)],
|
||||
help='Pay off lines in sent orders with a move on this '
|
||||
'account. You can only select accounts of type regular '
|
||||
'that are marked for reconciliation')
|
||||
transfer_journal_id = fields.Many2one(
|
||||
'account.journal', string='Transfer journal',
|
||||
help='Journal to write payment entries when confirming '
|
||||
'a debit order of this mode')
|
||||
transfer_move_option = fields.Selection([
|
||||
('date', 'One move per payment date'),
|
||||
('line', 'One move per payment line'),
|
||||
], string='Transfer move option', default='date')
|
||||
34
account_banking_payment_transfer/view/payment_mode.xml
Normal file
34
account_banking_payment_transfer/view/payment_mode.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!--
|
||||
Add the payment mode transfer account settings
|
||||
-->
|
||||
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.form.inherit</field>
|
||||
<field name="model">payment.mode</field>
|
||||
<field name="inherit_id" ref="account_banking_payment_export.view_payment_mode_form_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="/form/group[@col='4']" position="inside">
|
||||
<group name="trf-move-config" string="Transfer move settings" colspan="2">
|
||||
<field name="transfer_account_id"
|
||||
domain="[('type', '=', 'other'),
|
||||
('reconcile', '=', True),
|
||||
('company_id', '=', company_id)]"
|
||||
context="{
|
||||
'default_type': 'other',
|
||||
'default_reconcile': True,
|
||||
'default_company_id': company_id}"
|
||||
/>
|
||||
<field name="transfer_journal_id"
|
||||
domain="[('company_id', '=', company_id)]"
|
||||
/>
|
||||
<field name="transfer_move_option"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -2,21 +2,21 @@
|
||||
<openerp>
|
||||
<data>
|
||||
<!-- New activity for workflow payment order: sent -->
|
||||
<record id="account_banking.act_sent" model="workflow.activity">
|
||||
<record id="act_sent" model="workflow.activity">
|
||||
<field name="name">sent</field>
|
||||
<field name="wkf_id" ref="account_payment.wkf_payment_order"/>
|
||||
<field name="action">action_sent()</field>
|
||||
<field name="kind">function</field>
|
||||
</record>
|
||||
<!-- New activity for workflow payment order: sent -->
|
||||
<record id="account_banking.act_sent_wait" model="workflow.activity">
|
||||
<record id="act_sent_wait" model="workflow.activity">
|
||||
<field name="name">sent_wait</field>
|
||||
<field name="wkf_id" ref="account_payment.wkf_payment_order"/>
|
||||
<field name="action">write({'state': 'sent'})</field>
|
||||
<field name="kind">function</field>
|
||||
</record>
|
||||
<!-- New activity for workflow payment order: rejected -->
|
||||
<record id="account_banking.act_rejected" model="workflow.activity">
|
||||
<record id="act_rejected" model="workflow.activity">
|
||||
<field name="name">rejected</field>
|
||||
<field name="wkf_id" ref="account_payment.wkf_payment_order"/>
|
||||
<field name="action">action_rejected()
|
||||
@@ -26,30 +26,38 @@ write({'state':'rejected'})</field>
|
||||
<!-- Rewrite existing open -> done transition to include 'sent' stage -->
|
||||
<record id="account_payment.trans_open_done" model="workflow.transition">
|
||||
<field name="act_from" ref="account_payment.act_open"/>
|
||||
<field name="act_to" ref="account_banking.act_sent"/>
|
||||
<field name="act_to" ref="act_sent"/>
|
||||
<field name="signal">sent</field>
|
||||
</record>
|
||||
<!-- the done signal continues to work but goes to sent -->
|
||||
<record id="account_banking.trans_open_done" model="workflow.transition">
|
||||
<record id="trans_open_done" model="workflow.transition">
|
||||
<field name="act_from" ref="account_payment.act_open"/>
|
||||
<field name="act_to" ref="account_banking.act_sent"/>
|
||||
<field name="act_to" ref="act_sent"/>
|
||||
<field name="signal">done</field>
|
||||
</record>
|
||||
<!-- From sent straight to sent_wait -->
|
||||
<record id="account_banking.trans_sent_sent_wait" model="workflow.transition">
|
||||
<field name="act_from" ref="account_banking.act_sent"/>
|
||||
<field name="act_to" ref="account_banking.act_sent_wait"/>
|
||||
<record id="trans_sent_sent_wait" model="workflow.transition">
|
||||
<field name="act_from" ref="act_sent"/>
|
||||
<field name="act_to" ref="act_sent_wait"/>
|
||||
</record>
|
||||
<!-- Reconciliation from the banking statement leads to done state -->
|
||||
<record id="account_banking.trans_sent_done" model="workflow.transition">
|
||||
<field name="act_from" ref="account_banking.act_sent_wait"/>
|
||||
<record id="trans_sent_done" model="workflow.transition">
|
||||
<field name="act_from" ref="act_sent_wait"/>
|
||||
<field name="act_to" ref="account_payment.act_done"/>
|
||||
<field name="condition">test_done()</field>
|
||||
<field name="signal">done</field>
|
||||
</record>
|
||||
<record id="trans_sent_done_auto" model="workflow.transition">
|
||||
<field name="act_from" ref="act_sent_wait"/>
|
||||
<field name="act_to" ref="account_payment.act_done"/>
|
||||
<field name="condition">test_done()</field>
|
||||
<field name="trigger_model">account.move.line</field>
|
||||
<field name="trigger_expr_id">get_transfer_move_line_ids()</field>
|
||||
</record>
|
||||
<!-- Rejected by the bank -->
|
||||
<record id="account_banking.trans_sent_rejected" model="workflow.transition">
|
||||
<field name="act_from" ref="account_banking.act_sent"/>
|
||||
<field name="act_to" ref="account_banking.act_rejected"/>
|
||||
<record id="trans_sent_rejected" model="workflow.transition">
|
||||
<field name="act_from" ref="act_sent"/>
|
||||
<field name="act_to" ref="act_rejected"/>
|
||||
<field name="signal">rejected</field>
|
||||
</record>
|
||||
<!--
|
||||
@@ -60,16 +68,25 @@ write({'state':'rejected'})</field>
|
||||
unfortunately.
|
||||
-->
|
||||
<record id="account_payment.act_done" model="workflow.activity">
|
||||
<field name="action">action_done()
|
||||
write({'state':'done'})</field>
|
||||
<field name="flow_stop" eval="False"/>
|
||||
</record>
|
||||
|
||||
<!-- Cancel the reconciled payment order -->
|
||||
<record id="trans_done_sent" model="workflow.transition">
|
||||
<field name="act_from" ref="account_payment.act_done"/>
|
||||
<field name="act_to" ref="account_banking.act_sent_wait"/>
|
||||
<field name="act_to" ref="act_sent_wait"/>
|
||||
<field name="condition">test_undo_done()</field>
|
||||
<field name="signal">undo_done</field>
|
||||
</record>
|
||||
<record id="trans_done_sent_auto" model="workflow.transition">
|
||||
<field name="act_from" ref="account_payment.act_done"/>
|
||||
<field name="act_to" ref="act_sent_wait"/>
|
||||
<field name="condition">test_undo_done()</field>
|
||||
<field name="trigger_model">account.move.line</field>
|
||||
<field name="trigger_expr_id">get_transfer_move_line_ids()</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
@@ -23,5 +23,13 @@
|
||||
<field name="state">valid</field>
|
||||
</record>
|
||||
|
||||
<record id="res_partner_2_mandate" model="account.banking.mandate">
|
||||
<field name="partner_bank_id" ref="account_banking_payment_export.res_partner_2_iban"/>
|
||||
<field name="type">recurrent</field>
|
||||
<field name="recurrent_sequence_type">first</field>
|
||||
<field name="signature_date" eval="time.strftime('%Y-%m-01')" />
|
||||
<field name="state">valid</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
'category': 'Banking addons',
|
||||
'depends': [
|
||||
'account_accountant',
|
||||
'account_banking_payment',
|
||||
'account_banking_payment_transfer',
|
||||
'account_banking_sepa_credit_transfer',
|
||||
],
|
||||
'description': '''
|
||||
@@ -37,5 +37,5 @@ dependencies installed, so that you can run the tests. If you only
|
||||
run the tests manually, you don't even have to install this module,
|
||||
only its dependencies.
|
||||
''',
|
||||
'installable': False,
|
||||
'installable': True,
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import test_payment_roundtrip
|
||||
from . import test_payment_roundtrip
|
||||
|
||||
fast_suite = [
|
||||
test_payment_roundtrip,
|
||||
@@ -18,11 +18,11 @@
|
||||
#
|
||||
##############################################################################
|
||||
from datetime import datetime
|
||||
from openerp.tests.common import SingleTransactionCase
|
||||
from openerp import netsvc
|
||||
from openerp.tests.common import TransactionCase
|
||||
from openerp import workflow
|
||||
|
||||
|
||||
class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
class TestPaymentRoundtrip(TransactionCase):
|
||||
|
||||
def assert_payment_order_state(self, expected):
|
||||
"""
|
||||
@@ -121,7 +121,7 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
can be validated properly.
|
||||
"""
|
||||
partner_model = reg('res.partner')
|
||||
supplier1 = partner_model.create(
|
||||
self.supplier1 = partner_model.create(
|
||||
cr, uid, {
|
||||
'name': 'Supplier 1',
|
||||
'supplier': True,
|
||||
@@ -135,7 +135,7 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
})
|
||||
],
|
||||
}, context=context)
|
||||
supplier2 = partner_model.create(
|
||||
self.supplier2 = partner_model.create(
|
||||
cr, uid, {
|
||||
'name': 'Supplier 2',
|
||||
'supplier': True,
|
||||
@@ -160,7 +160,7 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
invoice_model = reg('account.invoice')
|
||||
values = {
|
||||
'type': 'in_invoice',
|
||||
'partner_id': supplier1,
|
||||
'partner_id': self.supplier1,
|
||||
'account_id': self.payable_id,
|
||||
'invoice_line': [
|
||||
(0, False, {
|
||||
@@ -179,7 +179,7 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
'type': 'in_invoice',
|
||||
})]
|
||||
values.update({
|
||||
'partner_id': supplier2,
|
||||
'partner_id': self.supplier2,
|
||||
'name': 'Purchase 2',
|
||||
'reference_type': 'structured',
|
||||
'supplier_invoice_number': 'INV2',
|
||||
@@ -189,13 +189,13 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
invoice_model.create(
|
||||
cr, uid, values, context={
|
||||
'type': 'in_invoice'}))
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
for invoice_id in self.invoice_ids:
|
||||
wf_service.trg_validate(
|
||||
workflow.trg_validate(
|
||||
uid, 'account.invoice', invoice_id, 'invoice_open', cr)
|
||||
self.assert_invoices_state('open')
|
||||
|
||||
def setup_payment_config(self, reg, cr, uid):
|
||||
def setup_payment_config(self, reg, cr, uid,
|
||||
transfer_move_option='line'):
|
||||
"""
|
||||
Configure an additional account and journal for payments
|
||||
in transit and configure a payment mode with them.
|
||||
@@ -235,6 +235,7 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
'company_id': self.company_id,
|
||||
'transfer_account_id': transfer_account_id,
|
||||
'transfer_journal_id': transfer_journal_id,
|
||||
'transfer_move_option': transfer_move_option,
|
||||
'type': payment_mode_type_id,
|
||||
})
|
||||
|
||||
@@ -242,11 +243,15 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
"""
|
||||
Create a payment order with the invoices' payable move lines.
|
||||
Check that the payment order can be confirmed.
|
||||
|
||||
date_preferred is set to 'now', to ensure one transfer move
|
||||
when transfer_move_option = 'date'.
|
||||
"""
|
||||
self.payment_order_id = reg('payment.order').create(
|
||||
cr, uid, {
|
||||
'reference': 'PAY001',
|
||||
'mode': self.payment_mode_id,
|
||||
'date_prefered': 'now',
|
||||
})
|
||||
context = {'active_id': self.payment_order_id}
|
||||
entries = reg('account.move.line').search(
|
||||
@@ -281,8 +286,7 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
'No payment line created from invoice 2 or with the wrong '
|
||||
'communication')
|
||||
|
||||
wf_service = netsvc.LocalService('workflow')
|
||||
wf_service.trg_validate(
|
||||
workflow.trg_validate(
|
||||
uid, 'payment.order', self.payment_order_id, 'open', cr)
|
||||
self.assert_payment_order_state('open')
|
||||
|
||||
@@ -294,9 +298,7 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
"""
|
||||
export_model = reg('banking.export.sepa.wizard')
|
||||
export_id = export_model.create(
|
||||
cr, uid, {
|
||||
'msg_identification': 'EXP001'},
|
||||
context={'active_ids': [self.payment_order_id]})
|
||||
cr, uid, {}, context={'active_ids': [self.payment_order_id]})
|
||||
export_model.create_sepa(
|
||||
cr, uid, [export_id])
|
||||
export_model.save_sepa(
|
||||
@@ -306,13 +308,13 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
|
||||
def setup_bank_statement(self, reg, cr, uid):
|
||||
"""
|
||||
Create a bank statement with a single line. Call the reconciliation
|
||||
Create a bank statement with a one line for each
|
||||
payment order line. Call the reconciliation
|
||||
wizard to match the line with the open payment order. Confirm the
|
||||
bank statement. Check if the payment order is done.
|
||||
"""
|
||||
statement_model = reg('account.bank.statement')
|
||||
line_model = reg('account.bank.statement.line')
|
||||
wizard_model = reg('banking.transaction.wizard')
|
||||
statement_id = statement_model.create(
|
||||
cr, uid, {
|
||||
'name': 'Statement',
|
||||
@@ -320,19 +322,67 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
'balance_end_real': -200.0,
|
||||
'period_id': reg('account.period').find(cr, uid)[0]
|
||||
})
|
||||
line_id = line_model.create(
|
||||
line1_id = line_model.create(
|
||||
cr, uid, {
|
||||
'name': 'Statement line',
|
||||
'statement_id': statement_id,
|
||||
'amount': -100.0,
|
||||
'account_id': self.payable_id,
|
||||
'partner_id': self.supplier1,
|
||||
})
|
||||
line1 = line_model.browse(cr, uid, line1_id)
|
||||
rec_line1 = line_model.\
|
||||
get_reconciliation_proposition(cr, uid, line1)[0]
|
||||
line_model.process_reconciliation(cr, uid, line1_id, [
|
||||
{'counterpart_move_line_id': rec_line1['id'],
|
||||
'debit': rec_line1['credit'],
|
||||
'credit': rec_line1['debit']}])
|
||||
line2_id = line_model.create(
|
||||
cr, uid, {
|
||||
'name': 'Statement line',
|
||||
'statement_id': statement_id,
|
||||
'amount': -100.0,
|
||||
'account_id': self.payable_id,
|
||||
'partner_id': self.supplier2,
|
||||
})
|
||||
line2 = line_model.browse(cr, uid, line2_id)
|
||||
rec_line2 = line_model.\
|
||||
get_reconciliation_proposition(cr, uid, line2)[0]
|
||||
line_model.process_reconciliation(cr, uid, line2_id, [
|
||||
{'counterpart_move_line_id': rec_line2['id'],
|
||||
'debit': rec_line2['credit'],
|
||||
'credit': rec_line2['debit']}])
|
||||
self.assert_payment_order_state('done')
|
||||
|
||||
def setup_bank_statement_one_move(self, reg, cr, uid):
|
||||
"""
|
||||
Create a bank statement with a single line. Call the reconciliation
|
||||
wizard to match the line with the open payment order. Confirm the
|
||||
bank statement. Check if the payment order is done.
|
||||
"""
|
||||
statement_model = reg('account.bank.statement')
|
||||
line_model = reg('account.bank.statement.line')
|
||||
statement_id = statement_model.create(
|
||||
cr, uid, {
|
||||
'name': 'Statement',
|
||||
'journal_id': self.bank_journal_id,
|
||||
'balance_end_real': -200.0,
|
||||
'period_id': reg('account.period').find(cr, uid)[0]
|
||||
})
|
||||
line1_id = line_model.create(
|
||||
cr, uid, {
|
||||
'name': 'Statement line',
|
||||
'statement_id': statement_id,
|
||||
'amount': -200.0,
|
||||
'account_id': self.payable_id,
|
||||
})
|
||||
wizard_id = wizard_model.create(
|
||||
cr, uid, {'statement_line_id': line_id})
|
||||
wizard_model.write(
|
||||
cr, uid, [wizard_id], {
|
||||
'manual_payment_order_id': self.payment_order_id})
|
||||
statement_model.button_confirm_bank(cr, uid, [statement_id])
|
||||
line1 = line_model.browse(cr, uid, line1_id)
|
||||
rec_line1 = line_model.\
|
||||
get_reconciliation_proposition(cr, uid, line1)[0]
|
||||
line_model.process_reconciliation(cr, uid, line1_id, [
|
||||
{'counterpart_move_line_id': rec_line1['id'],
|
||||
'debit': rec_line1['credit'],
|
||||
'credit': rec_line1['debit']}])
|
||||
self.assert_payment_order_state('done')
|
||||
|
||||
def check_reconciliations(self, reg, cr, uid):
|
||||
@@ -340,6 +390,10 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
Check if the payment order has any lines and that
|
||||
the transit move lines of those payment lines are
|
||||
reconciled by now.
|
||||
|
||||
The transit move line is the line that pays the invoice
|
||||
so it is reconciled as soon as the payment order
|
||||
is sent.
|
||||
"""
|
||||
payment_order = reg('payment.order').browse(
|
||||
cr, uid, self.payment_order_id)
|
||||
@@ -348,9 +402,27 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
assert line.transit_move_line_id, \
|
||||
'Payment order has no transfer move line'
|
||||
assert line.transit_move_line_id.reconcile_id, \
|
||||
'Transit move line on payment line is not reconciled'
|
||||
|
||||
def check_reconciliations_after_bank_statement(self, reg, cr, uid):
|
||||
"""
|
||||
Check if the payment order has any lines and that
|
||||
the transfer move lines of those payment lines are
|
||||
reconciled by now.
|
||||
"""
|
||||
payment_order = reg('payment.order').browse(
|
||||
cr, uid, self.payment_order_id)
|
||||
assert payment_order.line_ids, 'Payment order has no payment lines'
|
||||
for line in payment_order.line_ids:
|
||||
assert line.transfer_move_line_id, \
|
||||
'Payment order has no transfer move line'
|
||||
assert line.transfer_move_line_id.reconcile_id, \
|
||||
'Transfer move line on payment line is not reconciled'
|
||||
|
||||
def test_payment_roundtrip(self):
|
||||
""" Payment round trip using transfer account,
|
||||
with one move per payment order line on the transfer account
|
||||
"""
|
||||
reg, cr, uid, = self.registry, self.cr, self.uid
|
||||
self.setup_company(reg, cr, uid)
|
||||
self.setup_chart(reg, cr, uid)
|
||||
@@ -358,5 +430,21 @@ class TestPaymentRoundtrip(SingleTransactionCase):
|
||||
self.setup_payment_config(reg, cr, uid)
|
||||
self.setup_payment(reg, cr, uid)
|
||||
self.export_payment(reg, cr, uid)
|
||||
self.setup_bank_statement(reg, cr, uid)
|
||||
self.check_reconciliations(reg, cr, uid)
|
||||
self.setup_bank_statement(reg, cr, uid)
|
||||
self.check_reconciliations_after_bank_statement(reg, cr, uid)
|
||||
|
||||
def test_payment_roundtrip_one_move(self):
|
||||
""" Payment round trip using transfer account,
|
||||
with one move per payment order on the transfer account
|
||||
"""
|
||||
reg, cr, uid, = self.registry, self.cr, self.uid
|
||||
self.setup_company(reg, cr, uid)
|
||||
self.setup_chart(reg, cr, uid)
|
||||
self.setup_payables(reg, cr, uid)
|
||||
self.setup_payment_config(reg, cr, uid, transfer_move_option='date')
|
||||
self.setup_payment(reg, cr, uid)
|
||||
self.export_payment(reg, cr, uid)
|
||||
self.check_reconciliations(reg, cr, uid)
|
||||
self.setup_bank_statement_one_move(reg, cr, uid)
|
||||
self.check_reconciliations_after_bank_statement(reg, cr, uid)
|
||||
@@ -6,20 +6,18 @@
|
||||
<field name="model">account.invoice</field>
|
||||
<field name="inherit_id" ref="account.invoice_form"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<!--
|
||||
Add new state 'debit_denied' to applicable buttons.
|
||||
Maybe apply trick in fields_view_get instead, for
|
||||
better compatibility with other modules?
|
||||
-->
|
||||
<!-- button name="invoice_open" position="attributes">
|
||||
<attribute name="states">draft,proforma2,debit_denied</attribute>
|
||||
</button -->
|
||||
<!--
|
||||
Add new state 'debit_denied' to applicable buttons.
|
||||
Maybe apply trick in fields_view_get instead, for
|
||||
better compatibility with other modules?
|
||||
-->
|
||||
<!-- button name="invoice_open" position="attributes">
|
||||
<attribute name="states">draft,proforma2,debit_denied</attribute>
|
||||
</button -->
|
||||
<button name="invoice_open" position="after">
|
||||
<button name="invoice_debit_denied" states="paid"
|
||||
string="Debit Denied" icon="gtk-cancel"/>
|
||||
string="Debit Denied"/>
|
||||
</button>
|
||||
</data>
|
||||
</field>
|
||||
</record>
|
||||
<record id="view_account_invoice_filter" model="ir.ui.view">
|
||||
@@ -29,7 +27,7 @@
|
||||
<field name="inherit_id" ref="account.view_account_invoice_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
<filter name="invoices" position="after">
|
||||
<filter name="debit_denied" icon="terp-dolar_ok!"
|
||||
<filter name="debit_denied"
|
||||
string="Debit denied"
|
||||
domain="[('state','=','debit_denied')]"
|
||||
help="Show only invoices with state Debit denied"
|
||||
|
||||
1
account_payment_mode_term/__init__.py
Normal file
1
account_payment_mode_term/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from . import models
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
# (C) 2014 ACSONE SA/NV (<http://acsone.eu>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
@@ -23,21 +24,24 @@
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
from openerp.osv import orm, fields
|
||||
{
|
||||
'name': 'Account Banking - Payments Term Filter',
|
||||
'version': '0.1.1',
|
||||
'license': 'AGPL-3',
|
||||
'author': 'Banking addons community',
|
||||
'website': 'https://github.com/OCA/banking',
|
||||
'category': 'Banking addons',
|
||||
'depends': [
|
||||
'account_banking_payment_export',
|
||||
],
|
||||
'data': [
|
||||
'views/payment_mode.xml',
|
||||
],
|
||||
'description': '''Payment term filter on payment mode.
|
||||
|
||||
|
||||
class banking_import_line(orm.TransientModel):
|
||||
_inherit = 'banking.import.line'
|
||||
_columns = {
|
||||
'payment_order_id': fields.many2one(
|
||||
'payment.order', 'Payment order'),
|
||||
'transaction_type': fields.selection([
|
||||
# Add payment order related transaction types
|
||||
('invoice', 'Invoice payment'),
|
||||
('payment_order_line', 'Payment from a payment order'),
|
||||
('payment_order', 'Aggregate payment order'),
|
||||
('storno', 'Canceled debit order'),
|
||||
('bank_costs', 'Bank costs'),
|
||||
('unknown', 'Unknown'),
|
||||
], 'Transaction type'),
|
||||
}
|
||||
When set, only open invoices corresponding to the mode's
|
||||
payment term are proposed when populating payment orders.
|
||||
''',
|
||||
'auto_install': False,
|
||||
'installable': True,
|
||||
}
|
||||
2
account_payment_mode_term/models/__init__.py
Normal file
2
account_payment_mode_term/models/__init__.py
Normal file
@@ -0,0 +1,2 @@
|
||||
from . import payment_mode
|
||||
from . import payment_order_create
|
||||
@@ -3,6 +3,7 @@
|
||||
#
|
||||
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
|
||||
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
|
||||
# (C) 2014 ACSONE SA/NV (<http://acsone.eu>).
|
||||
#
|
||||
# All other contributions are (C) by their respective contributors
|
||||
#
|
||||
@@ -30,19 +31,6 @@ class payment_mode(orm.Model):
|
||||
_inherit = "payment.mode"
|
||||
|
||||
_columns = {
|
||||
'transfer_account_id': fields.many2one(
|
||||
'account.account', 'Transfer account',
|
||||
domain=[('type', '=', 'other'),
|
||||
('reconcile', '=', True)],
|
||||
help=('Pay off lines in sent orders with a move on this '
|
||||
'account. You can only select accounts of type regular '
|
||||
'that are marked for reconciliation'),
|
||||
),
|
||||
'transfer_journal_id': fields.many2one(
|
||||
'account.journal', 'Transfer journal',
|
||||
help=('Journal to write payment entries when confirming '
|
||||
'a debit order of this mode'),
|
||||
),
|
||||
'payment_term_ids': fields.many2many(
|
||||
'account.payment.term', 'account_payment_order_terms_rel',
|
||||
'mode_id', 'term_id', 'Payment terms',
|
||||
23
account_payment_mode_term/views/payment_mode.xml
Normal file
23
account_payment_mode_term/views/payment_mode.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
|
||||
<!--
|
||||
Add the payment mode term filter settings
|
||||
-->
|
||||
<record id="view_payment_mode_form_inherit" model="ir.ui.view">
|
||||
<field name="name">payment.mode.form.inherit</field>
|
||||
<field name="model">payment.mode</field>
|
||||
<field name="inherit_id" ref="account_banking_payment_export.view_payment_mode_form_inherit"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="/form/group[@col='4']" position="inside">
|
||||
<group string="Optional filter by payment term"
|
||||
name="filter-payment-term" colspan="2">
|
||||
<field name="payment_term_ids" nolabel="1" colspan="2"/>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user