diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index 43aaa2f58c1..18277dbb4dd 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -153,12 +153,11 @@ for a particular financial year and for preparation of vouchers there is a modul 'test/account_period_close.yml', 'test/account_use_model.yml', 'test/account_validate_account_move.yml', - 'test/account_fiscalyear_close.yml', #'test/account_bank_statement.yml', #'test/account_cash_statement.yml', 'test/test_edi_invoice.yml', 'test/account_report.yml', - 'test/account_fiscalyear_close_state.yml', #last test, as it will definitively close the demo fiscalyear + 'test/account_fiscalyear_close.yml', #last test, as it will definitively close the demo fiscalyear ], 'installable': True, 'auto_install': False, diff --git a/addons/account/account.py b/addons/account/account.py index c2e75e889f0..6368149ba5c 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -25,11 +25,12 @@ from dateutil.relativedelta import relativedelta from operator import itemgetter import time +import openerp from openerp import SUPERUSER_ID from openerp import pooler, tools -from openerp.osv import fields, osv +from openerp.osv import fields, osv, expression from openerp.tools.translate import _ -from openerp.tools.float_utils import float_round +from openerp.tools.float_utils import float_round as round import openerp.addons.decimal_precision as dp @@ -137,16 +138,27 @@ class account_account_type(osv.osv): _name = "account.account.type" _description = "Account Type" - def _get_current_report_type(self, cr, uid, ids, name, arg, context=None): + def _get_financial_report_ref(self, cr, uid, context=None): obj_data = self.pool.get('ir.model.data') obj_financial_report = self.pool.get('account.financial.report') + financial_report_ref = {} + for key, financial_report in [ + ('asset','account_financial_report_assets0'), + ('liability','account_financial_report_liability0'), + ('income','account_financial_report_income0'), + ('expense','account_financial_report_expense0'), + ]: + try: + financial_report_ref[key] = obj_financial_report.browse(cr, uid, + obj_data.get_object_reference(cr, uid, 'account', financial_report)[1], + context=context) + except ValueError: + pass + return financial_report_ref + + def _get_current_report_type(self, cr, uid, ids, name, arg, context=None): res = {} - financial_report_ref = { - 'asset': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_assets0')[1], context=context), - 'liability': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_liability0')[1], context=context), - 'income': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_income0')[1], context=context), - 'expense': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_expense0')[1], context=context), - } + financial_report_ref = self._get_financial_report_ref(cr, uid, context=context) for record in self.browse(cr, uid, ids, context=context): res[record.id] = 'none' for key, financial_report in financial_report_ref.items(): @@ -157,15 +169,9 @@ class account_account_type(osv.osv): def _save_report_type(self, cr, uid, account_type_id, field_name, field_value, arg, context=None): field_value = field_value or 'none' - obj_data = self.pool.get('ir.model.data') obj_financial_report = self.pool.get('account.financial.report') #unlink if it exists somewhere in the financial reports related to BS or PL - financial_report_ref = { - 'asset': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_assets0')[1], context=context), - 'liability': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_liability0')[1], context=context), - 'income': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_income0')[1], context=context), - 'expense': obj_financial_report.browse(cr, uid, obj_data.get_object_reference(cr, uid, 'account','account_financial_report_expense0')[1], context=context), - } + financial_report_ref = self._get_financial_report_ref(cr, uid, context=context) for key, financial_report in financial_report_ref.items(): list_ids = [x.id for x in financial_report.account_type_ids] if account_type_id in list_ids: @@ -576,15 +582,18 @@ class account_account(osv.osv): except: pass if name: - ids = self.search(cr, user, [('code', '=like', name+"%")]+args, limit=limit) - if not ids: - ids = self.search(cr, user, [('shortcut', '=', name)]+ args, limit=limit) - if not ids: - ids = self.search(cr, user, [('name', operator, name)]+ args, limit=limit) - if not ids and len(name.split()) >= 2: - #Separating code and name of account for searching - operand1,operand2 = name.split(' ',1) #name can contain spaces e.g. OpenERP S.A. - ids = self.search(cr, user, [('code', operator, operand1), ('name', operator, operand2)]+ args, limit=limit) + if operator not in expression.NEGATIVE_TERM_OPERATORS: + ids = self.search(cr, user, ['|', ('code', '=like', name+"%"), '|', ('shortcut', '=', name), ('name', operator, name)]+args, limit=limit) + if not ids and len(name.split()) >= 2: + #Separating code and name of account for searching + operand1,operand2 = name.split(' ',1) #name can contain spaces e.g. OpenERP S.A. + ids = self.search(cr, user, [('code', operator, operand1), ('name', operator, operand2)]+ args, limit=limit) + else: + ids = self.search(cr, user, ['&','!', ('code', '=like', name+"%"), ('name', operator, name)]+args, limit=limit) + # as negation want to restric, do if already have results + if ids and len(name.split()) >= 2: + operand1,operand2 = name.split(' ',1) #name can contain spaces e.g. OpenERP S.A. + ids = self.search(cr, user, [('code', operator, operand1), ('name', operator, operand2), ('id', 'in', ids)]+ args, limit=limit) else: ids = self.search(cr, user, args, context=context, limit=limit) return self.name_get(cr, user, ids, context=context) @@ -836,16 +845,11 @@ class account_journal(osv.osv): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100): if not args: args = [] - if context is None: - context = {} - ids = [] - if context.get('journal_type', False): - args += [('type','=',context.get('journal_type'))] - if name: - ids = self.search(cr, user, [('code', 'ilike', name)]+ args, limit=limit, context=context) - if not ids: - ids = self.search(cr, user, [('name', 'ilike', name)]+ args, limit=limit, context=context)#fix it ilike should be replace with operator - + if operator in expression.NEGATIVE_TERM_OPERATORS: + domain = [('code', operator, name), ('name', operator, name)] + else: + domain = ['|', ('code', operator, name), ('name', operator, name)] + ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) return self.name_get(cr, user, ids, context=context) account_journal() @@ -935,13 +939,11 @@ class account_fiscalyear(osv.osv): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): if args is None: args = [] - if context is None: - context = {} - ids = [] - if name: - ids = self.search(cr, user, [('code', 'ilike', name)]+ args, limit=limit) - if not ids: - ids = self.search(cr, user, [('name', operator, name)]+ args, limit=limit) + if operator in expression.NEGATIVE_TERM_OPERATORS: + domain = [('code', operator, name), ('name', operator, name)] + else: + domain = ['|', ('code', operator, name), ('name', operator, name)] + ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) return self.name_get(cr, user, ids, context=context) account_fiscalyear() @@ -1036,19 +1038,11 @@ class account_period(osv.osv): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=100): if args is None: args = [] - if context is None: - context = {} - ids = [] - if name: - ids = self.search(cr, user, - [('code', 'ilike', name)] + args, - limit=limit, - context=context) - if not ids: - ids = self.search(cr, user, - [('name', operator, name)] + args, - limit=limit, - context=context) + if operator in expression.NEGATIVE_TERM_OPERATORS: + domain = [('code', operator, name), ('name', operator, name)] + else: + domain = ['|', ('code', operator, name), ('name', operator, name)] + ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) return self.name_get(cr, user, ids, context=context) def write(self, cr, uid, ids, vals, context=None): @@ -1186,36 +1180,6 @@ class account_move(osv.osv): 'company_id': company_id, } - def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): - """ - Returns a list of tupples containing id, name, as internally it is called {def name_get} - result format: {[(id, name), (id, name), ...]} - - @param cr: A database cursor - @param user: ID of the user currently logged in - @param name: name to search - @param args: other arguments - @param operator: default operator is 'ilike', it can be changed - @param context: context arguments, like lang, time zone - @param limit: Returns first 'n' ids of complete result, default is 80. - - @return: Returns a list of tuples containing id and name - """ - - if not args: - args = [] - ids = [] - if name: - ids += self.search(cr, user, [('name','ilike',name)]+args, limit=limit, context=context) - - if not ids and name and type(name) == int: - ids += self.search(cr, user, [('id','=',name)]+args, limit=limit, context=context) - - if not ids: - ids += self.search(cr, user, args, limit=limit, context=context) - - return self.name_get(cr, user, ids, context=context) - def name_get(self, cursor, user, ids, context=None): if isinstance(ids, (int, long)): ids = [ids] @@ -1267,6 +1231,10 @@ class account_move(osv.osv): return [('id', 'in', tuple(ids))] return [('id', '=', '0')] + def _get_move_from_lines(self, cr, uid, ids, context=None): + line_obj = self.pool.get('account.move.line') + return [line.move_id.id for line in line_obj.browse(cr, uid, ids, context=context)] + _columns = { 'name': fields.char('Number', size=64, required=True), 'ref': fields.char('Reference', size=64), @@ -1276,7 +1244,10 @@ class account_move(osv.osv): help='All manually created new journal entries are usually in the status \'Unposted\', but you can set the option to skip that status on the related journal. In that case, they will behave as journal entries automatically created by the system on document validation (invoices, bank statements...) and will be created in \'Posted\' status.'), 'line_id': fields.one2many('account.move.line', 'move_id', 'Entries', states={'posted':[('readonly',True)]}), 'to_check': fields.boolean('To Review', help='Check this box if you are unsure of that journal entry and if you want to note it as \'to be reviewed\' by an accounting expert.'), - 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store=True), + 'partner_id': fields.related('line_id', 'partner_id', type="many2one", relation="res.partner", string="Partner", store={ + _name: (lambda self, cr,uid,ids,c: ids, ['line_id'], 10), + 'account.move.line': (_get_move_from_lines, ['partner_id'],10) + }), 'amount': fields.function(_amount_compute, string='Amount', digits_compute=dp.get_precision('Account'), type='float', fnct_search=_search_amount), 'date': fields.date('Date', required=True, states={'posted':[('readonly',True)]}, select=True), 'narration':fields.text('Internal Note'), @@ -1413,14 +1384,17 @@ class account_move(osv.osv): l[2]['period_id'] = default_period context['period_id'] = default_period - if 'line_id' in vals: + if vals.get('line_id', False): c = context.copy() c['novalidate'] = True c['period_id'] = vals['period_id'] if 'period_id' in vals else self._get_period(cr, uid, context) c['journal_id'] = vals['journal_id'] if 'date' in vals: c['date'] = vals['date'] result = super(account_move, self).create(cr, uid, vals, c) - self.validate(cr, uid, [result], context) + tmp = self.validate(cr, uid, [result], context) + journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'], context) + if journal.entry_posted and tmp: + self.button_validate(cr,uid, [result], context) else: result = super(account_move, self).create(cr, uid, vals, context) return result @@ -1441,6 +1415,8 @@ class account_move(osv.osv): def unlink(self, cr, uid, ids, context=None, check=True): if context is None: context = {} + if isinstance(ids, (int, long)): + ids = [ids] toremove = [] obj_move_line = self.pool.get('account.move.line') for move in self.browse(cr, uid, ids, context=context): @@ -1565,11 +1541,6 @@ class account_move(osv.osv): obj_analytic_line = self.pool.get('account.analytic.line') obj_move_line = self.pool.get('account.move.line') for move in self.browse(cr, uid, ids, context): - # Unlink old analytic lines on move_lines - for obj_line in move.line_id: - for obj in obj_line.analytic_lines: - obj_analytic_line.unlink(cr,uid,obj.id) - journal = move.journal_id amount = 0 line_ids = [] @@ -1642,9 +1613,11 @@ class account_move(osv.osv): else: # We can't validate it (it's unbalanced) # Setting the lines as draft - obj_move_line.write(cr, uid, line_ids, { - 'state': 'draft' - }, context, check=False) + not_draft_line_ids = list(set(line_ids) - set(line_draft_ids)) + if not_draft_line_ids: + obj_move_line.write(cr, uid, not_draft_line_ids, { + 'state': 'draft' + }, context, check=False) # Create analytic lines for the valid moves for record in valid_moves: obj_move_line.create_analytic_lines(cr, uid, [line.id for line in record.line_id], context) @@ -1835,10 +1808,12 @@ class account_tax_code(osv.osv): def name_search(self, cr, user, name, args=None, operator='ilike', context=None, limit=80): if not args: args = [] - if context is None: - context = {} - ids = self.search(cr, user, ['|',('name',operator,name),('code',operator,name)] + args, limit=limit, context=context) - return self.name_get(cr, user, ids, context) + if operator in expression.NEGATIVE_TERM_OPERATORS: + domain = [('code', operator, name), ('name', operator, name)] + else: + domain = ['|', ('code', operator, name), ('name', operator, name)] + ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) + return self.name_get(cr, user, ids, context=context) def name_get(self, cr, uid, ids, context=None): if isinstance(ids, (int, long)): @@ -1931,15 +1906,15 @@ class account_tax(osv.osv): # 'base_code_id': fields.many2one('account.tax.code', 'Account Base Code', help="Use this code for the tax declaration."), 'tax_code_id': fields.many2one('account.tax.code', 'Account Tax Code', help="Use this code for the tax declaration."), - 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), - 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), + 'base_sign': fields.float('Base Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), + 'tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), # Same fields for refund invoices 'ref_base_code_id': fields.many2one('account.tax.code', 'Refund Base Code', help="Use this code for the tax declaration."), 'ref_tax_code_id': fields.many2one('account.tax.code', 'Refund Tax Code', help="Use this code for the tax declaration."), - 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1."), - 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), + 'ref_base_sign': fields.float('Base Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), + 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1.", digits_compute=get_precision_tax()), 'include_base_amount': fields.boolean('Included in base amount', help="Indicates if the amount of tax must be included in the base amount for the computation of the next taxes"), 'company_id': fields.many2one('res.company', 'Company', required=True), 'description': fields.char('Tax Code'), @@ -1968,15 +1943,11 @@ class account_tax(osv.osv): """ if not args: args = [] - if context is None: - context = {} - ids = [] - if name: - ids = self.search(cr, user, [('description', '=', name)] + args, limit=limit, context=context) - if not ids: - ids = self.search(cr, user, [('name', operator, name)] + args, limit=limit, context=context) + if operator in expression.NEGATIVE_TERM_OPERATORS: + domain = [('description', operator, name), ('name', operator, name)] else: - ids = self.search(cr, user, args, limit=limit, context=context or {}) + domain = ['|', ('description', operator, name), ('name', operator, name)] + ids = self.search(cr, user, expression.AND([domain, args]), limit=limit, context=context) return self.name_get(cr, user, ids, context=context) def write(self, cr, uid, ids, vals, context=None): @@ -1985,15 +1956,17 @@ class account_tax(osv.osv): return super(account_tax, self).write(cr, uid, ids, vals, context=context) def search(self, cr, uid, args, offset=0, limit=None, order=None, context=None, count=False): + if context is None: + context = {} journal_pool = self.pool.get('account.journal') - if context and context.has_key('type'): + if context.get('type'): if context.get('type') in ('out_invoice','out_refund'): args += [('type_tax_use','in',['sale','all'])] elif context.get('type') in ('in_invoice','in_refund'): args += [('type_tax_use','in',['purchase','all'])] - if context and context.has_key('journal_id'): + if context.get('journal_id'): journal = journal_pool.browse(cr, uid, context.get('journal_id')) if journal.type in ('sale', 'purchase'): args += [('type_tax_use','in',[journal.type,'all'])] @@ -2137,7 +2110,7 @@ class account_tax(osv.osv): tax_compute_precision = precision if taxes and taxes[0].company_id.tax_calculation_rounding_method == 'round_globally': tax_compute_precision += 5 - totalin = totalex = float_round(price_unit * quantity, precision) + totalin = totalex = round(price_unit * quantity, precision) tin = [] tex = [] for tax in taxes: @@ -3056,11 +3029,19 @@ class wizard_multi_charts_accounts(osv.osv_memory): 'complete_tax_set': fields.boolean('Complete Set of Taxes', help='This boolean helps you to choose if you want to propose to the user to encode the sales and purchase rates or use the usual m2o fields. This last choice assumes that the set of tax defined for the chosen template is complete'), } - def onchange_company_id(self, cr, uid, ids, company_id, context=None): - currency_id = False - if company_id: - currency_id = self.pool.get('res.company').browse(cr, uid, company_id, context=context).currency_id.id - return {'value': {'currency_id': currency_id}} + + def _get_chart_parent_ids(self, cr, uid, chart_template, context=None): + """ Returns the IDs of all ancestor charts, including the chart itself. + (inverse of child_of operator) + + :param browse_record chart_template: the account.chart.template record + :return: the IDS of all ancestor charts, including the chart itself. + """ + result = [chart_template.id] + while chart_template.parent_id: + chart_template = chart_template.parent_id + result.append(chart_template.id) + return result def onchange_tax_rate(self, cr, uid, ids, rate=False, context=None): return {'value': {'purchase_tax_rate': rate or False}} @@ -3068,18 +3049,30 @@ class wizard_multi_charts_accounts(osv.osv_memory): def onchange_chart_template_id(self, cr, uid, ids, chart_template_id=False, context=None): res = {} tax_templ_obj = self.pool.get('account.tax.template') + ir_values = self.pool.get('ir.values') res['value'] = {'complete_tax_set': False, 'sale_tax': False, 'purchase_tax': False} if chart_template_id: data = self.pool.get('account.chart.template').browse(cr, uid, chart_template_id, context=context) - res['value'].update({'complete_tax_set': data.complete_tax_set}) + #set currecy_id based on selected COA template using ir.vaalues else current users company's currency + value_id = ir_values.search(cr, uid, [('model', '=', 'account.chart.template'), ('res_id', '=', chart_template_id)], limit=1, context=context) + if value_id: + currency_id = int(ir_values.browse(cr, uid, value_id[0], context=context).value) + else: + currency_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.currency_id.id + res['value'].update({'complete_tax_set': data.complete_tax_set, 'currency_id': currency_id}) if data.complete_tax_set: # default tax is given by the lowest sequence. For same sequence we will take the latest created as it will be the case for tax created while isntalling the generic chart of account - sale_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" - , "=", chart_template_id), ('type_tax_use', 'in', ('sale','all'))], order="sequence, id desc") - purchase_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" - , "=", chart_template_id), ('type_tax_use', 'in', ('purchase','all'))], order="sequence, id desc") - res['value'].update({'sale_tax': sale_tax_ids and sale_tax_ids[0] or False, 'purchase_tax': purchase_tax_ids and purchase_tax_ids[0] or False}) - + chart_ids = self._get_chart_parent_ids(cr, uid, data, context=context) + base_tax_domain = [("chart_template_id", "in", chart_ids), ('parent_id', '=', False)] + sale_tax_domain = base_tax_domain + [('type_tax_use', 'in', ('sale','all'))] + purchase_tax_domain = base_tax_domain + [('type_tax_use', 'in', ('purchase','all'))] + sale_tax_ids = tax_templ_obj.search(cr, uid, sale_tax_domain, order="sequence, id desc") + purchase_tax_ids = tax_templ_obj.search(cr, uid, purchase_tax_domain, order="sequence, id desc") + res['value'].update({'sale_tax': sale_tax_ids and sale_tax_ids[0] or False, + 'purchase_tax': purchase_tax_ids and purchase_tax_ids[0] or False}) + res.setdefault('domain', {}) + res['domain']['sale_tax'] = repr(sale_tax_domain) + res['domain']['purchase_tax'] = repr(purchase_tax_domain) if data.code_digits: res['value'].update({'code_digits': data.code_digits}) return res @@ -3087,6 +3080,8 @@ class wizard_multi_charts_accounts(osv.osv_memory): def default_get(self, cr, uid, fields, context=None): res = super(wizard_multi_charts_accounts, self).default_get(cr, uid, fields, context=context) tax_templ_obj = self.pool.get('account.tax.template') + account_chart_template = self.pool['account.chart.template'] + data_obj = self.pool.get('ir.model.data') if 'bank_accounts_id' in fields: res.update({'bank_accounts_id': [{'acc_name': _('Cash'), 'account_type': 'cash'},{'acc_name': _('Bank'), 'account_type': 'bank'}]}) @@ -3100,17 +3095,28 @@ class wizard_multi_charts_accounts(osv.osv_memory): currency_id = company_obj.on_change_country(cr, uid, company_id, country_id, context=context)['value']['currency_id'] res.update({'currency_id': currency_id}) - ids = self.pool.get('account.chart.template').search(cr, uid, [('visible', '=', True)], context=context) + ids = account_chart_template.search(cr, uid, [('visible', '=', True)], context=context) if ids: + #in order to set default chart which was last created set max of ids. + chart_id = max(ids) + if context.get("default_charts"): + data_ids = data_obj.search(cr, uid, [('model', '=', 'account.chart.template'), ('module', '=', context.get("default_charts"))], limit=1, context=context) + if data_ids: + chart_id = data_obj.browse(cr, uid, data_ids[0], context=context).res_id + chart = account_chart_template.browse(cr, uid, chart_id, context=context) + chart_hierarchy_ids = self._get_chart_parent_ids(cr, uid, chart, context=context) if 'chart_template_id' in fields: - res.update({'only_one_chart_template': len(ids) == 1, 'chart_template_id': ids[0]}) + res.update({'only_one_chart_template': len(ids) == 1, + 'chart_template_id': chart_id}) if 'sale_tax' in fields: - sale_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" - , "=", ids[0]), ('type_tax_use', 'in', ('sale','all'))], order="sequence") + sale_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id", "in", chart_hierarchy_ids), + ('type_tax_use', 'in', ('sale','all'))], + order="sequence") res.update({'sale_tax': sale_tax_ids and sale_tax_ids[0] or False}) if 'purchase_tax' in fields: - purchase_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id" - , "=", ids[0]), ('type_tax_use', 'in', ('purchase','all'))], order="sequence") + purchase_tax_ids = tax_templ_obj.search(cr, uid, [("chart_template_id", "in", chart_hierarchy_ids), + ('type_tax_use', 'in', ('purchase','all'))], + order="sequence") res.update({'purchase_tax': purchase_tax_ids and purchase_tax_ids[0] or False}) res.update({ 'purchase_tax_rate': 15.0, @@ -3378,12 +3384,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): obj_tax_temp = self.pool.get('account.tax.template') chart_template = obj_wizard.chart_template_id vals = {} - # get the ids of all the parents of the selected account chart template - current_chart_template = chart_template - all_parents = [current_chart_template.id] - while current_chart_template.parent_id: - current_chart_template = current_chart_template.parent_id - all_parents.append(current_chart_template.id) + all_parents = self._get_chart_parent_ids(cr, uid, chart_template, context=context) # create tax templates and tax code templates from purchase_tax_rate and sale_tax_rate fields if not chart_template.complete_tax_set: value = obj_wizard.sale_tax_rate @@ -3400,6 +3401,8 @@ class wizard_multi_charts_accounts(osv.osv_memory): all the provided information to create the accounts, the banks, the journals, the taxes, the tax codes, the accounting properties... accordingly for the chosen company. ''' + if uid != SUPERUSER_ID and not self.pool['res.users'].has_group(cr, uid, 'base.group_erp_manager'): + raise openerp.exceptions.AccessError(_("Only administrators can change the settings")) obj_data = self.pool.get('ir.model.data') ir_values_obj = self.pool.get('ir.values') obj_wizard = self.browse(cr, uid, ids[0]) @@ -3416,7 +3419,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): self.pool.get(tmp2[0]).write(cr, uid, tmp2[1], { 'currency_id': obj_wizard.currency_id.id }) - except ValueError, e: + except ValueError: pass # If the floats for sale/purchase rates have been filled, create templates from them diff --git a/addons/account/account_installer.xml b/addons/account/account_installer.xml index b03babc63ac..8d1b25b299f 100644 --- a/addons/account/account_installer.xml +++ b/addons/account/account_installer.xml @@ -10,7 +10,7 @@ diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 6201e9be162..55ba1058d58 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -117,7 +117,7 @@ class account_invoice(osv.osv): #we check if the invoice is partially reconciled and if there are other invoices #involved in this partial reconciliation (and we sum these invoices) for line in aml.reconcile_partial_id.line_partial_ids: - if line.invoice: + if line.invoice and invoice.type == line.invoice.type: nb_inv_in_partial_rec += 1 #store the max invoice id as for this invoice we will make a balance instead of a simple division max_invoice_id = max(max_invoice_id, line.invoice.id) @@ -560,10 +560,14 @@ class account_invoice(osv.osv): def onchange_payment_term_date_invoice(self, cr, uid, ids, payment_term_id, date_invoice): res = {} + if isinstance(ids, (int, long)): + ids = [ids] if not date_invoice: date_invoice = time.strftime('%Y-%m-%d') if not payment_term_id: - return {'value':{'date_due': date_invoice}} #To make sure the invoice has a due date when no payment term + inv = self.browse(cr, uid, ids[0]) + #To make sure the invoice due date should contain due date which is entered by user when there is no payment term defined + return {'value':{'date_due': inv.date_due and inv.date_due or date_invoice}} pterm_list = self.pool.get('account.payment.term').compute(cr, uid, payment_term_id, value=1, date_ref=date_invoice) if pterm_list: pterm_list = [line[0] for line in pterm_list] @@ -1403,6 +1407,7 @@ class account_invoice_line(osv.osv): _name = "account.invoice.line" _description = "Invoice Line" + _order = "invoice_id,sequence,id" _columns = { 'name': fields.text('Description', required=True), 'origin': fields.char('Source Document', size=256, help="Reference of the document that produced this invoice."), @@ -1439,6 +1444,7 @@ class account_invoice_line(osv.osv): 'discount': 0.0, 'price_unit': _price_unit_default, 'account_id': _default_account_id, + 'sequence': 10, } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index c30297b16ed..947aad481d1 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -191,6 +191,7 @@ + @@ -347,6 +348,7 @@ + @@ -455,7 +457,7 @@ - + diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index db031a745ed..61619276df7 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -193,6 +193,8 @@ class account_move_line(osv.osv): if obj_line.analytic_account_id: if not obj_line.journal_id.analytic_journal_id: raise osv.except_osv(_('No Analytic Journal!'),_("You have to define an analytic journal on the '%s' journal!") % (obj_line.journal_id.name, )) + if obj_line.analytic_lines: + acc_ana_line_obj.unlink(cr,uid,[obj.id for obj in obj_line.analytic_lines]) vals_line = self._prepare_analytic_line(cr, uid, obj_line, context=context) acc_ana_line_obj.create(cr, uid, vals_line) return True @@ -311,13 +313,13 @@ class account_move_line(osv.osv): context = {} c = context.copy() c['initital_bal'] = True - sql = """SELECT l2.id, SUM(l1.debit-l1.credit) - FROM account_move_line l1, account_move_line l2 - WHERE l2.account_id = l1.account_id - AND l1.id <= l2.id - AND l2.id IN %s AND """ + \ - self._query_get(cr, uid, obj='l1', context=c) + \ - " GROUP BY l2.id" + sql = """SELECT l1.id, COALESCE(SUM(l2.debit-l2.credit), 0) + FROM account_move_line l1 LEFT JOIN account_move_line l2 + ON (l1.account_id = l2.account_id + AND l2.id <= l1.id + AND """ + \ + self._query_get(cr, uid, obj='l2', context=c) + \ + ") WHERE l1.id IN %s GROUP BY l1.id" cr.execute(sql, [tuple(ids)]) return dict(cr.fetchall()) @@ -560,10 +562,11 @@ class account_move_line(osv.osv): ] def _auto_init(self, cr, context=None): - super(account_move_line, self)._auto_init(cr, context=context) + res = super(account_move_line, self)._auto_init(cr, context=context) cr.execute('SELECT indexname FROM pg_indexes WHERE indexname = \'account_move_line_journal_id_period_id_index\'') if not cr.fetchone(): cr.execute('CREATE INDEX account_move_line_journal_id_period_id_index ON account_move_line (journal_id, period_id)') + return res def _check_no_view(self, cr, uid, ids, context=None): lines = self.browse(cr, uid, ids, context=context) @@ -800,7 +803,7 @@ class account_move_line(osv.osv): r_id = move_rec_obj.create(cr, uid, { 'type': type, 'line_partial_ids': map(lambda x: (4,x,False), merges+unmerge) - }) + }, context=context) move_rec_obj.reconcile_partial_check(cr, uid, [r_id] + merges_rec, context=context) return True @@ -847,18 +850,17 @@ class account_move_line(osv.osv): (tuple(ids), )) r = cr.fetchall() #TODO: move this check to a constraint in the account_move_reconcile object + if len(r) != 1: + raise osv.except_osv(_('Error'), _('Entries are not of the same account or already reconciled ! ')) if not unrec_lines: raise osv.except_osv(_('Error!'), _('Entry is already reconciled.')) account = account_obj.browse(cr, uid, account_id, context=context) + if not account.reconcile: + raise osv.except_osv(_('Error'), _('The account is not defined to be reconciled !')) if r[0][1] != None: raise osv.except_osv(_('Error!'), _('Some entries are already reconciled.')) - if context.get('fy_closing'): - # We don't want to generate any write-off when being called from the - # wizard used to close a fiscal year (and it doesn't give us any - # writeoff_acc_id). - pass - elif (not currency_obj.is_zero(cr, uid, account.company_id.currency_id, writeoff)) or \ + if (not currency_obj.is_zero(cr, uid, account.company_id.currency_id, writeoff)) or \ (account.currency_id and (not currency_obj.is_zero(cr, uid, account.currency_id, currency))): if not writeoff_acc_id: raise osv.except_osv(_('Warning!'), _('You have to provide an account for the write off/exchange difference entry.')) @@ -1021,10 +1023,14 @@ class account_move_line(osv.osv): part_rec_ids = [rec['reconcile_partial_id'][0] for rec in part_recs] unlink_ids += rec_ids unlink_ids += part_rec_ids + all_moves = obj_move_line.search(cr, uid, ['|',('reconcile_id', 'in', unlink_ids),('reconcile_partial_id', 'in', unlink_ids)]) + all_moves = list(set(all_moves) - set(move_ids)) if unlink_ids: if opening_reconciliation: obj_move_rec.write(cr, uid, unlink_ids, {'opening_reconciliation': False}) obj_move_rec.unlink(cr, uid, unlink_ids) + if all_moves: + obj_move_line.reconcile_partial(cr, uid, all_moves, 'auto',context=context) return True def unlink(self, cr, uid, ids, context=None, check=True): @@ -1199,7 +1205,7 @@ class account_move_line(osv.osv): break # Automatically convert in the account's secondary currency if there is one and # the provided values were not already multi-currency - if account.currency_id and (vals.get('amount_currency', False) is False) and account.currency_id.id != account.company_id.currency_id.id: + if account.currency_id and 'amount_currency' not in vals and account.currency_id.id != account.company_id.currency_id.id: vals['currency_id'] = account.currency_id.id ctx = {} if 'date' in vals: @@ -1209,20 +1215,6 @@ class account_move_line(osv.osv): if not ok: raise osv.except_osv(_('Bad Account!'), _('You cannot use this general account in this journal, check the tab \'Entry Controls\' on the related journal.')) - if vals.get('analytic_account_id',False): - if journal.analytic_journal_id: - vals['analytic_lines'] = [(0,0, { - 'name': vals['name'], - 'date': vals.get('date', time.strftime('%Y-%m-%d')), - 'account_id': vals.get('analytic_account_id', False), - 'unit_amount': vals.get('quantity', 1.0), - 'amount': vals.get('debit', 0.0) or vals.get('credit', 0.0), - 'general_account_id': vals.get('account_id', False), - 'journal_id': journal.analytic_journal_id.id, - 'ref': vals.get('ref', False), - 'user_id': uid - })] - result = super(account_move_line, self).create(cr, uid, vals, context=context) # CREATE Taxes if vals.get('account_tax_id', False): @@ -1284,7 +1276,7 @@ class account_move_line(osv.osv): self.create(cr, uid, data, context) del vals['account_tax_id'] - if check and ((not context.get('no_store_function')) or journal.entry_posted): + if check and not context.get('novalidate') and ((not context.get('no_store_function')) or journal.entry_posted): tmp = move_obj.validate(cr, uid, [vals['move_id']], context) if journal.entry_posted and tmp: move_obj.button_validate(cr,uid, [vals['move_id']], context) diff --git a/addons/account/account_view.xml b/addons/account/account_view.xml index 10d9bf786f7..de51be0ddad 100644 --- a/addons/account/account_view.xml +++ b/addons/account/account_view.xml @@ -1440,7 +1440,7 @@ @@ -1588,7 +1588,6 @@ @@ -2125,7 +2124,7 @@ - +