[MRG] Migration of payment modules

This commit is contained in:
unknown
2013-07-01 15:10:40 +02:00
committed by Holger Brunn
47 changed files with 1138 additions and 1173 deletions

View File

@@ -267,7 +267,7 @@ class account_banking_imported_file(orm.Model):
}
_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 @@ class account_bank_statement(orm.Model):
['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 @@ class account_bank_statement(orm.Model):
# 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)],
@@ -450,7 +450,7 @@ class account_voucher(orm.Model):
context = {}
if not context.get('period_id') and context.get('move_line_ids'):
return self.pool.get('account.move.line').browse(
cr, uid , context.get('move_line_ids'))[0].period_id.id
cr, uid , context.get('move_line_ids'), context=context)[0].period_id.id
return super(account_voucher, self)._get_period(cr, uid, context)
account_voucher()
@@ -467,12 +467,12 @@ class account_bank_statement_line(orm.Model):
_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 @@ class account_bank_statement_line(orm.Model):
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 @@ class res_partner_bank(orm.Model):
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 @@ class res_partner_bank(orm.Model):
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 @@ class res_partner_bank(orm.Model):
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 @@ class res_partner_bank(orm.Model):
# 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 @@ class res_partner_bank(orm.Model):
return records
return records[0]
def check_iban(self, cursor, uid, ids):
def check_iban(self, cr, uid, ids, context=None):
'''
Check IBAN number
'''
for bank_acc in self.browse(cursor, uid, ids):
for bank_acc in self.browse(cr, uid, ids, context=context):
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 @@ class res_partner_bank(orm.Model):
)
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 @@ class res_partner_bank(orm.Model):
# 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, context=context)
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 @@ class res_partner_bank(orm.Model):
# 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], context=context)
if partner_bank_id.country_id:
country = partner_bank_id.country_id
country_ids = [country.id]
@@ -804,12 +804,12 @@ class res_partner_bank(orm.Model):
# 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, context=context).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, context=context)
# 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 @@ class res_partner_bank(orm.Model):
# 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 @@ class res_partner_bank(orm.Model):
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 @@ class res_bank(orm.Model):
'''
_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 @@ class res_bank(orm.Model):
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:

View File

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

View File

@@ -62,7 +62,7 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
# 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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
# 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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
' 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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
), 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 @@ class banking_import_transaction(orm.Model):
# 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 @@ class banking_import_transaction(orm.Model):
# 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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
'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 @@ class account_bank_statement_line(orm.Model):
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',
@@ -1755,14 +1754,14 @@ class account_bank_statement_line(orm.Model):
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

View File

@@ -28,9 +28,9 @@ This module contains the business logic of the wizard account_banking_import.
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_line(orm.TransientModel):
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 @@ class banking_import(orm.TransientModel):
# 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 @@ class banking_import(orm.TransientModel):
)
# 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 @@ class banking_import(orm.TransientModel):
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 @@ class banking_import(orm.TransientModel):
# (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 @@ class banking_import(orm.TransientModel):
# 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 @@ class banking_import(orm.TransientModel):
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 @@ class banking_import(orm.TransientModel):
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 @@ class banking_import(orm.TransientModel):
]
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)

View File

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

View File

@@ -90,56 +90,33 @@ class banking_transaction_wizard(orm.TransientModel):
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
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
@@ -147,8 +124,8 @@ class banking_transaction_wizard(orm.TransientModel):
# 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)
@@ -159,15 +136,12 @@ class banking_transaction_wizard(orm.TransientModel):
}, 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_ids or manual_invoice_ids:
move_line_obj = self.pool.get('account.move.line')
@@ -323,24 +297,9 @@ class banking_transaction_wizard(orm.TransientModel):
{'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(
@@ -392,8 +351,25 @@ class banking_transaction_wizard(orm.TransientModel):
'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)]),
'manual_move_line_id': fields.many2one(
'account.move.line', 'Or match this entry',
domain=[('account_id.reconcile', '=', True),
('reconcile_id', '=', False)]),
'manual_invoice_ids': fields.many2many(
'account.invoice',
'banking_transaction_wizard_account_invoice_rel',
@@ -417,8 +393,6 @@ class banking_transaction_wizard(orm.TransientModel):
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()

View File

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

View File

@@ -51,7 +51,7 @@ def get_period(pool, cr, uid, date, company, log=None):
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 @@ def get_bank_accounts(pool, cursor, uid, account_number, log, fail=False):
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 @@ def get_bank_accounts(pool, cursor, uid, account_number, log, fail=False):
% 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 @@ def get_partner(pool, cr, uid, name, address, postal_code, city,
'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 @@ def get_company_bank_account(pool, cursor, uid, account_number, currency,
# 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 @@ def get_company_bank_account(pool, cursor, uid, account_number, currency,
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 @@ def get_company_bank_account(pool, cursor, uid, account_number, currency,
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 @@ def get_or_create_bank(pool, cursor, uid, bic, online=False, code=None,
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 @@ def get_or_create_bank(pool, cursor, uid, bic, online=False, code=None,
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 @@ def get_or_create_bank(pool, cursor, uid, bic, online=False, code=None,
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,

View File

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

View File

@@ -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 _
from openerp.osv import orm, fields
from openerp.tools.translate import _
class clieop_export(osv.osv):
class clieop_export(orm.Model):
'''ClieOp3 Export'''
_name = 'banking.export.clieop'
_description = __doc__
@@ -80,7 +82,7 @@ class clieop_export(osv.osv):
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 @@ class clieop_export(osv.osv):
'state': 'draft',
'daynumber': get_daynr,
}
clieop_export()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

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

View File

@@ -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
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, format='%Y-%m-%d'):
def strpdate(arg):
'''shortcut'''
return datetime.strptime(arg, format).date()
return datetime.strptime(arg, DEFAULT_SERVER_DATE_FORMAT).date()
def strfdate(arg, format='%Y-%m-%d'):
def strfdate(arg):
'''shortcut'''
return arg.strftime(format)
return arg.strftime(DEFAULT_SERVER_DATE_FORMAT)
class banking_export_clieop_wizard(osv.osv_memory):
class banking_export_clieop_wizard(orm.TransientModel):
_name = 'banking.export.clieop.wizard'
_description = 'Client Opdrachten Export'
_columns = {
@@ -151,21 +153,17 @@ class banking_export_clieop_wizard(osv.osv_memory):
),
}
_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 @@ class banking_export_clieop_wizard(osv.osv_memory):
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 @@ class banking_export_clieop_wizard(osv.osv_memory):
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 @@ class banking_export_clieop_wizard(osv.osv_memory):
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 @@ class banking_export_clieop_wizard(osv.osv_memory):
'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 @@ class banking_export_clieop_wizard(osv.osv_memory):
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 @@ class banking_export_clieop_wizard(osv.osv_memory):
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 @@ class banking_export_clieop_wizard(osv.osv_memory):
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 @@ class banking_export_clieop_wizard(osv.osv_memory):
# 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 @@ class banking_export_clieop_wizard(osv.osv_memory):
# 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 @@ class banking_export_clieop_wizard(osv.osv_memory):
[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 @@ class banking_export_clieop_wizard(osv.osv_memory):
'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:

View File

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

View File

@@ -45,6 +45,7 @@ As a counter measure, all imported data is converted to SWIFT-format before usag
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 @@ class transaction_message(object):
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 @@ class transaction(models.mem_bank_transaction):
'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 @@ class transaction(models.mem_bank_transaction):
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):

View File

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

View File

@@ -4,7 +4,6 @@ import payment_mode
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

View File

@@ -73,6 +73,8 @@ class payment_order(orm.Model):
'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 @@ class payment_order(orm.Model):
])
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,172 @@ class payment_order(orm.Model):
'''
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):
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.
During import of bank statements, create the reconcile on the transfer
account containing all the open move lines on the transfer account.
"""
raise orm.except_orm(
_("Cannot reconcile"),
_("Cannot reconcile debit order: "+
"Not implemented."))
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')
labels = {
'payment': _('Payment order'),
'debit': _('Direct debit order'),
}
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 %s' % (labels[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 for %s') % (
labels[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
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."))

View File

@@ -27,37 +27,44 @@ from openerp.osv import orm, fields
from openerp import netsvc
from openerp.tools.translate import _
from openerp.addons.decimal_precision import decimal_precision as dp
from openerp.addons.account_banking.parsers.models import mem_bank_transaction as bt
class banking_import_transaction(orm.Model):
_inherit = 'banking.import.transaction'
def _match_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 @@ class banking_import_transaction(orm.Model):
# 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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
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 @@ class banking_import_transaction(orm.Model):
)
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 @@ class banking_import_transaction(orm.Model):
"""
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,
})

