diff --git a/addons/account_analytic_default/account_analytic_default.py b/addons/account_analytic_default/account_analytic_default.py index 7fa23b1b0f6..5b38ad413c1 100644 --- a/addons/account_analytic_default/account_analytic_default.py +++ b/addons/account_analytic_default/account_analytic_default.py @@ -131,3 +131,17 @@ class product_template(osv.Model): # Remove context so it is not going to filter on product_id with active_id of template result['context'] = "{}" return result + + +class stock_move(osv.Model): + _inherit = 'stock.move' + + def _create_invoice_line_from_vals(self, cr, uid, move, invoice_line_vals, context=None): + # It will set the default analtyic account on the invoice line + partner_id = self.pool['account.invoice'].browse(cr, uid, invoice_line_vals.get('invoice_id'), context=context).partner_id.id + if 'account_analytic_id' not in invoice_line_vals or not invoice_line_vals.get('account_analytic_id'): + rec = self.pool['account.analytic.default'].account_get(cr, uid, move.product_id.id, partner_id, uid, time.strftime('%Y-%m-%d'), company_id=move.company_id.id, context=context) + if rec: + invoice_line_vals.update({'account_analytic_id': rec.analytic_id.id}) + res = super(stock_move, self)._create_invoice_line_from_vals(cr, uid, move, invoice_line_vals, context=context) + return res diff --git a/addons/analytic/__openerp__.py b/addons/analytic/__openerp__.py index 5618b4c6023..0d351ae520e 100644 --- a/addons/analytic/__openerp__.py +++ b/addons/analytic/__openerp__.py @@ -6,7 +6,7 @@ 'version': '1.1', 'website' : 'https://www.odoo.com/page/accounting', 'category': 'Hidden/Dependency', - 'depends' : ['base', 'decimal_precision', 'mail'], + 'depends' : ['base', 'decimal_precision', 'mail', 'report'], 'description': """ Module for defining analytic accounting object. =============================================== diff --git a/addons/auth_signup/res_users.py b/addons/auth_signup/res_users.py index 4b99259a8a9..92ebcb093b0 100644 --- a/addons/auth_signup/res_users.py +++ b/addons/auth_signup/res_users.py @@ -265,8 +265,7 @@ class res_users(osv.Model): res_partner.signup_prepare(cr, uid, partner_ids, signup_type="reset", expiration=expiration, context=context) - if not context: - context = {} + context = dict(context or {}) # send email to users with their signup url template = False @@ -283,6 +282,7 @@ class res_users(osv.Model): for user in self.browse(cr, uid, ids, context): if not user.email: raise UserError(_("Cannot send email: user %s has no email address.") % user.name) + context['lang'] = user.lang self.pool.get('mail.template').send_mail(cr, uid, template.id, user.id, force_send=True, raise_exception=True, context=context) def create(self, cr, uid, values, context=None): diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py index 9c014d971c6..f83030eecf6 100644 --- a/addons/crm/crm_lead.py +++ b/addons/crm/crm_lead.py @@ -290,7 +290,15 @@ class crm_lead(format_address, osv.osv): def on_change_user(self, cr, uid, ids, user_id, context=None): """ When changing the user, also set a team_id or restrict team id to the ones user_id is member of. """ + if user_id and context.get('team_id'): + team = self.pool['crm.team'].browse(cr, uid, context['team_id'], context=context) + if user_id in team.member_ids.ids: + return {} team_id = self.pool['crm.team']._get_default_team_id(cr, uid, context=context, user_id=user_id) + if user_id and not team_id and self.pool['res.users'].has_group(cr, uid, 'base.group_multi_salesteams'): + team_ids = self.pool.get('crm.team').search(cr, uid, ['|', ('user_id', '=', user_id), ('member_ids', '=', user_id)], context=context) + if team_ids: + team_id = team_ids[0] return {'value': {'team_id': team_id}} def stage_find(self, cr, uid, cases, team_id, domain=None, order='sequence', context=None): diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml index 427cb54533b..2dacfa3baa1 100644 --- a/addons/crm/crm_lead_view.xml +++ b/addons/crm/crm_lead_view.xml @@ -158,7 +158,7 @@ + context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'base.group_sale_salesman_all_leads'], 'team_id': team_id}"/> @@ -496,7 +496,7 @@ - + diff --git a/addons/l10n_eu_service/wizard/wizard.xml b/addons/l10n_eu_service/wizard/wizard.xml index 9ceac6978db..04f604f0462 100644 --- a/addons/l10n_eu_service/wizard/wizard.xml +++ b/addons/l10n_eu_service/wizard/wizard.xml @@ -55,7 +55,6 @@ automatic - open diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index 3f892bb9b53..7eea6cb2725 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -6,6 +6,7 @@ var PosDB = require('point_of_sale.DB'); var devices = require('point_of_sale.devices'); var core = require('web.core'); var Model = require('web.DataModel'); +var formats = require('web.formats'); var session = require('web.session'); var time = require('web.time'); var utils = require('web.utils'); @@ -1070,8 +1071,7 @@ exports.Orderline = Backbone.Model.extend({ } this.product = options.product; this.price = options.product.price; - this.quantity = 1; - this.quantityStr = '1'; + this.set_quantity(1); this.discount = 0; this.discountStr = '0'; this.type = 'unit'; @@ -1134,7 +1134,8 @@ exports.Orderline = Backbone.Model.extend({ if(unit){ if (unit.rounding) { this.quantity = round_pr(quant, unit.rounding); - this.quantityStr = this.quantity.toFixed(Math.ceil(Math.log(1.0 / unit.rounding) / Math.log(10))); + var decimals = Math.ceil(Math.log(1.0 / unit.rounding) / Math.log(10)); + this.quantityStr = formats.format_value(round_di(this.quantity, decimals), { type: 'float', digits: [69, decimals]}); } else { this.quantity = round_pr(quant, 1); this.quantityStr = this.quantity.toFixed(0); diff --git a/addons/product/product.py b/addons/product/product.py index d07184ef5dd..465f83d342f 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -894,7 +894,12 @@ class product_product(osv.osv): def _get_image_variant(self, cr, uid, ids, name, args, context=None): result = dict.fromkeys(ids, False) for obj in self.browse(cr, uid, ids, context=context): - result[obj.id] = obj.image_variant or getattr(obj.product_tmpl_id, name) + if context.get('bin_size'): + result[obj.id] = obj.image_variant + else: + result[obj.id] = tools.image_get_resized_images(obj.image_variant, return_big=True, avoid_resize_medium=True)[name] + if not result[obj.id]: + result[obj.id] = getattr(obj.product_tmpl_id, name) return result def _set_image_variant(self, cr, uid, id, name, value, args, context=None): diff --git a/addons/sale_layout/models/sale_layout.py b/addons/sale_layout/models/sale_layout.py index 7d282c15ce2..a0867007fa8 100644 --- a/addons/sale_layout/models/sale_layout.py +++ b/addons/sale_layout/models/sale_layout.py @@ -67,10 +67,6 @@ class AccountInvoiceLine(osv.Model): sale_layout_cat_id = openerp.fields.Many2one('sale_layout.category', string='Section') categ_sequence = openerp.fields.Integer(related='sale_layout_cat_id.sequence', string='Layout Sequence', store=True) - _defaults = { - 'categ_sequence': 0 - } - class SaleOrder(osv.Model): _inherit = 'sale.order' @@ -100,10 +96,6 @@ class SaleOrderLine(osv.Model): # Store is intentionally set in order to keep the "historic" order. } - _defaults = { - 'categ_sequence': 0 - } - _order = 'order_id, categ_sequence, sale_layout_cat_id, sequence, id' def _prepare_order_line_invoice_line(self, cr, uid, line, account_id=False, context=None): diff --git a/addons/web_view_editor/static/src/js/view_editor.js b/addons/web_view_editor/static/src/js/view_editor.js index 29d4b642920..350a02441f6 100644 --- a/addons/web_view_editor/static/src/js/view_editor.js +++ b/addons/web_view_editor/static/src/js/view_editor.js @@ -1032,7 +1032,7 @@ var ViewEditor = Widget.extend({ render_new_field :function( result ) { var self = this; var action = { - context: {'default_model_id': result.id, 'manual': true, 'module' : result.model}, + context: {'default_model_id': result.id, 'module' : result.model}, res_model: "ir.model.fields", views: [[false, 'form']], type: 'ir.actions.act_window', diff --git a/addons/website/controllers/main.py b/addons/website/controllers/main.py index c274a14ac8d..182d05c3c51 100644 --- a/addons/website/controllers/main.py +++ b/addons/website/controllers/main.py @@ -38,9 +38,9 @@ class Website(openerp.addons.web.controllers.main.Home): else: first_menu = main_menu.child_id and main_menu.child_id[0] if first_menu: - if not (first_menu.url.startswith(('/page/', '/?', '/#')) or (first_menu.url=='/')): + if first_menu.url and (not (first_menu.url.startswith(('/page/', '/?', '/#')) or (first_menu.url == '/'))): return request.redirect(first_menu.url) - if first_menu.url.startswith('/page/'): + if first_menu.url and first_menu.url.startswith('/page/'): return request.registry['ir.http'].reroute(first_menu.url) return self.page(page) diff --git a/addons/website_forum/controllers/main.py b/addons/website_forum/controllers/main.py index 4dc59d118a3..1f982328db6 100644 --- a/addons/website_forum/controllers/main.py +++ b/addons/website_forum/controllers/main.py @@ -384,7 +384,7 @@ class WebsiteForum(http.Controller): def post_save(self, forum, post, **kwargs): if 'post_name' in kwargs and not kwargs.get('post_name').strip(): return request.website.render('website.http_error', {'status_code': _('Bad Request'), 'status_message': _('Title should not be empty.')}) - post_tags = forum._tag_to_write_vals(kwargs.get('post_tag', '')) + post_tags = forum._tag_to_write_vals(kwargs.get('post_tags', '')) vals = { 'tag_ids': post_tags, 'name': kwargs.get('post_name'), diff --git a/addons/website_quote/static/src/js/website_quotation.js b/addons/website_quote/static/src/js/website_quotation.js index 2539dc79365..f4e17c92bed 100644 --- a/addons/website_quote/static/src/js/website_quotation.js +++ b/addons/website_quote/static/src/js/website_quotation.js @@ -53,7 +53,7 @@ if(!$('.o_website_quote').length) { 'submit #accept': 'submitForm', }, initSignature: function(ev){ - this.$("#signature").empty().jSignature({'decor-color' : '#D1D0CE'}); + this.$("#signature").empty().jSignature({'decor-color' : '#D1D0CE', 'color': '#000', 'background-color': '#fff'}); this.empty_sign = this.$("#signature").jSignature("getData",'image'); }, clearSignature: function(ev){ diff --git a/addons/website_sale/static/src/js/website_sale.js b/addons/website_sale/static/src/js/website_sale.js index 676ccba1e2f..c316f69d53c 100644 --- a/addons/website_sale/static/src/js/website_sale.js +++ b/addons/website_sale/static/src/js/website_sale.js @@ -146,9 +146,9 @@ $('.oe_website_sale').each(function () { clickwatch(function(){ $dom_optional.each(function(){ + $(this).find('.js_quantity').text(value); product_ids.push($(this).find('span[data-product-id]').data('product-id')); }); - if (isNaN(value)) value = 0; $input.data('update_change', true); ajax.jsonRpc("/shop/cart/update_json", 'call', { diff --git a/addons/website_sale_stock/views/website_sale_stock.xml b/addons/website_sale_stock/views/website_sale_stock.xml index b624c72bf72..32a1729123b 100644 --- a/addons/website_sale_stock/views/website_sale_stock.xml +++ b/addons/website_sale_stock/views/website_sale_stock.xml @@ -31,4 +31,9 @@ + \ No newline at end of file diff --git a/doc/cla/corporate/invitu.md b/doc/cla/corporate/invitu.md new file mode 100644 index 00000000000..3b15373c298 --- /dev/null +++ b/doc/cla/corporate/invitu.md @@ -0,0 +1,15 @@ +French Polynesia (Tahiti), 2015-10-22 + +Invitu agrees to the terms of the Odoo Corporate Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Cyril Vinh-Tung contact@invitu.com https://github.com/invitu + +List of contributors: +Cyril Vinh-Tung contact@invitu.com https://github.com/invitu + diff --git a/openerp/addons/base/ir/ir_model.py b/openerp/addons/base/ir/ir_model.py index a00596fa418..000ae87e4a7 100644 --- a/openerp/addons/base/ir/ir_model.py +++ b/openerp/addons/base/ir/ir_model.py @@ -89,7 +89,7 @@ class ir_model(osv.osv): _defaults = { 'model': 'x_', - 'state': lambda self,cr,uid,ctx=None: (ctx and ctx.get('manual',False)) and 'manual' or 'base', + 'state': 'manual', } def _check_model_name(self, cr, uid, ids, context=None): @@ -173,10 +173,8 @@ class ir_model(osv.osv): def create(self, cr, user, vals, context=None): if context is None: context = {} - if context and context.get('manual'): - vals['state']='manual' res = super(ir_model,self).create(cr, user, vals, context) - if vals.get('state','base')=='manual': + if vals.get('state','manual')=='manual': # setup models; this automatically adds model in registry self.pool.setup_models(cr, partial=(not self.pool.ready)) # update database schema @@ -259,7 +257,7 @@ class ir_model_fields(osv.osv): 'selection': "", 'domain': "[]", 'name': 'x_', - 'state': lambda self,cr,uid,ctx=None: (ctx and ctx.get('manual',False)) and 'manual' or 'base', + 'state': 'manual', 'on_delete': 'set null', 'field_description': '', 'selectable': 1, @@ -439,14 +437,12 @@ class ir_model_fields(osv.osv): vals['model'] = model_data.model if context is None: context = {} - if context and context.get('manual',False): - vals['state'] = 'manual' if vals.get('ttype', False) == 'selection': if not vals.get('selection',False): raise UserError(_('For selection fields, the Selection Options must be given!')) self._check_selection(cr, user, vals['selection'], context=context) res = super(ir_model_fields,self).create(cr, user, vals, context) - if vals.get('state','base') == 'manual': + if vals.get('state','manual') == 'manual': if not vals['name'].startswith('x_'): raise UserError(_("Custom fields must have a name that starts with 'x_' !")) @@ -474,8 +470,6 @@ class ir_model_fields(osv.osv): def write(self, cr, user, ids, vals, context=None): if context is None: context = {} - if context and context.get('manual',False): - vals['state'] = 'manual' #For the moment renaming a sparse field or changing the storing system is not allowed. This may be done later if 'serialization_field_id' in vals or 'name' in vals: @@ -518,7 +512,7 @@ class ir_model_fields(osv.osv): raise UserError(_('Can only rename one field at a time!')) if vals['name'] in obj._fields: raise UserError(_('Cannot rename field to %s, because that field already exists!') % vals['name']) - if vals.get('state', 'base') == 'manual' and not vals['name'].startswith('x_'): + if vals.get('state', 'manual') == 'manual' and not vals['name'].startswith('x_'): raise UserError(_('New field name must still start with x_ , because it is a custom field!')) if '\'' in vals['name'] or '"' in vals['name'] or ';' in vals['name']: raise ValueError('Invalid character in column name') diff --git a/openerp/addons/base/ir/ir_model_view.xml b/openerp/addons/base/ir/ir_model_view.xml index 904ba7b4f5c..fce2edad237 100644 --- a/openerp/addons/base/ir/ir_model_view.xml +++ b/openerp/addons/base/ir/ir_model_view.xml @@ -41,7 +41,7 @@ - + @@ -203,7 +203,7 @@ Models ir.model form - {'manual':True} + {} @@ -351,7 +351,7 @@ Fields ir.model.fields form - {'manual':True} + {} diff --git a/openerp/addons/base/ir/ir_translation.py b/openerp/addons/base/ir/ir_translation.py index 14924a50665..95a68af013b 100644 --- a/openerp/addons/base/ir/ir_translation.py +++ b/openerp/addons/base/ir/ir_translation.py @@ -130,11 +130,13 @@ class ir_translation_import_cursor(object): AND irt.type = ti.type AND irt.module = ti.module AND irt.name = ti.name - AND (ti.type IN ('field', 'help') OR irt.src = ti.src) - AND ( ti.type NOT IN ('model', 'view') - OR (ti.type = 'model' AND ti.res_id = irt.res_id) - OR (ti.type = 'view' AND (irt.res_id IS NULL OR ti.res_id = irt.res_id)) - ) + AND ( + (ti.type = 'model' AND ti.res_id = irt.res_id AND irt.src = ti.src) + OR (ti.type = 'view' AND (irt.res_id IS NULL OR ti.res_id = irt.res_id) AND irt.src = ti.src) + OR (ti.type = 'field') + OR (ti.type = 'help') + OR (ti.type NOT IN ('model', 'view', 'field', 'help') AND irt.src = ti.src) + ) """ # Step 2: update existing (matching) translations diff --git a/openerp/cli/server.py b/openerp/cli/server.py index 5794855cf02..98e8359d76b 100644 --- a/openerp/cli/server.py +++ b/openerp/cli/server.py @@ -61,9 +61,9 @@ def report_configuration(): user = config['db_user'] or os.environ.get('PGUSER', 'default') _logger.info('database: %s@%s:%s', user, host, port) -def rm_pid_file(): +def rm_pid_file(main_pid): config = openerp.tools.config - if not openerp.evented and config['pidfile']: + if config['pidfile'] and main_pid == os.getpid(): try: os.unlink(config['pidfile']) except OSError: @@ -76,10 +76,10 @@ def setup_pid_file(): """ config = openerp.tools.config if not openerp.evented and config['pidfile']: + pid = os.getpid() with open(config['pidfile'], 'w') as fd: - pidtext = "%d" % (os.getpid()) - fd.write(pidtext) - atexit.register(rm_pid_file) + fd.write(str(pid)) + atexit.register(rm_pid_file, pid) def export_translation(): config = openerp.tools.config