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, 'auto_install': False,
'license': 'AGPL-3', 'license': 'AGPL-3',
'application': True, 'application': True,
} }

View File

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

View File

@@ -32,9 +32,9 @@ Reconcile rules with transaction_ref
'depends': ['account_advanced_reconcile'], 'depends': ['account_advanced_reconcile'],
'data': ['easy_reconcile_view.xml'], 'data': ['easy_reconcile_view.xml'],
'demo': [], '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, 'auto_install': False,
'installable': True, 'installable': True,
'images': [] 'images': []
} }
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@@ -19,7 +19,7 @@
############################################################################## ##############################################################################
from openerp.osv import orm from openerp.osv import orm
class easy_reconcile_advanced_transaction_ref(orm.TransientModel): class easy_reconcile_advanced_transaction_ref(orm.TransientModel):
@@ -35,10 +35,10 @@ class easy_reconcile_advanced_transaction_ref(orm.TransientModel):
return not (move_line.get('transaction_ref') and return not (move_line.get('transaction_ref') and
move_line.get('partner_id')) move_line.get('partner_id'))
def _matchers(self, cr, uid, rec, move_line, context=None): def _matchers(self, cr, uid, rec, move_line, context=None):
return (('partner_id', move_line['partner_id']), return (('partner_id', move_line['partner_id']),
('ref', move_line['transaction_ref'].lower().strip())) ('ref', move_line['transaction_ref'].lower().strip()))
def _opposite_matchers(self, cr, uid, rec, move_line, context=None): def _opposite_matchers(self, cr, uid, rec, move_line, context=None):
yield ('partner_id', move_line['partner_id']) yield ('partner_id', move_line['partner_id'])
yield ('ref', (move_line['transaction_ref'] or '').lower().strip()) yield ('ref', (move_line['transaction_ref'] or '').lower().strip())

View File

@@ -26,7 +26,7 @@ from openerp.osv import orm
class easy_reconcile_advanced(orm.AbstractModel): class easy_reconcile_advanced(orm.AbstractModel):
_inherit = 'easy.reconcile.advanced' _inherit = 'easy.reconcile.advanced'
def _base_columns(self, rec): def _base_columns(self, rec):
""" Mandatory columns for move lines queries """ Mandatory columns for move lines queries
An extra column aliased as ``key`` should be defined An extra column aliased as ``key`` should be defined
@@ -43,4 +43,4 @@ class easy_reconcile_advanced(orm.AbstractModel):
'account_id', 'account_id',
'move_id', 'move_id',
'transaction_ref') 'transaction_ref')
return ["account_move_line.%s" % col for col in aml_cols] return ["account_move_line.%s" % col for col in aml_cols]

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -33,7 +33,8 @@ class account_move(orm.Model):
if invoice: if invoice:
assert isinstance(invoice, orm.browse_record) assert isinstance(invoice, orm.browse_record)
invoice_obj = self.pool['account.invoice'] 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 = vals.copy()
vals['ref'] = ref vals['ref'] = ref
move_id = super(account_move, self).\ move_id = super(account_move, self).\

View File

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

View File

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

View File

