diff --git a/.gitignore b/.gitignore index 7828a605ef0..68382f297f6 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,9 @@ _build/ status # odoo filestore openerp/filestore +# maintenance migration scripts +openerp/addons/base/maintenance + # generated for windows installer? install/win32/*.bat install/win32/meta.py diff --git a/addons/account/account_invoice.py b/addons/account/account_invoice.py index 79fa7a4d848..70d644d6519 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -857,7 +857,7 @@ class account_invoice(osv.osv): for tax in inv.tax_line: if tax.manual: continue - key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id, tax.account_analytic_id.id) + key = (tax.tax_code_id.id, tax.base_code_id.id, tax.account_id.id) tax_key.append(key) if not key in compute_taxes: raise osv.except_osv(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !')) @@ -1770,7 +1770,15 @@ class account_invoice_tax(osv.osv): val['account_id'] = tax['account_paid_id'] or line.account_id.id val['account_analytic_id'] = tax['account_analytic_paid_id'] - key = (val['tax_code_id'], val['base_code_id'], val['account_id'], val['account_analytic_id']) + # If the taxes generate moves on the same financial account as the invoice line + # and no default analytic account is defined at the tax level, propagate the + # analytic account from the invoice line to the tax line. This is necessary + # in situations were (part of) the taxes cannot be reclaimed, + # to ensure the tax move is allocated to the proper analytic account. + if not val.get('account_analytic_id') and line.account_analytic_id and val['account_id'] == line.account_id.id: + val['account_analytic_id'] = line.account_analytic_id.id + + key = (val['tax_code_id'], val['base_code_id'], val['account_id']) if not key in tax_grouped: tax_grouped[key] = val else: diff --git a/addons/base_action_rule/base_action_rule.py b/addons/base_action_rule/base_action_rule.py index e74e7193b62..91433b2bf4c 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -219,8 +219,8 @@ class base_action_rule(osv.osv): ids = self.search(cr, SUPERUSER_ID, []) for action_rule in self.browse(cr, SUPERUSER_ID, ids): model = action_rule.model_id.model - model_obj = self.pool[model] - if not hasattr(model_obj, 'base_action_ruled'): + model_obj = self.pool.get(model) + if model_obj and not hasattr(model_obj, 'base_action_ruled'): model_obj.create = self._wrap_create(model_obj.create, model) model_obj.write = self._wrap_write(model_obj.write, model) model_obj.base_action_ruled = True diff --git a/addons/product/product.py b/addons/product/product.py index 0c08dbb221b..2110f243a70 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -821,6 +821,7 @@ class product_product(osv.osv): price_type_currency_id = pricetype_obj.browse(cr,uid,price_type_id).currency_id.id res = {} + company_id = self.pool['res.users'].read(cr, uid, uid, ['company_id'], context=context)['company_id'][0] # standard_price field can only be seen by users in base.group_user # Thus, in order to compute the sale price from the cost price for users not in this group # We fetch the standard price as the superuser @@ -828,7 +829,7 @@ class product_product(osv.osv): if ptype != 'standard_price': res[product.id] = product[ptype] or 0.0 else: - res[product.id] = self.read(cr, SUPERUSER_ID, product.id, [ptype], context=context)[ptype] or 0.0 + res[product.id] = self.read(cr, SUPERUSER_ID, product.id, [ptype], context=dict(context, force_company=company_id))[ptype] or 0.0 product_uom_obj = self.pool.get('product.uom') for product in products: diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index c3fa1eb01db..691a9fd83fc 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -287,7 +287,7 @@ class project_issue(osv.Model): 'progress': fields.function(_hours_get, string='Progress (%)', multi='hours', group_operator="avg", help="Computed as: Time Spent / Total Time.", store = { 'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['task_id'], 10), - 'project.task': (_get_issue_task, ['progress'], 10), + 'project.task': (_get_issue_task, ['work_ids', 'remaining_hours', 'planned_hours', 'state', 'stage_id'], 10), 'project.task.work': (_get_issue_work, ['hours'], 10), }), } diff --git a/addons/stock/stock.py b/addons/stock/stock.py index ee9437aafcf..e075c172b03 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -721,8 +721,8 @@ class stock_picking(osv.osv): if ('name' not in default) or (picking_obj.name == '/'): seq_obj_name = 'stock.picking.' + picking_obj.type default['name'] = self.pool.get('ir.sequence').get(cr, uid, seq_obj_name) - default['origin'] = '' - default['backorder_id'] = False + default.setdefault('origin', False) + default.setdefault('backorder_id', False) if 'invoice_state' not in default and picking_obj.invoice_state == 'invoiced': default['invoice_state'] = '2binvoiced' res = super(stock_picking, self).copy(cr, uid, id, default, context) @@ -1241,7 +1241,7 @@ class stock_picking(osv.osv): partial_data = partial_datas.get('move%s'%(move.id), {}) product_qty = partial_data.get('product_qty',0.0) move_product_qty[move.id] = product_qty - product_uom = partial_data.get('product_uom',False) + product_uom = partial_data.get('product_uom', move.product_uom.id) product_price = partial_data.get('product_price',0.0) product_currency = partial_data.get('product_currency',False) prodlot_id = partial_data.get('prodlot_id') diff --git a/addons/web/static/lib/cleditor/jquery.cleditor.js b/addons/web/static/lib/cleditor/jquery.cleditor.js index cf98288d6c6..5a30d29a191 100644 --- a/addons/web/static/lib/cleditor/jquery.cleditor.js +++ b/addons/web/static/lib/cleditor/jquery.cleditor.js @@ -366,7 +366,6 @@ // change - shortcut for .bind("change", handler) or .trigger("change") fn.change = function change(handler) { - console.log('change test'); var $this = $(this); return handler ? $this.bind(CHANGE, handler) : $this.trigger(CHANGE); }; diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 96c821abb11..db75e1ef352 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -4105,28 +4105,23 @@ instance.web.form.One2ManyListView = instance.web.ListView.extend({ this.o2m.trigger_on_change(); }, is_valid: function () { - var editor = this.editor; - var form = editor.form; - // If no edition is pending, the listview can not be invalid (?) - if (!editor.record) { - return true; - } - // If the form has not been modified, the view can only be valid - // NB: is_dirty will also be set on defaults/onchanges/whatever? - // oe_form_dirty seems to only be set on actual user actions - if (!form.$el.is('.oe_form_dirty')) { + var self = this; + if (!this.editable()){ return true; } this.o2m._dirty_flag = true; - - // Otherwise validate internal form - return _(form.fields).chain() - .invoke(function () { - this._check_css_flags(); - return this.is_valid(); - }) - .all(_.identity) - .value(); + var r; + return _.every(this.records.records, function(record){ + r = record; + _.each(self.editor.form.fields, function(field){ + field.set_value(r.attributes[field.name]); + }); + return _.every(self.editor.form.fields, function(field){ + field.process_modifiers(); + field._check_css_flags(); + return field.is_valid(); + }); + }); }, do_add_record: function () { if (this.editable()) {