This commit is contained in:
Pedro M. Baeza
2014-08-04 15:29:05 +02:00
parent 2c1773e7be
commit 44a59ce588
60 changed files with 615 additions and 458 deletions

View File

@@ -82,4 +82,4 @@ many offices.
'auto_install': False,
'license': 'AGPL-3',
'application': True,
}
}

View File

@@ -177,8 +177,8 @@ class easy_reconcile_advanced(orm.AbstractModel):
mkey, mvalue = matcher
omkey, omvalue = opposite_matcher
assert mkey == omkey, ("A matcher %s is compared with a matcher %s, "
" the _matchers and _opposite_matchers are probably wrong" %
(mkey, omkey))
" the _matchers and _opposite_matchers are probably wrong" %
(mkey, omkey))
if not isinstance(mvalue, (list, tuple)):
mvalue = mvalue,
if not isinstance(omvalue, (list, tuple)):
@@ -194,7 +194,7 @@ class easy_reconcile_advanced(orm.AbstractModel):
they are candidate for a reconciliation.
"""
opp_matchers = self._opposite_matchers(cr, uid, rec, opposite_move_line,
context=context)
context=context)
for matcher in matchers:
try:
opp_matcher = opp_matchers.next()

View File

@@ -32,9 +32,9 @@ Reconcile rules with transaction_ref
'depends': ['account_advanced_reconcile'],
'data': ['easy_reconcile_view.xml'],
'demo': [],
'test': [], # To be ported or migrate to unit tests or scenarios
'test': [], # To be ported or migrate to unit tests or scenarios
'auto_install': False,
'installable': True,
'images': []
}
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -36,4 +36,3 @@ class account_easy_reconcile_method(orm.Model):
'Advanced. Partner and Transaction Ref. vs Ref.'),
]
return methods

View File

@@ -24,6 +24,7 @@ from operator import itemgetter, attrgetter
class easy_reconcile_base(orm.AbstractModel):
"""Abstract Model for reconciliation methods"""
_name = 'easy.reconcile.base'
@@ -112,9 +113,9 @@ class easy_reconcile_base(orm.AbstractModel):
sums = reduce(
lambda line, memo:
dict((key, value + memo[key])
for key, value
in line.iteritems()
if key in keys), lines)
for key, value
in line.iteritems()
if key in keys), lines)
debit, credit = sums['debit'], sums['credit']
writeoff_amount = round(debit - credit, precision)

View File

@@ -26,6 +26,7 @@ from openerp.tools.translate import _
class easy_reconcile_options(orm.AbstractModel):
"""Options of a reconciliation profile
Columns shared by the configuration of methods
@@ -45,21 +46,21 @@ class easy_reconcile_options(orm.AbstractModel):
('newest_debit', 'Date of most recent debit')]
_columns = {
'write_off': fields.float('Write off allowed'),
'account_lost_id': fields.many2one(
'account.account', 'Account Lost'),
'account_profit_id': fields.many2one(
'account.account', 'Account Profit'),
'journal_id': fields.many2one(
'account.journal', 'Journal'),
'date_base_on': fields.selection(
_get_rec_base_date,
required=True,
string='Date of reconciliation'),
'filter': fields.char('Filter', size=128),
'analytic_account_id': fields.many2one(
'account.analytic.account', 'Analytic Account',
help="Analytic account for the write-off"),
'write_off': fields.float('Write off allowed'),
'account_lost_id': fields.many2one(
'account.account', 'Account Lost'),
'account_profit_id': fields.many2one(
'account.account', 'Account Profit'),
'journal_id': fields.many2one(
'account.journal', 'Journal'),
'date_base_on': fields.selection(
_get_rec_base_date,
required=True,
string='Date of reconciliation'),
'filter': fields.char('Filter', size=128),
'analytic_account_id': fields.many2one(
'account.analytic.account', 'Analytic Account',
help="Analytic account for the write-off"),
}
_defaults = {
@@ -81,31 +82,32 @@ class account_easy_reconcile_method(orm.Model):
return [
('easy.reconcile.simple.name', 'Simple. Amount and Name'),
('easy.reconcile.simple.partner', 'Simple. Amount and Partner'),
('easy.reconcile.simple.reference', 'Simple. Amount and Reference'),
]
('easy.reconcile.simple.reference',
'Simple. Amount and Reference'),
]
def _get_rec_method(self, cr, uid, context=None):
return self._get_all_rec_method(cr, uid, context=None)
_columns = {
'name': fields.selection(
_get_rec_method, 'Type', required=True),
'sequence': fields.integer(
'Sequence',
required=True,
help="The sequence field is used to order "
"the reconcile method"),
'task_id': fields.many2one(
'account.easy.reconcile',
string='Task',
required=True,
ondelete='cascade'),
'company_id': fields.related('task_id','company_id',
relation='res.company',
type='many2one',
string='Company',
store=True,
readonly=True),
'name': fields.selection(
_get_rec_method, 'Type', required=True),
'sequence': fields.integer(
'Sequence',
required=True,
help="The sequence field is used to order "
"the reconcile method"),
'task_id': fields.many2one(
'account.easy.reconcile',
string='Task',
required=True,
ondelete='cascade'),
'company_id': fields.related('task_id', 'company_id',
relation='res.company',
type='many2one',
string='Company',
store=True,
readonly=True),
}
_defaults = {
@@ -240,9 +242,9 @@ class account_easy_reconcile(orm.Model):
all_ml_partial_ids += ml_partial_ids
reconcile_ids = find_reconcile_ids(
'reconcile_id', all_ml_rec_ids)
'reconcile_id', all_ml_rec_ids)
partial_ids = find_reconcile_ids(
'reconcile_partial_id', all_ml_partial_ids)
'reconcile_partial_id', all_ml_partial_ids)
self.pool.get('easy.reconcile.history').create(
cr,
@@ -260,9 +262,9 @@ class account_easy_reconcile(orm.Model):
task.
"""
raise osv.except_osv(
_('Error'),
_('There is no history of reconciled '
'items on the task: %s.') % rec.name)
_('Error'),
_('There is no history of reconciled '
'items on the task: %s.') % rec.name)
def _open_move_line_list(sefl, cr, uid, move_line_ids, name, context=None):
return {
@@ -275,19 +277,19 @@ class account_easy_reconcile(orm.Model):
'nodestroy': True,
'target': 'current',
'domain': unicode([('id', 'in', move_line_ids)]),
}
}
def open_unreconcile(self, cr, uid, ids, context=None):
""" Open the view of move line with the unreconciled move lines
"""
assert len(ids) == 1 , \
"You can only open entries from one profile at a time"
assert len(ids) == 1, \
"You can only open entries from one profile at a time"
obj_move_line = self.pool.get('account.move.line')
res = {}
for task in self.browse(cr, uid, ids, context=context):
line_ids = obj_move_line.search(
line_ids = obj_move_line.search(
cr, uid,
[('account_id', '=', task.account.id),
('reconcile_id', '=', False),
@@ -301,13 +303,13 @@ class account_easy_reconcile(orm.Model):
""" Open the view of move line with the unreconciled move lines
"""
assert len(ids) == 1 , \
"You can only open entries from one profile at a time"
assert len(ids) == 1, \
"You can only open entries from one profile at a time"
obj_move_line = self.pool.get('account.move.line')
res = {}
for task in self.browse(cr, uid, ids, context=context):
line_ids = obj_move_line.search(
line_ids = obj_move_line.search(
cr, uid,
[('account_id', '=', task.account.id),
('reconcile_id', '=', False),
@@ -322,7 +324,7 @@ class account_easy_reconcile(orm.Model):
"""
if isinstance(rec_id, (tuple, list)):
assert len(rec_id) == 1, \
"Only 1 id expected"
"Only 1 id expected"
rec_id = rec_id[0]
rec = self.browse(cr, uid, rec_id, context=context)
if not rec.last_history:
@@ -335,7 +337,7 @@ class account_easy_reconcile(orm.Model):
"""
if isinstance(rec_id, (tuple, list)):
assert len(rec_id) == 1, \
"Only 1 id expected"
"Only 1 id expected"
rec_id = rec_id[0]
rec = self.browse(cr, uid, rec_id, context=context)
if not rec.last_history:

View File

@@ -24,6 +24,7 @@ from openerp.tools.translate import _
class easy_reconcile_history(orm.Model):
""" Store an history of the runs per profile
Each history stores the list of reconciliations done"""
@@ -40,39 +41,39 @@ class easy_reconcile_history(orm.Model):
move_line_ids = []
for reconcile in history.reconcile_ids:
move_line_ids += [line.id
for line
in reconcile.line_id]
for line
in reconcile.line_id]
result[history.id]['reconcile_line_ids'] = move_line_ids
move_line_ids = []
for reconcile in history.reconcile_partial_ids:
move_line_ids += [line.id
for line
in reconcile.line_partial_ids]
for line
in reconcile.line_partial_ids]
result[history.id]['partial_line_ids'] = move_line_ids
return result
_columns = {
'easy_reconcile_id': fields.many2one(
'account.easy.reconcile', 'Reconcile Profile', readonly=True),
'date': fields.datetime('Run date', readonly=True),
'reconcile_ids': fields.many2many(
'account.move.reconcile',
'account_move_reconcile_history_rel',
string='Reconciliations', readonly=True),
'reconcile_partial_ids': fields.many2many(
'account.move.reconcile',
'account_move_reconcile_history_partial_rel',
string='Partial Reconciliations', readonly=True),
'reconcile_line_ids':
fields.function(
_reconcile_line_ids,
string='Reconciled Items',
type='many2many',
relation='account.move.line',
readonly=True,
multi='lines'),
'easy_reconcile_id': fields.many2one(
'account.easy.reconcile', 'Reconcile Profile', readonly=True),
'date': fields.datetime('Run date', readonly=True),
'reconcile_ids': fields.many2many(
'account.move.reconcile',
'account_move_reconcile_history_rel',
string='Reconciliations', readonly=True),
'reconcile_partial_ids': fields.many2many(
'account.move.reconcile',
'account_move_reconcile_history_partial_rel',
string='Partial Reconciliations', readonly=True),
'reconcile_line_ids':
fields.function(
_reconcile_line_ids,
string='Reconciled Items',
type='many2many',
relation='account.move.line',
readonly=True,
multi='lines'),
'partial_line_ids':
fields.function(
_reconcile_line_ids,
@@ -81,14 +82,14 @@ class easy_reconcile_history(orm.Model):
relation='account.move.line',
readonly=True,
multi='lines'),
'company_id': fields.related('easy_reconcile_id','company_id',
'company_id': fields.related('easy_reconcile_id', 'company_id',
relation='res.company',
type='many2one',
string='Company',
store=True,
readonly=True),
}
}
def _open_move_lines(self, cr, uid, history_id, rec_type='full', context=None):
""" For an history record, open the view of move line with
@@ -99,7 +100,7 @@ class easy_reconcile_history(orm.Model):
:return: action to open the move lines
"""
assert rec_type in ('full', 'partial'), \
"rec_type must be 'full' or 'partial'"
"rec_type must be 'full' or 'partial'"
history = self.browse(cr, uid, history_id, context=context)
@@ -122,7 +123,7 @@ class easy_reconcile_history(orm.Model):
'nodestroy': True,
'target': 'current',
'domain': unicode([('id', 'in', move_line_ids)]),
}
}
def open_reconcile(self, cr, uid, history_ids, context=None):
""" For an history record, open the view of move line
@@ -136,7 +137,7 @@ class easy_reconcile_history(orm.Model):
assert len(history_ids) == 1, "only 1 ID is accepted"
history_ids = history_ids[0]
return self._open_move_lines(
cr, uid, history_ids, rec_type='full', context=None)
cr, uid, history_ids, rec_type='full', context=None)
def open_partial(self, cr, uid, history_ids, context=None):
""" For an history record, open the view of move line
@@ -150,4 +151,4 @@ class easy_reconcile_history(orm.Model):
assert len(history_ids) == 1, "only 1 ID is accepted"
history_ids = history_ids[0]
return self._open_move_lines(
cr, uid, history_ids, rec_type='partial', context=None)
cr, uid, history_ids, rec_type='partial', context=None)

View File

@@ -41,7 +41,7 @@ class easy_reconcile_simple(AbstractModel):
count = 0
res = []
while (count < len(lines)):
for i in xrange(count+1, len(lines)):
for i in xrange(count + 1, len(lines)):
writeoff_account_id = False
if lines[count][self._key_field] != lines[i][self._key_field]:
break
@@ -51,7 +51,7 @@ class easy_reconcile_simple(AbstractModel):
credit_line = lines[count]
debit_line = lines[i]
check = True
elif lines[i]['credit'] > 0 and lines[count]['debit'] > 0:
elif lines[i]['credit'] > 0 and lines[count]['debit'] > 0:
credit_line = lines[i]
debit_line = lines[count]
check = True

View File

@@ -19,15 +19,15 @@
#
##############################################################################
{'name' : 'Invoices Reference',
'version' : '1.0',
'author' : 'Camptocamp',
{'name': 'Invoices Reference',
'version': '1.0',
'author': 'Camptocamp',
'maintainer': 'Camptocamp',
'license': 'AGPL-3',
'category': 'category',
'complexity': "easy",
'depends' : ['account',
],
'depends': ['account',
],
'description': """
Invoices Reference
==================
@@ -143,4 +143,4 @@ Information propagated to the move lines:
],
'installable': True,
'auto_install': False,
}
}

