[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

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author: Nicolas Bessi # Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA # Copyright 2011-2012 Camptocamp SA
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
@@ -19,4 +19,4 @@
# #
############################################################################## ##############################################################################
import parser import statement

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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/>.
#
##############################################################################
{'name': "Bank statement base completion",
'version': '1.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal', #easy, normal, expert
'depends': ['account_statement_ext'],
'description': """
The goal of this module is to improve the basic bank statement, help dealing with huge volume of
reconciliation by providing basic rules to identify the partner of a bank statement line.
It will also take care of the chosen profile to make his work.
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [
'statement_view.xml',
],
'demo_xml': [],
'test': [],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
'active': False,
}

View File

@@ -0,0 +1,83 @@
# -*- encoding: utf-8 -*-
#################################################################################
# #
# Copyright (C) 2011 Akretion Sébastien BEAU <sebastien.beau@akretion.com> #
# #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU Affero General Public License as #
# published by the Free Software Foundation, either version 3 of the #
# License, or (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU Affero General Public License for more details. #
# #
# You should have received a copy of the GNU Affero General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. #
# #
#################################################################################
from osv import fields, osv
class res_partner(osv.osv):
"""Add a bank label on the partner so that we can use it to match
this partner when we found this in a statement line."""
_inherit = 'res.partner'
_columns = {
'bank_statement_label':fields.char('Bank Statement Label', size=100,
help="Enter the various label found on your bank statement separated by a ; If \
one of this label is include in the bank statement line, the partner will be automatically \
filled (as long as you use thi method in your statement profile)."),
}
# def get_partner_from_order_ref(self, cr, uid, order_ref, context=None):
# order_obj = self.pool.get('sale.order')
# so_id = order_obj.search(cr, uid, [('name', '=', order_ref)], context=context)
# if so_id:
# so = order_obj.browse(cr, uid, so_id, context=context)[0]
# return so.partner_id.id
# return False
#
# def get_partner_from_name(self, cr, uid, partner_name, context=None):
# #print 'try to get partner from name', partner_name
# return False
#
# def get_partner_from_email(self, cr, uid, partner_email, context=None):
# address_ids = self.pool.get('res.partner.address').search(cr, uid, [['email', '=', partner_email]], context=context)
# if address_ids:
# partner_id = self.search(cr, uid, [['address', 'in', address_ids]], context=context)
# return partner_id and partner_id[0]
# return False
def get_partner_from_label_based_on_bank_statement_label(self, cr, uid, label, context=None):
ids = self.search(cr, uid, [['bank_statement_label', '!=', False]], context=context)
for partner in self.browse(cr, uid, ids, context=context):
for partner_label in partner.bank_statement_label.split(';'):
if partner_label in label:
return partner.id
return False
def get_supplier_partner_from_label_based_on_name(self, cr, uid, label, context=None):
supplier_ids = self.search(cr, uid, [['supplier', '=', True]], context=context)
for partner in self.browse(cr, uid, supplier_ids, context=context):
if partner.name in label:
return partner.id
return False
def get_partner_account(self, cr, uid, id, amount, context=None):
partner = self.browse(cr, uid, id, context=context)
if partner.supplier and not partner.customer:
return partner.property_account_payable.id
if partner.customer and not partner.supplier:
return partner.property_account_receivable.id
if amount >0:
return partner.property_account_receivable.id
else:
return partner.property_account_payable.id
res_partner()

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<openerp>
<data>
<record id="bk_view_partner_form" model="ir.ui.view">
<field name="name">account_bank_statement_import.view.partner.form</field>
<field name="model">res.partner</field>
<field name="type">form</field>
<field name="priority">20</field>
<field name="inherit_id" ref="account.view_partner_property_form"/>
<field name="arch" type="xml">
<field name="property_account_payable" position="after">
<field name="bank_statement_label"/>
</field>
</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,201 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi, Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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 tools.translate import _
import netsvc
logger = netsvc.Logger()
from openerp.osv.orm import Model, fields
class AccountStatementProfil(Model):
_inherit = "account.statement.profil"
_columns={
# For now, we don't implement this features, but this would probably be there:
# 'auto_completion': fields.text('Auto Completion'),
# 'transferts_account_id':fields.many2one('account.account', 'Transferts Account'),
'rule_ids':fields.many2many('account.statement.completion.rule',
rel='account_statement_rule_statement_profile_to_rel',
'profile_id','rule_id',
'Related statement profiles'),
}
def find_partner_by_rules(self, cr, uid, ids, field_value, context=None):
"""This method will execute all rules, in their sequence order,
to match a partner for the given statement.line and return his id."""
if not context:
context={}
partner_id = False
for profile in self.browse(cr, uid, ids, context=context):
for rule in profile.rule_ids:
method_to_call = getattr(rule, rule.function_to_call)
partner_id = method_to_call(cr,uid,field_value,context)
if partner_id:
return partner_id
return partner_id
class AccountStatementCompletionRule(Model):
"""This will represent all the completion method that we can have to
fullfill the bank statement. You'll be able to extend them in you own module
and choose those to apply for every statement profile."""
_name = "account.statement.completion.rule"
_order = "sequence asc"
_get_functions = [('get_from_label_and_partner_field', 'From line label (based on partner field)'),\
('in', 'External -> OpenERP'), ('out', 'External <- OpenERP')]
_columns={
'sequence': fields.integer('Sequence'),
'name': fields.char('Name')
'profile_ids':fields.many2many('account.statement.profil',
rel='account_statement_rule_statement_profile_to_rel',
'rule_id', 'profile_id',
'Related statement profiles'),
'function_to_call': fields.selection(_get_functions, 'Type'),
}
def get_from_label_and_partner_field(self, cr, uid, field_value, context=None):
"""Match the partner based on the label field of the statement line
and the text defined in the 'bank_statement_label' field of the partner.
Remember that we can have values separated with ;"""
partner_obj = self.pool.get('res.partner')
ids = partner_obj.search(cr, uid, [['bank_statement_label', '!=', False]], context=context)
for partner in self.browse(cr, uid, ids, context=context):
for partner_label in partner.bank_statement_label.split(';'):
if partner_label in field_value:
return partner.id
return False
def get_from_label_and_partner_name(self, cr, uid, field_value, context=None):
"""Match the partner based on the label field of the statement line
and the name of the partner."""
supplier_ids = self.search(cr, uid, [['supplier', '=', True]], context=context)
sql = """SELECT id FROM res_partner WHERE name ilike """
for partner in self.browse(cr, uid, supplier_ids, context=context):
if partner.name in label:
return partner.id
return False
class AccountStatementLine(Model):
"""Add sparse field on the statement line to allow to store all the
bank infos that are given by an office. You can then add you own in your
module."""
_inherit = "account.bank.statement.line"
_columns={
# 'email_address': fields.char('Email', size=64),
# 'order_ref': fields.char('Order Ref', size=64),
# 'partner_name': fields.char('Partner Name', size=64),
#
# Only label for a start, but other module can add their own
'additionnal_bank_fields' : fields.serialized('Additionnal infos from bank', help="Used by completion and import system."),
'label': fields.sparse(type='char', string='Label',
serialization_field='additionnal_bank_fields'),
}
def auto_complete_line(self, cr, uid, line, context=None):
res={}
if not line.partner_id or line.account_id.id ==1:
partner_obj = self.pool.get('res.partner')
partner_id=False
if line.order_ref:
partner_id = partner_obj.get_partner_from_order_ref(cr, uid, line.order_ref, context=context)
if not partner_id and line.email_address:
partner_id = partner_obj.get_partner_from_email(cr, uid, line.email_address, context=context)
if not partner_id and line.partner_name:
partner_id = partner_obj.get_partner_from_name(cr, uid, line.partner_name, context=context)
if not partner_id and line.label:
partner_id = partner_obj.get_partner_from_label_based_on_bank_statement_label(cr, uid, line.label, context=context)
if partner_id:
res = {'partner_id': partner_id}
if context['auto_completion']:
#Build the space for expr
space = {
'self':self,
'cr':cr,
'uid':uid,
'line': line,
'res': res,
'context':context,
}
exec context['auto_completion'] in space
if space.get('result', False):
res.update(space['result'])
return res
class A(object):
def xx_toto():
print 'toto'
a = A()
funcs = ['yy_toto', 'xx_toto']
for i in funcs:
if hasattr(a, i):
to_call = getattr(a, i)
to_call()
else:
raise NameError('blblblb')
class AccountBankSatement(Model):
"""We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually entred.
"""
_inherit = "account.bank.statement"
def button_auto_completion(self, cr, uid, ids, context=None):
if not context:
context={}
stat_line_obj = self.pool.get('account.bank.statement.line')
for stat in self.browse(cr, uid, ids, context=context):
ctx = context.copy()
if stat.bank_statement_import_id:
ctx['partner_id'] = stat.bank_statement_import_id.partner_id.id
ctx['transferts_account_id'] = stat.bank_statement_import_id.transferts_account_id.id
ctx['credit_account_id'] = stat.bank_statement_import_id.credit_account_id.id
ctx['fee_account_id'] = stat.bank_statement_import_id.fee_account_id.id
ctx['auto_completion'] = stat.bank_statement_import_id.auto_completion
for line in stat.line_ids:
vals = stat_line_obj.auto_complete_line(cr, uid, line, context=ctx)
if not line.ref and not vals.get('ref', False):
vals['ref'] = stat.name
if vals:
stat_line_obj.write(cr, uid, line.id, vals, context=ctx)
return True
def auto_confirm(self, cr, uid, ids, context=None):
if not context:
context={}
ok=True
for stat in self.browse(cr, uid, ids, context=context):
for line in stat.line_ids:
if not line.partner_id or line.account_id.id == 1:
ok=False
continue
if ok:
self.button_confirm(cr, uid, [stat.id], context=context)
return True

View File

@@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="bank_statement_view_form" model="ir.ui.view">
<field name="name">account_bank_statement_import_base.bank_statement.view_form</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field eval="16" name="priority"/>
<field name="type">form</field>
<field name="arch" type="xml">
<data>
<xpath expr="/form/notebook/page/field[@name='line_ids']/form/field[@name='ref']" position="after">
<!-- <field name="partner_name" />
<field name="order_ref" />
<field name="email_address" /> -->
<field name="label" />
</xpath>
<xpath expr="/form/group[2]" position="attributes">
<attribute name="col">10</attribute>
</xpath>
<xpath expr="/form/group/field[@name='balance_end']" position="after">
<button name="button_auto_completion" string="Auto Completion" type="object" colspan="1"/>
</xpath>
</data>
</field>
</record>
</data>
</openerp>

View File

@@ -1,20 +1,22 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
############################################################################## ##############################################################################
# #
# Author Nicolas Bessi. Copyright Camptocamp SA # Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU Affero General Public License as
# the Free Software Foundation, either version 3 of the License, or # published by the Free Software Foundation, either version 3 of the
# (at your option) any later version. # License, or (at your option) any later version.
# #
# This program is distributed in the hope that it will be useful, # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of # but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details. # GNU Affero General Public License for more details.
# #
# You should have received a copy of the GNU General Public License # 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################## ##############################################################################
import import_statement
import statement

View File

@@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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/>.
#
##############################################################################
{'name': "Bank statement easy import",
'version': '1.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal', #easy, normal, expert
'depends': ['account_statement_ext','account_statement_base_completion'],
'description': """
The goal of this module is bring basic method and fields on bank statement to deal with
the importation of different bank and offices.
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [
],
'demo_xml': [],
'test': [],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
'active': False,
}

View File

@@ -0,0 +1,81 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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 tools.translate import _
import datetime
import netsvc
logger = netsvc.Logger()
from openerp.osv.orm import Model, fields
class AccountStatementProfil(Model):
_inherit = "account.statement.profil"
_columns = {
'launch_import_completion': fields.boolean("Launch completion after import",
help="Tic that box to automatically launch the completion on each imported\
file using this profile."),
'last_import_date': fields.date("Last Import Date"),
'rec_log': fields.text('log', readonly=True),
}
def launch_import_bank_statement(self, cr, uid, ids, context=None):
stat_obj = self.pool.get('account.bank.statement')
for id in ids:
logger = netsvc.Logger()
res = self.action_import_bank_statement(cr, uid, id, conteaccount_xt)
#autocomplete bank statement
stat_obj.button_auto_completion(cr, uid, res['crids'], context=context)
stat_obj.auto_confirm(cr, uid, res['crids'], context=context)
log = self.read(cr, uid, id, ['rec_log'], context=context)['rec_log']
log_line = log and log.split("\n") or []
log_line[0:0] = [datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ' : ' + str(len(res['crids'])) + _(' bank statement have been imported and ' + str(len(res['exist_ids'])) + _(' bank statement already exist'))]
log = "\n".join(log_line)
self.write(cr, uid, id, {'rec_log' : log}, context=context)
logger.notifyChannel('banck statement import', netsvc.LOG_INFO, "%s bank statement have been imported and %s bank statement already exist"%(len(res['crids']), len(res['exist_ids'])))
return True
def action_import_bank_statement(self, cr, uid, id, context=None):
'''not implemented in this module'''
return {}
def open_bank_statement(self, cr, uid, ids, context):
task = self.browse(cr, uid, ids, context=context)[0]
return {
'name': 'Bank ',
'view_type': 'form',
'view_mode': 'form',
'view_id': [252],
'res_model': self._name,
'type': 'ir.actions.act_window',
'nodestroy': True,
'target': 'current',
'res_id': self.read(cr, uid, ids, ['bank_statement_ids'],context=context)[0]['bank_statement_ids'],
}
class AccountBankSatement(Model):
_inherit = "account.bank.statement"

View File

@@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="statement_importer_view_form" model="ir.ui.view">
<field name="name">account.statement.profil.view</field>
<field name="model">account.statement.profil</field>
<field name="inherit_id" ref="account_statement_ext.statement_importer_view_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<field name="bank_statement_prefix" position="after">
<separator colspan="4" String="Import related infos"/>
<field name="launch_import_completion"/>
<field name="last_import_date"/>
<button icon="gtk-ok" name="launch_import_bank_statement" colspan = "2" string="Import Bank Statement" type="object"/>
<separator colspan="4" String="Import Logs"/>
<field name="rec_log" colspan="4" nolabel="1"/>
</field>
</field>
</record>
<!-- To port -->
<!-- <record id="action_bank_statement_import" model="ir.actions.act_window">
<field name="name">account bank statement import</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">account.bank.statement.import</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="context">{'wizard_object' : 'account.bank.statement.import', 'function' : 'action_import_bank_statement', 'object_link' : 'account.bank.statement.import' }</field>
</record>
<menuitem action="action_bank_statement_import" id="menu_account_bank_import" parent="account.menu_finance"/>
<record id="ir_action_create_scheduler_in_easy_reconcile" model="ir.values">
<field name="key2">client_action_multi</field>
<field name="model">account.bank.statement.import</field>
<field name="name">Create a Scheduler</field>
<field eval="'ir.actions.act_window,%d'%ref('base_scheduler_creator.action_scheduler_creator_wizard')" name="value"/>
<field eval="True" name="object"/>
</record> -->
</data>
</openerp>

View File

@@ -23,3 +23,4 @@ import file_parser
import wizard import wizard
import statement import statement
import report import report
import account

View File

@@ -27,15 +27,16 @@
'complexity': 'normal', #easy, normal, expert 'complexity': 'normal', #easy, normal, expert
'depends': ['base_transaction_id'], 'depends': ['base_transaction_id'],
'description': """ 'description': """
The goal of this module is to help dealing with huge volume of reconciliation through The goal of this module is to improve the basic bank statement, help dealing with huge volume of
payment offices like Paypal, Lazer, Visa, Amazon and so on. It's mostly used for reconciliation through payment offices like Paypal, Lazer, Visa, Amazon and so on.
E-commerce but can be usefule for other use cases as it introduce a notion of profil
on the bank statement to have more control on the generated entries. It will be mostly used for E-commerce but can be usefule for other use cases as it introduce a
notion of profil on the bank statement to have more control on the generated entries. It will
be the base for all new features developped to improve the reconciliation process.
Features: Features:
1) This module improves the bank statement that allow and you to import your bank transactions with 1) This module improves the bank statement that allow and you to define profile for each
a standard .csv or .xls file (you'll find it in the 'data' folder). You can now define profile for each
Office or Bank that will generate the entries based on some criteria. You can setup: Office or Bank that will generate the entries based on some criteria. You can setup:
- Account commission and partner relation - Account commission and partner relation
@@ -51,15 +52,19 @@
4) Remove the period on the bank statement, and compute it for each line based on their date instead. 4) Remove the period on the bank statement, and compute it for each line based on their date instead.
5) Provide a standard import format to create and fullfill a bank statement from a .csv or .xls file. For 5) Cancelling a bank statement is much more easy and will cancel all related entries, unreconcile them,
and finally delete it.
6) Add the ID in entries view so that you can easily filter on a statement ID to reconcile all related
entries at once (e.g. one statement (ID 100) for paypal on an intermediate account, and then another for
the bank on the bank account. You can then manually reconcile all the line from the first one with
one line of the second by finding them through the statement ID.)
""", """,
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'init_xml': [], 'init_xml': [],
'update_xml': [ 'update_xml': [
'statement_view.xml', 'statement_view.xml',
'wizard/import_statement_view.xml',
'report/bank_statement_webkit_header.xml', 'report/bank_statement_webkit_header.xml',
'report.xml', 'report.xml',
], ],

