banking-addons-team team mailing list archive
-
banking-addons-team team
-
Mailing list archive
-
Message #00652
[Merge] lp:~therp-nl/banking-addons/ba7.0-MIG-payment into lp:banking-addons/banking-addons-70
Stefan Rijnhart (Therp) has proposed merging lp:~therp-nl/banking-addons/ba7.0-MIG-payment into lp:banking-addons/banking-addons-70 with lp:~therp-nl/banking-addons/ba7.0-MIG-import as a prerequisite.
Commit message:
[MRG] Migration of payment modules
Requested reviews:
Banking Addons Core Editors (banking-addons-team)
For more details, see:
https://code.launchpad.net/~therp-nl/banking-addons/ba7.0-MIG-payment/+merge/166451
- Apply invoice integration of debit orders also to payment orders
- Allow for manual reconciliation of payment and debit orders
- Update payment order workflow so as to allow for unreconciliation
- Consistency in usage of cr and uid variable names
- Adapt to API changes
- Removed obsolete workaround for old bugs in OpenERP server
- Remove unused code paths
For migrations from 6.1, I am still considering how to provide an upgrade path that migrates payment order workflow instances in activity 'sent' to the new 'sent_wait' state.
--
https://code.launchpad.net/~therp-nl/banking-addons/ba7.0-MIG-payment/+merge/166451
Your team Banking Addons Core Editors is requested to review the proposed merge of lp:~therp-nl/banking-addons/ba7.0-MIG-payment into lp:banking-addons/banking-addons-70.
=== modified file 'account_banking/account_banking.py'
--- account_banking/account_banking.py 2013-04-29 09:17:46 +0000
+++ account_banking/account_banking.py 2013-06-04 14:51:27 +0000
@@ -267,7 +267,7 @@
}
_defaults = {
'date': fields.date.context_today,
- 'user_id': lambda self, cursor, uid, context: uid,
+ 'user_id': lambda self, cr, uid, context: uid,
}
account_banking_imported_file()
@@ -320,12 +320,12 @@
['journal_id','period_id']),
]
- def _get_period(self, cursor, uid, date, context=None):
+ def _get_period(self, cr, uid, date, context=None):
'''
Find matching period for date, not meant for _defaults.
'''
period_obj = self.pool.get('account.period')
- periods = period_obj.find(cursor, uid, dt=date, context=context)
+ periods = period_obj.find(cr, uid, dt=date, context=context)
return periods and periods[0] or False
def _prepare_move(
@@ -398,7 +398,7 @@
# Write stored reconcile_id and pay invoices through workflow
if st_line.reconcile_id:
move_ids = [move.id for move in st_line.move_ids]
- torec = account_move_obj.search(
+ torec = account_move_line_obj.search(
cr, uid, [
('move_id', 'in', move_ids),
('account_id', '=', st_line.account_id.id)],
@@ -467,12 +467,12 @@
_inherit = 'account.bank.statement.line'
_description = 'Bank Transaction'
- def _get_period(self, cursor, user, context=None):
+ def _get_period(self, cr, uid, context=None):
date = context.get('date', None)
- periods = self.pool.get('account.period').find(cursor, user, dt=date)
+ periods = self.pool.get('account.period').find(cr, uid, dt=date)
return periods and periods[0] or False
- def _get_currency(self, cursor, user, context=None):
+ def _get_currency(self, cr, uid, context=None):
'''
Get the default currency (required to allow other modules to function,
which assume currency to be a calculated field and thus optional)
@@ -480,7 +480,7 @@
which is inaccessible from within this method.
'''
res_users_obj = self.pool.get('res.users')
- return res_users_obj.browse(cursor, user, user,
+ return res_users_obj.browse(cr, uid, uid,
context=context).company_id.currency_id.id
def _get_invoice_id(self, cr, uid, ids, name, args, context=None):
@@ -605,7 +605,7 @@
iban = sepa.IBAN(acc_number)
return (str(iban), iban.localized_BBAN)
- def create(self, cursor, uid, vals, context=None):
+ def create(self, cr, uid, vals, context=None):
'''
Create dual function IBAN account for SEPA countries
'''
@@ -614,7 +614,7 @@
or vals.get('acc_number_domestic', False))
vals['acc_number'], vals['acc_number_domestic'] = (
self._correct_IBAN(iban))
- return self._founder.create(self, cursor, uid, vals, context)
+ return self._founder.create(self, cr, uid, vals, context)
def write(self, cr, uid, ids, vals, context=None):
'''
@@ -637,7 +637,7 @@
self._founder.write(self, cr, uid, account['id'], vals, context)
return True
- def search(self, cursor, uid, args, *rest, **kwargs):
+ def search(self, cr, uid, args, *rest, **kwargs):
'''
Overwrite search, as both acc_number and iban now can be filled, so
the original base_iban 'search and search again fuzzy' tactic now can
@@ -698,7 +698,7 @@
# Original search
results = super(res_partner_bank, self).search(
- cursor, uid, newargs, *rest, **kwargs)
+ cr, uid, newargs, *rest, **kwargs)
return results
def read(
@@ -721,23 +721,23 @@
return records
return records[0]
- def check_iban(self, cursor, uid, ids):
+ def check_iban(self, cr, uid, ids):
'''
Check IBAN number
'''
- for bank_acc in self.browse(cursor, uid, ids):
+ for bank_acc in self.browse(cr, uid, ids):
if bank_acc.state == 'iban' and bank_acc.acc_number:
iban = sepa.IBAN(bank_acc.acc_number)
if not iban.valid:
return False
return True
- def get_bban_from_iban(self, cursor, uid, ids, context=None):
+ def get_bban_from_iban(self, cr, uid, ids, context=None):
'''
Return the local bank account number aka BBAN from the IBAN.
'''
res = {}
- for record in self.browse(cursor, uid, ids, context):
+ for record in self.browse(cr, uid, ids, context):
if not record.state == 'iban':
res[record.id] = False
else:
@@ -763,7 +763,7 @@
)
def onchange_domestic(
- self, cursor, uid, ids, acc_number,
+ self, cr, uid, ids, acc_number,
partner_id, country_id, context=None):
'''
Trigger to find IBAN. When found:
@@ -785,7 +785,7 @@
# which can be overridden by the user.
# 1. Use provided country_id (manually filled)
if country_id:
- country = country_obj.browse(cursor, uid, country_id)
+ country = country_obj.browse(cr, uid, country_id)
country_ids = [country_id]
# 2. Use country_id of found bank accounts
# This can be usefull when there is no country set in the partners
@@ -793,7 +793,7 @@
# account itself before this method was triggered.
elif ids and len(ids) == 1:
partner_bank_obj = self.pool.get('res.partner.bank')
- partner_bank_id = partner_bank_obj.browse(cursor, uid, ids[0])
+ partner_bank_id = partner_bank_obj.browse(cr, uid, ids[0])
if partner_bank_id.country_id:
country = partner_bank_id.country_id
country_ids = [country.id]
@@ -804,12 +804,12 @@
# bank account, hence the additional check.
elif partner_id:
partner_obj = self.pool.get('res.partner')
- country = partner_obj.browse(cursor, uid, partner_id).country
+ country = partner_obj.browse(cr, uid, partner_id).country
country_ids = country and [country.id] or []
# 4. Without any of the above, take the country from the company of
# the handling user
if not country_ids:
- user = self.pool.get('res.users').browse(cursor, uid, uid)
+ user = self.pool.get('res.users').browse(cr, uid, uid)
# Try user companies partner (user no longer has address in 6.1)
if (user.company_id and
user.company_id.partner_id and
@@ -830,7 +830,7 @@
# Complete data with online database when available
if country_ids:
country = country_obj.browse(
- cursor, uid, country_ids[0], context=context)
+ cr, uid, country_ids[0], context=context)
values['country_id'] = country_ids[0]
if country and country.code in sepa.IBAN.countries:
try:
@@ -842,7 +842,7 @@
values['acc_number'] = unicode(iban_acc)
values['state'] = 'iban'
bank_id, country_id = get_or_create_bank(
- self.pool, cursor, uid,
+ self.pool, cr, uid,
info.bic or iban_acc.BIC_searchkey,
name = info.bank
)
@@ -911,7 +911,7 @@
'''
_inherit = 'res.bank'
- def onchange_bic(self, cursor, uid, ids, bic, name, context=None):
+ def onchange_bic(self, cr, uid, ids, bic, name, context=None):
'''
Trigger to auto complete other fields.
'''
@@ -924,7 +924,7 @@
if address and address.country_id:
country_id = self.pool.get('res.country').search(
- cursor, uid, [('code','=',address.country_id)]
+ cr, uid, [('code','=',address.country_id)]
)
country_id = country_id and country_id[0] or False
else:
=== modified file 'account_banking/account_banking_view.xml'
--- account_banking/account_banking_view.xml 2013-05-01 14:25:04 +0000
+++ account_banking/account_banking_view.xml 2013-06-04 14:51:27 +0000
@@ -36,7 +36,6 @@
<record model="ir.ui.view" id="view_banking_account_settings_form">
<field name="name">account.banking.account.settings.form</field>
<field name="model">account.banking.account.settings</field>
- <field name="type">form</field>
<field name="arch" type="xml">
<form string="Default Import Settings for Bank Account">
<field name="company_id"
@@ -70,7 +69,6 @@
<record model="ir.ui.view" id="view_banking_account_settings_tree">
<field name="name">account.banking.account.settings.tree</field>
<field name="model">account.banking.account.settings</field>
- <field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Default Import Settings for Bank Account">
<field name="company_id" />
@@ -99,7 +97,6 @@
<record model="ir.ui.view" id="view_account_banking_imported_file_form">
<field name="name">account.banking.imported.file.form</field>
<field name="model">account.banking.imported.file</field>
- <field name="type">form</field>
<field name="arch" type="xml">
<form string="Imported Bank Statements">
<notebook colspan="4">
@@ -124,7 +121,6 @@
<record model="ir.ui.view" id="view_account_banking_imported_file_tree">
<field name="name">account.banking.imported.file.tree</field>
<field name="model">account.banking.imported.file</field>
- <field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Imported Bank Statements Files" colors="red:state=='error';blue:state=='unfinished'">
<field name="company_id" />
@@ -182,7 +178,6 @@
<field name="name">account.bank.statement.tree.banking</field>
<field name="inherit_id" ref="account.view_bank_statement_tree" />
<field name="model">account.bank.statement</field>
- <field name="type">tree</field>
<field name="arch" type="xml">
<!-- Remove period from bank statement -->
<field name="period_id" position="replace">
@@ -197,7 +192,6 @@
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field name="model">account.bank.statement</field>
<field name="sequence" eval="60"/>
- <field name="type">form</field>
<field name="arch" type="xml">
<data>
<page string="Transactions" position="after">
@@ -292,7 +286,6 @@
<field name="name">res.partner.bank.form.banking-2</field>
<field name="model">res.partner.bank</field>
<field name="inherit_id" ref="base.view_partner_bank_form"/>
- <field name="type">form</field>
<field name="priority" eval="24"/>
<field name="arch" type="xml">
<data>
@@ -311,7 +304,6 @@
<field name="name">res.bank.form.banking-1</field>
<field name="model">res.bank</field>
<field name="inherit_id" ref="base.view_res_bank_form"/>
- <field name="type">form</field>
<field name="arch" type="xml">
<field name="bic" position="replace">
<field name="bic" on_change="onchange_bic(bic, name)"/>
@@ -322,7 +314,6 @@
<record model="ir.ui.view" id="view_bank_statement_line_tree">
<field name="name">Bank statement line tree view</field>
<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';darkmagenta:match_multi == True;crimson:duplicate == True;grey:state=='draft';">
<field name="sequence" readonly="1" invisible="1"/>
=== modified file 'account_banking/banking_import_transaction.py'
--- account_banking/banking_import_transaction.py 2013-06-03 09:39:56 +0000
+++ account_banking/banking_import_transaction.py 2013-06-04 14:51:27 +0000
@@ -62,7 +62,7 @@
return []
digits = dp.get_precision('Account')(cr)[1]
- amount = round(abs(trans.transferred_amount), digits)
+ amount = round(abs(trans.statement_line_id.amount), digits)
# Make sure to be able to pinpoint our costs invoice for later
# matching
reference = '%s.%s: %s' % (trans.statement, trans.transaction, trans.reference)
@@ -233,10 +233,6 @@
digits = dp.get_precision('Account')(cr)[1]
partial = False
- # Disabled splitting transactions for now
- # TODO allow splitting in the interactive wizard
- allow_splitting = False
-
# Search invoice on partner
if partner_ids:
candidates = [
@@ -276,7 +272,7 @@
candidates = [
x for x in move_lines
if (is_zero(x.move_id, ((x.debit or 0.0) - (x.credit or 0.0)) -
- trans.transferred_amount)
+ trans.statement_line_id.amount)
and convert.str2date(x.date, '%Y-%m-%d') <=
(convert.str2date(trans.execution_date, '%Y-%m-%d') +
self.payment_window)
@@ -292,7 +288,7 @@
# TODO: currency coercing
best = [x for x in candidates
if (is_zero(x.move_id, ((x.debit or 0.0) - (x.credit or 0.0)) -
- trans.transferred_amount)
+ trans.statement_line_id.amount)
and convert.str2date(x.date, '%Y-%m-%d') <=
(convert.str2date(trans.execution_date, '%Y-%m-%d') +
self.payment_window))
@@ -343,7 +339,7 @@
trans2 = None
if move_line and partial:
- found = round(trans.transferred_amount, digits)
+ found = round(trans.statement_line_id.amount, digits)
if abs(expected) == abs(found):
partial = False
# Last partial payment will not flag invoice paid without
@@ -358,24 +354,6 @@
elif abs(expected) > abs(found):
# Partial payment, reuse invoice
_cache(move_line, expected - found)
- elif abs(expected) < abs(found) and allow_splitting:
- # Possible combined payments, need to split transaction to
- # verify
- _cache(move_line)
- trans2 = self.copy(
- cr, uid, trans.id,
- dict(
- transferred_amount = trans.transferred_amount - expected,
- transaction = trans.transaction + 'b',
- parent_id = trans.id,
- ), context=context)
- # update the current record
- self.write(cr, uid, trans.id, dict(
- transferred_amount = expected,
- transaction = trans.transaction + 'a',
- ), context)
- # rebrowse the current record after writing
- trans = self.browse(cr, uid, trans.id, context=context)
if move_line:
account_ids = [
x.id for x in bank_account_ids
@@ -712,7 +690,7 @@
# due to float representation and rounding difficulties
for trans in self.browse(cr, uid, ids, context=context):
if self.pool.get('res.currency').is_zero(
- cr, uid,
+ cr, uid,
trans.statement_id.currency,
me['transferred_amount'] - trans.transferred_amount):
dupes.append(trans.id)
@@ -810,6 +788,12 @@
move_info['invoice_ids'][0]
)
return vals
+
+ def hook_match_payment(self, cr, uid, transaction, log, context=None):
+ """
+ To override in module 'account_banking_payment'
+ """
+ return False
def match(self, cr, uid, ids, results=None, context=None):
if not ids:
@@ -872,12 +856,6 @@
else:
transaction = transactions[i]
- if (transaction.statement_line_id and
- transaction.statement_line_id.state == 'confirmed'):
- raise orm.except_orm(
- _("Cannot perform match"),
- _("Cannot perform match on a confirmed transction"))
-
if transaction.local_account in error_accounts:
results['trans_skipped_cnt'] += 1
if not injected:
@@ -962,6 +940,44 @@
else:
info[transaction.local_account][currency_code] = account_info
+ # Link accounting period
+ period_id = banktools.get_period(
+ self.pool, cr, uid, transaction.effective_date,
+ company, results['log'])
+ if not period_id:
+ results['trans_skipped_cnt'] += 1
+ if not injected:
+ i += 1
+ continue
+
+ if transaction.statement_line_id:
+ if transaction.statement_line_id.state == 'confirmed':
+ raise orm.except_orm(
+ _("Cannot perform match"),
+ _("Cannot perform match on a confirmed transction"))
+ else:
+ values = {
+ 'name': '%s.%s' % (transaction.statement, transaction.transaction),
+ 'date': transaction.effective_date,
+ 'amount': transaction.transferred_amount,
+ 'statement_id': transaction.statement_id.id,
+ 'note': transaction.message,
+ 'ref': transaction.reference,
+ 'period_id': period_id,
+ 'currency': account_info.currency_id.id,
+ 'import_transaction_id': transaction.id,
+ 'account_id': (
+ transaction.transferred_amount < 0 and
+ account_info.default_credit_account_id.id or
+ account_info.default_debit_account_id.id),
+ }
+ statement_line_id = statement_line_obj.create(cr, uid, values, context)
+ results['trans_loaded_cnt'] += 1
+ transaction.write({'statement_line_id': statement_line_id})
+ transaction.refresh()
+ if transaction.statement_id.id not in imported_statement_ids:
+ imported_statement_ids.append(transaction.statement_id.id)
+
# Final check: no coercion of currencies!
if transaction.local_currency \
and account_info.currency_id.name != transaction.local_currency:
@@ -971,8 +987,8 @@
' uses different currency than the defined bank journal.'
) % {
'bank_account': transactions.local_account,
- 'transaction_id': transaction.statement,
- 'statement_id': transaction.transaction,
+ 'statement_id': transaction.statement,
+ 'transaction_id': transaction.transaction,
}
)
error_accounts[transaction.local_account] = True
@@ -981,16 +997,6 @@
i += 1
continue
- # Link accounting period
- period_id = banktools.get_period(
- self.pool, cr, uid, transaction.effective_date,
- company, results['log'])
- if not period_id:
- results['trans_skipped_cnt'] += 1
- if not injected:
- i += 1
- continue
-
# When bank costs are part of transaction itself, split it.
if transaction.type != bt.BANK_COSTS and transaction.provision_costs:
# Create new transaction for bank costs
@@ -1022,13 +1028,12 @@
), context=context)
# rebrowse the current record after writing
transaction = self.browse(cr, uid, transaction.id, context=context)
- # Match full direct debit orders
- if transaction.type == bt.DIRECT_DEBIT and has_payment:
- move_info = self._match_debit_order(
- cr, uid, transaction, results['log'], context)
- if transaction.type == bt.STORNO and has_payment:
- move_info = self._match_storno(
- cr, uid, transaction, results['log'], context)
+
+ # Match payment and direct debit orders
+ move_info_payment = self.hook_match_payment(
+ cr, uid, transaction, results['log'], context=context)
+ if move_info_payment:
+ move_info = move_info_payment
# Allow inclusion of generated bank invoices
if transaction.type == bt.BANK_COSTS:
@@ -1075,7 +1080,7 @@
# Credit means payment... isn't it?
if (not move_info
- and transaction.transferred_amount < 0 and payment_lines):
+ and transaction.statement_line_id.amount < 0 and payment_lines):
# Link open payment - if any
# Note that _match_payment is defined in the
# account_banking_payment module which should be installed
@@ -1106,7 +1111,7 @@
# settings to overrule this. Note that you need to change
# the internal type of these accounts to either 'payable'
# or 'receivable' to enable usage like this.
- if transaction.transferred_amount < 0:
+ if transaction.statement_line_id.amount < 0:
if len(partner_banks) == 1:
account_id = (
partner_banks[0].partner_id.property_account_payable and
@@ -1122,7 +1127,7 @@
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)
- values = {}
+ values = {'account_id': account_id}
self_values = {}
if move_info:
results['trans_matched_cnt'] += 1
@@ -1140,28 +1145,8 @@
len(partner_banks) == 1):
values['partner_bank_id'] = partner_banks[0].id
- if not transaction.statement_line_id:
- values.update(dict(
- name = '%s.%s' % (transaction.statement, transaction.transaction),
- date = transaction.effective_date,
- amount = transaction.transferred_amount,
- statement_id = transaction.statement_id.id,
- note = transaction.message,
- ref = transaction.reference,
- period_id = period_id,
- currency = account_info.currency_id.id,
- account_id = account_id,
- import_transaction_id = transaction.id,
- ))
-
- statement_line_id = statement_line_obj.create(cr, uid, values, context)
- results['trans_loaded_cnt'] += 1
- self_values['statement_line_id'] = statement_line_id
- if transaction.statement_id.id not in imported_statement_ids:
- imported_statement_ids.append(transaction.statement_id.id)
- else:
- statement_line_obj.write(
- cr, uid, transaction.statement_line_id.id, values, context)
+ statement_line_obj.write(
+ cr, uid, transaction.statement_line_id.id, values, context)
self.write(cr, uid, transaction.id, self_values, context)
if not injected:
i += 1
@@ -1350,9 +1335,16 @@
'parent_id': fields.many2one(
'banking.import.transaction', 'Split off from this transaction'),
# match fields
- 'match_type': fields.selection(
- [('manual', 'Manual'), ('move','Move'), ('invoice', 'Invoice'),
- ], 'Match type'),
+ 'match_type': fields.selection([
+ ('move','Move'),
+ ('invoice', 'Invoice'),
+ ('payment', 'Payment line'),
+ ('payment_order', 'Payment order'),
+ ('storno', 'Storno'),
+ ('manual', 'Manual'),
+ ('payment_manual', 'Payment line (manual)'),
+ ('payment_order_manual', 'Payment order (manual)'),
+ ], 'Match type'),
'match_multi': fields.function(
_get_match_multi, method=True, string='Multi match',
type='boolean'),
@@ -1439,9 +1431,16 @@
string='Possible duplicate import', readonly=True),
'match_type': fields.related(
'import_transaction_id', 'match_type', type='selection',
- selection=[('manual', 'Manual'), ('move','Move'),
- ('invoice', 'Invoice'),
- ],
+ selection=[
+ ('move','Move'),
+ ('invoice', 'Invoice'),
+ ('payment', 'Payment line'),
+ ('payment_order', 'Payment order'),
+ ('storno', 'Storno'),
+ ('manual', 'Manual'),
+ ('payment_manual', 'Payment line (manual)'),
+ ('payment_order_manual', 'Payment order (manual)'),
+ ],
string='Match type', readonly=True,),
'state': fields.selection(
[('draft', 'Draft'), ('confirmed', 'Confirmed')], 'State',
@@ -1751,14 +1750,14 @@
class account_bank_statement(orm.Model):
_inherit = 'account.bank.statement'
- def _end_balance(self, cursor, user, ids, name, attr, context=None):
+ def _end_balance(self, cr, uid, ids, name, attr, context=None):
"""
This method taken from account/account_bank_statement.py and
altered to take the statement line subflow into account
"""
res = {}
- statements = self.browse(cursor, user, ids, context=context)
+ statements = self.browse(cr, uid, ids, context=context)
for statement in statements:
res[statement.id] = statement.balance_start
=== modified file 'account_banking/wizard/bank_import.py'
--- account_banking/wizard/bank_import.py 2013-04-27 10:14:24 +0000
+++ account_banking/wizard/bank_import.py 2013-06-04 14:51:27 +0000
@@ -28,9 +28,9 @@
The parsing is done in the parser modules. Every parser module is required to
use parser.models as a mean of communication with the business logic.
'''
-from osv import orm, fields
import base64
import datetime
+from openerp.osv import orm, fields
from openerp.tools.translate import _
from openerp.addons.account_banking.parsers import models
from openerp.addons.account_banking.parsers import convert
@@ -97,13 +97,13 @@
class banking_import(orm.TransientModel):
_name = 'account.banking.bank.import'
- def import_statements_file(self, cursor, uid, ids, context):
+ def import_statements_file(self, cr, uid, ids, context):
'''
Import bank statements / bank transactions file.
This method is a wrapper for the business logic on the transaction.
The parser modules represent the decoding logic.
'''
- banking_import = self.browse(cursor, uid, ids, context)[0]
+ banking_import = self.browse(cr, uid, ids, context)[0]
statements_file = banking_import.file
data = base64.decodestring(statements_file)
@@ -125,10 +125,10 @@
# Get the company
company = (banking_import.company or
- user_obj.browse(cursor, uid, uid, context).company_id)
+ user_obj.browse(cr, uid, uid, context).company_id)
# Parse the file
- statements = parser.parse(cursor, data)
+ statements = parser.parse(cr, data)
if any([x for x in statements if not x.is_valid()]):
raise orm.except_orm(
@@ -137,7 +137,7 @@
)
# Create the file now, as the statements need to be linked to it
- import_id = statement_file_obj.create(cursor, uid, dict(
+ import_id = statement_file_obj.create(cr, uid, dict(
company_id = company.id,
file = statements_file,
state = 'unfinished',
@@ -184,7 +184,7 @@
else:
# Pull account info/currency
account_info = banktools.get_company_bank_account(
- self.pool, cursor, uid, statement.local_account,
+ self.pool, cr, uid, statement.local_account,
statement.local_currency, company, results.log
)
if not account_info:
@@ -238,7 +238,7 @@
# (e.g. a datetime string of the moment of import)
# and have potential duplicates flagged by the
# matching procedure
- statement_ids = statement_obj.search(cursor, uid, [
+ statement_ids = statement_obj.search(cr, uid, [
('name', '=', statement.id),
('date', '=', convert.date2str(statement.date)),
])
@@ -252,7 +252,7 @@
# Get the period for the statement (as bank statement object checks this)
period_ids = period_obj.search(
- cursor, uid, [
+ cr, uid, [
('company_id', '=', company.id),
('date_start', '<=', statement.date),
('date_stop', '>=', statement.date),
@@ -270,7 +270,7 @@
continue
# Create the bank statement record
- statement_id = statement_obj.create(cursor, uid, dict(
+ statement_id = statement_obj.create(cr, uid, dict(
name = statement.id,
journal_id = account_info.journal_id.id,
date = convert.date2str(statement.date),
@@ -302,21 +302,21 @@
values['local_currency'] = statement.local_currency
transaction_id = import_transaction_obj.create(
- cursor, uid, values, context=context)
+ cr, uid, values, context=context)
transaction_ids.append(transaction_id)
results.stat_loaded_cnt += 1
- import_transaction_obj.match(cursor, uid, transaction_ids, results=results, context=context)
+ import_transaction_obj.match(cr, uid, transaction_ids, results=results, context=context)
#recompute statement end_balance for validation
statement_obj.button_dummy(
- cursor, uid, imported_statement_ids, context=context)
+ cr, uid, imported_statement_ids, context=context)
# Original code. Didn't take workflow logistics into account...
#
- #cursor.execute(
+ #cr.execute(
# "UPDATE payment_order o "
# "SET state = 'done', "
# "date_done = '%s' "
@@ -358,13 +358,13 @@
]
text_log = '\n'.join(report + results.log)
state = results.error_cnt and 'error' or 'ready'
- statement_file_obj.write(cursor, uid, import_id, dict(
+ statement_file_obj.write(cr, uid, import_id, dict(
state = state, log = text_log,
), context)
if not imported_statement_ids or not results.trans_loaded_cnt:
# file state can be 'ready' while import state is 'error'
state = 'error'
- self.write(cursor, uid, [ids[0]], dict(
+ self.write(cr, uid, [ids[0]], dict(
import_id = import_id, log = text_log, state = state,
statement_ids = [(6, 0, imported_statement_ids)],
), context)
=== modified file 'account_banking/wizard/bank_import_view.xml'
--- account_banking/wizard/bank_import_view.xml 2012-05-02 15:09:49 +0000
+++ account_banking/wizard/bank_import_view.xml 2013-06-04 14:51:27 +0000
@@ -4,7 +4,6 @@
<record id="view_banking_import" model="ir.ui.view">
<field name="name">account.banking.bank.import</field>
<field name="model">account.banking.bank.import</field>
- <field name="type">form</field>
<field name="arch" type="xml">
<form string="Import Bank Transactions File">
<group colspan="4" states="init,ready,error">
=== modified file 'account_banking/wizard/banking_transaction_wizard.py'
--- account_banking/wizard/banking_transaction_wizard.py 2013-05-01 14:25:04 +0000
+++ account_banking/wizard/banking_transaction_wizard.py 2013-06-04 14:51:27 +0000
@@ -90,9 +90,11 @@
statement_line_obj = self.pool.get('account.bank.statement.line')
transaction_obj = self.pool.get('banking.import.transaction')
- if not vals:
+ if not vals or not ids:
return True
+ wiz = self.browse(cr, uid, ids[0], context=context)
+
# The following fields get never written
# they are just triggers for manual matching
# which populates regular fields on the transaction
@@ -101,47 +103,22 @@
manual_invoice_ids = vals.pop('manual_invoice_ids', [])
manual_move_line_ids = vals.pop('manual_move_line_ids', [])
- # Support for writing fields.related is still flakey:
- # https://bugs.launchpad.net/openobject-server/+bug/915975
- # Will do so myself.
-
- # Separate the related fields
- transaction_vals = {}
- wizard_vals = vals.copy()
- for key in vals.keys():
- field = self._columns[key]
- if (isinstance(field, fields.related) and
- field._arg[0] == 'import_transaction_id'):
- transaction_vals[field._arg[1]] = vals[key]
- del wizard_vals[key]
-
- # write the related fields on the transaction model
- if isinstance(ids, int):
- ids = [ids]
- for wizard in self.browse(cr, uid, ids, context=context):
- if wizard.import_transaction_id:
- transaction_obj.write(
- cr, uid, wizard.import_transaction_id.id,
- transaction_vals, context=context)
-
- # write other fields to the wizard model
res = super(banking_transaction_wizard, self).write(
- cr, uid, ids, wizard_vals, context=context)
+ cr, uid, ids, vals, context=context)
+ wiz.refresh()
- # End of workaround for lp:915975
-
- """ Process the logic of the written values """
+ # Process the logic of the written values
# An invoice is selected from multiple candidates
if vals and 'invoice_id' in vals:
- for wiz in self.browse(cr, uid, ids, context=context):
- if (wiz.import_transaction_id.match_type == 'invoice' and
+ if (wiz.import_transaction_id.match_type == 'invoice' and
wiz.import_transaction_id.invoice_id):
- # the current value might apply
- if (wiz.move_line_id and wiz.move_line_id.invoice and
- wiz.move_line_id.invoice.id == wiz.invoice_id.id):
- found = True
- continue
+ found = False
+ # the current value might apply
+ if (wiz.move_line_id and wiz.move_line_id.invoice and
+ wiz.move_line_id.invoice == wiz.invoice_id):
+ found = True
+ else:
# Otherwise, retrieve the move line for this invoice
# Given the arity of the relation, there is are always
# multiple possibilities but the move lines here are
@@ -149,8 +126,8 @@
# and the regular invoice workflow should only come up with
# one of those only.
for move_line in wiz.import_transaction_id.move_line_ids:
- if (move_line.invoice.id ==
- wiz.import_transaction_id.invoice_id.id):
+ if (move_line.invoice ==
+ wiz.import_transaction_id.invoice_id):
transaction_obj.write(
cr, uid, wiz.import_transaction_id.id,
{ 'move_line_id': move_line.id, }, context=context)
@@ -161,15 +138,12 @@
}, context=context)
found = True
break
- # Cannot match the invoice
- if not found:
- # transaction_obj.write(
- # cr, uid, wiz.import_transaction_id.id,
- # { 'invoice_id': False, }, context=context)
- orm.except_orm(
- _("No entry found for the selected invoice"),
- _("No entry found for the selected invoice. " +
- "Try manual reconciliation."))
+ # Cannot match the invoice
+ if not found:
+ orm.except_orm(
+ _("No entry found for the selected invoice"),
+ _("No entry found for the selected invoice. " +
+ "Try manual reconciliation."))
if manual_move_line_id or manual_invoice_id \
or manual_move_line_ids or manual_invoice_ids:
@@ -320,24 +294,9 @@
{'duplicate': not wiz['duplicate']}, context=context)
return self.create_act_window(cr, uid, ids, context=None)
- def _get_default_match_type(self, cr, uid, context=None):
- """
- Take initial value for the match type from the statement line
- """
- res = False
- if context and 'statement_line_id' in context:
- res = self.pool.get('account.bank.statement.line').read(
- cr, uid, context['statement_line_id'],
- ['match_type'], context=context)['match_type']
- return res
-
def button_done(self, cr, uid, ids, context=None):
return {'type': 'ir.actions.act_window_close'}
- _defaults = {
-# 'match_type': _get_default_match_type,
- }
-
_columns = {
'name': fields.char('Name', size=64),
'statement_line_id': fields.many2one(
@@ -389,8 +348,18 @@
'import_transaction_id', 'match_multi',
type="boolean", string='Multiple matches'),
'match_type': fields.related(
- 'import_transaction_id', 'match_type',
- type="char", size=16, string='Match type', readonly=True),
+ 'import_transaction_id', 'match_type', type='selection',
+ selection=[
+ ('move','Move'),
+ ('invoice', 'Invoice'),
+ ('payment', 'Payment line'),
+ ('payment_order', 'Payment order'),
+ ('storno', 'Storno'),
+ ('manual', 'Manual'),
+ ('payment_manual', 'Payment line (manual)'),
+ ('payment_order_manual', 'Payment order (manual)'),
+ ],
+ string='Match type', readonly=True),
'manual_invoice_id': fields.many2one(
'account.invoice', 'Match this invoice',
domain=[('reconciled', '=', False)]),
@@ -421,8 +390,6 @@
string="Analytic Account"),
'move_currency_amount': fields.related('import_transaction_id','move_currency_amount',
type='float', string='Match Currency Amount', readonly=True),
- #'manual_payment_order_id': fields.many2one(
- # 'payment.order', "Payment order to reconcile"),
}
banking_transaction_wizard()
=== modified file 'account_banking/wizard/banking_transaction_wizard.xml'
--- account_banking/wizard/banking_transaction_wizard.xml 2013-04-24 14:36:15 +0000
+++ account_banking/wizard/banking_transaction_wizard.xml 2013-06-04 14:51:27 +0000
@@ -3,7 +3,6 @@
<data>
<record model="ir.ui.view" id="transaction_wizard_first">
<field name="name">transaction.wizard.first</field>
- <field name="type">form</field>
<field name="model">banking.transaction.wizard</field>
<field name="arch" type="xml">
<form string="Match transaction">
=== modified file 'account_banking/wizard/banktools.py'
--- account_banking/wizard/banktools.py 2013-05-01 14:25:04 +0000
+++ account_banking/wizard/banktools.py 2013-06-04 14:51:27 +0000
@@ -51,7 +51,7 @@
return False
return period_ids[0]
-def get_bank_accounts(pool, cursor, uid, account_number, log, fail=False):
+def get_bank_accounts(pool, cr, uid, account_number, log, fail=False):
'''
Get the bank account with account number account_number
'''
@@ -60,13 +60,13 @@
return []
partner_bank_obj = pool.get('res.partner.bank')
- bank_account_ids = partner_bank_obj.search(cursor, uid, [
+ bank_account_ids = partner_bank_obj.search(cr, uid, [
('acc_number', '=', account_number)
])
if not bank_account_ids:
# SR 2012-02-19 does the search() override in res_partner_bank
# provides this result on the previous query?
- bank_account_ids = partner_bank_obj.search(cursor, uid, [
+ bank_account_ids = partner_bank_obj.search(cr, uid, [
('acc_number_domestic', '=', account_number)
])
if not bank_account_ids:
@@ -76,7 +76,7 @@
% dict(account_no=account_number)
)
return []
- return partner_bank_obj.browse(cursor, uid, bank_account_ids)
+ return partner_bank_obj.browse(cr, uid, bank_account_ids)
def _has_attr(obj, attr):
# Needed for dangling addresses and a weird exception scheme in
@@ -129,14 +129,14 @@
'name %(name)s') % {'name': name})
return partner_ids and partner_ids[0] or False
-def get_company_bank_account(pool, cursor, uid, account_number, currency,
+def get_company_bank_account(pool, cr, uid, account_number, currency,
company, log):
'''
Get the matching bank account for this company. Currency is the ISO code
for the requested currency.
'''
results = struct()
- bank_accounts = get_bank_accounts(pool, cursor, uid, account_number, log,
+ bank_accounts = get_bank_accounts(pool, cr, uid, account_number, log,
fail=True)
if not bank_accounts:
return False
@@ -159,12 +159,12 @@
# Find matching journal for currency
journal_obj = pool.get('account.journal')
- journal_ids = journal_obj.search(cursor, uid, [
+ journal_ids = journal_obj.search(cr, uid, [
('type', '=', 'bank'),
('currency.name', '=', currency or company.currency_id.name)
])
if currency == company.currency_id.name:
- journal_ids_no_curr = journal_obj.search(cursor, uid, [
+ journal_ids_no_curr = journal_obj.search(cr, uid, [
('type', '=', 'bank'), ('currency', '=', False)
])
journal_ids.extend(journal_ids_no_curr)
@@ -172,9 +172,9 @@
criteria.append(('journal_id', 'in', journal_ids))
# Find bank account settings
- bank_settings_ids = bank_settings_obj.search(cursor, uid, criteria)
+ bank_settings_ids = bank_settings_obj.search(cr, uid, criteria)
if bank_settings_ids:
- settings = bank_settings_obj.browse(cursor, uid, bank_settings_ids)[0]
+ settings = bank_settings_obj.browse(cr, uid, bank_settings_ids)[0]
results.company_id = company
results.journal_id = settings.journal_id
@@ -192,7 +192,7 @@
return results
-def get_or_create_bank(pool, cursor, uid, bic, online=False, code=None,
+def get_or_create_bank(pool, cr, uid, bic, online=False, code=None,
name=None):
'''
Find or create the bank with the provided BIC code.
@@ -208,27 +208,27 @@
if len(bic) < 8:
# search key
bank_ids = bank_obj.search(
- cursor, uid, [
+ cr, uid, [
('bic', '=', bic[:6])
])
if not bank_ids:
bank_ids = bank_obj.search(
- cursor, uid, [
+ cr, uid, [
('bic', 'ilike', bic + '%')
])
else:
bank_ids = bank_obj.search(
- cursor, uid, [
+ cr, uid, [
('bic', '=', bic)
])
if bank_ids and len(bank_ids) == 1:
- banks = bank_obj.browse(cursor, uid, bank_ids)
+ banks = bank_obj.browse(cr, uid, bank_ids)
return banks[0].id, banks[0].country.id
country_obj = pool.get('res.country')
country_ids = country_obj.search(
- cursor, uid, [('code', '=', bic[4:6])]
+ cr, uid, [('code', '=', bic[4:6])]
)
country_id = country_ids and country_ids[0] or False
bank_id = False
@@ -236,7 +236,7 @@
if online:
info, address = sepa.online.bank_info(bic)
if info:
- bank_id = bank_obj.create(cursor, uid, dict(
+ bank_id = bank_obj.create(cr, uid, dict(
code = info.code,
name = info.name,
street = address.street,
@@ -250,7 +250,7 @@
info = struct(name=name, code=code)
if not online or not bank_id:
- bank_id = bank_obj.create(cursor, uid, dict(
+ bank_id = bank_obj.create(cr, uid, dict(
code = info.code or 'UNKNOW',
name = info.name or _('Unknown Bank'),
country = country_id,
=== modified file 'account_banking_nl_clieop/__openerp__.py'
--- account_banking_nl_clieop/__openerp__.py 2013-04-15 14:14:27 +0000
+++ account_banking_nl_clieop/__openerp__.py 2013-06-04 14:51:27 +0000
@@ -37,5 +37,5 @@
ClieOp format is used by Dutch banks to batch national bank transfers.
This module uses the account_banking logic.
''',
- 'installable': False,
+ 'installable': True,
}
=== modified file 'account_banking_nl_clieop/account_banking_nl_clieop.py'
--- account_banking_nl_clieop/account_banking_nl_clieop.py 2013-04-15 13:59:50 +0000
+++ account_banking_nl_clieop/account_banking_nl_clieop.py 2013-06-04 14:51:27 +0000
@@ -1,6 +1,7 @@
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
+# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
@@ -18,11 +19,12 @@
#
##############################################################################
-from osv import osv, fields
from datetime import date
-from tools.translate import _
-
-class clieop_export(osv.osv):
+from openerp.osv import orm, fields
+from openerp.tools.translate import _
+
+
+class clieop_export(orm.Model):
'''ClieOp3 Export'''
_name = 'banking.export.clieop'
_description = __doc__
@@ -80,7 +82,7 @@
last = 1
last_ids = self.search(cr, uid, [
('date_generated', '=',
- fields.date.context_today(cr,uid,context))
+ fields.date.context_today(self, cr,uid,context))
], context=context)
if last_ids:
last = 1 + max([x['daynumber'] for x in self.read(
@@ -94,6 +96,3 @@
'state': 'draft',
'daynumber': get_daynr,
}
-clieop_export()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== modified file 'account_banking_nl_clieop/account_banking_nl_clieop.xml'
--- account_banking_nl_clieop/account_banking_nl_clieop.xml 2013-01-02 15:14:53 +0000
+++ account_banking_nl_clieop/account_banking_nl_clieop.xml 2013-06-04 14:51:27 +0000
@@ -11,7 +11,6 @@
<record id="view_banking_export_clieop_form" model="ir.ui.view">
<field name="name">account.banking.export.clieop.form</field>
<field name="model">banking.export.clieop</field>
- <field name="type">form</field>
<field name="arch" type="xml">
<form string="Client Opdrachten Export">
<notebook>
@@ -48,7 +47,6 @@
<record id="view_banking_export_clieop_tree" model="ir.ui.view">
<field name="name">account.banking.export.clieop.tree</field>
<field name="model">banking.export.clieop</field>
- <field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Client Opdrachten Export">
<field name="filetype" />
=== modified file 'account_banking_nl_clieop/wizard/export_clieop.py'
--- account_banking_nl_clieop/wizard/export_clieop.py 2013-04-15 13:59:50 +0000
+++ account_banking_nl_clieop/wizard/export_clieop.py 2013-06-04 14:51:27 +0000
@@ -2,6 +2,7 @@
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
+# 2011 - 2013 Therp BV (<http://therp.nl>).
# All Rights Reserved
#
# This program is free software: you can redistribute it and/or modify
@@ -21,21 +22,22 @@
import base64
from datetime import datetime, date, timedelta
-from osv import osv, fields
-from tools.translate import _
-import netsvc
-from account_banking import sepa
-import clieop
-
-def strpdate(arg, format='%Y-%m-%d'):
- '''shortcut'''
- return datetime.strptime(arg, format).date()
-
-def strfdate(arg, format='%Y-%m-%d'):
- '''shortcut'''
- return arg.strftime(format)
-
-class banking_export_clieop_wizard(osv.osv_memory):
+from openerp.osv import orm, fields
+from openerp.tools.translate import _
+from openerp import netsvc
+from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+from openerp.addons.account_banking import sepa
+from openerp.addons.account_banking_nl_clieop.wizard import clieop
+
+def strpdate(arg):
+ '''shortcut'''
+ return datetime.strptime(arg, DEFAULT_SERVER_DATE_FORMAT).date()
+
+def strfdate(arg):
+ '''shortcut'''
+ return arg.strftime(DEFAULT_SERVER_DATE_FORMAT)
+
+class banking_export_clieop_wizard(orm.TransientModel):
_name = 'banking.export.clieop.wizard'
_description = 'Client Opdrachten Export'
_columns = {
@@ -151,21 +153,17 @@
),
}
- _defaults = {
- 'test': True,
- }
-
- def create(self, cursor, uid, vals, context=None):
+ def create(self, cr, 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)
+ self.check_orders(cr, uid, vals, context)
return super(banking_export_clieop_wizard, self).create(
- cursor, uid, vals, context)
+ cr, uid, vals, context)
- def check_orders(self, cursor, uid, vals, context):
+ def check_orders(self, cr, uid, vals, context):
'''
Check payment type for all orders.
@@ -177,14 +175,14 @@
Also mind that rates for batches are way higher than those for
transactions. It pays to limit the number of batches.
'''
- today = date.today()
+ today = strpdate(fields.date.context_today(self, cr, uid, context=context))
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, payment_order_ids)
+ payment_orders = payment_order_obj.browse(cr, uid, payment_order_ids)
for payment_order in payment_orders:
payment_type = payment_order.mode.type.code
@@ -194,8 +192,8 @@
runs[payment_type] = [payment_order]
if payment_order.date_prefered == 'fixed':
- if payment_order.date_planned:
- execution_date = strpdate(payment_order.date_planned)
+ if payment_order.date_scheduled:
+ execution_date = strpdate(payment_order.date_scheduled)
else:
execution_date = today
elif payment_order.date_prefered == 'now':
@@ -212,12 +210,12 @@
else:
execution_date = today
if execution_date and execution_date >= max_date:
- raise osv.except_osv(
+ raise orm.except_orm(
_('Error'),
_('You can\'t create ClieOp orders more than 30 days in advance.')
)
if len(runs) != 1:
- raise osv.except_osv(
+ raise orm.except_orm(
_('Error'),
_('You can only combine payment orders of the same type')
)
@@ -231,12 +229,12 @@
'state': 'create',
})
- def create_clieop(self, cursor, uid, ids, context):
+ def create_clieop(self, cr, uid, ids, context):
'''
Wizard to actually create the ClieOp3 file
'''
payment_order_obj = self.pool.get('payment.order')
- clieop_export = self.browse(cursor, uid, ids, context)[0]
+ clieop_export = self.browse(cr, uid, ids, context)[0]
clieopfile = None
for payment_order in clieop_export.payment_order_ids:
if not clieopfile:
@@ -253,7 +251,7 @@
else:
our_account_nr = payment_order.mode.bank_id.acc_number
if not our_account_nr:
- raise osv.except_osv(
+ raise orm.except_orm(
_('Error'),
_('Your bank account has to have a valid account number')
)
@@ -267,7 +265,7 @@
accountno_sender = our_account_nr,
seqno = self.pool.get(
'banking.export.clieop').get_daynr(
- cursor, uid, context=context),
+ cr, uid, context=context),
test = clieop_export['test']
)
@@ -291,7 +289,7 @@
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 osv.except_osv(
+ raise orm.except_orm(
_('Error'),
_('There is insufficient information.\r\n'
'Both destination address and account '
@@ -314,7 +312,7 @@
# Is this an IBAN account?
if iban.valid:
if iban.countrycode != 'NL':
- raise osv.except_osv(
+ raise orm.except_orm(
_('Error'),
_('You cannot send international bank transfers '
'through ClieOp3!')
@@ -331,7 +329,7 @@
# Generate the specifics of this clieopfile
order = clieopfile.order
file_id = self.pool.get('banking.export.clieop').create(
- cursor, uid, dict(
+ cr, uid, dict(
filetype = order.name_transactioncode,
identification = order.identification,
prefered_date = strfdate(order.preferred_execution_date),
@@ -346,7 +344,7 @@
[6, 0, [x.id for x in clieop_export['payment_order_ids']]]
],
), context)
- self.write(cursor, uid, [ids[0]], dict(
+ self.write(cr, uid, [ids[0]], dict(
filetype = order.name_transactioncode,
testcode = order.testcode,
file_id = file_id,
@@ -364,31 +362,27 @@
'res_id': ids[0] or False,
}
- def cancel_clieop(self, cursor, uid, ids, context):
+ def cancel_clieop(self, cr, uid, ids, context):
'''
Cancel the ClieOp: just drop the file
'''
- clieop_export = self.read(cursor, uid, ids, ['file_id'], context)[0]
- self.pool.get('banking.export.clieop').unlink(cursor, uid, clieop_export['file_id'][0])
+ clieop_export = self.read(cr, uid, ids, ['file_id'], context)[0]
+ self.pool.get('banking.export.clieop').unlink(cr, uid, clieop_export['file_id'][0])
return {'type': 'ir.actions.act_window_close'}
- def save_clieop(self, cursor, uid, ids, context):
+ def save_clieop(self, cr, uid, ids, context):
'''
Save the ClieOp: mark all payments in the file as 'sent', if not a test
'''
clieop_export = self.browse(
- cursor, uid, ids, context)[0]
+ cr, 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, clieop_export['file_id'].id, {'state': 'sent'}
+ cr, uid, clieop_export['file_id'].id, {'state': 'sent'}
)
wf_service = netsvc.LocalService('workflow')
for order in clieop_export['payment_order_ids']:
- wf_service.trg_validate(uid, 'payment.order', order.id, 'sent', cursor)
+ wf_service.trg_validate(uid, 'payment.order', order.id, 'sent', cr)
return {'type': 'ir.actions.act_window_close'}
-
-banking_export_clieop_wizard()
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
=== modified file 'account_banking_nl_clieop/wizard/export_clieop_view.xml'
--- account_banking_nl_clieop/wizard/export_clieop_view.xml 2013-01-02 15:14:53 +0000
+++ account_banking_nl_clieop/wizard/export_clieop_view.xml 2013-06-04 14:51:27 +0000
@@ -4,7 +4,6 @@
<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"/>
=== modified file 'account_banking_nl_girotel/girotel.py'
--- account_banking_nl_girotel/girotel.py 2013-04-15 13:59:50 +0000
+++ account_banking_nl_girotel/girotel.py 2013-06-04 14:51:27 +0000
@@ -45,6 +45,7 @@
from account_banking.parsers import models
from account_banking.parsers.convert import str2date, to_swift
from tools.translate import _
+import re
import csv
bt = models.mem_bank_transaction
@@ -105,6 +106,13 @@
self.date = str2date(self.date, '%Y%m%d')
if self.direction == 'A':
self.transferred_amount = -float(self.transferred_amount)
+ if (self.transfer_type == 'VZ'
+ and (not self.remote_account or self.remote_account == '0')
+ and (not self.message or re.match('^\s*$', self.message))
+ and self.remote_owner.startswith('TOTAAL ')):
+ self.transfer_type = 'PB'
+ self.message = self.remote_owner
+ self.remove_owner = False
else:
self.transferred_amount = float(self.transferred_amount)
self.local_account = self.local_account.zfill(10)
@@ -140,7 +148,8 @@
'GT': bt.ORDER,
'IC': bt.DIRECT_DEBIT,
'OV': bt.ORDER,
- 'VZ': bt.PAYMENT_BATCH,
+ 'VZ': bt.ORDER,
+ 'PB': bt.PAYMENT_BATCH,
}
def __init__(self, line, *args, **kwargs):
@@ -171,11 +180,14 @@
4. Cash withdrawals from banks are too not seen as a transfer between
two accounts - the cash exits the banking system. These withdrawals
have their transfer_type set to 'GM'.
+ 5. Aggregated payment batches. These transactions have transfer type
+ 'VZ' natively but are changed to 'PB' while parsing. These transactions
+ have no remote account.
'''
return bool(self.transferred_amount and self.execution_date and (
self.remote_account or
self.transfer_type in [
- 'DV', 'BT', 'BA', 'GM',
+ 'DV', 'PB', 'BT', 'BA', 'GM',
]))
def refold_message(self, message):
=== modified file 'account_banking_payment/__openerp__.py'
--- account_banking_payment/__openerp__.py 2013-03-17 12:53:37 +0000
+++ account_banking_payment/__openerp__.py 2013-06-04 14:51:27 +0000
@@ -37,6 +37,7 @@
'data': [
'view/account_payment.xml',
'view/banking_transaction_wizard.xml',
+ 'view/payment_mode.xml',
'view/payment_mode_type.xml',
'view/bank_payment_manual.xml',
'data/payment_mode_type.xml',
@@ -53,5 +54,5 @@
account_banking_nl_clieop
''',
'auto_install': True,
- 'installable': False,
+ 'installable': True,
}
=== modified file 'account_banking_payment/model/__init__.py'
--- account_banking_payment/model/__init__.py 2013-03-16 19:00:59 +0000
+++ account_banking_payment/model/__init__.py 2013-06-04 14:51:27 +0000
@@ -4,7 +4,6 @@
import payment_mode_type
import payment_order_create
import banking_import_transaction
-import account_bank_statement_line
import banking_transaction_wizard
import bank_payment_manual
import banking_import_line
=== removed file 'account_banking_payment/model/account_bank_statement_line.py'
--- account_banking_payment/model/account_bank_statement_line.py 2013-03-17 09:10:15 +0000
+++ account_banking_payment/model/account_bank_statement_line.py 1970-01-01 00:00:00 +0000
@@ -1,40 +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
-
-
-class account_bank_statement_line(orm.Model):
- _inherit = 'account.bank.statement.line'
- _columns = {
- 'match_type': fields.related(
- # Add payment and storno types
- 'import_transaction_id', 'match_type', type='selection',
- selection=[('manual', 'Manual'), ('move','Move'),
- ('invoice', 'Invoice'), ('payment', 'Payment'),
- ('payment_order', 'Payment order'),
- ('storno', 'Storno')],
- string='Match type', readonly=True,),
- }
=== modified file 'account_banking_payment/model/account_payment.py'
--- account_banking_payment/model/account_payment.py 2013-04-06 05:02:18 +0000
+++ account_banking_payment/model/account_payment.py 2013-06-04 14:51:27 +0000
@@ -73,6 +73,8 @@
'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)]
@@ -172,32 +174,10 @@
])
payment_line_obj.write(cr, uid, line_ids, kwargs)
- def set_to_draft(self, cr, uid, ids, *args):
- '''
- Set both self and payment lines to state 'draft'.
- '''
- self._write_payment_lines(cr, uid, ids, export_state='draft')
- return super(payment_order, self).set_to_draft(
- cr, uid, ids, *args
- )
-
- def action_sent(self, cr, uid, ids, context=None):
- '''
- Set both self and payment lines to state 'sent'.
- '''
- self._write_payment_lines(cr, uid, ids, export_state='sent')
- self.write(cr, uid, ids, {
- 'state': 'sent',
- 'date_sent': fields.date.context_today(
- self, cr, uid, context=context),
- }, context=context)
- return True
-
def action_rejected(self, cr, uid, ids, *args):
'''
Set both self and payment lines to state 'rejected'.
'''
- self._write_payment_lines(cr, uid, ids, export_state='rejected')
wf_service = netsvc.LocalService('workflow')
for id in ids:
wf_service.trg_validate(uid, 'payment.order', id, 'rejected', cr)
@@ -209,32 +189,168 @@
'''
self._write_payment_lines(
cr, uid, ids,
- export_state='done',
date_done=fields.date.context_today(self, cr, uid))
return super(payment_order, self).set_done(
cr, uid, ids, *args
)
- """
- Hooks for processing direct debit orders, such as implemented in
- account_direct_debit module.
- """
- def debit_reconcile_transfer(
- self, cr, uid, payment_order_id, amount, currency, context=None):
- """
- Reconcile the payment order if the amount is correct. Return the
- id of the reconciliation.
- """
- raise orm.except_orm(
- _("Cannot reconcile"),
- _("Cannot reconcile debit order: "+
- "Not implemented."))
-
- def debit_unreconcile_transfer(
- self, cr, uid, payment_order_id, reconcile_id, amount, currency,
- context=None):
- """ Unreconcile the payment_order if at all possible """
- raise orm.except_orm(
- _("Cannot unreconcile"),
- _("Cannot unreconcile debit order: "+
- "Not implemented."))
+ 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 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')
+ for order in self.browse(cr, uid, ids, context=context):
+ for line in order.line_ids:
+ # basic checks
+ if not line.move_line_id:
+ raise orm.except_orm(
+ _('Error'),
+ _('No move line provided for line %s') % line.name)
+ if 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, {
+ 'journal_id': order.mode.transfer_journal_id.id,
+ 'name': '%s order %s' % (order.payment_order_type,
+ line.move_line_id.move_id.name),
+ 'reference': '%s%s' % (order.payment_order_type[:3].upper(),
+ line.move_line_id.move_id.name),
+ }, context=context)
+
+ # TODO: take multicurrency into account
+
+ # create the debit move line on the transfer account
+ vals = {
+ 'name': '%s order for %s' % (
+ order.payment_order_type,
+ line.move_line_id.invoice and
+ line.move_line_id.invoice.number or
+ line.move_line_id.name),
+ 'move_id': move_id,
+ 'partner_id': line.partner_id.id,
+ '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),
+ }
+ transfer_move_line_id = account_move_line_obj.create(
+ cr, uid, vals, context=context)
+
+ # create the debit move line on the receivable account
+ vals.update({
+ 'account_id': line.move_line_id.account_id.id,
+ 'credit': (order.payment_order_type == 'debit'
+ and line.amount or 0.0),
+ 'debit': (order.payment_order_type == 'payment'
+ and line.amount or 0.0),
+ })
+ reconcile_move_line_id = account_move_line_obj.create(
+ cr, uid, 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)
+
+ payment_line_obj.debit_reconcile(
+ cr, uid, line.id, 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
+
+
=== modified file 'account_banking_payment/model/banking_import_transaction.py'
--- account_banking_payment/model/banking_import_transaction.py 2013-06-03 09:47:11 +0000
+++ account_banking_payment/model/banking_import_transaction.py 2013-06-04 14:51:27 +0000
@@ -27,37 +27,44 @@
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_debit_order(
- self, cr, uid, trans, log, context=None):
+ def _match_payment_order(
+ self, cr, uid, trans, log, order_type='payment', context=None):
- def is_zero(total):
+ 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_id.currency, total)
+ 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', '=', 'debit'),
+ 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
- is_zero(x.total - trans.transferred_amount) and
- x.line_ids and x.line_ids[0].debit_move_line_id]
+ equals_order_amount(x, trans.statement_line_id.amount)]
if len(candidates) > 0:
# retrieve the common account_id, if any
account_id = False
- for line in candidates[0].line_ids[0].debit_move_line_id.move_id.line_id:
- if line.account_id.type == 'other':
- account_id = line.account_id.id
- break
+ if (candidates[0].line_ids[0].transit_move_line_id):
+ for line in candidates[0].line_ids[0].transit_move_line_id.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',
@@ -82,7 +89,7 @@
# 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.transferred_amount,
+ cr, uid, line_ids[0], trans.statement_line_id.amount,
trans.statement_id.currency, context=None)
if account_id:
return dict(
@@ -114,7 +121,7 @@
x for x in payment_lines
if x.communication == trans.reference
and round(x.amount, digits) == -round(
- trans.transferred_amount, digits)
+ trans.statement_line_id.amount, digits)
and trans.remote_account in (x.bank_id.acc_number,
x.bank_id.acc_number_domestic)
]
@@ -171,10 +178,6 @@
raise orm.except_orm(
_("Cannot reconcile"),
_("Cannot reconcile: no direct debit order"))
- if transaction.payment_order_id.payment_order_type != 'debit':
- raise orm.except_orm(
- _("Cannot reconcile"),
- _("Reconcile payment order not implemented"))
reconcile_id = payment_order_obj.debit_reconcile_transfer(
cr, uid,
transaction.payment_order_id.id,
@@ -195,7 +198,6 @@
payment_line_obj = self.pool.get('payment.line')
payment_line_obj.write(
cr, uid, transaction.payment_line_id.id, {
- 'export_state': 'done',
'date_done': transaction.statement_line_id.date,
}
)
@@ -232,11 +234,12 @@
if not transaction.payment_order_id:
raise orm.except_orm(
_("Cannot unreconcile"),
- _("Cannot unreconcile: no direct debit order"))
- if transaction.payment_order_id.payment_order_type != 'debit':
+ _("Cannot unreconcile: no payment or direct debit order"))
+ if not transaction.statement_line_id.reconcile_id:
raise orm.except_orm(
_("Cannot unreconcile"),
- _("Unreconcile payment order not implemented"))
+ _("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,
@@ -302,17 +305,6 @@
cr, uid, transaction.payment_line_id.id, context)
_columns = {
- 'match_type': fields.selection(
- # Add payment and storno types
- [
- ('manual', 'Manual'),
- ('move','Move'),
- ('invoice', 'Invoice'),
- ('payment', 'Payment'),
- ('payment_order', 'Payment order'),
- ('storno', 'Storno'),
- ],
- 'Match type'),
'payment_order_ids': fields.many2many(
'payment.order', 'banking_transaction_payment_order_rel',
'order_id', 'transaction_id', 'Payment orders'),
@@ -334,14 +326,14 @@
return res
def clear_and_write(self, cr, uid, ids, vals=None, context=None):
- super(banking_import_transaction, self).clear_and_write(
+ 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)
- return self.write(
- cr, uid, ids, {
- 'payment_line_id': False,
- 'payment_order_ids': [(6, 0, [])],
- },
- context=context)
def move_info2values(self, move_info):
vals = super(banking_import_transaction, self).move_info2values(
@@ -356,11 +348,25 @@
)
return vals
- def match(self, cr, uid, ids, results=None, context=None):
- res = super(banking_import_transaction, self).match(
- cr, uid, ids, results=results, context=context)
-
- return res
+ 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):
"""
@@ -369,14 +375,18 @@
"""
super(banking_import_transaction, self).__init__(pool, cr)
- banking_import_transaction.confirm_map.update({
+ 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,
})
- banking_import_transaction.cancel_map.update({
+ 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,
})
=== modified file 'account_banking_payment/model/banking_transaction_wizard.py'
--- account_banking_payment/model/banking_transaction_wizard.py 2013-03-17 09:10:15 +0000
+++ account_banking_payment/model/banking_transaction_wizard.py 2013-06-04 14:51:27 +0000
@@ -24,10 +24,59 @@
##############################################################################
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',
@@ -42,4 +91,13 @@
'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),
+ ]),
}
=== modified file 'account_banking_payment/model/payment_line.py'
--- account_banking_payment/model/payment_line.py 2013-03-17 09:10:15 +0000
+++ account_banking_payment/model/payment_line.py 2013-06-04 14:51:27 +0000
@@ -24,11 +24,12 @@
##############################################################################
from openerp.osv import orm, fields
-
+from openerp import netsvc
+from openerp.tools.translate import _
class payment_line(orm.Model):
'''
- Add extra export_state and date_done fields; make destination bank account
+ Add some fields; make destination bank account
mandatory, as it makes no sense to send payments into thin air.
Edit: Payments can be by cash too, which is prohibited by mandatory bank
accounts.
@@ -36,146 +37,34 @@
_inherit = 'payment.line'
_columns = {
# New fields
- 'export_state': fields.selection([
- ('draft', 'Draft'),
- ('open','Confirmed'),
- ('cancel','Cancelled'),
- ('sent', 'Sent'),
- ('rejected', 'Rejected'),
- ('done','Done'),
- ], 'State', select=True
- ),
'msg': fields.char('Message', size=255, required=False, readonly=True),
-
- # Redefined fields: added states
- 'date_done': fields.datetime('Date Confirmed', select=True,
- readonly=True),
- 'name': fields.char(
- 'Your Reference', size=64, required=True,
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
+ 'date_done': fields.date(
+ 'Date Confirmed', select=True, readonly=True),
+ # Communication: required is dependend on the mode
'communication': fields.char(
'Communication', size=64, required=False,
help=("Used as the message between ordering customer and current "
"company. Depicts 'What do you want to say to the recipient"
" about this order ?'"
),
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
),
+ # Communication2: enlarge to 128
'communication2': fields.char(
'Communication 2', size=128,
help='The successor message of Communication.',
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'move_line_id': fields.many2one(
- 'account.move.line', 'Entry line',
- domain=[('reconcile_id','=', False),
- ('account_id.type', '=','payable')
- ],
- help=('This Entry Line will be referred for the information of '
- 'the ordering customer.'
- ),
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'amount_currency': fields.float(
- 'Amount in Partner Currency', digits=(16,2),
- required=True,
- help='Payment amount in the partner currency',
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'currency': fields.many2one(
- 'res.currency', 'Partner Currency', required=True,
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'bank_id': fields.many2one(
- 'res.partner.bank', 'Destination Bank account',
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'order_id': fields.many2one(
- 'payment.order', 'Order', required=True,
- ondelete='cascade', select=True,
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'partner_id': fields.many2one(
- 'res.partner', string="Partner", required=True,
- help='The Ordering Customer',
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'date': fields.date(
- 'Payment Date',
- help=("If no payment date is specified, the bank will treat this "
- "payment line directly"
- ),
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('readonly', True)]
- },
- ),
- 'state': fields.selection([
- ('normal','Free'),
- ('structured','Structured')
- ], 'Communication Type', required=True,
- states={
- 'sent': [('readonly', True)],
- 'rejected': [('readonly', True)],
- 'done': [('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',
+ readonly=True,
+ help="Move line through which the debit order pays the invoice",
+ ),
+ }
+
_defaults = {
- 'export_state': 'draft',
- 'date_done': False,
'msg': '',
- }
-
- def fields_get(self, cr, uid, fields=None, context=None):
- res = super(payment_line, self).fields_get(cr, uid, fields, context)
- if 'communication' in res:
- res['communication'].setdefault('states', {})
- res['communication']['states']['structured'] = [('required', True)]
- if 'communication2' in res:
- res['communication2'].setdefault('states', {})
- res['communication2']['states']['structured'] = [('readonly', True)]
- res['communication2']['states']['normal'] = [('readonly', False)]
-
- return res
+ }
"""
Hooks for processing direct debit orders, such as implemented in
@@ -216,3 +105,76 @@
"""
return False
+
+ def debit_reconcile(self, cr, uid, payment_line_id, context=None):
+ """
+ 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
+ account though.
+
+ :param payment_line_id: the single id of the canceled payment line
+ """
+
+ if isinstance(payment_line_id, (list, tuple)):
+ payment_line_id = payment_line_id[0]
+ reconcile_obj = self.pool.get('account.move.reconcile')
+ move_line_obj = self.pool.get('account.move.line')
+ payment_line = self.browse(cr, uid, payment_line_id, context=context)
+
+ transit_move_line = payment_line.transit_move_line_id
+ torec_move_line = payment_line.move_line_id
+
+ if (not transit_move_line or not torec_move_line):
+ raise orm.except_orm(
+ _('Can not reconcile'),
+ _('No move line for line %s') % payment_line.name)
+ if torec_move_line.reconcile_id: # torec_move_line.reconcile_partial_id:
+ raise orm.except_orm(
+ _('Error'),
+ _('Move line %s has already been reconciled') %
+ torec_move_line.name
+ )
+ if transit_move_line.reconcile_id or transit_move_line.reconcile_partial_id:
+ raise orm.except_orm(
+ _('Error'),
+ _('Move line %s has already been reconciled') %
+ transit_move_line.name
+ )
+
+ def is_zero(total):
+ return self.pool.get('res.currency').is_zero(
+ cr, uid, transit_move_line.company_id.currency_id, total)
+
+ line_ids = [transit_move_line.id, torec_move_line.id]
+ if torec_move_line.reconcile_partial_id:
+ line_ids = [
+ x.id for x in
+ transit_move_line.reconcile_partial_id.line_partial_ids
+ ] + [torec_move_line.id]
+
+ total = move_line_obj.get_balance(cr, uid, line_ids)
+ vals = {
+ 'type': 'auto',
+ 'line_id': is_zero(total) and [(6, 0, line_ids)] or [(6, 0, [])],
+ 'line_partial_ids': is_zero(total) and [(6, 0, [])] or [(6, 0, line_ids)],
+ }
+
+ if torec_move_line.reconcile_partial_id:
+ reconcile_obj.write(
+ cr, uid, transit_move_line.reconcile_partial_id.id,
+ vals, context=context)
+ else:
+ reconcile_obj.create(
+ cr, uid, vals, context=context)
+ for line_id in line_ids:
+ netsvc.LocalService("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(
+ uid, 'account.invoice', torec_move_line.invoice.id,
+ 'undo_debit_denied', cr)
=== modified file 'account_banking_payment/model/payment_mode.py'
--- account_banking_payment/model/payment_mode.py 2013-03-17 09:10:15 +0000
+++ account_banking_payment/model/payment_mode.py 2013-06-04 14:51:27 +0000
@@ -46,6 +46,27 @@
_columns = {
'type': fields.many2one(
'payment.mode.type', 'Payment type',
+ required=True,
help='Select the Payment Type for the Payment Mode.'
),
+ '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. For debit type modes only. '
+ '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',
+ help=('Limit selected invoices to invoices with these payment '
+ 'terms')
+ ),
}
=== modified file 'account_banking_payment/model/payment_order_create.py'
--- account_banking_payment/model/payment_order_create.py 2013-05-24 09:00:42 +0000
+++ account_banking_payment/model/payment_order_create.py 2013-06-04 14:51:27 +0000
@@ -26,11 +26,77 @@
from datetime import datetime
from openerp.osv import orm, fields
from openerp.tools import DEFAULT_SERVER_DATE_FORMAT
+from openerp.tools.translate import _
class payment_order_create(orm.TransientModel):
_inherit = 'payment.order.create'
+ def extend_payment_order_domain(
+ self, cr, uid, payment_order, domain, context=None):
+ if payment_order.payment_order_type == 'payment':
+ domain += [
+ ('account_id.type', '=', 'payable'),
+ ('amount_to_pay', '>', 0)
+ ]
+ return True
+
+ def search_entries(self, cr, uid, ids, context=None):
+ """
+ This method taken from account_payment module.
+ We adapt the domain based on the payment_order_type
+ """
+ line_obj = self.pool.get('account.move.line')
+ mod_obj = self.pool.get('ir.model.data')
+ if context is None:
+ context = {}
+ data = self.read(cr, uid, ids, ['duedate'], context=context)[0]
+ search_due_date = data['duedate']
+
+ ### start account_banking_payment ###
+ payment = self.pool.get('payment.order').browse(
+ cr, uid, context['active_id'], context=context)
+ # Search for move line to pay:
+ domain = [
+ ('move_id.state', '=', 'posted'),
+ ('reconcile_id', '=', False),
+ ('company_id', '=', payment.mode.company_id.id),
+ ]
+ # apply payment term filter
+ if payment.mode.payment_term_ids:
+ domain += [
+ ('invoice.payment_term', 'in',
+ [term.id for term in payment.mode.payment_term_ids]
+ )
+ ]
+ self.extend_payment_order_domain(
+ cr, uid, payment, domain, context=context)
+ ### end account_direct_debit ###
+
+ domain = domain + [
+ '|', ('date_maturity', '<=', search_due_date),
+ ('date_maturity', '=', False)
+ ]
+ line_ids = line_obj.search(cr, uid, domain, context=context)
+ 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': _('Entry Lines'),
+ 'context': context,
+ 'view_type': 'form',
+ 'view_mode': 'form',
+ 'res_model': 'payment.order.create',
+ 'views': [(resource_id, 'form')],
+ 'type': 'ir.actions.act_window',
+ 'target': 'new',
+ }
+
def create_payment(self, cr, uid, ids, context=None):
'''
This method is a slightly modified version of the existing method on this
@@ -51,7 +117,8 @@
if not line_ids:
return {'type': 'ir.actions.act_window_close'}
- payment = order_obj.browse(cr, uid, context['active_id'], context=context)
+ 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)
@@ -75,10 +142,10 @@
### end account banking
elif payment.date_prefered == 'fixed':
### account_banking
- # date_to_pay = payment.date_planned
+ # date_to_pay = payment.date_scheduled
date_to_pay = (
- payment.date_planned
- if payment.date_planned and payment.date_planned > _today
+ payment.date_scheduled
+ if payment.date_scheduled and payment.date_scheduled > _today
else False)
### end account banking
@@ -121,6 +188,8 @@
'state': state,
### end account banking
'date': date_to_pay,
- 'currency': line.invoice and line.invoice.currency_id.id or line.journal_id.currency.id or line.journal_id.company_id.currency_id.id,
+ 'currency': (line.invoice and line.invoice.currency_id.id
+ or line.journal_id.currency.id
+ or line.journal_id.company_id.currency_id.id),
}, context=context)
return {'type': 'ir.actions.act_window_close'}
=== modified file 'account_banking_payment/view/account_payment.xml'
--- account_banking_payment/view/account_payment.xml 2013-05-01 14:40:54 +0000
+++ account_banking_payment/view/account_payment.xml 2013-06-04 14:51:27 +0000
@@ -9,33 +9,28 @@
<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="type">form</field>
<field name="arch" type="xml">
<data>
- <xpath expr="/form/group/button[@string='Select Invoices to Pay']"
- position="attributes">
- <attribute name="attrs">{'invisible':[('state','!=','draft')]}</attribute>
- </xpath>
- <xpath expr="/form/group/button[@string='Make Payments']"
- position="replace">
- <button name="launch_wizard" states="open" string="Make Payments" type="object" icon="gtk-execute"/>
- <newline/>
+ <xpath expr="//button[@string='Select Invoices to Pay']"
+ position="attributes">
+ <attribute name="attrs">{
+ 'invisible':[('state','!=','draft')]
+ }</attribute>
+ </xpath>
+ <xpath expr="//button[@string='Make Payments']"
+ position="attributes">
+ <attribute name="name">launch_wizard</attribute>
+ </xpath>
+ <!-- Communication only used for 'structured' communication -->
+ <xpath expr="//field[@name='line_ids']/form//field[@name='communication']"
+ position="attributes">
+ <attribute name="attrs">{
+ 'readonly': [('state', '=', 'normal')]
+ }</attribute>
</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>
-
</data>
</openerp>
=== modified file 'account_banking_payment/view/bank_payment_manual.xml'
--- account_banking_payment/view/bank_payment_manual.xml 2013-03-17 20:04:17 +0000
+++ account_banking_payment/view/bank_payment_manual.xml 2013-06-04 14:51:27 +0000
@@ -4,9 +4,8 @@
<record id="view_payment_manual_form" model="ir.ui.view">
<field name="name">Form for manual payment wizard</field>
<field name="model">payment.manual</field>
- <field name="type">form</field>
<field name="arch" type="xml">
- <form>
+ <form string="Manual payment">
<label string="Payment order(s) have been set to 'sent'"/>
<button special="cancel" icon="gtk-ok" string="OK"/>
</form>
=== modified file 'account_banking_payment/view/banking_transaction_wizard.xml'
--- account_banking_payment/view/banking_transaction_wizard.xml 2013-03-17 20:04:17 +0000
+++ account_banking_payment/view/banking_transaction_wizard.xml 2013-06-04 14:51:27 +0000
@@ -6,7 +6,6 @@
<field name="model">banking.transaction.wizard</field>
<field name="inherit_id"
ref="account_banking.transaction_wizard_first" />
- <field name="type">form</field>
<field name="arch" type="xml">
<field name="invoice_ids" position="before">
<field name="payment_order_ids" invisible="True"/>
@@ -14,20 +13,23 @@
<xpath expr="//group/separator[@string='Multiple matches']/.."
position="after">
<field name='payment_line_id'
- attrs="{'invisible': [
- ('match_type', '!=', 'storno'),
- ('match_type', '!=', 'payment')]
- }" />
+ attrs="{'invisible': [('match_type', 'not in',
+ ('storno', 'payment', 'payment_manual'))]}"
+ />
</xpath>
<field name="move_line_id" position="after">
<field name='payment_order_id'
- attrs="{'readonly': [
- ('match_multi', '=', False)],
- 'invisible': [
- ('match_type', '!=', 'payment_order')]}"
+ attrs="{'readonly': [('match_multi', '=', False)],
+ 'invisible': [('match_type', 'not in',
+ ('payment_order', 'payment_order_manual'))]
+ }"
domain="[('id', 'in', payment_order_ids[0][2])]"
/>
</field>
+ <field name="manual_move_line_id" position="after">
+ <field name="manual_payment_line_id"/>
+ <field name="manual_payment_order_id"/>
+ </field>
</field>
</record>
</data>
=== added file 'account_banking_payment/view/payment_mode.xml'
--- account_banking_payment/view/payment_mode.xml 1970-01-01 00:00:00 +0000
+++ account_banking_payment/view/payment_mode.xml 2013-06-04 14:51:27 +0000
@@ -0,0 +1,43 @@
+<?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_payment.view_payment_mode_form"/>
+ <field name="arch" type="xml">
+ <field name="company_id" position="after">
+ <field name="type"/>
+ <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>
=== modified file 'account_banking_payment/view/payment_mode_type.xml'
--- account_banking_payment/view/payment_mode_type.xml 2013-03-16 16:44:19 +0000
+++ account_banking_payment/view/payment_mode_type.xml 2013-06-04 14:51:27 +0000
@@ -2,24 +2,10 @@
<openerp>
<data>
- <!-- Add the payment mode type to the payment mode views -->
- <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_payment.view_payment_mode_form"/>
- <field name="type">form</field>
- <field name="arch" type="xml">
- <field name="company_id" position="after">
- <field name="type"/>
- </field>
- </field>
- </record>
-
<record id="view_payment_mode_tree_inherit" model="ir.ui.view">
<field name="name">payment.mode.tree.inherit</field>
<field name="model">payment.mode</field>
<field name="inherit_id" ref="account_payment.view_payment_mode_tree"/>
- <field name="type">tree</field>
<field name="arch" type="xml">
<field name="company_id" position="after">
<field name="type"/>
@@ -31,9 +17,8 @@
<record model="ir.ui.view" id="view_payment_mode_type_form">
<field name="name">view.payment.mode.type.form</field>
<field name="model">payment.mode.type</field>
- <field name="type">form</field>
<field name="arch" type="xml">
- <form>
+ <form string="Payment mode">
<field name="name" />
<field name="code" />
<field name="suitable_bank_types"/>
=== modified file 'account_banking_payment/workflow/account_payment.xml'
--- account_banking_payment/workflow/account_payment.xml 2013-03-16 16:44:19 +0000
+++ account_banking_payment/workflow/account_payment.xml 2013-06-04 14:51:27 +0000
@@ -8,6 +8,13 @@
<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">
+ <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">
<field name="name">rejected</field>
@@ -16,23 +23,47 @@
write({'state':'rejected'})</field>
<field name="kind">function</field>
</record>
- <!-- Add new transition sent -> done -->
+ <!-- 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="signal">sent</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>
+ <!-- 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"/>
+ <field name="act_from" ref="account_banking.act_sent_wait"/>
<field name="act_to" ref="account_payment.act_done"/>
<field name="signal">done</field>
</record>
- <!-- Add new transition sent -> rejected -->
+ <!-- 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"/>
<field name="signal">rejected</field>
</record>
- <!-- Rewrite existing open -> done transition to include 'sent' -->
- <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="signal">sent</field>
- </record>
+ <!--
+ 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.
+ -->
+ <record id="account_payment.act_done" model="workflow.activity">
+ <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="condition">test_undo_done()</field>
+ <field name="signal">undo_done</field>
+ </record>
+
</data>
</openerp>
=== modified file 'account_direct_debit/__openerp__.py'
--- account_direct_debit/__openerp__.py 2013-04-15 14:14:27 +0000
+++ account_direct_debit/__openerp__.py 2013-06-04 14:51:27 +0000
@@ -1,6 +1,6 @@
##############################################################################
#
-# Copyright (C) 2011 Therp BV (<http://therp.nl>).
+# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
# Copyright (C) 2011 Smile (<http://smile.fr>).
# All Rights Reserved
#
@@ -20,9 +20,9 @@
##############################################################################
{
'name': 'Direct Debit',
- 'version': '6.1.1.134',
+ 'version': '7.0.2.134',
'license': 'AGPL-3',
- 'author': 'Therp BV / Smile',
+ 'author': ['Therp BV', 'Smile'],
'website': 'https://launchpad.net/banking-addons',
'category': 'Banking addons',
'depends': ['account_banking'],
@@ -30,7 +30,6 @@
'view/account_payment.xml',
'view/account_invoice.xml',
'workflow/account_invoice.xml',
- 'workflow/account_payment.xml',
'data/account_payment_term.xml',
],
'description': '''
@@ -49,5 +48,5 @@
banking institutions. The banking addons are a continuation of Account Banking
Framework by Edusense BV. See https://launchpad.net/banking-addons.
''',
- 'installable': False,
+ 'installable': True,
}
=== modified file 'account_direct_debit/i18n/nl.po'
--- account_direct_debit/i18n/nl.po 2012-08-08 10:32:52 +0000
+++ account_direct_debit/i18n/nl.po 2013-06-04 14:51:27 +0000
@@ -175,7 +175,7 @@
msgstr "De betaalregelnaam moet uniek zijn!"
#. module: account_direct_debit
-#: field:payment.line,debit_move_line_id:0
+#: field:payment.line,transit_move_line_id:0
msgid "Debit move line"
msgstr "Debetboeking"
@@ -200,7 +200,7 @@
msgstr "Factuur"
#. module: account_direct_debit
-#: help:payment.line,debit_move_line_id:0
+#: help:payment.line,transit_move_line_id:0
msgid "Move line through which the debit order pays the invoice"
msgstr "Dagboekregel waarmee de incasso-opdracht de factuur voldoet"
=== added directory 'account_direct_debit/migrations'
=== added directory 'account_direct_debit/migrations/7.0.2'
=== added file 'account_direct_debit/migrations/7.0.2/pre-migration.py'
--- account_direct_debit/migrations/7.0.2/pre-migration.py 1970-01-01 00:00:00 +0000
+++ account_direct_debit/migrations/7.0.2/pre-migration.py 2013-06-04 14:51:27 +0000
@@ -0,0 +1,57 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 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/>.
+#
+##############################################################################
+
+def rename_columns(cr, column_spec):
+ """
+ Rename table columns. Taken from OpenUpgrade.
+
+ :param column_spec: a hash with table keys, with lists of tuples as values. \
+ Tuples consist of (old_name, new_name).
+
+ """
+ for table in column_spec.keys():
+ for (old, new) in column_spec[table]:
+ logger.info("table %s, column %s: renaming to %s",
+ table, old, new)
+ cr.execute('ALTER TABLE "%s" RENAME "%s" TO "%s"' % (table, old, new,))
+ cr.execute('DROP INDEX IF EXISTS "%s_%s_index"' % (table, old))
+
+def migrate(cr, version):
+ if not version:
+ return
+
+ # workflow state moved to another module
+ cr.execute(
+ """
+ UPDATE ir_model_data
+ SET module = 'account_banking_payment'
+ WHERE name = 'trans_done_sent'
+ AND module = 'account_direct_debit'
+ """)
+
+ # rename field debit_move_line_id
+ rename_columns(cr, {
+ 'payment_line': [
+ ('debit_move_line_id', 'transit_move_line_id'),
+ ]})
=== modified file 'account_direct_debit/model/__init__.py'
--- account_direct_debit/model/__init__.py 2011-12-11 15:00:41 +0000
+++ account_direct_debit/model/__init__.py 2013-06-04 14:51:27 +0000
@@ -1,3 +1,5 @@
import account_payment
+import payment_line
import account_move_line
import account_invoice
+import payment_order_create
=== modified file 'account_direct_debit/model/account_invoice.py'
--- account_direct_debit/model/account_invoice.py 2012-01-12 10:58:49 +0000
+++ account_direct_debit/model/account_invoice.py 2013-06-04 14:51:27 +0000
@@ -1,6 +1,29 @@
# -*- coding: utf-8 -*-
-from osv import osv, fields
-from tools.translate import _
+##############################################################################
+#
+# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
+#
+# All other contributions are (C) by their respective contributors
+#
+# All Rights Reserved
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from openerp.osv import orm, fields
+from openerp.tools.translate import _
"""
This module adds support for Direct debit orders as applicable
@@ -98,7 +121,7 @@
open invoices with a matured invoice- or due date.
"""
-class account_invoice(osv.osv):
+class account_invoice(orm.Model):
_inherit = "account.invoice"
def __init__(self, pool, cr):
@@ -139,5 +162,3 @@
if not invoice['reconciled']:
return False
return True
-
-account_invoice()
=== modified file 'account_direct_debit/model/account_move_line.py'
--- account_direct_debit/model/account_move_line.py 2013-04-24 14:36:15 +0000
+++ account_direct_debit/model/account_move_line.py 2013-06-04 14:51:27 +0000
@@ -2,9 +2,8 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
-# This module additional (C) 2011 Therp BV (<http://therp.nl>).
-# (C) 2011 Smile Benelux (<http://smile.fr>).
+# This module (C) 2011 - 2013 Therp BV (<http://therp.nl>).
+# (C) 2011 Smile Benelux (<http://smile.fr>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -22,10 +21,9 @@
##############################################################################
from operator import itemgetter
-from osv import fields, osv
-from tools.translate import _
+from openerp.osv import fields, orm
-class account_move_line(osv.osv):
+class account_move_line(orm.Model):
_inherit = "account.move.line"
def amount_to_receive(self, cr, uid, ids, name, arg={}, context=None):
@@ -55,6 +53,9 @@
return r
def _to_receive_search(self, cr, uid, obj, name, args, context=None):
+ """
+ Reverse of account_payment/account_move_line.py:_to_pay_search()
+ """
if not args:
return []
line_obj = self.pool.get('account.move.line')
@@ -86,70 +87,9 @@
return [('id', '=', '0')]
return [('id', 'in', map(lambda x:x[0], res))]
- def _dummy(self, cr, user, ids, name, arg, context=None):
- res = {}
- if ids:
- res = dict([(x, False) for x in ids])
- return res
-
- def _invoice_payment_term_id_search(
- self, cr, uid, obj, name, args, context=None):
- """
- Allow to search move lines associated with an invoice with
- a particular payment term
- """
- if not args:
- return []
- invoice_obj = self.pool.get('account.invoice')
- invoice_ids = invoice_obj.search(
- cr, uid, [('payment_term', args[0][1], args[0][2])],
- context=context)
- operator = 'in' # (args[0][1] not in ['in', '=', '==', 'like', 'ilike']
- # and 'not in' or 'in')
- if not invoice_ids:
- return [('id', operator, [])]
- cr.execute('SELECT l.id ' \
- 'FROM account_move_line l, account_invoice i ' \
- 'WHERE l.move_id = i.move_id AND i.id in %s', (tuple(invoice_ids),))
- res = cr.fetchall()
- if not res:
- return [('id', '=', False)]
- return [('id', operator, [x[0] for x in res])]
-
- def _invoice_state_search(self, cr, uid, obj, name, args, context=None):
- if not args:
- return []
- invoice_obj = self.pool.get('account.invoice')
- invoice_ids = invoice_obj.search(
- cr, uid, [('state', args[0][1], args[0][2])],
- context=context)
- operator = 'in' # (args[0][1] not in ['in', '=', '==', 'like', 'ilike']
- # and 'not in' or 'in')
- if not invoice_ids:
- return [('id', operator, [])]
- cr.execute('SELECT l.id ' \
- 'FROM account_move_line l, account_invoice i ' \
- 'WHERE l.move_id = i.move_id AND i.id in %s', (tuple(invoice_ids),))
- res = cr.fetchall()
- if not res:
- return [('id', '=', False)]
- return [('id', operator, [x[0] for x in res])]
-
_columns = {
'amount_to_receive': fields.function(
amount_to_receive, method=True,
type='float', string='Amount to receive',
fnct_search=_to_receive_search),
- 'payment_term_id': fields.function(
- _dummy, method=True,
- string='Select by invoice payment term',
- type='many2one', relation='account.payment.term',
- fnct_search=_invoice_payment_term_id_search),
- 'invoice_state': fields.function(
- _dummy, method=True,
- string='Select by invoice state',
- type='char', size=24,
- fnct_search=_invoice_state_search),
}
-
-account_move_line()
=== modified file 'account_direct_debit/model/account_payment.py'
--- account_direct_debit/model/account_payment.py 2013-01-21 11:30:46 +0000
+++ account_direct_debit/model/account_payment.py 2013-06-04 14:51:27 +0000
@@ -1,36 +1,9 @@
# -*- coding: utf-8 -*-
-import time
-from osv import osv, fields
+from openerp.osv import orm, fields
import netsvc
from tools.translate import _
-class payment_mode(osv.osv):
- _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. For debit type modes only. ' +
- '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',
- help=('Limit selected invoices to invoices with these payment ' +
- 'terms')
- ),
- }
-payment_mode()
-
-class payment_order(osv.osv):
+class payment_order(orm.Model):
_inherit = 'payment.order'
def fields_view_get(self, cr, user, view_id=None, view_type='form',
@@ -56,56 +29,11 @@
context['search_payment_order_type'])]
# the magic is in the value of the selection
res['fields']['mode']['selection'] = mode_obj._name_search(
- cr, user, args=domain)
+ cr, user, args=domain, context=context)
# also update the domain
res['fields']['mode']['domain'] = domain
return res
- 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
- for order_line in order.line_ids:
- 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 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.
- """
- 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
@@ -117,362 +45,5 @@
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
- the debit order. This happens when the debit order file is
- generated.
- """
- res = super(payment_order, self).action_sent(
- cr, uid, ids, context)
-
- 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')
- for order in self.browse(cr, uid, ids, context=context):
- if order.payment_order_type != 'debit':
- continue
- for line in order.line_ids:
- # basic checks
- if not line.move_line_id:
- raise osv.except_osv(
- _('Error'),
- _('No move line provided for line %s') % line.name)
- if line.move_line_id.reconcile_id:
- raise osv.except_osv(
- _('Error'),
- _('Move line %s has already been paid/reconciled') %
- line.move_line_id.name
- )
-
- move_id = account_move_obj.create(cr, uid, {
- 'journal_id': order.mode.transfer_journal_id.id,
- 'name': 'Debit order %s' % line.move_line_id.move_id.name,
- 'reference': 'DEB%s' % line.move_line_id.move_id.name,
- }, context=context)
-
- # TODO: take multicurrency into account
-
- # create the debit move line on the transfer account
- vals = {
- 'name': 'Debit order for %s' % (
- line.move_line_id.invoice and
- line.move_line_id.invoice.number or
- line.move_line_id.name),
- 'move_id': move_id,
- 'partner_id': line.partner_id.id,
- 'account_id': order.mode.transfer_account_id.id,
- 'credit': 0.0,
- 'debit': line.amount,
- 'date': time.strftime('%Y-%m-%d'),
- }
- transfer_move_line_id = account_move_line_obj.create(
- cr, uid, vals, context=context)
-
- # create the debit move line on the receivable account
- vals.update({
- 'account_id': line.move_line_id.account_id.id,
- 'credit': line.amount,
- 'debit': 0.0,
- })
- reconcile_move_line_id = account_move_line_obj.create(
- cr, uid, 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,
- {'debit_move_line_id': reconcile_move_line_id},
- context=context)
-
- payment_line_obj.debit_reconcile(
- cr, uid, line.id, context=context)
- account_move_obj.post(cr, uid, [move_id], context=context)
- return res
-
-payment_order()
-
-class payment_line(osv.osv):
- _inherit = 'payment.line'
-
- def debit_storno(self, cr, uid, payment_line_id, amount,
- currency, storno_retry=True, context=None):
- """
- The processing of a storno is triggered by a debit
- transfer on one of the company's bank accounts.
- This method offers to re-reconcile the original debit
- payment. For this purpose, we have registered that
- payment move on the payment line.
-
- Return the (now incomplete) reconcile id. The caller MUST
- re-reconcile this reconcile with the bank transfer and
- re-open the associated invoice.
-
- :param payment_line_id: the single payment line id
- :param amount: the (signed) amount debited from the bank account
- :param currency: the bank account's currency *browse object*
- :param boolean storno_retry: when True, attempt to reopen the invoice, \
- set the invoice to 'Debit denied' otherwise.
- :return: an incomplete reconcile for the caller to fill
- :rtype: database id of an account.move.reconcile resource.
- """
-
- move_line_obj = self.pool.get('account.move.line')
- reconcile_obj = self.pool.get('account.move.reconcile')
- line = self.browse(cr, uid, payment_line_id)
- reconcile_id = False
- if (line.debit_move_line_id and not line.storno and
- self.pool.get('res.currency').is_zero(
- cr, uid, currency, (
- (line.debit_move_line_id.credit or 0.0) -
- (line.debit_move_line_id.debit or 0.0) + amount))):
- # Two different cases, full and partial
- # Both cases differ subtly in the procedure to follow
- # Needs refractoring, but why is this not in the OpenERP API?
- # Actually, given the nature of a direct debit order and storno,
- # we should not need to take partial into account on the side of
- # the debit_move_line.
- if line.debit_move_line_id.reconcile_partial_id:
- reconcile_id = line.debit_move_line_id.reconcile_partial_id.id
- attribute = 'reconcile_partial_id'
- if len(line.debit_move_line_id.reconcile_id.line_partial_ids) == 2:
- # reuse the simple reconcile for the storno transfer
- reconcile_obj.write(
- cr, uid, reconcile_id, {
- 'line_id': [(6, 0, line.debit_move_line_id.id)],
- 'line_partial_ids': [(6, 0, [])],
- }, context=context)
- else:
- # split up the original reconcile in a partial one
- # and a new one for reconciling the storno transfer
- reconcile_obj.write(
- cr, uid, reconcile_id, {
- 'line_partial_ids': [(3, line.debit_move_line_id.id)],
- }, context=context)
- reconcile_id = reconcile_obj.create(
- cr, uid, {
- 'type': 'auto',
- 'line_id': [(6, 0, line.debit_move_line_id.id)],
- }, context=context)
- elif line.debit_move_line_id.reconcile_id:
- reconcile_id = line.debit_move_line_id.reconcile_id.id
- if len(line.debit_move_line_id.reconcile_id.line_id) == 2:
- # reuse the simple reconcile for the storno transfer
- reconcile_obj.write(
- cr, uid, reconcile_id, {
- 'line_id': [(6, 0, [line.debit_move_line_id.id])]
- }, context=context)
- else:
- # split up the original reconcile in a partial one
- # and a new one for reconciling the storno transfer
- partial_ids = [
- x.id for x in line.debit_move_line_id.reconcile_id.line_id
- if x.id != line.debit_move_line_id.id
- ]
- reconcile_obj.write(
- cr, uid, reconcile_id, {
- 'line_partial_ids': [(6, 0, partial_ids)],
- 'line_id': [(6, 0, [])],
- }, context=context)
- reconcile_id = reconcile_obj.create(
- cr, uid, {
- 'type': 'auto',
- 'line_id': [(6, 0, line.debit_move_line_id.id)],
- }, context=context)
- # mark the payment line for storno processed
- if reconcile_id:
- self.write(cr, uid, [payment_line_id],
- {'storno': True}, context=context)
- # put forth the invoice workflow
- if line.move_line_id.invoice:
- activity = (storno_retry and 'open_test'
- or 'invoice_debit_denied')
- netsvc.LocalService("workflow").trg_validate(
- uid, 'account.invoice', line.move_line_id.invoice.id,
- activity, cr)
- return reconcile_id
-
- def get_storno_account_id(self, cr, uid, payment_line_id, amount,
- currency, context=None):
- """
- Check the match of the arguments, and return the account associated
- with the storno.
- Used in account_banking interactive mode
-
- :param payment_line_id: the single payment line id
- :param amount: the (signed) amount debited from the bank account
- :param currency: the bank account's currency *browse object*
- :return: an account if there is a full match, False otherwise
- :rtype: database id of an account.account resource.
- """
-
- line = self.browse(cr, uid, payment_line_id)
- account_id = False
- if (line.debit_move_line_id and not line.storno and
- self.pool.get('res.currency').is_zero(
- cr, uid, currency, (
- (line.debit_move_line_id.credit or 0.0) -
- (line.debit_move_line_id.debit or 0.0) + amount))):
- account_id = line.debit_move_line_id.account_id.id
- return account_id
-
- def debit_reconcile(self, cr, uid, payment_line_id, context=None):
- """
- 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
- account though.
-
- :param payment_line_id: the single id of the canceled payment line
- """
-
- if isinstance(payment_line_id, (list, tuple)):
- payment_line_id = payment_line_id[0]
- reconcile_obj = self.pool.get('account.move.reconcile')
- move_line_obj = self.pool.get('account.move.line')
- payment_line = self.browse(cr, uid, payment_line_id, context=context)
-
- debit_move_line = payment_line.debit_move_line_id
- torec_move_line = payment_line.move_line_id
-
- if payment_line.storno:
- raise osv.except_osv(
- _('Can not reconcile'),
- _('Cancelation of payment line \'%s\' has already been ' +
- 'processed') % payment_line.name)
- if (not debit_move_line or not torec_move_line):
- raise osv.except_osv(
- _('Can not reconcile'),
- _('No move line for line %s') % payment_line.name)
- if torec_move_line.reconcile_id: # torec_move_line.reconcile_partial_id:
- raise osv.except_osv(
- _('Error'),
- _('Move line %s has already been reconciled') %
- torec_move_line.name
- )
- if debit_move_line.reconcile_id or debit_move_line.reconcile_partial_id:
- raise osv.except_osv(
- _('Error'),
- _('Move line %s has already been reconciled') %
- debit_move_line.name
- )
-
- def is_zero(total):
- return self.pool.get('res.currency').is_zero(
- cr, uid, debit_move_line.company_id.currency_id, total)
-
- line_ids = [debit_move_line.id, torec_move_line.id]
- if torec_move_line.reconcile_partial_id:
- line_ids = [
- x.id for x in debit_move_line.reconcile_partial_id.line_partial_ids] + [torec_move_line_id]
-
- total = move_line_obj.get_balance(cr, uid, line_ids)
- vals = {
- 'type': 'auto',
- 'line_id': is_zero(total) and [(6, 0, line_ids)] or [(6, 0, [])],
- 'line_partial_ids': is_zero(total) and [(6, 0, [])] or [(6, 0, line_ids)],
- }
-
- if torec_move_line.reconcile_partial_id:
- reconcile_obj.write(
- cr, uid, debit_move_line.reconcile_partial_id.id,
- vals, context=context)
- else:
- reconcile_obj.create(
- cr, uid, vals, context=context)
- for line_id in line_ids:
- netsvc.LocalService("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(
- uid, 'account.invoice', torec_move_line.invoice.id,
- 'undo_debit_denied', cr)
-
-
-
- _columns = {
- 'debit_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',
- readonly=True,
- help="Move line through which the debit order pays the invoice"),
- 'storno': fields.boolean(
- 'Storno',
- readonly=True,
- help=("If this is true, the debit order has been canceled " +
- "by the bank or by the customer")),
- }
-payment_line()
-
-
-class payment_order_create(osv.osv_memory):
- _inherit = 'payment.order.create'
-
- def search_entries(self, cr, uid, ids, context=None):
- """
- This method taken from account_payment module.
- We adapt the domain based on the payment_order_type
- """
- line_obj = self.pool.get('account.move.line')
- mod_obj = self.pool.get('ir.model.data')
- if context is None:
- context = {}
- data = self.read(cr, uid, ids, [], context=context)[0]
- search_due_date = data['duedate']
-
- ### start account_direct_debit ###
- 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 = [
- ('reconcile_id', '=', False),
- ('account_id.type', '=', 'receivable'),
- ('invoice_state', '!=', 'debit_denied'),
- ('amount_to_receive', '>', 0),
- ]
- else:
- domain = [
- ('reconcile_id', '=', False),
- ('account_id.type', '=', 'payable'),
- ('amount_to_pay', '>', 0)
- ]
- domain.append(('company_id', '=', payment.mode.company_id.id))
- # 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 ###
-
- domain = domain + ['|', ('date_maturity', '<=', search_due_date), ('date_maturity', '=', False)]
- line_ids = line_obj.search(cr, uid, domain, context=context)
- 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': ('Entry Lines'),
- 'context': context,
- 'view_type': 'form',
- 'view_mode': 'form',
- 'res_model': 'payment.order.create',
- 'views': [(resource_id,'form')],
- 'type': 'ir.actions.act_window',
- 'target': 'new',
- }
-payment_order_create()
-
-
-
+ return super(payment_order, self).test_undo_done(
+ cr, uid, ids, context=context)
=== added file 'account_direct_debit/model/payment_line.py'
--- account_direct_debit/model/payment_line.py 1970-01-01 00:00:00 +0000
+++ account_direct_debit/model/payment_line.py 2013-06-04 14:51:27 +0000
@@ -0,0 +1,152 @@
+# -*- coding: utf-8 -*-
+from openerp.osv import orm, fields
+import netsvc
+from tools.translate import _
+
+class payment_line(orm.Model):
+ _inherit = 'payment.line'
+
+ def debit_storno(self, cr, uid, payment_line_id, amount,
+ currency, storno_retry=True, context=None):
+ """
+ The processing of a storno is triggered by a debit
+ transfer on one of the company's bank accounts.
+ This method offers to re-reconcile the original debit
+ payment. For this purpose, we have registered that
+ payment move on the payment line.
+
+ Return the (now incomplete) reconcile id. The caller MUST
+ re-reconcile this reconcile with the bank transfer and
+ re-open the associated invoice.
+
+ :param payment_line_id: the single payment line id
+ :param amount: the (signed) amount debited from the bank account
+ :param currency: the bank account's currency *browse object*
+ :param boolean storno_retry: when True, attempt to reopen the invoice, \
+ set the invoice to 'Debit denied' otherwise.
+ :return: an incomplete reconcile for the caller to fill
+ :rtype: database id of an account.move.reconcile resource.
+ """
+
+ move_line_obj = self.pool.get('account.move.line')
+ reconcile_obj = self.pool.get('account.move.reconcile')
+ line = self.browse(cr, uid, payment_line_id)
+ reconcile_id = False
+ if (line.transit_move_line_id and not line.storno and
+ self.pool.get('res.currency').is_zero(
+ cr, uid, currency, (
+ (line.transit_move_line_id.credit or 0.0) -
+ (line.transit_move_line_id.debit or 0.0) + amount))):
+ # Two different cases, full and partial
+ # Both cases differ subtly in the procedure to follow
+ # Needs refractoring, but why is this not in the OpenERP API?
+ # Actually, given the nature of a direct debit order and storno,
+ # we should not need to take partial into account on the side of
+ # the transit_move_line.
+ if line.transit_move_line_id.reconcile_partial_id:
+ reconcile_id = line.transit_move_line_id.reconcile_partial_id.id
+ attribute = 'reconcile_partial_id'
+ if len(line.transit_move_line_id.reconcile_id.line_partial_ids) == 2:
+ # reuse the simple reconcile for the storno transfer
+ reconcile_obj.write(
+ cr, uid, reconcile_id, {
+ 'line_id': [(6, 0, line.transit_move_line_id.id)],
+ 'line_partial_ids': [(6, 0, [])],
+ }, context=context)
+ else:
+ # split up the original reconcile in a partial one
+ # and a new one for reconciling the storno transfer
+ reconcile_obj.write(
+ cr, uid, reconcile_id, {
+ 'line_partial_ids': [(3, line.transit_move_line_id.id)],
+ }, context=context)
+ reconcile_id = reconcile_obj.create(
+ cr, uid, {
+ 'type': 'auto',
+ 'line_id': [(6, 0, line.transit_move_line_id.id)],
+ }, context=context)
+ elif line.transit_move_line_id.reconcile_id:
+ reconcile_id = line.transit_move_line_id.reconcile_id.id
+ if len(line.transit_move_line_id.reconcile_id.line_id) == 2:
+ # reuse the simple reconcile for the storno transfer
+ reconcile_obj.write(
+ cr, uid, reconcile_id, {
+ 'line_id': [(6, 0, [line.transit_move_line_id.id])]
+ }, context=context)
+ else:
+ # split up the original reconcile in a partial one
+ # and a new one for reconciling the storno transfer
+ partial_ids = [
+ x.id for x in line.transit_move_line_id.reconcile_id.line_id
+ if x.id != line.transit_move_line_id.id
+ ]
+ reconcile_obj.write(
+ cr, uid, reconcile_id, {
+ 'line_partial_ids': [(6, 0, partial_ids)],
+ 'line_id': [(6, 0, [])],
+ }, context=context)
+ reconcile_id = reconcile_obj.create(
+ cr, uid, {
+ 'type': 'auto',
+ 'line_id': [(6, 0, line.transit_move_line_id.id)],
+ }, context=context)
+ # mark the payment line for storno processed
+ if reconcile_id:
+ self.write(cr, uid, [payment_line_id],
+ {'storno': True}, context=context)
+ # put forth the invoice workflow
+ if line.move_line_id.invoice:
+ activity = (storno_retry and 'open_test'
+ or 'invoice_debit_denied')
+ netsvc.LocalService("workflow").trg_validate(
+ uid, 'account.invoice', line.move_line_id.invoice.id,
+ activity, cr)
+ return reconcile_id
+
+ def get_storno_account_id(self, cr, uid, payment_line_id, amount,
+ currency, context=None):
+ """
+ Check the match of the arguments, and return the account associated
+ with the storno.
+ Used in account_banking interactive mode
+
+ :param payment_line_id: the single payment line id
+ :param amount: the (signed) amount debited from the bank account
+ :param currency: the bank account's currency *browse object*
+ :return: an account if there is a full match, False otherwise
+ :rtype: database id of an account.account resource.
+ """
+
+ line = self.browse(cr, uid, payment_line_id)
+ account_id = False
+ if (line.transit_move_line_id and not line.storno and
+ self.pool.get('res.currency').is_zero(
+ cr, uid, currency, (
+ (line.transit_move_line_id.credit or 0.0) -
+ (line.transit_move_line_id.debit or 0.0) + amount))):
+ account_id = line.transit_move_line_id.account_id.id
+ return account_id
+
+ def debit_reconcile(self, cr, uid, payment_line_id, context=None):
+ """
+ Raise if a payment line is passed for which storno is True
+ """
+ if isinstance(payment_line_id, (list, tuple)):
+ payment_line_id = payment_line_id[0]
+ payment_line = self.read(
+ cr, uid, payment_line_id, ['storno', 'name'], context=context)
+ if payment_line['storno']:
+ raise orm.except_orm(
+ _('Can not reconcile'),
+ _('Cancelation of payment line \'%s\' has already been '
+ 'processed') % payment_line['name'])
+ return super(self, payment_line).debit_reconcile(
+ cr, uid, payment_line_id, context=context)
+
+ _columns = {
+ 'storno': fields.boolean(
+ 'Storno',
+ readonly=True,
+ help=("If this is true, the debit order has been canceled "
+ "by the bank or by the customer")),
+ }
=== added file 'account_direct_debit/model/payment_order_create.py'
--- account_direct_debit/model/payment_order_create.py 1970-01-01 00:00:00 +0000
+++ account_direct_debit/model/payment_order_create.py 2013-06-04 14:51:27 +0000
@@ -0,0 +1,41 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (C) 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
+
+
+class payment_order_create(orm.TransientModel):
+ _inherit = 'payment.order.create'
+
+ def extend_payment_order_domain(
+ self, cr, uid, payment_order, domain, context=None):
+ super(payment_order_create, self).extend_payment_order_domain(
+ cr, uid, payment_order, domain, context=context)
+ if payment_order.payment_order_type == 'debit':
+ domain += [
+ ('account_id.type', '=', 'receivable'),
+ ('invoice.state', '!=', 'debit_denied'),
+ ('amount_to_receive', '>', 0),
+ ]
+ return True
=== modified file 'account_direct_debit/view/account_invoice.xml'
--- account_direct_debit/view/account_invoice.xml 2012-05-01 20:36:44 +0000
+++ account_direct_debit/view/account_invoice.xml 2013-06-04 14:51:27 +0000
@@ -4,7 +4,6 @@
<record id="invoice_form" model="ir.ui.view">
<field name="name">account.invoice.form</field>
<field name="model">account.invoice</field>
- <field name="type">form</field>
<field name="inherit_id" ref="account.invoice_form"/>
<field name="arch" type="xml">
<data>
@@ -16,14 +15,6 @@
<!-- button name="invoice_open" position="attributes">
<attribute name="states">draft,proforma2,debit_denied</attribute>
</button -->
- <button string='Re-Open' position="attributes">
- <attribute name="states">paid,debit_denied</attribute>
- <!--
- unintentional fix of
- https://bugs.launchpad.net/openobject-addons/+bug/807543
- -->
- <attribute name="groups"/>
- </button>
<button name="invoice_open" position="after">
<button name="invoice_debit_denied" states="paid"
string="Debit Denied" icon="gtk-cancel"/>
@@ -38,7 +29,11 @@
<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!" string="Debit denied" domain="[('state','=','debit_denied')]" help="Show only invoices with state Debit denied"/>
+ <filter name="debit_denied" icon="terp-dolar_ok!"
+ string="Debit denied"
+ domain="[('state','=','debit_denied')]"
+ help="Show only invoices with state Debit denied"
+ />
</filter>
</field>
</record>
=== modified file 'account_direct_debit/view/account_payment.xml'
--- account_direct_debit/view/account_payment.xml 2013-01-21 11:19:04 +0000
+++ account_direct_debit/view/account_payment.xml 2013-06-04 14:51:27 +0000
@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
+
<!-- distinguish between payment orders and debit orders in the menu -->
<record id="account_payment.action_payment_order_tree" model="ir.actions.act_window">
<field name="domain">[('payment_order_type', '=', 'payment')]</field>
@@ -24,7 +25,6 @@
<record id="view_payment_order_form" model="ir.ui.view">
<field name="name">payment.order.form</field>
<field name="model">payment.order</field>
- <field name="type">form</field>
<field name="inherit_id" ref="account_payment.view_payment_order_form"/>
<field name="priority" eval="60"/>
<field name="arch" type="xml">
@@ -46,40 +46,13 @@
icon="gtk-find"
/>
</xpath>
- <!-- the attrs do not work like this, apparently
- <xpath expr="//tree[@string='Payment Line']" position="inside">
- <field name="storno" attrs="{'invisible': [(parent.payment_order_type, '!=', 'debit')]}"/>
- </xpath>
- -->
</data>
</field>
</record>
- <!-- Add transfer account for debit type modes -->
- <record model="ir.ui.view" id="view_payment_mode_form">
- <field name="name">payment.mode.form add transfer account</field>
- <field name="model">payment.mode</field>
- <field name="inherit_id" ref="account_banking.view_payment_mode_form_inherit"/>
- <field name="type">form</field>
- <field name="arch" type="xml">
- <field name="type" position="after">
- <field name="transfer_account_id"
- domain="[('type', '=', 'other'),
- ('reconcile', '=', True),
- ('company_id', '=', company_id)]"
- />
- <field name="transfer_journal_id"
- domain="[('company_id', '=', company_id)]"
- />
- <field name="payment_term_ids"/>
- </field>
- </field>
- </record>
-
<record id="view_payment_line_tree" model="ir.ui.view">
<field name="name">Payment Lines</field>
<field name="model">payment.line</field>
- <field name="type">tree</field>
<field name="inherit_id" ref="account_payment.view_payment_line_tree"/>
<field eval="4" name="priority"/>
<field name="arch" type="xml">
@@ -88,5 +61,6 @@
</field>
</field>
</record>
+
</data>
</openerp>
=== removed file 'account_direct_debit/workflow/account_payment.xml'
--- account_direct_debit/workflow/account_payment.xml 2013-01-02 14:45:02 +0000
+++ account_direct_debit/workflow/account_payment.xml 1970-01-01 00:00:00 +0000
@@ -1,25 +0,0 @@
-<?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" eval="False"/>
- </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>
=== modified file 'account_payment_shortcut/__init__.py'
--- account_payment_shortcut/__init__.py 2011-12-09 13:07:52 +0000
+++ account_payment_shortcut/__init__.py 2013-06-04 14:51:27 +0000
@@ -1,1 +1,2 @@
+# -*- coding: utf-8 -*-
import payment_order
=== modified file 'account_payment_shortcut/payment_order.py'
--- account_payment_shortcut/payment_order.py 2011-12-09 13:07:52 +0000
+++ account_payment_shortcut/payment_order.py 2013-06-04 14:51:27 +0000
@@ -1,8 +1,30 @@
# -*- coding: utf-8 -*-
-from osv import osv, fields
-
-class payment_order_create(osv.osv_memory):
-
+##############################################################################
+#
+# Copyright (C) 2011 - 2013 Therp BV (<http://therp.nl>).
+#
+# All other contributions are (C) by their respective contributors
+#
+# All Rights Reserved
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as
+# published by the Free Software Foundation, either version 3 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+#
+##############################################################################
+
+from osv import orm
+
+class payment_order_create(orm.TransientModel):
_inherit = 'payment.order.create'
def default_get(self, cr, uid, fields_list, context=None):
@@ -27,5 +49,3 @@
res['entries'] = context['line_ids']
return res
-
-payment_order_create()
=== modified file 'bank_statement_instant_voucher/model/account_bank_statement_line.py'
--- bank_statement_instant_voucher/model/account_bank_statement_line.py 2012-12-05 20:16:14 +0000
+++ bank_statement_instant_voucher/model/account_bank_statement_line.py 2013-06-04 14:51:27 +0000
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
-# This module copyright (C) 2012 Therp BV (<http://therp.nl>).
+# This module copyright (C) 2012 - 2013 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -19,10 +19,10 @@
#
##############################################################################
-from openerp.osv import osv, fields
-
-
-class account_bank_statement_line(osv.Model):
+from openerp.osv import orm, fields
+
+
+class account_bank_statement_line(orm.Model):
_inherit = 'account.bank.statement.line'
def create_instant_voucher(self, cr, uid, ids, context=None):
res = False
=== modified file 'bank_statement_instant_voucher/model/account_voucher_instant.py'
--- bank_statement_instant_voucher/model/account_voucher_instant.py 2012-12-05 20:16:14 +0000
+++ bank_statement_instant_voucher/model/account_voucher_instant.py 2013-06-04 14:51:27 +0000
@@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
-# This module copyright (C) 2012 Therp BV (<http://therp.nl>).
+# This module copyright (C) 2012 - 2013 Therp BV (<http://therp.nl>).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -19,12 +19,12 @@
#
##############################################################################
-from openerp.osv import osv, fields
+from openerp.osv import orm, fields
from openerp.tools.translate import _
from openerp.addons.decimal_precision import decimal_precision as dp
-class instant_voucher(osv.TransientModel):
+class instant_voucher(orm.TransientModel):
_name = 'account.voucher.instant'
_description = 'Instant Voucher'
@@ -76,7 +76,7 @@
cr, uid, [('company_id', '=', line.company_id.id),
('type', '=', voucher_type)])
if not journal_ids:
- osv.exept_osv(
+ orm.exept_orm(
_('Error'),
_('No %s journal defined') % voucher_type)
@@ -156,7 +156,7 @@
context.get('active_id') or
context.get('active_ids') and context.get('active_ids')[0])
if not res['statement_line_id']:
- raise osv.except_osv(
+ raise orm.except_orm(
_('Error'),
_('Cannot determine statement line'))
line = self.pool.get('account.bank.statement.line').browse(
@@ -212,7 +212,7 @@
instant.voucher_id.company_id.currency_id)
if (instant.statement_line_id.statement_id.currency.id !=
voucher_currency.id):
- raise osv.except_osv(
+ raise orm.except_orm(
_("Error"),
_("Currency on the bank statement line needs to be the "
"same as on the voucher. Currency conversion is not yet "
@@ -222,7 +222,7 @@
cr, uid, [instant.voucher_id.id], context=context)
instant.refresh()
if instant.voucher_id.state != 'posted':
- raise osv.except_osv(
+ raise orm.except_orm(
_("Error"),
_("The voucher could not be posted."))
if instant.voucher_id.move_id.state != 'posted':
@@ -230,12 +230,12 @@
cr, uid, [instant.voucher_id.move_id.id], context=context)
instant.refresh()
if instant.voucher_id.move_id.state != 'posted':
- raise osv.except_osv(
+ raise orm.except_orm(
_("Error"),
_("The voucher's move line could not be posted."))
if not self.pool.get('res.currency').is_zero(
cr, uid, voucher_currency, instant.balance):
- raise osv.except_osv(
+ raise orm.except_orm(
_("Error"),
_("The amount on the bank statement line needs to be the "
"same as on the voucher. Write-off is not yet "
@@ -245,7 +245,7 @@
# and trigger its posting and reconciliation.
if 'import_transaction_id' in statement_line_obj._columns:
if instant.statement_line_id.state == 'confirmed':
- raise osv.except_osv(
+ raise orm.except_orm(
_("Error"),
_("Cannot match a confirmed statement line"))
if not instant.statement_line_id.import_transaction_id:
=== modified file 'bank_statement_instant_voucher/view/account_bank_statement_line.xml'
--- bank_statement_instant_voucher/view/account_bank_statement_line.xml 2012-11-28 13:49:36 +0000
+++ bank_statement_instant_voucher/view/account_bank_statement_line.xml 2013-06-04 14:51:27 +0000
@@ -5,7 +5,6 @@
<field name="name">Add instant voucher button to bank statement line on statement form</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field name="model">account.bank.statement</field>
- <field name="type">form</field>
<field name="priority" eval="30"/>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page/field[@name='line_ids']/tree/field[@name='voucher_id']"
=== modified file 'bank_statement_instant_voucher/view/account_voucher_instant.xml'
--- bank_statement_instant_voucher/view/account_voucher_instant.xml 2012-11-28 13:49:36 +0000
+++ bank_statement_instant_voucher/view/account_voucher_instant.xml 2013-06-04 14:51:27 +0000
@@ -4,7 +4,6 @@
<record id="instant_voucher_form" model="ir.ui.view">
<field name="name">Instant voucher form view</field>
<field name="model">account.voucher.instant</field>
- <field name="type">form</field>
<field name="arch" type="xml">
<form>
<field name="state" invisible="1" readonly="1"/>
Follow ups