View File

@@ -33,7 +33,8 @@ class account_move(orm.Model):
if invoice:
assert isinstance(invoice, orm.browse_record)
invoice_obj = self.pool['account.invoice']
ref = invoice_obj._ref_from_invoice(cr, uid, invoice, context=context)
ref = invoice_obj._ref_from_invoice(
cr, uid, invoice, context=context)
vals = vals.copy()
vals['ref'] = ref
move_id = super(account_move, self).\

View File

@@ -40,4 +40,4 @@ Needs `statement_voucher_killer`
'test': [],
'installable': True,
'auto_install': True,
}
}

View File

@@ -27,6 +27,7 @@ from openerp.addons.account_statement_base_completion.statement import ErrorTooM
class AccountStatementCompletionRule(Model):
"""Add a rule based on transaction ID"""
_inherit = "account.statement.completion.rule"
@@ -64,15 +65,20 @@ class AccountStatementCompletionRule(Model):
raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more than '
'one partner for account number "%s".') % (st_line['name'], st_line['ref'], partner_acc_number))
if len(ids) == 1:
partner = res_bank_obj.browse(cr, uid, ids[0], context=context).partner_id
partner = res_bank_obj.browse(
cr, uid, ids[0], context=context).partner_id
res['partner_id'] = partner.id
st_vals = st_obj.get_values_for_line(cr,
uid,
profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
partner_id=res.get('partner_id', False),
profile_id=st_line[
'profile_id'],
master_account_id=st_line[
'master_account_id'],
partner_id=res.get(
'partner_id', False),
line_type=st_line['type'],
amount=st_line['amount'] if st_line['amount'] else 0.0,
amount=st_line['amount'] if st_line[
'amount'] else 0.0,
context=context)
res.update(st_vals)
return res

View File

@@ -30,14 +30,20 @@ class bankaccount_completion(common.TransactionCase):
def prepare(self):
self.company_a = self.browse_ref('base.main_company')
self.profile_obj = self.registry("account.statement.profile")
self.account_bank_statement_obj = self.registry("account.bank.statement")
self.account_bank_statement_line_obj = self.registry("account.bank.statement.line")
self.completion_rule_id = self.ref('account_statement_bankaccount_completion.bank_statement_completion_rule_10')
self.journal_id = self.registry("ir.model.data").get_object_reference(self.cr, self. uid, "account", "bank_journal")[1]
self.account_bank_statement_obj = self.registry(
"account.bank.statement")
self.account_bank_statement_line_obj = self.registry(
"account.bank.statement.line")
self.completion_rule_id = self.ref(
'account_statement_bankaccount_completion.bank_statement_completion_rule_10')
self.journal_id = self.registry("ir.model.data").get_object_reference(
self.cr, self. uid, "account", "bank_journal")[1]
self.partner_id = self.ref('base.main_partner')
# Create the profile
self.account_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "a_recv")[1]
self.journal_id = self.registry("ir.model.data").get_object_reference(self.cr, self. uid, "account", "bank_journal")[1]
self.account_id = self.registry("ir.model.data").get_object_reference(
self.cr, self.uid, "account", "a_recv")[1]
self.journal_id = self.registry("ir.model.data").get_object_reference(
self.cr, self. uid, "account", "bank_journal")[1]
self.profile_id = self.profile_obj.create(self.cr, self.uid, {
"name": "TEST",
"commission_account_id": self.account_id,
@@ -82,10 +88,15 @@ class bankaccount_completion(common.TransactionCase):
statement line
"""
self.prepare()
statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line_id)
statement_line = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, self.statement_line_id)
# before import, the
self.assertFalse(statement_line.partner_id, "Partner_id must be blank before completion")
statement_obj = self.account_bank_statement_obj.browse(self.cr, self.uid, self.statement_id)
self.assertFalse(
statement_line.partner_id, "Partner_id must be blank before completion")
statement_obj = self.account_bank_statement_obj.browse(
self.cr, self.uid, self.statement_id)
statement_obj.button_auto_completion()
statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line_id)
self.assertEquals(self.partner_id, statement_line.partner_id['id'], "Missing expected partner id after completion")
statement_line = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, self.statement_line_id)
self.assertEquals(self.partner_id, statement_line.partner_id[
'id'], "Missing expected partner id after completion")

View File

@@ -65,13 +65,13 @@
],
'demo': [],
'test': [
'test/partner.yml',
'test/invoice.yml',
'test/supplier_invoice.yml',
'test/completion_test.yml'
'test/partner.yml',
'test/invoice.yml',
'test/supplier_invoice.yml',
'test/completion_test.yml'
],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}
}

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*-
#################################################################################
##########################################################################
# #
# Copyright (C) 2011 Akretion & Camptocamp
# Author : Sébastien BEAU, Joel Grand-Guillaume #
@@ -17,13 +17,14 @@
# 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
from openerp.osv import fields
class res_partner(Model):
"""
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.
@@ -32,7 +33,7 @@ class res_partner(Model):
_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 this method/rules in your statement profile)."),
}
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 this method/rules in your statement profile)."),
}

View File