View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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/>.
#
##############################################################################
import netsvc
logger = netsvc.Logger()
from openerp.osv.orm import Model, fields
class account_move(osv.osv):
_inherit='account.move'
def unlink(self, cr, uid, ids, context=None):
"""Delete the reconciliation when we delete the moves. This
allow an easier way of cancelling the bank statement."""
for move in self.browse(cr, uid, ids, context=context):
for move_line in move.line_id:
if move_line.reconcile_id:
move_line.reconcile_id.unlink(context=context)
return super(account_move, self).unlink(cr, uid, ids, context=context)
account_move()

View File

@@ -1,4 +0,0 @@
"transaction_id";"date";"amount";"commission_amount";"label"
50969286;2011-03-07 13:45:14;118.4;-11.84;"label a"
51065326;2011-03-05 13:45:14;189;-15.12;"label b"
51179306;2011-03-02 17:45:14;189;-15.12;"label c"
1 transaction_id date amount commission_amount label
2 50969286 2011-03-07 13:45:14 118.4 -11.84 label a
3 51065326 2011-03-05 13:45:14 189 -15.12 label b
4 51179306 2011-03-02 17:45:14 189 -15.12 label c

View File

@@ -1,131 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Copyright Camptocamp SA
# Author Nicolas Bessi
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
"""
Parser for csv or xml for file containing credit transfert data from
financial institure as VISA
"""
from tools.translate import _
import base64
import csv
import tempfile
import datetime
try:
import xlrd
except:
raise Exception(_('Please install python lib xlrd'))
def UnicodeDictReader(utf8_data, **kwargs):
csv_reader = csv.DictReader(utf8_data, **kwargs)
for row in csv_reader:
yield dict([(key, unicode(value, 'utf-8')) for key, value in row.iteritems()])
# TODO extract into a lib helper module
class FileParser(object):
def __init__(self, filebuffer, keys_to_validate=None, decode_base_64=True, ftype='csv'):
if ftype in ('csv', 'xls'):
self.ftype = ftype
else:
raise Exception(_('Invalide file type %s. please use csv or xls') % (ftype))
self.keys_to_validate = keys_to_validate
self.decode_base_64 = decode_base_64
if filebuffer:
self.filebuffer = filebuffer
else:
raise Exception(_('No buffer file'))
def parse(self):
"launch parsing of csv or xls"
if self.decode_base_64:
self._decode_64b_stream()
res = None
if self.ftype == 'csv':
res = self.parse_csv()
else:
res = self.parse_xls()
if self.keys_to_validate:
self._validate_column(res, self.keys_to_validate)
return res
def _decode_64b_stream(self):
self.filebuffer = base64.b64decode(self.filebuffer)
return self.filebuffer
def _validate_column(self, array_of_dict, cols):
parsed_cols = array_of_dict[0].keys()
for col in cols:
if col not in parsed_cols:
raise Exception(_('col %s not present in file') % (col))
def parse_csv(self, delimiter=';'):
"return an array of dict from csv file"
csv_file = tempfile.NamedTemporaryFile()
csv_file.write(self.filebuffer)
# We ensure that cursor is at beginig of file
csv_file.seek(0)
reader = UnicodeDictReader(
open(csv_file.name).readlines(),
delimiter=delimiter
)
return [x for x in reader]
def parse_xls(self):
"return an array of dict from csv file"
wb_file = tempfile.NamedTemporaryFile()
wb_file.write(self.filebuffer)
# We ensure that cursor is at beginig of file
wb_file.seek(0)
wb = xlrd.open_workbook(wb_file.name)
sheet = wb.sheet_by_index(0)
header = sheet.row_values(0)
res = []
for rownum in range(1, sheet.nrows):
res.append(dict(zip(header, sheet.row_values(rownum))))
try:
wb_file.close()
except Exception, e:
pass #file is allready closed
return res
def _from_csv(self, result_set, conversion_rules):
for line in result_set:
for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime:
date_string = line[rule].split(' ')[0]
line[rule] = datetime.datetime.strptime(date_string,
'%Y-%m-%d')
else:
line[rule] = conversion_rules[rule](line[rule])
return result_set
def _from_xls(self, result_set, conversion_rules):
for line in result_set:
for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime:
t_tuple = xlrd.xldate_as_tuple(line[rule], 1)
line[rule] = datetime.datetime(*t_tuple)
else:
line[rule] = conversion_rules[rule](line[rule])
return result_set
def cast_rows(self, result_set, conversion_rules):
func = getattr(self, '_from_%s'%(self.ftype))
return func(result_set, conversion_rules)

