[REF] Payment wizards are now new style (osv_memory) wizards

[FIX] Allow for ClieOp export with empty reference
This commit is contained in:
Stefan Rijnhart
2011-07-21 13:30:59 +02:00
parent 78f4f755cc
commit c9a655cc20
20 changed files with 494 additions and 646 deletions

View File

@@ -35,8 +35,6 @@
'update_xml': [
'security/ir.model.access.csv',
'data/account_banking_data.xml',
'account_banking_import_wizard.xml',
'account_banking_payment_wizard.xml',
'wizard/bank_import_view.xml',
'account_banking_view.xml',
'account_banking_workflow.xml',

View File

@@ -203,58 +203,16 @@ class payment_mode_type(osv.osv):
'bank_type_payment_type_rel',
'pay_type_id','bank_type_id',
'Suitable bank types', required=True),
'ir_model_id': fields.many2one(
'ir.model', 'Payment wizard',
help=('Select the Payment Wizard for payments of this type. '
'Leave empty for manual processing'),
domain=[('osv_memory', '=', True)],
),
}
payment_mode_type()
class payment_order_create(osv.osv_memory):
_inherit = "payment.order.create"
def create_payment(self, cr, uid, ids, context=None):
""" This method is equal to the one in
account_payment/wizard/account_payment_order.py
except for a fix in passing the payment mode to line2bank()
"""
order_obj = self.pool.get('payment.order')
line_obj = self.pool.get('account.move.line')
payment_obj = self.pool.get('payment.line')
if context is None:
context = {}
data = self.read(cr, uid, ids, [], context=context)[0]
line_ids = data['entries']
if not line_ids:
return {'type': 'ir.actions.act_window_close'}
payment = order_obj.browse(cr, uid, context['active_id'], context=context)
### account banking
# t = None
# line2bank = line_obj.line2bank(cr, uid, line_ids, t, context)
line2bank = line_obj.line2bank(cr, uid, line_ids, payment.mode.id, context)
### end account banking
## Finally populate the current payment with new lines:
for line in line_obj.browse(cr, uid, line_ids, context=context):
if payment.date_prefered == "now":
#no payment date => immediate payment
date_to_pay = False
elif payment.date_prefered == 'due':
date_to_pay = line.date_maturity
elif payment.date_prefered == 'fixed':
date_to_pay = payment.date_scheduled
payment_obj.create(cr, uid,{
'move_line_id': line.id,
'amount_currency': line.amount_to_pay,
'bank_id': line2bank.get(line.id),
'order_id': payment.id,
'partner_id': line.partner_id and line.partner_id.id or False,
'communication': line.ref or '/',
'date': date_to_pay,
'currency': line.invoice and line.invoice.currency_id.id or False,
}, context=context)
return {'type': 'ir.actions.act_window_close'}
payment_order_create()
class payment_mode(osv.osv):
''' Restoring the payment type from version 5,
used to select the export wizard (if any) '''
@@ -794,41 +752,7 @@ class payment_order(osv.osv):
'''
_inherit = 'payment.order'
def _get_id_proxy(self, cr, uid, ids, name, args, context=None):
return dict([(id, id) for id in ids])
_columns = {
#
# 'id_proxy' is simply a reference to the resource's id
# for the following reason:
#
# The GTK client 6.0 lacks necessary support for old style wizards.
# It does not pass the resource's id if the wizard is called from
# a button on a form.
#
# As a workaround, we pass the payment order id in the context
# Evaluating 'id' in the context in the webclient on a form
# in readonly mode dies with an error "Invalid Syntax", because 'id'
# evaluates to "<built-in function id>" and the resource's field
# values are not passed to eval in readonly mode at all.
# See /addons/openerp/static/javascript/form.js line 308
#
# Evaluating any other variable in the webclient on a form in
# readonly mode fails silently. That is actually ok, because the
# webclient still supports passing the resource id when an old style
# wizard is called.
#
# Therefore, if we want to pass the id in the context safely we have to
# pass it under a different name.
#
# TODO: migrate old style wizards to osv_memory
#
'id_proxy': fields.function(
_get_id_proxy, method=True, string='Copy ID', type='integer',
# Would have used store={}, but the system does not seem to
# generate triggers on the field 'id'.
store=False,
),
'date_scheduled': fields.date(
'Scheduled date if fixed',
states={
@@ -904,25 +828,39 @@ class payment_order(osv.osv):
If type is manual. just confirm the order.
Previously (pre-v6) in account_payment/wizard/wizard_pay.py
"""
if context == None:
context={}
result = {}
obj_model = self.pool.get('ir.model.data')
order = self.browse(cr, uid, ids[0], context)
t = order.mode and order.mode.type.code or 'manual'
res_id = False
if t != 'manual':
gw = self.get_wizard(t)
if gw:
module, wizard = gw
wiz_id = obj_model._get_id(cr, uid, module, wizard)
if wiz_id:
res_id = obj_model.read(
cr, uid, [wiz_id], ['res_id'])[0]['res_id']
if res_id:
result = self.pool.get('ir.actions.wizard').read(
cr, uid, [res_id])[0]
orders = self.browse(cr, uid, ids, context)
order = orders[0]
# check if a wizard is defined for the first order
if order.mode.type and order.mode.type.ir_model_id:
context['active_ids'] = ids
wizard_model = order.mode.type.ir_model_id.model
wizard_obj = self.pool.get(wizard_model)
wizard_id = wizard_obj.create(cr, uid, {}, context)
result = {
'name': wizard_obj._description or 'Payment Order Export',
'view_type': 'form',
'view_mode': 'form',
'res_model': wizard_model,
'domain': [],
'context': context,
'type': 'ir.actions.act_window',
'target': 'new',
'res_id': wizard_id,
'nodestroy': True,
}
else:
# should all be manual orders without type or wizard model
for order in orders[1:]:
if order.mode.type and order.mode.type.ir_model_id:
raise osv.except_osv(
_('Error'),
_('You can only combine payment orders of the same type')
)
# process manual payments
self.action_sent(cr, uid, ids, context)
result['nodestroy'] = True
return result
def _write_payment_lines(self, cursor, uid, ids, **kwargs):

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) EduSense BV <http://www.edusense.nl>
All rights reserved.
The licence is in the file __terp__.py
-->
<openerp>
<data>
<wizard id="wizard_account_banking_import_file"
string="Import Bank Statements File"
model="account.bank.statement"
name="account_banking.banking_import"
/>
</data>
</openerp>

View File

@@ -1,22 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) EduSense BV <http://www.edusense.nl>
All rights reserved.
The licence is in the file __terp__.py
-->
<openerp>
<data>
<wizard id="wizard_account_banking_payment_manual"
menu="False"
string="Manual Bank Payment"
model="account_payment.payment.order"
name="account_banking.payment_manual"
/>
<wizard id="account_payment.wizard_populate_payment"
menu="False"
string="Populate payment"
model="payment.order"
name="account_payment.populate_payment"
/>
</data>
</openerp>

View File

@@ -143,6 +143,15 @@
action="wizard_account_banking_import_file"
sequence="15"/>
<!-- Add the import wizard to the statement's right menu -->
<act_window name="Import Bank Statements File"
res_model="account.banking.bank.import"
src_model="account.bank.statement"
view_mode="form"
target="new"
key2="client_action_multi"
id="act_account_banking_import_wizard"/>
<!-- Create right menu entry to see statements -->
<act_window name="Bank Statements File"
domain="[('id','=',banking_id)]"
@@ -283,19 +292,30 @@
<xpath expr="/form/group/button[@string='Select Invoices to Pay']"
position="replace">
<button colspan="2" name="%(account_payment.action_create_payment_order)s"
string="Select Invoices to Pay" type="action" states="draft,open"
string="Select Invoices to Pay" type="action"
attrs="{'invisible':[('state','!=','draft')]}"
icon="gtk-find"
/>
</xpath>
<xpath expr="/form/group/button[@string='Make Payments']"
position="replace">
<field name="id_proxy" invisible="1"/>
<button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute" context="{'payment_order_id': id_proxy}"/>
<button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
<newline/>
</xpath>
</data>
</field>
</record>
<record id="view_banking_payment_order_tree_1" model="ir.ui.view">
<field name="name">account.payment.order.tree.banking-1</field>
<field name="inherit_id" ref="account_payment.view_payment_order_tree" />
<field name="model">payment.order</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<button string="Make Payments" position="replace">
<button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
</button>
</field>
</record>
<!-- Set trigger on IBAN and acc_number fields in res_partner_bank form -->
<record id="view_partner_bank_account_banking_form_1" model="ir.ui.view">
@@ -403,6 +423,7 @@
<field name="name" />
<field name="code" />
<field name="suitable_bank_types"/>
<field name="ir_model_id"/>
</form>
</field>
</record>

View File

@@ -17,11 +17,18 @@
Ratio: one can have bank accounts in foreign banks. Foreign
addresses not automatically involve international banking.
-->
<record id="bank_normal_field_contry" model="res.partner.bank.type.field">
<record id="base.bank_normal_field_contry" model="res.partner.bank.type.field">
<field name="name">country_id</field>
<field name="bank_type_id" ref="base.bank_normal"/>
<field eval="False" name="required"/>
<field eval="False" name="readonly"/>
</record>
<!-- Add manual bank transfer as default payment option -->
<record model="payment.mode.type" id="account_banking.manual_bank_tranfer">
<field name="name">Manual Bank Transfer</field>
<field name="code">BANKMAN</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base.bank_normal'),ref('base_iban.bank_iban'),])]" />
</record>
</data>
</openerp>

View File

@@ -1,3 +1,5 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_account_banking_settings","account.banking.account.settings","model_account_banking_account_settings","account.group_account_manager",1,1,1,1
"access_account_banking_settings_user","account.banking.account.settings user","model_account_banking_account_settings","account.group_account_user",1,0,0,0
"access_account_banking_import","account.bankimport","model_account_banking_imported_file","account.group_account_user",1,1,1,1
"access_payment_mode_type","payment.mode.type","model_payment_mode_type","account_payment.group_account_payment",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_account_banking_settings account.banking.account.settings model_account_banking_account_settings account.group_account_manager 1 1 1 1
3 access_account_banking_settings_user account.banking.account.settings user model_account_banking_account_settings account.group_account_user 1 0 0 0
4 access_account_banking_import account.bankimport model_account_banking_imported_file account.group_account_user 1 1 1 1
5 access_payment_mode_type payment.mode.type model_payment_mode_type account_payment.group_account_payment 1 1 1 1

View File

@@ -20,6 +20,6 @@
##############################################################################
import bank_import
import bank_payment_manual
import wizard_payment_order
import account_payment_order
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -0,0 +1,113 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import datetime
from osv import osv
from account_banking.struct import struct
from account_banking.parsers import convert
today = datetime.date.today
def str2date(str):
dt = convert.str2date(str, '%Y-%m-%d')
return datetime.date(dt.year, dt.month, dt.day)
class payment_order_create(osv.osv_memory):
_inherit = 'payment.order.create'
def create_payment(self, cr, uid, ids, context=None):
'''
This method is a slightly modified version of the existing method on this
model in account_payment.
- pass the payment mode to line2bank()
- allow invoices to create influence on the payment process: not only 'Free'
references are allowed, but others as well
- check date_to_pay is not in the past.
'''
order_obj = self.pool.get('payment.order')
line_obj = self.pool.get('account.move.line')
payment_obj = self.pool.get('payment.line')
if context is None:
context = {}
data = self.read(cr, uid, ids, [], context=context)[0]
line_ids = data['entries']
if not line_ids:
return {'type': 'ir.actions.act_window_close'}
payment = order_obj.browse(cr, uid, context['active_id'], context=context)
### account banking
# t = None
# line2bank = line_obj.line2bank(cr, uid, line_ids, t, context)
line2bank = line_obj.line2bank(cr, uid, line_ids, payment.mode.id, context)
_today = today()
### end account banking
## Finally populate the current payment with new lines:
for line in line_obj.browse(cr, uid, line_ids, context=context):
if payment.date_prefered == "now":
#no payment date => immediate payment
date_to_pay = False
elif payment.date_prefered == 'due':
### account_banking
# date_to_pay = line.date_maturity
date_to_pay = line.date_maturity and \
str2date(line.date_maturity) > _today\
and line.date_maturity or False
### end account banking
elif payment.date_prefered == 'fixed':
### account_banking
# date_to_pay = payment.date_planned
date_to_pay = payment.date_planned and \
str2date(payment.date_planned) > _today\
and payment.date_planned or False
### end account banking
### account_banking
state = communication2 = False
communication = line.ref or '/'
if line.invoice:
if line.invoice.reference_type == 'structured':
state = 'structured'
communication = line.invoice.reference
else:
state = 'normal'
communication2 = line.invoice.reference
### end account_banking
payment_obj.create(cr, uid,{
'move_line_id': line.id,
'amount_currency': line.amount_to_pay,
'bank_id': line2bank.get(line.id),
'order_id': payment.id,
'partner_id': line.partner_id and line.partner_id.id or False,
### account banking
# 'communication': line.ref or '/'
'communication': communication,
'communication2': communication2,
'state': state,
### end account banking
'date': date_to_pay,
'currency': line.invoice and line.invoice.currency_id.id or False,
}, context=context)
return {'type': 'ir.actions.act_window_close'}
payment_order_create()

View File

@@ -974,6 +974,7 @@ class banking_import(osv.osv_memory):
'State', readonly=True),
'import_id': fields.many2one(
'account.banking.imported.file', 'Import File'),
# osv_memory does not seem to support one2many
'statement_ids': fields.many2many(
'account.bank.statement', 'rel_wiz_statements', 'wizard_id',
'statement_id', 'Imported Bank Statements'),

View File

@@ -1,212 +0,0 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Copyright (C) EduSense BV (<http://www.edusense.nl>).
# All Rights Reserved.
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import datetime
import wizard
import pooler
from tools.misc import UpdateableStr
from tools.translate import _
from account_banking.struct import struct
from account_banking.parsers import convert
__doc__ = '''
This module is a slightly modified version of the identical named payment
order wizard in account_payment. The rationale for this bulk copy is the
inability to inherit wizards in OpenERP versions prior to version 6.
The modifications in this wizard allows invoices to create influence on the
payment process: not only 'Free' references are allowed, but others as well.
In order to allow further projects based on account_banking to inherit from
this wizard, the complete wizard is made object oriented, as is should have
been from the start.
'''
today = datetime.date.today
def str2date(str):
dt = convert.str2date(str, '%Y-%m-%d')
return datetime.date(dt.year, dt.month, dt.day)
class wizard_payment_order(wizard.interface):
'''
Create a payment object with lines corresponding to the account move line
to pay according to the date and the mode provided by the user.
Hypothesis:
- Small number of non-reconcilied move line , payment mode and bank account type,
- Big number of partner and bank account.
If a type is given, unsuitable account move lines are ignored.
'''
FORM = UpdateableStr()
FIELDS = {
'entries': {
'string':'Entries',
'type':'many2many',
'relation': 'account.move.line',
},
}
field_duedate = {
'duedate': {
'string': 'Due Date',
'type': 'date',
'required': True,
'default': lambda *a: convert.date2str(today()),
},
}
arch_duedate='''<?xml version="1.0"?>
<form string="Search Payment lines">
<field name="duedate" />
</form>'''
def search_entries(self, cursor, uid, data, context):
'''
Search for invoices that can be paid
'''
search_due_date = data['form']['duedate']
pool = pooler.get_pool(cursor.dbname)
order_obj = pool.get('payment.order')
move_line_obj = pool.get('account.move.line')
payment = order_obj.browse(cursor, uid, data['id'], context=context)
if payment.mode:
ctx = '''context="{'journal_id': %d}"''' % payment.mode.journal.id
else:
ctx = ''
# Search account.move.line to pay:
domain = [
('reconcile_id', '=', False),
('account_id.type', '=', 'payable'),
('amount_to_pay', '>', 0),
'|', ('date_maturity','<=',search_due_date),
('date_maturity','=',False)
]
line_ids = move_line_obj.search(cursor, uid, domain, context=context)
self.FORM.string = '''<?xml version="1.0"?>
<form string="Populate Payment:">
<field name="entries" colspan="4" height="300" width="800" nolabel="1"
domain="[('id', 'in', [%s])]" %s
/>
</form>''' % (','.join([str(x) for x in line_ids]), ctx)
return {}
def get_communication(self, line):
'''
Method to fill the communication and communication2 lines of a payment
line. Returns (state, comm1, comm2).
'''
if line.invoice.reference_type == 'structured':
return ('structured', line.invoice.reference, '')
return ('normal', '', line.invoice.reference)
def create_payment(self, cursor, uid, data, context):
'''
Create payment lines from the data of previously created payable
invoices
'''
ids = data['form']['entries'][0][2]
if not ids:
return {}
pool = pooler.get_pool(cursor.dbname)
order_obj = pool.get('payment.order')
move_line_obj = pool.get('account.move.line')
payment_line_obj = pool.get('payment.line')
payment = order_obj.browse(cursor, uid, data['id'], context=context)
ptype = payment.mode and payment.mode.type.id or None
line2bank = move_line_obj.line2bank(cursor, uid, ids, ptype, context)
_today = today()
retval = struct()
# Populate the current payment with new lines
for line in move_line_obj.browse(cursor, uid, ids, context=context):
if payment.date_prefered == 'now':
# no payment date means immediate payment
date_to_pay = False
elif payment.date_prefered == 'due':
date_to_pay = line.date_maturity and \
str2date(line.date_maturity) > _today\
and line.date_maturity or False
elif payment.date_prefered == 'fixed':
date_to_pay = payment.date_planned and \
str2date(payment.date_planned) > _today\
and payment.date_planned or False
values = struct(
move_line_id = line.id,
amount_currency = line.amount_to_pay,
bank_id = line2bank.get(line.id),
order_id = payment.id,
partner_id = line.partner_id and line.partner_id.id or False,
date = date_to_pay,
currency = False,
)
if line.invoice:
values.state, values.communication, values.communication2 = \
self.get_communication(line)
values.currency = line.invoice.currency_id.id,
payment_line_obj.create(cursor, uid, values, context=context)
return {}
states = {
'init': {
'actions': [],
'result': {
'type': 'form',
'arch': arch_duedate,
'fields': field_duedate,
'state': [
('end','_Cancel'),
('search','_Search', '', True)
]
},
},
'search': {
'actions': [search_entries],
'result': {
'type': 'form',
'arch': FORM,
'fields': FIELDS,
'state': [
('end','_Cancel'),
('create','_Add to payment order', '', True)
]
},
},
'create': {
'actions': [],
'result': {
'type': 'action',
'action': create_payment,
'state': 'end'
}
},
}
wizard_payment_order('account_payment.populate_payment')
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -33,10 +33,10 @@
'depends': ['account_banking'],
'init_xml': [],
'update_xml': [
#'security/ir.model.access.csv',
'account_banking_nl_clieop.xml',
'account_banking_export_wizard.xml',
'wizard/export_clieop_view.xml',
'data/banking_export_clieop.xml',
'security/ir.model.access.csv',
],
'demo_xml': [],
'description': '''