@@ -40,10 +40,12 @@ _logger = logging.getLogger(__name__)
class ErrorTooManyPartner(Exception):
"""
New Exception definition that is raised when more than one partner is matched by
the completion rule.
"""
def __init__(self, value):
self.value = value
@@ -55,6 +57,7 @@ class ErrorTooManyPartner(Exception):
class AccountStatementProfil(orm.Model):
"""
Extend the class to add rules per profile that will match at least the partner,
but it could also be used to match other values as well.
@@ -100,7 +103,8 @@ class AccountStatementProfil(orm.Model):
if context is None:
context = {}
if not calls:
calls = self._get_rules(cr, uid, line['profile_id'], context=context)
calls = self._get_rules(
cr, uid, line['profile_id'], context=context)
rule_obj = self.pool.get('account.statement.completion.rule')
for call in calls:
@@ -116,6 +120,7 @@ class AccountStatementProfil(orm.Model):
class AccountStatementCompletionRule(orm.Model):
"""
This will represent all the completion method that we can have to
fullfill the bank statement lines. You'll be able to extend them in you own module
@@ -134,9 +139,12 @@ class AccountStatementCompletionRule(orm.Model):
List of available methods for rules. Override this to add you own.
"""
return [
('get_from_ref_and_invoice', 'From line reference (based on customer invoice number)'),
('get_from_ref_and_supplier_invoice', 'From line reference (based on supplier invoice number)'),
('get_from_label_and_partner_field', 'From line label (based on partner field)'),
('get_from_ref_and_invoice',
'From line reference (based on customer invoice number)'),
('get_from_ref_and_supplier_invoice',
'From line reference (based on supplier invoice number)'),
('get_from_label_and_partner_field',
'From line label (based on partner field)'),
('get_from_label_and_partner_name', 'From line label (based on partner name)')]
def __get_functions(self, cr, uid, context=None):
@@ -272,7 +280,8 @@ class AccountStatementCompletionRule(orm.Model):
[('bank_statement_label', '!=', False)])
line_ids = context.get('line_ids', [])
for partner in partner_obj.browse(cr, uid, partner_ids, context=context):
vals = '|'.join(re.escape(x.strip()) for x in partner.bank_statement_label.split(';'))
vals = '|'.join(re.escape(x.strip())
for x in partner.bank_statement_label.split(';'))
or_regex = ".*%s.*" % vals
sql = ("SELECT id from account_bank_statement_line"
" WHERE id in %s"
@@ -292,11 +301,15 @@ class AccountStatementCompletionRule(orm.Model):
res['partner_id'] = found_partner[0].id
st_vals = st_obj.get_values_for_line(cr,
uid,
profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
partner_id=found_partner[0].id,
profile_id=st_line[
'profile_id'],
master_account_id=st_line[
'master_account_id'],
partner_id=found_partner[
0].id,
line_type=False,
amount=st_line['amount'] if st_line['amount'] else 0.0,
amount=st_line['amount'] if st_line[
'amount'] else 0.0,
context=context)
res.update(st_vals)
return res
@@ -320,7 +333,8 @@ class AccountStatementCompletionRule(orm.Model):
res = {}
# We memoize allowed partner
if not context.get('partner_memoizer'):
context['partner_memoizer'] = tuple(self.pool['res.partner'].search(cr, uid, []))
context['partner_memoizer'] = tuple(
self.pool['res.partner'].search(cr, uid, []))
if not context['partner_memoizer']:
return res
st_obj = self.pool.get('account.bank.statement.line')
@@ -335,7 +349,8 @@ class AccountStatementCompletionRule(orm.Model):
SELECT id, regexp_matches(%s, regexp_replace(name,'([\.\^\$\*\+\?\(\)\[\{\\\|])', %s, 'g'), 'i') AS name_match FROM res_partner
WHERE id IN %s) AS res_patner_matcher
WHERE name_match IS NOT NULL"""
cr.execute(sql, (st_line['name'], r"\\\1", context['partner_memoizer']))
cr.execute(
sql, (st_line['name'], r"\\\1", context['partner_memoizer']))
result = cr.fetchall()
if not result:
return res
@@ -347,14 +362,17 @@ class AccountStatementCompletionRule(orm.Model):
st_vals = st_obj.get_values_for_line(cr,
uid,
profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
master_account_id=st_line[
'master_account_id'],
partner_id=res['partner_id'],
line_type=False,
amount=st_line['amount'] if st_line['amount'] else 0.0,
amount=st_line['amount'] if st_line[
'amount'] else 0.0,
context=context)
res.update(st_vals)
return res
class AccountStatement(orm.Model):
_inherit = "account.bank.statement"
@@ -364,17 +382,18 @@ class AccountStatement(orm.Model):
line_without_account = line_obj.search(cr, uid, [
['statement_id', '=', stat_id],
['account_id', '=', False],
], context=context)
], context=context)
if line_without_account:
stat = self.browse(cr, uid, stat_id, context=context)
raise orm.except_orm(_('User error'),
_('You should fill all account on the line of the'
' statement %s')%stat.name)
_('You should fill all account on the line of the'
' statement %s') % stat.name)
return super(AccountStatement, self).button_confirm_bank(
cr, uid, ids, context=context)
cr, uid, ids, context=context)
class AccountStatementLine(orm.Model):
"""
Add sparse field on the statement line to allow to store all the
bank infos that are given by a bank/office. You can then add you own in your
@@ -424,7 +443,8 @@ class AccountStatementLine(orm.Model):
if line.get('already_completed'):
return {}
# Ask the rule
vals = profile_obj._find_values_from_rules(cr, uid, rules, line, context)
vals = profile_obj._find_values_from_rules(
cr, uid, rules, line, context)
if vals:
vals['id'] = line['id']
return vals
@@ -434,15 +454,16 @@ class AccountStatementLine(orm.Model):
"""Return writeable by SQL columns"""
statement_line_obj = self.pool['account.bank.statement.line']
model_cols = statement_line_obj._columns
avail = [k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')]
avail = [
k for k, col in model_cols.iteritems() if not hasattr(col, '_fnct')]
keys = [k for k in statement_store[0].keys() if k in avail]
# add sparse fields..
if include_serializable:
for k, col in model_cols.iteritems():
if k in statement_store[0].keys() and \
isinstance(col, fields.sparse) and \
col.serialization_field not in keys and \
col._type == 'char':
isinstance(col, fields.sparse) and \
col.serialization_field not in keys and \
col._type == 'char':
keys.append(col.serialization_field)
keys.sort()
return keys
@@ -472,7 +493,8 @@ class AccountStatementLine(orm.Model):
"""
statement_line_obj = self.pool['account.bank.statement.line']
model_cols = statement_line_obj._columns
sparse_fields = dict([(k, col) for k, col in model_cols.iteritems() if isinstance(col, fields.sparse) and col._type == 'char'])
sparse_fields = dict([(k, col) for k, col in model_cols.iteritems() if isinstance(
col, fields.sparse) and col._type == 'char'])
values = []
for statement in statement_store:
to_json_k = set()
@@ -480,7 +502,8 @@ class AccountStatementLine(orm.Model):
for k, col in sparse_fields.iteritems():
if k in st_copy:
to_json_k.add(col.serialization_field)
serialized = st_copy.setdefault(col.serialization_field, {})
serialized = st_copy.setdefault(
col.serialization_field, {})
serialized[k] = st_copy[k]
for k in to_json_k:
st_copy[k] = simplejson.dumps(st_copy[k])
@@ -493,13 +516,16 @@ class AccountStatementLine(orm.Model):
does not exist"""
statement_line_obj = self.pool['account.bank.statement.line']
statement_line_obj.check_access_rule(cr, uid, [], 'create')
statement_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True)
cols = self._get_available_columns(statement_store, include_serializable=True)
statement_line_obj.check_access_rights(
cr, uid, 'create', raise_exception=True)
cols = self._get_available_columns(
statement_store, include_serializable=True)
statement_store = self._prepare_manyinsert(statement_store, cols)
tmp_vals = (', '.join(cols), ', '.join(['%%(%s)s' % i for i in cols]))
sql = "INSERT INTO account_bank_statement_line (%s) VALUES (%s);" % tmp_vals
try:
cr.executemany(sql, tuple(self._serialize_sparse_fields(cols, statement_store)))
cr.executemany(
sql, tuple(self._serialize_sparse_fields(cols, statement_store)))
except psycopg2.Error as sql_err:
cr.rollback()
raise osv.except_osv(_("ORM bypass error"),
@@ -526,6 +552,7 @@ class AccountStatementLine(orm.Model):
class AccountBankStatement(orm.Model):
"""
We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually fullfill.
@@ -556,11 +583,13 @@ class AccountBankStatement(orm.Model):
context=context)['completion_logs']
log = log if log else ""
completion_date = datetime.datetime.now().strftime(DEFAULT_SERVER_DATETIME_FORMAT)
completion_date = datetime.datetime.now().strftime(
DEFAULT_SERVER_DATETIME_FORMAT)
message = (_("%s Bank Statement ID %s has %s/%s lines completed by %s \n%s\n%s\n") %
(completion_date, stat_id, number_imported, number_line, user_name,
error_msg, log))
self.write(cr, uid, [stat_id], {'completion_logs': message}, context=context)
self.write(
cr, uid, [stat_id], {'completion_logs': message}, context=context)
body = (_('Statement ID %s auto-completed for %s/%s lines completed') %
(stat_id, number_imported, number_line)),
@@ -581,14 +610,16 @@ class AccountBankStatement(orm.Model):
profile_obj = self.pool.get('account.statement.profile')
compl_lines = 0
stat_line_obj.check_access_rule(cr, uid, [], 'create')
stat_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True)
stat_line_obj.check_access_rights(
cr, uid, 'create', raise_exception=True)
for stat in self.browse(cr, uid, ids, context=context):
msg_lines = []
ctx = context.copy()
ctx['line_ids'] = tuple((x.id for x in stat.line_ids))
b_profile = stat.profile_id
rules = profile_obj._get_rules(cr, uid, b_profile, context=context)
profile_id = b_profile.id # Only for perfo even it gains almost nothing
# Only for perfo even it gains almost nothing
profile_id = b_profile.id
master_account_id = b_profile.receivable_account_id
master_account_id = master_account_id.id if master_account_id else False
res = False
@@ -606,17 +637,20 @@ class AccountBankStatement(orm.Model):
except Exception, exc:
msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
if res:
# stat_line_obj.write(cr, uid, [line.id], vals, context=ctx)
try:
stat_line_obj._update_line(cr, uid, res, context=context)
stat_line_obj._update_line(
cr, uid, res, context=context)
except Exception as exc:
msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st)
# we can commit as it is not needed to be atomic

View File