View File

@@ -27,13 +27,16 @@ logger = netsvc.Logger()
from openerp.osv.orm import Model, fields from openerp.osv.orm import Model, fields
class AccountStatementProfil(Model): 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" _name = "account.statement.profil"
_description = "Statement Profil" _description = "Statement Profil"
_columns = { _columns = {
'name': fields.char('Name', size=128, required=True), 'name': fields.char('Name', size=128, required=True),
'partner_id': fields.many2one('res.partner', '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\ 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)."), on the counterpart of the intermediate/banking move if you tic the corresponding checkbox)."),
'journal_id': fields.many2one('account.journal', '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\ 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." 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 = {} _defaults = {}
@@ -73,43 +80,40 @@ class AccountStatementProfil(Model):
class AccountBankSatement(Model): class AccountBankSatement(Model):
"""A kind of bank statement for intermediate move between customer and real bank, used """We improve the bank statement class mostly for :
for manageing check, payment office like paypal or marketplace like amazon. - Removing the period and compute it from the date of each line.
We inherit account.bank.statement because it's a very close object with only some - Allow to remove the balance check depending on the chosen profil
difference. But we want some method to be completely different, so we create a new object.""" - 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" _inherit = "account.bank.statement"
_columns = { _columns = {
'import_config_id': fields.many2one('account.statement.profil', 'profile_id': fields.many2one('account.statement.profil',
'Profil', required=True, states={'draft': [('readonly', False)]}), 'Profil', required=True, states={'draft': [('readonly', False)]}),
'credit_partner_id': fields.related( 'credit_partner_id': fields.related(
'import_config_id', 'profile_id',
'partner_id', 'partner_id',
type='many2one', type='many2one',
relation='res.partner', relation='res.partner',
string='Financial Partner', string='Financial Partner',
store=True, readonly=True), store=True, readonly=True),
'balance_check': fields.related( 'balance_check': fields.related(
'import_config_id', 'profile_id',
'balance_check', 'balance_check',
type='boolean', type='boolean',
string='Balance check', string='Balance check',
store=True, readonly=True), store=True, readonly=True),
'journal_id': fields.related( 'journal_id': fields.related(
'import_config_id', 'profile_id',
'journal_id', 'journal_id',
type='many2one', type='many2one',
relation='account.journal', relation='account.journal',
string='Journal', string='Journal',
store=True, readonly=True), 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), '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): def create(self, cr, uid, vals, context=None):
"""Need to pass the journal_id in vals anytime because of account.cash.statement """Need to pass the journal_id in vals anytime because of account.cash.statement
that need it.""" that need it."""
if 'import_config_id' in vals: if 'profile_id' in vals:
profil_obj = self.pool.get('account.statement.profil') 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 vals['journal_id'] = profile.journal_id.id
return super(AccountBankSatement, self).create(cr, uid, vals, context=context) return super(AccountBankSatement, self).create(cr, uid, vals, context=context)
def _get_period(self, cursor, uid, date, context=None): 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') period_obj = self.pool.get('account.period')
periods = period_obj.find(cursor, uid, dt=date, context=context) 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']), (_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): 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 """Override a large portion of the code to compute the periode for each line instead of
taking the period of the whole statement. 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, 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 - 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 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 statement line
""" """
if context is None: if context is None:
@@ -246,8 +266,8 @@ class AccountBankSatement(Model):
amount_currency = st_line.amount amount_currency = st_line.amount
currency_id = st.currency.id currency_id = st.currency.id
# GET THE RIGHT PARTNER ACCORDING TO THE CHOSEN PROFIL # Chg # GET THE RIGHT PARTNER ACCORDING TO THE CHOSEN PROFIL # Chg
if st.import_config_id.force_partner_on_bank: # Chg if st.profile_id.force_partner_on_bank: # Chg
bank_parrtner_id = st.import_config_id.partner_id.id # Chg bank_parrtner_id = st.profile_id.partner_id.id # Chg
else: # Chg else: # Chg
bank_parrtner_id = ((st_line.partner_id) and st_line.partner_id.id) or False # 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) account_move_obj.post(cr, uid, [move_id], context=context)
return move_id 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 """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 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} c = {'fiscalyear_id': year}
obj_seq = self.pool.get('ir.sequence') obj_seq = self.pool.get('ir.sequence')
if journal_sequence_id: if journal_sequence_id:
st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c) st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c)
else: else:
st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement', context=c) 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 return st_number
def button_confirm_bank(self, cr, uid, ids, context=None): def button_confirm_bank(self, cr, uid, ids, context=None):
@@ -319,12 +342,7 @@ class AccountBankSatement(Model):
else: else:
# Begin Changes # Begin Changes
seq_id = st.journal_id.sequence_id and st.journal_id.sequence_id.id or False 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) st_number = self._get_st_number_period_profil(cr, uid, st.date, st.profile_id.id, 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)
# End Changes # End Changes
for line in st.move_line_ids: for line in st.move_line_ids:
if line.state <> 'valid': 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,)) 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) return self.write(cr, uid, ids, {'state':'confirm'}, context=context)
def get_partner_from_so(self, cursor, uid,transaction_id): def get_account_for_counterpart(self, cursor, uid,
"""Look for the SO that has the given transaction_id, if not amount, account_receivable, account_payable):
found, try to match the SO name instead. If still nothing, """Give the amount, payable and receivable account (that can be found using
return False""" get_default_pay_receiv_accounts).
so_obj = self.pool.get('sale.order') Return the default account to be used by statement line as the counterpart
so_id = so_obj.search(cursor, uid, [('transaction_id', '=', transaction_id)]) of the journal account depending on the amount"""
if so_id and len(so_id) == 1: account_id = False
return so_obj.browse(cursor, uid, so_id[0]).partner_id.id if amount >= 0:
account_id = account_receivable
else: else:
so_id2 = so_obj.search(cursor, uid, [('name', '=', transaction_id)]) account_id = account_payable
if so_id2 and len(so_id2) == 1: if not account_id:
return so_obj.browse(cursor, uid, so_id2[0]).partner_id.id raise osv.except_osv(
return False _('Can not determine account'),
_('Please ensure that minimal properties are set')
)
return account_id
def get_default_pay_receiv_accounts(self, cursor, uid, receivable_account_id, context=None):
def get_default_accounts(self, cursor, uid, receivable_account_id, context=None): """We try to determine default payable/receivable accounts to be used as counterpart
"""We try to determine default accounts if not receivable_account_id set, otherwise of the journal one.
take it for both receivable and payable account"""
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_receivable = False
account_payable = False account_payable = False
if receivable_account_id: if receivable_account_id:
@@ -410,21 +434,6 @@ class AccountBankSatement(Model):
account_payable = erp_property.value_reference.id account_payable = erp_property.value_reference.id
return account_receivable, account_payable 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): 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, """Balance check depends on the profil. If no check for this profil is required,
return True""" return True"""
@@ -433,25 +442,11 @@ class AccountBankSatement(Model):
return super(AccountBankSatement,self).balance_check(cr, uid, st_id, journal_type, context) return super(AccountBankSatement,self).balance_check(cr, uid, st_id, journal_type, context)
else: else:
return True 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): def onchange_imp_config_id(self, cr, uid, ids, profile_id, context=None):
if not import_config_id: if not profile_id:
return {} 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 journal_id = import_config.journal_id.id
account_id = import_config.journal_id.default_debit_account_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 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, '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): class AccountBankSatementLine(Model):
_inherit = "account.bank.statement.line" _inherit = "account.bank.statement.line"
@@ -596,11 +465,7 @@ class AccountBankSatementLine(Model):
return periods and periods[0] or False return periods and periods[0] or False
_columns = { _columns = {
# 'statement_id': fields.many2one('account.bank.statement', 'Statement', # Set them as required
# select=True, required=True, ondelete='cascade'),
# 'move_ids': fields.many2many('account.move',
# 'account_treasury_statement_line_move_rel', 'statement_line_id','move_id',
# 'Moves'),
'ref': fields.char('Reference', size=32, required=True), 'ref': fields.char('Reference', size=32, required=True),
'period_id': fields.many2one('account.period', 'Period', required=True), 'period_id': fields.many2one('account.period', 'Period', required=True),
} }
@@ -608,18 +473,11 @@ class AccountBankSatementLine(Model):
'period_id': _get_period, '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): def onchange_partner_id(self, cr, uid, ids, partner_id, profile_id, context=None):
# import pdb;pdb.set_trace() """When changing the partner, we'll now need to look in the profil to determine which
# if context is None: account to use."""
# 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
obj_partner = self.pool.get('res.partner') obj_partner = self.pool.get('res.partner')
if context is None: if context is None:
context = {} context = {}
@@ -635,34 +493,26 @@ class AccountBankSatementLine(Model):
type = 'supplier' type = 'supplier'
if part.customer == True: if part.customer == True:
type = 'customer' 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): if res_type['value'] and res_type['value'].get('account_id', False):
res = {'value': {'type': type, 'account_id': res_type['value']['account_id']}} res = {'value': {'type': type, 'account_id': res_type['value']['account_id']}}
else: else:
res = {'value': {'type': type}} 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 acc_id=c.receivable_account_id and c.receivable_account_id.id or False
if acc_id: if acc_id:
res['value'].update({'account_id':acc_id}) res['value'].update({'account_id':acc_id})
return res return res
# TOFIX # TOFIX: don't seems to work as expected
def onchange_type(self, cr, uid, line_id, partner_id, type, import_config_id, context=None): def onchange_type(self, cr, uid, line_id, partner_id, type, profile_id, context=None):
if context is None: if context is None:
context = {} context = {}
res = super(AccountBankSatementLine,self).onchange_type(cr, uid, line_id, partner_id, type, 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 acc_id=c.receivable_account_id and c.receivable_account_id.id or False
if acc_id: if acc_id:
res['value'].update({'account_id':acc_id}) res['value'].update({'account_id':acc_id})
return res 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),
# }

