mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[FIX] a number of bugs
[ADD] payment order reconciliation
This commit is contained in:
@@ -182,7 +182,7 @@
|
||||
<data>
|
||||
<field name="period_id" position="replace"/>
|
||||
<xpath expr="/form/notebook/page[@string='Transaction']/field/tree" position="attributes">
|
||||
<attribute name="colors">black:state == 'confirmed';red:duplicate == True;purple:match_multi == True;grey:state == 'draft';</attribute>
|
||||
<attribute name="colors">black:state == 'confirmed';darkmagenta:match_multi == True;grey:state=='draft';crimson:duplicate == True;</attribute>
|
||||
</xpath>
|
||||
<xpath expr="/form/notebook/page[@string='Transaction']/field/tree/field[@name='name']" position="replace">
|
||||
<field name="name" required="1"/>
|
||||
@@ -468,7 +468,7 @@
|
||||
<field name="model">account.bank.statement.line</field>
|
||||
<field name="type">tree</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Statement lines" colors="black:state == 'confirmed';red:duplicate == True;purple:match_multi == True;grey:state == 'draft';">
|
||||
<tree string="Statement lines" colors="black:state == 'confirmed';darkmagenta:match_multi == True;crimson:duplicate == True;grey:state=='draft';">
|
||||
<field name="sequence" readonly="1" invisible="1"/>
|
||||
<field name="date" groups="base.group_extended"/>
|
||||
<field name="name"/>
|
||||
|
||||
@@ -138,15 +138,16 @@ class banking_import_transaction(osv.osv):
|
||||
candidates = [x for x in orders if
|
||||
is_zero(x.total - trans.transferred_amount)]
|
||||
if len(candidates) > 0:
|
||||
# retrieve the common account_id, if any
|
||||
account_id = False
|
||||
for order in candidates:
|
||||
for line in order_line.debit_move_line_id.move_id.line_id:
|
||||
for line in order.line_ids[0].debit_move_line_id.move_id.line_id:
|
||||
if line.account_id.type == 'other':
|
||||
if account_id != line.account_id.id:
|
||||
if account_id and account_id != line.account_id.id:
|
||||
account_id = False
|
||||
break
|
||||
else:
|
||||
account_id = line.account_id
|
||||
account_id = line.account_id.id
|
||||
|
||||
# TODO at statement line confirm
|
||||
# this action generates a reconcile object
|
||||
@@ -536,7 +537,7 @@ class banking_import_transaction(osv.osv):
|
||||
payment_line_obj = self.pool.get('payment.line')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_line_id:
|
||||
osv.except_osv(
|
||||
raise osv.except_osv(
|
||||
_("Cannot link with storno"),
|
||||
_("No direct debit order item"))
|
||||
return payment_line_obj.debit_storno(
|
||||
@@ -547,6 +548,49 @@ class banking_import_transaction(osv.osv):
|
||||
transaction.storno_retry,
|
||||
context=context)
|
||||
|
||||
def _reconcile_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')
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
if not transaction.payment_order_id:
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Cannot reconcile: no direct debit order"))
|
||||
if transaction.payment_order_id.payment_order_type != 'debit':
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile"),
|
||||
_("Reconcile payment order not implemented"))
|
||||
return 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)
|
||||
|
||||
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 osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile: no direct debit order"))
|
||||
if transaction.payment_order_id.payment_order_type != 'debit':
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Unreconcile payment order not implemented"))
|
||||
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_move(
|
||||
self, cr, uid, transaction_id, context=None):
|
||||
statement_line_obj = self.pool.get('account.bank.statement.line')
|
||||
@@ -572,11 +616,11 @@ class banking_import_transaction(osv.osv):
|
||||
transaction = self.browse(cr, uid, transaction_id, context=context)
|
||||
|
||||
if not transaction.payment_line_id:
|
||||
osv.except_osv(
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("No direct debit order item"))
|
||||
if not transaction.payment_line_id.storno:
|
||||
osv.except_osv(
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("The direct debit order item is not marked for storno"))
|
||||
|
||||
@@ -591,7 +635,7 @@ class banking_import_transaction(osv.osv):
|
||||
cancel_line = line
|
||||
break
|
||||
if not cancel_line: # debug
|
||||
osv.except_osv(
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel link with storno"),
|
||||
_("Line id not found"))
|
||||
reconcile = cancel_line.reconcile_id or cancel_line.reconcile_partial_id
|
||||
@@ -618,13 +662,14 @@ class banking_import_transaction(osv.osv):
|
||||
'storno': _cancel_storno,
|
||||
'invoice': _cancel_move,
|
||||
'move': _cancel_move,
|
||||
'payment_order': _cancel_payment_order,
|
||||
}
|
||||
def cancel(self, cr, uid, ids, context=None):
|
||||
if ids and isinstance(ids, (int, float)):
|
||||
ids = [ids]
|
||||
for transaction in self.browse(cr, uid, ids, context):
|
||||
if transaction.match_type not in self.cancel_map:
|
||||
osv.except_osv(
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel type %s" % transaction.match_type),
|
||||
_("No method found to cancel this type"))
|
||||
self.cancel_map[transaction.match_type](self, cr, uid, transaction.id, context)
|
||||
@@ -633,6 +678,7 @@ class banking_import_transaction(osv.osv):
|
||||
reconcile_map = {
|
||||
'storno': _reconcile_storno,
|
||||
'invoice': _reconcile_move,
|
||||
'payment_order': _reconcile_payment_order,
|
||||
'move': _reconcile_move,
|
||||
}
|
||||
def reconcile(self, cr, uid, ids, context=None):
|
||||
@@ -640,7 +686,7 @@ class banking_import_transaction(osv.osv):
|
||||
ids = [ids]
|
||||
for transaction in self.browse(cr, uid, ids, context):
|
||||
if transaction.match_type not in self.reconcile_map:
|
||||
osv.except_osv(
|
||||
raise osv.except_osv(
|
||||
_("Cannot reconcile type %s" % transaction.match_type),
|
||||
_("No method found to reconcile this type"))
|
||||
# run the method that is appropriate for this match type
|
||||
@@ -731,7 +777,7 @@ class banking_import_transaction(osv.osv):
|
||||
for key in self.signal_duplicate_keys]
|
||||
ids = self.search(cr, uid, search_vals, context=context)
|
||||
if len(ids) < 1:
|
||||
osv.except_osv(_('Cannot check for duplicate'),
|
||||
raise osv.except_osv(_('Cannot check for duplicate'),
|
||||
_("I can't find myself..."))
|
||||
if len(ids) > 1:
|
||||
self.write(
|
||||
@@ -1050,8 +1096,12 @@ class banking_import_transaction(osv.osv):
|
||||
country_code = iban.countrycode
|
||||
elif transaction.remote_owner_country_code:
|
||||
country_code = transaction.remote_owner_country_code
|
||||
elif hasattr(parser, 'country_code') and parser.country_code:
|
||||
country_code = parser.country_code
|
||||
# TODO: pass the parser's local country code to the transaction
|
||||
# elif hasattr(parser, 'country_code') and parser.country_code:
|
||||
# country_code = parser.country_code
|
||||
# For now, substituted by the company's country
|
||||
elif company.partner_id and company.partner_id.country:
|
||||
country_code = company.partner_id.country.code
|
||||
else:
|
||||
country_code = None
|
||||
partner_id = get_or_create_partner(
|
||||
@@ -1104,7 +1154,8 @@ class banking_import_transaction(osv.osv):
|
||||
if remainder:
|
||||
injected.append(remainder)
|
||||
|
||||
if not move_info:
|
||||
account_id = move_info and move_info.get('account_id', False)
|
||||
if not account_id:
|
||||
# Use the default settings, but allow individual partner
|
||||
# settings to overrule this. Note that you need to change
|
||||
# the internal type of these accounts to either 'payable'
|
||||
@@ -1125,17 +1176,15 @@ class banking_import_transaction(osv.osv):
|
||||
if len(partner_banks) != 1 or not account_id or account_id == def_rec_account_id:
|
||||
account_id = (account_info.default_debit_account_id and
|
||||
account_info.default_debit_account_id.id)
|
||||
else:
|
||||
account_id = move_info['account_id']
|
||||
results['trans_matched_cnt'] += 1
|
||||
values = {}
|
||||
self_values = {}
|
||||
if move_info:
|
||||
results['trans_matched_cnt'] += 1
|
||||
self_values['match_type'] = move_info['match_type']
|
||||
self_values['payment_line_id'] = move_info.get('payment_line_id', False)
|
||||
self_values['move_line_ids'] = [(6, 0, move_info.get('move_line_ids', []))]
|
||||
self_values['invoice_ids'] = [(6, 0, move_info.get('invoice_ids', []))]
|
||||
self_values['payment_order_ids'] = [(6, 0, move_info.get('payment_order_ids', []))]
|
||||
self_values['move_line_ids'] = [(6, 0, move_info.get('move_line_ids') or [])]
|
||||
self_values['invoice_ids'] = [(6, 0, move_info.get('invoice_ids') or [])]
|
||||
self_values['payment_order_ids'] = [(6, 0, move_info.get('payment_order_ids') or [])]
|
||||
self_values['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]
|
||||
@@ -1254,7 +1303,7 @@ class banking_import_transaction(osv.osv):
|
||||
'transferred_amount': fields.float('transferred_amount'),
|
||||
'message': fields.char('message', size=1024),
|
||||
'remote_owner': fields.char('remote_owner', size=24),
|
||||
'remote_address': fields.char('remote_address', size=24),
|
||||
'remote_owner_address': fields.char('remote_owner_address', size=24),
|
||||
'remote_owner_city': fields.char('remote_owner_city', size=24),
|
||||
'remote_owner_postalcode': fields.char('remote_owner_postalcode', size=24),
|
||||
'remote_owner_country_code': fields.char('remote_owner_country_code', size=24),
|
||||
@@ -1592,7 +1641,7 @@ class account_bank_statement_line(osv.osv):
|
||||
if st_line.state != 'confirmed':
|
||||
continue
|
||||
if st_line.statement_id.state != 'draft':
|
||||
osv.except_osv(
|
||||
raise osv.except_osv(
|
||||
_("Cannot cancel bank transaction"),
|
||||
_("The bank statement that this transaction belongs to has " +
|
||||
"already been confirmed"))
|
||||
|
||||
@@ -519,7 +519,6 @@ class banking_import(osv.osv_memory):
|
||||
# TODO: code _link_canceled_debit
|
||||
return False
|
||||
|
||||
|
||||
def import_statements_file(self, cursor, uid, ids, context):
|
||||
'''
|
||||
Import bank statements / bank transactions file.
|
||||
|
||||
@@ -30,12 +30,12 @@
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
<page attrs="{'invisible': [('state', '!=', 'ready')]}" string="Statements">
|
||||
<field name="statement_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
<page attrs="{'invisible': [('state', '=', 'init'')]}" string="Log">
|
||||
<field name="log" colspan="4" nolabel="1" width="500"/>
|
||||
</page>
|
||||
<page attrs="{'invisible': [('state', '!=', 'ready')]}" string="Statements">
|
||||
<field name="statement_ids" colspan="4" nolabel="1"/>
|
||||
</page>
|
||||
</notebook>
|
||||
<group colspan="2" >
|
||||
<button icon="gtk-cancel"
|
||||
|
||||
@@ -136,11 +136,12 @@ def get_or_create_partner(pool, cursor, uid, name, address, postal_code, city,
|
||||
)
|
||||
country_id = country_ids and country_ids[0] or False
|
||||
filter.append(('country_id', '=', country_id))
|
||||
if address:
|
||||
# disable for now. Apparently, address is an array of lines.
|
||||
if address and False:
|
||||
if len(address) >= 1:
|
||||
filter.append(('street', 'ilike', addres[0]))
|
||||
filter.append(('street', 'ilike', address[0]))
|
||||
if len(address) > 1:
|
||||
filter.append(('street2', 'ilike', addres[1]))
|
||||
filter.append(('street2', 'ilike', address[1]))
|
||||
if city:
|
||||
filter.append(('city', 'ilike', city))
|
||||
if postal_code:
|
||||
|
||||
@@ -50,9 +50,6 @@ class transaction_message(object):
|
||||
'date', 'remote_owner', 'local_account', 'remote_account',
|
||||
'transfer_type', 'debcred', 'transferred_amount',
|
||||
'transfer_type_verbose', 'message'
|
||||
|
||||
# 'date', 'local_account', 'transferred_amount', 'debcred',
|
||||
# 'remote_owner', 'remote_account', 'transfer_type', 'reference',
|
||||
]
|
||||
|
||||
def __init__(self, values, subno):
|
||||
@@ -70,9 +67,9 @@ class transaction_message(object):
|
||||
if self.debcred == 'Af':
|
||||
self.transferred_amount = -self.transferred_amount
|
||||
self.execution_date = self.effective_date = str2date(self.date, '%Y%m%d')
|
||||
# Set statement_id based on week number
|
||||
self.statement_id = self.effective_date.strftime('%Yw%W')
|
||||
self.statement_id = '' #self.effective_date.strftime('%Yw%W')
|
||||
self.id = str(subno).zfill(4)
|
||||
self.reference = ''
|
||||
# Normalize basic account numbers
|
||||
self.remote_account = self.remote_account.replace('.', '').zfill(10)
|
||||
self.local_account = self.local_account.replace('.', '').zfill(10)
|
||||
@@ -84,7 +81,7 @@ class transaction(models.mem_bank_transaction):
|
||||
attrnames = ['local_account', 'remote_account',
|
||||
'remote_owner', 'transferred_amount',
|
||||
'execution_date', 'effective_date', 'transfer_type',
|
||||
'id', #'reference',
|
||||
'id', 'reference', 'statement_id', 'message',
|
||||
]
|
||||
|
||||
"""
|
||||
@@ -121,9 +118,6 @@ class transaction(models.mem_bank_transaction):
|
||||
super(transaction, self).__init__(*args, **kwargs)
|
||||
# Copy attributes from auxiliary class to self.
|
||||
for attr in self.attrnames:
|
||||
#if attr == 'reference':
|
||||
# setattr(self, 'reference', False)
|
||||
#else:
|
||||
setattr(self, attr, getattr(line, attr))
|
||||
# self.message = ''
|
||||
# Decompose structured messages
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
'view/account_payment.xml',
|
||||
'view/account_invoice.xml',
|
||||
'workflow/account_invoice.xml',
|
||||
'workflow/account_payment.xml',
|
||||
'data/account_payment_term.xml',
|
||||
],
|
||||
'demo_xml': [],
|
||||
|
||||
@@ -19,15 +19,6 @@ class payment_mode(osv.osv):
|
||||
help=('Journal to write payment entries when confirming ' +
|
||||
'a debit order of this mode'),
|
||||
),
|
||||
# 'reference_filter': fields.char(
|
||||
# 'Reference filter', size=16,
|
||||
# help=(
|
||||
# 'Optional substring filter on move line references. ' +
|
||||
# 'You can use this in combination with a specific journal ' +
|
||||
# 'for items that you want to handle with this mode. Use ' +
|
||||
# 'a separate sequence for the journal with a distinguished ' +
|
||||
# 'prefix or suffix and enter that character string here.'),
|
||||
# ),
|
||||
'payment_term_ids': fields.many2many(
|
||||
'account.payment.term', 'account_payment_order_terms_rel',
|
||||
'mode_id', 'term_id', 'Payment terms',
|
||||
@@ -67,17 +58,12 @@ class payment_order(osv.osv):
|
||||
return res
|
||||
|
||||
def debit_reconcile_transfer(self, cr, uid, payment_order_id,
|
||||
amount, log, context=None):
|
||||
amount, currency, context=None):
|
||||
"""
|
||||
During import of bank statements, create the reconcile on the transfer
|
||||
account containing all the move lines on the transfer account.
|
||||
account containing all the open move lines on the transfer account.
|
||||
"""
|
||||
move_line_obj = self.pool.get('account.move.line')
|
||||
|
||||
def is_zero(move_line, total):
|
||||
return self.pool.get('res.currency').is_zero(
|
||||
cr, uid, move_line.company_id.currency_id, total)
|
||||
|
||||
order = self.browse(cr, uid, payment_order_id, context)
|
||||
line_ids = []
|
||||
reconcile_id = False
|
||||
@@ -85,8 +71,9 @@ class payment_order(osv.osv):
|
||||
for line in order_line.debit_move_line_id.move_id.line_id:
|
||||
if line.account_id.type == 'other' and not line.reconcile_id:
|
||||
line_ids.append(line.id)
|
||||
if is_zero(order.line_ids[0].debit_move_line_id,
|
||||
move_line_obj.get_balance(cr, uid, line_ids) - amount):
|
||||
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)]},
|
||||
@@ -95,9 +82,44 @@ class payment_order(osv.osv):
|
||||
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, but
|
||||
rip out the reconcile first or the workflow may not allow the undo.
|
||||
Raise on failure for rollback.
|
||||
"""
|
||||
self.pool.get('account.move.reconcile').unlink(
|
||||
cr, uid, reconcile_id, context=context)
|
||||
wkf_ok = netsvc.LocalService('workflow').trg_validate(
|
||||
uid, 'payment.order', payment_order_id, 'undo_done', cr)
|
||||
if not wkf_ok:
|
||||
raise osv.except_osv(
|
||||
_("Cannot unreconcile"),
|
||||
_("Cannot unreconcile debit 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
|
||||
"""
|
||||
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
|
||||
else:
|
||||
# TODO: define conditions for 'payment' orders
|
||||
return False
|
||||
return True
|
||||
|
||||
def action_sent(self, cr, uid, ids, context=None):
|
||||
"""
|
||||
Create the moves that pay off the move lines from
|
||||
@@ -407,7 +429,8 @@ class payment_order_create(osv.osv_memory):
|
||||
search_due_date = data['duedate']
|
||||
|
||||
### start account_direct_debit ###
|
||||
payment = self.pool.get('payment.order').browse(cr, uid, context['active_id'], context=context)
|
||||
payment = self.pool.get('payment.order').browse(
|
||||
cr, uid, context['active_id'], context=context)
|
||||
# Search for move line to pay:
|
||||
if payment.payment_order_type == 'debit':
|
||||
domain = [
|
||||
@@ -415,22 +438,19 @@ class payment_order_create(osv.osv_memory):
|
||||
('account_id.type', '=', 'receivable'),
|
||||
('amount_to_receive', '>', 0),
|
||||
]
|
||||
# cannot filter on properties of (searchable)
|
||||
# function fields. Needs work in expression.expression.parse()
|
||||
# Currently gives an SQL error.
|
||||
# apply payment term filter
|
||||
if payment.mode.payment_term_ids:
|
||||
term_ids = [term.id for term in payment.mode.payment_term_ids]
|
||||
domain = domain + [
|
||||
'|', ('invoice', '=', False),
|
||||
('payment_term_id', 'in', term_ids),
|
||||
]
|
||||
else:
|
||||
domain = [
|
||||
('reconcile_id', '=', False),
|
||||
('account_id.type', '=', 'payable'),
|
||||
('amount_to_pay', '>', 0)
|
||||
]
|
||||
# apply payment term filter
|
||||
if payment.mode.payment_term_ids:
|
||||
domain = domain + [
|
||||
('payment_term_id', 'in',
|
||||
[term.id for term in payment.mode.payment_term_ids]
|
||||
)
|
||||
]
|
||||
# domain = [('reconcile_id', '=', False), ('account_id.type', '=', 'payable'), ('amount_to_pay', '>', 0)]
|
||||
### end account_direct_debit ###
|
||||
|
||||
@@ -439,7 +459,7 @@ class payment_order_create(osv.osv_memory):
|
||||
context.update({'line_ids': line_ids})
|
||||
model_data_ids = mod_obj.search(cr, uid,[('model', '=', 'ir.ui.view'), ('name', '=', 'view_create_payment_order_lines')], context=context)
|
||||
resource_id = mod_obj.read(cr, uid, model_data_ids, fields=['res_id'], context=context)[0]['res_id']
|
||||
return {'name': ('Entrie Lines'),
|
||||
return {'name': ('Entry Lines'),
|
||||
'context': context,
|
||||
'view_type': 'form',
|
||||
'view_mode': 'form',
|
||||
|
||||
25
account_direct_debit/workflow/account_payment.xml
Normal file
25
account_direct_debit/workflow/account_payment.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<openerp>
|
||||
<data>
|
||||
<!--
|
||||
Transition to undo the payment order and reset to
|
||||
sent, triggered by
|
||||
cancelling a bank transaction with which the order
|
||||
was reconciled.
|
||||
For this, we need to cancel the flow stop on the done state,
|
||||
unfortunately.
|
||||
TODO: what is below is not enough. We need to inject
|
||||
another state, 'sent_wait' between sent and done.
|
||||
-->
|
||||
<record id="account_payment.act_done" model="workflow.activity">
|
||||
<field name="flow_stop">False</field>
|
||||
</record>
|
||||
|
||||
<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"/>
|
||||
<field name="condition">test_undo_done()</field>
|
||||
<field name="signal">undo_done</field>
|
||||
</record>
|
||||
</data>
|
||||
</openerp>
|
||||
Reference in New Issue
Block a user