diff --git a/addons/account/views/account_view.xml b/addons/account/views/account_view.xml index 870a7b81266..3605c4ebc89 100644 --- a/addons/account/views/account_view.xml +++ b/addons/account/views/account_view.xml @@ -1267,7 +1267,7 @@ - + diff --git a/addons/auth_oauth/res_users.py b/addons/auth_oauth/res_users.py index 7b3e1a3a0d5..1c9eb492b06 100644 --- a/addons/auth_oauth/res_users.py +++ b/addons/auth_oauth/res_users.py @@ -38,11 +38,11 @@ class res_users(osv.Model): def _auth_oauth_validate(self, cr, uid, provider, access_token, context=None): """ return the validation data corresponding to the access token """ p = self.pool.get('auth.oauth.provider').browse(cr, uid, provider, context=context) - validation = self._auth_oauth_rpc(cr, uid, p.validation_endpoint, access_token) + validation = self._auth_oauth_rpc(cr, uid, p.validation_endpoint, access_token, context=context) if validation.get("error"): raise Exception(validation['error']) if p.data_endpoint: - data = self._auth_oauth_rpc(cr, uid, p.data_endpoint, access_token) + data = self._auth_oauth_rpc(cr, uid, p.data_endpoint, access_token, context=context) validation.update(data) return validation @@ -98,7 +98,7 @@ class res_users(osv.Model): # else: # continue with the process access_token = params.get('access_token') - validation = self._auth_oauth_validate(cr, uid, provider, access_token) + validation = self._auth_oauth_validate(cr, uid, provider, access_token, context=context) # required check if not validation.get('user_id'): # Workaround: facebook does not send 'user_id' in Open Graph Api diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 4e7d853194d..63e0032b006 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -798,6 +798,11 @@ class calendar_event(osv.Model): meeting_data[field] = meeting.stop_date if meeting.allday else meeting.stop_datetime return res + def _get_recurrent_fields(self, cr, uid, context=None): + return ['byday', 'recurrency', 'final_date', 'rrule_type', 'month_by', + 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', + 'su', 'day', 'week_list'] + def _get_rulestring(self, cr, uid, ids, name, arg, context=None): """ Gets Recurrence rule string according to value type RECUR of iCalendar from the values given. @@ -808,10 +813,8 @@ class calendar_event(osv.Model): ids = [ids] #read these fields as SUPERUSER because if the record is private a normal search could raise an error - events = self.read(cr, SUPERUSER_ID, ids, - ['id', 'byday', 'recurrency', 'final_date', 'rrule_type', 'month_by', - 'interval', 'count', 'end_type', 'mo', 'tu', 'we', 'th', 'fr', 'sa', - 'su', 'day', 'week_list'], context=context) + recurrent_fields = self._get_recurrent_fields(cr, uid, context=context) + events = self.read(cr, SUPERUSER_ID, ids, recurrent_fields, context=context) for event in events: if event['recurrency']: result[event['id']] = self.compute_rule_string(event) diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py index 81d1bb82f5f..33acda6b82d 100644 --- a/addons/google_calendar/google_calendar.py +++ b/addons/google_calendar/google_calendar.py @@ -971,9 +971,10 @@ class calendar_event(osv.Model): _inherit = "calendar.event" def get_fields_need_update_google(self, cr, uid, context=None): - return ['name', 'description', 'allday', 'start', 'date_end', 'stop', - 'attendee_ids', 'alarm_ids', 'location', 'class', 'active', - 'start_date', 'start_datetime', 'stop_date', 'stop_datetime'] + recurrent_fields = self._get_recurrent_fields(cr, uid, context=context) + return recurrent_fields + ['name', 'description', 'allday', 'start', 'date_end', 'stop', + 'attendee_ids', 'alarm_ids', 'location', 'class', 'active', + 'start_date', 'start_datetime', 'stop_date', 'stop_datetime'] def write(self, cr, uid, ids, vals, context=None): if context is None: diff --git a/addons/mail/wizard/invite.py b/addons/mail/wizard/invite.py index a45a055b236..bb6e8e9aeb4 100644 --- a/addons/mail/wizard/invite.py +++ b/addons/mail/wizard/invite.py @@ -19,7 +19,7 @@ class Invite(models.TransientModel): if 'message' in fields and model and res_id: model_name = self.env['ir.model'].search([('model', '=', self.pool[model]._name)]).name_get()[0][1] document_name = self.env[model].browse(res_id).name_get()[0][1] - message = _('

Hello,

%s invited you to follow %s document: %s.

') % (user_name, model_name, document_name) + message = _('

Hello,

%s invited you to follow %s document: %s.

') % (user_name, model_name, document_name) result['message'] = message elif 'message' in fields: result['message'] = _('

Hello,

%s invited you to follow a new document.