View File

@@ -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 @@ class banking_transaction_wizard(orm.TransientModel):
'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),
]),
}

View File

@@ -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 @@ class payment_line(orm.Model):
_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 @@ class payment_line(orm.Model):
"""
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)

View File

@@ -46,6 +46,27 @@ class payment_mode(orm.Model):
_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')
),
}

View File

@@ -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 @@ class payment_order_create(orm.TransientModel):
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 @@ class payment_order_create(orm.TransientModel):
### 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
@@ -125,6 +192,8 @@ class payment_order_create(orm.TransientModel):
'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'}

View File

@@ -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 expr="//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='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>

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 -->
<record id="account_banking.trans_sent_done" model="workflow.transition">
<field name="act_from" ref="account_banking.act_sent"/>
<field name="act_to" ref="account_payment.act_done"/>
<field name="signal">done</field>
</record>
<!-- Add new transition sent -> rejected -->
<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' -->
<!-- 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_wait"/>
<field name="act_to" ref="account_payment.act_done"/>
<field name="signal">done</field>
</record>
<!-- Rejected by the bank -->
<record id="account_banking.trans_sent_rejected" model="workflow.transition">
<field name="act_from" ref="account_banking.act_sent"/>
<field name="act_to" ref="account_banking.act_rejected"/>
<field name="signal">rejected</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>

View File

@@ -25,7 +25,7 @@
'author': 'credativ Ltd',
'website': 'http://www.credativ.co.uk',
'category': 'Account Banking',
'depends': ['account_banking'],
'depends': ['account_banking_payment'],
'data': [
'account_banking_uk_hsbc.xml',
'hsbc_clientid_view.xml',

View File

@@ -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 @@ of modules helps you to provide support for communications with your local
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,
}

View File

@@ -175,7 +175,7 @@ msgid "The payment line name must be unique!"
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 @@ msgid "Invoice"
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"

View File

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

View File

@@ -1,3 +1,5 @@
import account_payment
import payment_line
import account_move_line
import account_invoice
import payment_order_create

View File

@@ -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 @@ Two cases need to be distinguisted:
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 @@ class account_invoice(osv.osv):
if not invoice['reconciled']:
return False
return True
account_invoice()

View File

@@ -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 @@ class account_move_line(osv.osv):
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 @@ class account_move_line(osv.osv):
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()

View File

@@ -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 @@ class payment_order(osv.osv):
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 @@ class payment_order(osv.osv):
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)

View File

@@ -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")),
}

View File

@@ -1,8 +1,7 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright (C) 2009 EduSense BV (<http://www.edusense.nl>).
# (C) 2011 - 2013 Therp BV (<http://therp.nl>).
# Copyright (C) 2013 Therp BV (<http://therp.nl>).
#
# All other contributions are (C) by their respective contributors
#
@@ -23,18 +22,20 @@
#
##############################################################################
from openerp.osv import orm, fields
from openerp.osv import orm
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,),
}
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

View File

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

View File

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

View File

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

View File

@@ -1 +1,2 @@
# -*- coding: utf-8 -*-
import payment_order

View File

@@ -1,8 +1,30 @@
# -*- coding: utf-8 -*-
from osv import osv, fields
##############################################################################
#
# 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/>.
#
##############################################################################
class payment_order_create(osv.osv_memory):
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 @@ class payment_order_create(osv.osv_memory):
res['entries'] = context['line_ids']
return res
payment_order_create()

View File

@@ -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
from openerp.osv import orm, fields
class account_bank_statement_line(osv.Model):
class account_bank_statement_line(orm.Model):
_inherit = 'account.bank.statement.line'
def create_instant_voucher(self, cr, uid, ids, context=None):
res = False

View File

@@ -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 @@ class instant_voucher(osv.TransientModel):
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 @@ class instant_voucher(osv.TransientModel):
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 @@ class instant_voucher(osv.TransientModel):
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 @@ class instant_voucher(osv.TransientModel):
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 @@ class instant_voucher(osv.TransientModel):
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 @@ class instant_voucher(osv.TransientModel):
# 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:

View File

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

View File

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