mirror of
https://github.com/OCA/bank-payment.git
synced 2025-02-02 10:37:31 +02:00
[MRG] Migration of payment modules
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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):
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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."))
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
|
||||
@@ -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),
|
||||
]),
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
),
|
||||
}
|
||||
|
||||
@@ -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'}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
43
account_banking_payment/view/payment_mode.xml
Normal file
43
account_banking_payment/view/payment_mode.xml
Normal 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>
|
||||
@@ -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"/>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
|
||||
57
account_direct_debit/migrations/7.0.2/pre-migration.py
Normal file
57
account_direct_debit/migrations/7.0.2/pre-migration.py
Normal 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'),
|
||||
]})
|
||||
@@ -1,3 +1,5 @@
|
||||
import account_payment
|
||||
import payment_line
|
||||
import account_move_line
|
||||
import account_invoice
|
||||
import payment_order_create
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
|
||||
152
account_direct_debit/model/payment_line.py
Normal file
152
account_direct_debit/model/payment_line.py
Normal 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")),
|
||||
}
|
||||
@@ -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
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
@@ -1 +1,2 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import payment_order
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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']"
|
||||
|
||||
@@ -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"/>
|
||||
|
||||
Reference in New Issue
Block a user