') % user_name diff --git a/addons/point_of_sale/README.md b/addons/point_of_sale/README.md index 7bdc1cf7e8f..d45c48a84ff 100644 --- a/addons/point_of_sale/README.md +++ b/addons/point_of_sale/README.md @@ -87,15 +87,6 @@ your store's efficiency. No more hassle of having to integrate softwares: get all your sales and inventory operations automatically posted in your G/L. -Self-checkout Interface ------------------------ - -Reduce costs by limitting the number of required cashiers. The self-checkout -mode allows customers to scan products and pay by themselves with a dedicated -and super easy user interface. - -Use lights and sounds to control the checkout distantly. - Unified Data Amongst All Shops ------------------------------ diff --git a/addons/point_of_sale/static/description/index.html b/addons/point_of_sale/static/description/index.html index aec1bb1b324..a49b14a876b 100644 --- a/addons/point_of_sale/static/description/index.html +++ b/addons/point_of_sale/static/description/index.html @@ -176,26 +176,6 @@ -
-
-

Self-checkout Interface

-
-

- Reduce costs by limitting the number of required cashiers. - The self-checkout mode allows customers to scan products and pay by - themselves with a dedicated and super easy user interface. -

-

- Use lights and sounds to control the checkout distantly. -

-
-
- -
-
-
- -

Unified Data Amongst All Shops