View File

@@ -40,6 +40,7 @@
<field name="receivable_account_id" /> <field name="receivable_account_id" />
<field name="force_partner_on_bank"/> <field name="force_partner_on_bank"/>
<field name="balance_check"/> <field name="balance_check"/>
<field name="bank_statement_prefix"/>
</form> </form>
</field> </field>
</record> </record>
@@ -90,7 +91,7 @@
<field name="type">search</field> <field name="type">search</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="/search/group/field[@name='name']" position="before"> <xpath expr="/search/group/field[@name='name']" position="before">
<field name="import_config_id"/> <field name="profile_id"/>
<field name="credit_partner_id"/> <field name="credit_partner_id"/>
<separator orientation="vertical"/> <separator orientation="vertical"/>
</xpath> </xpath>
@@ -109,7 +110,7 @@
<field name="type">tree</field> <field name="type">tree</field>
<field name="arch" type="xml"> <field name="arch" type="xml">
<xpath expr="/tree/field[@name='name']" position="after"> <xpath expr="/tree/field[@name='name']" position="after">
<field name="import_config_id"/> <field name="profile_id"/>
</xpath> </xpath>
<xpath expr="/tree/field[@name='period_id']" position="replace"> <xpath expr="/tree/field[@name='period_id']" position="replace">
<field name="credit_partner_id"/> <field name="credit_partner_id"/>
@@ -133,7 +134,7 @@
<xpath expr="/form/group[@col='7']" position="before"> <xpath expr="/form/group[@col='7']" position="before">
<group col="6" colspan="4"> <group col="6" colspan="4">
<field name="name" select="1"/> <field name="name" select="1"/>
<field name="import_config_id" select="1" required="1" on_change="onchange_imp_config_id(import_config_id)" widget="selection"/> <field name="profile_id" select="1" required="1" on_change="onchange_imp_config_id(profile_id)" widget="selection"/>
<field name="date" select="1" on_change="onchange_date(date, company_id)"/> <field name="date" select="1" on_change="onchange_date(date, company_id)"/>
</group> </group>
<separator string="Details" colspan="4"/> <separator string="Details" colspan="4"/>
@@ -166,23 +167,23 @@
<field name="id"/> <field name="id"/>
</xpath> </xpath>
<xpath expr="/form/notebook/page/field/tree/field[@name='partner_id']" position="replace"> <xpath expr="/form/notebook/page/field/tree/field[@name='partner_id']" position="replace">
<field name="partner_id" on_change="onchange_partner_id(partner_id,parent.import_config_id)"/> <field name="partner_id" on_change="onchange_partner_id(partner_id,parent.profile_id)"/>
</xpath> </xpath>
<xpath expr="/form/notebook/page/field/form/field[@name='date']" position="before"> <xpath expr="/form/notebook/page/field/form/field[@name='date']" position="before">
<field name="id"/> <field name="id"/>
</xpath> </xpath>
<!-- Adapt onchange signature --> <!-- Adapt onchange signature -->
<xpath expr="/form/notebook/page/field/tree/field[@name='partner_id']" position="replace"> <xpath expr="/form/notebook/page/field/tree/field[@name='partner_id']" position="replace">
<field name="partner_id" on_change="onchange_partner_id(partner_id,parent.import_config_id)"/> <field name="partner_id" on_change="onchange_partner_id(partner_id,parent.profile_id)"/>
</xpath> </xpath>
<xpath expr="/form/notebook/page/field/form/field[@name='partner_id']" position="replace"> <xpath expr="/form/notebook/page/field/form/field[@name='partner_id']" position="replace">
<field name="partner_id" on_change="onchange_partner_id(partner_id,parent.import_config_id)"/> <field name="partner_id" on_change="onchange_partner_id(partner_id,parent.profile_id)"/>
</xpath> </xpath>
<xpath expr="/form/notebook/page/field/form/field[@name='type']" position="replace"> <xpath expr="/form/notebook/page/field/form/field[@name='type']" position="replace">
<field name="type" on_change="onchange_type(partner_id, type, parent.import_config_id)"/> <field name="type" on_change="onchange_type(partner_id, type, parent.profile_id)"/>
</xpath> </xpath>
<xpath expr="/form/notebook/page/field/tree/field[@name='type']" position="replace"> <xpath expr="/form/notebook/page/field/tree/field[@name='type']" position="replace">
<field name="type" on_change="onchange_type(partner_id, type, parent.import_config_id)"/> <field name="type" on_change="onchange_type(partner_id, type, parent.profile_id)"/>
</xpath> </xpath>
</field> </field>

