diff --git a/.gitignore b/.gitignore index 4c3277c5215..2f01ce29ff4 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 f7d372e2750..31015ebd41c 100644 --- a/addons/account/account_invoice.py +++ b/addons/account/account_invoice.py @@ -720,7 +720,7 @@ class account_invoice(models.Model): for tax in self.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 key not in compute_taxes: raise except_orm(_('Warning!'), _('Global taxes defined, but they are not in invoice lines !')) @@ -1560,7 +1560,15 @@ class account_invoice_tax(models.Model): 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 864d283d89f..ac95da93534 100644 --- a/addons/base_action_rule/base_action_rule.py +++ b/addons/base_action_rule/base_action_rule.py @@ -230,8 +230,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'): # monkey-patch methods create and write model_obj._patch_method('create', make_create()) model_obj._patch_method('write', make_write()) diff --git a/addons/gamification/wizard/grant_badge.py b/addons/gamification/wizard/grant_badge.py index 44f739fff4e..513a1526c8b 100644 --- a/addons/gamification/wizard/grant_badge.py +++ b/addons/gamification/wizard/grant_badge.py @@ -36,7 +36,6 @@ class grant_badge_wizard(osv.TransientModel): def action_grant_badge(self, cr, uid, ids, context=None): """Wizard action for sending a badge to a chosen user""" - badge_obj = self.pool.get('gamification.badge') badge_user_obj = self.pool.get('gamification.badge.user') for wiz in self.browse(cr, uid, ids, context=context): @@ -51,6 +50,6 @@ class grant_badge_wizard(osv.TransientModel): 'comment': wiz.comment, } badge_user = badge_user_obj.create(cr, uid, values, context=context) - result = badge_obj._send_badge(cr, uid, badge_user, context=context) + result = badge_user_obj._send_badge(cr, uid, badge_user, context=context) return result diff --git a/addons/marketing_campaign/marketing_campaign_view.xml b/addons/marketing_campaign/marketing_campaign_view.xml index 1ba5621ff0a..7d4b3d36618 100644 --- a/addons/marketing_campaign/marketing_campaign_view.xml +++ b/addons/marketing_campaign/marketing_campaign_view.xml @@ -315,14 +315,14 @@ - +
- + @@ -332,14 +332,14 @@ - + - + diff --git a/addons/payment_paypal/models/paypal.py b/addons/payment_paypal/models/paypal.py index c94622a690b..2bdaead2c5d 100644 --- a/addons/payment_paypal/models/paypal.py +++ b/addons/payment_paypal/models/paypal.py @@ -113,7 +113,7 @@ class AcquirerPaypal(osv.Model): paypal_tx_values.update({ 'cmd': '_xclick', 'business': acquirer.paypal_email_account, - 'item_name': tx_values['reference'], + 'item_name': '%s: %s' % (acquirer.company_id.name, tx_values['reference']), 'item_number': tx_values['reference'], 'amount': tx_values['amount'], 'currency_code': tx_values['currency'] and tx_values['currency'].name or '', diff --git a/addons/product/product.py b/addons/product/product.py index 0e38e80542c..52ae27ed945 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -590,7 +590,9 @@ class product_template(osv.osv): if ptype != 'standard_price': res[product.id] = product[ptype] or 0.0 else: - res[product.id] = product.sudo()[ptype] + company_id = self.pool['res.users'].read(cr, uid, uid, ['company_id'], context=context)[0] + product = product.with_context(force_company=company_id) + res[product.id] = res[product.id] = product.sudo()[ptype] if ptype == 'list_price': res[product.id] += product._name == "product.product" and product.price_extra or 0.0 if 'uom' in context: diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index 338eebf099d..15039912ce7 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -272,7 +272,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), }), } @@ -535,7 +535,7 @@ class project_project(osv.Model): if use_tasks and not use_issues: values['alias_model'] = 'project.task' elif not use_tasks and use_issues: - values['alias_model'] = 'project.issues' + values['alias_model'] = 'project.issue' return {'value': values} def create(self, cr, uid, vals, context=None): diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index 307edeca8ad..67a16a8019a 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -4389,28 +4389,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()) {