diff --git a/addons/point_of_sale/static/description/pos_checkout.jpg b/addons/point_of_sale/static/description/pos_checkout.jpg deleted file mode 100644 index d87e5b8499d..00000000000 Binary files a/addons/point_of_sale/static/description/pos_checkout.jpg and /dev/null differ diff --git a/addons/procurement/procurement.py b/addons/procurement/procurement.py index 1459955acf2..e3ff3e49e89 100644 --- a/addons/procurement/procurement.py +++ b/addons/procurement/procurement.py @@ -90,7 +90,6 @@ class procurement_order(osv.osv): _description = "Procurement" _order = 'priority desc, date_planned, id asc' _inherit = ['mail.thread','ir.needaction_mixin'] - _log_create = False _columns = { 'name': fields.text('Description', required=True), diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 7ba0b01f663..fa568cc2f48 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -182,7 +182,7 @@ class PurchaseOrder(models.Model): @api.multi def unlink(self): for order in self: - if order.state not in ['draft', 'cancel']: + if not order.state == 'cancel': raise UserError(_('In order to delete a purchase order, you must cancel it first.')) return super(PurchaseOrder, self).unlink() diff --git a/addons/purchase/test/ui/delete_order.yml b/addons/purchase/test/ui/delete_order.yml index 6f9ba54e89d..9cde313ac1c 100644 --- a/addons/purchase/test/ui/delete_order.yml +++ b/addons/purchase/test/ui/delete_order.yml @@ -13,15 +13,19 @@ self.unlink(cr, uid, [ref("purchase_order_1")]) except Exception,e: pass +- + I delete a cancelled order. +- + !python {model: purchase.order}: | + from openerp.exceptions import UserError + try: + self.unlink(cr, uid, [ref("purchase_order_7")]) + except UserError: + pass - I delete a draft order. - !python {model: purchase.order}: | self.unlink(cr, uid, [ref("purchase_order_5")]) -- - I delete a cancelled order. -- - !python {model: purchase.order}: | - self.unlink(cr, uid, [ref("purchase_order_7")]) diff --git a/addons/stock/product.py b/addons/stock/product.py index c2461d3e8c0..edf63baa840 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -151,7 +151,9 @@ class product_product(osv.osv): moves_in = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_in)) moves_out = dict(map(lambda x: (x['product_id'][0], x['product_qty']), moves_out)) res = {} - for product in self.browse(cr, uid, ids, context=context): + ctx = context.copy() + ctx.update({'prefetch_fields': False}) + for product in self.browse(cr, uid, ids, context=ctx): id = product.id qty_available = float_round(quants.get(id, 0.0), precision_rounding=product.uom_id.rounding) incoming_qty = float_round(moves_in.get(id, 0.0), precision_rounding=product.uom_id.rounding) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 999bbcb3a82..7acb847d79d 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -1764,7 +1764,6 @@ class stock_move(osv.osv): _name = "stock.move" _description = "Stock Move" _order = 'picking_id, sequence, id' - _log_create = False def get_price_unit(self, cr, uid, move, context=None): """ Returns the unit price to store on the quant """ diff --git a/addons/web_editor/models/ir_translation.py b/addons/web_editor/models/ir_translation.py index d54591908bd..1dedb26d1bf 100644 --- a/addons/web_editor/models/ir_translation.py +++ b/addons/web_editor/models/ir_translation.py @@ -3,6 +3,10 @@ from openerp import models, api +def edit_translation_mapping(data): + data = dict(data, model=data['name'].partition(',')[0]) + return '%(value)s' % data + class ir_translation(models.Model): _inherit = 'ir.translation' @@ -10,5 +14,5 @@ class ir_translation(models.Model): def _get_terms_mapping(self, field, records): if self._context.get('edit_translations'): self.insert_missing(field, records) - return lambda data: '%(value)s' % data + return edit_translation_mapping return super(ir_translation, self)._get_terms_mapping(field, records) diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 0da99622cea..194fa73f9be 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -478,7 +478,8 @@ class website(osv.osv): :rtype: bool """ endpoint = rule.endpoint - methods = rule.methods or ['GET'] + methods = endpoint.routing.get('method') or ['GET'] + converters = rule._converters.values() if not ('GET' in methods and endpoint.routing['type'] == 'http' diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index a6c1ccc5c21..5d01e05759e 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -5,7 +5,7 @@ var base = require('web_editor.base'); var core = require('web.core'); var _t = core._t; -var shopping_cart_link = $('ul#top_menu li a[href^="/shop/cart"]'); +var shopping_cart_link = $('ul#top_menu li a[href$="/shop/cart"]'); var shopping_cart_link_counter; shopping_cart_link.popover({ trigger: 'manual', diff --git a/openerp/api.py b/openerp/api.py index d252abf212b..d5596550ceb 100644 --- a/openerp/api.py +++ b/openerp/api.py @@ -140,6 +140,12 @@ def constrains(*args): Should raise :class:`~openerp.exceptions.ValidationError` if the validation failed. + + .. warning:: + + ``@constrains`` only supports simple field names, dotted names + (fields of relational fields e.g. ``partner_id.customer``) are not + supported and will be ignored """ return lambda method: decorate(method, '_constrains', args) @@ -165,6 +171,12 @@ def onchange(*args): 'warning': {'title': "Warning", 'message': "What is this?"}, } + + .. warning:: + + ``@onchange`` only supports simple field names, dotted names + (fields of relational fields e.g. ``partner_id.tz``) are not + supported and will be ignored """ return lambda method: decorate(method, '_onchange', args) diff --git a/openerp/models.py b/openerp/models.py index 9b144be4438..d143b311add 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -342,7 +342,6 @@ class BaseModel(object): _inherit_fields = {} _table = None - _log_create = False _sql_constraints = [] # model dependencies, for models backed up by sql views: @@ -4358,13 +4357,6 @@ class BaseModel(object): # recompute new-style fields recs.recompute() - if self._log_create and recs.env.recompute and context.get('recompute', True): - message = self._description + \ - " '" + \ - self.name_get(cr, user, [id_new], context=context)[0][1] + \ - "' " + _("created.") - self.log(cr, user, id_new, message, True, context=context) - self.check_access_rule(cr, user, [id_new], 'create', context=context) self.create_workflow(cr, user, [id_new], context=context) return id_new @@ -4611,11 +4603,24 @@ class BaseModel(object): """ lang = self._context.get('lang') if lang and lang != 'en_US': + # Sub-select to return at most one translation per record. + # Even if it shoud probably not be the case, + # this is possible to have multiple translations for a same record in the same language. + # The parenthesis surrounding the select are important, as this is a sub-select. + # The quotes surrounding `ir_translation` are important as well. + unique_translation_subselect = """ + (SELECT DISTINCT ON (res_id) res_id, value + FROM "ir_translation" + WHERE + name = %s AND + lang = %s AND + value != %s + ORDER BY res_id, id DESC) + """ alias, alias_statement = query.add_join( - (table_alias, 'ir_translation', 'id', 'res_id', field), + (table_alias, unique_translation_subselect, 'id', 'res_id', field), implicit=False, outer=True, - extra='"{rhs}"."name" = %s AND "{rhs}"."lang" = %s AND "{rhs}"."value" != %s', extra_params=["%s,%s" % (self._name, field), lang, ""], ) return 'COALESCE("%s"."%s", "%s"."%s")' % (alias, 'value', table_alias, field) diff --git a/openerp/osv/query.py b/openerp/osv/query.py index 3fbdc476d4e..5fe53cfdf87 100644 --- a/openerp/osv/query.py +++ b/openerp/osv/query.py @@ -132,8 +132,8 @@ class Query(object): self.tables.append(alias_statement) join_tuple = (alias, lhs_col, col, outer and 'LEFT JOIN' or 'JOIN') self.joins.setdefault(lhs, []).append(join_tuple) - if extra: - extra = extra.format(lhs=lhs, rhs=alias) + if extra or extra_params: + extra = (extra or '').format(lhs=lhs, rhs=alias) self.extras[(lhs, join_tuple)] = (extra, extra_params) return alias, alias_statement @@ -152,9 +152,11 @@ class Query(object): (join, alias_mapping[rhs], lhs, lhs_col, rhs, rhs_col)) extra = self.extras.get((lhs, (rhs, lhs_col, rhs_col, join))) if extra: - from_clause.append(' AND ') - from_clause.append(extra[0]) - from_params.extend(extra[1]) + if extra[0]: + from_clause.append(' AND ') + from_clause.append(extra[0]) + if extra[1]: + from_params.extend(extra[1]) from_clause.append(')') add_joins_for_table(rhs)