@@ -23,22 +23,26 @@ from openerp.tests import common
import time
from collections import namedtuple
name_completion_case = namedtuple("name_completion_case", ["partner_name", "line_label", "should_match"])
name_completion_case = namedtuple(
"name_completion_case", ["partner_name", "line_label", "should_match"])
NAMES_COMPLETION_CASES = [
name_completion_case("Acsone", "Line for Acsone SA", True),
name_completion_case("Acsone", "Line for Acsone", True),
name_completion_case("Acsone", "Acsone for line", True),
name_completion_case("acsone", "Acsone for line", True),
name_completion_case("Acsone SA", "Line for Acsone SA test", True),
name_completion_case("Ac..ne", "Acsone for line", False),
name_completion_case("é@|r{}", "Acsone é@|r{} for line", True),
name_completion_case("Acsone", "A..one for line", False),
name_completion_case("A.one SA", "A.one SA for line", True),
name_completion_case("Acsone SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", False),
name_completion_case("Acsone ([^a-zA-Z0-9 -]) SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", True),
name_completion_case(r"Acsone (.^$*+?()[{\| -]\) SA", r"Line for Acsone (.^$*+?()[{\| -]\) SA test", True),
name_completion_case("Acšone SA", "Line for Acšone SA test", True),
]
name_completion_case("Acsone", "Line for Acsone SA", True),
name_completion_case("Acsone", "Line for Acsone", True),
name_completion_case("Acsone", "Acsone for line", True),
name_completion_case("acsone", "Acsone for line", True),
name_completion_case("Acsone SA", "Line for Acsone SA test", True),
name_completion_case("Ac..ne", "Acsone for line", False),
name_completion_case("é@|r{}", "Acsone é@|r{} for line", True),
name_completion_case("Acsone", "A..one for line", False),
name_completion_case("A.one SA", "A.one SA for line", True),
name_completion_case(
"Acsone SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", False),
name_completion_case(
"Acsone ([^a-zA-Z0-9 -]) SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", True),
name_completion_case(
r"Acsone (.^$*+?()[{\| -]\) SA", r"Line for Acsone (.^$*+?()[{\| -]\) SA test", True),
name_completion_case("Acšone SA", "Line for Acšone SA test", True),
]
class base_completion(common.TransactionCase):
@@ -48,8 +52,10 @@ class base_completion(common.TransactionCase):
self.company_a = self.browse_ref('base.main_company')
self.profile_obj = self.registry("account.statement.profile")
self.partner_obj = self.registry("res.partner")
self.account_bank_statement_obj = self.registry("account.bank.statement")
self.account_bank_statement_line_obj = self.registry("account.bank.statement.line")
self.account_bank_statement_obj = self.registry(
"account.bank.statement")
self.account_bank_statement_line_obj = self.registry(
"account.bank.statement.line")
self.journal_id = self.ref("account.bank_journal")
self.partner_id = self.ref('base.main_partner')
self.account_id = self.ref("account.a_recv")
@@ -60,7 +66,8 @@ class base_completion(common.TransactionCase):
Test the automatic completion of the partner_id based if the name of the partner appears in
the statement line label
"""
self.completion_rule_id = self.ref('account_statement_base_completion.bank_statement_completion_rule_3')
self.completion_rule_id = self.ref(
'account_statement_base_completion.bank_statement_completion_rule_3')
# Create the profile
self.profile_id = self.profile_obj.create(self.cr, self.uid, {
"name": "TEST",
@@ -77,21 +84,26 @@ class base_completion(common.TransactionCase):
})
for case in NAMES_COMPLETION_CASES:
self.partner_obj.write(self.cr, self.uid, self.partner_id, {'name': case.partner_name})
self.partner_obj.write(
self.cr, self.uid, self.partner_id, {'name': case.partner_name})
statement_line_id = self.account_bank_statement_line_obj.create(self.cr, self.uid, {
'amount': 1000.0,
'name': case.line_label,
'ref': 'My ref',
'statement_id': self.statement_id,
})
statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, statement_line_id)
self.assertFalse(statement_line.partner_id, "Partner_id must be blank before completion")
statement_obj = self.account_bank_statement_obj.browse(self.cr, self.uid, self.statement_id)
'amount': 1000.0,
'name': case.line_label,
'ref': 'My ref',
'statement_id': self.statement_id,
})
statement_line = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, statement_line_id)
self.assertFalse(
statement_line.partner_id, "Partner_id must be blank before completion")
statement_obj = self.account_bank_statement_obj.browse(
self.cr, self.uid, self.statement_id)
statement_obj.button_auto_completion()
statement_line = self.account_bank_statement_line_obj.browse(self.cr, self.uid, statement_line_id)
statement_line = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, statement_line_id)
if case.should_match:
self.assertEquals(self.partner_id, statement_line.partner_id['id'],
"Missing expected partner id after completion (partner_name: %s, line_name: %s)" % (case.partner_name, case.line_label))
"Missing expected partner id after completion (partner_name: %s, line_name: %s)" % (case.partner_name, case.line_label))
else:
self.assertNotEquals(self.partner_id, statement_line.partner_id['id'],
"Partner id should be empty after completion(partner_name: %s, line_name: %s)" % (case.partner_name, case.line_label))

View File

@@ -28,7 +28,7 @@
'depends': [
'account_statement_ext',
'account_statement_base_completion'
],
],
'description': """
This module brings basic methods and fields on bank statement to deal with
the importation of different bank and offices. A generic abstract method is defined and an
@@ -58,12 +58,12 @@
""",
'website': 'http://www.camptocamp.com',
'data': [
"wizard/import_statement_view.xml",
"statement_view.xml",
"wizard/import_statement_view.xml",
"statement_view.xml",
],
'test': [],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}
}

View File

@@ -28,12 +28,15 @@ try:
except:
raise Exception(_('Please install python lib xlrd'))
def float_or_zero(val):
""" Conversion function used to manage
empty string into float usecase"""
return float(val) if val else 0.0
class FileParser(BankStatementImportParser):
"""
Generic abstract class for defining parser for .csv, .xls or .xlsx file format.
"""
@@ -48,7 +51,7 @@ class FileParser(BankStatementImportParser):
"""
super(FileParser, self).__init__(parse_name, **kwargs)
if ftype in ('csv', 'xls' ,'xlsx'):
if ftype in ('csv', 'xls', 'xlsx'):
self.ftype = ftype[0:3]
else:
raise except_osv(_('User Error'),
@@ -64,8 +67,8 @@ class FileParser(BankStatementImportParser):
self.keys_to_validate = self.conversion_dict.keys()
self.fieldnames = header
self._datemode = 0 # used only for xls documents,
# 0 means Windows mode (1900 based dates).
# Set in _parse_xls, from the contents of the file
# 0 means Windows mode (1900 based dates).
# Set in _parse_xls, from the contents of the file
def _custom_format(self, *args, **kwargs):
"""
@@ -161,8 +164,10 @@ class FileParser(BankStatementImportParser):
" value: %s \n \n"
" \n Please check the line with ref: %s"
" \n \n Detail: %s") % (rule,
line.get(rule, _('Missing')),
line.get('ref', line),
line.get(
rule, _('Missing')),
line.get(
'ref', line),
repr(err)))
else:
try:
@@ -173,7 +178,8 @@ class FileParser(BankStatementImportParser):
"\n Please check the line with ref %s:"
"\n \n Detail: %s") % (line.get(rule, _('Missing')),
rule,
line.get('ref', line),
line.get(
'ref', line),
repr(err)))
return result_set
@@ -186,7 +192,8 @@ class FileParser(BankStatementImportParser):
for rule in conversion_rules:
if conversion_rules[rule] == datetime.datetime:
try:
t_tuple = xlrd.xldate_as_tuple(line[rule], self._datemode)
t_tuple = xlrd.xldate_as_tuple(
line[rule], self._datemode)
line[rule] = datetime.datetime(*t_tuple)
except Exception as err:
raise except_osv(_("Date format is not valid"),
@@ -195,8 +202,10 @@ class FileParser(BankStatementImportParser):
" value: %s"
"\n Please check the line with ref: %s"
"\n \n Detail: %s") % (rule,
line.get(rule, _('Missing')),
line.get('ref', line),
line.get(
rule, _('Missing')),
line.get(
'ref', line),
repr(err)))
else:
try:
@@ -207,7 +216,8 @@ class FileParser(BankStatementImportParser):
"\n Please check the line with ref %s:"
"\n \n Detail: %s") % (line.get(rule, _('Missing')),
rule,
line.get('ref', line),
line.get(
'ref', line),
repr(err)))
return result_set

View File

@@ -31,6 +31,7 @@ except:
class GenericFileParser(FileParser):
"""
Standard parser that use a define format in csv or xls to import into a
bank statement. This is mostely an example of how to proceed to create a new
@@ -38,7 +39,8 @@ class GenericFileParser(FileParser):
"""
def __init__(self, parse_name, ftype='csv', **kwargs):
super(GenericFileParser, self).__init__(parse_name, ftype=ftype, **kwargs)
super(GenericFileParser, self).__init__(
parse_name, ftype=ftype, **kwargs)
@classmethod
def parser_for(cls, parser_name):

View File

@@ -35,6 +35,7 @@ def UnicodeDictReader(utf8_data, **kwargs):
class BankStatementImportParser(object):
"""
Generic abstract class for defining parser for different files and
format to import in a bank statement. Inherit from it to create your
@@ -125,10 +126,10 @@ class BankStatementImportParser(object):
:return: dict of vals that represent additional infos for the statement
"""
return {
'name': self.statement_name or '/',
'balance_start': self.balance_start,
'balance_end_real': self.balance_end,
'date': self.statement_date or datetime.now()
'name': self.statement_name or '/',
'balance_start': self.balance_start,
'balance_end_real': self.balance_end,
'date': self.statement_date or datetime.now()
}
def get_st_line_vals(self, line, *args, **kwargs):

View File

@@ -46,7 +46,8 @@ class AccountStatementProfil(Model):
help="Tic that box to automatically launch the completion "
"on each imported file using this profile."),
'last_import_date': fields.datetime("Last Import Date"),
# we remove deprecated as it floods logs in standard/warning level sob...
# we remove deprecated as it floods logs in standard/warning level
# sob...
'rec_log': fields.text('log', readonly=True), # Deprecated
'import_type': fields.selection(
__get_import_type_selection,
@@ -57,8 +58,8 @@ class AccountStatementProfil(Model):
}
_defaults = {
'import_type': 'generic_csvxls_so'
}
'import_type': 'generic_csvxls_so'
}
def _write_extra_statement_lines(
self, cr, uid, parser, result_row_list, profile, statement_id, context):
@@ -91,7 +92,7 @@ class AccountStatementProfil(Model):
context=context)
return True
#Deprecated remove on V8
# Deprecated remove on V8
def prepare_statetement_lines_vals(self, *args, **kwargs):
return self.prepare_statement_lines_vals(*args, **kwargs)
@@ -121,11 +122,13 @@ class AccountStatementProfil(Model):
else:
# This is awfully slow...
periods = self.pool.get('account.period').find(cr, uid,
dt=values.get('date'),
dt=values.get(
'date'),
context=context)
values['period_id'] = periods[0]
period_memoizer[date] = periods[0]
values = statement_line_obj._add_missing_default_values(cr, uid, values, context)
values = statement_line_obj._add_missing_default_values(
cr, uid, values, context)
return values
def prepare_statement_vals(self, cr, uid, profile_id, result_row_list,
@@ -160,14 +163,14 @@ class AccountStatementProfil(Model):
prof_obj = self.pool['account.statement.profile']
if not profile_id:
raise osv.except_osv(_("No Profile!"),
_("You must provide a valid profile to import a bank statement!"))
_("You must provide a valid profile to import a bank statement!"))
prof = prof_obj.browse(cr, uid, profile_id, context=context)
parser = new_bank_statement_parser(prof, ftype=ftype)
res = []
for result_row_list in parser.parse(file_stream):
statement_id = self._statement_import(cr, uid, ids, prof, parser,
file_stream, ftype=ftype, context=context)
file_stream, ftype=ftype, context=context)
res.append(statement_id)
return res
@@ -199,7 +202,8 @@ class AccountStatementProfil(Model):
_("Column %s you try to import is not "
"present in the bank statement line!") % col)
statement_vals = self.prepare_statement_vals(cr, uid, prof.id, result_row_list, parser, context)
statement_vals = self.prepare_statement_vals(
cr, uid, prof.id, result_row_list, parser, context)
statement_id = statement_obj.create(cr, uid,
statement_vals,
context=context)
@@ -214,7 +218,8 @@ class AccountStatementProfil(Model):
context)
statement_store.append(values)
# Hack to bypass ORM poor perfomance. Sob...
statement_line_obj._insert_lines(cr, uid, statement_store, context=context)
statement_line_obj._insert_lines(
cr, uid, statement_store, context=context)
self._write_extra_statement_lines(
cr, uid, parser, result_row_list, prof, statement_id, context)
@@ -222,7 +227,8 @@ class AccountStatementProfil(Model):
start_bal = statement_obj.read(
cr, uid, statement_id, ['balance_start'], context=context)
start_bal = start_bal['balance_start']
statement_obj.write(cr, uid, [statement_id], {'balance_start': start_bal})
statement_obj.write(
cr, uid, [statement_id], {'balance_start': start_bal})
attachment_data = {
'name': 'statement file',
@@ -235,7 +241,8 @@ class AccountStatementProfil(Model):
# If user ask to launch completion at end of import, do it!
if prof.launch_import_completion:
statement_obj.button_auto_completion(cr, uid, [statement_id], context)
statement_obj.button_auto_completion(
cr, uid, [statement_id], context)
# Write the needed log infos on profile
self.write_logs_after_import(cr, uid, prof.id,
@@ -245,11 +252,12 @@ class AccountStatementProfil(Model):
except Exception:
error_type, error_value, trbk = sys.exc_info()
st = "Error: %s\nDescription: %s\nTraceback:" % (error_type.__name__, error_value)
st = "Error: %s\nDescription: %s\nTraceback:" % (
error_type.__name__, error_value)
st += ''.join(traceback.format_tb(trbk, 30))
#TODO we should catch correctly the exception with a python
#Exception and only re-catch some special exception.
#For now we avoid re-catching error in debug mode
# TODO we should catch correctly the exception with a python
# Exception and only re-catch some special exception.
# For now we avoid re-catching error in debug mode
if config['debug_mode']:
raise
raise osv.except_osv(_("Statement import error"),

View File

@@ -31,8 +31,10 @@ class test_coda_import(common.TransactionCase):
def prepare(self):
self.company_a = self.browse_ref('base.main_company')
self.profile_obj = self.registry("account.statement.profile")
self.account_bank_statement_obj = self.registry("account.bank.statement")
# create the 2009 fiscal year since imported coda file reference statement lines in 2009
self.account_bank_statement_obj = self.registry(
"account.bank.statement")
# create the 2009 fiscal year since imported coda file reference
# statement lines in 2009
self.fiscalyear_id = self._create_fiscalyear("2011", self.company_a.id)
self.account_id = self.ref("account.a_recv")
@@ -71,15 +73,18 @@ class test_coda_import(common.TransactionCase):
'input_statement': base64.b64encode(content),
'file_name': os.path.basename(file_name),
})
res = self.import_wizard_obj.import_statement(self.cr, self.uid, wizard_id)
statement_id = self.account_bank_statement_obj.search(self.cr, self.uid, eval(res['domain']))
res = self.import_wizard_obj.import_statement(
self.cr, self.uid, wizard_id)
statement_id = self.account_bank_statement_obj.search(
self.cr, self.uid, eval(res['domain']))
return self.account_bank_statement_obj.browse(self.cr, self.uid, statement_id)[0]
def test_simple_xls(self):
"""Test import from xls
"""
self.prepare()
file_name = self._filename_to_abs_filename(os.path.join("..", "data", "statement.xls"))
file_name = self._filename_to_abs_filename(
os.path.join("..", "data", "statement.xls"))
statement = self._import_file(file_name)
self._validate_imported_satement(statement)
@@ -87,7 +92,8 @@ class test_coda_import(common.TransactionCase):
"""Test import from csv
"""
self.prepare()
file_name = self._filename_to_abs_filename(os.path.join("..", "data", "statement.csv"))
file_name = self._filename_to_abs_filename(
os.path.join("..", "data", "statement.csv"))
statement = self._import_file(file_name)
self._validate_imported_satement(statement)

View File

@@ -37,11 +37,13 @@ class CreditPartnerStatementImporter(orm.TransientModel):
context = {}
res = {}
if (context.get('active_model', False) == 'account.statement.profile' and
context.get('active_ids', False)):
context.get('active_ids', False)):
ids = context['active_ids']
assert len(ids) == 1, 'You cannot use this on more than one profile !'
assert len(
ids) == 1, 'You cannot use this on more than one profile !'
res['profile_id'] = ids[0]
other_vals = self.onchange_profile_id(cr, uid, [], res['profile_id'], context=context)
other_vals = self.onchange_profile_id(
cr, uid, [], res['profile_id'], context=context)
res.update(other_vals.get('value', {}))
return res
@@ -72,21 +74,21 @@ class CreditPartnerStatementImporter(orm.TransientModel):
res = {}
if profile_id:
c = self.pool.get("account.statement.profile").browse(
cr, uid, profile_id, context=context)
cr, uid, profile_id, context=context)
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,
'receivable_account_id': c.receivable_account_id and c.receivable_account_id.id or False,
'force_partner_on_bank': c.force_partner_on_bank,
'balance_check': c.balance_check,
}
}
{'partner_id': c.partner_id and c.partner_id.id or False,
'journal_id': c.journal_id and c.journal_id.id or False,
'receivable_account_id': c.receivable_account_id and c.receivable_account_id.id or False,
'force_partner_on_bank': c.force_partner_on_bank,
'balance_check': c.balance_check,
}
}
return res
def _check_extension(self, filename):
(__, ftype) = os.path.splitext(filename)
if not ftype:
#We do not use osv exception we do not want to have it logged
# We do not use osv exception we do not want to have it logged
raise Exception(_('Please use a file with an extention'))
return ftype
@@ -99,18 +101,19 @@ class CreditPartnerStatementImporter(orm.TransientModel):
ftype = self._check_extension(importer.file_name)
context['file_name'] = importer.file_name
sid = self.pool.get(
'account.statement.profile').multi_statement_import(
cr,
uid,
False,
importer.profile_id.id,
importer.input_statement,
ftype.replace('.', ''),
context=context
)
'account.statement.profile').multi_statement_import(
cr,
uid,
False,
importer.profile_id.id,
importer.input_statement,
ftype.replace('.', ''),
context=context
)
model_obj = self.pool.get('ir.model.data')
action_obj = self.pool.get('ir.actions.act_window')
action_id = model_obj.get_object_reference(cr, uid, 'account', 'action_bank_statement_tree')[1]
action_id = model_obj.get_object_reference(
cr, uid, 'account', 'action_bank_statement_tree')[1]
res = action_obj.read(cr, uid, action_id)
res['domain'] = res['domain'][:-1] + ",('id', 'in', %s)]" % sid
return res

View File

@@ -34,7 +34,7 @@ class wizard_cancel_statement(orm.TransientModel):
'Show reconcile warning',
help='This is a hidden field set with a default in the context '
'to choose between two different warning messages in the view.'
),
),
}
def do_cancel_button(self, cr, uid, ids, context=None):

View File

@@ -34,14 +34,14 @@
This module brings commission support to bank statement imports. It computes the sum of a commission
field on each transaction and creates a statement entry for it.
""",
'website': 'http://www.camptocamp.com',
'data': [
"statement_view.xml",
"import_statement_view.xml",
],
'test': [],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
'website': 'http://www.camptocamp.com',
'data': [
"statement_view.xml",
"import_statement_view.xml",
],
'test': [],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}

