diff --git a/addons/account/models/account_invoice.py b/addons/account/models/account_invoice.py index 3044c5612ca..58dbf3546d4 100644 --- a/addons/account/models/account_invoice.py +++ b/addons/account/models/account_invoice.py @@ -515,7 +515,7 @@ class AccountInvoice(models.Model): self.date_due = self.date_due or self.date_invoice else: pterm = self.payment_term_id - pterm_list = pterm.with_context(currency_id=self.currency_id.id).compute(value=1, date_ref=date_invoice)[0] + pterm_list = pterm.with_context(currency_id=self.company_id.currency_id.id).compute(value=1, date_ref=date_invoice)[0] self.date_due = max(line[0] for line in pterm_list) @api.multi @@ -782,7 +782,7 @@ class AccountInvoice(models.Model): name = inv.name or '/' if inv.payment_term_id: - totlines = inv.with_context(ctx).payment_term_id.with_context(currency_id=inv.currency_id.id).compute(total, date_invoice)[0] + totlines = inv.with_context(ctx).payment_term_id.with_context(currency_id=company_currency.id).compute(total, date_invoice)[0] res_amount_currency = total_currency ctx['date'] = date_invoice for i, t in enumerate(totlines): diff --git a/addons/account/security/account_security.xml b/addons/account/security/account_security.xml index 1984c94d8f3..645fa38fe06 100644 --- a/addons/account/security/account_security.xml +++ b/addons/account/security/account_security.xml @@ -80,6 +80,13 @@ ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + Invoice Analysis multi-company + + + ['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])] + + Account fiscal Mapping company rule diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index ae3c9ae60be..90cc95571d0 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -276,7 +276,7 @@ class HrExpense(models.Model): #convert eml into an osv-valid format lines = map(lambda x:(0, 0, expense._prepare_move_line(x)), move_lines) - move.write({'line_ids': lines}) + move.with_context(dont_create_taxes=True).write({'line_ids': lines}) move.post() expense.write({'account_move_id': move.id, 'state': 'post'}) if expense.payment_mode == 'company_account': diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml index cdbc60bfa3d..368fdc9f884 100644 --- a/addons/hr_expense/views/hr_expense_views.xml +++ b/addons/hr_expense/views/hr_expense_views.xml @@ -79,7 +79,7 @@ - + diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js index f6634c0665a..98e0ae5b42a 100644 --- a/addons/im_livechat/static/src/js/im_livechat.js +++ b/addons/im_livechat/static/src/js/im_livechat.js @@ -32,6 +32,7 @@ var LivechatButton = Widget.extend({ this.channel = null; this.chat_window = null; this.messages = []; + this.server_url = server_url; }, willStart: function () { @@ -203,10 +204,11 @@ var LivechatButton = Widget.extend({ this.options.default_username; // Compute the avatar_url + msg.avatar_src = this.server_url; if (msg.author_id && msg.author_id[0]) { - msg.avatar_src = "/web/image/res.partner/" + msg.author_id[0] + "/image_small"; + msg.avatar_src += "/web/image/res.partner/" + msg.author_id[0] + "/image_small"; } else { - msg.avatar_src = "/mail/static/src/img/smiley/avatar.jpg"; + msg.avatar_src += "/mail/static/src/img/smiley/avatar.jpg"; } if (options && options.prepend) { diff --git a/addons/l10n_gt/account_chart.xml b/addons/l10n_gt/account_chart.xml index 8efcc1a12e6..06abe06f34c 100644 --- a/addons/l10n_gt/account_chart.xml +++ b/addons/l10n_gt/account_chart.xml @@ -12,7 +12,7 @@ Plantilla de cuentas de Guatemala (sencilla) 1.0.01.0 1.0.02.0 - 6 + 9 diff --git a/addons/mrp_byproduct/mrp_byproduct.py b/addons/mrp_byproduct/mrp_byproduct.py index a86f8bf485c..6baa6da131b 100644 --- a/addons/mrp_byproduct/mrp_byproduct.py +++ b/addons/mrp_byproduct/mrp_byproduct.py @@ -87,7 +87,6 @@ class mrp_production(osv.osv): 'product_uom': sub_product.product_uom.id, 'location_id': source, 'location_dest_id': production.location_dest_id.id, - 'move_dest_id': production.move_prod_id.id, 'production_id': production.id, 'origin': production.name, 'subproduct_id': sub_product.id diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index d0cb2adba9f..a645c265d65 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -675,6 +675,8 @@ exports.PosModel = Backbone.Model.extend({ flushed.always(function(ids){ pushed.resolve(); }); + + return flushed; }); return pushed; }, @@ -775,6 +777,11 @@ exports.PosModel = Backbone.Model.extend({ var self = this; var timeout = typeof options.timeout === 'number' ? options.timeout : 7500 * orders.length; + // Keep the order ids that are about to be sent to the + // backend. In between create_from_ui and the success callback + // new orders may have been added to it. + var order_ids_to_sync = _.pluck(orders, 'id'); + // we try to send the order. shadow prevents a spinner if it takes too long. (unless we are sending an invoice, // then we want to notify the user that we are waiting on something ) var posOrderModel = new Model('pos.order'); @@ -789,8 +796,8 @@ exports.PosModel = Backbone.Model.extend({ timeout: timeout } ).then(function (server_ids) { - _.each(orders, function (order) { - self.db.remove_order(order.id); + _.each(order_ids_to_sync, function (order_id) { + self.db.remove_order(order_id); }); self.set('failed',false); return server_ids; diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml index a3639c73996..717e5ef65f7 100644 --- a/addons/project/project_data.xml +++ b/addons/project/project_data.xml @@ -111,7 +111,7 @@ - + Start here to discover Odoo followers @@ -124,25 +124,25 @@ - + 1 New - + 2 Basic - + 3 Advanced - + 1 0 @@ -153,7 +153,7 @@ - + 2 0 @@ -163,7 +163,7 @@ - + 3 0 @@ -173,7 +173,7 @@ - + 4 0 @@ -183,7 +183,7 @@ - + 0 @@ -192,7 +192,7 @@ - + Converse with your customers and colleagues project.task @@ -202,12 +202,12 @@ - + NeedAssistance - + 3 0 @@ -219,7 +219,7 @@ - + 4 0 @@ -230,7 +230,7 @@ - + 5 0 @@ -240,7 +240,7 @@ - + 0 @@ -249,7 +249,7 @@ Click on the bar in the upper right corner - + planner_icon.png planner_icon.png @@ -257,7 +257,7 @@ - + How to open the implementation guide ? project.task @@ -268,11 +268,11 @@ - + - + 0 @@ -281,7 +281,7 @@ Click on the gear icon on the column, to archive the stage, with all the tasks in it. You can also archive one card only, by clicking on the button in the task form view. - + 0 @@ -290,7 +290,7 @@ Click on the last column to create a stage. The name depends on the process. For example, in a customer service process, a stage name may be 'Backlog', 'Waiting Customer Feedback' or 'Done'. - + 0 @@ -299,7 +299,7 @@ - + 0 @@ -308,7 +308,7 @@ In the chatter, sending an email with an attachment will display the picture on the card. When there are several image attachments, you can choose which one you want to display. - + bird.jpg bird.jpg @@ -316,7 +316,7 @@ - + How to open the planner? project.task @@ -326,7 +326,7 @@ - + diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index 57e4eaab38b..c4e6637b40d 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -51,7 +51,7 @@ class ReportController(Controller): #------------------------------------------------------ # Misc. route utils #------------------------------------------------------ - @route(['/report/barcode', '/report/barcode//'], type='http', auth="user") + @route(['/report/barcode', '/report/barcode//'], type='http', auth="public") def report_barcode(self, type, value, width=600, height=100, humanreadable=0): """Contoller able to render barcode images thanks to reportlab. Samples: diff --git a/addons/sale_mrp/sale_mrp.py b/addons/sale_mrp/sale_mrp.py index bcb2c1442ee..74d5f8d416e 100644 --- a/addons/sale_mrp/sale_mrp.py +++ b/addons/sale_mrp/sale_mrp.py @@ -38,9 +38,9 @@ class SaleOrderLine(models.Model): # nothing policy. A product can have several BoMs, we don't know which one was used when the # delivery was created. bom_delivered = {} - for bom in self.product_id.product_tmpl_id.bom_ids: - if bom.type != 'phantom': - continue + bom_id = self.env['mrp.bom']._bom_find(product_id=self.product_id.id, properties=self.property_ids.ids) + bom = self.env['mrp.bom'].browse(bom_id) + if bom and bom.type == 'phantom': bom_delivered[bom.id] = False product_uom_qty_bom = self.env['product.uom']._compute_qty_obj(self.product_uom, self.product_uom_qty, bom.product_uom) bom_exploded = self.env['mrp.bom']._bom_explode(bom, self.product_id, product_uom_qty_bom)[0] diff --git a/addons/stock/wizard/stock_change_product_qty.py b/addons/stock/wizard/stock_change_product_qty.py index 1dc26554bff..b33dbcc8579 100644 --- a/addons/stock/wizard/stock_change_product_qty.py +++ b/addons/stock/wizard/stock_change_product_qty.py @@ -36,7 +36,7 @@ class ProductChangeQuantity(models.TransientModel): def onchange_location_id(self): # TDE FIXME: should'nt we use context / location ? if self.location_id and self.product_id: - availability = self.product_id._product_available() + availability = self.product_id.with_context(compute_child=False)._product_available() self.new_quantity = availability[self.product_id.id]['qty_available'] @api.onchange('product_id') diff --git a/addons/web/static/src/js/views/pivot_view.js b/addons/web/static/src/js/views/pivot_view.js index 5a57601b7f7..9283bd584c2 100644 --- a/addons/web/static/src/js/views/pivot_view.js +++ b/addons/web/static/src/js/views/pivot_view.js @@ -206,6 +206,10 @@ var PivotView = View.extend({ this.do_push_state({}); return this.data_loaded.done(function () { self.display_table(); + self.$buttons.find('.o_pivot_measures_list li').removeClass('selected'); + self.active_measures.forEach(function (measure) { + self.$buttons.find('li[data-field="' + measure + '"]').addClass('selected'); + }); _super(); }); }, diff --git a/addons/web_editor/static/src/js/translator.js b/addons/web_editor/static/src/js/translator.js index ead8ea8271c..318fc0c08fa 100644 --- a/addons/web_editor/static/src/js/translator.js +++ b/addons/web_editor/static/src/js/translator.js @@ -177,6 +177,10 @@ var Translate = Widget.extend({ }, rte_changed: function (node) { var $node = $(node); + $node.find("p").each(function () { // remove

element which might have been inserted because of copy-paste + var $p = $(this); + $p.after($p.html()).remove(); + }); var trans = this.getTranlationObject($node[0]); $node.toggleClass('o_dirty', trans.value !== $node.html().replace(/[ \t\n\r]+/, ' ')); }, diff --git a/doc/cla/corporate/nubark.md b/doc/cla/corporate/nubark.md new file mode 100644 index 00000000000..a570c6f3ddc --- /dev/null +++ b/doc/cla/corporate/nubark.md @@ -0,0 +1,15 @@ +Colombia, 2017-01-21 + +Nubark 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, + +Esteban Echeverry eecheverry@nubark.com https://github.com/nubark + +List of contributors: + +Esteban Echeverry eecheverry@nubark.com https://github.com/tebanep diff --git a/doc/cla/individual/hendrikw25.md b/doc/cla/individual/hendrikw25.md new file mode 100644 index 00000000000..27d6bf3fccb --- /dev/null +++ b/doc/cla/individual/hendrikw25.md @@ -0,0 +1,11 @@ +Indonesia, 2017-01-25 + +I hereby agree to the terms of the Odoo Individual Contributor License +Agreement v1.0. + +I declare that I am authorized and able to make this agreement and sign this +declaration. + +Signed, + +Hendrik hendrikwidjaja25@gmail.com https://github.com/hendrikw25 diff --git a/openerp/addons/base/ir/ir_model_view.xml b/openerp/addons/base/ir/ir_model_view.xml index c59622bec29..9d38a2997f7 100644 --- a/openerp/addons/base/ir/ir_model_view.xml +++ b/openerp/addons/base/ir/ir_model_view.xml @@ -34,7 +34,7 @@ - + diff --git a/openerp/models.py b/openerp/models.py index 657ef4b5f03..9f2b32e45d1 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -3855,6 +3855,8 @@ class BaseModel(object): return True if isinstance(ids, (int, long)): ids = [ids] + if not context: + context = {} result_store = self._store_get_values(cr, uid, ids, self._fields.keys(), context) @@ -3930,7 +3932,8 @@ class BaseModel(object): obj._store_set_values(cr, uid, rids, fields, context) # recompute new-style fields - recs.recompute() + if recs.env.recompute and context.get('recompute', True): + recs.recompute() # auditing: deletions are infrequent and leave no trace in the database _unlink.info('User #%s deleted %s records with IDs: %r', uid, self._name, ids) diff --git a/openerp/tools/config.py b/openerp/tools/config.py index 81afc47c1f4..1993c2f52bd 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -568,9 +568,13 @@ class configmanager(object): @property def addons_data_dir(self): - d = os.path.join(self['data_dir'], 'addons', release.series) + add_dir = os.path.join(self['data_dir'], 'addons') + d = os.path.join(add_dir, release.series) if not os.path.exists(d): try: + # bootstrap parent dir +rwx + if not os.path.exists(add_dir): + os.makedirs(add_dir, 0700) # try to make +rx placeholder dir, will need manual +w to activate it os.makedirs(d, 0500) except OSError: