# -*- coding: utf-8 -*- ############################################################################## # # Author: Joel Grand-Guillaume # Copyright 2011-2012 Camptocamp SA # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # ############################################################################## from osv import fields, osv from tools.translate import _ from account_statement_import.file_parser.parser import FileParser import datetime import netsvc logger = netsvc.Logger() class AccountSatement(osv.osv): """Override account bank statement to remove the period on it and compute it for each line.""" _inherit = "account.bank.statement" _columns = { 'period_id': fields.many2one('account.period', 'Period', required=False, readonly=True), } _defaults = { 'period_id': lambda *a: False, } def _get_period(self, cursor, uid, date, context=None): ''' Find matching period for date, used in thestatement line creation. ''' period_obj = self.pool.get('account.period') periods = period_obj.find(cursor, uid, dt=date, context=context) return periods and periods[0] or False def _check_company_id(self, cr, uid, ids, context=None): """ Adapt this constraint method from the account module to reflect the move of period_id to the statement line """ for statement in self.browse(cr, uid, ids, context=context): if (statement.period_id and statement.company_id.id != statement.period_id.company_id.id): return False for line in statement.line_ids: if (line.period_id and statement.company_id.id != line.period_id.company_id.id): return False return True # Redefine the constraint, or it still refer to the original method _constraints = [ (_check_company_id, 'The journal and period chosen have to belong to the same company.', ['journal_id','period_id']), ] def create_move_from_st_line(self, cr, uid, st_line_id, company_currency_id, st_line_number, context=None): """Override a large portion of the code to compute the periode for each line instead of taking the period of the whole statement. Remove the entry posting on generated account moves.""" if context is None: context = {} res_currency_obj = self.pool.get('res.currency') account_move_obj = self.pool.get('account.move') account_move_line_obj = self.pool.get('account.move.line') account_bank_statement_line_obj = self.pool.get('account.bank.statement.line') st_line = account_bank_statement_line_obj.browse(cr, uid, st_line_id, context=context) st = st_line.statement_id context.update({'date': st_line.date}) ctx = context.copy() # Chg ctx['company_id'] = st_line.company_id.id # Chg period_id = self._get_period( # Chg cr, uid, st_line.date, context=ctx) move_id = account_move_obj.create(cr, uid, { 'journal_id': st.journal_id.id, 'period_id': period_id, # Chg 'date': st_line.date, 'name': st_line_number, 'ref': st_line.ref, }, context=context) account_bank_statement_line_obj.write(cr, uid, [st_line.id], { 'move_ids': [(4, move_id, False)] }) torec = [] if st_line.amount >= 0: account_id = st.journal_id.default_credit_account_id.id else: account_id = st.journal_id.default_debit_account_id.id acc_cur = ((st_line.amount<=0) and st.journal_id.default_debit_account_id) or st_line.account_id context.update({ 'res.currency.compute.account': acc_cur, }) amount = res_currency_obj.compute(cr, uid, st.currency.id, company_currency_id, st_line.amount, context=context) val = { 'name': st_line.name, 'date': st_line.date, 'ref': st_line.ref, 'move_id': move_id, 'partner_id': ((st_line.partner_id) and st_line.partner_id.id) or False, 'account_id': (st_line.account_id) and st_line.account_id.id, 'credit': ((amount>0) and amount) or 0.0, 'debit': ((amount<0) and -amount) or 0.0, 'statement_id': st.id, 'journal_id': st.journal_id.id, 'period_id': period_id, #Chg 'currency_id': st.currency.id, 'analytic_account_id': st_line.analytic_account_id and st_line.analytic_account_id.id or False } if st.currency.id <> company_currency_id: amount_cur = res_currency_obj.compute(cr, uid, company_currency_id, st.currency.id, amount, context=context) val['amount_currency'] = -amount_cur if st_line.account_id and st_line.account_id.currency_id and st_line.account_id.currency_id.id <> company_currency_id: val['currency_id'] = st_line.account_id.currency_id.id amount_cur = res_currency_obj.compute(cr, uid, company_currency_id, st_line.account_id.currency_id.id, amount, context=context) val['amount_currency'] = -amount_cur move_line_id = account_move_line_obj.create(cr, uid, val, context=context) torec.append(move_line_id) # Fill the secondary amount/currency # if currency is not the same than the company amount_currency = False currency_id = False if st.currency.id <> company_currency_id: amount_currency = st_line.amount currency_id = st.currency.id account_move_line_obj.create(cr, uid, { 'name': st_line.name, 'date': st_line.date, 'ref': st_line.ref, 'move_id': move_id, 'partner_id': ((st_line.partner_id) and st_line.partner_id.id) or False, 'account_id': account_id, 'credit': ((amount < 0) and -amount) or 0.0, 'debit': ((amount > 0) and amount) or 0.0, 'statement_id': st.id, 'journal_id': st.journal_id.id, 'period_id': period_id, #Chg 'amount_currency': amount_currency, 'currency_id': currency_id, }, context=context) for line in account_move_line_obj.browse(cr, uid, [x.id for x in account_move_obj.browse(cr, uid, move_id, context=context).line_id], context=context): if line.state <> 'valid': raise osv.except_osv(_('Error !'), _('Journal item "%s" is not valid.') % line.name) # Bank statements will not consider boolean on journal entry_posted account_move_obj.post(cr, uid, [move_id], context=context) return move_id def _get_st_number_period(self, cr, uid, date, journal_sequence_id): """Retrieve the name of bank statement from sequence, according to the period corresponding to the date passed in args""" year = self.pool.get('account.period').browse(cr, uid, self._get_period(cr, uid, date)).fiscalyear_id.id c = {'fiscalyear_id': year} obj_seq = self.pool.get('ir.sequence') if journal_sequence_id: st_number = obj_seq.next_by_id(cr, uid, journal_sequence_id, context=c) else: st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement', context=c) return st_number def button_confirm_bank(self, cr, uid, ids, context=None): """Completely override the method in order to have an error message which displays all the messages instead of having them pop one by one. We have to copy paste a big block of code, changing the error stack + managing period from date.""" # obj_seq = self.pool.get('irerrors_stack.sequence') if context is None: context = {} for st in self.browse(cr, uid, ids, context=context): j_type = st.journal_id.type company_currency_id = st.journal_id.company_id.currency_id.id if not self.check_status_condition(cr, uid, st.state, journal_type=j_type): continue self.balance_check(cr, uid, st.id, journal_type=j_type, context=context) if (not st.journal_id.default_credit_account_id) \ or (not st.journal_id.default_debit_account_id): raise osv.except_osv(_('Configuration Error !'), _('Please verify that an account is defined in the journal.')) if not st.name == '/': st_number = st.name else: # Begin Changes seq_id = st.journal_id.sequence_id and st.journal_id.sequence_id.id or False st_number = self._get_st_number_period(cr, uid, st.date, seq_id) # c = {'fiscalyear_id': st.period_id.fiscalyear_id.id} # if st.journal_id.sequence_id: # st_number = obj_seq.next_by_id(cr, uid, st.journal_id.sequence_id.id, context=c) # else: # st_number = obj_seq.next_by_code(cr, uid, 'account.bank.statement', context=c) # End Changes for line in st.move_line_ids: if line.state <> 'valid': raise osv.except_osv(_('Error !'), _('The account entries lines are not in valid state.')) # begin changes errors_stack = [] for st_line in st.line_ids: try: if st_line.analytic_account_id: if not st.journal_id.analytic_journal_id: raise osv.except_osv(_('No Analytic Journal !'), _("You have to assign an analytic journal on the '%s' journal!") % (st.journal_id.name,)) if not st_line.amount: continue st_line_number = self.get_next_st_line_number(cr, uid, st_number, st_line, context) self.create_move_from_st_line(cr, uid, st_line.id, company_currency_id, st_line_number, context) except osv.except_osv, exc: msg = "Line ID %s with ref %s had following error: %s" % (st_line.id, st_line.ref, exc.value) errors_stack.append(msg) except Exception, exc: msg = "Line ID %s with ref %s had following error: %s" % (st_line.id, st_line.ref, str(exc)) errors_stack.append(msg) if errors_stack: msg = u"\n".join(errors_stack) raise osv.except_osv(_('Error'), msg) #end changes self.write(cr, uid, [st.id], { 'name': st_number, 'balance_end_real': st.balance_end }, context=context) self.log(cr, uid, st.id, _('Statement %s is confirmed, journal items are created.') % (st_number,)) return self.write(cr, uid, ids, {'state':'confirm'}, context=context) class AccountSatementLine(osv.osv): ''' Adds the period on line, matched on the date. ''' _inherit = 'account.bank.statement.line' def _get_period(self, cursor, user, context=None): date = context.get('date', None) periods = self.pool.get('account.period').find(cursor, user, dt=date) return periods and periods[0] or False _columns = { 'period_id': fields.many2one('account.period', 'Period', required=True), } _defaults = { 'period_id': _get_period, }