View File

@@ -16,7 +16,8 @@ class AccountStatementProfil(orm.Model):
"""
global_commission_amount = 0
for row in parser.result_row_list:
global_commission_amount += float_or_zero(row.get('commission_amount', '0.0'))
global_commission_amount += float_or_zero(
row.get('commission_amount', '0.0'))
if not global_commission_amount:
return
partner_id = profile.partner_id and profile.partner_id.id or False
@@ -56,7 +57,7 @@ class CreditPartnerStatementImporter(orm.TransientModel):
'commission_account_id': fields.many2one('account.account',
'Commission account'),
'commission_analytic_id': fields.many2one('account.analytic.account',
'Commission analytic account'),
'Commission analytic account'),
}
def onchange_profile_id(self, cr, uid, ids, profile_id, context=None):
@@ -64,7 +65,7 @@ class CreditPartnerStatementImporter(orm.TransientModel):
cr, uid, ids, profile_id, context=context)
if profile_id:
c = self.pool.get("account.statement.profile").browse(
cr, uid, profile_id, context=context)
cr, uid, profile_id, context=context)
res['value']['commission_account_id'] = \
c.commission_account_id and c.commission_account_id.id or False
res['value']['commission_a'] = \

View File

@@ -22,4 +22,3 @@
from . import partner
from . import statement

View File

@@ -37,13 +37,12 @@
'website': 'http://www.akretion.com/',
'depends': ['account_statement_base_completion'],
'data': [
'partner_view.xml',
'statement_view.xml',
'security/ir.model.access.csv',
'security/ir_rule.xml',
'partner_view.xml',
'statement_view.xml',
'security/ir.model.access.csv',
'security/ir_rule.xml',
],
'demo': [],
'installable': True,
'active': False,
}

View File

@@ -26,10 +26,12 @@ from openerp.addons.account_statement_base_completion.statement import ErrorTooM
class ErrorTooManyLabel(Exception):
"""
New Exception definition that is raised when more than one label is matched
by the completion rule.
"""
def __init__(self, value):
self.value = value
@@ -38,6 +40,7 @@ class ErrorTooManyLabel(Exception):
class AccountBankSatement(orm.Model):
"""
We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually fullfill.
@@ -114,6 +117,7 @@ class AccountStatementCompletionRule(orm.Model):
class AccountStatementLabel(orm.Model):
"""Create a new class to map an account statement label to a partner
and a specific account
"""
@@ -125,7 +129,7 @@ class AccountStatementLabel(orm.Model):
'partner_id': fields.many2one('res.partner', 'Partner'),
'label': fields.char('Bank Statement Label', size=100),
'account_id': fields.many2one('account.account', 'Account',
required = True,
required=True,
help='Account corresponding to the label '
'for a given partner'),
'company_id': fields.related('account_id', 'company_id',
@@ -139,7 +143,7 @@ class AccountStatementLabel(orm.Model):
}
_defaults = {
'company_id': lambda s,cr,uid,c:
'company_id': lambda s, cr, uid, c:
s.pool.get('res.company')._company_default_get(cr, uid,
'account.statement.label',
context=c),

View File

@@ -28,14 +28,14 @@
'depends': [
'account_statement_base_completion',
'account_voucher'
],
],
'description': """
This module is only needed when using account_statement_base_completion with voucher in order adapt the view correctly.
""",
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [
"statement_view.xml",
"statement_view.xml",
],
'demo_xml': [],
'test': [],
@@ -43,4 +43,4 @@
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}
}

View File