@@ -30,14 +30,20 @@ class bankaccount_completion(common.TransactionCase):
def prepare(self): def prepare(self):
self.company_a = self.browse_ref('base.main_company') self.company_a = self.browse_ref('base.main_company')
self.profile_obj = self.registry("account.statement.profile") self.profile_obj = self.registry("account.statement.profile")
self.account_bank_statement_obj = self.registry("account.bank.statement") self.account_bank_statement_obj = self.registry(
self.account_bank_statement_line_obj = self.registry("account.bank.statement.line") "account.bank.statement")
self.completion_rule_id = self.ref('account_statement_bankaccount_completion.bank_statement_completion_rule_10') self.account_bank_statement_line_obj = self.registry(
self.journal_id = self.registry("ir.model.data").get_object_reference(self.cr, self. uid, "account", "bank_journal")[1] "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') self.partner_id = self.ref('base.main_partner')
# Create the profile # Create the profile
self.account_id = self.registry("ir.model.data").get_object_reference(self.cr, self.uid, "account", "a_recv")[1] self.account_id = self.registry("ir.model.data").get_object_reference(
self.journal_id = self.registry("ir.model.data").get_object_reference(self.cr, self. uid, "account", "bank_journal")[1] 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, { self.profile_id = self.profile_obj.create(self.cr, self.uid, {
"name": "TEST", "name": "TEST",
"commission_account_id": self.account_id, "commission_account_id": self.account_id,
@@ -77,15 +83,20 @@ class bankaccount_completion(common.TransactionCase):
def test_00(self): def test_00(self):
"""Test complete partner_id from bank account number """Test complete partner_id from bank account number
Test the automatic completion of the partner_id based on the account number associated to the Test the automatic completion of the partner_id based on the account number associated to the
statement line statement line
""" """
self.prepare() 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 # before import, the
self.assertFalse(statement_line.partner_id, "Partner_id must be blank before completion") self.assertFalse(
statement_obj = self.account_bank_statement_obj.browse(self.cr, self.uid, self.statement_id) 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_obj.button_auto_completion()
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.assertEquals(self.partner_id, statement_line.partner_id['id'], "Missing expected partner id after completion") 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': [], 'demo': [],
'test': [ 'test': [
'test/partner.yml', 'test/partner.yml',
'test/invoice.yml', 'test/invoice.yml',
'test/supplier_invoice.yml', 'test/supplier_invoice.yml',
'test/completion_test.yml' 'test/completion_test.yml'
], ],
'installable': True, 'installable': True,
'images': [], 'images': [],
'auto_install': False, 'auto_install': False,
'license': 'AGPL-3', 'license': 'AGPL-3',
} }

View File

@@ -1,5 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
################################################################################# ##########################################################################
# # # #
# Copyright (C) 2011 Akretion & Camptocamp # Copyright (C) 2011 Akretion & Camptocamp
# Author : Sébastien BEAU, Joel Grand-Guillaume # # Author : Sébastien BEAU, Joel Grand-Guillaume #
@@ -17,13 +17,14 @@
# You should have received a copy of the GNU Affero General Public License # # You should have received a copy of the GNU Affero General Public License #
# along with this program. If not, see <http://www.gnu.org/licenses/>. # # along with this program. If not, see <http://www.gnu.org/licenses/>. #
# # # #
################################################################################# ##########################################################################
from openerp.osv.orm import Model from openerp.osv.orm import Model
from openerp.osv import fields from openerp.osv import fields
class res_partner(Model): class res_partner(Model):
""" """
Add a bank label on the partner so that we can use it to match 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. this partner when we found this in a statement line.
@@ -32,7 +33,7 @@ class res_partner(Model):
_columns = { _columns = {
'bank_statement_label': fields.char('Bank Statement Label', size=100, 'bank_statement_label': fields.char('Bank Statement Label', size=100,
help="Enter the various label found on your bank statement separated by a ; If " 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 " "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)."), "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): class ErrorTooManyPartner(Exception):
""" """
New Exception definition that is raised when more than one partner is matched by New Exception definition that is raised when more than one partner is matched by
the completion rule. the completion rule.
""" """
def __init__(self, value): def __init__(self, value):
self.value = value self.value = value
@@ -55,6 +57,7 @@ class ErrorTooManyPartner(Exception):
class AccountStatementProfil(orm.Model): class AccountStatementProfil(orm.Model):
""" """
Extend the class to add rules per profile that will match at least the partner, 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. but it could also be used to match other values as well.
@@ -100,7 +103,8 @@ class AccountStatementProfil(orm.Model):
if context is None: if context is None:
context = {} context = {}
if not calls: 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') rule_obj = self.pool.get('account.statement.completion.rule')
for call in calls: for call in calls:
@@ -116,6 +120,7 @@ class AccountStatementProfil(orm.Model):
class AccountStatementCompletionRule(orm.Model): class AccountStatementCompletionRule(orm.Model):
""" """
This will represent all the completion method that we can have to 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 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. List of available methods for rules. Override this to add you own.
""" """
return [ return [
('get_from_ref_and_invoice', 'From line reference (based on customer invoice number)'), ('get_from_ref_and_invoice',
('get_from_ref_and_supplier_invoice', 'From line reference (based on supplier invoice number)'), 'From line reference (based on customer invoice number)'),
('get_from_label_and_partner_field', 'From line label (based on partner field)'), ('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)')] ('get_from_label_and_partner_name', 'From line label (based on partner name)')]
def __get_functions(self, cr, uid, context=None): def __get_functions(self, cr, uid, context=None):
@@ -272,7 +280,8 @@ class AccountStatementCompletionRule(orm.Model):
[('bank_statement_label', '!=', False)]) [('bank_statement_label', '!=', False)])
line_ids = context.get('line_ids', []) line_ids = context.get('line_ids', [])
for partner in partner_obj.browse(cr, uid, partner_ids, context=context): 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 or_regex = ".*%s.*" % vals
sql = ("SELECT id from account_bank_statement_line" sql = ("SELECT id from account_bank_statement_line"
" WHERE id in %s" " WHERE id in %s"
@@ -292,11 +301,15 @@ class AccountStatementCompletionRule(orm.Model):
res['partner_id'] = found_partner[0].id res['partner_id'] = found_partner[0].id
st_vals = st_obj.get_values_for_line(cr, st_vals = st_obj.get_values_for_line(cr,
uid, uid,
profile_id=st_line['profile_id'], profile_id=st_line[
master_account_id=st_line['master_account_id'], 'profile_id'],
partner_id=found_partner[0].id, master_account_id=st_line[
'master_account_id'],
partner_id=found_partner[
0].id,
line_type=False, 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) context=context)
res.update(st_vals) res.update(st_vals)
return res return res
@@ -320,7 +333,8 @@ class AccountStatementCompletionRule(orm.Model):
res = {} res = {}
# We memoize allowed partner # We memoize allowed partner
if not context.get('partner_memoizer'): 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']: if not context['partner_memoizer']:
return res return res
st_obj = self.pool.get('account.bank.statement.line') 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 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 id IN %s) AS res_patner_matcher
WHERE name_match IS NOT NULL""" 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() result = cr.fetchall()
if not result: if not result:
return res return res
@@ -347,14 +362,17 @@ class AccountStatementCompletionRule(orm.Model):
st_vals = st_obj.get_values_for_line(cr, st_vals = st_obj.get_values_for_line(cr,
uid, uid,
profile_id=st_line['profile_id'], 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'], partner_id=res['partner_id'],
line_type=False, 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) context=context)
res.update(st_vals) res.update(st_vals)
return res return res
class AccountStatement(orm.Model): class AccountStatement(orm.Model):
_inherit = "account.bank.statement" _inherit = "account.bank.statement"
@@ -364,17 +382,18 @@ class AccountStatement(orm.Model):
line_without_account = line_obj.search(cr, uid, [ line_without_account = line_obj.search(cr, uid, [
['statement_id', '=', stat_id], ['statement_id', '=', stat_id],
['account_id', '=', False], ['account_id', '=', False],
], context=context) ], context=context)
if line_without_account: if line_without_account:
stat = self.browse(cr, uid, stat_id, context=context) stat = self.browse(cr, uid, stat_id, context=context)
raise orm.except_orm(_('User error'), raise orm.except_orm(_('User error'),
_('You should fill all account on the line of the' _('You should fill all account on the line of the'
' statement %s')%stat.name) ' statement %s') % stat.name)
return super(AccountStatement, self).button_confirm_bank( return super(AccountStatement, self).button_confirm_bank(
cr, uid, ids, context=context) cr, uid, ids, context=context)
class AccountStatementLine(orm.Model): class AccountStatementLine(orm.Model):
""" """
Add sparse field on the statement line to allow to store all the 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 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'): if line.get('already_completed'):
return {} return {}
# Ask the rule # 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: if vals:
vals['id'] = line['id'] vals['id'] = line['id']
return vals return vals
@@ -434,15 +454,16 @@ class AccountStatementLine(orm.Model):
"""Return writeable by SQL columns""" """Return writeable by SQL columns"""
statement_line_obj = self.pool['account.bank.statement.line'] statement_line_obj = self.pool['account.bank.statement.line']
model_cols = statement_line_obj._columns 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] keys = [k for k in statement_store[0].keys() if k in avail]
# add sparse fields.. # add sparse fields..
if include_serializable: if include_serializable:
for k, col in model_cols.iteritems(): for k, col in model_cols.iteritems():
if k in statement_store[0].keys() and \ if k in statement_store[0].keys() and \
isinstance(col, fields.sparse) and \ isinstance(col, fields.sparse) and \
col.serialization_field not in keys and \ col.serialization_field not in keys and \
col._type == 'char': col._type == 'char':
keys.append(col.serialization_field) keys.append(col.serialization_field)
keys.sort() keys.sort()
return keys return keys
@@ -472,7 +493,8 @@ class AccountStatementLine(orm.Model):
""" """
statement_line_obj = self.pool['account.bank.statement.line'] statement_line_obj = self.pool['account.bank.statement.line']
model_cols = statement_line_obj._columns 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 = [] values = []
for statement in statement_store: for statement in statement_store:
to_json_k = set() to_json_k = set()
@@ -480,7 +502,8 @@ class AccountStatementLine(orm.Model):
for k, col in sparse_fields.iteritems(): for k, col in sparse_fields.iteritems():
if k in st_copy: if k in st_copy:
to_json_k.add(col.serialization_field) 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] serialized[k] = st_copy[k]
for k in to_json_k: for k in to_json_k:
st_copy[k] = simplejson.dumps(st_copy[k]) st_copy[k] = simplejson.dumps(st_copy[k])
@@ -493,13 +516,16 @@ class AccountStatementLine(orm.Model):
does not exist""" does not exist"""
statement_line_obj = self.pool['account.bank.statement.line'] statement_line_obj = self.pool['account.bank.statement.line']
statement_line_obj.check_access_rule(cr, uid, [], 'create') statement_line_obj.check_access_rule(cr, uid, [], 'create')
statement_line_obj.check_access_rights(cr, uid, 'create', raise_exception=True) statement_line_obj.check_access_rights(
cols = self._get_available_columns(statement_store, include_serializable=True) cr, uid, 'create', raise_exception=True)
cols = self._get_available_columns(
statement_store, include_serializable=True)
statement_store = self._prepare_manyinsert(statement_store, cols) statement_store = self._prepare_manyinsert(statement_store, cols)
tmp_vals = (', '.join(cols), ', '.join(['%%(%s)s' % i for i in 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 sql = "INSERT INTO account_bank_statement_line (%s) VALUES (%s);" % tmp_vals
try: 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: except psycopg2.Error as sql_err:
cr.rollback() cr.rollback()
raise osv.except_osv(_("ORM bypass error"), raise osv.except_osv(_("ORM bypass error"),
@@ -526,6 +552,7 @@ class AccountStatementLine(orm.Model):
class AccountBankStatement(orm.Model): class AccountBankStatement(orm.Model):
""" """
We add a basic button and stuff to support the auto-completion We add a basic button and stuff to support the auto-completion
of the bank statement once line have been imported or manually fullfill. of the bank statement once line have been imported or manually fullfill.
@@ -556,11 +583,13 @@ class AccountBankStatement(orm.Model):
context=context)['completion_logs'] context=context)['completion_logs']
log = log if log else "" 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") % 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, (completion_date, stat_id, number_imported, number_line, user_name,
error_msg, log)) 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') % body = (_('Statement ID %s auto-completed for %s/%s lines completed') %
(stat_id, number_imported, number_line)), (stat_id, number_imported, number_line)),
@@ -581,14 +610,16 @@ class AccountBankStatement(orm.Model):
profile_obj = self.pool.get('account.statement.profile') profile_obj = self.pool.get('account.statement.profile')
compl_lines = 0 compl_lines = 0
stat_line_obj.check_access_rule(cr, uid, [], 'create') 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): for stat in self.browse(cr, uid, ids, context=context):
msg_lines = [] msg_lines = []
ctx = context.copy() ctx = context.copy()
ctx['line_ids'] = tuple((x.id for x in stat.line_ids)) ctx['line_ids'] = tuple((x.id for x in stat.line_ids))
b_profile = stat.profile_id b_profile = stat.profile_id
rules = profile_obj._get_rules(cr, uid, b_profile, context=context) 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 = b_profile.receivable_account_id
master_account_id = master_account_id.id if master_account_id else False master_account_id = master_account_id.id if master_account_id else False
res = False res = False
@@ -606,17 +637,20 @@ class AccountBankStatement(orm.Model):
except Exception, exc: except Exception, exc:
msg_lines.append(repr(exc)) msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info() 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)) st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st) _logger.error(st)
if res: if res:
# stat_line_obj.write(cr, uid, [line.id], vals, context=ctx) # stat_line_obj.write(cr, uid, [line.id], vals, context=ctx)
try: 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: except Exception as exc:
msg_lines.append(repr(exc)) msg_lines.append(repr(exc))
error_type, error_value, trbk = sys.exc_info() 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)) st += ''.join(traceback.format_tb(trbk, 30))
_logger.error(st) _logger.error(st)
# we can commit as it is not needed to be atomic # we can commit as it is not needed to be atomic

View File

@@ -23,22 +23,26 @@ from openerp.tests import common
import time import time
from collections import namedtuple 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 = [ NAMES_COMPLETION_CASES = [
name_completion_case("Acsone", "Line for Acsone SA", True), name_completion_case("Acsone", "Line for Acsone SA", True),
name_completion_case("Acsone", "Line for Acsone", 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", "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("Acsone SA", "Line for Acsone SA test", True),
name_completion_case("Ac..ne", "Acsone for line", False), name_completion_case("Ac..ne", "Acsone for line", False),
name_completion_case("é@|r{}", "Acsone é@|r{} for line", True), name_completion_case("é@|r{}", "Acsone é@|r{} for line", True),
name_completion_case("Acsone", "A..one for line", False), name_completion_case("Acsone", "A..one for line", False),
name_completion_case("A.one SA", "A.one SA for line", True), 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(
name_completion_case("Acsone ([^a-zA-Z0-9 -]) SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", True), "Acsone SA", "Line for Acsone ([^a-zA-Z0-9 -]) SA test", False),
name_completion_case(r"Acsone (.^$*+?()[{\| -]\) SA", r"Line for Acsone (.^$*+?()[{\| -]\) SA test", True), name_completion_case(
name_completion_case("Acšone SA", "Line for Acšone SA test", True), "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): class base_completion(common.TransactionCase):
@@ -48,8 +52,10 @@ class base_completion(common.TransactionCase):
self.company_a = self.browse_ref('base.main_company') self.company_a = self.browse_ref('base.main_company')
self.profile_obj = self.registry("account.statement.profile") self.profile_obj = self.registry("account.statement.profile")
self.partner_obj = self.registry("res.partner") self.partner_obj = self.registry("res.partner")
self.account_bank_statement_obj = self.registry("account.bank.statement") self.account_bank_statement_obj = self.registry(
self.account_bank_statement_line_obj = self.registry("account.bank.statement.line") "account.bank.statement")
self.account_bank_statement_line_obj = self.registry(
"account.bank.statement.line")
self.journal_id = self.ref("account.bank_journal") self.journal_id = self.ref("account.bank_journal")
self.partner_id = self.ref('base.main_partner') self.partner_id = self.ref('base.main_partner')
self.account_id = self.ref("account.a_recv") 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 Test the automatic completion of the partner_id based if the name of the partner appears in
the statement line label 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 # Create the profile
self.profile_id = self.profile_obj.create(self.cr, self.uid, { self.profile_id = self.profile_obj.create(self.cr, self.uid, {
"name": "TEST", "name": "TEST",
@@ -77,21 +84,26 @@ class base_completion(common.TransactionCase):
}) })
for case in NAMES_COMPLETION_CASES: 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, { statement_line_id = self.account_bank_statement_line_obj.create(self.cr, self.uid, {
'amount': 1000.0, 'amount': 1000.0,
'name': case.line_label, 'name': case.line_label,
'ref': 'My ref', 'ref': 'My ref',
'statement_id': self.statement_id, 'statement_id': self.statement_id,
}) })
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.assertFalse(statement_line.partner_id, "Partner_id must be blank before completion") self.cr, self.uid, statement_line_id)
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_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: if case.should_match:
self.assertEquals(self.partner_id, statement_line.partner_id['id'], 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: else:
self.assertNotEquals(self.partner_id, statement_line.partner_id['id'], 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)) "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': [ 'depends': [
'account_statement_ext', 'account_statement_ext',
'account_statement_base_completion' 'account_statement_base_completion'
], ],
'description': """ 'description': """
This module brings basic methods and fields on bank statement to deal with 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 the importation of different bank and offices. A generic abstract method is defined and an
@@ -58,12 +58,12 @@
""", """,
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'data': [ 'data': [
"wizard/import_statement_view.xml", "wizard/import_statement_view.xml",
"statement_view.xml", "statement_view.xml",
], ],
'test': [], 'test': [],
'installable': True, 'installable': True,
'images': [], 'images': [],
'auto_install': False, 'auto_install': False,
'license': 'AGPL-3', 'license': 'AGPL-3',
} }

View File

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

View File

@@ -31,6 +31,7 @@ except:
class GenericFileParser(FileParser): class GenericFileParser(FileParser):
""" """
Standard parser that use a define format in csv or xls to import into a 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 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): 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 @classmethod
def parser_for(cls, parser_name): def parser_for(cls, parser_name):

View File

@@ -35,6 +35,7 @@ def UnicodeDictReader(utf8_data, **kwargs):
class BankStatementImportParser(object): class BankStatementImportParser(object):
""" """
Generic abstract class for defining parser for different files and Generic abstract class for defining parser for different files and
format to import in a bank statement. Inherit from it to create your 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: dict of vals that represent additional infos for the statement
""" """
return { return {
'name': self.statement_name or '/', 'name': self.statement_name or '/',
'balance_start': self.balance_start, 'balance_start': self.balance_start,
'balance_end_real': self.balance_end, 'balance_end_real': self.balance_end,
'date': self.statement_date or datetime.now() 'date': self.statement_date or datetime.now()
} }
def get_st_line_vals(self, line, *args, **kwargs): def get_st_line_vals(self, line, *args, **kwargs):
@@ -221,7 +222,7 @@ def itersubclasses(cls, _seen=None):
def new_bank_statement_parser(profile, *args, **kwargs): def new_bank_statement_parser(profile, *args, **kwargs):
"""Return an instance of the good parser class based on the given profile. """Return an instance of the good parser class based on the given profile.
:param profile: browse_record of import profile. :param profile: browse_record of import profile.
:return: class instance for given profile import type. :return: class instance for given profile import type.
""" """

View File

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

View File

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

View File

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

View File

@@ -34,7 +34,7 @@ class wizard_cancel_statement(orm.TransientModel):
'Show reconcile warning', 'Show reconcile warning',
help='This is a hidden field set with a default in the context ' help='This is a hidden field set with a default in the context '
'to choose between two different warning messages in the view.' 'to choose between two different warning messages in the view.'
), ),
} }
def do_cancel_button(self, cr, uid, ids, context=None): 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 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. field on each transaction and creates a statement entry for it.
""", """,
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'data': [ 'data': [
"statement_view.xml", "statement_view.xml",
"import_statement_view.xml", "import_statement_view.xml",
], ],
'test': [], 'test': [],
'installable': True, 'installable': True,
'images': [], 'images': [],
'auto_install': False, 'auto_install': False,
'license': 'AGPL-3', 'license': 'AGPL-3',
} }

View File

@@ -16,7 +16,8 @@ class AccountStatementProfil(orm.Model):
""" """
global_commission_amount = 0 global_commission_amount = 0
for row in parser.result_row_list: 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: if not global_commission_amount:
return return
partner_id = profile.partner_id and profile.partner_id.id or False 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_id': fields.many2one('account.account',
'Commission account'), 'Commission account'),
'commission_analytic_id': fields.many2one('account.analytic.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): 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) cr, uid, ids, profile_id, context=context)
if profile_id: if profile_id:
c = self.pool.get("account.statement.profile").browse( 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'] = \ res['value']['commission_account_id'] = \
c.commission_account_id and c.commission_account_id.id or False c.commission_account_id and c.commission_account_id.id or False
res['value']['commission_a'] = \ res['value']['commission_a'] = \

View File

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

View File

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

View File

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

View File

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

View File

@@ -22,4 +22,4 @@
import statement import statement
import report import report
import account import account
import voucher import voucher

View File

@@ -28,15 +28,17 @@ from openerp.addons.report_webkit import webkit_report
class BankStatementWebkit(report_sxw.rml_parse): class BankStatementWebkit(report_sxw.rml_parse):
def __init__(self, cr, uid, name, context): 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.pool = pooler.get_pool(self.cr.dbname)
self.cursor = self.cr self.cursor = self.cr
company = self.pool.get('res.users').browse( 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'), header_report_name = ' - '.join((_('BORDEREAU DE REMISE DE CHEQUES'),
company.name, company.currency_id.name)) 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({ self.localcontext.update({
'cr': cr, 'cr': cr,
'uid': uid, 'uid': uid,
@@ -50,7 +52,8 @@ class BankStatementWebkit(report_sxw.rml_parse):
('--header-left', header_report_name), ('--header-left', header_report_name),
('--header-spacing', '2'), ('--header-spacing', '2'),
('--footer-left', footer_date_time), ('--footer-left', footer_date_time),
('--footer-right', ' '.join((_('Page'), '[page]', _('of'), '[topage]'))), ('--footer-right',
' '.join((_('Page'), '[page]', _('of'), '[topage]'))),
('--footer-line',), ('--footer-line',),
], ],
}) })
@@ -58,11 +61,11 @@ class BankStatementWebkit(report_sxw.rml_parse):
def _get_bank_statement_data(self, statement): def _get_bank_statement_data(self, statement):
statement_obj = self.pool.get('account.bank.statement.line') statement_obj = self.pool.get('account.bank.statement.line')
statement_line_ids = statement_obj.search( statement_line_ids = statement_obj.search(
self.cr, self.cr,
self.uid, self.uid,
[('statement_id', '=', statement.id)]) [('statement_id', '=', statement.id)])
statement_lines = statement_obj.browse( statement_lines = statement_obj.browse(
self.cr, self.uid, statement_line_ids) self.cr, self.uid, statement_line_ids)
return statement_lines return statement_lines
webkit_report.WebKitParser('report.bank_statement_webkit', 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.""" I will do it when I have time."""
res = super(stat_mod.account_bank_statement, self).write(cr, uid, ids, res = super(stat_mod.account_bank_statement, self).write(cr, uid, ids,
vals, context=context) 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" cr.execute("UPDATE account_bank_statement_line"
" SET sequence = account_bank_statement_line.id + 1" " SET sequence = account_bank_statement_line.id + 1"
" where statement_id in %s", (tuple(ids),)) " where statement_id in %s", (tuple(ids),))
@@ -40,6 +40,7 @@ stat_mod.account_bank_statement.write = fixed_write
class AccountStatementProfile(Model): class AccountStatementProfile(Model):
""" """
A Profile will contain all infos related to the type of A Profile will contain all infos related to the type of
bank statement, and related generated entries. It defines the bank statement, and related generated entries. It defines the
@@ -57,20 +58,20 @@ class AccountStatementProfile(Model):
'partner_id': fields.many2one( 'partner_id': fields.many2one(
'res.partner', 'res.partner',
'Bank/Payment Office partner', 'Bank/Payment Office partner',
help="Put a partner if you want to have it on the " help="Put a partner if you want to have it on the "
"commission move (and optionaly on the counterpart " "commission move (and optionaly on the counterpart "
"of the intermediate/banking move if you tick the " "of the intermediate/banking move if you tick the "
"corresponding checkbox)."), "corresponding checkbox)."),
'journal_id': fields.many2one( 'journal_id': fields.many2one(
'account.journal', 'account.journal',
'Financial journal to use for transaction', 'Financial journal to use for transaction',
required=True), required=True),
'commission_account_id': fields.many2one( 'commission_account_id': fields.many2one(
'account.account', 'account.account',
'Commission account', 'Commission account',
required=True), required=True),
'commission_analytic_id': fields.many2one( 'commission_analytic_id': fields.many2one(
'account.analytic.account', 'account.analytic.account',
@@ -110,16 +111,18 @@ class AccountStatementProfile(Model):
return True return True
_constraints = [ _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 = [ _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): class AccountBankStatement(Model):
""" """
We improve the bank statement class mostly for : We improve the bank statement class mostly for :
- Removing the period and compute it from the date of each line. - Removing the period and compute it from the date of each line.
@@ -140,7 +143,8 @@ class AccountBankStatement(Model):
if context is None: if context is None:
context = {} context = {}
period_obj = self.pool.get('account.period') 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 return periods and periods[0] or False
def _default_profile(self, cr, uid, context=None): def _default_profile(self, cr, uid, context=None):
@@ -155,7 +159,8 @@ class AccountBankStatement(Model):
user_obj = self.pool.get('res.users') user_obj = self.pool.get('res.users')
profile_obj = self.pool.get('account.statement.profile') profile_obj = self.pool.get('account.statement.profile')
user = user_obj.browse(cr, uid, uid, context=context) 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 return profile_ids[0] if profile_ids else False
@@ -219,11 +224,11 @@ class AccountBankStatement(Model):
}, },
readonly=True), readonly=True),
'period_id': fields.many2one( 'period_id': fields.many2one(
'account.period', 'account.period',
'Period', 'Period',
required=False, required=False,
readonly=False, readonly=False,
invisible=True), invisible=True),
} }
_defaults = { _defaults = {
@@ -236,7 +241,8 @@ class AccountBankStatement(Model):
need it.""" need it."""
if 'profile_id' in vals: if 'profile_id' in vals:
profile_obj = self.pool.get('account.statement.profile') 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 vals['journal_id'] = profile.journal_id.id
return super(AccountBankStatement, self return super(AccountBankStatement, self
).create(cr, uid, vals, context=context) ).create(cr, uid, vals, context=context)
@@ -319,12 +325,12 @@ class AccountBankStatement(Model):
if context is None: if context is None:
context = {} context = {}
res = super(AccountBankStatement, self)._prepare_move_line_vals( res = super(AccountBankStatement, self)._prepare_move_line_vals(
cr, uid, st_line, move_id, debit, credit, cr, uid, st_line, move_id, debit, credit,
currency_id=currency_id, currency_id=currency_id,
amount_currency=amount_currency, amount_currency=amount_currency,
account_id=account_id, account_id=account_id,
analytic_id=analytic_id, analytic_id=analytic_id,
partner_id=partner_id, context=context) partner_id=partner_id, context=context)
ctx = context.copy() ctx = context.copy()
ctx['company_id'] = st_line.company_id.id ctx['company_id'] = st_line.company_id.id
period_id = self._get_period(cr, uid, st_line.date, context=ctx) 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( year = self.pool.get('account.period').browse(
cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id
profile = self.pool.get('account.statement.profile').browse(cr, uid, profile_id) profile = self.pool.get(
'account.statement.profile').browse(cr, uid, profile_id)
c = {'fiscalyear_id': year} c = {'fiscalyear_id': year}
obj_seq = self.pool.get('ir.sequence') obj_seq = self.pool.get('ir.sequence')
journal_sequence_id = (profile.journal_id.sequence_id and journal_sequence_id = (profile.journal_id.sequence_id and
profile.journal_id.sequence_id.id or False) profile.journal_id.sequence_id.id or False)
if journal_sequence_id: if journal_sequence_id:
st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c) st_number = obj_seq.next_by_id(
cr, uid, journal_sequence_id, context=c)
else: else:
st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement', context=c) st_number = obj_seq.next_by_code(
cr, uid, 'account.bank.statement', context=c)
if profile.bank_statement_prefix: if profile.bank_statement_prefix:
st_number = profile.bank_statement_prefix + st_number st_number = profile.bank_statement_prefix + st_number
return 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): if not self.check_status_condition(cr, uid, st.state, journal_type=j_type):
continue 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) \ if (not st.journal_id.default_credit_account_id) \
or (not st.journal_id.default_debit_account_id): or (not st.journal_id.default_debit_account_id):
raise osv.except_osv(_('Configuration Error!'), raise osv.except_osv(_('Configuration Error!'),
@@ -402,8 +412,9 @@ class AccountBankStatement(Model):
if not st.name == '/': if not st.name == '/':
st_number = st.name st_number = st.name
else: else:
# Begin Changes # Begin Changes
st_number = self._get_st_number_period_profile(cr, uid, st.date, st.profile_id.id) st_number = self._get_st_number_period_profile(
cr, uid, st.date, st.profile_id.id)
# End Changes # End Changes
for line in st.move_line_ids: for line in st.move_line_ids:
if line.state != 'valid': if line.state != 'valid':
@@ -420,16 +431,19 @@ class AccountBankStatement(Model):
" journal on the '%s' journal!") % st.journal_id.name) " journal on the '%s' journal!") % st.journal_id.name)
if not st_line.amount: if not st_line.amount:
continue 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, self.create_move_from_st_line(cr, uid, st_line.id,
company_currency_id, company_currency_id,
st_line_number, st_line_number,
context) context)
except osv.except_osv, exc: 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) errors_stack.append(msg)
except Exception, exc: 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) errors_stack.append(msg)
if errors_stack: if errors_stack:
msg = u"\n".join(errors_stack) msg = u"\n".join(errors_stack)
@@ -439,7 +453,8 @@ class AccountBankStatement(Model):
{'name': st_number, {'name': st_number,
'balance_end_real': st.balance_end}, 'balance_end_real': st.balance_end},
context=context) 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], self.message_post(cr, uid, [st.id],
body, body,
context=context) context=context)
@@ -515,7 +530,8 @@ class AccountBankStatement(Model):
as 'customer' or 'supplier'. as 'customer' or 'supplier'.
""" """
account_id = False 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': if ltype == 'supplier':
account_id = account_payable account_id = account_payable
else: else:
@@ -574,13 +590,14 @@ class AccountBankStatement(Model):
if not profile_id: if not profile_id:
return {} return {}
import_config = self.pool.get("account.statement.profile").browse( 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 journal_id = import_config.journal_id.id
return {'value': {'journal_id': journal_id, return {'value': {'journal_id': journal_id,
'balance_check': import_config.balance_check}} 'balance_check': import_config.balance_check}}
class AccountBankStatementLine(Model): class AccountBankStatementLine(Model):
""" """
Override to compute the period from the date of the line, add a method to retrieve 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 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 # on profile
if profile_id and master_account_id is None: if profile_id and master_account_id is None:
profile = self.pool.get("account.statement.profile").browse( 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: if profile.receivable_account_id:
res['account_id'] = profile.receivable_account_id.id res['account_id'] = profile.receivable_account_id.id
# We return general as default instead of get_type_for_counterpart # 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 receiv_account = part.property_account_receivable.id
# If no value, look on the default company property # If no value, look on the default company property
if not pay_account or not receiv_account: 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, account_id, comp_line_type = obj_stat.get_account_and_type_for_counterpart(cr, uid, amount,
receiv_account, pay_account, receiv_account, pay_account,
partner_id=partner_id) partner_id=partner_id)
@@ -702,8 +720,10 @@ class AccountBankStatementLine(Model):
obj_stat = self.pool.get('account.bank.statement') obj_stat = self.pool.get('account.bank.statement')
if not partner_id: if not partner_id:
return {} return {}
line_type = obj_stat.get_type_for_counterpart(cr, uid, 0.0, partner_id=partner_id) line_type = obj_stat.get_type_for_counterpart(
res_type = self.onchange_type(cr, uid, ids, partner_id, line_type, profile_id, context=context) 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): if res_type['value'] and res_type['value'].get('account_id', False):
return {'value': {'type': line_type, return {'value': {'type': line_type,
'account_id': res_type['value']['account_id'], '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 period not in context, take it from the move lines"""
if not context.get('period_id') and context.get('move_line_ids'): if not context.get('period_id') and context.get('move_line_ids'):
res = self.pool.get('account.move.line').browse( 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 context['period_id'] = res
elif context.get('date'): elif context.get('date'):
periods = self.pool.get('account.period').find( periods = self.pool.get('account.period').find(
cr, uid, dt=context['date'], context=context) cr, uid, dt=context['date'], context=context)
if periods: if periods:
context['period_id'] = periods[0] context['period_id'] = periods[0]
return super(AccountVoucher, self)._get_period(cr, uid, context) 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 # This change has been proposed for merging to fix lp:125375
def mp_prepare_bank_statement(self, cr, uid, pos_config, journal, context=None): def mp_prepare_bank_statement(self, cr, uid, pos_config, journal, context=None):
bank_values = { bank_values = {
'journal_id' : journal.id, 'journal_id': journal.id,
'user_id' : uid, 'user_id': uid,
'company_id' : pos_config.shop_id.company_id.id 'company_id': pos_config.shop_id.company_id.id
} }
return bank_values return bank_values
def mp_create(self, cr, uid, values, context=None): def mp_create(self, cr, uid, values, context=None):
context = context or {} 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: if not config_id:
raise osv.except_osv( _('Error!'), raise osv.except_osv(_('Error!'),
_("You should assign a Point of Sale to your session.")) _("You should assign a Point of Sale to your session."))
# journal_id is not required on the pos_config because it does not # journal_id is not required on the pos_config because it does not
# exists at the installation. If nothing is configured at the # 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) pos_config = jobj.browse(cr, uid, config_id, context=context)
context.update({'company_id': pos_config.shop_id.company_id.id}) context.update({'company_id': pos_config.shop_id.company_id.id})
if not pos_config.journal_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: 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: else:
raise osv.except_osv( _('error!'), raise osv.except_osv(_('error!'),
_("Unable to open the session. You have to assign a sale journal to your point of sale.")) _("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 # define some cash journal if no payment method exists
if not pos_config.journal_ids: if not pos_config.journal_ids:
journal_proxy = self.pool.get('account.journal') 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: 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: if not cashids:
cashids = journal_proxy.search(cr, uid, [('journal_user','=',True)], context=context) cashids = journal_proxy.search(
cr, uid, [('journal_user', '=', True)], context=context)
jobj.write(cr, uid, [pos_config.id], {'journal_ids': [(6,0, cashids)]})
jobj.write(
cr, uid, [pos_config.id], {'journal_ids': [(6, 0, cashids)]})
pos_config = jobj.browse(cr, uid, config_id, context=context) pos_config = jobj.browse(cr, uid, config_id, context=context)
bank_statement_ids = [] bank_statement_ids = []
for journal in pos_config.journal_ids: for journal in pos_config.journal_ids:
bank_values = self._prepare_bank_statement(cr, uid, pos_config, journal, context) bank_values = self._prepare_bank_statement(
statement_id = self.pool.get('account.bank.statement').create(cr, uid, bank_values, context=context) 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) bank_statement_ids.append(statement_id)
values.update({ values.update({
'name' : pos_config.sequence_id._next(), 'name': pos_config.sequence_id._next(),
'statement_ids' : [(6, 0, bank_statement_ids)], 'statement_ids': [(6, 0, bank_statement_ids)],
'config_id': config_id 'config_id': config_id
}) })
return super(std_pos_session, self).create(cr, uid, values, context=context) 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') profile_obj = self.pool.get('account.statement.profile')
user = user_obj.browse(cr, uid, uid, context=context) user = user_obj.browse(cr, uid, uid, context=context)
defaults = self.pool['account.bank.statement'].default_get(cr, uid, defaults = self.pool['account.bank.statement'].default_get(cr, uid,
['profile_id', 'period_id'], ['profile_id',
'period_id'],
context=context) context=context)
profile_ids = profile_obj.search(cr, uid, profile_ids = profile_obj.search(cr, uid,
[('company_id', '=', user.company_id.id), [('company_id', '=', user.company_id.id),

View File

@@ -28,7 +28,7 @@
'depends': [ 'depends': [
'account_statement_ext', 'account_statement_ext',
'account_voucher' 'account_voucher'
], ],
'description': """ 'description': """
This module is deprecated. It was only needed when using account_bank_statement_ext with voucher in order to compute the period 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. 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', 'website': 'http://www.camptocamp.com',
'init_xml': [], 'init_xml': [],
'update_xml': [ 'update_xml': [
"statement_voucher_view.xml", "statement_voucher_view.xml",
], ],
'demo_xml': [], 'demo_xml': [],
'test': [], 'test': [],
@@ -48,5 +48,5 @@
'images': [], 'images': [],
'auto_install': False, 'auto_install': False,
'license': 'AGPL-3', 'license': 'AGPL-3',
} }

View File

@@ -32,11 +32,11 @@ class AccountVoucher(Model):
context = {} context = {}
if not context.get('period_id') and context.get('move_line_ids'): if not context.get('period_id') and context.get('move_line_ids'):
res = self.pool.get('account.move.line').browse( 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 context['period_id'] = res
elif context.get('date'): elif context.get('date'):
periods = self.pool.get('account.period').find( periods = self.pool.get('account.period').find(
cr, uid, dt=context['date'], context=context) cr, uid, dt=context['date'], context=context)
if periods: if periods:
context['period_id'] = periods[0] context['period_id'] = periods[0]
return super(AccountVoucher, self)._get_period(cr, uid, context) 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/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
############################################################################### ###############################################################################

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -24,7 +24,7 @@ from openerp.osv import fields, orm, osv
class AccountStatementProfile(orm.Model): class AccountStatementProfile(orm.Model):
_inherit = "account.statement.profile" _inherit = "account.statement.profile"
_columns = { _columns = {
'one_move': fields.boolean( 'one_move': fields.boolean(
'Group Journal Items', 'Group Journal Items',
@@ -36,6 +36,7 @@ class AccountStatementProfile(orm.Model):
"for the refunds and one for the payments.") "for the refunds and one for the payments.")
} }
class account_bank_statement(orm.Model): class account_bank_statement(orm.Model):
_inherit = "account.bank.statement" _inherit = "account.bank.statement"
@@ -49,52 +50,54 @@ class account_bank_statement(orm.Model):
'period_id': period_id, 'period_id': period_id,
'date': st_line.statement_id.date, 'date': st_line.statement_id.date,
'name': st_line.ref, 'name': st_line.ref,
}) })
return res return res
return res return res
def _prepare_move(self, cr, uid, st_line, st_line_number, context=None): def _prepare_move(self, cr, uid, st_line, st_line_number, context=None):
res = super(account_bank_statement, self).\ 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({ res.update({
'ref': st_line.statement_id.name, 'ref': st_line.statement_id.name,
'name': st_line.statement_id.name, 'name': st_line.statement_id.name,
'date': st_line.statement_id.date, 'date': st_line.statement_id.date,
}) })
return res return res
def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id,
st_line_number, context=None): st_line_number, context=None):
if context is None: if context is None:
context = {} 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_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, st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id,
context=context) context=context)
st = st_line.statement_id st = st_line.statement_id
if st.profile_id.one_move: if st.profile_id.one_move:
if not context.get('move_id'): if not context.get('move_id'):
move_vals = self._prepare_move(cr, uid, st_line, st_line_number, context=context) move_vals = self._prepare_move(
context['move_id'] = account_move_obj.create(cr, uid, move_vals, context=context) cr, uid, st_line, st_line_number, context=context)
self.create_move_line_from_st_line(cr, uid, context['move_id'], context['move_id'] = account_move_obj.create(
st_line_id, company_currency_id, cr, uid, move_vals, context=context)
context=context) self.create_move_line_from_st_line(cr, uid, context['move_id'],
st_line_id, company_currency_id,
context=context)
return context['move_id'] return context['move_id']
else: else:
return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line_id, return super(account_bank_statement, self).create_move_from_st_line(cr, uid, st_line_id,
company_currency_id, company_currency_id,
st_line_number, st_line_number,
context=context) context=context)
def create_move_line_from_st_line(self, cr, uid, move_id, st_line_id, 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. """Create the account move line from the statement line.
:param int/long move_id: ID of the account.move :param int/long move_id: ID of the account.move
:param int/long st_line_id: ID of the account.bank.statement.line to create the move line from. :param int/long st_line_id: ID of the account.bank.statement.line to create the move line from.
:param int/long company_currency_id: ID of the res.currency of the company :param int/long company_currency_id: ID of the res.currency of the company
@@ -104,23 +107,26 @@ class account_bank_statement(orm.Model):
context = {} context = {}
res_currency_obj = self.pool.get('res.currency') res_currency_obj = self.pool.get('res.currency')
account_move_line_obj = self.pool.get('account.move.line') account_move_line_obj = self.pool.get('account.move.line')
account_bank_statement_line_obj = self.pool.get('account.bank.statement.line') account_bank_statement_line_obj = self.pool.get(
st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id, context=context) 'account.bank.statement.line')
st_line = account_bank_statement_line_obj.browse(
cr, uid, st_line_id, context=context)
st = st_line.statement_id st = st_line.statement_id
context.update({'date': st_line.date}) 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({ context.update({
'res.currency.compute.account': acc_cur, 'res.currency.compute.account': acc_cur,
}) })
amount = res_currency_obj.compute(cr, uid, st.currency.id, amount = res_currency_obj.compute(cr, uid, st.currency.id,
company_currency_id, company_currency_id,
st_line.amount, st_line.amount,
context=context) context=context)
bank_move_vals = self._prepare_bank_move_line(cr, uid, st_line, move_id, amount, 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) return account_move_line_obj.create(cr, uid, bank_move_vals, context=context)
def _valid_move(self, cr, uid, move_id, context=None): 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) move_obj.post(cr, uid, [move_id], context=context)
return True return True
def _prepare_transfer_move_line_vals(self, cr, uid, st, name, amount, move_id, context=None): 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. Prepare the dict of values to create the transfer move lines.
@@ -157,7 +162,6 @@ class account_bank_statement(orm.Model):
} }
return vals return vals
def create_move_transfer_lines(self, cr, uid, move, st, context=None): def create_move_transfer_lines(self, cr, uid, move, st, context=None):
move_line_obj = self.pool.get('account.move.line') move_line_obj = self.pool.get('account.move.line')
move_id = move.id move_id = move.id
@@ -165,11 +169,11 @@ class account_bank_statement(orm.Model):
payment = 0.0 payment = 0.0
transfer_lines = [] transfer_lines = []
transfer_line_ids = [] 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: for move_line in move.line_id:
refund -= move_line.debit refund -= move_line.debit
payment += move_line.credit 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 st.profile_id.split_transfer_line:
if refund: if refund:
transfer_lines.append(['Refund Transfer', refund]) transfer_lines.append(['Refund Transfer', refund])
@@ -180,15 +184,15 @@ class account_bank_statement(orm.Model):
if amount: if amount:
transfer_lines.append(['Transfer', amount]) transfer_lines.append(['Transfer', amount])
for transfer_line in transfer_lines: for transfer_line in transfer_lines:
vals = self._prepare_transfer_move_line_vals(cr, uid, st, vals = self._prepare_transfer_move_line_vals(cr, uid, st,
transfer_line[0], transfer_line[0],
transfer_line[1], transfer_line[1],
move_id, move_id,
context=context) 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 return transfer_line_ids
def button_confirm_bank(self, cr, uid, ids, context=None): def button_confirm_bank(self, cr, uid, ids, context=None):
st_line_obj = self.pool.get('account.bank.statement.line') st_line_obj = self.pool.get('account.bank.statement.line')
move_obj = self.pool.get('account.move') 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): if st.profile_id.one_move and context.get('move_id', False):
move_id = context['move_id'] move_id = context['move_id']
move = move_obj.browse(cr, uid, move_id, context=context) 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) self._valid_move(cr, uid, move_id, context=context)
lines_ids = [x.id for x in st.line_ids] lines_ids = [x.id for x in st.line_ids]
st_line_obj.write(cr, uid, lines_ids, st_line_obj.write(cr, uid, lines_ids,
{'move_ids': [(4, move_id, False)]}, {'move_ids': [(4, move_id, False)]},
context=context) context=context)
return True return True
def button_cancel(self, cr, uid, ids, context=None): def button_cancel(self, cr, uid, ids, context=None):
@@ -216,10 +221,8 @@ class account_bank_statement(orm.Model):
if move.state != 'draft': if move.state != 'draft':
move.button_cancel(context=context) move.button_cancel(context=context)
move.unlink(context=context) move.unlink(context=context)
st.write({'state':'draft'}, context=context) st.write({'state': 'draft'}, context=context)
else: else:
super(account_bank_statement, self).button_cancel(cr, uid, ids, super(account_bank_statement, self).button_cancel(cr, uid, ids,
context=context) context=context)
return True 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. and update account to use in the bank statement line with the specified account.
""", """,
"data": ['statement_view.xml', "data": ['statement_view.xml',
], ],
"demo": [], "demo": [],
"test": [], "test": [],
"active": False, "active": False,

View File

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

View File

@@ -36,14 +36,17 @@ ACC_NUMBER = "BE38733040385372"
class test_regex_account_completion(common.TransactionCase): class test_regex_account_completion(common.TransactionCase):
def prepare(self): def prepare(self):
self.account_bank_statement_obj = self.registry("account.bank.statement") self.account_bank_statement_obj = self.registry(
self.account_bank_statement_line_obj = self.registry("account.bank.statement.line") "account.bank.statement")
self.account_bank_statement_line_obj = self.registry(
"account.bank.statement.line")
self.account_id = self.ref('account.a_expense') self.account_id = self.ref('account.a_expense')
# create the completion rule # create the completion rule
rule_vals = {'function_to_call': 'set_account', rule_vals = {'function_to_call': 'set_account',
'regex': '^My statement', 'regex': '^My statement',
'account_id': self.account_id} '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 # Create the profile
journal_id = self.ref("account.bank_journal") journal_id = self.ref("account.bank_journal")
@@ -83,9 +86,14 @@ class test_regex_account_completion(common.TransactionCase):
"""Test the automatic completion on account """Test the automatic completion on account
""" """
self.prepare() 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_obj.button_auto_completion()
statement_line1 = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line1_id) statement_line1 = self.account_bank_statement_line_obj.browse(
self.assertEquals(self.account_id, statement_line1.account_id.id, "The account should be the account of the completion") self.cr, self.uid, self.statement_line1_id)
statement_line2 = self.account_bank_statement_line_obj.browse(self.cr, self.uid, self.statement_line2_id) self.assertEquals(self.account_id, statement_line1.account_id.id,
self.assertNotEqual(self.account_id, statement_line2.account_id.id, "The account should be not the account of the completion") "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': [ 'depends': [
'account_statement_base_completion', 'account_statement_base_completion',
'base_transaction_id' 'base_transaction_id'
], ],
'description': """ 'description': """
Add a completion method based on transaction ID providen by the bank/office. Add a completion method based on transaction ID providen by the bank/office.
@@ -44,8 +44,8 @@
'website': 'http://www.camptocamp.com', 'website': 'http://www.camptocamp.com',
'init_xml': [], 'init_xml': [],
'update_xml': [ 'update_xml': [
"statement_view.xml", "statement_view.xml",
"data.xml", "data.xml",
], ],
'demo_xml': [], 'demo_xml': [],
'test': [ 'test': [
@@ -58,4 +58,4 @@
'images': [], 'images': [],
'auto_install': True, 'auto_install': True,
'license': 'AGPL-3', 'license': 'AGPL-3',
} }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -35,9 +35,9 @@ class SaleOrder(Model):
} }
def _prepare_invoice(self, cr, uid, order, lines, context=None): 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( invoice_vals = super(SaleOrder, self)._prepare_invoice(
cr, uid, order, lines, context=context) cr, uid, order, lines, context=context)
invoice_vals.update({ invoice_vals.update({
'transaction_id': order.transaction_id}) 'transaction_id': order.transaction_id})
return invoice_vals return invoice_vals

View File

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

View File

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

View File

@@ -45,7 +45,8 @@ class AccountStatementFromInvoiceLines(orm.TransientModel):
statement_line_obj = self.pool.get('account.bank.statement.line') statement_line_obj = self.pool.get('account.bank.statement.line')
currency_obj = self.pool.get('res.currency') currency_obj = self.pool.get('res.currency')
line_date = time.strftime('%Y-%m-%d') 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 each selected move lines
for line in line_obj.browse(cr, uid, line_ids, context=context): for line in line_obj.browse(cr, uid, line_ids, context=context):
ctx = context.copy() ctx = context.copy()
@@ -60,10 +61,10 @@ class AccountStatementFromInvoiceLines(orm.TransientModel):
if line.amount_currency: if line.amount_currency:
amount = currency_obj.compute(cr, uid, line.currency_id.id, 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): elif (line.invoice and line.invoice.currency_id.id != statement.currency.id):
amount = currency_obj.compute(cr, uid, line.invoice.currency_id.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], context.update({'move_line_ids': [line.id],
'invoice_id': line.invoice.id}) 'invoice_id': line.invoice.id})
@@ -109,13 +110,15 @@ class AccountPaymentPopulateStatement(orm.TransientModel):
if not line_ids: if not line_ids:
return {'type': 'ir.actions.act_window_close'} 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): for line in line_obj.browse(cr, uid, line_ids, context=context):
ctx = context.copy() 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, 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: if not line.move_line_id.id:
continue continue
context.update({'move_line_ids': [line.move_line_id.id]}) 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, st_line_id = statement_line_obj.create(cr, uid, vals,
context=context) 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'} return {'type': 'ir.actions.act_window_close'}
def _prepare_statement_line_vals(self, cr, uid, payment_line, amount, def _prepare_statement_line_vals(self, cr, uid, payment_line, amount,