View File

@@ -1,107 +0,0 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi, Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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/>.
#
##############################################################################
"""
Wizard to import financial institute date in bank statement
"""
from osv import fields, osv
from tools.translate import _
import os
class CreditPartnerStatementImporter(osv.osv_memory):
"""Import Credit statement"""
_name = "credit.statement.import"
_description = __doc__
_columns = {
'import_config_id': fields.many2one('account.statement.profil',
'Import configuration parameter',
required=True),
'partner_id': fields.many2one('res.partner',
'Credit insitute partner',
),
'journal_id': fields.many2one('account.journal',
'Financial journal to use transaction',
),
'input_statement': fields.binary('Statement file', required=True),
'file_name': fields.char('File Name', size=128),
'commission_account_id': fields.many2one('account.account',
'Commission account',
),
'commission_analytic_id': fields.many2one('account.analytic.account',
'Commission analytic account',
),
'receivable_account_id': fields.many2one('account.account',
'Force Receivable/Payable Account'),
'force_partner_on_bank': fields.boolean('Force partner on bank move',
help="Tic that box if you want to use the credit insitute partner\
in the counterpart of the treasury/banking move."
),
'balance_check': fields.boolean('Balance check',
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."
),
}
def onchange_import_config_id(self, cr, uid, ids, import_config_id, context=None):
res={}
if import_config_id:
c = self.pool.get("account.statement.profil").browse(cr,uid,import_config_id)
res = {'value': {'partner_id': c.partner_id and c.partner_id.id or False,
'journal_id': c.journal_id and c.journal_id.id or False, 'commission_account_id': \
c.commission_account_id and c.commission_account_id.id or False,
'receivable_account_id': c.receivable_account_id and c.receivable_account_id.id or False,
'commission_a':c.commission_analytic_id and c.commission_analytic_id.id or False,
'force_partner_on_bank':c.force_partner_on_bank,
'balance_check':c.balance_check,}}
return res
def import_statement(self, cursor, uid, req_id, context=None):
"""This Function import credit card agency statement"""
context = context or {}
if isinstance(req_id, list):
req_id = req_id[0]
importer = self.browse(cursor, uid, req_id, context)
(shortname, ftype) = os.path.splitext(importer.file_name)
if not ftype:
#We do not use osv exception we do not want to have it logged
raise Exception(_('Please use a file with an extention'))
sid = self.pool.get(
'account.bank.statement').credit_statement_import(
cursor,
uid,
False,
importer.import_config_id.id,
importer.input_statement,
ftype.replace('.',''),
context=context
)
obj_data = self.pool.get('ir.model.data')
act_obj = self.pool.get('ir.actions.act_window')
result = obj_data.get_object_reference(cursor, uid, 'account_statement_import', 'action_treasury_statement_tree')
id = result and result[1] or False
result = act_obj.read(cursor, uid, [id], context=context)[0]
result['domain'] = str([('id','in',[sid])])
return result