@@ -28,15 +28,17 @@ from openerp.addons.report_webkit import webkit_report
class BankStatementWebkit(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context):
super(BankStatementWebkit, self).__init__(cr, uid, name, context=context)
super(BankStatementWebkit, self).__init__(
cr, 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
self.cr, uid, uid, context=context).company_id
header_report_name = ' - '.join((_('BORDEREAU DE REMISE DE CHEQUES'),
company.name, company.currency_id.name))
footer_date_time = self.formatLang(str(datetime.today())[:19], date_time=True)
footer_date_time = self.formatLang(
str(datetime.today())[:19], date_time=True)
self.localcontext.update({
'cr': cr,
'uid': uid,
@@ -50,7 +52,8 @@ class BankStatementWebkit(report_sxw.rml_parse):
('--header-left', header_report_name),
('--header-spacing', '2'),
('--footer-left', footer_date_time),
('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
('--footer-right',
' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
('--footer-line',),
],
})
@@ -58,11 +61,11 @@ class BankStatementWebkit(report_sxw.rml_parse):
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)])
self.cr,
self.uid,
[('statement_id', '=', statement.id)])
statement_lines = statement_obj.browse(
self.cr, self.uid, statement_line_ids)
self.cr, self.uid, statement_line_ids)
return statement_lines
webkit_report.WebKitParser('report.bank_statement_webkit',

View File

@@ -31,7 +31,7 @@ def fixed_write(self, cr, uid, ids, vals, context=None):
I will do it when I have time."""
res = super(stat_mod.account_bank_statement, self).write(cr, uid, ids,
vals, context=context)
if ids: # will be false for an new empty bank statement
if ids: # will be false for an new empty bank statement
cr.execute("UPDATE account_bank_statement_line"
" SET sequence = account_bank_statement_line.id + 1"
" where statement_id in %s", (tuple(ids),))
@@ -40,6 +40,7 @@ stat_mod.account_bank_statement.write = fixed_write
class AccountStatementProfile(Model):
"""
A Profile will contain all infos related to the type of
bank statement, and related generated entries. It defines the
@@ -57,20 +58,20 @@ class AccountStatementProfile(Model):
'partner_id': fields.many2one(
'res.partner',
'Bank/Payment Office partner',
help="Put a partner if you want to have it on the "
"commission move (and optionaly on the counterpart "
"of the intermediate/banking move if you tick the "
"corresponding checkbox)."),
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 tick the "
"corresponding checkbox)."),
'journal_id': fields.many2one(
'account.journal',
'Financial journal to use for transaction',
required=True),
'Financial journal to use for transaction',
required=True),
'commission_account_id': fields.many2one(
'account.account',
'Commission account',
required=True),
'Commission account',
required=True),
'commission_analytic_id': fields.many2one(
'account.analytic.account',
@@ -110,16 +111,18 @@ class AccountStatementProfile(Model):
return True
_constraints = [
(_check_partner, "You need to put a partner if you tic the 'Force partner on bank move'!", []),
(_check_partner,
"You need to put a partner if you tic the 'Force partner on bank move'!", []),
]
_sql_constraints = [
('name_uniq', 'unique (name, company_id)', 'The name of the bank statement must be unique !')
('name_uniq', 'unique (name, company_id)',
'The name of the bank statement must be unique !')
]
class AccountBankStatement(Model):
"""
We improve the bank statement class mostly for :
- Removing the period and compute it from the date of each line.
@@ -140,7 +143,8 @@ class AccountBankStatement(Model):
if context is None:
context = {}
period_obj = self.pool.get('account.period')
periods = period_obj.find(cr, uid, dt=context.get('date'), context=context)
periods = period_obj.find(
cr, uid, dt=context.get('date'), context=context)
return periods and periods[0] or False
def _default_profile(self, cr, uid, context=None):
@@ -155,7 +159,8 @@ class AccountBankStatement(Model):
user_obj = self.pool.get('res.users')
profile_obj = self.pool.get('account.statement.profile')
user = user_obj.browse(cr, uid, uid, context=context)
profile_ids = profile_obj.search(cr, uid, [('company_id', '=', user.company_id.id)], context=context)
profile_ids = profile_obj.search(
cr, uid, [('company_id', '=', user.company_id.id)], context=context)
return profile_ids[0] if profile_ids else False
@@ -219,11 +224,11 @@ class AccountBankStatement(Model):
},
readonly=True),
'period_id': fields.many2one(
'account.period',
'Period',
required=False,
readonly=False,
invisible=True),
'account.period',
'Period',
required=False,
readonly=False,
invisible=True),
}
_defaults = {
@@ -236,7 +241,8 @@ class AccountBankStatement(Model):
need it."""
if 'profile_id' in vals:
profile_obj = self.pool.get('account.statement.profile')
profile = profile_obj.browse(cr, uid, vals['profile_id'], context=context)
profile = profile_obj.browse(
cr, uid, vals['profile_id'], context=context)
vals['journal_id'] = profile.journal_id.id
return super(AccountBankStatement, self
).create(cr, uid, vals, context=context)
@@ -319,12 +325,12 @@ class AccountBankStatement(Model):
if context is None:
context = {}
res = super(AccountBankStatement, self)._prepare_move_line_vals(
cr, uid, st_line, move_id, debit, credit,
currency_id=currency_id,
amount_currency=amount_currency,
account_id=account_id,
analytic_id=analytic_id,
partner_id=partner_id, context=context)
cr, uid, st_line, move_id, debit, credit,
currency_id=currency_id,
amount_currency=amount_currency,
account_id=account_id,
analytic_id=analytic_id,
partner_id=partner_id, context=context)
ctx = context.copy()
ctx['company_id'] = st_line.company_id.id
period_id = self._get_period(cr, uid, st_line.date, context=ctx)
@@ -362,16 +368,19 @@ class AccountBankStatement(Model):
"""
year = self.pool.get('account.period').browse(
cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id
profile = self.pool.get('account.statement.profile').browse(cr, uid, profile_id)
cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id
profile = self.pool.get(
'account.statement.profile').browse(cr, uid, profile_id)
c = {'fiscalyear_id': year}
obj_seq = self.pool.get('ir.sequence')
journal_sequence_id = (profile.journal_id.sequence_id and
profile.journal_id.sequence_id.id or False)
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:
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
@@ -393,7 +402,8 @@ class AccountBankStatement(Model):
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)
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):
raise osv.except_osv(_('Configuration Error!'),
@@ -402,8 +412,9 @@ class AccountBankStatement(Model):
if not st.name == '/':
st_number = st.name
else:
# Begin Changes
st_number = self._get_st_number_period_profile(cr, uid, st.date, st.profile_id.id)
# Begin Changes
st_number = self._get_st_number_period_profile(
cr, uid, st.date, st.profile_id.id)
# End Changes
for line in st.move_line_ids:
if line.state != 'valid':
@@ -420,16 +431,19 @@ class AccountBankStatement(Model):
" journal on the '%s' journal!") % st.journal_id.name)
if not st_line.amount:
continue
st_line_number = self.get_next_st_line_number(cr, uid, st_number, st_line, context)
st_line_number = self.get_next_st_line_number(
cr, uid, st_number, st_line, context)
self.create_move_from_st_line(cr, uid, st_line.id,
company_currency_id,
st_line_number,
context)
except osv.except_osv, exc:
msg = "Line ID %s with ref %s had following error: %s" % (st_line.id, st_line.ref, exc.value)
msg = "Line ID %s with ref %s had following error: %s" % (
st_line.id, st_line.ref, exc.value)
errors_stack.append(msg)
except Exception, exc:
msg = "Line ID %s with ref %s had following error: %s" % (st_line.id, st_line.ref, str(exc))
msg = "Line ID %s with ref %s had following error: %s" % (
st_line.id, st_line.ref, str(exc))
errors_stack.append(msg)
if errors_stack:
msg = u"\n".join(errors_stack)
@@ -439,7 +453,8 @@ class AccountBankStatement(Model):
{'name': st_number,
'balance_end_real': st.balance_end},
context=context)
body = _('Statement %s confirmed, journal items were created.') % st_number
body = _(
'Statement %s confirmed, journal items were created.') % st_number
self.message_post(cr, uid, [st.id],
body,
context=context)
@@ -515,7 +530,8 @@ class AccountBankStatement(Model):
as 'customer' or 'supplier'.
"""
account_id = False
ltype = self.get_type_for_counterpart(cr, uid, amount, partner_id=partner_id)
ltype = self.get_type_for_counterpart(
cr, uid, amount, partner_id=partner_id)
if ltype == 'supplier':
account_id = account_payable
else:
@@ -574,13 +590,14 @@ class AccountBankStatement(Model):
if not profile_id:
return {}
import_config = self.pool.get("account.statement.profile").browse(
cr, uid, profile_id, context=context)
cr, uid, profile_id, context=context)
journal_id = import_config.journal_id.id
return {'value': {'journal_id': journal_id,
'balance_check': import_config.balance_check}}
class AccountBankStatementLine(Model):
"""
Override to compute the period from the date of the line, add a method to retrieve
the values for a line from the profile. Override the on_change method to take care of
@@ -663,7 +680,7 @@ class AccountBankStatementLine(Model):
# on profile
if profile_id and master_account_id is None:
profile = self.pool.get("account.statement.profile").browse(
cr, uid, profile_id, context=context)
cr, uid, profile_id, context=context)
if profile.receivable_account_id:
res['account_id'] = profile.receivable_account_id.id
# We return general as default instead of get_type_for_counterpart
@@ -684,7 +701,8 @@ class AccountBankStatementLine(Model):
receiv_account = part.property_account_receivable.id
# If no value, look on the default company property
if not pay_account or not receiv_account:
receiv_account, pay_account = obj_stat.get_default_pay_receiv_accounts(cr, uid, context=None)
receiv_account, pay_account = obj_stat.get_default_pay_receiv_accounts(
cr, uid, context=None)
account_id, comp_line_type = obj_stat.get_account_and_type_for_counterpart(cr, uid, amount,
receiv_account, pay_account,
partner_id=partner_id)
@@ -702,8 +720,10 @@ class AccountBankStatementLine(Model):
obj_stat = self.pool.get('account.bank.statement')
if not partner_id:
return {}
line_type = obj_stat.get_type_for_counterpart(cr, uid, 0.0, partner_id=partner_id)
res_type = self.onchange_type(cr, uid, ids, partner_id, line_type, profile_id, context=context)
line_type = obj_stat.get_type_for_counterpart(
cr, uid, 0.0, partner_id=partner_id)
res_type = self.onchange_type(
cr, uid, ids, partner_id, line_type, profile_id, context=context)
if res_type['value'] and res_type['value'].get('account_id', False):
return {'value': {'type': line_type,
'account_id': res_type['value']['account_id'],

View File

@@ -30,11 +30,11 @@ class AccountVoucher(Model):
"""If period not in context, take it from the move lines"""
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'), context=context)[0].period_id.id
cr, uid, context.get('move_line_ids'), context=context)[0].period_id.id
context['period_id'] = res
elif context.get('date'):
periods = self.pool.get('account.period').find(
cr, uid, dt=context['date'], context=context)
cr, uid, dt=context['date'], context=context)
if periods:
context['period_id'] = periods[0]
return super(AccountVoucher, self)._get_period(cr, uid, context)

View File

@@ -36,18 +36,19 @@ if not hasattr(std_pos_session, '_prepare_bank_statement'):
# This change has been proposed for merging to fix lp:125375
def mp_prepare_bank_statement(self, cr, uid, pos_config, journal, context=None):
bank_values = {
'journal_id' : journal.id,
'user_id' : uid,
'company_id' : pos_config.shop_id.company_id.id
}
'journal_id': journal.id,
'user_id': uid,
'company_id': pos_config.shop_id.company_id.id
}
return bank_values
def mp_create(self, cr, uid, values, context=None):
context = context or {}
config_id = values.get('config_id', False) or context.get('default_config_id', False)
config_id = values.get('config_id', False) or context.get(
'default_config_id', False)
if not config_id:
raise osv.except_osv( _('Error!'),
_("You should assign a Point of Sale to your session."))
raise osv.except_osv(_('Error!'),
_("You should assign a Point of Sale to your session."))
# journal_id is not required on the pos_config because it does not
# exists at the installation. If nothing is configured at the
@@ -57,35 +58,42 @@ if not hasattr(std_pos_session, '_prepare_bank_statement'):
pos_config = jobj.browse(cr, uid, config_id, context=context)
context.update({'company_id': pos_config.shop_id.company_id.id})
if not pos_config.journal_id:
jid = jobj.default_get(cr, uid, ['journal_id'], context=context)['journal_id']
jid = jobj.default_get(
cr, uid, ['journal_id'], context=context)['journal_id']
if jid:
jobj.write(cr, uid, [pos_config.id], {'journal_id': jid}, context=context)
jobj.write(
cr, uid, [pos_config.id], {'journal_id': jid}, context=context)
else:
raise osv.except_osv( _('error!'),
_("Unable to open the session. You have to assign a sale journal to your point of sale."))
raise osv.except_osv(_('error!'),
_("Unable to open the session. You have to assign a sale journal to your point of sale."))
# define some cash journal if no payment method exists
if not pos_config.journal_ids:
journal_proxy = self.pool.get('account.journal')
cashids = journal_proxy.search(cr, uid, [('journal_user', '=', True), ('type','=','cash')], context=context)
cashids = journal_proxy.search(
cr, uid, [('journal_user', '=', True), ('type', '=', 'cash')], context=context)
if not cashids:
cashids = journal_proxy.search(cr, uid, [('type', '=', 'cash')], context=context)
cashids = journal_proxy.search(
cr, uid, [('type', '=', 'cash')], context=context)
if not cashids:
cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context)
jobj.write(cr, uid, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
cashids = journal_proxy.search(
cr, uid, [('journal_user', '=', True)], context=context)
jobj.write(
cr, uid, [pos_config.id], {'journal_ids': [(6, 0, cashids)]})
pos_config = jobj.browse(cr, uid, config_id, context=context)
bank_statement_ids = []
for journal in pos_config.journal_ids:
bank_values = self._prepare_bank_statement(cr, uid, pos_config, journal, context)
statement_id = self.pool.get('account.bank.statement').create(cr, uid, bank_values, context=context)
bank_values = self._prepare_bank_statement(
cr, uid, pos_config, journal, context)
statement_id = self.pool.get('account.bank.statement').create(
cr, uid, bank_values, context=context)
bank_statement_ids.append(statement_id)
values.update({
'name' : pos_config.sequence_id._next(),
'statement_ids' : [(6, 0, bank_statement_ids)],
'name': pos_config.sequence_id._next(),
'statement_ids': [(6, 0, bank_statement_ids)],
'config_id': config_id
})
return super(std_pos_session, self).create(cr, uid, values, context=context)
@@ -111,7 +119,8 @@ class pos_session(orm.Model):
profile_obj = self.pool.get('account.statement.profile')
user = user_obj.browse(cr, uid, uid, context=context)
defaults = self.pool['account.bank.statement'].default_get(cr, uid,
['profile_id', 'period_id'],
['profile_id',
'period_id'],
context=context)
profile_ids = profile_obj.search(cr, uid,
[('company_id', '=', user.company_id.id),

View File

@@ -28,7 +28,7 @@
'depends': [
'account_statement_ext',
'account_voucher'
],
],
'description': """
This module is deprecated. It was 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.
@@ -40,7 +40,7 @@
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [
"statement_voucher_view.xml",
"statement_voucher_view.xml",
],
'demo_xml': [],
'test': [],
@@ -49,4 +49,4 @@
'auto_install': False,
'license': 'AGPL-3',
}
}