View File

@@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) EduSense BV <http://www.edusense.nl>
All rights reserved.
The licence is in the file __terp__.py
-->
<openerp>
<data>
<wizard id="wizard_account_banking_export_clieop"
string="Export ClieOp File"
model="payment.order"
name="account_banking_nl_clieop.banking_export_clieop"
/>
</data>
</openerp>

View File

@@ -22,25 +22,19 @@ from osv import osv, fields
from datetime import date
from tools.translate import _
class payment_order(osv.osv):
'''
Attach export_clieop wizard to payment order and allow traceability
'''
_inherit = 'payment.order'
def get_wizard(self, type):
if type in ['CLIEOPPAY', 'CLIEOPINC', 'CLIEOPSAL']:
return self._module, 'wizard_account_banking_export_clieop'
return super(payment_order, self).get_wizard(type)
payment_order()
class clieop_export(osv.osv):
'''ClieOp3 Export'''
_name = 'banking.export.clieop'
_description = __doc__
_rec_name = 'identification'
_columns = {
'payment_order_ids':
fields.text('Payment Orders'),
'payment_order_ids': fields.many2many(
'payment.order',
'account_payment_order_clieop_rel',
'banking_export_clieop_id', 'account_order_id',
'Payment Orders',
readonly=True),
'testcode':
fields.selection([('T', _('Yes')), ('P', _('No'))],
'Test Run', readonly=True),

View File

@@ -14,19 +14,33 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Client Opdrachten Export">
<separator string="General Information" colspan="4" />
<field name="filetype" />
<field name="identification" />
<separator string="ClieOp Information" colspan="4" />
<field name="total_amount" />
<field name="check_no_accounts" />
<field name="no_transactions" />
<separator string="Processing Information" colspan="4" />
<field name="prefered_date" />
<field name="date_generated" />
<field name="testcode" />
<newline />
<field name="file" colspan="4" />
<notebook>
<page string="General Information">
<field name="filetype" />
<field name="identification" />
<separator string="ClieOp Information" colspan="4" />
<field name="total_amount" />
<field name="check_no_accounts" />
<field name="no_transactions" />
<separator string="Processing Information" colspan="4" />
<field name="prefered_date" />
<field name="date_generated" />
<field name="testcode" />
<newline />
<field name="file" colspan="4" />
</page>
<page string="Payment Orders">
<field name="payment_order_ids" colspan="4" nolabel="1">
<tree colors="blue:state in ('draft');gray:state in ('cancel','done');black:state in ('open')" string="Payment order">
<field name="reference"/>
<field name="date_created"/>
<field name="date_done"/>
<field name="total"/>
<field name="state"/>
</tree>
</field>
</page>
</notebook>
</form>
</field>
</record>
@@ -59,5 +73,14 @@
sequence="12"
/>
<!-- Create right menu entry to see generated files -->
<act_window name="Generated ClieOp3 Files"
domain="[('payment_order_ids', '=', active_id)]"
res_model="banking.export.clieop"
src_model="payment.order"
view_type="form"
view_mode="tree,form"
id="act_banking_export_clieop_payment_order"/>
</data>
</openerp>

View File

@@ -6,6 +6,8 @@
<field name="code">CLIEOPINC</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban'),ref('base.bank_normal'),])]" />
<field name="ir_model_id"
ref="account_banking_nl_clieop.model_banking_export_clieop_wizard"/>
</record>
<record model="payment.mode.type" id="account_banking_nl_clieop.export_clieop_pay">
@@ -13,6 +15,8 @@
<field name="code">CLIEOPPAY</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban'),ref('base.bank_normal'),])]" />
<field name="ir_model_id"
ref="account_banking_nl_clieop.model_banking_export_clieop_wizard"/>
</record>
<record model="payment.mode.type" id="account_banking_nl_clieop.export_clieop_sal">
@@ -20,6 +24,8 @@
<field name="code">CLIEOPSAL</field>
<field name="suitable_bank_types"
eval="[(6,0,[ref('base_iban.bank_iban'),ref('base.bank_normal'),])]" />
<field name="ir_model_id"
ref="account_banking_nl_clieop.model_banking_export_clieop_wizard"/>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,2 @@
"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink"
"access_banking_export_clieop","banking.export.clieop","model_banking_export_clieop","account_payment.group_account_payment",1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_banking_export_clieop banking.export.clieop model_banking_export_clieop account_payment.group_account_payment 1 1 1 1

View File

@@ -76,7 +76,7 @@ class HeaderRecord(record.Record): #{{{
def __init__(self, id='1', seqno=1, duplicate=False):
super(HeaderRecord, self).__init__()
self.sender_id = id
self.sender_id = id or ''
self.file_id = '%02d%02d' % (self.creation_date.day, seqno)
self.duplicatecode = duplicate and '2' or '1'
#}}}
@@ -362,7 +362,7 @@ class Batch(object):
self.header.variantcode = batch_id and 'C' or 'B'
self.header.transactiongroup = transactiongroup
self.header.batch_tracer = batch_tracer
self.header.batch_id = batch_id
self.header.batch_id = batch_id or ''
self.header.accountno_sender = rekeningnr
self.sender.name_sender = sender
self.sender.preferred_execution_date = execution_date

View File

@@ -19,12 +19,11 @@
#
##############################################################################
import wizard
import pooler
import base64
from datetime import datetime, date, timedelta
from account_banking import sepa
from osv import osv, fields
from tools.translate import _
from account_banking import sepa
import clieop
def strpdate(arg, format='%Y-%m-%d'):
@@ -35,151 +34,131 @@ def strfdate(arg, format='%Y-%m-%d'):
'''shortcut'''
return arg.strftime(format)
class wizard_banking_export_clieop(wizard.interface):
form = '''<?xml version="1.0"?>
<form string="Client Opdrachten Export">
<separator colspan="4" string="Processing Details" />
<field name="batchtype" />
<field name="execution_date" />
<field name="test" />
<separator colspan="4" string="Reference for further communication" />
<field name="reference" colspan="2" />
<separator colspan="4" string="Additional message for all transactions" />
<field name="fixed_message" />
</form>'''
fields = {
'reference' : {
'string': 'Reference',
'type': 'char',
'size': 5,
'required': False,
'help': ('The bank will use this reference in feedback communication '
'to refer to this run. Only five characters are available.'
)
},
'batchtype': {
'string': 'Type',
'type': 'selection',
'selection': [
class banking_export_clieop_wizard(osv.osv_memory):
_name = 'banking.export.clieop.wizard'
_description = 'Client Opdrachten Export'
_columns = {
'state': fields.selection(
[
('create', 'Create'),
('finish', 'Finish')
],
'State',
readonly=True,
),
'reference': fields.char(
'Reference', size=5,
help=('The bank will use this reference in feedback communication '
'to refer to this run. Only five characters are available.'
),
),
'batchtype': fields.selection(
[
('CLIEOPPAY', 'Payments'),
('CLIEOPSAL', 'Salary Payments'),
('CLIEOPINC', 'Direct Debits'),
],
'readonly': True,
},
'execution_date': {
'string': 'Execution Date',
'type': 'date',
'required': False,
'help': ('This is the date the file should be processed by the bank. '
'Don\'t choose a date beyond the nearest date in your '
'payments. The latest allowed date is 30 days from now.\n'
'Please keep in mind that banks only execute on working days '
'and typically use a delay of two days between execution date '
'and effective transfer date.'
),
},
'test': {
'string': 'Test Run',
'type': 'boolean',
'required': True,
'default': True,
'help': ('Select this if you want your bank to run a test process '
'rather then execute your orders for real.'
)
},
'fixed_message': {
'string': 'Fixed Message',
'type': 'char',
'size': 32,
'required': False,
'default': '',
'help': ('A fixed message to apply to all transactions in addition to '
'the individual messages.'
),
},
}
file_form = '''<?xml version="1.0"?>
<form string="Client Opdrachten Export">
<field name="filetype" />
<field name="identification" />
<field name="total_amount" />
<field name="check_no_accounts" />
<field name="no_transactions" />
<field name="prefered_date" />
<field name="testcode" />
<newline/>
<field name="file" />
<field name="log" colspan="4" nolabel="1" />
</form>'''
file_fields = {
'testcode': {
'string': 'Test Run',
'type': 'selection',
'selection': [('T', _('Yes')), ('P', _('No'))],
'required': False,
'readonly': True,
},
'prefered_date': {
'string': 'Prefered Processing Date',
'type': 'date',
'required': False,
'readonly': True,
},
'no_transactions': {
'string': 'Number of Transactions',
'type': 'int',
'required': False,
'readonly': True,
},
'check_no_accounts': {
'string': 'Check Number Accounts',
'type': 'char',
'size': 5,
'required': False,
'readonly': True,
},
'total_amount': {
'string': 'Total Amount',
'type': 'float',
'required': False,
'readonly': True,
},
'identification': {
'string': 'Identification',
'type': 'char',
'size': 6,
'required': False,
'readonly': True,
},
'filetype': {
'string': 'File Type',
'type': 'selection',
'selection': [
], 'Type', readonly=True,
),
'execution_date': fields.date(
'Execution Date',
help=('This is the date the file should be processed by the bank. '
'Don\'t choose a date beyond the nearest date in your '
'payments. The latest allowed date is 30 days from now.\n'
'Please keep in mind that banks only execute on working days '
'and typically use a delay of two days between execution date '
'and effective transfer date.'
),
),
'test': fields.boolean(
'Test Run',
help=('Select this if you want your bank to run a test process '
'rather then execute your orders for real.'
),
),
'fixed_message': fields.char(
'Fixed Message', size=32,
help=('A fixed message to apply to all transactions in addition to '
'the individual messages.'
),
),
# file fields
'file_id': fields.many2one(
'banking.export.clieop',
'ClieOp File',
readonly=True
),
# fields.related does not seem to support
# fields of type selection
'testcode': fields.selection(
[('T', _('Yes')), ('P', _('No'))],
'Test Run', readonly=True,
),
'filetype': fields.selection(
[
('CREDBET', 'Payment Batch'),
('SALARIS', 'Salary Payment Batch'),
('INCASSO', 'Direct Debit Batch'),
],
'required': False,
'readonly': True,
},
'file': {
'string': 'ClieOp File',
'type': 'binary',
'required': False,
'readonly': True,
},
'log': {
'string': 'Log',
'type': 'text',
'readonly': True,
},
}
],
'File Type',
readonly=True,
),
'prefered_date': fields.related(
'file_id', 'prefered_date',
type='date',
string='Prefered Processing Date',
readonly=True,
),
'no_transactions': fields.related(
'file_id', 'no_transactions',
type ='integer',
string='Number of Transactions',
readonly=True,
),
'check_no_accounts': fields.related(
'file_id', 'check_no_accounts',
type='char', size=5,
string='Check Number Accounts',
readonly=True,
),
'total_amount': fields.related(
'file_id', 'total_amount',
type='float',
string='Total Amount',
readonly=True,
),
'identification': fields.related(
'file_id', 'identification',
type='char', size=6,
string='Identification',
readonly=True,
),
'file': fields.related(
'file_id', 'file', type='binary',
readonly=True,
string='File',
),
'payment_order_ids': fields.many2many(
'payment.order', 'rel_wiz_payorders', 'wizard_id',
'payment_order_id', 'Payment Orders',
readonly=True,
),
}
_defaults = {
'test': True,
}
def _check_orders(self, cursor, uid, data, context):
def create(self, cursor, uid, vals, context=None):
'''
Retrieve a sane set of default values based on the payment orders
from the context.
'''
if 'batchtype' not in vals:
self.check_orders(cursor, uid, vals, context)
return super(banking_export_clieop_wizard, self).create(
cursor, uid, vals, context)
def check_orders(self, cursor, uid, vals, context):
'''
Check payment type for all orders.
@@ -191,19 +170,14 @@ class wizard_banking_export_clieop(wizard.interface):
Also mind that rates for batches are way higher than those for
transactions. It pays to limit the number of batches.
'''
form = data['form']
today = date.today()
pool = pooler.get_pool(cursor.dbname)
payment_order_obj = pool.get('payment.order')
# Can get id from context. Seems necessary due to lack of support for
# old style wizards in the GTK client
if 'ids' not in data and context.get('payment_order_id', False):
data['ids'] = [context['payment_order_id']]
payment_order_obj = self.pool.get('payment.order')
# Payment order ids are provided in the context
payment_order_ids = context.get('active_ids', [])
runs = {}
# Only orders of same type can be combined
payment_orders = payment_order_obj.browse(cursor, uid, data['ids'])
payment_orders = payment_order_obj.browse(cursor, uid, payment_order_ids)
for payment_order in payment_orders:
payment_type = payment_order.mode.type.code
@@ -231,34 +205,33 @@ class wizard_banking_export_clieop(wizard.interface):
else:
execution_date = today
if execution_date and execution_date >= max_date:
raise wizard.except_wizard(
raise osv.except_osv(
_('Error'),
_('You can\'t create ClieOp orders more than 30 days in advance.')
)
# Sanity check: can't process in the past
form['execution_date'] = strfdate(max(execution_date, today))
if len(runs) != 1:
raise wizard.except_wizard(
raise osv.except_osv(
_('Error'),
_('You can only combine payment orders of the same type')
)
form['batchtype'] = type = runs.keys()[0]
form['reference'] = runs[type][0].reference[-5:]
return form
type = runs.keys()[0]
vals.update({
'execution_date': strfdate(max(execution_date, today)),
'batchtype': type,
'reference': runs[type][0].reference[-5:],
'payment_order_ids': [[6, 0, payment_order_ids]],
'state': 'create',
})
def _create_clieop(self, cursor, uid, data, context):
def create_clieop(self, cursor, uid, ids, context):
'''
Wizard to actually create the ClieOp3 file
'''
pool = pooler.get_pool(cursor.dbname)
payment_order_obj = pool.get('payment.order')
form = data['form']
payment_order_obj = self.pool.get('payment.order')
clieop_export = self.browse(cursor, uid, ids, context)[0]
clieopfile = None
payment_orders = payment_order_obj.browse(cursor, uid, data['ids'])
for payment_order in payment_orders:
for payment_order in clieop_export.payment_order_ids:
if not clieopfile:
# Just once: create clieop file
our_account_owner = payment_order.mode.bank_id.owner_name \
@@ -269,19 +242,19 @@ class wizard_banking_export_clieop(wizard.interface):
payment_order.mode.bank_id.iban
).localized_BBAN
if not our_account_nr:
raise wizard.except_wizard(
raise osv.except_osv(
_('Error'),
_('Your bank account has to have a valid account number')
)
clieopfile = {'CLIEOPPAY': clieop.PaymentsFile,
'CLIEOPINC': clieop.DirectDebitFile,
'CLIEOPSAL': clieop.SalaryPaymentsFile,
}[form['batchtype']](
identification = form['reference'],
execution_date = form['execution_date'],
}[clieop_export['batchtype']](
identification = clieop_export['reference'],
execution_date = clieop_export['execution_date'],
name_sender = our_account_owner,
accountno_sender = our_account_nr,
test = form['test']
test = clieop_export['test']
)
# ClieOp3 files can contain multiple batches, but we put all
@@ -290,21 +263,21 @@ class wizard_banking_export_clieop(wizard.interface):
# cheaper to combine than it is to split. As we split out all
# reported errors afterwards, there is no additional gain in
# using multiple batches.
if form['fixed_message']:
messages = [form['fixed_message']]
if clieop_export['fixed_message']:
messages = [clieop_export['fixed_message']]
else:
messages = []
# The first payment order processed sets the reference of the
# batch.
batch = clieopfile.batch(
messages = messages,
batch_id = payment_order.reference
batch_id = clieop_export['reference']
)
for line in payment_order.line_ids:
# Check on missing partner of bank account (this can happen!)
if not line.bank_id or not line.bank_id.partner_id:
raise wizard.except_wizard(
raise osv.except_osv(
_('Error'),
_('There is insufficient information.\r\n'
'Both destination address and account '
@@ -323,13 +296,13 @@ class wizard_banking_export_clieop(wizard.interface):
# Is this an IBAN account?
if iban.valid:
if iban.countrycode != 'NL':
raise wizard.except_wizard(
raise osv.except_osv(
_('Error'),
_('You cannot send international bank transfers '
'through ClieOp3!')
)
other_account_nr = iban.localized_BBAN
if form['batchtype'] == 'CLIEOPINC':
if clieop_export['batchtype'] == 'CLIEOPINC':
kwargs['accountno_beneficiary'] = our_account_nr
kwargs['accountno_payer'] = other_account_nr
else:
@@ -339,87 +312,63 @@ class wizard_banking_export_clieop(wizard.interface):
# Generate the specifics of this clieopfile
order = clieopfile.order
values = dict(
filetype = order.name_transactioncode,
identification = order.identification,
prefered_date = strfdate(order.preferred_execution_date),
total_amount = int(order.total_amount) / 100.0,
check_no_accounts = order.total_accountnos,
no_transactions = order.nr_posts,
testcode = order.testcode,
file = base64.encodestring(clieopfile.rawdata),
)
form.update(values)
values['daynumber'] = int(clieopfile.header.file_id[2:])
values['payment_order_ids'] = ','.join(map(str, data['ids']))
data['file_id'] = pool.get('banking.export.clieop').create(cursor, uid, values)
data['clieop'] = clieopfile
form['log'] = ''
return form
file_id = self.pool.get('banking.export.clieop').create(
cursor, uid, dict(
filetype = order.name_transactioncode,
identification = order.identification,
prefered_date = strfdate(order.preferred_execution_date),
total_amount = int(order.total_amount) / 100.0,
check_no_accounts = order.total_accountnos,
no_transactions = order.nr_posts,
testcode = order.testcode,
file = base64.encodestring(clieopfile.rawdata),
daynumber = int(clieopfile.header.file_id[2:]),
payment_order_ids = [
[6, 0, [x.id for x in clieop_export['payment_order_ids']]]
],
), context)
self.write(cursor, uid, [ids[0]], dict(
filetype = order.name_transactioncode,
testcode = order.testcode,
file_id = file_id,
state = 'finish',
), context)
return {
'name': _('Client Opdrachten Export'),
'view_type': 'form',
'view_mode': 'form',
'res_model': self._name,
'domain': [],
'context': dict(context, active_ids=ids),
'type': 'ir.actions.act_window',
'target': 'new',
'res_id': ids[0] or False,
}
def _cancel_clieop(self, cursor, uid, data, context):
def cancel_clieop(self, cursor, uid, ids, context):
'''
Cancel the ClieOp: just drop the file
'''
pool = pooler.get_pool(cursor.dbname)
pool.get('banking.export.clieop').unlink(cursor, uid, data['file_id'])
return {'state': 'end'}
clieop_export = self.read(cursor, uid, ids, ['file_id'], context)[0]
self.pool.get('banking.export.clieop').unlink(cursor, uid, clieop_export['file_id'])
return {'type': 'ir.actions.act_window_close'}
def _save_clieop(self, cursor, uid, data, context):
def save_clieop(self, cursor, uid, ids, context):
'''
Save the ClieOp: mark all payments in the file as 'sent', if not a test
'''
if 'test' not in data['form'] or not data['form']['test']:
pool = pooler.get_pool(cursor.dbname)
clieop_obj = pool.get('banking.export.clieop')
payment_order_obj = pool.get('payment.order')
clieop_export = self.browse(
cursor, uid, ids, context)[0]
if not clieop_export['test']:
clieop_obj = self.pool.get('banking.export.clieop')
payment_order_obj = self.pool.get('payment.order')
clieop_file = clieop_obj.write(
cursor, uid, data['file_id'], {'state':'sent'}
cursor, uid, clieop_export['file_id'].id, {'state':'sent'}
)
payment_order_obj.action_sent(cursor, uid, data['ids'])
return {'state': 'end'}
payment_order_obj.action_sent(
cursor, uid, [x.id for x in clieop_export['payment_order_ids']])
return {'type': 'ir.actions.act_window_close'}
states = {
'init': {
'actions': [_check_orders],
'result': {
'type': 'form',
'arch': form,
'fields' : fields,
'state': [
('end', 'Cancel', 'gtk-cancel'),
('create', 'Create', 'gtk-ok'),
]
}
},
'create': {
'actions': [_create_clieop],
'result': {
'type': 'form',
'arch': file_form,
'fields': file_fields,
'state': [
('cancel', 'Cancel', 'gtk-cancel'),
('save', 'Finish', 'gtk-ok'),
]
},
},
'cancel': {
'actions': [_cancel_clieop],
'result': {
'type': 'state',
'state': 'end'
}
},
'save': {
'actions': [_save_clieop],
'result': {
'type': 'state',
'state': 'end'
},
}
}
wizard_banking_export_clieop('account_banking_nl_clieop.banking_export_clieop')
banking_export_clieop_wizard()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="banking_export_clieop_wizard_view" model="ir.ui.view">
<field name="name">banking.export.clieop.wizard.view</field>
<field name="model">banking.export.clieop.wizard</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Client Opdrachten Export">
<field name="state" invisible="True"/>
<group states="create">
<separator colspan="4" string="Processing Details" />
<field name="batchtype" />
<field name="execution_date" />
<field name="test" />
<separator colspan="4" string="Reference for further communication" />
<field name="reference" colspan="2" />
<separator colspan="4" string="Additional message for all transactions" />
<field name="fixed_message" />
<newline/>
<button icon="gtk-close"
special="cancel"
string="Cancel"
/>
<button icon="gtk-ok"
string="Create"
name="create_clieop"
type="object"
/>
</group>
<group states="finish">
<field name="filetype" />
<field name="identification" />
<field name="total_amount" />
<field name="check_no_accounts" />
<field name="no_transactions" />
<field name="prefered_date" />
<field name="testcode" />
<newline/>
<field name="file_id" />
<field name="file" />
<newline/>
<button icon="gtk-close"
string="Cancel"
name="cancel_clieop"
type="object"
/>
<button icon="gtk-ok"
string="Finish"
name="save_clieop"
type="object"
/>
</group>
</form>
</field>
</record>
</data>
</openerp>