diff --git a/addons/account/__openerp__.py b/addons/account/__openerp__.py index e1b9078ec3f..c538614fd0f 100644 --- a/addons/account/__openerp__.py +++ b/addons/account/__openerp__.py @@ -19,11 +19,11 @@ # ############################################################################## { - "name" : "eInvoicing", - "version" : "1.1", - "author" : "OpenERP SA", - "category": 'Accounting & Finance', - "description": """ + 'name' : 'eInvoicing', + 'version' : '1.1', + 'author' : 'OpenERP SA', + 'category' : 'Accounting & Finance', + 'description' : """ Accounting and Financial Management. ==================================== @@ -44,14 +44,13 @@ Creates a dashboard for accountants that includes: * Company Analysis * Graph of Treasury -The processes like maintaining of general ledger is done through the defined financial Journals (entry move line orgrouping is maintained through journal) for a particular -financial year and for preparation of vouchers there is a module named account_voucher. +The processes like maintaining of general ledger is done through the defined financial Journals (entry move line orgrouping is maintained through journal) +for a particular financial year and for preparation of vouchers there is a module named account_voucher. """, 'website': 'http://www.openerp.com', 'images' : ['images/accounts.jpeg','images/bank_statement.jpeg','images/cash_register.jpeg','images/chart_of_accounts.jpeg','images/customer_invoice.jpeg','images/journal_entries.jpeg'], - 'init_xml': [], - "depends" : ["base_setup", "product", "analytic", "process", "board", "edi"], - 'update_xml': [ + 'depends' : ['base_setup', 'product', 'analytic', 'process', 'board', 'edi'], + 'data': [ 'security/account_security.xml', 'security/ir.model.access.csv', 'account_menuitem.xml', @@ -64,6 +63,8 @@ financial year and for preparation of vouchers there is a module named account_v 'wizard/account_use_model_view.xml', 'account_installer.xml', 'wizard/account_period_close_view.xml', + 'wizard/account_reconcile_view.xml', + 'wizard/account_unreconcile_view.xml', 'account_view.xml', 'account_report.xml', 'account_financial_report_data.xml', @@ -86,14 +87,12 @@ financial year and for preparation of vouchers there is a module named account_v 'wizard/account_journal_select_view.xml', 'wizard/account_change_currency_view.xml', 'wizard/account_validate_move_view.xml', - 'wizard/account_unreconcile_view.xml', 'wizard/account_report_general_ledger_view.xml', 'wizard/account_invoice_state_view.xml', 'wizard/account_report_partner_balance_view.xml', 'wizard/account_report_account_balance_view.xml', 'wizard/account_report_aged_partner_balance_view.xml', 'wizard/account_report_partner_ledger_view.xml', - 'wizard/account_reconcile_view.xml', 'wizard/account_reconcile_partner_process_view.xml', 'wizard/account_automatic_reconcile_view.xml', 'wizard/account_financial_report_view.xml', @@ -122,12 +121,20 @@ financial year and for preparation of vouchers there is a module named account_v 'ir_sequence_view.xml', 'company_view.xml', 'board_account_view.xml', - "edi/invoice_action_data.xml", - "account_bank_view.xml", - "res_config_view.xml", - "account_pre_install.yml" + 'edi/invoice_action_data.xml', + 'account_bank_view.xml', + 'res_config_view.xml', + 'account_pre_install.yml' ], - 'demo_xml': [ + 'js': [ + 'static/src/js/account_move_reconciliation.js', + ], + 'qweb' : [ + "static/src/xml/account_move_reconciliation.xml", + ], + 'css':['static/src/css/account_move_reconciliation.css' + ], + 'demo': [ 'demo/account_demo.xml', 'project/project_demo.xml', 'project/analytic_account_demo.xml', @@ -152,6 +159,5 @@ financial year and for preparation of vouchers there is a module named account_v ], 'installable': True, 'auto_install': False, - 'certificate': '0080331923549', } # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/account/account.py b/addons/account/account.py index fa30b20f2ca..cfba30856a9 100644 --- a/addons/account/account.py +++ b/addons/account/account.py @@ -30,6 +30,8 @@ from osv import fields, osv import decimal_precision as dp from tools.translate import _ from tools.float_utils import float_round +from openerp import SUPERUSER_ID +import tools _logger = logging.getLogger(__name__) @@ -109,7 +111,7 @@ class account_payment_term_line(osv.osv): 'days': fields.integer('Number of Days', required=True, help="Number of days to add before computation of the day of month." \ "If Date=15/01, Number of Days=22, Day of Month=-1, then the due date is 28/02."), 'days2': fields.integer('Day of the Month', required=True, help="Day of the month, set -1 for the last day of the current month. If it's positive, it gives the day of the next month. Set 0 for net days (otherwise it's based on the beginning of the month)."), - 'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True), + 'payment_id': fields.many2one('account.payment.term', 'Payment Term', required=True, select=True, ondelete='cascade'), } _defaults = { 'value': 'balance', @@ -153,6 +155,7 @@ class account_account_type(osv.osv): return res 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 @@ -179,7 +182,7 @@ class account_account_type(osv.osv): 'Balance' will generally be used for cash accounts. 'Detail' will copy each existing journal item of the previous year, even the reconciled ones. 'Unreconciled' will copy only the journal items that were unreconciled on the first day of the new fiscal year."""), - 'report_type': fields.function(_get_current_report_type, fnct_inv=_save_report_type, type='selection', string='P&L / BS Category', + 'report_type': fields.function(_get_current_report_type, fnct_inv=_save_report_type, type='selection', string='P&L / BS Category', store=True, selection= [('none','/'), ('income', _('Profit & Loss (Income account)')), ('expense', _('Profit & Loss (Expense account)')), @@ -225,7 +228,7 @@ class account_account(osv.osv): while pos < len(args): if args[pos][0] == 'code' and args[pos][1] in ('like', 'ilike') and args[pos][2]: - args[pos] = ('code', '=like', str(args[pos][2].replace('%', ''))+'%') + args[pos] = ('code', '=like', tools.ustr(args[pos][2].replace('%', ''))+'%') if args[pos][0] == 'journal_id': if not args[pos][2]: del args[pos] @@ -297,7 +300,6 @@ class account_account(osv.osv): if aml_query.strip(): wheres.append(aml_query.strip()) filters = " AND ".join(wheres) - _logger.debug('Filters: %s',(filters)) # IN might not work ideally in case there are too many # children_and_consolidated, in that case join on a # values() e.g.: @@ -313,7 +315,6 @@ class account_account(osv.osv): " GROUP BY l.account_id") params = (tuple(children_and_consolidated),) + query_params cr.execute(request, params) - _logger.debug('Status: %s',(cr.statusmessage)) for row in cr.dictfetchall(): accounts[row['id']] = row @@ -540,10 +541,18 @@ class account_account(osv.osv): return False return True + def _check_company_account(self, cr, uid, ids, context=None): + for account in self.browse(cr, uid, ids, context=context): + if account.parent_id: + if account.company_id != account.parent_id.company_id: + return False + return True + _constraints = [ (_check_recursion, 'Error!\nYou cannot create recursive accounts.', ['parent_id']), (_check_type, 'Configuration Error!\nYou cannot define children to an account with internal type different of "View".', ['type']), (_check_account_type, 'Configuration Error!\nYou cannot select an account type with a deferral method different of "Unreconciled" for accounts with internal type "Payable/Receivable".', ['user_type','type']), + (_check_company_account, 'Error!\nYou cannot create an account which has parent account of different company.', ['parent_id']), ] _sql_constraints = [ ('code_company_uniq', 'unique (code,company_id)', 'The code of the account must be unique per company !') @@ -582,6 +591,8 @@ class account_account(osv.osv): def name_get(self, cr, uid, ids, context=None): if not ids: return [] + if isinstance(ids, (int, long)): + ids = [ids] reads = self.read(cr, uid, ids, ['name', 'code'], context=context) res = [] for record in reads: @@ -591,13 +602,13 @@ class account_account(osv.osv): res.append((record['id'], name)) return res - def copy(self, cr, uid, id, default={}, context=None, done_list=[], local=False): + def copy(self, cr, uid, id, default=None, context=None, done_list=None, local=False): + default = {} if default is None else default.copy() + if done_list is None: + done_list = [] account = self.browse(cr, uid, id, context=context) new_child_ids = [] - if not default: - default = {} - default = default.copy() - default['code'] = (account['code'] or '') + '(copy)' + default.update(code=_("%s (copy)") % (account['code'] or '')) if not local: done_list = [] if account.id in done_list: @@ -678,7 +689,7 @@ class account_journal_view(osv.osv): _name = "account.journal.view" _description = "Journal View" _columns = { - 'name': fields.char('Journal View', size=64, required=True), + 'name': fields.char('Journal View', size=64, required=True, translate=True), 'columns_id': fields.one2many('account.journal.column', 'view_id', 'Columns') } _order = "name" @@ -744,9 +755,11 @@ class account_journal(osv.osv): 'profit_account_id' : fields.many2one('account.account', 'Profit Account'), 'loss_account_id' : fields.many2one('account.account', 'Loss Account'), 'internal_account_id' : fields.many2one('account.account', 'Internal Transfers Account', select=1), + 'cash_control' : fields.boolean('Cash Control', help='If you want the journal should be control at opening/closing, check this option'), } _defaults = { + 'cash_control' : False, 'with_last_closing_balance' : False, 'user_id': lambda self, cr, uid, context: uid, 'company_id': lambda self, cr, uid, c: self.pool.get('res.users').browse(cr, uid, uid, c).company_id.id, @@ -771,14 +784,15 @@ class account_journal(osv.osv): (_check_currency, 'Configuration error!\nThe currency chosen should be shared by the default accounts too.', ['currency','default_debit_account_id','default_credit_account_id']), ] - def copy(self, cr, uid, id, default={}, context=None, done_list=[], local=False): + def copy(self, cr, uid, id, default=None, context=None, done_list=None, local=False): + default = {} if default is None else default.copy() + if done_list is None: + done_list = [] journal = self.browse(cr, uid, id, context=context) - if not default: - default = {} - default = default.copy() - default['code'] = (journal['code'] or '') + '(copy)' - default['name'] = (journal['name'] or '') + '(copy)' - default['sequence_id'] = False + default.update( + code=_("%s (copy)") % (journal['code'] or ''), + name=_("%s (copy)") % (journal['name'] or ''), + sequence_id=False) return super(account_journal, self).copy(cr, uid, id, default, context=context) def write(self, cr, uid, ids, vals, context=None): @@ -815,7 +829,7 @@ class account_journal(osv.osv): if not 'sequence_id' in vals or not vals['sequence_id']: # if we have the right to create a journal, we should be able to # create it's sequence. - vals.update({'sequence_id': self.create_sequence(cr, 1, vals, context)}) + vals.update({'sequence_id': self.create_sequence(cr, SUPERUSER_ID, vals, context)}) return super(account_journal, self).create(cr, uid, vals, context) def name_get(self, cr, user, ids, context=None): @@ -830,6 +844,10 @@ class account_journal(osv.osv): @return: Returns a list of tupples containing id, name """ + if not ids: + return [] + if isinstance(ids, (int, long)): + ids = [ids] result = self.browse(cr, user, ids, context=context) res = [] for rs in result: @@ -997,7 +1015,7 @@ class account_period(osv.osv): 'date_stop': fields.date('End of Period', required=True, states={'done':[('readonly',True)]}), 'fiscalyear_id': fields.many2one('account.fiscalyear', 'Fiscal Year', required=True, states={'done':[('readonly',True)]}, select=True), 'state': fields.selection([('draft','Open'), ('done','Closed')], 'Status', readonly=True, - help='When monthly periods are created. The state is \'Draft\'. At the end of monthly period it is in \'Done\' state.'), + help='When monthly periods are created. The status is \'Draft\'. At the end of monthly period it is in \'Done\' status.'), 'company_id': fields.related('fiscalyear_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True) } _defaults = { @@ -1124,7 +1142,7 @@ class account_journal_period(osv.osv): 'icon': fields.function(_icon_get, string='Icon', type='char', size=32), 'active': fields.boolean('Active', required=True, help="If the active field is set to False, it will allow you to hide the journal period without removing it."), 'state': fields.selection([('draft','Draft'), ('printed','Printed'), ('done','Done')], 'Status', required=True, readonly=True, - help='When journal period is created. The state is \'Draft\'. If a report is printed it comes to \'Printed\' state. When all transactions are done, it comes in \'Done\' state.'), + help='When journal period is created. The status is \'Draft\'. If a report is printed it comes to \'Printed\' status. When all transactions are done, it comes in \'Done\' status.'), 'fiscalyear_id': fields.related('period_id', 'fiscalyear_id', string='Fiscal Year', type='many2one', relation='account.fiscalyear'), 'company_id': fields.related('journal_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, readonly=True) } @@ -1167,7 +1185,8 @@ class account_fiscalyear(osv.osv): 'end_journal_period_id':fields.many2one('account.journal.period','End of Year Entries Journal', readonly=True), } - def copy(self, cr, uid, id, default={}, context=None): + def copy(self, cr, uid, id, default=None, context=None): + default = {} if default is None else default.copy() default.update({ 'period_ids': [], 'end_journal_period_id': False @@ -1271,7 +1290,7 @@ class account_move(osv.osv): 'period_id': fields.many2one('account.period', 'Period', required=True, states={'posted':[('readonly',True)]}), 'journal_id': fields.many2one('account.journal', 'Journal', required=True, states={'posted':[('readonly',True)]}), 'state': fields.selection([('draft','Unposted'), ('posted','Posted')], 'Status', required=True, readonly=True, - help='All manually created new journal entries are usually in the state \'Unposted\', but you can set the option to skip that state 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\' state.'), + 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), @@ -1369,7 +1388,7 @@ class account_move(osv.osv): balance = 0.0 for line in line_ids: if line[2]: - balance += (line[2]['debit'] or 0.00)- (line[2]['credit'] or 0.00) + balance += (line[2].get('debit',0.00)- (line[2].get('credit',0.00))) return {'value': {'balance': balance}} def write(self, cr, uid, ids, vals, context=None): @@ -1392,7 +1411,7 @@ class account_move(osv.osv): if not l[0]: l[2].update({ 'reconcile_id':False, - 'reconcil_partial_id':False, + 'reconcile_partial_id':False, 'analytic_lines':False, 'invoice':False, 'ref':False, @@ -1420,15 +1439,18 @@ class account_move(osv.osv): if 'line_id' in vals: c = context.copy() c['novalidate'] = True + c['period_id'] = vals['period_id'] + c['journal_id'] = vals['journal_id'] + c['date'] = vals['date'] result = super(account_move, self).create(cr, uid, vals, c) self.validate(cr, uid, [result], context) else: result = super(account_move, self).create(cr, uid, vals, context) return result - def copy(self, cr, uid, id, default={}, context=None): - if context is None: - context = {} + def copy(self, cr, uid, id, default=None, context=None): + default = {} if default is None else default.copy() + context = {} if context is None else context.copy() default.update({ 'state':'draft', 'name':'/', @@ -1854,7 +1876,7 @@ class account_tax(osv.osv): def get_precision_tax(): def change_digit_tax(cr): - res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, 1, 'Account') + res = pooler.get_pool(cr.dbname).get('decimal.precision').precision_get(cr, SUPERUSER_ID, 'Account') return (16, res+2) return change_digit_tax @@ -1897,7 +1919,7 @@ class account_tax(osv.osv): 'ref_tax_sign': fields.float('Tax Code Sign', help="Usually 1 or -1."), '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',size=32), + 'description': fields.char('Tax Code'), 'price_include': fields.boolean('Tax Included in Price', help="Check this if the price you use on the product and invoices includes this tax."), 'type_tax_use': fields.selection([('sale','Sale'),('purchase','Purchase'),('all','All')], 'Tax Application', required=True) @@ -2258,7 +2280,10 @@ class account_model(osv.osv): _defaults = { 'legend': lambda self, cr, uid, context:_('You can specify year, month and date in the name of the model using the following labels:\n\n%(year)s: To Specify Year \n%(month)s: To Specify Month \n%(date)s: Current Date\n\ne.g. My model on %(date)s'), } - def generate(self, cr, uid, ids, datas={}, context=None): + + def generate(self, cr, uid, ids, data=None, context=None): + if data is None: + data = {} move_ids = [] entry = {} account_move_obj = self.pool.get('account.move') @@ -2269,8 +2294,8 @@ class account_model(osv.osv): if context is None: context = {} - if datas.get('date', False): - context.update({'date': datas['date']}) + if data.get('date', False): + context.update({'date': data['date']}) move_date = context.get('date', time.strftime('%Y-%m-%d')) move_date = datetime.strptime(move_date,"%Y-%m-%d") @@ -2332,6 +2357,16 @@ class account_model(osv.osv): return move_ids + def onchange_journal_id(self, cr, uid, ids, journal_id, context=None): + company_id = False + + if journal_id: + journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context) + if journal.company_id.id: + company_id = journal.company_id.id + + return {'value': {'company_id': company_id}} + account_model() class account_model_line(osv.osv): @@ -2446,10 +2481,10 @@ class account_subscription_line(osv.osv): all_moves = [] obj_model = self.pool.get('account.model') for line in self.browse(cr, uid, ids, context=context): - datas = { + data = { 'date': line.date, } - move_ids = obj_model.generate(cr, uid, [line.subscription_id.model_id.id], datas, context) + move_ids = obj_model.generate(cr, uid, [line.subscription_id.model_id.id], data, context) tocheck[line.subscription_id.id] = True self.write(cr, uid, [line.id], {'move_id':move_ids[0]}) all_moves.extend(move_ids) @@ -2497,7 +2532,7 @@ class account_account_template(osv.osv): 'reconcile': fields.boolean('Allow Reconciliation', help="Check this option if you want the user to reconcile entries in this account."), 'shortcut': fields.char('Shortcut', size=12), 'note': fields.text('Note'), - 'parent_id': fields.many2one('account.account.template', 'Parent Account Template', ondelete='cascade'), + 'parent_id': fields.many2one('account.account.template', 'Parent Account Template', ondelete='cascade', domain=[('type','=','view')]), 'child_parent_ids':fields.one2many('account.account.template', 'parent_id', 'Children'), 'tax_ids': fields.many2many('account.tax.template', 'account_account_template_tax_rel', 'account_id', 'tax_id', 'Default Taxes'), 'nocreate': fields.boolean('Optional create', help="If checked, the new chart of accounts will not contain this by default."), @@ -2510,20 +2545,9 @@ class account_account_template(osv.osv): 'nocreate': False, } - def _check_type(self, cr, uid, ids, context=None): - if context is None: - context = {} - accounts = self.browse(cr, uid, ids, context=context) - for account in accounts: - if account.parent_id and account.parent_id.type != 'view': - return False - return True - _check_recursion = check_cycle _constraints = [ (_check_recursion, 'Error!\nYou cannot create recursive account templates.', ['parent_id']), - (_check_type, 'Configuration Error!\nYou cannot define children to an account that has internal type other than "View".', ['type']), - ] def name_get(self, cr, uid, ids, context=None): @@ -2755,7 +2779,6 @@ class account_chart_template(osv.osv): 'property_account_income_categ': fields.many2one('account.account.template', 'Income Category Account'), 'property_account_expense': fields.many2one('account.account.template', 'Expense Account on Product Template'), 'property_account_income': fields.many2one('account.account.template', 'Income Account on Product Template'), - 'property_reserve_and_surplus_account': fields.many2one('account.account.template', 'Reserve and Profit/Loss Account', domain=[('type', '=', 'payable')], help='This Account is used for transferring Profit/Loss(If It is Profit: Amount will be added, Loss: Amount will be deducted.), Which is calculated from Profilt & Loss Report'), 'property_account_income_opening': fields.many2one('account.account.template', 'Opening Entries Income Account'), 'property_account_expense_opening': fields.many2one('account.account.template', 'Opening Entries Expense Account'), } @@ -2804,7 +2827,7 @@ class account_tax_template(osv.osv): '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."), 'include_base_amount': fields.boolean('Include in Base Amount', help="Set if the amount of tax must be included in the base amount before computing the next taxes."), - 'description': fields.char('Internal Name', size=32), + 'description': fields.char('Internal Name'), 'type_tax_use': fields.selection([('sale','Sale'),('purchase','Purchase'),('all','All')], 'Tax Use In', required=True,), 'price_include': fields.boolean('Tax Included in Price', help="Check this if the price you use on the product and invoices includes this tax."), } @@ -2993,6 +3016,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): _columns = { 'company_id':fields.many2one('res.company', 'Company', required=True), + 'currency_id': fields.many2one('res.currency', 'Currency', help="Currency as per company's country."), 'only_one_chart_template': fields.boolean('Only One Chart Template Available'), 'chart_template_id': fields.many2one('account.chart.template', 'Chart Template', required=True), 'bank_accounts_id': fields.one2many('account.bank.accounts.wizard', 'bank_account_id', 'Cash and Banks', required=True), @@ -3003,6 +3027,13 @@ class wizard_multi_charts_accounts(osv.osv_memory): 'purchase_tax_rate': fields.float('Purchase Tax(%)'), '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 onchange_tax_rate(self, cr, uid, ids, rate=False, context=None): return {'value': {'purchase_tax_rate': rate or False}} @@ -3033,6 +3064,13 @@ class wizard_multi_charts_accounts(osv.osv_memory): res.update({'bank_accounts_id': [{'acc_name': _('Cash'), 'account_type': 'cash'},{'acc_name': _('Bank'), 'account_type': 'bank'}]}) if 'company_id' in fields: res.update({'company_id': self.pool.get('res.users').browse(cr, uid, [uid], context=context)[0].company_id.id}) + if 'currency_id' in fields: + company_id = res.get('company_id') or False + if company_id: + company_obj = self.pool.get('res.company') + country_id = company_obj.browse(cr, uid, company_id, context=context).country_id.id + 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) if ids: @@ -3188,7 +3226,6 @@ class wizard_multi_charts_accounts(osv.osv_memory): ('property_account_income_categ','product.category','account.account'), ('property_account_expense','product.template','account.account'), ('property_account_income','product.template','account.account'), - ('property_reserve_and_surplus_account','res.company','account.account') ] template = self.pool.get('account.chart.template').browse(cr, uid, chart_template_id, context=context) for record in todo_list: @@ -3211,7 +3248,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): property_obj.create(cr, uid, vals, context=context) return True - def _install_template(self, cr, uid, template_id, company_id, code_digits=None, obj_wizard=None, acc_ref={}, taxes_ref={}, tax_code_ref={}, context=None): + def _install_template(self, cr, uid, template_id, company_id, code_digits=None, obj_wizard=None, acc_ref=None, taxes_ref=None, tax_code_ref=None, context=None): ''' This function recursively loads the template objects and create the real objects from them. @@ -3229,6 +3266,12 @@ class wizard_multi_charts_accounts(osv.osv_memory): * a last identical containing the mapping of tax code templates and tax codes :rtype: tuple(dict, dict, dict) ''' + if acc_ref is None: + acc_ref = {} + if taxes_ref is None: + taxes_ref = {} + if tax_code_ref is None: + tax_code_ref = {} template = self.pool.get('account.chart.template').browse(cr, uid, template_id, context=context) if template.parent_id: tmp1, tmp2, tmp3 = self._install_template(cr, uid, template.parent_id.id, company_id, code_digits=code_digits, acc_ref=acc_ref, taxes_ref=taxes_ref, tax_code_ref=tax_code_ref, context=context) @@ -3241,7 +3284,7 @@ class wizard_multi_charts_accounts(osv.osv_memory): tax_code_ref.update(tmp3) return acc_ref, taxes_ref, tax_code_ref - def _load_template(self, cr, uid, template_id, company_id, code_digits=None, obj_wizard=None, account_ref={}, taxes_ref={}, tax_code_ref={}, context=None): + def _load_template(self, cr, uid, template_id, company_id, code_digits=None, obj_wizard=None, account_ref=None, taxes_ref=None, tax_code_ref=None, context=None): ''' This function generates all the objects from the templates @@ -3259,6 +3302,12 @@ class wizard_multi_charts_accounts(osv.osv_memory): * a last identical containing the mapping of tax code templates and tax codes :rtype: tuple(dict, dict, dict) ''' + if account_ref is None: + account_ref = {} + if taxes_ref is None: + taxes_ref = {} + if tax_code_ref is None: + tax_code_ref = {} template = self.pool.get('account.chart.template').browse(cr, uid, template_id, context=context) obj_tax_code_template = self.pool.get('account.tax.code.template') obj_acc_tax = self.pool.get('account.tax') @@ -3337,19 +3386,18 @@ class wizard_multi_charts_accounts(osv.osv_memory): ir_values_obj = self.pool.get('ir.values') obj_wizard = self.browse(cr, uid, ids[0]) company_id = obj_wizard.company_id.id + self.pool.get('res.company').write(cr, uid, [company_id], {'currency_id': obj_wizard.currency_id.id}) # If the floats for sale/purchase rates have been filled, create templates from them self._create_tax_templates_from_rates(cr, uid, obj_wizard, company_id, context=context) # Install all the templates objects and generate the real objects acc_template_ref, taxes_ref, tax_code_ref = self._install_template(cr, uid, obj_wizard.chart_template_id.id, company_id, code_digits=obj_wizard.code_digits, obj_wizard=obj_wizard, context=context) - # write values of default taxes for product + # write values of default taxes for product as super user if obj_wizard.sale_tax and taxes_ref: - ir_values_obj.set(cr, uid, key='default', key2=False, name="taxes_id", company=company_id, - models =[('product.product',False)], value=[taxes_ref[obj_wizard.sale_tax.id]]) + ir_values_obj.set_default(cr, SUPERUSER_ID, 'product.product', "taxes_id", [taxes_ref[obj_wizard.sale_tax.id]], for_all_users=True, company_id=company_id) if obj_wizard.purchase_tax and taxes_ref: - ir_values_obj.set(cr, uid, key='default', key2=False, name="supplier_taxes_id", company=company_id, - models =[('product.product',False)], value=[taxes_ref[obj_wizard.purchase_tax.id]]) + ir_values_obj.set_default(cr, SUPERUSER_ID, 'product.product', "supplier_taxes_id", [taxes_ref[obj_wizard.purchase_tax.id]], for_all_users=True, company_id=company_id) # Create Bank journals self._create_bank_journals_from_o2m(cr, uid, obj_wizard, company_id, acc_template_ref, context=context) diff --git a/addons/account/account_analytic_line.py b/addons/account/account_analytic_line.py index 2117f6e49ea..066f8d1abec 100644 --- a/addons/account/account_analytic_line.py +++ b/addons/account/account_analytic_line.py @@ -39,7 +39,6 @@ class account_analytic_line(osv.osv): } _defaults = { - 'date': fields.date.context_today, 'company_id': lambda self,cr,uid,c: self.pool.get('res.company')._company_default_get(cr, uid, 'account.analytic.line', context=c), } _order = 'date desc' @@ -108,7 +107,7 @@ class account_analytic_line(osv.osv): if journal_id: journal = analytic_journal_obj.browse(cr, uid, journal_id, context=context) if journal.type == 'sale': - product_price_type_ids = product_price_type_obj.search(cr, uid, [('field','=','list_price')], context) + product_price_type_ids = product_price_type_obj.search(cr, uid, [('field','=','list_price')], context=context) if product_price_type_ids: pricetype = product_price_type_obj.browse(cr, uid, product_price_type_ids, context=context)[0] # Take the company currency as the reference one diff --git a/addons/account/account_bank.py b/addons/account/account_bank.py index ef146128a67..2323f8aaa8a 100644 --- a/addons/account/account_bank.py +++ b/addons/account/account_bank.py @@ -29,12 +29,12 @@ class bank(osv.osv): 'currency_id': fields.related('journal_id', 'currency', type="many2one", relation='res.currency', readonly=True, string="Currency", help="Currency of the related account journal."), } - def create(self, cr, uid, data, context={}): + def create(self, cr, uid, data, context=None): result = super(bank, self).create(cr, uid, data, context=context) self.post_write(cr, uid, [result], context=context) return result - def write(self, cr, uid, ids, data, context={}): + def write(self, cr, uid, ids, data, context=None): result = super(bank, self).write(cr, uid, ids, data, context=context) self.post_write(cr, uid, ids, context=context) return result @@ -43,13 +43,17 @@ class bank(osv.osv): "Return the name to use when creating a bank journal" return (bank.bank_name or '') + ' ' + bank.acc_number - def _prepare_name_get(self, cr, uid, bank_type_obj, bank_obj, context=None): - """Add ability to have %(currency_name)s in the format_layout of - res.partner.bank.type""" - bank_obj._data[bank_obj.id]['currency_name'] = bank_obj.currency_id and bank_obj.currency_id.name or '' - return super(bank, self)._prepare_name_get(cr, uid, bank_type_obj, bank_obj, context=context) + def _prepare_name_get(self, cr, uid, bank_dicts, context=None): + """Add ability to have %(currency_name)s in the format_layout of res.partner.bank.type""" + currency_ids = list(set(data['currency_id'][0] for data in bank_dicts if data['currency_id'])) + currencies = self.pool.get('res.currency').browse(cr, uid, currency_ids, context=context) + currency_name = dict((currency.id, currency.name) for currency in currencies) - def post_write(self, cr, uid, ids, context={}): + for data in bank_dicts: + data['currency_name'] = data['currency_id'] and currency_name[data['currency_id'][0]] or '' + return super(bank, self)._prepare_name_get(cr, uid, bank_dicts, context=context) + + def post_write(self, cr, uid, ids, context=None): if isinstance(ids, (int, long)): ids = [ids] diff --git a/addons/account/account_bank_statement.py b/addons/account/account_bank_statement.py index 8df291a98d1..4422537007f 100644 --- a/addons/account/account_bank_statement.py +++ b/addons/account/account_bank_statement.py @@ -61,7 +61,7 @@ class account_bank_statement(osv.osv): return res def _get_period(self, cr, uid, context=None): - periods = self.pool.get('account.period').find(cr, uid) + periods = self.pool.get('account.period').find(cr, uid,context=context) if periods: return periods[0] return False @@ -123,8 +123,8 @@ class account_bank_statement(osv.osv): ('open','Open'), # used by cash statements ('confirm', 'Closed')], 'Status', required=True, readonly="1", - help='When new statement is created the state will be \'Draft\'.\n' - 'And after getting confirmation from the bank it will be in \'Confirmed\' state.'), + help='When new statement is created the status will be \'Draft\'.\n' + 'And after getting confirmation from the bank it will be in \'Confirmed\' status.'), 'currency': fields.function(_currency, string='Currency', type='many2one', relation='res.currency'), 'account_id': fields.related('journal_id', 'default_debit_account_id', type='many2one', relation='account.account', string='Account used in this journal', readonly=True, help='used in statement reconciliation domain, but shouldn\'t be used elswhere.'), @@ -304,7 +304,7 @@ class account_bank_statement(osv.osv): 'date': st_line.date, 'ref': st_line.ref, 'move_id': move_id, - 'partner_id': partner_id, + 'partner_id': par_id, 'account_id': acc_id, 'credit': credit, 'debit': debit, @@ -430,7 +430,7 @@ class account_bank_statement(osv.osv): 'name': st_number, 'balance_end_real': st.balance_end }, context=context) - self.message_append_note(cr, uid, [st.id], body=_('Statement %s is confirmed, journal items are created.') % (st_number,), context=context) + self.message_post(cr, uid, [st.id], body=_('Statement %s confirmed, journal items were created.') % (st_number,), context=context) return self.write(cr, uid, ids, {'state':'confirm'}, context=context) def button_cancel(self, cr, uid, ids, context=None): @@ -447,20 +447,24 @@ class account_bank_statement(osv.osv): return self.write(cr, uid, done, {'state':'draft'}, context=context) def _compute_balance_end_real(self, cr, uid, journal_id, context=None): - cr.execute('SELECT balance_end_real \ - FROM account_bank_statement \ - WHERE journal_id = %s AND NOT state = %s \ - ORDER BY date DESC,id DESC LIMIT 1', (journal_id, 'draft')) - res = cr.fetchone() + res = False + if journal_id: + cr.execute('SELECT balance_end_real \ + FROM account_bank_statement \ + WHERE journal_id = %s AND NOT state = %s \ + ORDER BY date DESC,id DESC LIMIT 1', (journal_id, 'draft')) + res = cr.fetchone() return res and res[0] or 0.0 def onchange_journal_id(self, cr, uid, statement_id, journal_id, context=None): + if not journal_id: + return {} balance_start = self._compute_balance_end_real(cr, uid, journal_id, context=context) - journal_data = self.pool.get('account.journal').read(cr, uid, journal_id, ['default_debit_account_id', 'company_id'], context=context) - account_id = journal_data['default_debit_account_id'] + journal_data = self.pool.get('account.journal').read(cr, uid, journal_id, ['company_id', 'currency'], context=context) company_id = journal_data['company_id'] - return {'value': {'balance_start': balance_start, 'account_id': account_id, 'company_id': company_id}} + currency_id = journal_data['currency'] or self.pool.get('res.company').browse(cr, uid, company_id[0], context=context).currency_id.id + return {'value': {'balance_start': balance_start, 'company_id': company_id, 'currency': currency_id}} def unlink(self, cr, uid, ids, context=None): stat = self.read(cr, uid, ids, ['state'], context=context) @@ -482,6 +486,19 @@ class account_bank_statement(osv.osv): default['move_line_ids'] = [] return super(account_bank_statement, self).copy(cr, uid, id, default, context=context) + def button_journal_entries(self, cr, uid, ids, context=None): + ctx = (context or {}).copy() + ctx['journal_id'] = self.browse(cr, uid, ids[0], context=context).journal_id.id + return { + 'view_type':'form', + 'view_mode':'tree', + 'res_model':'account.move.line', + 'view_id':False, + 'type':'ir.actions.act_window', + 'domain':[('statement_id','in',ids)], + 'context':ctx, + } + account_bank_statement() class account_bank_statement_line(osv.osv): diff --git a/addons/account/account_bank_view.xml b/addons/account/account_bank_view.xml index 8ceb8a91885..ef793a1bef5 100644 --- a/addons/account/account_bank_view.xml +++ b/addons/account/account_bank_view.xml @@ -15,7 +15,7 @@ - + @@ -28,7 +28,7 @@ - + diff --git a/addons/account/account_cash_statement.py b/addons/account/account_cash_statement.py index 9af0dcf60f4..c1f30824461 100644 --- a/addons/account/account_cash_statement.py +++ b/addons/account/account_cash_statement.py @@ -78,7 +78,7 @@ class account_cash_statement(osv.osv): """ res = {} for statement in self.browse(cr, uid, ids, context=context): - if statement.journal_id.type not in ('cash',): + if (statement.journal_id.type not in ('cash',)) or (not statement.journal_id.cash_control): continue start = end = 0 for line in statement.details_ids: @@ -194,12 +194,27 @@ class account_cash_statement(osv.osv): journal = self.pool.get('account.journal').browse(cr, uid, vals['journal_id'], context=context) if journal and (journal.type == 'cash') and not vals.get('details_ids'): vals['details_ids'] = [] + + last_pieces = None + + if journal.with_last_closing_balance == True: + domain = [('journal_id', '=', journal.id), + ('state', '=', 'confirm')] + last_bank_statement_ids = self.search(cr, uid, domain, limit=1, order='create_date desc', context=context) + if last_bank_statement_ids: + last_bank_statement = self.browse(cr, uid, last_bank_statement_ids[0], context=context) + + last_pieces = dict( + (line.pieces, line.number_closing) for line in last_bank_statement.details_ids + ) + for value in journal.cashbox_line_ids: nested_values = { 'number_closing' : 0, - 'number_opening' : 0, + 'number_opening' : last_pieces.get(value.pieces, 0) if isinstance(last_pieces, dict) else 0, 'pieces' : value.pieces } + vals['details_ids'].append([0, False, nested_values]) res_id = super(account_cash_statement, self).create(cr, uid, vals, context=context) @@ -274,13 +289,13 @@ class account_cash_statement(osv.osv): super(account_cash_statement, self).button_confirm_bank(cr, uid, ids, context=context) absl_proxy = self.pool.get('account.bank.statement.line') - TABLES = (('Profit', 'profit_account_id'), ('Loss', 'loss_account_id'),) + TABLES = ((_('Profit'), 'profit_account_id'), (_('Loss'), 'loss_account_id'),) for obj in self.browse(cr, uid, ids, context=context): if obj.difference == 0.0: continue - for item_label, item_account in TALBES: + for item_label, item_account in TABLES: if getattr(obj.journal_id, item_account): raise osv.except_osv(_('Error!'), _('There is no %s Account on the journal %s.') % (item_label, obj.journal_id.name,)) diff --git a/addons/account/account_installer.xml b/addons/account/account_installer.xml index c92d4eda602..58e824a6250 100644 --- a/addons/account/account_installer.xml +++ b/addons/account/account_installer.xml @@ -14,8 +14,12 @@ +