View File

@@ -32,11 +32,11 @@ class AccountVoucher(Model):
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'), context=context)[0].period_id.id
cr, uid, context.get('move_line_ids'), context=context)[0].period_id.id
context['period_id'] = res
elif context.get('date'):
periods = self.pool.get('account.period').find(
cr, uid, dt=context['date'], context=context)
cr, uid, dt=context['date'], context=context)
if periods:
context['period_id'] = periods[0]
return super(AccountVoucher, self)._get_period(cr, uid, context)

View File

@@ -19,4 +19,3 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
###############################################################################

View File

@@ -30,7 +30,7 @@
'website': 'http://www.akretion.com/',
'depends': [
'account_voucher',
],
],
'data': [
'statement_view.xml',
],

View File

@@ -46,4 +46,4 @@
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}
}

View File

@@ -19,4 +19,3 @@
#
##############################################################################
from . import ofx_parser

View File

@@ -30,7 +30,9 @@ try:
except:
raise Exception(_('Please install python lib ofxparse'))
class OfxParser(BankStatementImportParser):
"""Class for defining parser for OFX file format."""
@classmethod
@@ -110,4 +112,3 @@ class OfxParser(BankStatementImportParser):
'ref': line.get('ref', '/'),
'label': line.get('label', ''),
}

View File

@@ -21,6 +21,7 @@
from openerp.tools.translate import _
from openerp.osv import fields, orm
class AccountStatementProfil(orm.Model):
_inherit = "account.statement.profile"
@@ -30,6 +31,6 @@ class AccountStatementProfil(orm.Model):
"""
selection = super(AccountStatementProfil, self
)._get_import_type_selection(cr, uid,
context=context)
context=context)
selection.append(('ofx_so', _('OFX - Open Financial Exchange')))
return selection

View File

@@ -36,6 +36,7 @@ class AccountStatementProfile(orm.Model):
"for the refunds and one for the payments.")
}
class account_bank_statement(orm.Model):
_inherit = "account.bank.statement"
@@ -49,41 +50,43 @@ class account_bank_statement(orm.Model):
'period_id': period_id,
'date': st_line.statement_id.date,
'name': st_line.ref,
})
})
return res
return res
def _prepare_move(self, cr, uid, st_line, st_line_number, context=None):
res = super(account_bank_statement, self).\
_prepare_move(cr, uid, st_line, st_line_number, context=context)
_prepare_move(cr, uid, st_line, st_line_number, context=context)
res.update({
'ref': st_line.statement_id.name,
'name': st_line.statement_id.name,
'date': st_line.statement_id.date,
})
})
return res
def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id,
st_line_number, context=None):
if context is None:
context = {}
context['from_parent_object'] = True #For compability with module account_constraints
# For compability with module account_constraints
context['from_parent_object'] = True
account_move_obj = self.pool.get('account.move')
account_bank_statement_line_obj = self.pool.get('account.bank.statement.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)
st = st_line.statement_id
if st.profile_id.one_move:
if not context.get('move_id'):
move_vals = self._prepare_move(cr, uid, st_line, st_line_number, context=context)
context['move_id'] = account_move_obj.create(cr, uid, move_vals, context=context)
move_vals = self._prepare_move(
cr, uid, st_line, st_line_number, context=context)
context['move_id'] = account_move_obj.create(
cr, uid, move_vals, context=context)
self.create_move_line_from_st_line(cr, uid, context['move_id'],
st_line_id, company_currency_id,
context=context)
st_line_id, company_currency_id,
context=context)
return context['move_id']
else:
return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line_id,
@@ -92,7 +95,7 @@ class account_bank_statement(orm.Model):
context=context)
def create_move_line_from_st_line(self, cr, uid, move_id, st_line_id,
company_currency_id, context=None):
company_currency_id, context=None):
"""Create the account move line from the statement line.
:param int/long move_id: ID of the account.move
@@ -104,23 +107,26 @@ class account_bank_statement(orm.Model):
context = {}
res_currency_obj = self.pool.get('res.currency')
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')
st_line = account_bank_statement_line_obj.browse(
cr, uid, st_line_id, context=context)
st = st_line.statement_id
context.update({'date': st_line.date})
acc_cur = ((st_line.amount<=0) and st.journal_id.default_debit_account_id) or st_line.account_id
acc_cur = ((st_line.amount <= 0)
and st.journal_id.default_debit_account_id) or st_line.account_id
context.update({
'res.currency.compute.account': acc_cur,
})
'res.currency.compute.account': acc_cur,
})
amount = res_currency_obj.compute(cr, uid, st.currency.id,
company_currency_id,
st_line.amount,
context=context)
bank_move_vals = self._prepare_bank_move_line(cr, uid, st_line, move_id, amount,
company_currency_id, context=context)
company_currency_id, context=context)
return account_move_line_obj.create(cr, uid, bank_move_vals, context=context)
def _valid_move(self, cr, uid, move_id, context=None):
@@ -129,7 +135,6 @@ class account_bank_statement(orm.Model):
move_obj.post(cr, uid, [move_id], context=context)
return True
def _prepare_transfer_move_line_vals(self, cr, uid, st, name, amount, move_id, context=None):
"""
Prepare the dict of values to create the transfer move lines.
@@ -157,7 +162,6 @@ class account_bank_statement(orm.Model):
}
return vals
def create_move_transfer_lines(self, cr, uid, move, st, context=None):
move_line_obj = self.pool.get('account.move.line')
move_id = move.id
@@ -165,11 +169,11 @@ class account_bank_statement(orm.Model):
payment = 0.0
transfer_lines = []
transfer_line_ids = []
#Calculate the part of the refund amount and the payment amount
# Calculate the part of the refund amount and the payment amount
for move_line in move.line_id:
refund -= move_line.debit
payment += move_line.credit
#Create 2 Transfer lines or One global tranfer line
# Create 2 Transfer lines or One global tranfer line
if st.profile_id.split_transfer_line:
if refund:
transfer_lines.append(['Refund Transfer', refund])
@@ -185,10 +189,10 @@ class account_bank_statement(orm.Model):
transfer_line[1],
move_id,
context=context)
transfer_line_ids.append(move_line_obj.create(cr, uid, vals, context=context))
transfer_line_ids.append(
move_line_obj.create(cr, uid, vals, context=context))
return transfer_line_ids
def button_confirm_bank(self, cr, uid, ids, context=None):
st_line_obj = self.pool.get('account.bank.statement.line')
move_obj = self.pool.get('account.move')
@@ -200,12 +204,13 @@ class account_bank_statement(orm.Model):
if st.profile_id.one_move and context.get('move_id', False):
move_id = context['move_id']
move = move_obj.browse(cr, uid, move_id, context=context)
transfe_line_ids = self.create_move_transfer_lines(cr, uid, move, st, context=context)
transfe_line_ids = self.create_move_transfer_lines(
cr, uid, move, st, context=context)
self._valid_move(cr, uid, move_id, context=context)
lines_ids = [x.id for x in st.line_ids]
st_line_obj.write(cr, uid, lines_ids,
{'move_ids': [(4, move_id, False)]},
context=context)
{'move_ids': [(4, move_id, False)]},
context=context)
return True
def button_cancel(self, cr, uid, ids, context=None):
@@ -216,10 +221,8 @@ class account_bank_statement(orm.Model):
if move.state != 'draft':
move.button_cancel(context=context)
move.unlink(context=context)
st.write({'state':'draft'}, context=context)
st.write({'state': 'draft'}, context=context)
else:
super(account_bank_statement, self).button_cancel(cr, uid, ids,
context=context)
context=context)
return True

View File

@@ -44,7 +44,7 @@ Account Statement Regex Account Completion addon
and update account to use in the bank statement line with the specified account.
""",
"data": ['statement_view.xml',
],
],
"demo": [],
"test": [],
"active": False,