View File

@@ -1,44 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="statement_importer_view" model="ir.ui.view">
<field name="name">credit.statement.import.config.view</field>
<field name="model">credit.statement.import</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Import statement">
<group colspan="4" >
<field name="import_config_id" on_change="onchange_import_config_id(import_config_id)"/>
<field name="input_statement" filename="file_name" colspan="2"/>
<field name="file_name" colspan="2" invisible="1"/>
<separator string="Import Parameters Summary" colspan="4"/>
<field name="partner_id" readonly="1"/>
<field name="journal_id" readonly="1"/>
<field name="commission_account_id" readonly="1"/>
<field name="commission_analytic_id" readonly="1"/>
<field name="receivable_account_id" readonly="1"/>
<field name="force_partner_on_bank" readonly="1"/>
<field name="balance_check" readonly="1"/>
</group>
<separator string="" colspan="4"/>
<group colspan="4" col="6">
<button icon="gtk-cancel" special="cancel" string="Cancel"/>
<button icon="gtk-ok" name="import_statement" string="Import statement" type="object"/>
</group>
</form>
</field>
</record>
<record id="statement_importer_action" model="ir.actions.act_window">
<field name="name">Import statement</field>
<field name="res_model">credit.statement.import</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
<field name="view_id" ref="statement_importer_view"/>
<field name="target">new</field>
</record>
<menuitem id="statement_importer_menu" name="Import Treasury Statement" action="statement_importer_action" parent="account.menu_finance_periodical_processing"/>
</data>
</openerp>

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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/>.
#
##############################################################################
import statement