+ Select a configuration package to setup automatically your + taxes and chart of accounts. +

- + @@ -32,7 +36,7 @@ - Configure your Chart of Accounts + Configure Accounting Data ir.actions.act_window account.installer diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 012bde42eff..3e86c4963b7 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -185,6 +185,7 @@ class account_invoice(osv.osv): _columns = { 'name': fields.char('Description', size=64, select=True, readonly=True, states={'draft':[('readonly',False)]}), 'origin': fields.char('Source Document', size=64, help="Reference of the document that produced this invoice.", readonly=True, states={'draft':[('readonly',False)]}), + 'supplier_invoice_number': fields.char('Supplier Invoice Number', size=64, help="The reference of this invoice as provided by the supplier.", readonly=True, states={'draft':[('readonly',False)]}), 'type': fields.selection([ ('out_invoice','Customer Invoice'), ('in_invoice','Supplier Invoice'), @@ -206,15 +207,15 @@ class account_invoice(osv.osv): ('open','Open'), ('paid','Paid'), ('cancel','Cancelled'), - ],'State', select=True, readonly=True, - help=' * The \'Draft\' state is used when a user is encoding a new and unconfirmed Invoice. \ - \n* The \'Pro-forma\' when invoice is in Pro-forma state,invoice does not have an invoice number. \ - \n* The \'Open\' state is used when user create invoice,a invoice number is generated.Its in open state till user does not pay invoice. \ - \n* The \'Paid\' state is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled. \ - \n* The \'Cancelled\' state is used when user cancel invoice.'), + ],'Status', select=True, readonly=True, + help=' * The \'Draft\' status is used when a user is encoding a new and unconfirmed Invoice. \ + \n* The \'Pro-forma\' when invoice is in Pro-forma status,invoice does not have an invoice number. \ + \n* The \'Open\' status is used when user create invoice,a invoice number is generated.Its in open status till user does not pay invoice. \ + \n* The \'Paid\' status is set automatically when the invoice is paid. Its related journal entries may or may not be reconciled. \ + \n* The \'Cancelled\' status is used when user cancel invoice.'), 'sent': fields.boolean('Sent', readonly=True, help="It indicates that the invoice has been sent."), 'date_invoice': fields.date('Invoice Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="Keep empty to use the current date"), - 'date_due': fields.date('Due Date', states={'paid':[('readonly',True)], 'open':[('readonly',True)], 'close':[('readonly',True)]}, select=True, + 'date_due': fields.date('Due Date', readonly=True, states={'draft':[('readonly',False)]}, select=True, help="If you use payment terms, the due date will be computed automatically at the generation "\ "of accounting entries. If you keep the payment term and the due date empty, it means direct payment. The payment term may compute several due dates, for example 50% now, 50% in one month."), 'partner_id': fields.many2one('res.partner', 'Partner', change_default=True, readonly=True, required=True, states={'draft':[('readonly',False)]}), @@ -253,7 +254,7 @@ class account_invoice(osv.osv): 'currency_id': fields.many2one('res.currency', 'Currency', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'journal_id': fields.many2one('account.journal', 'Journal', required=True, readonly=True, states={'draft':[('readonly',False)]}), 'company_id': fields.many2one('res.company', 'Company', required=True, change_default=True, readonly=True, states={'draft':[('readonly',False)]}), - 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), states={'open':[('readonly',True)],'close':[('readonly',True)]}), + 'check_total': fields.float('Verification Total', digits_compute=dp.get_precision('Account'), readonly=True, states={'draft':[('readonly',False)]}), 'reconciled': fields.function(_reconciled, string='Paid/Reconciled', type='boolean', store={ 'account.invoice': (lambda self, cr, uid, ids, c={}: ids, None, 50), # Check if we can remove ? @@ -395,18 +396,24 @@ class account_invoice(osv.osv): template_id = template and template[1] or False res = mod_obj.get_object_reference(cr, uid, 'mail', 'email_compose_message_wizard_form') res_id = res and res[1] or False - ctx = dict(context, active_model='account.invoice', active_id=ids[0]) - ctx.update({'mail.compose.template_id': template_id}) + ctx = dict(context) + ctx.update({ + 'default_model': 'account.invoice', + 'default_res_id': ids[0], + 'default_use_template': True, + 'default_template_id': template_id, + 'default_composition_mode': 'comment', + }) return { - 'view_type': 'form', - 'view_mode': 'form', - 'res_model': 'mail.compose.message', - 'views': [(res_id, 'form')], - 'view_id': res_id, - 'type': 'ir.actions.act_window', - 'target': 'new', - 'context': ctx, - 'nodestroy': True, + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': 'mail.compose.message', + 'views': [(res_id, 'form')], + 'view_id': res_id, + 'type': 'ir.actions.act_window', + 'target': 'new', + 'context': ctx, + 'nodestroy': True, } def confirm_paid(self, cr, uid, ids, context=None): @@ -425,7 +432,7 @@ class account_invoice(osv.osv): if t['state'] in ('draft', 'cancel') and t['internal_number']== False: unlink_ids.append(t['id']) else: - raise osv.except_osv(_('Invalid Action!'), _('You cannot delete an invoice which is open or paid. You should refund it instead.')) + raise osv.except_osv(_('Invalid Action!'), _('You can not delete an invoice which is not cancelled. You should refund it instead.')) osv.osv.unlink(self, cr, uid, unlink_ids, context=context) return True @@ -503,8 +510,10 @@ class account_invoice(osv.osv): if journal_id: journal = self.pool.get('account.journal').browse(cr, uid, journal_id, context=context) currency_id = journal.currency and journal.currency.id or journal.company_id.currency_id.id + company_id = journal.company_id.id result = {'value': { 'currency_id': currency_id, + 'company_id': company_id, } } return result @@ -767,17 +776,20 @@ class account_invoice(osv.osv): if not key in tax_key: raise osv.except_osv(_('Warning!'), _('Taxes are missing!\nClick on compute button.')) - def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines): + def compute_invoice_totals(self, cr, uid, inv, company_currency, ref, invoice_move_lines, context=None): + if context is None: + context={} total = 0 total_currency = 0 cur_obj = self.pool.get('res.currency') for i in invoice_move_lines: if inv.currency_id.id != company_currency: + context.update({'date': inv.date_invoice or time.strftime('%Y-%m-%d')}) i['currency_id'] = inv.currency_id.id i['amount_currency'] = i['price'] i['price'] = cur_obj.compute(cr, uid, inv.currency_id.id, company_currency, i['price'], - context={'date': inv.date_invoice or time.strftime('%Y-%m-%d')}) + context=context) else: i['amount_currency'] = False i['currency_id'] = False @@ -854,8 +866,11 @@ class account_invoice(osv.osv): self.check_tax_lines(cr, uid, inv, compute_taxes, ait_obj) # I disabled the check_total feature - #if inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0): - # raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe real total does not match the computed total.')) + group_check_total_id = self.pool.get('ir.model.data').get_object_reference(cr, uid, 'account', 'group_supplier_inv_check_total')[1] + group_check_total = self.pool.get('res.groups').browse(cr, uid, group_check_total_id, context=context) + if group_check_total and uid in [x.id for x in group_check_total.users]: + if (inv.type in ('in_invoice', 'in_refund') and abs(inv.check_total - inv.amount_total) >= (inv.currency_id.rounding/2.0)): + raise osv.except_osv(_('Bad total !'), _('Please verify the price of the invoice !\nThe encoded total does not match the computed total.')) if inv.payment_term: total_fixed = total_percent = 0 @@ -887,7 +902,7 @@ class account_invoice(osv.osv): # create one move line for the total and possibly adjust the other lines amount total = 0 total_currency = 0 - total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml) + total, total_currency, iml = self.compute_invoice_totals(cr, uid, inv, company_currency, ref, iml, context=ctx) acc_id = inv.account_id.id name = inv['name'] or '/' @@ -969,13 +984,13 @@ class account_invoice(osv.osv): for i in line: i[2]['period_id'] = period_id + ctx.update(invoice=inv) move_id = move_obj.create(cr, uid, move, context=ctx) new_move_name = move_obj.browse(cr, uid, move_id, context=ctx).name # make the invoice point to that move self.write(cr, uid, [inv.id], {'move_id': move_id,'period_id':period_id, 'move_name':new_move_name}, context=ctx) # Pass invoice in context in method post: used if you want to get the same # account move reference when creating the same invoice after a cancelled one: - ctx.update({'invoice':inv}) move_obj.post(cr, uid, [move_id], context=ctx) self._log_event(cr, uid, ids) return True @@ -1045,7 +1060,7 @@ class account_invoice(osv.osv): if obj_inv.type in ('out_invoice', 'out_refund'): ctx = self.get_log_context(cr, uid, context=ctx) message = _("Invoice '%s' is validated.") % name - self.message_append_note(cr, uid, [inv_id], body=message, context=context) + self.message_post(cr, uid, [inv_id], body=message, context=context) return True def action_cancel(self, cr, uid, ids, *args): @@ -1095,10 +1110,10 @@ class account_invoice(osv.osv): if not ids: return [] types = { - 'out_invoice': 'CI: ', - 'in_invoice': 'SI: ', - 'out_refund': 'OR: ', - 'in_refund': 'SR: ', + 'out_invoice': 'Invoice ', + 'in_invoice': 'Sup. Invoice ', + 'out_refund': 'Refund ', + 'in_refund': 'Supplier Refund ', } return [(r['id'], (r['number']) or types[r['type']] + (r['name'] or '')) for r in self.read(cr, uid, ids, ['type', 'number', 'name'], context, load='_classic_write')] @@ -1127,7 +1142,7 @@ class account_invoice(osv.osv): return map(lambda x: (0,0,x), lines) def refund(self, cr, uid, ids, date=None, period_id=None, description=None, journal_id=None): - invoices = self.read(cr, uid, ids, ['name', 'type', 'number', 'reference', 'comment', 'date_due', 'partner_id', 'partner_contact', 'partner_insite', 'partner_ref', 'payment_term', 'account_id', 'currency_id', 'invoice_line', 'tax_line', 'journal_id']) + invoices = self.read(cr, uid, ids, ['name', 'type', 'number', 'reference', 'comment', 'date_due', 'partner_id', 'partner_contact', 'partner_insite', 'partner_ref', 'payment_term', 'account_id', 'currency_id', 'invoice_line', 'tax_line', 'journal_id', 'company_id']) obj_invoice_line = self.pool.get('account.invoice.line') obj_invoice_tax = self.pool.get('account.invoice.tax') obj_journal = self.pool.get('account.journal') @@ -1175,7 +1190,7 @@ class account_invoice(osv.osv): 'name': description, }) # take the id part of the tuple returned for many2one fields - for field in ('partner_id', + for field in ('partner_id', 'company_id', 'account_id', 'currency_id', 'payment_term', 'journal_id'): invoice[field] = invoice[field] and invoice[field][0] # create the new invoice @@ -1275,7 +1290,7 @@ class account_invoice(osv.osv): # TODO: use currency's formatting function msg = _("Invoice '%s' is paid partially: %s%s of %s%s (%s%s remaining).") % \ (name, pay_amount, code, invoice.amount_total, code, total, code) - self.message_append_note(cr, uid, [inv_id], body=msg, context=context) + self.message_post(cr, uid, [inv_id], body=msg, context=context) self.pool.get('account.move.line').reconcile_partial(cr, uid, line_ids, 'manual', context) # Update the stored value (fields.function), so we write to trigger recompute @@ -1288,26 +1303,29 @@ class account_invoice(osv.osv): def _get_document_type(self, type): type_dict = { - 'out_invoice': 'Customer invoice', - 'in_invoice': 'Supplier invoice', - 'out_refund': 'Customer Refund', - 'in_refund': 'Supplier Refund', + # Translation markers will have no effect at runtime, only used to properly flag export + 'out_invoice': _('Customer invoice'), + 'in_invoice': _('Supplier invoice'), + 'out_refund': _('Customer Refund'), + 'in_refund': _('Supplier Refund'), } return type_dict.get(type, 'Invoice') def create_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id],body=_("%s created.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s created.") % (self._get_document_type(obj.type)), + subtype="account.mt_invoice_new", context=context) def confirm_paid_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s paid.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s paid.") % (self._get_document_type(obj.type)), + subtype="account.mt_invoice_paid", context=context) def invoice_cancel_send_note(self, cr, uid, ids, context=None): for obj in self.browse(cr, uid, ids, context=context): - self.message_append_note(cr, uid, [obj.id], body=_("%s cancelled.") % (self._get_document_type(obj.type)), context=context) + self.message_post(cr, uid, [obj.id], body=_("%s cancelled.") % (self._get_document_type(obj.type)), + context=context) -account_invoice() class account_invoice_line(osv.osv): @@ -1346,7 +1364,8 @@ class account_invoice_line(osv.osv): _description = "Invoice Line" _columns = { 'name': fields.text('Description', required=True), - 'origin': fields.char('Source', size=256, help="Reference of the document that produced this invoice."), + 'origin': fields.char('Source Document', size=256, help="Reference of the document that produced this invoice."), + 'sequence': fields.integer('Sequence', help="Gives the sequence of this line when displaying the invoice."), 'invoice_id': fields.many2one('account.invoice', 'Invoice Reference', ondelete='cascade', select=True), 'uos_id': fields.many2one('product.uom', 'Unit of Measure', ondelete='set null'), 'product_id': fields.many2one('product.product', 'Product', ondelete='set null'), @@ -1361,10 +1380,19 @@ class account_invoice_line(osv.osv): 'company_id': fields.related('invoice_id','company_id',type='many2one',relation='res.company',string='Company', store=True, readonly=True), 'partner_id': fields.related('invoice_id','partner_id',type='many2one',relation='res.partner',string='Partner',store=True) } + + def _default_account_id(self, cr, uid, context=None): + # XXX this gets the default account for the user's company, + # it should get the default account for the invoice's company + # however, the invoice's company does not reach this point + prop = self.pool.get('ir.property').get(cr, uid, 'property_account_income_categ', 'product.category', context=context) + return prop and prop.id or False + _defaults = { 'quantity': 1, 'discount': 0.0, 'price_unit': _price_unit_default, + 'account_id': _default_account_id, } def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False): @@ -1386,7 +1414,7 @@ class account_invoice_line(osv.osv): context = {} company_id = company_id if company_id != None else context.get('company_id',False) context = dict(context) - context.update({'company_id': company_id}) + context.update({'company_id': company_id, 'force_company': company_id}) if not partner_id: raise osv.except_osv(_('No Partner Defined !'),_("You must first select a partner !") ) if not product: @@ -1473,10 +1501,11 @@ class account_invoice_line(osv.osv): prod = self.pool.get('product.product').browse(cr, uid, product, context=context) prod_uom = self.pool.get('product.uom').browse(cr, uid, uom, context=context) if prod.uom_id.category_id.id != prod_uom.category_id.id: - warning = { + warning = { 'title': _('Warning!'), 'message': _('The selected unit of measure is not compatible with the unit of measure of the product.') - } + } + res['value'].update({'uos_id': prod.uom_id.id}) return {'value': res['value'], 'warning': warning} return res @@ -1540,16 +1569,19 @@ class account_invoice_line(osv.osv): def onchange_account_id(self, cr, uid, ids, product_id, partner_id, inv_type, fposition_id, account_id): if not account_id: return {} - taxes = self.pool.get('account.account').browse(cr, uid, account_id).tax_ids + unique_tax_ids = [] fpos = fposition_id and self.pool.get('account.fiscal.position').browse(cr, uid, fposition_id) or False - tax_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes) - - product_change_result = self.product_id_change(cr, uid, ids, product_id, False, type=inv_type, - partner_id=partner_id, fposition_id=fposition_id) - unique_tax_ids = set(tax_ids) - if product_change_result and 'value' in product_change_result and 'invoice_line_tax_id' in product_change_result['value']: - unique_tax_ids |= set(product_change_result['value']['invoice_line_tax_id']) - return {'value':{'invoice_line_tax_id': list(unique_tax_ids)}} + account = self.pool.get('account.account').browse(cr, uid, account_id) + if not product_id: + taxes = account.tax_ids + unique_tax_ids = self.pool.get('account.fiscal.position').map_tax(cr, uid, fpos, taxes) + else: + product_change_result = self.product_id_change(cr, uid, ids, product_id, False, type=inv_type, + partner_id=partner_id, fposition_id=fposition_id, + company_id=account.company_id.id) + if product_change_result and 'value' in product_change_result and 'invoice_line_tax_id' in product_change_result['value']: + unique_tax_ids = product_change_result['value']['invoice_line_tax_id'] + return {'value':{'invoice_line_tax_id': unique_tax_ids}} account_invoice_line() @@ -1634,14 +1666,13 @@ class account_invoice_tax(osv.osv): for line in inv.invoice_line: for tax in tax_obj.compute_all(cr, uid, line.invoice_line_tax_id, (line.price_unit* (1-(line.discount or 0.0)/100.0)), line.quantity, line.product_id, inv.partner_id)['taxes']: - tax['price_unit'] = cur_obj.round(cr, uid, cur, tax['price_unit']) val={} val['invoice_id'] = inv.id val['name'] = tax['name'] val['amount'] = tax['amount'] val['manual'] = False val['sequence'] = tax['sequence'] - val['base'] = tax['price_unit'] * line['quantity'] + val['base'] = cur_obj.round(cr, uid, cur, tax['price_unit'] * line['quantity']) if inv.type in ('out_invoice','in_invoice'): val['base_code_id'] = tax['base_code_id'] diff --git a/addons/account/account_invoice_view.xml b/addons/account/account_invoice_view.xml index 143c5514141..d3697b13ea9 100644 --- a/addons/account/account_invoice_view.xml +++ b/addons/account/account_invoice_view.xml @@ -127,7 +127,7 @@ - + @@ -143,13 +143,11 @@
- -
@@ -171,11 +169,12 @@ domain="[('supplier', '=', True)]"/> -