[IMP] Merge treasury statement into bank.statement object. We don't need both finaly

(lp:c2c-financial-addons/6.1 rev 24.1.15)
This commit is contained in:
Joël Grand-Guillaume
2012-06-08 16:25:17 +02:00
parent 5d32afb787
commit 523093e2a2
20 changed files with 1460 additions and 70 deletions

View File

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

View File

@@ -1,10 +1,8 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Author: Nicolas Bessi, Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
# Thanks to EduSense BV (<http://www.edusense.nl>) for some part and idea
# taken from the account_banking module
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@@ -21,21 +19,49 @@
#
##############################################################################
{'name': "Account Bank Statement without Period",
{'name': "Bank statement extension and profiles",
'version': '1.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal', #easy, normal, expert
'depends': ['account'],
'depends': ['base_transaction_id'],
'description': """
Remove the period on the bank statement, and compute it for each line based on their date instead.
If errors occurs, it will summarize them all in one popup instead of blocking all the process at every error.
The goal of this module is to help dealing with huge volume of reconciliation through
payment offices like Paypal, Lazer, Visa, Amazon and so on. It's 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.
Features:
1) This module improves the bank statement that allow and you to import your bank transactions with
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:
- Account commission and partner relation
- Can force an account for the reconciliation
- Choose to use balance check or not
- Analytic account for commission
- Force Partner on the counter-part move (e.g. 100.- debit, Partner: M.Martin; 100.- credit, Partner: HSBC)
2) Adds a report on bank statement that can be used for Checks
3) When an error occurs in a bank statement confirmation, it will go through all line anyway and summarize
all the erronous line in a same popup instead of raising and crashing on every step.
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
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [
'statement_view.xml',
'wizard/import_statement_view.xml',
'report/bank_statement_webkit_header.xml',
'report.xml',
],
'demo_xml': [],
'test': [],

View File

@@ -0,0 +1,4 @@
"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

Binary file not shown.

View File

@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi
# 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 parser

View File

@@ -0,0 +1,131 @@
# -*- 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

@@ -0,0 +1,24 @@
<?xml version="1.0" ?>
<openerp>
<data>
<report auto="False"
id="report_bank_statement_webkit"
model="account.bank.statement"
name="account_statement_import.report_bank_statement_webkit"
file="account_statement_import/report/bank_statement_report.mako"
string="Bank Statement"
report_type="webkit"
header="1"/>
<record id="action_print_bank_statement_webkit" model="ir.values">
<field name="name">Bank Statement</field>
<field name="key2">client_print_multi</field>
<field name="value_unpickle"
eval="'ir.actions.report.xml,' +str(ref('report_bank_statement_webkit'))"/>
<field name="key">action</field>
<field name="model">account.bank.statement</field>
</record>
</data>
</openerp>

View File

@@ -0,0 +1,31 @@
# -*- encoding: utf-8 -*-
#
# __init__.py
#
# Copyright (c) 2009 CamptoCamp. All rights reserved.
##############################################################################
#
# WARNING: This program as such is intended to be used by professional
# programmers who take the whole responsability of assessing all potential
# consequences resulting from its eventual inadequacies and bugs
# End users who are looking for a ready-to-use solution with commercial
# garantees and support are strongly adviced to contract a Free Software
# Service Company
#
# 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 2
# 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, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
##############################################################################
import bank_statement_report

View File

@@ -0,0 +1,69 @@
<!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<style type="text/css">
${css}
</style>
</head>
<body>
<%!
def amount(text):
return text.replace('-', '&#8209;') # replace by a non-breaking hyphen (it will not word-wrap between hyphen and numbers)
%>
<%setLang(user.context_lang)%>
%for statement in objects:
<div class="act_as_table data_table">
<div class="act_as_row labels">
<div class="act_as_cell">${_('Bordereau')}</div>
<div class="act_as_cell">${_('Date')}</div>
</div>
<div class="act_as_row">
<div class="act_as_cell">${ statement.name }</div>
<div class="act_as_cell">${ formatLang(statement.date,date=True) }</div>
</div>
</div>
<!-- we use div with css instead of table for tabular data because div do not cut rows at half at page breaks -->
<div class="act_as_table list_table" style="margin-top: 10px;">
<div class="act_as_thead">
<div class="act_as_row labels">
## date
<div class="act_as_cell first_column" style="width: 100px;">${_('Reference')}</div>
## period
<div class="act_as_cell" style="width: 175px;">${_('Partenaire')}</div>
## move
<div class="act_as_cell" style="width: 60px;">${_('Montant')}</div>
## journal
</div>
</div>
<% sum_statement = 0.0 %>
%for statementline in statement.line_ids:
<div class="act_as_tbody">
## curency code
<div class="act_as_cell">${statementline.name or '' }</div>
## currency balance
<div class="act_as_cell">${statementline.partner_id.name or '' }</div>
## currency balance cumulated
<div class="act_as_cell amount">${formatLang(statementline.amount) | amount }</div>
<% sum_statement += statementline.amount %>
</div>
%endfor
<div class="act_as_tbody">
## curency code
<div class="act_as_cell"></div>
## currency balance
<div class="act_as_cell">Total</div>
## currency balance cumulated
<div class="act_as_cell amount_total">${formatLang(sum_statement) }</div>
</div>
</div>
%endfor
</body>
</html>

View File

@@ -0,0 +1,72 @@
# -*- encoding: utf-8 -*-
##############################################################################
#
# Author: Nicolas Bessi. Copyright 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 time
from report import report_sxw
from osv import osv
from tools.translate import _
import pooler
from operator import add, itemgetter
from itertools import groupby
from datetime import datetime
#from common_report_header_webkit import CommonReportHeaderWebkit
from report_webkit import webkit_report
class BankStatementWebkit(report_sxw.rml_parse):
def __init__(self, cursor, uid, name, context):
super(BankStatementWebkit, self).__init__(cursor, uid, name, context=context)
self.pool = pooler.get_pool(self.cr.dbname)
self.cursor = self.cr
company = self.pool.get('res.users').browse(self.cr, uid, uid, context=context).company_id
header_report_name = ' - '.join((_('BORDEREAU DE REMISE DE CHEQUES'),
company.name, company.currency_id.name))
statement = self.pool.get('account.bank.statement').browse(cursor,uid,context['active_id']);
footer_date_time = self.formatLang(str(datetime.today())[:19], date_time=True)
self.localcontext.update({
'cr': cursor,
'uid': uid,
'get_bank_statement' : self._get_bank_statement_data,
'report_name': _('BORDEREAU DE REMISE DE CHEQUES'),
'additional_args': [
('--header-font-name', 'Helvetica'),
('--footer-font-name', 'Helvetica'),
('--header-font-size', '10'),
('--footer-font-size', '6'),
('--header-left', header_report_name),
('--header-spacing', '2'),
('--footer-left', footer_date_time),
('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
('--footer-line',),
],
})
def _get_bank_statement_data(self,statement):
statement_obj = self.pool.get('account.bank.statement.line')
statement_line_ids = statement_obj.search(self.cr,self.uid,[['statement_id','=',statement.id]])
statement_lines = statement_obj.browse(self.cr,self.uid,statement_line_ids)
return statement_lines
webkit_report.WebKitParser('report.report_bank_statement_webkit',
'account.bank.statement',
'addons/account_statement_import/report/bank_statement_report.mako',
parser=BankStatementWebkit)

View File

@@ -0,0 +1,180 @@
<?xml version="1.0" ?>
<openerp>
<data>
<record id="bank_statement_landscape_header" model="ir.header_webkit">
<field name="footer_html"><![CDATA[
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
<script>
function subst() {
var vars={};
var x=document.location.search.substring(1).split('&');
for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
for(var i in x) {
var y = document.getElementsByClassName(x[i]);
for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
}
}
</script>
</head>
<% import datetime %>
<body style="border:0; margin: 0;" onload="subst()">
<table style="border-top: 1px solid black; width: 1080px">
<tr style="border-collapse:collapse;">
<td style="text-align:left;font-size:10;width:350px;">${formatLang( str(datetime.datetime.today()), date_time=True)}</td>
<td style="text-align:center;font-size:10;width:350px;">${user.name}</td>
<td style="text-align:right;font-size:10;width:350px;">Page&nbsp;<span class="page"/></td>
<td style="text-align:left;font-size:10;width:30px">&nbsp;of&nbsp;<span class="topage"/></td>
</tr>
</table>
</body>
</html>]]></field>
<field name="orientation">Portrait</field>
<field name="format">A4</field>
<field name="html"><![CDATA[
<html>
<head>
<meta content="text/html; charset=UTF-8" http-equiv="content-type"/>
<script>
function subst() {
var vars={};
var x=document.location.search.substring(1).split('&');
for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}
var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];
for(var i in x) {
var y = document.getElementsByClassName(x[i]);
for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];
}
}
</script>
<style type="text/css">
${css}
</style>
</head>
<body style="border:0; margin: 0;" onload="subst()">
<table class="header" style="border-bottom: 0px solid black; width: 100%">
<tr>
<td style="text-align:left; font-size:11px; font-weight: bold;"><span style="text-transform:uppercase; font-size:12px;">${report_name}</span> - ${company.partner_id.name | entity} - ${company.currency_id.name | entity}</td>
</tr>
</table> ${_debug or ''|n} </body>
</html>]]>
</field>
<field eval="0.0" name="margin_top"/>
<field name="css"><![CDATA[
body, table, td, span, div {
font-family: Helvetica, Arial;
}
.act_as_table {
display: table;
}
.act_as_row {
display: table-row;
}
.act_as_cell {
display: table-cell;
}
.act_as_thead {
display: table-header-group;
}
.act_as_tbody {
display: table-row-group;
}
.act_as_tfoot {
display: table-footer-group;
}
.act_as_caption {
display: table-caption;
}
act_as_colgroup {
display: table-column-group;
}
.list_table, .data_table {
width: 720px;
table-layout: fixed
}
.bg, .act_as_row.labels {
background-color:#F0F0F0;
}
.list_table, .data_table, .list_table .act_as_row {
border-left:0px;
border-right:0px;
text-align:left;
font-size:9px;
padding-right:3px;
padding-left:3px;
padding-top:2px;
padding-bottom:2px;
border-collapse:collapse;
}
.list_table .act_as_row.labels, .list_table .act_as_row.initial_balance, .list_table .act_as_row.lines {
border-color:gray;
border-bottom:1px solid lightGrey;
}
.data_table .act_as_cell {
border: 1px solid lightGrey;
text-align: center;
}
.data_table .act_as_cell, .list_table .lines .act_as_cell {
word-wrap: break-word;
}
.nobreak {
word-wrap: normal;
}
.data_table .act_as_row.labels {
font-weight: bold;
}
.initial_balance .act_as_cell {
font-style:italic;
}
.account_title {
font-size:10px;
font-weight:bold;
page-break-after: avoid;
}
.act_as_cell.amount {
word-wrap:normal;
text-align:right;
}
.act_as_cell.amount_total {
word-wrap:normal;
text-align:right;
font-weight: bold;
}
.list_table .act_as_cell{
padding-left: 5px;
/* border-right:1px solid lightGrey; uncomment to active column lines */
}
.list_table .act_as_cell.first_column {
padding-left: 0px;
/* border-left:1px solid lightGrey; uncomment to active column lines */
}
.sep_left {
border-left: 1px solid lightGrey;
}
]]>
</field>
<field name="name">Bank Statement Landscape Header</field>
</record>
</data>
</openerp>

View File

@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author: Joel Grand-Guillaume
# Author: Nicolas Bessi, Joel Grand-Guillaume
# Copyright 2011-2012 Camptocamp SA
#
# This program is free software: you can redistribute it and/or modify
@@ -19,28 +19,113 @@
#
##############################################################################
from osv import fields, osv
from tools.translate import _
from account_statement_import.file_parser.parser import FileParser
from account_statement_ext.file_parser.parser import FileParser
import datetime
import netsvc
logger = netsvc.Logger()
from openerp.osv.orm import Model, fields
class AccountStatementProfil(Model):
_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',
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',
'Financial journal to use for transaction',
required=True),
'commission_account_id': fields.many2one('account.account',
'Commission account',
required=True),
'commission_analytic_id': fields.many2one('account.analytic.account',
'Commission analytic account'),
'receivable_account_id': fields.many2one('account.account',
'Force Receivable/Payable Account',
help="Choose a receivable account to force the default\
debit/credit account (eg. an intermediat bank account instead of\
default debitors)."),
'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 intermediat/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."
),
}
_defaults = {}
def _check_partner(self, cr, uid, ids, context=None):
obj = self.browse(cr, uid, ids[0], context=context)
if obj.partner_id == False and obj.force_partner_on_bank:
return False
return True
_constraints = [
(_check_partner, "You need to put a partner if you tic the 'Force partner on bank move' !", []),
]
class AccountSatement(osv.osv):
"""Override account bank statement to remove the period on it
and compute it for each line."""
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."""
_inherit = "account.bank.statement"
_columns = {
'import_config_id': fields.many2one('account.statement.profil',
'Profil', required=True, states={'draft': [('readonly', False)]}),
'credit_partner_id': fields.related(
'import_config_id',
'partner_id',
type='many2one',
relation='res.partner',
string='Financial Partner',
store=True, readonly=True),
'balance_check': fields.related(
'import_config_id',
'balance_check',
type='boolean',
string='Balance check',
store=True, readonly=True),
'journal_id': fields.related(
'import_config_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),
}
_defaults = {
'period_id': lambda *a: False,
}
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:
profil_obj = self.pool.get('account.statement.profil')
profile = profil_obj.browse(cr,uid,vals['import_config_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.
@@ -68,18 +153,28 @@ class AccountSatement(osv.osv):
_constraints = [
(_check_company_id, 'The journal and period chosen have to belong to the same company.', ['journal_id','period_id']),
]
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.
Remove the entry posting on generated account moves."""
Remove the entry posting on generated account moves.
Point to account.bank.statement.line instead of account.bank.statement.line.
In Treasury Statement, unlike the Bank statement, we will change the move line generated from the
lines depending on the profil (config import):
- If receivable_account_id is set, we'll use it instead of the "partner" one
- 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
statement line
"""
if context is None:
context = {}
res_currency_obj = self.pool.get('res.currency')
account_move_obj = self.pool.get('account.move')
account_move_line_obj = self.pool.get('account.move.line')
account_bank_statement_line_obj = self.pool.get('account.bank.statement.line')
st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id, context=context)
account_bank_statement_line_obj = self.pool.get('account.bank.statement.line') # Chg
st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id, context=context) # Chg
st = st_line.statement_id
context.update({'date': st_line.date})
@@ -95,7 +190,7 @@ class AccountSatement(osv.osv):
'name': st_line_number,
'ref': st_line.ref,
}, context=context)
account_bank_statement_line_obj.write(cr, uid, [st_line.id], {
account_bank_statement_line_obj.write(cr, uid, [st_line.id], { # Chg
'move_ids': [(4, move_id, False)]
})
@@ -121,7 +216,8 @@ class AccountSatement(osv.osv):
'account_id': (st_line.account_id) and st_line.account_id.id,
'credit': ((amount>0) and amount) or 0.0,
'debit': ((amount<0) and -amount) or 0.0,
'statement_id': st.id,
# Replace with the treasury one instead of bank #Chg
'statement_id': st.id,
'journal_id': st.journal_id.id,
'period_id': period_id, #Chg
'currency_id': st.currency.id,
@@ -149,16 +245,23 @@ class AccountSatement(osv.osv):
if st.currency.id <> company_currency_id:
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
else: # Chg
bank_parrtner_id = ((st_line.partner_id) and st_line.partner_id.id) or False # Chg
account_move_line_obj.create(cr, uid, {
'name': st_line.name,
'date': st_line.date,
'ref': st_line.ref,
'move_id': move_id,
'partner_id': ((st_line.partner_id) and st_line.partner_id.id) or False,
'partner_id': bank_parrtner_id, # Chg
'account_id': account_id,
'credit': ((amount < 0) and -amount) or 0.0,
'debit': ((amount > 0) and amount) or 0.0,
'statement_id': st.id,
# Replace with the treasury one instead of bank #Chg
'statement_id': st.id,
'journal_id': st.journal_id.id,
'period_id': period_id, #Chg
'amount_currency': amount_currency,
@@ -199,12 +302,12 @@ class AccountSatement(osv.osv):
if context is None:
context = {}
for st in self.browse(cr, uid, ids, context=context):
j_type = st.journal_id.type
company_currency_id = st.journal_id.company_id.currency_id.id
if not self.check_status_condition(cr, uid, st.state, journal_type=j_type):
continue
self.balance_check(cr, uid, st.id, journal_type=j_type, context=context)
if (not st.journal_id.default_credit_account_id) \
or (not st.journal_id.default_debit_account_id):
@@ -256,11 +359,236 @@ class AccountSatement(osv.osv):
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)
class AccountSatementLine(osv.osv):
'''
Adds the period on line, matched on the date.
'''
_inherit = 'account.bank.statement.line'
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
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
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"""
account_receivable = False
account_payable = False
if receivable_account_id:
account_receivable = account_payable = receivable_account_id
else:
context = context or {}
property_obj = self.pool.get('ir.property')
model_fields_obj = self.pool.get('ir.model.fields')
model_fields_ids = model_fields_obj.search(
cursor,
uid,
[('name', 'in', ['property_account_receivable',
'property_account_payable']),
('model', '=', 'res.partner'),],
context=context
)
property_ids = property_obj.search(
cursor,
uid, [
('fields_id', 'in', model_fields_ids),
('res_id', '=', False),
],
context=context
)
for erp_property in property_obj.browse(cursor, uid,
property_ids, context=context):
if erp_property.fields_id.name == 'property_account_receivable':
account_receivable = erp_property.value_reference.id
elif erp_property.fields_id.name == 'property_account_payable':
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"""
st = self.browse(cr, uid, st_id, context=context)
if st.balance_check:
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:
return {}
import_config = self.pool.get("account.statement.profil").browse(cr,uid,import_config_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
return {'value': {'journal_id':journal_id, 'account_id': account_id,
'balance_check':import_config.balance_check,
'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"
def _get_period(self, cursor, user, context=None):
date = context.get('date', None)
@@ -268,9 +596,73 @@ class AccountSatementLine(osv.osv):
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'),
'ref': fields.char('Reference', size=32, required=True),
'period_id': fields.many2one('account.period', 'Period', required=True),
}
_defaults = {
'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
obj_partner = self.pool.get('res.partner')
if context is None:
context = {}
if not partner_id:
return {}
part = obj_partner.browse(cr, uid, partner_id, context=context)
if not part.supplier and not part.customer:
type = 'general'
elif part.supplier and part.customer:
type = 'general'
else:
if part.supplier == True:
type = 'supplier'
if part.customer == True:
type = 'customer'
res_type = self.onchange_type(cr, uid, ids, partner_id, type, import_config_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)
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):
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)
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),
# }

View File

@@ -1,51 +1,75 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<record id="view_bank_statement_form" model="ir.ui.view">
<field name="name">account.bank.statement.form</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<!-- Account Move Line : add statement_treasury_id -->
<record id="view_move_line_tree" model="ir.ui.view">
<field name="name">account.move.line.tree</field>
<field name="model">account.move.line</field>
<field name="type">tree</field>
<field name="inherit_id" ref="account.view_move_line_tree"/>
<field name="arch" type="xml">
<field name="ref" position="after">
<field name="statement_id"/>
</field>
</field>
</record>
<record id="view_account_move_line_filter" model="ir.ui.view">
<field name="model">account.move.line</field>
<field name="type">search</field>
<field name="inherit_id" ref="account.view_account_move_line_filter"/>
<field name="arch" type="xml">
<field name="period_id" context="{'period_id':self}" position="after">
<field name="statement_id"/>
</field>
</field>
</record>
<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="type">form</field>
<field name="arch" type="xml">
<field name="period_id" position="replace"/>
</field>
</record>
<record id="view_banking_bank_statement_tree" model="ir.ui.view">
<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">
<field name="period_id" position="replace"/>
<form string="Import statement">
<separator string="" colspan="4"/>
<field name="name" select="1" />
<field name="partner_id" select="1"/>
<field name="journal_id" select="1"/>
<field name="commission_account_id" />
<field name="commission_analytic_id" />
<field name="receivable_account_id" />
<field name="force_partner_on_bank"/>
<field name="balance_check"/>
</form>
</field>
</record>
<record id="view_banking_bank_statement_form_add_period" model="ir.ui.view">
<field name="name">account.bank.statement.form.add_period</field>
<field name="inherit_id" ref="account.view_bank_statement_form" />
<field name="model">account.bank.statement</field>
<field name="type">form</field>
<record id="statement_importer_view_tree" model="ir.ui.view">
<field name="name">account.statement.profil.view</field>
<field name="model">account.statement.profil</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<xpath expr="/form/notebook/page[@string='Transaction']/field/tree/field[@name='ref']" position="after">
<field name="period_id"/>
</xpath>
<tree string="Import statement">
<field name="name" />
<field name="partner_id" />
<field name="journal_id" />
<field name="commission_account_id" />
<field name="commission_analytic_id" />
<field name="receivable_account_id" />
<field name="force_partner_on_bank"/>
<field name="balance_check"/>
</tree>
</field>
</record>
<record id="view_banking_bank_statement_add_period_2" model="ir.ui.view">
<field name="name">account.bank.statement.form.add_period2</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="arch" type="xml">
<data>
<xpath expr="/form/notebook/page[@string='Transaction']/field/form/field[@name='ref']" position="after">
<field name="period_id"/>
</xpath>
</data>
</field>
<record id="action_treasury_statement_profil_tree" model="ir.actions.act_window">
<field name="name">Bank Statements Profile</field>
<field name="res_model">account.statement.profil</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem string="Bank Statements Profile" action="action_treasury_statement_profil_tree" id="menu_treasury_statement_profil_tree" parent="account.menu_configuration_misc" sequence="30"/>
<record model="ir.ui.view" id="id_in_statement_line">
<field name="name">account.bank.statement.line.inherit</field>
<field name="model">account.bank.statement</field>
@@ -58,6 +82,137 @@
</field>
</record>
<record id="view_treasury_statement_search" model="ir.ui.view">
<field name="name">account.bank.statement.search</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_search"/>
<field name="type">search</field>
<field name="arch" type="xml">
<xpath expr="/search/group/field[@name='name']" position="before">
<field name="import_config_id"/>
<field name="credit_partner_id"/>
<separator orientation="vertical"/>
</xpath>
<xpath expr="/search/group/field[@name='period_id']" position="replace">
</xpath>
<xpath expr="/search/group/filter[@string='Period']" position="replace">
<filter string="Financial Partner" context="{'group_by': 'credit_partner_id'}" icon="terp-partner"/>
</xpath>
</field>
</record>
<record id="view_treasury_statement_tree" model="ir.ui.view">
<field name="name">account.bank.statement.tree</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_tree"/>
<field name="type">tree</field>
<field name="arch" type="xml">
<xpath expr="/tree/field[@name='name']" position="after">
<field name="import_config_id"/>
</xpath>
<xpath expr="/tree/field[@name='period_id']" position="replace">
<field name="credit_partner_id"/>
</xpath>
</field>
</record>
<record id="view_treasury_statement_form" model="ir.ui.view">
<field name="name">account.bank.statement.form</field>
<field name="model">account.bank.statement</field>
<field name="inherit_id" ref="account.view_bank_statement_form"/>
<field name="type">form</field>
<field name="arch" type="xml">
<!-- Remove name and date from group tag -->
<xpath expr="/form/group/field[@name='name']" position="replace">
</xpath>
<xpath expr="/form/group/field[@name='date']" position="replace">
</xpath>
<!-- Add a new group before the first one with name, profil and date -->
<xpath expr="/form/group[@col='7']" position="before">
<group col="6" colspan="4">
<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="date" select="1" on_change="onchange_date(date, company_id)"/>
</group>
<separator string="Details" colspan="4"/>
</xpath>
<!-- Make balance visible or not depending on profil -->
<xpath expr="/form/group/field[@name='balance_start']" position="replace">
</xpath>
<xpath expr="/form/group/field[@name='balance_end_real']" position="replace">
</xpath>
<xpath expr="/form/group[@col='7']" position="after">
<separator string="Balance Check" colspan="4" attrs="{'invisible':[('balance_check','=',False)]}"/>
<group col="6" colspan="4" attrs="{'invisible':[('balance_check','=',False)]}">
<field name="balance_start" />
<field name="balance_end_real" />
</group>
</xpath>
<xpath expr="/form/group/field[@name='balance_end']" position="replace">
<field name="balance_end" attrs="{'invisible':[('balance_check','=',False)]}"/>
</xpath>
<xpath expr="/form/group/field[@name='journal_id']" position="attributes">
<attribute name="widget"></attribute>
</xpath>
<xpath expr="/form/group/field[@name='period_id']" position="replace">
<field name="credit_partner_id"/>
<field name="account_id" invisible="1"/>
<field name="balance_check" invisible="1"/>
</xpath>
<xpath expr="/form/notebook/page/field/tree/field[@name='sequence']" position="after">
<field name="id"/>
</xpath>
<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)"/>
</xpath>
<xpath expr="/form/notebook/page/field/form/field[@name='date']" position="before">
<field name="id"/>
</xpath>
<!-- Adapt onchange signature -->
<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)"/>
</xpath>
<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)"/>
</xpath>
<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)"/>
</xpath>
<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)"/>
</xpath>
</field>
</record>
<!-- <record id="action_treasury_statement_tree" model="ir.actions.act_window">
<field name="name">Intermediate Statements</field>
<field name="res_model">account.bank.statement</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,graph</field>
<field name="domain">[('journal_id.type', '=', 'bank')]</field>
<field name="context">{'journal_type':'bank'}</field>
<field name="search_view_id" ref="view_treasury_statement_search"/>
<field name="help">A treasury statement is a summary of all financial transactions occurring a credit card or any other type of intermediate financial account.</field>
</record>
<record model="ir.actions.act_window.view" id="action_treasury_statement_tree_bank">
<field name="sequence" eval="1"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="view_treasury_statement_tree"/>
<field name="act_window_id" ref="action_treasury_statement_tree"/>
</record>
<record model="ir.actions.act_window.view" id="action_treasury_statement_form_bank">
<field name="sequence" eval="1"/>
<field name="view_mode">form</field>
<field name="view_id" ref="view_treasury_statement_form"/>
<field name="act_window_id" ref="action_treasury_statement_tree"/>
</record>
<menuitem string="Treasury Statements" action="action_treasury_statement_tree" id="menu_treasury_statement_tree" parent="account.menu_finance_bank_and_cash" sequence="9"/> -->
</data>
</openerp>