View File

@@ -0,0 +1,47 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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/>.
#
##############################################################################
{'name': "Bank statement completion from transaction ID",
'version': '1.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal', #easy, normal, expert
'depends': ['base_transaction_id','account_statement_base_completion'],
'description': """
Add a completion method based on transaction ID providen by the bank/office. This
transaction ID has been recorded on the SO (by a mapping through the e-commerce connector,
or manually). Completion will look in the SO with that transaction ID to match the partner.
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [
"statement_view.xml",
],
'demo_xml': [],
'test': [],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
'active': False,
}

View File

@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# 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 tools.translate import _
import datetime
import netsvc
logger = netsvc.Logger()
from openerp.osv.orm import Model, fields
class AccountStatementProfil(Model):
_inherit = "account.statement.profil"
class AccountBankSatement(Model):
_inherit = "account.bank.statement"
class AccountStatementLine(Model):
_inherit = "account.bank.statement.line"
_columns={
# 'additionnal_bank_fields' : fields.serialized('Additionnal infos from bank', help="Used by completion and import system."),
'transaction_id': fields.sparse(type='char', string='Transaction ID',
size=128,
serialization_field='additionnal_bank_fields',
help="Transction id from the financial institute"),
}

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="bank_statement_view_form" model="ir.ui.view">
<field name="name">account_bank_statement_import_base.bank_statement.view_form</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field eval="16" name="priority"/>
<field name="type">form</field>
<field name="arch" type="xml">
<data>
<xpath expr="/form/notebook/page/field[@name='line_ids']/form/field[@name='ref']" position="after">
<field name="transaction_id" />
</xpath>
</data>
</field>
</record>
</data>
</openerp>