[IMP] Rebuild of all bank statment stuffs. This commit is more a backup cause it is under hard devs

(lp:c2c-financial-addons/6.1 rev 24.1.17)
This commit is contained in:
Joël Grand-Guillaume
2012-06-12 16:21:34 +02:00
parent 523093e2a2
commit 6360ac7369
24 changed files with 860 additions and 546 deletions

View File

@@ -27,13 +27,16 @@ logger = netsvc.Logger()
from openerp.osv.orm import Model, fields
class AccountStatementProfil(Model):
"""A Profile will contain all infos related to the type of
bank statement, and related generated entries. It define the
journal to use, the partner and commision account and so on."""
_name = "account.statement.profil"
_description = "Statement Profil"
_columns = {
'name': fields.char('Name', size=128, required=True),
'partner_id': fields.many2one('res.partner',
'Credit insitute partner',
'Bank/Payment Office partner',
help="Put a partner if you want to have it on the commission move (and optionaly\
on the counterpart of the intermediate/banking move if you tic the corresponding checkbox)."),
'journal_id': fields.many2one('account.journal',
@@ -57,6 +60,10 @@ class AccountStatementProfil(Model):
help="Tic that box if you want OpenERP to control the start/end balance\
before confirming a bank statement. If don't ticked, no balance control will be done."
),
'bank_statement_prefix': fields.char('Bank Statement Prefix', size=32),
'bank_statement_ids': fields.one2many('account.bank.statement', 'profile_id', 'Bank Statement Imported'),
}
_defaults = {}
@@ -73,43 +80,40 @@ class AccountStatementProfil(Model):
class AccountBankSatement(Model):
"""A kind of bank statement for intermediate move between customer and real bank, used
for manageing check, payment office like paypal or marketplace like amazon.
We inherit account.bank.statement because it's a very close object with only some
difference. But we want some method to be completely different, so we create a new object."""
"""We improve the bank statement class mostly for :
- Removing the period and compute it from the date of each line.
- Allow to remove the balance check depending on the chosen profil
- Report errors on confirmation all at once instead of crashing onr by one
- Add a profil notion that can change the generated entries on statement
confirmation.
For this, we'll had to override quite some long method and we'll need to maintain
them up to date. Changes are point up by '#Chg' comment."""
_inherit = "account.bank.statement"
_columns = {
'import_config_id': fields.many2one('account.statement.profil',
'profile_id': fields.many2one('account.statement.profil',
'Profil', required=True, states={'draft': [('readonly', False)]}),
'credit_partner_id': fields.related(
'import_config_id',
'profile_id',
'partner_id',
type='many2one',
relation='res.partner',
string='Financial Partner',
store=True, readonly=True),
'balance_check': fields.related(
'import_config_id',
'profile_id',
'balance_check',
type='boolean',
string='Balance check',
store=True, readonly=True),
'journal_id': fields.related(
'import_config_id',
'profile_id',
'journal_id',
type='many2one',
relation='account.journal',
string='Journal',
store=True, readonly=True),
# 'line_ids': fields.one2many('account.bank.statement.line',
# 'statement_id', 'Statement lines',
# states={'confirm':[('readonly', True)]}),
# 'move_line_ids': fields.one2many('account.move.line', 'statement_treasury_id',
# 'Entry lines', states={'confirm':[('readonly',True)]}),
# Redefine this field to avoid his computation (it is a function field on bank statement)
# 'balance_end': fields.dummy(string="Computed Balance"),
'period_id': fields.many2one('account.period', 'Period', required=False, readonly=True),
}
@@ -120,15 +124,15 @@ class AccountBankSatement(Model):
def create(self, cr, uid, vals, context=None):
"""Need to pass the journal_id in vals anytime because of account.cash.statement
that need it."""
if 'import_config_id' in vals:
if 'profile_id' in vals:
profil_obj = self.pool.get('account.statement.profil')
profile = profil_obj.browse(cr,uid,vals['import_config_id'],context)
profile = profil_obj.browse(cr,uid,vals['profile_id'],context)
vals['journal_id'] = profile.journal_id.id
return super(AccountBankSatement, self).create(cr, uid, vals, context=context)
def _get_period(self, cursor, uid, date, context=None):
'''
Find matching period for date, used in thestatement line creation.
Find matching period for date, used in the statement line creation.
'''
period_obj = self.pool.get('account.period')
periods = period_obj.find(cursor, uid, dt=date, context=context)
@@ -154,6 +158,22 @@ class AccountBankSatement(Model):
(_check_company_id, 'The journal and period chosen have to belong to the same company.', ['journal_id','period_id']),
]
def button_cancel(self, cr, uid, ids, context={}):
"""We cancel the related move, delete them and finally put the
statement in draft state."""
done = []
for st in self.browse(cr, uid, ids, context=context):
if st.state=='draft':
continue
ids = []
for line in st.line_ids:
for move in line.move_ids:
move.button_cancel(context=context)
move.unlink(context=context)
done.append(st.id)
self.write(cr, uid, done, {'state':'draft'}, context=context)
return True
def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, st_line_number, context=None):
"""Override a large portion of the code to compute the periode for each line instead of
taking the period of the whole statement.
@@ -165,7 +185,7 @@ class AccountBankSatement(Model):
- If partner_id is set, we'll us it for the commission (when imported throufh the wizard)
- If partner_id is set and force_partner_on_bank is ticked, we'll let the partner of each line
for the debit line, but we'll change it on the credit move line for the choosen partner_id
=> This will ease the reconsiliation process with the bank as the partner will match the bank
=> This will ease the reconciliation process with the bank as the partner will match the bank
statement line
"""
if context is None:
@@ -246,8 +266,8 @@ class AccountBankSatement(Model):
amount_currency = st_line.amount
currency_id = st.currency.id
# GET THE RIGHT PARTNER ACCORDING TO THE CHOSEN PROFIL # Chg
if st.import_config_id.force_partner_on_bank: # Chg
bank_parrtner_id = st.import_config_id.partner_id.id # Chg
if st.profile_id.force_partner_on_bank: # Chg
bank_parrtner_id = st.profile_id.partner_id.id # Chg
else: # Chg
bank_parrtner_id = ((st_line.partner_id) and st_line.partner_id.id) or False # Chg
@@ -280,16 +300,19 @@ class AccountBankSatement(Model):
account_move_obj.post(cr, uid, [move_id], context=context)
return move_id
def _get_st_number_period(self, cr, uid, date, journal_sequence_id):
def _get_st_number_period_profil(self, cr, uid, date, profile_id, journal_sequence_id):
"""Retrieve the name of bank statement from sequence, according to the period
corresponding to the date passed in args"""
corresponding to the date passed in args. Add a prefix if set in the profil."""
year = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id
profile = self.pool.get('account.statement.profil').browse(cr,uid, profile_id)
c = {'fiscalyear_id': year}
obj_seq = self.pool.get('ir.sequence')
if journal_sequence_id:
st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c)
else:
st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement', context=c)
if profile.bank_statement_prefix:
st_number = profile.bank_statement_prefix + st_number
return st_number
def button_confirm_bank(self, cr, uid, ids, context=None):
@@ -319,12 +342,7 @@ class AccountBankSatement(Model):
else:
# Begin Changes
seq_id = st.journal_id.sequence_id and st.journal_id.sequence_id.id or False
st_number = self._get_st_number_period(cr, uid, st.date, seq_id)
# c = {'fiscalyear_id': st.period_id.fiscalyear_id.id}
# if st.journal_id.sequence_id:
# st_number = obj_seq.next_by_id(cr, uid, st.journal_id.sequence_id.id, context=c)
# else:
# st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement', context=c)
st_number = self._get_st_number_period_profil(cr, uid, st.date, st.profile_id.id, seq_id)
# End Changes
for line in st.move_line_ids:
if line.state <> 'valid':
@@ -359,24 +377,30 @@ class AccountBankSatement(Model):
self.log(cr, uid, st.id, _('Statement %s is confirmed, journal items are created.') % (st_number,))
return self.write(cr, uid, ids, {'state':'confirm'}, context=context)
def get_partner_from_so(self, cursor, uid,transaction_id):
"""Look for the SO that has the given transaction_id, if not
found, try to match the SO name instead. If still nothing,
return False"""
so_obj = self.pool.get('sale.order')
so_id = so_obj.search(cursor, uid, [('transaction_id', '=', transaction_id)])
if so_id and len(so_id) == 1:
return so_obj.browse(cursor, uid, so_id[0]).partner_id.id
def get_account_for_counterpart(self, cursor, uid,
amount, account_receivable, account_payable):
"""Give the amount, payable and receivable account (that can be found using
get_default_pay_receiv_accounts).
Return the default account to be used by statement line as the counterpart
of the journal account depending on the amount"""
account_id = False
if amount >= 0:
account_id = account_receivable
else:
so_id2 = so_obj.search(cursor, uid, [('name', '=', transaction_id)])
if so_id2 and len(so_id2) == 1:
return so_obj.browse(cursor, uid, so_id2[0]).partner_id.id
return False
account_id = account_payable
if not account_id:
raise osv.except_osv(
_('Can not determine account'),
_('Please ensure that minimal properties are set')
)
return account_id
def get_default_accounts(self, cursor, uid, receivable_account_id, context=None):
"""We try to determine default accounts if not receivable_account_id set, otherwise
take it for both receivable and payable account"""
def get_default_pay_receiv_accounts(self, cursor, uid, receivable_account_id, context=None):
"""We try to determine default payable/receivable accounts to be used as counterpart
of the journal one.
If receivable_account_id is set (from the profil), take it as receivable/payable
account. Otherwise take it from the property of the partner."""
account_receivable = False
account_payable = False
if receivable_account_id:
@@ -410,21 +434,6 @@ class AccountBankSatement(Model):
account_payable = erp_property.value_reference.id
return account_receivable, account_payable
def _get_account_id(self, cursor, uid,
amount, account_receivable, account_payable):
"return the default account to be used by statement line"
account_id = False
if amount >= 0:
account_id = account_receivable
else:
account_id = account_payable
if not account_id:
raise osv.except_osv(
_('Can not determine account'),
_('Please ensure that minimal properties are set')
)
return account_id
def balance_check(self, cr, uid, st_id, journal_type='bank', context=None):
"""Balance check depends on the profil. If no check for this profil is required,
return True"""
@@ -433,25 +442,11 @@ class AccountBankSatement(Model):
return super(AccountBankSatement,self).balance_check(cr, uid, st_id, journal_type, context)
else:
return True
def _get_value_from_import_config(self, cr, uid, import_config_id):
"""Return a dict with with values taken from the given config.
e.g. (journal_id, partner_id, commission_account_id, mode, forced_account_id)
"""
# Get variable from config
import_config = self.pool.get("account.statement.profil").browse(cr,uid,import_config_id)
forced_account_id = import_config.receivable_account_id and import_config.receivable_account_id.id or False
journal_id = import_config.journal_id and import_config.journal_id.id or False
partner_id = import_config.partner_id and import_config.partner_id.id or False
commission_account_id = import_config.commission_account_id.id
commission_analytic_id = import_config.commission_analytic_id and import_config.commission_analytic_id.id or False
force_partner_on_bank = import_config.force_partner_on_bank
return journal_id, partner_id, commission_account_id, commission_analytic_id, forced_account_id, force_partner_on_bank
def onchange_imp_config_id(self, cr, uid, ids, import_config_id, context=None):
if not import_config_id:
def onchange_imp_config_id(self, cr, uid, ids, profile_id, context=None):
if not profile_id:
return {}
import_config = self.pool.get("account.statement.profil").browse(cr,uid,import_config_id)
import_config = self.pool.get("account.statement.profil").browse(cr,uid,profile_id)
journal_id = import_config.journal_id.id
account_id = import_config.journal_id.default_debit_account_id.id
credit_partner_id = import_config.partner_id and import_config.partner_id.id or False
@@ -460,132 +455,6 @@ class AccountBankSatement(Model):
'credit_partner_id':credit_partner_id,
}}
def credit_statement_import(self, cursor, uid, ids,
import_config_id,
file_stream,
ftype="csv",
context=None):
"Create statement from file stream encoded in base 64"
context = context or {}
statement_obj = self.pool.get('account.bank.statement')
statement_line_obj = self.pool.get('account.bank.statement.line')
attachment_obj = self.pool.get('ir.attachment')
# Get variable from config
journal_id, partner_id, commission_account_id, commission_analytic_id, \
forced_account_id, force_partner_on_bank = self._get_value_from_import_config(cursor,uid,import_config_id)
account_receivable, account_payable = self.get_default_accounts(cursor, uid, forced_account_id)
##Order of cols does not matter but first row has to be header
keys = ['transaction_id', 'label', 'date', 'amount', 'commission_amount']
#required_values = ['transaction_id', 'amount', 'commission_amount']
convertion_dict = {
'transaction_id': unicode,
'label': unicode,
'date': datetime.datetime,
'amount': float,
'commission_amount': float
}
f_parser = FileParser(file_stream,
keys_to_validate=keys,
decode_base_64=True,
ftype=ftype)
statement_lines = f_parser.parse()
statement_lines = f_parser.cast_rows(statement_lines, convertion_dict)
journal = self.pool.get('account.journal').browse(cursor, uid, journal_id)
statement_id = statement_obj.create(cursor,
uid,
{ 'import_config_id':import_config_id,
'journal_id': journal_id,
'journal_id': journal_id,
'credit_partner_id': partner_id,
'statement_type': 'credit_partner',
},
context)
commission_global_amount = 0.0
if not journal.default_debit_account_id \
or not journal.default_credit_account_id:
raise osv.except_osv(
_("Missing default account on journal %s")%(journal.name),
_("Please correct the journal"))
try:
for line in statement_lines:
line_partner_id = False
line_to_reconcile = False
# We ensure that required values of the line are set
# for val in required_values:
# if not line.get(val, False) and line.get(val, False) != 0.0:
# raise osv.except_osv(
# _("Field %s not set for line %s")%(str(line),),
# _("Please correct the file"))
commission_global_amount += line.get('commission_amount', 0.0)
values = {
'name': "IN %s %s"%(line['transaction_id'],
line.get('label', '')),
'date': line.get('date', datetime.datetime.now().date()),
'amount': line['amount'],
'ref': "TID_%s"%(line['transaction_id'],),
'type': 'customer',
'statement_id': statement_id,
#'account_id': journal.default_debit_account_id
}
values['account_id'] = self._get_account_id(
cursor,
uid,
line['amount'],
account_receivable,
account_payable
)
if not line_partner_id:
line_partner_id = self.get_partner_from_so(cursor,
uid, line['transaction_id'])
values['partner_id'] = line_partner_id
# we finally create the line in system
statement_line_obj.create(cursor, uid, values, context=context)
# we create commission line
if commission_global_amount:
comm_values = {
'name': 'IN '+ _('Commission line'),
'date': datetime.datetime.now().date(),
'amount': commission_global_amount,
'partner_id': partner_id,
'type': 'general',
'statement_id': statement_id,
'account_id': commission_account_id,
'ref': 'commission',
'analytic_account_id': commission_analytic_id
}
statement_line_obj.create(cursor, uid,
comm_values,
context=context)
attachment_obj.create(
cursor,
uid,
{
'name': 'statement file',
'datas': file_stream,
'datas_fname': "%s.%s"%(datetime.datetime.now().date(),
ftype),
'res_model': 'account.bank.statement',
'res_id': statement_id,
},
context=context
)
except Exception, exc:
logger.notifyChannel("Statement import",
netsvc.LOG_ERROR,
_("Statement can not be created %s") %(exc,))
statement_obj.unlink(cursor, uid, [statement_id])
raise exc
return statement_id
class AccountBankSatementLine(Model):
_inherit = "account.bank.statement.line"
@@ -596,11 +465,7 @@ class AccountBankSatementLine(Model):
return periods and periods[0] or False
_columns = {
# 'statement_id': fields.many2one('account.bank.statement', 'Statement',
# select=True, required=True, ondelete='cascade'),
# 'move_ids': fields.many2many('account.move',
# 'account_treasury_statement_line_move_rel', 'statement_line_id','move_id',
# 'Moves'),
# Set them as required
'ref': fields.char('Reference', size=32, required=True),
'period_id': fields.many2one('account.period', 'Period', required=True),
}
@@ -608,18 +473,11 @@ class AccountBankSatementLine(Model):
'period_id': _get_period,
}
# WARNING => Crash cause the super method here calls onchange_type => and then
# we don't call it from the good model.... => We'll need to override the complete method here
def onchange_partner_id(self, cr, uid, ids, partner_id, import_config_id, context=None):
# import pdb;pdb.set_trace()
# if context is None:
# context = {}
# res = super(AccountTreasurySatementLine,self).onchange_partner_id(cr, uid, ids, partner_id, context)
# c = self.pool.get("account.statement.profil").browse(cr,uid,import_config_id)
# acc_id=c.receivable_account_id and c.receivable_account_id.id or False
# if acc_id:
# res['value'].update({'account_id':acc_id})
# return res
def onchange_partner_id(self, cr, uid, ids, partner_id, profile_id, context=None):
"""When changing the partner, we'll now need to look in the profil to determine which
account to use."""
obj_partner = self.pool.get('res.partner')
if context is None:
context = {}
@@ -635,34 +493,26 @@ class AccountBankSatementLine(Model):
type = 'supplier'
if part.customer == True:
type = 'customer'
res_type = self.onchange_type(cr, uid, ids, partner_id, type, import_config_id, context=context)
res_type = self.onchange_type(cr, uid, ids, partner_id, type, profile_id, context=context)
if res_type['value'] and res_type['value'].get('account_id', False):
res = {'value': {'type': type, 'account_id': res_type['value']['account_id']}}
else:
res = {'value': {'type': type}}
c = self.pool.get("account.statement.profil").browse(cr,uid,import_config_id)
c = self.pool.get("account.statement.profil").browse(cr,uid,profile_id)
acc_id=c.receivable_account_id and c.receivable_account_id.id or False
if acc_id:
res['value'].update({'account_id':acc_id})
return res
# TOFIX
def onchange_type(self, cr, uid, line_id, partner_id, type, import_config_id, context=None):
# TOFIX: don't seems to work as expected
def onchange_type(self, cr, uid, line_id, partner_id, type, profile_id, context=None):
if context is None:
context = {}
res = super(AccountBankSatementLine,self).onchange_type(cr, uid, line_id, partner_id, type, context)
c = self.pool.get("account.statement.profil").browse(cr,uid,import_config_id)
c = self.pool.get("account.statement.profil").browse(cr,uid,profile_id)
acc_id=c.receivable_account_id and c.receivable_account_id.id or False
if acc_id:
res['value'].update({'account_id':acc_id})
return res
# class AccountMoveLine(Model):
# _inherit = "account.move.line"
#
# _columns = {
# 'statement_treasury_id': fields.many2one('account.bank.statement', 'Statement', help="The intermediate statement used for reconciliation", select=1),
# }