View File

@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# Author Nicolas Bessi. Copyright Camptocamp SA
#
# 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/>.
#
##############################################################################
import import_statement

View File

@@ -0,0 +1,107 @@
# -*- 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

@@ -0,0 +1,44 @@
<?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_voucher

View File

@@ -0,0 +1,43 @@
# -*- 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 extension with voucher",
'version': '1.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'category': 'Finance',
'complexity': 'normal', #easy, normal, expert
'depends': ['account_statement_ext','account_voucher'],
'description': """
This module is only needed when using account_bank_statement_ext with voucher in order to compute the period
correctly. This is mainly because with account_bank_statement_ext, the period is computed for each line.
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [],
'demo_xml': [],
'test': [],
'installable': True,
'images': [],
'auto_install': True,
'license': 'AGPL-3',
'active': False,
}

View File

@@ -0,0 +1,41 @@
# -*- 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 openerp.osv.orm import Model, fields
class AccountVoucher(Model):
_inherit = 'account.voucher'
def _get_period(self, cr, uid, context=None):
"""If perdiod not in context, take it from the move lines"""
if context is None: context = {}
if not context.get('period_id') and context.get('move_line_ids'):
res = self.pool.get('account.move.line').browse(cr, uid , context.get('move_line_ids'))[0].period_id.id
context['period_id'] = res
return super(AccountVoucher, self)._get_period(cr, uid, context)
def create(self, cr, uid, values, context=None):
"""If no period defined in values, ask it from moves."""
if values.get('period_id') == False and context.get('move_line_ids'):
values['period_id'] = self._get_period(cr, uid, context)
return super(AccountVoucher, self).create(cr, uid, values, context)

View File

@@ -26,9 +26,13 @@
'category': 'Hidden/Dependency',
'complexity': 'easy', #easy, normal, expert
'depends': ['account', 'sale','stock'],
'description': """Adds transaction id to invoice and sale models and views. This is mostely used for E-commerce handling. You
can then add a mapping on that SO field to save the E-Commerce financial Transaction ID into the OpenERP SO field. The main
purpose is to ease the reconciliation process.""",
'description': """
Adds transaction id to invoice and sale models and views. On Sales order, you can specify the transaction ID
used for the payment and it will be propagated to the invoice (even if made from packing).
This is mostely used for E-commerce handling. You can then add a mapping on that SO field to save the E-Commerce
financial Transaction ID into the OpenERP SO field. The main purpose is to ease the reconciliation process and
be able to find the partner when importing the bank statement.
""",
'website': 'http://www.openerp.com',
'init_xml': [],
'update_xml': ['invoice_view.xml', 'sale_view.xml'],