View File

@@ -35,6 +35,7 @@ import re
class AccountStatementCompletionRule(Model):
"""Add a rule to complete account based on a regular expression"""
_inherit = "account.statement.completion.rule"

View File

@@ -36,14 +36,17 @@ ACC_NUMBER = "BE38733040385372"
class test_regex_account_completion(common.TransactionCase):
def prepare(self):
self.account_bank_statement_obj = self.registry("account.bank.statement")
self.account_bank_statement_line_obj = self.registry("account.bank.statement.line")
self.account_bank_statement_obj = self.registry(
"account.bank.statement")
self.account_bank_statement_line_obj = self.registry(
"account.bank.statement.line")
self.account_id = self.ref('account.a_expense')
# create the completion rule
rule_vals = {'function_to_call': 'set_account',
'regex': '^My statement',
'account_id': self.account_id}
completion_rule_id = self.registry("account.statement.completion.rule").create(self.cr, self.uid, rule_vals)
completion_rule_id = self.registry(
"account.statement.completion.rule").create(self.cr, self.uid, rule_vals)
# Create the profile
journal_id = self.ref("account.bank_journal")
@@ -83,9 +86,14 @@ class test_regex_account_completion(common.TransactionCase):
"""Test the automatic completion on account
"""
self.prepare()
statement_obj = self.account_bank_statement_obj.browse(self.cr, self.uid, self.statement_id)
statement_obj = self.account_bank_statement_obj.browse(
self.cr, self.uid, self.statement_id)
statement_obj.button_auto_completion()
statement_line1 = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line1_id)
self.assertEquals(self.account_id, statement_line1.account_id.id, "The account should be the account of the completion")
statement_line2 = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line2_id)
self.assertNotEqual(self.account_id, statement_line2.account_id.id, "The account should be not the account of the completion")
statement_line1 = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, self.statement_line1_id)
self.assertEquals(self.account_id, statement_line1.account_id.id,
"The account should be the account of the completion")
statement_line2 = self.account_bank_statement_line_obj.browse(
self.cr, self.uid, self.statement_line2_id)
self.assertNotEqual(self.account_id, statement_line2.account_id.id,
"The account should be not the account of the completion")

View File

@@ -28,7 +28,7 @@
'depends': [
'account_statement_base_completion',
'base_transaction_id'
],
],
'description': """
Add a completion method based on transaction ID providen by the bank/office.
@@ -44,8 +44,8 @@
'website': 'http://www.camptocamp.com',
'init_xml': [],
'update_xml': [
"statement_view.xml",
"data.xml",
"statement_view.xml",
"data.xml",
],
'demo_xml': [],
'test': [
@@ -58,4 +58,4 @@
'images': [],
'auto_install': True,
'license': 'AGPL-3',
}
}

View File

@@ -26,13 +26,14 @@ from openerp.addons.account_statement_base_completion.statement import ErrorTooM
class AccountStatementCompletionRule(Model):
"""Add a rule based on transaction ID"""
_inherit = "account.statement.completion.rule"
def _get_functions(self, cr, uid, context=None):
res = super(AccountStatementCompletionRule, self)._get_functions(
cr, uid, context=context)
cr, uid, context=context)
res += [
('get_from_transaction_id_and_so',
'Match Sales Order using transaction ID'),
@@ -59,7 +60,8 @@ class AccountStatementCompletionRule(Model):
so_obj = self.pool.get('sale.order')
so_id = so_obj.search(cr,
uid,
[('transaction_id', '=', st_line['transaction_id'])],
[('transaction_id', '=',
st_line['transaction_id'])],
context=context)
if len(so_id) > 1:
raise ErrorTooManyPartner(_('Line named "%s" (Ref:%s) was matched by more than '
@@ -70,11 +72,15 @@ class AccountStatementCompletionRule(Model):
res['ref'] = so.name
st_vals = st_obj.get_values_for_line(cr,
uid,
profile_id=st_line['profile_id'],
master_account_id=st_line['master_account_id'],
partner_id=res.get('partner_id', False),
profile_id=st_line[
'profile_id'],
master_account_id=st_line[
'master_account_id'],
partner_id=res.get(
'partner_id', False),
line_type=st_line['type'],
amount=st_line['amount'] if st_line['amount'] else 0.0,
amount=st_line['amount'] if st_line[
'amount'] else 0.0,
context=context)
res.update(st_vals)
return res

View File

@@ -28,7 +28,7 @@
'depends': [
'account_statement_base_import',
'account_statement_transactionid_completion'
],
],
'description': """
This module brings generic methods and fields on bank statement to deal with
the importation of different bank and offices that uses transactionID.
@@ -57,4 +57,4 @@
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}
}

View File

@@ -22,6 +22,7 @@ from account_statement_base_import.parser.file_parser import FileParser
class TransactionIDFileParser(FileParser):
"""
TransactionID parser that use a define format in csv or xls to import
bank statement.
@@ -40,7 +41,8 @@ class TransactionIDFileParser(FileParser):
super(TransactionIDFileParser, self).__init__(profile, extra_fields=extra_fields,
ftype=ftype, header=header, **kwargs)
# ref is replaced by transaction_id thus we delete it from check
self.keys_to_validate = [k for k in self.keys_to_validate if k != 'ref']
self.keys_to_validate = [
k for k in self.keys_to_validate if k != 'ref']
del self.conversion_dict['ref']
@classmethod

View File

@@ -31,7 +31,7 @@ class AccountStatementProfil(Model):
Has to be inherited to add parser
"""
res = super(AccountStatementProfil, self)._get_import_type_selection(
cr, uid, context=context)
cr, uid, context=context)
res.append(('generic_csvxls_transaction',
'Generic .csv/.xls based on SO transaction ID'))
return res

View File

@@ -29,7 +29,7 @@
'account',
'sale',
'stock'
],
],
'description': """
Adds transaction id to invoice and sale models and views.
On Sales order, you can specify the transaction ID used
@@ -47,11 +47,11 @@
'update_xml': [
'invoice_view.xml',
'sale_view.xml'
],
],
'demo_xml': [],
'test': [],
'installable': True,
'images': [],
'auto_install': False,
'license': 'AGPL-3',
}
}

View File

@@ -35,9 +35,9 @@ class SaleOrder(Model):
}
def _prepare_invoice(self, cr, uid, order, lines, context=None):
#we put the transaction id in the generated invoices
# we put the transaction id in the generated invoices
invoice_vals = super(SaleOrder, self)._prepare_invoice(
cr, uid, order, lines, context=context)
cr, uid, order, lines, context=context)
invoice_vals.update({
'transaction_id': order.transaction_id})
return invoice_vals

View File

@@ -29,14 +29,14 @@ class StockPicking(Model):
self, cr, uid, ids, journal_id=False, group=False,
type='out_invoice', context=None):
res = super(StockPicking, self).action_invoice_create(
cr, uid, ids, journal_id, group, type, context)
cr, uid, ids, journal_id, group, type, context)
for pick_id in res:
pick = self.browse(cr, uid, pick_id, context=context)
if pick.sale_id and pick.sale_id.transaction_id:
self.pool.get('account.invoice').write(
cr,
uid,
res[pick_id],
{'transaction_id': pick.sale_id.transaction_id},
context=context)
cr,
uid,
res[pick_id],
{'transaction_id': pick.sale_id.transaction_id},
context=context)
return res

View File

@@ -41,7 +41,7 @@ line will be take from imported line in this order:
'depends': ['account_voucher', 'account_payment'],
'data': [
'statement_view.xml',
],
],
'test': [],
'installable': True,
'active': False,

View File

@@ -45,7 +45,8 @@ class AccountStatementFromInvoiceLines(orm.TransientModel):
statement_line_obj = self.pool.get('account.bank.statement.line')
currency_obj = self.pool.get('res.currency')
line_date = time.strftime('%Y-%m-%d')
statement = statement_obj.browse(cr, uid, statement_id, context=context)
statement = statement_obj.browse(
cr, uid, statement_id, context=context)
# for each selected move lines
for line in line_obj.browse(cr, uid, line_ids, context=context):
ctx = context.copy()
@@ -60,10 +61,10 @@ class AccountStatementFromInvoiceLines(orm.TransientModel):
if line.amount_currency:
amount = currency_obj.compute(cr, uid, line.currency_id.id,
statement.currency.id, line.amount_currency, context=ctx)
statement.currency.id, line.amount_currency, context=ctx)
elif (line.invoice and line.invoice.currency_id.id != statement.currency.id):
amount = currency_obj.compute(cr, uid, line.invoice.currency_id.id,
statement.currency.id, amount, context=ctx)
statement.currency.id, amount, context=ctx)
context.update({'move_line_ids': [line.id],
'invoice_id': line.invoice.id})
@@ -109,13 +110,15 @@ class AccountPaymentPopulateStatement(orm.TransientModel):
if not line_ids:
return {'type': 'ir.actions.act_window_close'}
statement = statement_obj.browse(cr, uid, context['active_id'], context=context)
statement = statement_obj.browse(
cr, uid, context['active_id'], context=context)
for line in line_obj.browse(cr, uid, line_ids, context=context):
ctx = context.copy()
ctx['date'] = line.ml_maturity_date # Last value_date earlier,but this field exists no more now
# Last value_date earlier,but this field exists no more now
ctx['date'] = line.ml_maturity_date
amount = currency_obj.compute(cr, uid, line.currency.id,
statement.currency.id, line.amount_currency, context=ctx)
statement.currency.id, line.amount_currency, context=ctx)
if not line.move_line_id.id:
continue
context.update({'move_line_ids': [line.move_line_id.id]})
@@ -124,7 +127,8 @@ class AccountPaymentPopulateStatement(orm.TransientModel):
st_line_id = statement_line_obj.create(cr, uid, vals,
context=context)
line_obj.write(cr, uid, [line.id], {'bank_statement_line_id': st_line_id})
line_obj.write(
cr, uid, [line.id], {'bank_statement_line_id': st_line_id})
return {'type': 'ir.actions.act_window_close'}
def _prepare_statement_line_vals(self, cr, uid, payment_line, amount,