From 335a8afe79d0d25c91508d881d49d06c862b79f1 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Mon, 18 May 2015 14:52:06 +0200 Subject: [PATCH 01/36] [FIX] stock: remove state modifiers for field note in stock.picking Remove the state modifiers for field note in stock.picking in order to have a behavior which is consistent with sales orders or invoices. opw-639956 --- addons/stock/stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 9354fa0bf4f..f01e0995acc 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -642,7 +642,7 @@ class stock_picking(osv.osv): 'origin': fields.char('Source Document', size=64, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="Reference of the document", select=True), 'backorder_id': fields.many2one('stock.picking', 'Back Order of', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="If this shipment was split, then this field links to the shipment which contains the already processed part.", select=True), 'type': fields.selection([('out', 'Sending Goods'), ('in', 'Getting Goods'), ('internal', 'Internal')], 'Shipping Type', required=True, select=True, help="Shipping type specify, goods coming in or going out."), - 'note': fields.text('Notes', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}), + 'note': fields.text('Notes'), 'stock_journal_id': fields.many2one('stock.journal','Stock Journal', select=True, states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}), 'location_id': fields.many2one('stock.location', 'Location', states={'done':[('readonly', True)], 'cancel':[('readonly',True)]}, help="Keep empty if you produce at the location where the finished products are needed." \ "Set a location if you produce at a fixed location. This can be a partner location " \ From 235cd2d015980265dbf8de5cff7caa50d1ce543b Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Mon, 18 May 2015 17:13:40 +0200 Subject: [PATCH 02/36] [FIX] mrp: find company bom when MO created from procurement through cron Manufacturing Orders can be created from procurements, as SUPERUSER_ID, since the procurements can be processed through the WH scheduler, which is always ran as SUPERUSER_ID In such a case, the record rules are ignored, and a BOM normally not accessible to a user thanks to the multi-company record rule could be chosen as the BOM of the MO. This revision forces to find a BOM from a specific company in such a case. opw-640120 --- addons/mrp/mrp.py | 4 ++++ addons/mrp/procurement.py | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/mrp/mrp.py b/addons/mrp/mrp.py index 854349f110b..7ca680ad762 100644 --- a/addons/mrp/mrp.py +++ b/addons/mrp/mrp.py @@ -212,6 +212,8 @@ class mrp_bom(osv.osv): @param properties: List of related properties. @return: False or BoM id. """ + if not context: + context = {} if properties is None: properties = [] if product_id: @@ -229,6 +231,8 @@ class mrp_bom(osv.osv): else: # neither product nor template, makes no sense to search return False + if context.get('company_id'): + domain = domain + [('company_id', '=', context['company_id'])] domain = domain + [ '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATE_FORMAT)), '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATE_FORMAT))] # order to prioritize bom with product_id over the one without diff --git a/addons/mrp/procurement.py b/addons/mrp/procurement.py index dd14cd89c93..bbcfbedb828 100644 --- a/addons/mrp/procurement.py +++ b/addons/mrp/procurement.py @@ -86,7 +86,7 @@ class procurement_order(osv.osv): else: properties = [x.id for x in procurement.property_ids] bom_id = bom_obj._bom_find(cr, uid, product_id=procurement.product_id.id, - properties=properties, context=context) + properties=properties, context=dict(context, company_id=procurement.company_id.id)) bom = bom_obj.browse(cr, uid, bom_id, context=context) routing_id = bom.routing_id.id return { From d5aa22ea5fb6821fd6eb8aaa0dd6c957f0d49fe3 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Mon, 18 May 2015 19:24:19 +0200 Subject: [PATCH 03/36] [FIX] website, website_report: backwards compatibility with outdated templates * Complements commits a696913364ffc4d5f1ce0675bc9be82f84f3ff93 and 21d4b3fda9dd1ba480ee407f62c83375cfe1d8ef by adding the missing `data-lang` attribute also in the report layout (in saas-6 a single QWeb template is used for language links in both reports and website layouts). * Fix the "Edit Master" link to work also for outdated templates where the data-lang attribute is dynamically set to 'default', and thus cannot be used as URL prefix -> use /website/lang controller to switch lang instead. --- addons/website/static/src/js/website.translator.js | 6 ++++-- addons/website_report/views/layouts.xml | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/addons/website/static/src/js/website.translator.js b/addons/website/static/src/js/website.translator.js index d5cbbf6668d..bea3406a40f 100644 --- a/addons/website/static/src/js/website.translator.js +++ b/addons/website/static/src/js/website.translator.js @@ -60,9 +60,11 @@ ev.preventDefault(); var link = $('.js_language_selector a[data-default-lang]')[0]; if (link) { - link.pathname = $(link).data('lang') + link.pathname; link.search += (link.search ? '&' : '?') + 'enable_editor=1'; - window.location = link.attributes.href.value; + var url = link.pathname + link.search + window.location.hash; + link.pathname = '/website/lang/default'; + link.search = '?' + $.param({r: url}); + window.location = link.href; } }, translate: function () { diff --git a/addons/website_report/views/layouts.xml b/addons/website_report/views/layouts.xml index c1cc3b49365..c40d5c8f62a 100644 --- a/addons/website_report/views/layouts.xml +++ b/addons/website_report/views/layouts.xml @@ -23,6 +23,7 @@
  • From 31d817e849719ed893a5691d66eb136b481beb5b Mon Sep 17 00:00:00 2001 From: Coin Newell Date: Mon, 18 May 2015 23:41:16 +0100 Subject: [PATCH 04/36] [FIX] web: rotate session identifiers after login/logout As recommended by OWASP https://www.owasp.org/index.php/Session_Management_Cheat_Sheet#Renew_the_Session_ID_After_Any_Privilege_Level_Change Closes #6760 --- openerp/http.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/openerp/http.py b/openerp/http.py index b972c610329..f4386a41dd3 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -917,6 +917,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): self.inited = True self._default_values() self.modified = False + self.rotate = False def __getattr__(self, attr): return self.get(attr, None) @@ -948,6 +949,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): uid = dispatch_rpc('common', 'authenticate', [db, login, password, env]) else: security.check(db, uid, password) + self.rotate = True self.db = db self.uid = uid self.login = login @@ -973,6 +975,7 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): if not (keep_db and k == 'db'): del self[k] self._default_values() + self.rotate = True def _default_values(self): self.setdefault("db", None) @@ -1369,6 +1372,10 @@ class Root(object): response = result if httprequest.session.should_save: + if httprequest.session.rotate: + self.session_store.delete(httprequest.session) + httprequest.session.sid = self.session_store.generate_key() + httprequest.session.modified = True self.session_store.save(httprequest.session) # We must not set the cookie if the session id was specified using a http header or a GET parameter. # There are two reasons to this: From a6cba47cd7379a3613aa29408794b1888ea4efec Mon Sep 17 00:00:00 2001 From: Coin Newell Date: Mon, 18 May 2015 22:14:39 +0100 Subject: [PATCH 05/36] [CLA] Colin Newell --- doc/cla/individual/colinnewell.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 doc/cla/individual/colinnewell.md diff --git a/doc/cla/individual/colinnewell.md b/doc/cla/individual/colinnewell.md new file mode 100644 index 00000000000..89a4ab891bb --- /dev/null +++ b/doc/cla/individual/colinnewell.md @@ -0,0 +1,12 @@ +United Kingdom, 2015-05-18 + +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, + +Colin Newell colin.newell@gmail.com https://github.com/colinnewell + From 6e063188282e3594178c9ae367fa939ea890c9f9 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Sat, 16 May 2015 22:21:06 +0200 Subject: [PATCH 06/36] [FIX] base: value = 0 for record ir_property When a record is created with a field property of type integer or float equal to 0, there is no record created in ir_property for this field. Then for a search of a record with this type of field equal to 0, it must match all the records that have no corresponding property in the table "ir.property" for this field. ps: in 7.0, the function search_multi doesn't exist and the bug linked to cost price doesn't happen because it's a float field. opw:639746 --- openerp/addons/base/res/ir_property.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/openerp/addons/base/res/ir_property.py b/openerp/addons/base/res/ir_property.py index 382f7ae2b95..b90d18233a3 100644 --- a/openerp/addons/base/res/ir_property.py +++ b/openerp/addons/base/res/ir_property.py @@ -260,6 +260,7 @@ class ir_property(osv.osv): def search_multi(self, name, model, operator, value): """ Return a domain for the records that match the given condition. """ default_matches = False + include_zero = False field = self.env[model]._fields[name] if field.type == 'many2one': @@ -281,6 +282,27 @@ class ir_property(osv.osv): target_names = target.name_search(value, operator=operator, limit=None) target_ids = map(itemgetter(0), target_names) operator, value = 'in', map(makeref, target_ids) + elif field.type in ('integer', 'float'): + # No record is created in ir.property if the field's type is float or integer with a value + # equal to 0. Then to match with the records that are linked to a property field equal to 0, + # the negation of the operator must be taken to compute the goods and the domain returned + # to match the searched records is just the opposite. + if value == 0 and operator == '=': + operator = '!=' + include_zero = True + elif value <= 0 and operator == '>=': + operator = '<' + include_zero = True + elif value <= 0 and operator == '>': + operator = '<=' + include_zero = True + elif value >= 0 and operator == '<=': + operator = '>' + include_zero = True + elif value >= 0 and operator == '<': + operator = '>=' + include_zero = True + # retrieve the properties that match the condition domain = self._get_domain(name, model) @@ -297,7 +319,9 @@ class ir_property(osv.osv): else: default_matches = True - if default_matches: + if include_zero: + return [('id', 'not in', good_ids)] + elif default_matches: # exclude all records with a property that does not match all_ids = [] props = self.search(domain + [('res_id', '!=', False)]) From 6d25a1a18a1b45c4faed0f4ba644259ec4f11893 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Mon, 18 May 2015 14:17:20 +0200 Subject: [PATCH 07/36] [FIX] google_calendar: sync date on event date change Set the record update time when the date is changed via another view than calendar view. So when an synchronization is done, the update is taken into account when updating calendar from the odoo to google. closes #6752 opw-634674 --- addons/google_calendar/google_calendar.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py index 31e2dc8210d..32c030b6ea8 100644 --- a/addons/google_calendar/google_calendar.py +++ b/addons/google_calendar/google_calendar.py @@ -976,7 +976,9 @@ class calendar_event(osv.Model): _inherit = "calendar.event" def get_fields_need_update_google(self, cr, uid, context=None): - return ['name', 'description', 'allday', 'date', 'date_end', 'stop', 'attendee_ids', 'alarm_ids', 'location', 'class', 'active'] + return ['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: From c5a613840f6f40f8c446d8b35e19f3c340cc056d Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Sat, 16 May 2015 15:42:21 +0200 Subject: [PATCH 08/36] [FIX] point of sale: function is_paid: When rounding globally, the function compute_all in charge to compute the taxes for each line uses a very high currency rounding to avoid rounding per line. The round must be done on the sum of each total amount line with taxes. To validate a payment with the pos, the function is_paid verified that this sum is the same that the amount just paid. Inspired from "sale.order.line", the precision of 'price_unit' and 'price_subtotal' in "pos.order.line" must be taken from 'Product Price' to avoid rounding per line. ps: we could not use the function get_all_prices to round globally because it used on every line. Fixes #6681 Closes #6682 opw:639686 --- addons/point_of_sale/point_of_sale.py | 4 ++-- addons/point_of_sale/static/src/js/models.js | 24 ++++++++++---------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/addons/point_of_sale/point_of_sale.py b/addons/point_of_sale/point_of_sale.py index 5683b40ff96..3728b958438 100644 --- a/addons/point_of_sale/point_of_sale.py +++ b/addons/point_of_sale/point_of_sale.py @@ -1335,9 +1335,9 @@ class pos_order_line(osv.osv): 'name': fields.char('Line No', required=True, copy=False), 'notice': fields.char('Discount Notice'), 'product_id': fields.many2one('product.product', 'Product', domain=[('sale_ok', '=', True)], required=True, change_default=True), - 'price_unit': fields.float(string='Unit Price', digits_compute=dp.get_precision('Account')), + 'price_unit': fields.float(string='Unit Price', digits_compute=dp.get_precision('Product Price')), 'qty': fields.float('Quantity', digits_compute=dp.get_precision('Product UoS')), - 'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', digits_compute=dp.get_precision('Account'), string='Subtotal w/o Tax', store=True), + 'price_subtotal': fields.function(_amount_line_all, multi='pos_order_line_amount', digits_compute=dp.get_precision('Product Price'), string='Subtotal w/o Tax', store=True), 'price_subtotal_incl': fields.function(_amount_line_all, multi='pos_order_line_amount', digits_compute=dp.get_precision('Account'), string='Subtotal', store=True), 'discount': fields.float('Discount (%)', digits_compute=dp.get_precision('Account')), 'order_id': fields.many2one('pos.order', 'Order Ref', ondelete='cascade'), diff --git a/addons/point_of_sale/static/src/js/models.js b/addons/point_of_sale/static/src/js/models.js index f18059905e2..a85090647f9 100644 --- a/addons/point_of_sale/static/src/js/models.js +++ b/addons/point_of_sale/static/src/js/models.js @@ -1118,29 +1118,29 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal return this.get('name'); }, getSubtotal : function(){ - return (this.get('orderLines')).reduce((function(sum, orderLine){ + return round_pr((this.get('orderLines')).reduce((function(sum, orderLine){ return sum + orderLine.get_display_price(); - }), 0); + }), 0), this.pos.currency.rounding); }, getTotalTaxIncluded: function() { - return (this.get('orderLines')).reduce((function(sum, orderLine) { + return round_pr((this.get('orderLines')).reduce((function(sum, orderLine) { return sum + orderLine.get_price_with_tax(); - }), 0); + }), 0), this.pos.currency.rounding); }, getDiscountTotal: function() { - return (this.get('orderLines')).reduce((function(sum, orderLine) { + return round_pr((this.get('orderLines')).reduce((function(sum, orderLine) { return sum + (orderLine.get_unit_price() * (orderLine.get_discount()/100) * orderLine.get_quantity()); - }), 0); + }), 0), this.pos.currency.rounding); }, getTotalTaxExcluded: function() { - return (this.get('orderLines')).reduce((function(sum, orderLine) { + return round_pr((this.get('orderLines')).reduce((function(sum, orderLine) { return sum + orderLine.get_price_without_tax(); - }), 0); + }), 0), this.pos.currency.rounding); }, getTax: function() { - return (this.get('orderLines')).reduce((function(sum, orderLine) { + return round_pr((this.get('orderLines')).reduce((function(sum, orderLine) { return sum + orderLine.get_tax(); - }), 0); + }), 0), this.pos.currency.rounding); }, getTaxDetails: function(){ var details = {}; @@ -1164,9 +1164,9 @@ function openerp_pos_models(instance, module){ //module is instance.point_of_sal return fulldetails; }, getPaidTotal: function() { - return (this.get('paymentLines')).reduce((function(sum, paymentLine) { + return round_pr((this.get('paymentLines')).reduce((function(sum, paymentLine) { return sum + paymentLine.get_amount(); - }), 0); + }), 0), this.pos.currency.rounding); }, getChange: function() { return this.getPaidTotal() - this.getTotalTaxIncluded(); From 9b2b437df400af9c01aebff62858e6b8d34bc0f5 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 12 May 2015 10:38:24 +0200 Subject: [PATCH 09/36] [FIX] sale_stock: add check on invoice type during generation of invoice from delivery If route is set on dropshipping and the PO invoice is generated from picking, the unit price used in the supplier invoice must be the cost price. In this particular configuration, this was not the case because the unit price was replaced by the sale price. opw-634898 --- addons/sale_stock/sale_stock.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 5d4430da3f3..57634684e47 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -381,7 +381,7 @@ class stock_move(osv.osv): if move.procurement_id and move.procurement_id.sale_line_id and move.procurement_id.sale_line_id.order_id.order_policy == 'picking': sale_order = move.procurement_id.sale_line_id.order_id return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id - elif move.picking_id.sale_id and context.get('inv_type') == 'out_invoice': + elif move.picking_id.sale_id and context.get('inv_type') in ('out_invoice', 'out_refund'): # In case of extra move, it is better to use the same data as the original moves sale_order = move.picking_id.sale_id return sale_order.partner_invoice_id, sale_order.user_id.id, sale_order.pricelist_id.currency_id.id @@ -389,7 +389,7 @@ class stock_move(osv.osv): def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None): res = super(stock_move, self)._get_invoice_line_vals(cr, uid, move, partner, inv_type, context=context) - if move.procurement_id and move.procurement_id.sale_line_id: + if inv_type in ('out_invoice', 'out_refund') and move.procurement_id and move.procurement_id.sale_line_id: sale_line = move.procurement_id.sale_line_id res['invoice_line_tax_id'] = [(6, 0, [x.id for x in sale_line.tax_id])] res['account_analytic_id'] = sale_line.order_id.project_id and sale_line.order_id.project_id.id or False From bac5b87b7387ac9e202f6e942d6ffc1c53a74db4 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 19 May 2015 11:03:47 +0200 Subject: [PATCH 10/36] [FIX] calendar: update cron as SUPERUSER. As regular employees are not allowed to alter cron activities. opw-639419 --- addons/calendar/calendar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 711b37c205a..0bc1336a85a 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -585,7 +585,7 @@ class calendar_alarm(osv.Model): def _update_cron(self, cr, uid, context=None): try: cron = self.pool['ir.model.data'].get_object( - cr, uid, 'calendar', 'ir_cron_scheduler_alarm', context=context) + cr, SUPERUSER_ID, 'calendar', 'ir_cron_scheduler_alarm', context=context) except ValueError: return False return cron.toggle(model=self._name, domain=[('type', '=', 'email')]) From c0deb1f726462ca6cf341e56e9874f275b128587 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Mon, 11 May 2015 12:06:52 +0200 Subject: [PATCH 11/36] [FIX] sale_stock: check if moves are already done when cancelling a SO It prevents to cancel a SO for which moves are already done. opw-634415 --- addons/sale_stock/sale_stock.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 57634684e47..6827f5372e3 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -355,6 +355,14 @@ class sale_order_line(osv.osv): res.update({'warning': warning}) return res + def button_cancel(self, cr, uid, ids, context=None): + lines = self.browse(cr, uid, ids, context=context) + for procurement in lines.mapped('procurement_ids'): + for move in procurement.move_ids: + if move.state == 'done' and not move.scrapped: + raise osv.except_osv(_('Invalid Action!'), _('You cannot cancel a sale order line which is linked to a stock move already done.')) + return super(sale_order_line, self).button_cancel(cr, uid, ids, context=context) + class stock_move(osv.osv): _inherit = 'stock.move' From cc61d467fafac83b9785fd506ae86f2722e28288 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Tue, 19 May 2015 12:51:24 +0200 Subject: [PATCH 12/36] [FIX] purchase: validate invoice A user that has the rights in Accounting & Finance equal to Invoicing & Payments must have the right to validate a supplier invoice. opw:634601 --- addons/purchase/purchase.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 98365546c60..a56813774eb 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -1325,7 +1325,7 @@ class account_invoice(osv.Model): user_id = uid po_ids = purchase_order_obj.search(cr, user_id, [('invoice_ids', 'in', ids)], context=context) wf_service = netsvc.LocalService("workflow") - for order in purchase_order_obj.browse(cr, uid, po_ids, context=context): + for order in purchase_order_obj.browse(cr, user_id, po_ids, context=context): # Signal purchase order workflow that an invoice has been validated. invoiced = [] shipped = True @@ -1338,8 +1338,8 @@ class account_invoice(osv.Model): if all(line.invoice_id.state not in ['draft', 'cancel'] for line in po_line.invoice_lines): invoiced.append(po_line.id) if invoiced and shipped: - self.pool['purchase.order.line'].write(cr, uid, invoiced, {'invoiced': True}) - wf_service.trg_write(uid, 'purchase.order', order.id, cr) + self.pool['purchase.order.line'].write(cr, user_id, invoiced, {'invoiced': True}) + wf_service.trg_write(user_id, 'purchase.order', order.id, cr) return res # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: From eeedd2d9f52d46d8193059854e7430ca0c1fd6c0 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 11 May 2015 18:32:28 +0200 Subject: [PATCH 13/36] [FIX] fields: `digits()` computation Ensure we always have an valid cursor when determining `digits()`. fixes #6605, fixes #6650 --- openerp/fields.py | 3 ++- openerp/osv/fields.py | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index d780bf5adb3..cd25295d8e6 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1054,7 +1054,8 @@ class Float(Field): @property def digits(self): if callable(self._digits): - return self._digits(fields.get_cursor()) + with fields._get_cursor() as cr: + return self._digits(cr) else: return self._digits diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 02d24fdf47b..00eba168552 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -43,6 +43,7 @@ import pytz import re import xmlrpclib from operator import itemgetter +from contextlib import contextmanager from psycopg2 import Binary import openerp @@ -52,13 +53,22 @@ from openerp.tools import float_repr, float_round, frozendict, html_sanitize import simplejson from openerp import SUPERUSER_ID, registry -def get_cursor(): - # retrieve a valid cursor from any environment +@contextmanager +def _get_cursor(): + # yield a valid cursor from any environment or create a new one if none found from openerp.api import Environment + from openerp.http import request + try: + request.env # force request's env to be computed + except RuntimeError: + pass # ignore if not in a request for env in Environment.envs: if not env.cr.closed: - return env.cr - raise RuntimeError("No valid cursor found") + yield env.cr + break + else: + with registry().cursor() as cr: + yield cr EMPTY_DICT = frozendict() @@ -396,7 +406,8 @@ class float(_column): @property def digits(self): if self._digits_compute: - return self._digits_compute(get_cursor()) + with _get_cursor() as cr: + return self._digits_compute(cr) else: return self._digits @@ -1316,7 +1327,8 @@ class function(_column): @property def digits(self): if self._digits_compute: - return self._digits_compute(get_cursor()) + with _get_cursor() as cr: + return self._digits_compute(cr) else: return self._digits From 0fcb15c797fe3fba63640a2b792c57527d3a9b5d Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 19 May 2015 17:23:05 +0200 Subject: [PATCH 14/36] [FIX] payment_adyen: wrong error message opw-640208 --- addons/payment_adyen/models/adyen.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/payment_adyen/models/adyen.py b/addons/payment_adyen/models/adyen.py index 1ab2f1839f2..594aef58ca6 100644 --- a/addons/payment_adyen/models/adyen.py +++ b/addons/payment_adyen/models/adyen.py @@ -171,7 +171,7 @@ class TxAdyen(osv.Model): }) return True else: - error = 'Paypal: feedback error' + error = 'Adyen: feedback error' _logger.info(error) tx.write({ 'state': 'error', From ac92551406c333dc6829126e4c2fc6f0f1c613f9 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 20 May 2015 11:05:11 +0200 Subject: [PATCH 15/36] [FIX] ir_attachment: hide Attachments > Add/Delete if not write access To be able to see record attachments, you need to have read access to the record. To be able to add or delete attachment, you need to have write access to the record. The button add/delete were displayed as soon as you had the read access, but if you had not the write access, both buttons failed. This revision do not display the add/delete buttons of Attachment dropdown menu if you do not have the right to add / delete attachments to the record (you do not have write access to the record) opw-640242 --- addons/web/static/src/js/views.js | 2 +- addons/web/static/src/xml/base.xml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/views.js b/addons/web/static/src/js/views.js index 7f9e2f09420..5b08c317b2b 100644 --- a/addons/web/static/src/js/views.js +++ b/addons/web/static/src/js/views.js @@ -1164,7 +1164,7 @@ instance.web.Sidebar = instance.web.Widget.extend({ init: function(parent) { var self = this; this._super(parent); - var view = this.getParent(); + this.view = this.getParent(); this.sections = [ { 'name' : 'print', 'label' : _t('Print'), }, { 'name' : 'other', 'label' : _t('More'), } diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index d28d021e298..1f22adace16 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -648,9 +648,9 @@ - x + x
  • -
  • +
  • /web/binary/upload_attachment From db3e5716b3977719d0bd9a65f615cc8475078f81 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 20 May 2015 11:09:05 +0200 Subject: [PATCH 16/36] [FIX] ir_attachment: write traceback to logs when upload fail When the upload of an attachment failed, e.g. for access rights reasons, the traceback wasn't written or returned anywhere, preventing the easy debugging. opw-640242 --- addons/web/controllers/main.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/web/controllers/main.py b/addons/web/controllers/main.py index 3ebbfb02af6..8b3a21fdee8 100644 --- a/addons/web/controllers/main.py +++ b/addons/web/controllers/main.py @@ -1168,6 +1168,7 @@ class Binary(http.Controller): } except Exception: args = {'error': "Something horrible happened"} + _logger.exception("Fail to upload attachment %s" % ufile.filename) return out % (simplejson.dumps(callback), simplejson.dumps(args)) @http.route([ From 453b373a685d652bc8e542284a0da899ce5ffccd Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 20 May 2015 12:16:49 +0200 Subject: [PATCH 17/36] [FIX] report: datetime already imported, differently This rev. is related to 439cdb6871b03a4af9a64fe5945e862d6c8768e2. The module datetime was already available in the reports, and it was the entire datetime module that was imported, not just the class. This rev. is not recompatible with 439cdb6871b03a4af9a64fe5945e862d6c8768e2, but there is no other choice, since other existing reports might already expected the datetime module and not the class opw-640299 --- addons/report/models/report.py | 2 -- addons/report/views/layouts.xml | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/addons/report/models/report.py b/addons/report/models/report.py index 9c3e0cccd50..10142ff0695 100644 --- a/addons/report/models/report.py +++ b/addons/report/models/report.py @@ -38,7 +38,6 @@ import lxml.html import os import subprocess from contextlib import closing -from datetime import datetime from distutils.version import LooseVersion from functools import partial from pyPdf import PdfFileWriter, PdfFileReader @@ -143,7 +142,6 @@ class Report(osv.Model): context = dict(context, translatable=context.get('lang') != request.website.default_lang_code) values.update( time=time, - datetime=datetime, context_timestamp=lambda t: fields.datetime.context_timestamp(cr, uid, t, context), translate_doc=translate_doc, editable=True, diff --git a/addons/report/views/layouts.xml b/addons/report/views/layouts.xml index 963810c384e..dbd115401c1 100644 --- a/addons/report/views/layouts.xml +++ b/addons/report/views/layouts.xml @@ -123,7 +123,7 @@
    - +
    From 7b306fc17a9e8f3d33df809cbd39af38ab694552 Mon Sep 17 00:00:00 2001 From: Thomas Groutars Date: Mon, 18 May 2015 15:57:38 +0200 Subject: [PATCH 18/36] [IMP] stock: improve detailed transfer performance Transfers were slowed because of the continuous computation of pickings remaining quantities. This revision delay the computation at the end of the transfer, so the computation is performed only once, and it therefore leads to a huge performance improvment. opw-634416 --- addons/stock/wizard/stock_transfer_details.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/stock/wizard/stock_transfer_details.py b/addons/stock/wizard/stock_transfer_details.py index 17f302f6317..437509aacf3 100644 --- a/addons/stock/wizard/stock_transfer_details.py +++ b/addons/stock/wizard/stock_transfer_details.py @@ -91,7 +91,7 @@ class stock_transfer_details(models.TransientModel): 'owner_id': prod.owner_id.id, } if prod.packop_id: - prod.packop_id.write(pack_datas) + prod.packop_id.with_context(no_recompute=True).write(pack_datas) processed_ids.append(prod.packop_id.id) else: pack_datas['picking_id'] = self.picking_id.id @@ -99,8 +99,7 @@ class stock_transfer_details(models.TransientModel): processed_ids.append(packop_id.id) # Delete the others packops = self.env['stock.pack.operation'].search(['&', ('picking_id', '=', self.picking_id.id), '!', ('id', 'in', processed_ids)]) - for packop in packops: - packop.unlink() + packops.unlink() # Execute the transfer of the picking self.picking_id.do_transfer() From 4e8f84053d1f306924a2e5fd99e5cf3fa7a9bf74 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Wed, 20 May 2015 09:49:52 +0200 Subject: [PATCH 19/36] [FIX] pos_restaurant: changed unit caused issue on splitting Commit 0fd51c2e6 changed the unit rounding from units from 1 to 0.001, it was done mainly because it's the default unit and in the interface we see quantities with 3 decimals after the floating point, so people might expect to be able to set floating point quantities. This change caused an issue when splitting a bill in the pos restaurant, instead of a click being a piece of the product, it was 0.001 part of a product. This fix overrides the rounding of units in this case. closes #6780 opw-639865 --- addons/pos_restaurant/static/src/js/splitbill.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/pos_restaurant/static/src/js/splitbill.js b/addons/pos_restaurant/static/src/js/splitbill.js index 81591ccc589..ee55402c835 100644 --- a/addons/pos_restaurant/static/src/js/splitbill.js +++ b/addons/pos_restaurant/static/src/js/splitbill.js @@ -45,7 +45,7 @@ function openerp_restaurant_splitbill(instance, module){ } }else{ if( split.quantity < line.get_quantity()){ - split.quantity += line.get_unit().rounding; + split.quantity += line.get_unit().is_unit ? 1 : line.get_unit().rounding; if(split.quantity > line.get_quantity()){ split.quantity = line.get_quantity(); } From 54a90179bb477a0ce3814395a38e5e98c8e9e477 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 20 May 2015 15:33:10 +0200 Subject: [PATCH 20/36] [FIX] expression: ensure tuple to compare leaf with TRUE/FALSE leaf When setting a custom filter with as domain [(0, '=', 1)] the domain was rejected, because (0, '=', 1) wasn't considered as a valid leaf, while it is. This is because the Javascript converts this domain using list instead of tuple [(0, '=', 1)] -> [[0, '=', 1]] And therefore, comparing the "list" leaf to the TRUE/FALSE leaf tuple failed. Ensuring "element" as a tuple solves the issue. opw-640306 --- openerp/osv/expression.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 18c82306bc5..4715d9268c4 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -401,7 +401,7 @@ def is_leaf(element, internal=False): and len(element) == 3 \ and element[1] in INTERNAL_OPS \ and ((isinstance(element[0], basestring) and element[0]) - or element in (TRUE_LEAF, FALSE_LEAF)) + or tuple(element) in (TRUE_LEAF, FALSE_LEAF)) # -------------------------------------------------- From 7d69759e73dae9be7f44757040dd86d14b5bbff8 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 20 May 2015 15:49:36 +0200 Subject: [PATCH 21/36] [FIX] calendar: display event reminders This issue is related to 4f03a6224d462c248c9dbb5f833de24312447f75. The above revision aimed to not fetch uselessly fields values The thing is, `partner_id` of `get_next_potential_limit_alarm` method expects a partner id, while partner['id'] is actually a user id. The `partner_id` of the user must be taken, not the user id itself. --- addons/calendar/calendar.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/calendar/calendar.py b/addons/calendar/calendar.py index 0bc1336a85a..d1d165acde7 100644 --- a/addons/calendar/calendar.py +++ b/addons/calendar/calendar.py @@ -479,13 +479,13 @@ class calendar_alarm_manager(osv.AbstractModel): def get_next_notif(self, cr, uid, context=None): ajax_check_every_seconds = 300 - partner = self.pool['res.users'].read(cr, SUPERUSER_ID, uid, ['calendar_last_notif_ack'], context=context) + partner = self.pool['res.users'].read(cr, SUPERUSER_ID, uid, ['partner_id', 'calendar_last_notif_ack'], context=context) all_notif = [] if not partner: return [] - all_events = self.get_next_potential_limit_alarm(cr, uid, ajax_check_every_seconds, partner_id=partner['id'], mail=False, context=context) + all_events = self.get_next_potential_limit_alarm(cr, uid, ajax_check_every_seconds, partner_id=partner['partner_id'][0], mail=False, context=context) for event in all_events: # .values() max_delta = all_events[event]['max_duration'] From 9c6ae3cf6c3772fcfd2127022cacc8f3233fea0a Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 19 May 2015 13:41:32 +0200 Subject: [PATCH 22/36] [FIX] mrp_operations: adapt the date_planned of the WO when date_planned of MO is changed Changing the scheduled date (date_planned) of the MO triggers a computation of the scheduled date of the associated WO in order to keep the data consistent. Fixes #6694 opw-639771 --- addons/mrp_operations/mrp_operations.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mrp_operations/mrp_operations.py b/addons/mrp_operations/mrp_operations.py index d841e344a9e..f77d5cf839f 100644 --- a/addons/mrp_operations/mrp_operations.py +++ b/addons/mrp_operations/mrp_operations.py @@ -354,7 +354,7 @@ class mrp_production(osv.osv): for po in self.browse(cr, uid, ids, context=context): direction[po.id] = cmp(po.date_start, vals.get('date_start', False)) result = super(mrp_production, self).write(cr, uid, ids, vals, context=context) - if (vals.get('workcenter_lines', False) or vals.get('date_start', False)) and update: + if (vals.get('workcenter_lines', False) or vals.get('date_start', False) or vals.get('date_planned', False)) and update: self._compute_planned_workcenter(cr, uid, ids, context=context, mini=mini) for d in direction: if direction[d] == 1: From ba896aabe7dad10f3108a4b83c4d8e304ae0935f Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Tue, 19 May 2015 16:27:50 +0200 Subject: [PATCH 23/36] [FIX] stock: if cost_method of a product is 'average' do not round the currency conversion This prevents rounding errors if product decimal precision is different from currency precision. Fixes #6547 opw-634390 Do not fowardport since already done in v8.0: https://github.com/odoo/odoo/blob/8.0/addons/stock_account/stock_account.py#L310 --- addons/stock/stock.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index f01e0995acc..16deddcca5b 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2354,7 +2354,11 @@ class stock_move(osv.osv): new_std_price = new_price else: # Get the standard price - amount_unit = product.price_get('standard_price', context=context)[product.id] + pricetype_obj = self.pool.get('product.price.type') + price_type_id = pricetype_obj.search(cr, uid, [('field','=','standard_price')])[0] + price_type_currency_id = pricetype_obj.browse(cr, uid, price_type_id).currency_id.id + amount_unit = self.pool.get('res.currency').compute(cr, uid, price_type_currency_id, + context['currency_id'], product.standard_price, round=False, context=context) new_std_price = ((amount_unit * product_avail[product.id])\ + (new_price * qty))/(product_avail[product.id] + qty) From 2e123fbb854dce26d1ddc34c63dbfd959b709989 Mon Sep 17 00:00:00 2001 From: Joao Alfredo Gama Batista Date: Wed, 20 May 2015 11:22:25 -0400 Subject: [PATCH 24/36] [FIX] Fix exception translation --- addons/account/account_move_line.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/account/account_move_line.py b/addons/account/account_move_line.py index 9f85f58fb00..cfc883d2a5e 100644 --- a/addons/account/account_move_line.py +++ b/addons/account/account_move_line.py @@ -1119,7 +1119,7 @@ class account_move_line(osv.osv): period = period_obj.browse(cr, uid, period_id, context=context) for (state,) in result: if state == 'done': - raise osv.except_osv(_('Error!'), _('You can not add/modify entries in a closed period %s of journal %s.' % (period.name,journal.name))) + raise osv.except_osv(_('Error!'), _('You can not add/modify entries in a closed period %s of journal %s.') % (period.name, journal.name)) if not result: jour_period_obj.create(cr, uid, { 'name': (journal.code or journal.name)+':'+(period.name or ''), From 49cb46fb78de4e7e88b054f2b5df21612778fd40 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 20 May 2015 19:13:04 +0200 Subject: [PATCH 25/36] Revert "[FIX] fields: `digits()` computation" This reverts commit eeedd2d9f52d46d8193059854e7430ca0c1fd6c0. This revision introduces an issue more serious than the ones it fixes. This is no longer possible to receive an email aimed a sale.order thread with catchall. To reproduce the issue: - Create a new sale order - Send a message in the thread to the customer - Reply to the mail received in the customer mailbox - Traceback, AttributeError: digits opw-640370 --- openerp/fields.py | 3 +-- openerp/osv/fields.py | 24 ++++++------------------ 2 files changed, 7 insertions(+), 20 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index cd25295d8e6..d780bf5adb3 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1054,8 +1054,7 @@ class Float(Field): @property def digits(self): if callable(self._digits): - with fields._get_cursor() as cr: - return self._digits(cr) + return self._digits(fields.get_cursor()) else: return self._digits diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 00eba168552..02d24fdf47b 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -43,7 +43,6 @@ import pytz import re import xmlrpclib from operator import itemgetter -from contextlib import contextmanager from psycopg2 import Binary import openerp @@ -53,22 +52,13 @@ from openerp.tools import float_repr, float_round, frozendict, html_sanitize import simplejson from openerp import SUPERUSER_ID, registry -@contextmanager -def _get_cursor(): - # yield a valid cursor from any environment or create a new one if none found +def get_cursor(): + # retrieve a valid cursor from any environment from openerp.api import Environment - from openerp.http import request - try: - request.env # force request's env to be computed - except RuntimeError: - pass # ignore if not in a request for env in Environment.envs: if not env.cr.closed: - yield env.cr - break - else: - with registry().cursor() as cr: - yield cr + return env.cr + raise RuntimeError("No valid cursor found") EMPTY_DICT = frozendict() @@ -406,8 +396,7 @@ class float(_column): @property def digits(self): if self._digits_compute: - with _get_cursor() as cr: - return self._digits_compute(cr) + return self._digits_compute(get_cursor()) else: return self._digits @@ -1327,8 +1316,7 @@ class function(_column): @property def digits(self): if self._digits_compute: - with _get_cursor() as cr: - return self._digits_compute(cr) + return self._digits_compute(get_cursor()) else: return self._digits From 5a10903e9b3b3f8143a8f8d3703632e3243efead Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Wed, 20 May 2015 12:38:44 +0200 Subject: [PATCH 26/36] [FIX] point_of_sale: float format The POS must adapt the float format according to the language of the user. Inspired from 1da5d89ba32526f241b3a0ce82b25a2fa9ee858f opw:639567 --- addons/point_of_sale/static/src/js/screens.js | 10 +++++++++- addons/point_of_sale/static/src/js/widget_base.js | 1 + addons/point_of_sale/static/src/xml/pos.xml | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index 6a813f2d574..b96af46757c 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -1008,6 +1008,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa },this); this.bind_events(); + this.decimal_point = instance.web._t.database.parameters.decimal_point; this.line_delete_handler = function(event){ var node = this; @@ -1026,7 +1027,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa node = node.parentNode; } if(node){ - node.line.set_amount(this.value); + var amount; + try{ + amount = instance.web.parse_value(this.value, {type: "float"}); + } + catch(e){ + amount = 0; + } + node.line.set_amount(amount); } }; diff --git a/addons/point_of_sale/static/src/js/widget_base.js b/addons/point_of_sale/static/src/js/widget_base.js index 934cc130a6c..5d1ff78fab2 100644 --- a/addons/point_of_sale/static/src/js/widget_base.js +++ b/addons/point_of_sale/static/src/js/widget_base.js @@ -29,6 +29,7 @@ function openerp_pos_basewidget(instance, module){ //module is instance.point_of if (typeof amount === 'number') { amount = round_di(amount,decimals).toFixed(decimals); + amount = openerp.instances[this.session.name].web.format_value(parseFloat(amount), { type : 'float' }); } if (currency.position === 'after') { diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index c9dd59305ba..0a7ced2cd26 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -873,7 +873,10 @@
    - + From 2c0529858fb2119252a4c04a0018ee6adaa2d520 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Thu, 21 May 2015 10:23:56 +0200 Subject: [PATCH 27/36] [FIX] point_of_sale: float format The POS must adapt the float format according to the language of the user. Inspired from 1da5d89ba32526f241b3a0ce82b25a2fa9ee858f opw:639567 --- addons/point_of_sale/static/src/js/screens.js | 10 +++++++++- addons/point_of_sale/static/src/js/widget_base.js | 1 + addons/point_of_sale/static/src/xml/pos.xml | 5 ++++- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/addons/point_of_sale/static/src/js/screens.js b/addons/point_of_sale/static/src/js/screens.js index cbd6779f33c..11641c36614 100644 --- a/addons/point_of_sale/static/src/js/screens.js +++ b/addons/point_of_sale/static/src/js/screens.js @@ -885,6 +885,7 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa },this); this.bind_events(); + this.decimal_point = instance.web._t.database.parameters.decimal_point; this.line_delete_handler = function(event){ var node = this; @@ -903,7 +904,14 @@ function openerp_pos_screens(instance, module){ //module is instance.point_of_sa node = node.parentNode; } if(node){ - node.line.set_amount(this.value); + var amount; + try{ + amount = instance.web.parse_value(this.value, {type: "float"}); + } + catch(e){ + amount = 0; + } + node.line.set_amount(amount); } }; diff --git a/addons/point_of_sale/static/src/js/widget_base.js b/addons/point_of_sale/static/src/js/widget_base.js index 443d66647a1..5d676e53d37 100644 --- a/addons/point_of_sale/static/src/js/widget_base.js +++ b/addons/point_of_sale/static/src/js/widget_base.js @@ -31,6 +31,7 @@ function openerp_pos_basewidget(instance, module){ //module is instance.point_of if(typeof amount === 'number'){ amount = Math.round(amount*100)/100; amount = amount.toFixed(decimals); + amount = openerp.instances[this.session.name].web.format_value(parseFloat(amount), { type : 'float' }); } if(this.currency.position === 'after'){ return amount + ' ' + (this.currency.symbol || ''); diff --git a/addons/point_of_sale/static/src/xml/pos.xml b/addons/point_of_sale/static/src/xml/pos.xml index 1afd0fe4359..68c07c25a01 100644 --- a/addons/point_of_sale/static/src/xml/pos.xml +++ b/addons/point_of_sale/static/src/xml/pos.xml @@ -588,7 +588,10 @@
    - + From 245b5afcfdb0b86f88637fdab901b8307a97a16c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20B=C3=A9al?= Date: Tue, 12 May 2015 14:36:27 +0200 Subject: [PATCH 28/36] [FIX] base_import: prioritise field name over string When importing csv data, the column name is matched on the field name and string attribute. For some fields (e.g. name & display_name), you could get a match on both the string and technical field name for different fields The order of the fields is not deterministic as stored in a dictionnary so different results were possible at different import. The technical name should be prioritised (more stable, unique constraint). Fixes #6657 --- addons/base_import/models.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/addons/base_import/models.py b/addons/base_import/models.py index 9778391521a..784ae7f49f1 100644 --- a/addons/base_import/models.py +++ b/addons/base_import/models.py @@ -158,12 +158,19 @@ class ir_import(orm.TransientModel): all the fields to traverse :rtype: list(Field) """ + string_match = None for field in fields: # FIXME: should match all translations & original # TODO: use string distance (levenshtein? hamming?) - if header.lower() == field['name'].lower() \ - or header.lower() == field['string'].lower(): + if header.lower() == field['name'].lower(): return [field] + if header.lower() == field['string'].lower(): + # matching string are not reliable way because + # strings have no unique constraint + string_match = field + if string_match: + # this behavior is only applied if there is no matching field['name'] + return [string_match] if '/' not in header: return [] From e2db33a5d448023b867e6f0957a5e8937ed34e77 Mon Sep 17 00:00:00 2001 From: Samuel Lefever Date: Tue, 14 Apr 2015 20:02:37 +0200 Subject: [PATCH 29/36] [ADD] CLA for samuellefever --- doc/cla/individual/samuellefever.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/samuellefever.md diff --git a/doc/cla/individual/samuellefever.md b/doc/cla/individual/samuellefever.md new file mode 100644 index 00000000000..83fb7bc6bd5 --- /dev/null +++ b/doc/cla/individual/samuellefever.md @@ -0,0 +1,11 @@ +Belgium, 2015-04-14 + +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, + +Samuel Lefever sam@niboo.be https://github.com/samuellefever \ No newline at end of file From f57f8c87ab9fa84992d2ecac28c507b4406747f7 Mon Sep 17 00:00:00 2001 From: Samuel Lefever Date: Tue, 14 Apr 2015 19:47:48 +0200 Subject: [PATCH 30/36] [FIX] website : reset lang variable in template In website template, it was not possible to use the variable "lang", containing the current language, as the variable was overwrote in website.layout, in a loop context. Changing the variable name used by the loop solves the issue. opw-639488 closes #6320 --- addons/website/views/website_templates.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/website/views/website_templates.xml b/addons/website/views/website_templates.xml index ea943c6b31c..56c8b941e27 100644 --- a/addons/website/views/website_templates.xml +++ b/addons/website/views/website_templates.xml @@ -85,8 +85,8 @@ - - + + From eaaca6559429a8d36c7f6dc1f8232cf222ad2a32 Mon Sep 17 00:00:00 2001 From: Nicolas Martinelli Date: Thu, 21 May 2015 11:35:13 +0200 Subject: [PATCH 31/36] [FIX] mail: does not write "False" if mail body is empty opw-640309 --- addons/mail/wizard/mail_compose_message.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py index 7e269ab7379..733057de1cf 100644 --- a/addons/mail/wizard/mail_compose_message.py +++ b/addons/mail/wizard/mail_compose_message.py @@ -234,7 +234,7 @@ class mail_compose_message(osv.TransientModel): # mail.message values, according to the wizard options post_values = { 'subject': wizard.subject, - 'body': wizard.body, + 'body': wizard.body or '', 'parent_id': wizard.parent_id and wizard.parent_id.id, 'partner_ids': [partner.id for partner in wizard.partner_ids], 'attachment_ids': [attach.id for attach in wizard.attachment_ids], From 6e54c8d17a0eee73d0f0eea6a1ad8ab4098716b5 Mon Sep 17 00:00:00 2001 From: Colin Newell Date: Wed, 20 May 2015 20:14:51 +0100 Subject: [PATCH 32/36] [FIX] web: avoid marking session as modified every time The `rotate` flag introduced by 31d817e849719ed893a5691d66eb136b481beb5b was initialized at the very end of the session init, after the reset of the `modified` flag. This had the side-effect of marking the session as modified for every request, saving the session to disk every time even without any change. Closes #6795 --- openerp/http.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/openerp/http.py b/openerp/http.py index f4386a41dd3..e65ce3e1a6c 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -913,11 +913,11 @@ class OpenERPSession(werkzeug.contrib.sessions.Session): def __init__(self, *args, **kwargs): self.inited = False self.modified = False + self.rotate = False super(OpenERPSession, self).__init__(*args, **kwargs) self.inited = True self._default_values() self.modified = False - self.rotate = False def __getattr__(self, attr): return self.get(attr, None) From 291119c802af576a09cc1ac0eb479ea7f592b8f8 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 21 May 2015 12:49:36 +0200 Subject: [PATCH 33/36] [FIX] http: `request.env` and request.cr` now raise `RuntimeError`. These method used to raise an `AttributeError` when `self.registry` returns `None`. Now raises a more appropriated exception. --- openerp/http.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openerp/http.py b/openerp/http.py index e65ce3e1a6c..6ac8c23695d 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -201,7 +201,11 @@ class WebRequest(object): def env(self): """ The :class:`~openerp.api.Environment` bound to current request. + Raises a :class:`RuntimeError` if the current requests is not bound + to a database. """ + if not self.db: + return RuntimeError('request not bound to a database') return openerp.api.Environment(self.cr, self.uid, self.context) @lazy_property @@ -236,6 +240,8 @@ class WebRequest(object): """ # can not be a lazy_property because manual rollback in _call_function # if already set (?) + if not self.db: + return RuntimeError('request not bound to a database') if not self._cr: self._cr = self.registry.cursor() return self._cr From 1cf5723835235c71df140938282420360116f0c5 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 21 May 2015 12:53:56 +0200 Subject: [PATCH 34/36] Revert "Revert "[FIX] fields: `digits()` computation"" Thamks to parent commit, `request.env` doesn't raise `AttributeError` anymore for requests without session bound to a database. This exception was bubbling up to `digits` property (and `__getattr__`) This reverts commit 49cb46fb78de4e7e88b054f2b5df21612778fd40. This reinstate commit eeedd2d9f52d46d8193059854e7430ca0c1fd6c0. --- openerp/fields.py | 3 ++- openerp/osv/fields.py | 24 ++++++++++++++++++------ 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index d780bf5adb3..cd25295d8e6 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1054,7 +1054,8 @@ class Float(Field): @property def digits(self): if callable(self._digits): - return self._digits(fields.get_cursor()) + with fields._get_cursor() as cr: + return self._digits(cr) else: return self._digits diff --git a/openerp/osv/fields.py b/openerp/osv/fields.py index 02d24fdf47b..00eba168552 100644 --- a/openerp/osv/fields.py +++ b/openerp/osv/fields.py @@ -43,6 +43,7 @@ import pytz import re import xmlrpclib from operator import itemgetter +from contextlib import contextmanager from psycopg2 import Binary import openerp @@ -52,13 +53,22 @@ from openerp.tools import float_repr, float_round, frozendict, html_sanitize import simplejson from openerp import SUPERUSER_ID, registry -def get_cursor(): - # retrieve a valid cursor from any environment +@contextmanager +def _get_cursor(): + # yield a valid cursor from any environment or create a new one if none found from openerp.api import Environment + from openerp.http import request + try: + request.env # force request's env to be computed + except RuntimeError: + pass # ignore if not in a request for env in Environment.envs: if not env.cr.closed: - return env.cr - raise RuntimeError("No valid cursor found") + yield env.cr + break + else: + with registry().cursor() as cr: + yield cr EMPTY_DICT = frozendict() @@ -396,7 +406,8 @@ class float(_column): @property def digits(self): if self._digits_compute: - return self._digits_compute(get_cursor()) + with _get_cursor() as cr: + return self._digits_compute(cr) else: return self._digits @@ -1316,7 +1327,8 @@ class function(_column): @property def digits(self): if self._digits_compute: - return self._digits_compute(get_cursor()) + with _get_cursor() as cr: + return self._digits_compute(cr) else: return self._digits From 8a430c4a238912b45e3c4d805bb78a1131dd7612 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 21 May 2015 13:46:38 +0200 Subject: [PATCH 35/36] [FIX] base: ir.http: only server_attachement for 404 errors fixes #6792 --- openerp/addons/base/ir/ir_http.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/openerp/addons/base/ir/ir_http.py b/openerp/addons/base/ir/ir_http.py index ba402b5b79d..5320a0c0b95 100644 --- a/openerp/addons/base/ir/ir_http.py +++ b/openerp/addons/base/ir/ir_http.py @@ -137,9 +137,10 @@ class ir_http(osv.AbstractModel): def _handle_exception(self, exception): # This is done first as the attachment path may # not match any HTTP controller. - attach = self._serve_attachment() - if attach: - return attach + if isinstance(exception, werkzeug.exceptions.HTTPException) and exception.code == 404: + attach = self._serve_attachment() + if attach: + return attach # If handle_exception returns something different than None, it will be used as a response try: From 02b8be4a8b84fdfe0c6dba116f14c67642eca7d9 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 21 May 2015 13:54:39 +0200 Subject: [PATCH 36/36] [FIX] mrp: restore removed method _check_phantom_bom Was removed at 5b479e6 as no longer used (and was a wrongly used there as dit not check if a "normal" BoM has an higher sequence). However for stablility reason, we do not remove methods in version 8.0 (if was used in community). Will be removed in master. --- addons/mrp/stock.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/addons/mrp/stock.py b/addons/mrp/stock.py index ed6c5c4049c..5af185266ec 100644 --- a/addons/mrp/stock.py +++ b/addons/mrp/stock.py @@ -43,6 +43,23 @@ class StockMove(osv.osv): if move.raw_material_production_id and move.location_dest_id.usage == 'production' and move.raw_material_production_id.product_id.track_production and not move.consumed_for: raise osv.except_osv(_('Warning!'), _("Because the product %s requires it, you must assign a serial number to your raw material %s to proceed further in your production. Please use the 'Produce' button to do so.") % (move.raw_material_production_id.product_id.name, move.product_id.name)) + # TODO master: remove me, no longer used + def _check_phantom_bom(self, cr, uid, move, context=None): + """check if product associated to move has a phantom bom + return list of ids of mrp.bom for that product """ + user_company = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + #doing the search as SUPERUSER because a user with the permission to write on a stock move should be able to explode it + #without giving him the right to read the boms. + domain = [ + '|', ('product_id', '=', move.product_id.id), + '&', ('product_id', '=', False), ('product_tmpl_id.product_variant_ids', '=', move.product_id.id), + ('type', '=', 'phantom'), + '|', ('date_start', '=', False), ('date_start', '<=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)), + '|', ('date_stop', '=', False), ('date_stop', '>=', time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)), + ('company_id', '=', user_company)] + return self.pool.get('mrp.bom').search(cr, SUPERUSER_ID, domain, context=context) + + def _action_explode(self, cr, uid, move, context=None): """ Explodes pickings. @param move: Stock moves