From f7b9046c7b57272e7504ad19137515951c0e9194 Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 20 Feb 2015 11:16:45 +0100 Subject: [PATCH 01/87] Add a failing test to reproduce #5398 --- openerp/addons/test_inherits/__init__.py | 2 + openerp/addons/test_inherits/__openerp__.py | 16 ++++++++ openerp/addons/test_inherits/demo_data.xml | 19 +++++++++ .../addons/test_inherits/ir.model.access.csv | 4 ++ openerp/addons/test_inherits/models.py | 41 +++++++++++++++++++ .../addons/test_inherits/tests/__init__.py | 3 ++ .../test_inherits/tests/test_inherits.py | 17 ++++++++ 7 files changed, 102 insertions(+) create mode 100644 openerp/addons/test_inherits/__init__.py create mode 100644 openerp/addons/test_inherits/__openerp__.py create mode 100644 openerp/addons/test_inherits/demo_data.xml create mode 100644 openerp/addons/test_inherits/ir.model.access.csv create mode 100644 openerp/addons/test_inherits/models.py create mode 100644 openerp/addons/test_inherits/tests/__init__.py create mode 100644 openerp/addons/test_inherits/tests/test_inherits.py diff --git a/openerp/addons/test_inherits/__init__.py b/openerp/addons/test_inherits/__init__.py new file mode 100644 index 00000000000..a0fdc10fe11 --- /dev/null +++ b/openerp/addons/test_inherits/__init__.py @@ -0,0 +1,2 @@ +# -*- coding: utf-8 -*- +from . import models diff --git a/openerp/addons/test_inherits/__openerp__.py b/openerp/addons/test_inherits/__openerp__.py new file mode 100644 index 00000000000..21010738f8e --- /dev/null +++ b/openerp/addons/test_inherits/__openerp__.py @@ -0,0 +1,16 @@ +# -*- coding: utf-8 -*- +{ + 'name': 'test-inherits', + 'version': '0.1', + 'category': 'Tests', + 'description': """A module to verify the inheritance using _inherits.""", + 'author': 'Camptocamp', + 'website': 'http://www.camptocamp.com', + 'depends': ['base'], + 'data': [ + 'ir.model.access.csv', + 'demo_data.xml', + ], + 'installable': True, + 'auto_install': False, +} diff --git a/openerp/addons/test_inherits/demo_data.xml b/openerp/addons/test_inherits/demo_data.xml new file mode 100644 index 00000000000..98b19095acd --- /dev/null +++ b/openerp/addons/test_inherits/demo_data.xml @@ -0,0 +1,19 @@ + + + + Unit A + a + + + + + A + + + + + A + + + + diff --git a/openerp/addons/test_inherits/ir.model.access.csv b/openerp/addons/test_inherits/ir.model.access.csv new file mode 100644 index 00000000000..22ecf366e31 --- /dev/null +++ b/openerp/addons/test_inherits/ir.model.access.csv @@ -0,0 +1,4 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_test_unit,access_test_unit,model_test_unit,,1,1,1,1 +access_test_box,access_test_box,model_test_box,,1,1,1,1 +access_test_pallet,access_test_pallet,model_test_pallet,,1,1,1,1 diff --git a/openerp/addons/test_inherits/models.py b/openerp/addons/test_inherits/models.py new file mode 100644 index 00000000000..c8f86ec52d7 --- /dev/null +++ b/openerp/addons/test_inherits/models.py @@ -0,0 +1,41 @@ +# -*- coding: utf-8 -*- +from openerp import models, fields, api, osv + + +# We just create a new model +class Unit(models.Model): + _name = 'test.unit' + + _columns = { + 'name': osv.fields.char('Name', required=True), + 'state': osv.fields.selection([('a', 'A'), ('b', 'B')], + string='State'), + } + + surname = fields.Char(compute='_compute_surname') + + @api.one + @api.depends('name') + def _compute_surname(self): + self.surname = self.name or '' + + +# We want to _inherits from the parent model and we add some fields +# in the child object +class Box(models.Model): + _name = 'test.box' + _inherits = {'test.unit': 'unit_id'} + + unit_id = fields.Many2one('test.unit', 'Unit', required=True, + ondelete='cascade') + field_in_box = fields.Char('Field1') + + +# We add a third level of _inherits +class Pallet(models.Model): + _name = 'test.pallet' + _inherits = {'test.box': 'box_id'} + + box_id = fields.Many2one('test.box', 'Box', required=True, + ondelete='cascade') + field_in_pallet = fields.Char('Field2') diff --git a/openerp/addons/test_inherits/tests/__init__.py b/openerp/addons/test_inherits/tests/__init__.py new file mode 100644 index 00000000000..694d1720d65 --- /dev/null +++ b/openerp/addons/test_inherits/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import test_inherits diff --git a/openerp/addons/test_inherits/tests/test_inherits.py b/openerp/addons/test_inherits/tests/test_inherits.py new file mode 100644 index 00000000000..1fcdbb2f3ec --- /dev/null +++ b/openerp/addons/test_inherits/tests/test_inherits.py @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +from openerp.tests import common + + +class test_inherits(common.TransactionCase): + + def test_create_3_levels_inherits(self): + """ Check that we can create an inherits on 3 levels """ + pallet = self.env['test.pallet'].create({ + 'name': 'B', + 'field_in_box': 'box', + 'field_in_pallet': 'pallet', + }) + self.assertTrue(pallet) + self.assertEqual(pallet.name, 'B') + self.assertEqual(pallet.field_in_box, 'box') + self.assertEqual(pallet.field_in_pallet, 'pallet') From 461c93081d54db2fa08a166837140ae8e59f3d8f Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Fri, 20 Feb 2015 11:25:57 +0100 Subject: [PATCH 02/87] Add a failing test for write as well --- openerp/addons/test_inherits/tests/test_inherits.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/openerp/addons/test_inherits/tests/test_inherits.py b/openerp/addons/test_inherits/tests/test_inherits.py index 1fcdbb2f3ec..7ff5dee814b 100644 --- a/openerp/addons/test_inherits/tests/test_inherits.py +++ b/openerp/addons/test_inherits/tests/test_inherits.py @@ -15,3 +15,9 @@ class test_inherits(common.TransactionCase): self.assertEqual(pallet.name, 'B') self.assertEqual(pallet.field_in_box, 'box') self.assertEqual(pallet.field_in_pallet, 'pallet') + + def test_write_3_levels_inherits(self): + """ Check that we can create an inherits on 3 levels """ + pallet = self.env.ref('test_inherits.pallet_a') + pallet.write({'name': 'C'}) + self.assertEqual(pallet.name, 'C') From 608e58cce13e51d40e6b741dd8ea336fc9cfcb5a Mon Sep 17 00:00:00 2001 From: Guewen Baconnier Date: Tue, 24 Feb 2015 10:34:09 +0100 Subject: [PATCH 03/87] Reload fields of parent inherits, fixes #5398 --- openerp/models.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openerp/models.py b/openerp/models.py index 3a8cb88423e..8f0ac390d3e 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -2896,6 +2896,7 @@ class BaseModel(object): cls._inherit_fields = struct = {} for parent_model, parent_field in cls._inherits.iteritems(): parent = cls.pool[parent_model] + parent._inherits_reload() for name, column in parent._columns.iteritems(): struct[name] = (parent_model, parent_field, column, parent_model) for name, source in parent._inherit_fields.iteritems(): From d776711e6273ee5a67bdfc05785c01bbfc5bf374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Tue, 24 Feb 2015 15:44:32 +0100 Subject: [PATCH 04/87] [FIX]website_event: track website_published field if you want to trigger subtypes. This field is defined in the website_publish mixin; it is redefined here just to add the track_visibility parameter. --- addons/website_event/models/event.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/website_event/models/event.py b/addons/website_event/models/event.py index 31644790e25..6b5f527a026 100644 --- a/addons/website_event/models/event.py +++ b/addons/website_event/models/event.py @@ -18,6 +18,7 @@ class event(models.Model): } twitter_hashtag = fields.Char('Twitter Hashtag', default=lambda self: self._default_hashtag()) + website_published = fields.Boolean(track_visibility='onchange') # TDE TODO FIXME: when website_mail/mail_thread.py inheritance work -> this field won't be necessary website_message_ids = fields.One2many( 'mail.message', 'res_id', From 38fb6119b486732e7eb5c2034a35b7379dd9b793 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Tue, 24 Feb 2015 16:50:57 +0100 Subject: [PATCH 05/87] [FIX] Web: base.sass: 4 spaces indentation base.sass did not compile because those lines where indented using 2 spaces instead of 4. --- addons/web/static/src/css/base.css | 4 ++-- addons/web/static/src/css/base.sass | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index bf13ccdb012..3dfed2985a5 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -35,7 +35,7 @@ border: 0; } -.oe_indicator{ +.oe_indicator { z-index: 99999; position: absolute; top: 10px; @@ -541,7 +541,7 @@ background-image: -webkit-radial-gradient(circle, #eee 0%, #ccc 40%, #bbb 100%); background-image: -moz-radial-gradient(#eee 0%, #ccc 40%, #bbb 100%); background-image: -ms-radial-gradient(#eee 0%, #ccc 40%, #bbb 100%); - background-image: radial-gradient(circle, #eee 0%, #ccc 40%, #bbb 100%); + background-image: radial-gradient(circle, #eeeeee 0%, #cccccc 40%, #bbbbbb 100%); } .openerp .oe_kanban_status_green { background: green; diff --git a/addons/web/static/src/css/base.sass b/addons/web/static/src/css/base.sass index 368fc53e5fb..9659cd5e483 100644 --- a/addons/web/static/src/css/base.sass +++ b/addons/web/static/src/css/base.sass @@ -146,16 +146,16 @@ $sheet-padding: 16px // Notifications {{{ .oe_indicator - z-index: 99999 - position: absolute - top: 10px - display: inline-block - color: white - background: #a24689 - padding: 6px 16px - border-radius: 4px - box-shadow: 0 0 1px black - left: 40% + z-index: 99999 + position: absolute + top: 10px + display: inline-block + color: white + background: #a24689 + padding: 6px 16px + border-radius: 4px + box-shadow: 0 0 1px black + left: 40% .oe_notification z-index: 1600 From 22606c89057cb70ecf8f379144092319bc7275e2 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Tue, 24 Feb 2015 17:33:35 +0100 Subject: [PATCH 06/87] [FIX] report: abstract reports default lang In ir_ui_view.py, in method render (line 132 atm), the values passed to the rendering engine is a merge of the context and the values. Therefore, if at this place, the language is rightly set in the context, the report lang will be as well in the values. In abstract_report.py, the values passed to the render method is the wrapped report localcontext in which are added some key/values (docs, doc_ids, doc_model). By default, the lang in the localcontext is False See __init__ method of rml_parse class in report_sxw.py. If setLang method is not called, the lang in the localcontext remains False. In this rev., we avoid to overwrite the lang from the context by the lang of the localcontext if this one is False, so the lang of the report is set with the current context lang. Forcing the lang of the report to False had as side-effect to prevent the editing of report using the website editor(e.g. playslip_report) opw-628720 --- addons/report/models/abstract_report.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/addons/report/models/abstract_report.py b/addons/report/models/abstract_report.py index 08d70e83e20..cccaa339b63 100644 --- a/addons/report/models/abstract_report.py +++ b/addons/report/models/abstract_report.py @@ -54,7 +54,9 @@ class AbstractReport(osv.AbstractModel): # Rendering self._template with the wrapped report instance localcontext as # rendering environment - docargs = wrapped_report.localcontext + docargs = dict(wrapped_report.localcontext) + if not docargs.get('lang'): + docargs.pop('lang', False) docargs['docs'] = docargs.get('objects') # Used in template translation (see translate_doc method from report model) From 3050e35d31d30a0329dd2abdeb0875edcfdbb9a4 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Tue, 24 Feb 2015 18:18:25 +0100 Subject: [PATCH 07/87] [FIX] google_calendar: don't send mail from calendar when date has been changed. If a meeting has been changed in google, a mail has been already sent. --- addons/google_calendar/google_calendar.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/google_calendar/google_calendar.py b/addons/google_calendar/google_calendar.py index 1e40b708b58..14847b5402d 100644 --- a/addons/google_calendar/google_calendar.py +++ b/addons/google_calendar/google_calendar.py @@ -502,6 +502,7 @@ class google_calendar(osv.AbstractModel): rrule = [rule for rule in single_event_dict["recurrence"] if rule.startswith("RRULE:")][0][6:] result['rrule'] = rrule + context = dict(context or {}, no_mail_to_attendees=True) if type == "write": res = calendar_event.write(cr, uid, event['id'], result, context=context) elif type == "copy": From cb6e27c992e514083b751f58b74edd20bd4f58a0 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 20 Feb 2015 16:31:36 +0100 Subject: [PATCH 08/87] [IMP] When invoicing from multiple pickings, services should be checked for all purchases/sales [FIX] Should make sure purchase order lines are not invoiced twice --- addons/purchase/stock.py | 26 ++++++++++++++------------ addons/sale_stock/sale_stock.py | 14 +++++++------- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index cf7b445fbf6..2b415f0115f 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -69,6 +69,20 @@ class stock_move(osv.osv): self.pool.get('purchase.order').write(cr, uid, [purchase_line.order_id.id], { 'invoice_ids': [(4, invoice_line_vals['invoice_id'])], }) + purchase_line_obj = self.pool.get('purchase.order.line') + purchase_obj = self.pool.get('purchase.order') + invoice_line_obj = self.pool.get('account.invoice.line') + purchase_id = move.purchase_line_id.order_id.id + purchase_line_ids = purchase_line_obj.search(cr, uid, [('order_id', '=', purchase_id), ('product_id.type', '=', 'service'), ('invoice_lines', '=', False)], context=context) + if purchase_line_ids: + inv_lines = [] + for po_line in purchase_line_obj.browse(cr, uid, purchase_line_ids, context=context): + acc_id = purchase_obj._choose_account_from_po_line(cr, uid, po_line, context=context) + inv_line_data = purchase_obj._prepare_inv_line(cr, uid, acc_id, po_line, context=context) + inv_line_id = invoice_line_obj.create(cr, uid, inv_line_data, context=context) + inv_lines.append(inv_line_id) + po_line.write({'invoice_lines': [(4, inv_line_id)]}) + invoice_line_obj.write(cr, uid, inv_lines, {'invoice_id': invoice_line_vals['invoice_id']}, context=context) return invoice_line_id def _get_master_data(self, cr, uid, move, company, context=None): @@ -148,18 +162,6 @@ class stock_picking(osv.osv): purchase_line_obj = self.pool.get('purchase.order.line') invoice_line_obj = self.pool.get('account.invoice.line') invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context) - if picking.move_lines and picking.move_lines[0].purchase_line_id: - purchase_id = picking.move_lines[0].purchase_line_id.order_id.id - purchase_line_ids = purchase_line_obj.search(cr, uid, [('order_id', '=', purchase_id), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context) - if purchase_line_ids: - inv_lines = [] - for po_line in purchase_line_obj.browse(cr, uid, purchase_line_ids, context=context): - acc_id = purchase_obj._choose_account_from_po_line(cr, uid, po_line, context=context) - inv_line_data = purchase_obj._prepare_inv_line(cr, uid, acc_id, po_line, context=context) - inv_line_id = invoice_line_obj.create(cr, uid, inv_line_data, context=context) - inv_lines.append(inv_line_id) - po_line.write({'invoice_lines': [(4, inv_line_id)]}) - invoice_line_obj.write(cr, uid, inv_lines, {'invoice_id': invoice_id}, context=context) return invoice_id def _get_invoice_vals(self, cr, uid, key, inv_type, journal_id, move, context=None): diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index 66e01cd99a5..b6250abdfe8 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -368,6 +368,13 @@ class stock_move(osv.osv): self.pool.get('sale.order').write(cr, uid, [sale_line.order_id.id], { 'invoice_ids': [(4, invoice_line_vals['invoice_id'])], }) + sale_line_obj = self.pool.get('sale.order.line') + invoice_line_obj = self.pool.get('account.invoice.line') + sale_line_ids = sale_line_obj.search(cr, uid, [('order_id', '=', move.procurement_id.sale_line_id.order_id.id), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context) + if sale_line_ids: + created_lines = sale_line_obj.invoice_line_create(cr, uid, sale_line_ids, context=context) + invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_line_vals['invoice_id']}, context=context) + return invoice_line_id def _get_master_data(self, cr, uid, move, company, context=None): @@ -436,13 +443,6 @@ class stock_picking(osv.osv): sale_line_obj = self.pool.get('sale.order.line') invoice_line_obj = self.pool.get('account.invoice.line') invoice_id = super(stock_picking, self)._create_invoice_from_picking(cr, uid, picking, vals, context=context) - if picking.group_id: - sale_ids = sale_obj.search(cr, uid, [('procurement_group_id', '=', picking.group_id.id)], context=context) - if sale_ids: - sale_line_ids = sale_line_obj.search(cr, uid, [('order_id', 'in', sale_ids), ('product_id.type', '=', 'service'), ('invoiced', '=', False)], context=context) - if sale_line_ids: - created_lines = sale_line_obj.invoice_line_create(cr, uid, sale_line_ids, context=context) - invoice_line_obj.write(cr, uid, created_lines, {'invoice_id': invoice_id}, context=context) return invoice_id def _get_invoice_vals(self, cr, uid, key, inv_type, journal_id, move, context=None): From d416492259f08cff03004e8679bf63afde260ab2 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Wed, 18 Feb 2015 18:24:03 +0100 Subject: [PATCH 09/87] [IMP] To determine the invoice we put extra moves in, it is better to take the values from the sale order / purchase order if we can find it through the picking [IMP] Add comments [IMP] Simplify code --- addons/purchase/stock.py | 8 +++++++- addons/sale_stock/sale_stock.py | 4 ++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/addons/purchase/stock.py b/addons/purchase/stock.py index 2b415f0115f..c4d3ffe4b65 100644 --- a/addons/purchase/stock.py +++ b/addons/purchase/stock.py @@ -89,7 +89,13 @@ class stock_move(osv.osv): if move.purchase_line_id: purchase_order = move.purchase_line_id.order_id return purchase_order.partner_id, purchase_order.create_uid.id, purchase_order.currency_id.id - else: + elif move.picking_id: + # In case of an extra move, it is better to use the data from the original moves + for purchase_move in move.picking_id.move_lines: + if purchase_move.purchase_line_id: + purchase_order = purchase_move.purchase_line_id.order_id + return purchase_order.partner_id, purchase_order.create_uid.id, purchase_order.currency_id.id + partner = move.picking_id and move.picking_id.partner_id or False code = self.get_code_from_locs(cr, uid, move, context=context) if partner and partner.property_product_pricelist_purchase and code == 'incoming': diff --git a/addons/sale_stock/sale_stock.py b/addons/sale_stock/sale_stock.py index b6250abdfe8..cbac47c8e59 100644 --- a/addons/sale_stock/sale_stock.py +++ b/addons/sale_stock/sale_stock.py @@ -381,6 +381,10 @@ 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: + # 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 return super(stock_move, self)._get_master_data(cr, uid, move, company, context=context) def _get_invoice_line_vals(self, cr, uid, move, partner, inv_type, context=None): From 37f945961071c6905e5b4aadb629c2f1255018b8 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Tue, 17 Feb 2015 11:43:49 +0100 Subject: [PATCH 10/87] [IMP] Return of return should be linked with original destination move should fix #5253 [FIX] Split of return move should keep the returned move [IMP] Better comment and check for state not cancelled --- addons/stock/stock.py | 1 + addons/stock/wizard/stock_return_picking.py | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 9c1bb5dd55b..7a694f7cc32 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2514,6 +2514,7 @@ class stock_move(osv.osv): 'split_from': move.id, 'procurement_id': move.procurement_id.id, 'move_dest_id': move.move_dest_id.id, + 'origin_returned_move_id': move.origin_returned_move_id.id, } if context.get('source_location_id'): defaults['location_id'] = context['source_location_id'] diff --git a/addons/stock/wizard/stock_return_picking.py b/addons/stock/wizard/stock_return_picking.py index deb624ea797..29b83d9200f 100644 --- a/addons/stock/wizard/stock_return_picking.py +++ b/addons/stock/wizard/stock_return_picking.py @@ -132,6 +132,12 @@ class stock_return_picking(osv.osv_memory): raise osv.except_osv(_('Warning !'), _("You have manually created product lines, please delete them to proceed")) new_qty = data_get.quantity if new_qty: + # The return of a return should be linked with the original's destination move if it was not cancelled + if move.origin_returned_move_id.move_dest_id.id and move.origin_returned_move_id.move_dest_id.state != 'cancel': + move_dest_id = move.origin_returned_move_id.move_dest_id.id + else: + move_dest_id = False + returned_lines += 1 move_obj.copy(cr, uid, move.id, { 'product_id': data_get.product_id.id, @@ -144,6 +150,7 @@ class stock_return_picking(osv.osv_memory): 'origin_returned_move_id': move.id, 'procure_method': 'make_to_stock', 'restrict_lot_id': data_get.lot_id.id, + 'move_dest_id': move_dest_id, }) if not returned_lines: From 2a37a94a67d3af78c83562b0f011fe2f903eb53f Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Wed, 25 Feb 2015 17:03:17 +0530 Subject: [PATCH 11/87] [FIX] purchase: scope of variables is a bitch --- addons/purchase/purchase.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index a1f69fd0fe5..4919d3569ad 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -1429,8 +1429,8 @@ class procurement_order(osv.osv): po_line_ids = po_line_obj.search(cr, uid, [('order_id', '=', add_purchase), ('product_id', 'in', [x.product_id.id for x in procurements])], context=context) po_lines = po_line_obj.browse(cr, uid, po_line_ids, context=context) po_prod_dict = {} - for po in po_lines: - po_prod_dict[po.product_id.id] = po + for pol in po_lines: + po_prod_dict[po.product_id.id] = pol procs_to_create = [] #Check which procurements need a new line and which need to be added to an existing one for proc in procurements: From 22acc5d379ecec68f2c5148096aaa274f1bd6097 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Wed, 25 Feb 2015 18:26:04 +0100 Subject: [PATCH 12/87] [FIX] project_timesheet: retrocompatibility for 73f7a2ba35e21d60ef98fe8ad5f2a9d735afe196 It looks like it was possible to pass vals['date'] in date format (!= datetime format) to _create_analytic_entries. This rev. is a retrocompatible patch for 73f7a2ba35e21d60ef98fe8ad5f2a9d735afe196. In addition, it solves the same issue than the rev. 73f7a2ba35e21d60ef98fe8ad5f2a9d735afe196, but in the case the project is set on the task after the work hours are created. See ab5ecef476b82c7e00dbd1868de19832ac044f15 opw-628729 --- addons/project_timesheet/project_timesheet.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/addons/project_timesheet/project_timesheet.py b/addons/project_timesheet/project_timesheet.py index 2b04a1a4de3..cefb1e0ef40 100644 --- a/addons/project_timesheet/project_timesheet.py +++ b/addons/project_timesheet/project_timesheet.py @@ -118,9 +118,12 @@ class project_work(osv.osv): vals_line['user_id'] = vals['user_id'] vals_line['product_id'] = result['product_id'] if vals.get('date'): - timestamp = datetime.datetime.strptime(vals['date'], tools.DEFAULT_SERVER_DATETIME_FORMAT) - ts = fields.datetime.context_timestamp(cr, uid, timestamp, context) - vals_line['date'] = ts.strftime(tools.DEFAULT_SERVER_DATE_FORMAT) + if len(vals['date']) > 10: + timestamp = datetime.datetime.strptime(vals['date'], tools.DEFAULT_SERVER_DATETIME_FORMAT) + ts = fields.datetime.context_timestamp(cr, uid, timestamp, context) + vals_line['date'] = ts.strftime(tools.DEFAULT_SERVER_DATE_FORMAT) + else: + vals_line['date'] = vals['date'] # Calculate quantity based on employee's product's uom vals_line['unit_amount'] = vals['hours'] @@ -263,7 +266,7 @@ class task(osv.osv): missing_analytic_entries[task_work.id] = { 'name' : task_work.name, 'user_id' : task_work.user_id.id, - 'date' : task_work.date and task_work.date[:10] or False, + 'date' : task_work.date, 'account_id': acc_id, 'hours' : task_work.hours, 'task_id' : task_obj.id From fcbb825cd0d2b53283fbfbb2650aeafae825dbc4 Mon Sep 17 00:00:00 2001 From: Jeremy Kersten Date: Wed, 25 Feb 2015 18:47:59 +0100 Subject: [PATCH 13/87] [IMP] website_forum: replace 'My Profile' by user karma and remove useless code --- addons/website_forum/views/website_forum.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/addons/website_forum/views/website_forum.xml b/addons/website_forum/views/website_forum.xml index 3471417b66b..0ea9e319d0e 100644 --- a/addons/website_forum/views/website_forum.xml +++ b/addons/website_forum/views/website_forum.xml @@ -82,7 +82,9 @@ Badges
  • - My Profile + + (k: ) +
  • - + From bc9701a3fd636fce8591af5ebc759ef3cb10b0c9 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Wed, 25 Feb 2015 19:05:40 +0100 Subject: [PATCH 14/87] [FIX] sale_stock: correct sale.order report After 222fcb137bc9c25d15ee0ad851078caa99e16bdf, the main record has been rename to `doc`. --- addons/sale_stock/sale_stock_view.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/sale_stock/sale_stock_view.xml b/addons/sale_stock/sale_stock_view.xml index 68bb5635530..bb3ddda3e88 100644 --- a/addons/sale_stock/sale_stock_view.xml +++ b/addons/sale_stock/sale_stock_view.xml @@ -151,9 +151,9 @@ From c70b17fcbcb58728a123f760cd5b91238896c105 Mon Sep 17 00:00:00 2001 From: Martin Trigaux Date: Thu, 26 Feb 2015 10:16:52 +0530 Subject: [PATCH 15/87] [FIX] purchase: fixup of 2a37a94, missing rename --- addons/purchase/purchase.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 4919d3569ad..6122c46edb9 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -1430,7 +1430,7 @@ class procurement_order(osv.osv): po_lines = po_line_obj.browse(cr, uid, po_line_ids, context=context) po_prod_dict = {} for pol in po_lines: - po_prod_dict[po.product_id.id] = pol + po_prod_dict[pol.product_id.id] = pol procs_to_create = [] #Check which procurements need a new line and which need to be added to an existing one for proc in procurements: From e9b391c3080b8f0d12d0e7a7a36dc29f8d5a90d3 Mon Sep 17 00:00:00 2001 From: Fabrice Henrion Date: Thu, 26 Feb 2015 00:30:20 -0800 Subject: [PATCH 16/87] [FIX] base_import: typos closes #5478 --- addons/base_import/static/src/xml/import.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/base_import/static/src/xml/import.xml b/addons/base_import/static/src/xml/import.xml index 1f8bd6705a8..0f17765e5b2 100644 --- a/addons/base_import/static/src/xml/import.xml +++ b/addons/base_import/static/src/xml/import.xml @@ -209,9 +209,9 @@ (e.g. a customer that has multiple tags)?

    The tags should be separated by a comma without any - spacing. For example, if you want you customer to be - lined to both tags 'Manufacturer' and 'Retailer' - then you will encode it as follow "Manufacturer, + spacing. For example, if you want your customer to be + linked to both tags 'Manufacturer' and 'Retailer' + then you will encode "Manufacturer, Retailer" in the same column of your CSV file.

    CSV file for Manufacturer, Retailer
    From 73103b0545729c43ce3cc687cc8703150c9b9a07 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 26 Feb 2015 10:21:09 +0100 Subject: [PATCH 17/87] [FIX] project: fixed typos / wording in helps / tooltips. --- addons/project/project_tip_data.xml | 2 +- addons/project/project_view.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/project/project_tip_data.xml b/addons/project/project_tip_data.xml index aefdb33472f..ba920200a20 100644 --- a/addons/project/project_tip_data.xml +++ b/addons/project/project_tip_data.xml @@ -23,7 +23,7 @@ - This where you can track history and collaborate on tasks. Send a new message to notify followers or log an internal note to keep history but don’t notify followers. + This where you can track history and collaborate on tasks. Send a new message to notify followers or log an internal note to store information without notifying the followers. project.task form diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml index 01aaf42e378..8d8b18ae81c 100644 --- a/addons/project/project_view.xml +++ b/addons/project/project_view.xml @@ -729,7 +729,7 @@

    - You can also add a description to help your coworkers understanding the meaning and purpose of the stage. + You can also add a description to help your coworkers understand the meaning and purpose of the stage.

    From 6d9e0c071c087849b5067adab67406bfb0cf04ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 26 Feb 2015 10:23:43 +0100 Subject: [PATCH 18/87] [FIX] website_quote: wording in tips. --- addons/website_quote/data/quotation_tip_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/website_quote/data/quotation_tip_data.xml b/addons/website_quote/data/quotation_tip_data.xml index 61c03b25710..20c809e867c 100644 --- a/addons/website_quote/data/quotation_tip_data.xml +++ b/addons/website_quote/data/quotation_tip_data.xml @@ -3,7 +3,7 @@ - + sale.order form .oe_form_buttons_view:visible, header >.oe_button.oe_form_button:nth-child(3):not(.oe_form_invisible) From 10a1d443891001b1ba2094600e8635b59901f33d Mon Sep 17 00:00:00 2001 From: Vijay Baladaniya Date: Thu, 26 Feb 2015 11:17:06 +0530 Subject: [PATCH 19/87] [FIX] Change the target_version for anonymization. Fixed #5476 --- addons/anonymization/anonymization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/anonymization/anonymization.py b/addons/anonymization/anonymization.py index a6d14ec2e05..92a60469ac3 100644 --- a/addons/anonymization/anonymization.py +++ b/addons/anonymization/anonymization.py @@ -538,7 +538,7 @@ class ir_model_fields_anonymize_wizard(osv.osv_memory): data = pickle.loads(base64.decodestring(wizard.file_import)) migration_fix_obj = self.pool.get('ir.model.fields.anonymization.migration.fix') - fix_ids = migration_fix_obj.search(cr, uid, [('target_version', '=', '7.0')]) + fix_ids = migration_fix_obj.search(cr, uid, [('target_version', '=', '8.0')]) fixes = migration_fix_obj.read(cr, uid, fix_ids, ['model_name', 'field_name', 'query', 'query_type', 'sequence']) fixes = group(fixes, ('model_name', 'field_name')) From e332b2bb202ddfbc7427c54dd369ec12bbbfc2e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Thu, 26 Feb 2015 10:47:47 +0100 Subject: [PATCH 20/87] [FIX] view_form, web_kanban: KanbanSelection widget in form and kanban: tooltip now takes the stage custom tooltip if present, like the selection value, instead of the default one. --- addons/web/static/src/js/view_form.js | 1 + addons/web_kanban/static/src/js/kanban.js | 1 + 2 files changed, 2 insertions(+) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index b47a5b2c929..bed0cefbd9e 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2427,6 +2427,7 @@ instance.web.form.KanbanSelection = instance.web.form.FieldChar.extend({ } if (stage_data && stage_data[0][self.options.states_legend[res[0]]]) { value['state_name'] = stage_data[0][self.options.states_legend[res[0]]]; + value['tooltip'] = stage_data[0][self.options.states_legend[res[0]]]; } if (res[0] == 'normal') { value['state_class'] = 'oe_kanban_status'; } else if (res[0] == 'done') { value['state_class'] = 'oe_kanban_status oe_kanban_status_green'; } diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index d2bbb9045de..6b8843bd0f7 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -1367,6 +1367,7 @@ instance.web_kanban.KanbanSelection = instance.web_kanban.AbstractField.extend({ var leg_opt = self.options && self.options.states_legend || null; if (leg_opt && leg_opt[res[0]] && self.parent.group.values && self.parent.group.values[leg_opt[res[0]]]) { value['state_name'] = self.parent.group.values[leg_opt[res[0]]]; + value['tooltip'] = self.parent.group.values[leg_opt[res[0]]]; } if (res[0] == 'normal') { value['state_class'] = 'oe_kanban_status'; } else if (res[0] == 'done') { value['state_class'] = 'oe_kanban_status oe_kanban_status_green'; } From e700805a43916cc9a90a6a287990e95060a9f84c Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Thu, 26 Feb 2015 11:28:39 +0100 Subject: [PATCH 21/87] [FIX] ws doc: language switcher in some iOS versions --- doc/_themes/odoodoc/static/app.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/_themes/odoodoc/static/app.js b/doc/_themes/odoodoc/static/app.js index 64a77c15c40..16f5445d860 100644 --- a/doc/_themes/odoodoc/static/app.js +++ b/doc/_themes/odoodoc/static/app.js @@ -106,8 +106,10 @@ $(function () { // programming language switcher function findSheet(pattern, fromSheet) { if (fromSheet) { - for(var i=0; i Date: Thu, 26 Feb 2015 12:05:57 +0100 Subject: [PATCH 22/87] [FIX] account: set group on view instead of *in* view --- addons/account/partner_view.xml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/addons/account/partner_view.xml b/addons/account/partner_view.xml index ae488f3eb08..9c85d091a71 100644 --- a/addons/account/partner_view.xml +++ b/addons/account/partner_view.xml @@ -138,9 +138,10 @@ res.partner 2 + - + @@ -172,7 +173,7 @@ - +

    Accounting-related settings are managed on

    From bf31ab6718a8b9c8afdd29120e5056533b0a2ade Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 25 Feb 2015 17:46:38 +0100 Subject: [PATCH 23/87] [FIX] product.template: name_search compatibility with product.product In the rare places with a m2o field to product.template (e.g BoMs), it is necessary to be able to search on product codes, EAN, etc, the way it works on product.product. This is done by delegating the name_search to product.product and then returning the corresponding templates. This has a small penalty of executing name_get() twice, but is simpler and more robust for future changes. An alternative would have been to extract the name_search() method into a mixin and mix it on both product.product and product.template. However this would be more brittle and only work as long as the name_search implementation strictly uses fields that are present in both tables. OPW 626662 --- addons/product/product.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/addons/product/product.py b/addons/product/product.py index 5e0abcc691a..95e21eac7a7 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -22,8 +22,10 @@ import math import re import time +from collections import OrderedDict from _common import ceiling + from openerp import SUPERUSER_ID from openerp import tools from openerp.osv import osv, fields, expression @@ -797,9 +799,16 @@ class product_template(osv.osv): pass return super(product_template, self).name_get(cr, user, ids, context) - - - + def name_search(self, cr, user, name='', args=None, operator='ilike', context=None, limit=100): + product_product = self.pool['product.product'] + results = product_product.name_search( + cr, user, name, args, operator=operator, context=context, limit=limit) + product_ids = [p[0] for p in results] + template_ids = [p.product_tmpl_id.id + for p in product_product.browse( + cr, user, product_ids, context=context)] + uniq_ids = OrderedDict.fromkeys(template_ids).keys() + return self.name_get(cr, user, uniq_ids, context=context) class product_product(osv.osv): _name = "product.product" From 05bbe78d87eb143b52a54e5be50faa10cc5c7d24 Mon Sep 17 00:00:00 2001 From: Olivier Dony Date: Wed, 25 Feb 2015 17:52:03 +0100 Subject: [PATCH 24/87] [FIX] product.product: name_search() must preserve natural order The natural order was discarded when merging results with a set, and this was actually un-necessary as the second search() excludes the previously found `ids`. --- addons/product/product.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/addons/product/product.py b/addons/product/product.py index 95e21eac7a7..ad53e5dc3e0 100644 --- a/addons/product/product.py +++ b/addons/product/product.py @@ -1084,12 +1084,11 @@ class product_product(osv.osv): # on a database with thousands of matching products, due to the huge merge+unique needed for the # OR operator (and given the fact that the 'name' lookup results come from the ir.translation table # Performing a quick memory merge of ids in Python will give much better performance - ids = set(self.search(cr, user, args + [('default_code', operator, name)], limit=limit, context=context)) + ids = self.search(cr, user, args + [('default_code', operator, name)], limit=limit, context=context) if not limit or len(ids) < limit: # we may underrun the limit because of dupes in the results, that's fine limit2 = (limit - len(ids)) if limit else False - ids.update(self.search(cr, user, args + [('name', operator, name), ('id', 'not in', list(ids))], limit=limit2, context=context)) - ids = list(ids) + ids += self.search(cr, user, args + [('name', operator, name), ('id', 'not in', ids)], limit=limit2, context=context) elif not ids and operator in expression.NEGATIVE_TERM_OPERATORS: ids = self.search(cr, user, args + ['&', ('default_code', operator, name), ('name', operator, name)], limit=limit, context=context) if not ids and operator in positive_operators: From f99cea5f69d233c9ff6cfc98627f6abee9efde87 Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Wed, 25 Feb 2015 13:48:53 +0100 Subject: [PATCH 25/87] [FIX] versioning: override read_group in Experiment to always display all Experiment states. --- addons/website_version/models/experiment.py | 29 ++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/addons/website_version/models/experiment.py b/addons/website_version/models/experiment.py index e5cf5f11e6b..cfed7fd5af1 100644 --- a/addons/website_version/models/experiment.py +++ b/addons/website_version/models/experiment.py @@ -62,9 +62,31 @@ class Experiment(models.Model): if not exp_ver.version_id.website_id.id == exp.website_id.id: raise ValidationError('This experiment must have versions which are in the same website') - _group_by_full = { - 'state': lambda *args, **kwargs: ([('running', 'Running'), ('paused', 'Paused'), ('ended', 'Ended')], dict()) - } + def read_group(self, cr, uid, domain, fields, groupby, offset=0, limit=None, context=None, orderby=False, lazy=True): + """ Override read_group to always display all states. """ + if groupby and groupby[0] == "state": + # Default result structure + # states = self._get_state_list(cr, uid, context=context) + states = [('running', 'Running'), ('paused', 'Paused'), ('ended', 'Ended')] + read_group_all_states = [{ + '__context': {'group_by': groupby[1:]}, + '__domain': domain + [('state', '=', state_value)], + 'state': state_value, + 'state_count': 0, + } for state_value, state_name in states] + # Get standard results + read_group_res = super(Experiment, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby) + # Update standard results with default results + result = [] + for state_value, state_name in states: + res = filter(lambda x: x['state'] == state_value, read_group_res) + if not res: + res = filter(lambda x: x['state'] == state_value, read_group_all_states) + res[0]['state'] = [state_value, state_name] + result.append(res[0]) + return result + else: + return super(Experiment, self).read_group(cr, uid, domain, fields, groupby, offset=offset, limit=limit, context=context, orderby=orderby) @api.one def _get_version_number(self): @@ -127,6 +149,7 @@ class Experiment(models.Model): self.env['google.management'].delete_an_experiment(exp.google_id, exp.website_id.id) return super(Experiment, self).unlink() + @api.multi def update_goals(self): gm_obj = self.env['google.management'] goals_obj = self.env['website_version.goals'] From f0dc416871a40b49981c2ed617fbe8abc10a1d8f Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 26 Feb 2015 14:03:02 +0100 Subject: [PATCH 26/87] [FIX] phantomjs 2.0 compatibility phantomjs 2.0 remove the deprecated attribute `phantom.args`. Use the new `system` module. --- addons/web_tests/tests/test_ui_load.js | 3 ++- openerp/tests/phantomtest.js | 6 ++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/web_tests/tests/test_ui_load.js b/addons/web_tests/tests/test_ui_load.js index 9d76250d239..8daf8e876e0 100644 --- a/addons/web_tests/tests/test_ui_load.js +++ b/addons/web_tests/tests/test_ui_load.js @@ -1,4 +1,5 @@ // Load helper -phantom.injectJs(phantom.args[0]); +var system = require('system'); +phantom.injectJs(system.args[1]); pt = new PhantomTest(); pt.run("/", "console.log('ok')", "console"); diff --git a/openerp/tests/phantomtest.js b/openerp/tests/phantomtest.js index 49da52e07d9..d37d1ea0fab 100644 --- a/openerp/tests/phantomtest.js +++ b/openerp/tests/phantomtest.js @@ -1,6 +1,8 @@ // Phantomjs odoo helper // jshint evil: true, loopfunc: true +var system = require('system'); + function waitFor (condition, callback, timeout, timeoutMessageCallback) { timeout = timeout || 10000; var start = new Date(); @@ -19,7 +21,7 @@ function waitFor (condition, callback, timeout, timeoutMessageCallback) { function PhantomTest() { var self = this; - this.options = JSON.parse(phantom.args[phantom.args.length-1]); + this.options = JSON.parse(system.args[system.args.length-1]); this.inject = this.options.inject || []; this.timeout = this.options.timeout ? Math.round(parseFloat(this.options.timeout)*1000 - 5000) : 10000; this.origin = 'http://localhost'; @@ -143,7 +145,7 @@ function PhantomTest() { } // js mode or jsfile mode -if(phantom.args.length === 1) { +if(system.args.length === 2) { pt = new PhantomTest(); pt.run(pt.options.url_path, pt.options.code, pt.options.ready); } From efb3de5034d55211d1d92edf4231043423125c7c Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 25 Feb 2015 09:13:41 +0100 Subject: [PATCH 27/87] [FIX] Web: ListView and ListView.Groups: pagers * Fixes the pager on grouped list views for which there was no specific behavior (except of doing +1 or -1) for next and previous actions on last and first pages respectively. * Hides the previous and next buttons of the main pager on grouped list views as these buttons have no effect in grouped mode. --- addons/web/static/src/js/view_list.js | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/addons/web/static/src/js/view_list.js b/addons/web/static/src/js/view_list.js index d42d8ecb099..dcd6ae77117 100644 --- a/addons/web/static/src/js/view_list.js +++ b/addons/web/static/src/js/view_list.js @@ -404,7 +404,7 @@ instance.web.ListView = instance.web.View.extend( /** @lends instance.web.ListVi var total = dataset.size(); var limit = this.limit() || total; - this.$pager.find('.oe-pager-button').toggle(total > limit); + this.$pager.find('.oe-pager-buttons').toggle(total > limit); this.$pager.find('.oe_pager_value').toggle(total !== 0); var spager = '-'; if (total) { @@ -1301,7 +1301,11 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we var $prev = $('') .click(function (e) { e.stopPropagation(); - self.page -= 1; + if (self.page > 0) { + self.page -= 1; + } else { + self.page = self.max_page_index; + } self.$row.closest('tbody').next() .replaceWith(self.render()); @@ -1309,7 +1313,11 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we var $next = $('') .click(function (e) { e.stopPropagation(); - self.page += 1; + if (self.page < self.max_page_index) { + self.page += 1; + } else { + self.page = 0; + } self.$row.closest('tbody').next() .replaceWith(self.render()); @@ -1505,19 +1513,19 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we self.$row.find('td.oe_list_group_pagination').find('button').remove(); self.$row.find('td.oe_list_group_pagination').find('span').remove(); } else { - var pages = Math.ceil(dataset.size() / limit); + self.max_page_index = Math.ceil(dataset.size() / limit) - 1; self.$row .find('.oe_list_pager_state') .text(_.str.sprintf(_t("%(page)d/%(page_count)d"), { page: page + 1, - page_count: pages + page_count: self.max_page_index + 1 })) .end() .find('button[data-pager-action=previous]') .toggleClass('disabled', page === 0) .end() .find('button[data-pager-action=next]') - .toggleClass('disabled', page === pages - 1); + .toggleClass('disabled', page === self.max_page_index); } } @@ -1608,7 +1616,7 @@ instance.web.ListView.Groups = instance.web.Class.extend( /** @lends instance.we .pluck('name').value(), function (groups) { // page count is irrelevant on grouped page, replace by limit - self.view.$pager.find('.oe_pager_group').hide(); + self.view.$pager.find('.oe-pager-buttons').hide(); self.view.$pager.find('.oe_list_pager_state').text(self.view._limit ? self.view._limit : '∞'); $el[0].appendChild( self.render_groups(groups)); From 51f61ed23d1fcc311900f45f6e7b88c53f51db93 Mon Sep 17 00:00:00 2001 From: Daniel Kraft Date: Mon, 23 Feb 2015 19:03:31 +0100 Subject: [PATCH 28/87] [ADD] D9T GmbH CLA closes #5433 --- doc/cla/corporate/d9t.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 doc/cla/corporate/d9t.md diff --git a/doc/cla/corporate/d9t.md b/doc/cla/corporate/d9t.md new file mode 100644 index 00000000000..3c20e3fa4a4 --- /dev/null +++ b/doc/cla/corporate/d9t.md @@ -0,0 +1,16 @@ +Germany, 2015-02-23 + +D9T GmbH 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, + +Daniel Kraft, daniel.kraft@d9t.de https://github.com/wamdam + +List of contributors: + +Daniel Kraft, daniel.kraft@d9t.de https://github.com/wamdam +Oliver Roch, oliver.roch@d9t.de https://github.com/veroc From 5f45e7ca96f34fa8e904cdaa46576b60ffcfb477 Mon Sep 17 00:00:00 2001 From: David Monjoie Date: Thu, 26 Feb 2015 15:42:01 +0100 Subject: [PATCH 29/87] [FIX] hr_timesheet_sheet: properly unlink timesheets when deleting a timesheet_sheet Adaptation of https://github.com/acsone/odoo/commit/afe4a97d037b4075c8cd0514b6d1b829e1208629, but we properly unlink the timesheet lines themselves instead of going for the account.analytic.lines like the original commit proposition did. Actually, the unlink method of hr.analytic.timesheet already unlinks the corresponding acount.analytic.line, but those records were previously deleted via ondelete='cascade', which didn't trigger the unlink method at all, so the associated analytic lines were not deleted accordingly. Fixes opw 628256. --- addons/hr_timesheet_sheet/hr_timesheet_sheet.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py index f59bceb9a82..d2d0d0a0533 100644 --- a/addons/hr_timesheet_sheet/hr_timesheet_sheet.py +++ b/addons/hr_timesheet_sheet/hr_timesheet_sheet.py @@ -256,6 +256,14 @@ class hr_timesheet_sheet(osv.osv): raise osv.except_osv(_('Invalid Action!'), _('You cannot delete a timesheet which is already confirmed.')) elif sheet['total_attendance'] <> 0.00: raise osv.except_osv(_('Invalid Action!'), _('You cannot delete a timesheet which have attendance entries.')) + + toremove = [] + analytic_timesheet = self.pool.get('hr.analytic.timesheet') + for sheet in self.browse(cr, uid, ids, context=context): + for timesheet in sheet.timesheet_ids: + toremove.append(timesheet.id) + analytic_timesheet.unlink(cr, uid, toremove, context=context) + return super(hr_timesheet_sheet, self).unlink(cr, uid, ids, context=context) def onchange_employee_id(self, cr, uid, ids, employee_id, context=None): From d169e6f935134e21ebe589eae24f8dc5402128b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Maes?= Date: Wed, 25 Feb 2015 16:07:22 +0100 Subject: [PATCH 30/87] [FIX] im_livechat : remove the cookie when close anonymous conversation, and prefix image link by server url in template --- addons/im_livechat/static/src/js/im_livechat.js | 3 ++- addons/im_livechat/static/src/xml/im_livechat.xml | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/addons/im_livechat/static/src/js/im_livechat.js b/addons/im_livechat/static/src/js/im_livechat.js index f75e9b40833..2ecb17d5049 100644 --- a/addons/im_livechat/static/src/js/im_livechat.js +++ b/addons/im_livechat/static/src/js/im_livechat.js @@ -60,6 +60,7 @@ this.feedback.on("feedback_sent", this, this.click_close); }else{ this._super.apply(this, arguments); + openerp.set_cookie(im_livechat.COOKIE_NAME, "", -1); } }, }); @@ -99,7 +100,7 @@ this.load_template(db, channel, options, rule); }, _get_template_list: function(){ - return ['/im_chat/static/src/xml/im_chat.xml', '/im_livechat/static/src/xml/im_livechat.xml']; + return ['/im_livechat/static/src/xml/im_livechat.xml', '/im_chat/static/src/xml/im_chat.xml']; }, load_template: function(db, channel, options, rule){ var self = this; diff --git a/addons/im_livechat/static/src/xml/im_livechat.xml b/addons/im_livechat/static/src/xml/im_livechat.xml index 0176cb00419..410b3b13c81 100644 --- a/addons/im_livechat/static/src/xml/im_livechat.xml +++ b/addons/im_livechat/static/src/xml/im_livechat.xml @@ -12,9 +12,9 @@ Did we correctly answer your question ?
    - Good - OK - Bad + Good + OK + Bad
    From 8ab68d8048e4a9ea60ca73d3754ee4fe8a22e5bf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Maes?= Date: Thu, 26 Feb 2015 14:36:11 +0100 Subject: [PATCH 31/87] [FIX] web : fix the dropdown style to delete attachments --- addons/web/static/src/css/base.css | 3 ++- addons/web/static/src/xml/base.xml | 26 ++++++++++++++------------ 2 files changed, 16 insertions(+), 13 deletions(-) diff --git a/addons/web/static/src/css/base.css b/addons/web/static/src/css/base.css index 3dfed2985a5..bc9edd3c5a7 100644 --- a/addons/web/static/src/css/base.css +++ b/addons/web/static/src/css/base.css @@ -1092,7 +1092,8 @@ } .openerp .oe-view-manager-header .oe-view-manager-sidebar .oe_sidebar_delete_item { padding: 0; - display: inline-block; + position: absolute; + right: 10px; } .openerp .oe-view-manager-header .oe-view-manager-sidebar .dropdown-menu li a { width: 100%; diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 8652cdea547..ab242df110b 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -94,7 +94,7 @@ - + @@ -493,7 +493,7 @@
    - +
    @@ -536,8 +536,8 @@
  • Technical Translation
  • Manage Views
  • Edit View
  • -
  • Edit SearchView
  • -
  • Edit Action
  • +
  • Edit SearchView
  • +
  • Edit Action
  • Edit Workflow
  • Print Workflow
  • @@ -588,7 +588,7 @@
    -
  • @@ -801,8 +803,8 @@
    - @@ -927,7 +929,7 @@
    -
    +
    @@ -959,7 +961,7 @@
    -
    +
    @@ -1157,7 +1159,7 @@ > + /> @@ -1485,7 +1487,7 @@
    - + From f6ddf1a617ee70432530dfdbb42ce62e9b89a6d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Maes?= Date: Thu, 26 Feb 2015 14:54:30 +0100 Subject: [PATCH 32/87] [FIX] hr: employee kanban alignment style --- addons/hr/static/src/css/hr.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/hr/static/src/css/hr.css b/addons/hr/static/src/css/hr.css index 0a1074b3b3e..ba3dfd7ccdf 100644 --- a/addons/hr/static/src/css/hr.css +++ b/addons/hr/static/src/css/hr.css @@ -1,6 +1,6 @@ .oe_employee_details{ min-height: 168px !important; - max-width: 336px; + width: 350px; } .oe_employee_image { From 25bf2674d542129d7689640fe824428fb618f65b Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 26 Feb 2015 17:19:04 +0100 Subject: [PATCH 33/87] [FIX] web: prevent set value of priority if readonly The priority widget did not handle the readonly concept It was therefore possible to set the priority of an issue (for instance) even if the field was marked as readonly. opw-628960 --- addons/web/static/src/js/view_form.js | 4 +++- addons/web/static/src/xml/base.xml | 3 ++- addons/web_kanban/static/src/js/kanban.js | 8 +++++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index f2802c3f5f9..df1b0033569 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2424,7 +2424,9 @@ instance.web.form.Priority = instance.web.form.FieldChar.extend({ this.record_id = this.view.datarecord.id; this.priorities = this.prepare_priority(); this.$el.html(QWeb.render("Priority", {'widget': this})); - this.$el.find('li').on('click', this.set_priority.bind(this)); + if (!this.get('readonly')){ + this.$el.find('li').on('click', this.set_priority.bind(this)); + } }, /* setting the value: in view mode, perform an asynchronous call and reload the form view; in edit mode, use set_value to save the new value that will diff --git a/addons/web/static/src/xml/base.xml b/addons/web/static/src/xml/base.xml index 864bc899960..145d9a02d95 100644 --- a/addons/web/static/src/xml/base.xml +++ b/addons/web/static/src/xml/base.xml @@ -1056,9 +1056,10 @@ diff --git a/addons/web_kanban/static/src/js/kanban.js b/addons/web_kanban/static/src/js/kanban.js index cd92aadf303..fc9c53ed836 100644 --- a/addons/web_kanban/static/src/js/kanban.js +++ b/addons/web_kanban/static/src/js/kanban.js @@ -1300,8 +1300,14 @@ instance.web_kanban.Priority = instance.web_kanban.AbstractField.extend({ var self = this; this.record_id = self.parent.id; this.priorities = self.prepare_priority(); + var readonly = this.field && this.field.readonly; + if (readonly){ + this.set('readonly', true); + } this.$el = $(QWeb.render("Priority", {'widget': this})); - this.$el.find('li').click(self.do_action.bind(self)); + if (!readonly){ + this.$el.find('li').click(self.do_action.bind(self)); + } }, do_action: function(e) { var self = this; From d4f41a18a6a21ea627f2cabccb17d4049f4ad0cc Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Thu, 26 Feb 2015 17:38:39 +0100 Subject: [PATCH 34/87] [FIX] sale: sales count button on variants The button was no more redirecting to the sales order lines list view, following a distraction error introduced during 68b8286d5fe384af2ce05b58807e532ffa27a796 --- addons/sale/sale_view.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/sale_view.xml b/addons/sale/sale_view.xml index cdcdf275e1e..6afdb967414 100644 --- a/addons/sale/sale_view.xml +++ b/addons/sale/sale_view.xml @@ -503,7 +503,7 @@ From f6d32a9fe9311e7c2c8c5dd5fd1d1afc182391c5 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Thu, 26 Feb 2015 17:59:14 +0100 Subject: [PATCH 35/87] [FIX] website_quote: use a copy of the context. --- addons/website_quote/models/order.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/addons/website_quote/models/order.py b/addons/website_quote/models/order.py index 6b15301c7cd..9090d68a68e 100644 --- a/addons/website_quote/models/order.py +++ b/addons/website_quote/models/order.py @@ -173,9 +173,8 @@ class sale_order(osv.osv): if not template_id: return True - if context is None: - context = {} if partner: + context = dict(context or {}) context['lang'] = self.pool['res.partner'].browse(cr, uid, partner, context).lang lines = [(5,)] From daf16c2abc78cb61b1e38f517ec585276e246808 Mon Sep 17 00:00:00 2001 From: Markus Schneider Date: Thu, 26 Feb 2015 18:07:43 +0100 Subject: [PATCH 36/87] add CLA for initOS --- doc/cla/corporate/initos.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 doc/cla/corporate/initos.md diff --git a/doc/cla/corporate/initos.md b/doc/cla/corporate/initos.md new file mode 100644 index 00000000000..0bede7135e2 --- /dev/null +++ b/doc/cla/corporate/initos.md @@ -0,0 +1,21 @@ +Germany, 2015-02-26 + +initOS GmbH & Co. KG 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, + +Markus Schneider markus.schneider@initos.com https://github.com/OSguard + +List of contributors: + +Markus Schneider markus.schneider@initos.com https://github.com/OSguard +Thomas Rehn thomas.rehn@initos.com https://github.com/tremlin +Katja Matthes katja.matthes@initos.com https://github.com/kmatthes +Frederik Kramer frederik.kramer@initos.com https://github.com/OSevangelist +Nikolina Todorova nikolina.todorova@initos.com https://github.com/ntodorova +Peter Hahn peter.hahn@initos.com https://github.com/codingforfun +Claudia Haida claudia.haida@initos.com From 9e62efc8fb891aef975586b8ba29054374a6e147 Mon Sep 17 00:00:00 2001 From: Leonardo Rochael Almeida Date: Thu, 26 Feb 2015 16:10:55 -0300 Subject: [PATCH 37/87] [CLA] Leonardo Rochael Almeida --- doc/cla/individual/leorochael.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/leorochael.md diff --git a/doc/cla/individual/leorochael.md b/doc/cla/individual/leorochael.md new file mode 100644 index 00000000000..8833e872814 --- /dev/null +++ b/doc/cla/individual/leorochael.md @@ -0,0 +1,11 @@ +Brazil, 2015-02-26 + +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, + +Leonardo Rochael Almeida leorochael@gmail.com https://github.com/leorochael From 12e7d802732e052d5de9e5162b9379488cb17a7d Mon Sep 17 00:00:00 2001 From: Leonardo Rochael Almeida Date: Thu, 26 Feb 2015 16:15:50 -0300 Subject: [PATCH 38/87] Declare PyChart package name in requirements.txt While calling `pip install -r requirements.txt`, the PyChart tarball is always downloaded even if `pip` has a download cache configured (which it does by default in later versions). This is because `pip` has no way of knowing which package the PyChart tarball contains before downloading and running its `setup.py`. By declaring the PyChart "egg" in `requirements.txt` the tarball is no longer downloaded if it's already in the download cache. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 12ab04ecd51..047bc19d8f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,7 +3,7 @@ Jinja2==2.7.3 Mako==1.0.0 MarkupSafe==0.23 Pillow==2.5.1 -http://download.gna.org/pychart/PyChart-1.39.tar.gz +http://download.gna.org/pychart/PyChart-1.39.tar.gz#egg=PyChart PyYAML==3.11 Werkzeug==0.9.6 argparse==1.2.1 From 2e9e100d200783639885bcae9089a35e74eabc12 Mon Sep 17 00:00:00 2001 From: Toilal Date: Thu, 19 Feb 2015 23:49:03 +0100 Subject: [PATCH 39/87] [FIX] Packaging: include all files from scaffold templates in MANIFEST.in closes #5393 --- MANIFEST.in | 1 + doc/cla/individual/Toilal.md | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 doc/cla/individual/Toilal.md diff --git a/MANIFEST.in b/MANIFEST.in index 2ef37c2bb2e..54a5dcd8994 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -25,3 +25,4 @@ recursive-include openerp *.xml recursive-include openerp *.yml recursive-exclude * *.py[co] recursive-exclude * *.hg* +graft openerp/cli/templates diff --git a/doc/cla/individual/Toilal.md b/doc/cla/individual/Toilal.md new file mode 100644 index 00000000000..706d3a3ff14 --- /dev/null +++ b/doc/cla/individual/Toilal.md @@ -0,0 +1,11 @@ +France, 2015-02-20 + +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, + +Rémi Alvergnat toilal.dev@gmail.com https://github.com/Toilal From 816faf133b8daaf85460b6c36ced06f40568ee14 Mon Sep 17 00:00:00 2001 From: vnsoft Date: Fri, 27 Feb 2015 16:50:06 +0800 Subject: [PATCH 40/87] Sign CLA agreement for vnsofthe --- doc/cla/individual/vnsofthe.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/vnsofthe.md diff --git a/doc/cla/individual/vnsofthe.md b/doc/cla/individual/vnsofthe.md new file mode 100644 index 00000000000..70c0c189c12 --- /dev/null +++ b/doc/cla/individual/vnsofthe.md @@ -0,0 +1,11 @@ +China, 2015-02-27 + +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, + +vnsoft vnsoft.he@gmail.com https://github.com/vnsofthe From 05905b794eb1e9eaee85e2ee235e42f0f0072556 Mon Sep 17 00:00:00 2001 From: vnsofthe Date: Fri, 27 Feb 2015 11:17:29 +0800 Subject: [PATCH 41/87] [FIX] mrp: xmlid of Manufacture route Is present in mrp and not stock module. --- addons/mrp/stock.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/mrp/stock.py b/addons/mrp/stock.py index e22105edd42..8aba4fdac38 100644 --- a/addons/mrp/stock.py +++ b/addons/mrp/stock.py @@ -251,7 +251,7 @@ class stock_warehouse(osv.osv): route_obj = self.pool.get('stock.location.route') data_obj = self.pool.get('ir.model.data') try: - manufacture_route_id = data_obj.get_object_reference(cr, uid, 'stock', 'route_warehouse0_manufacture')[1] + manufacture_route_id = data_obj.get_object_reference(cr, uid, 'mrp', 'route_warehouse0_manufacture')[1] except: manufacture_route_id = route_obj.search(cr, uid, [('name', 'like', _('Manufacture'))], context=context) manufacture_route_id = manufacture_route_id and manufacture_route_id[0] or False From 68f14c68709bbb50cb7fb66d288955e1d769c5ff Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 27 Feb 2015 13:19:02 +0100 Subject: [PATCH 42/87] [FIX] handling of big fields in CSV fixes #2742 --- addons/base_import/tests/test_cases.py | 32 +++++++++++++++++++++++--- openerp/cli/server.py | 6 +++++ 2 files changed, 35 insertions(+), 3 deletions(-) diff --git a/addons/base_import/tests/test_cases.py b/addons/base_import/tests/test_cases.py index bad79bc7cdc..768754df07c 100644 --- a/addons/base_import/tests/test_cases.py +++ b/addons/base_import/tests/test_cases.py @@ -210,9 +210,6 @@ class test_preview(TransactionCase): }) self.assertTrue('error' in result) - def test_csv_errors(self): - Import, id = self.make_import() - result = Import.parse_preview(self.cr, self.uid, id, { 'quoting': '"', 'separator': 'bob', @@ -356,3 +353,32 @@ class test_convert_import_data(TransactionCase): Import._convert_import_data, record, [False, False, False], {'quoting': '"', 'separator': ',', 'headers': True,}) + +class test_failures(TransactionCase): + def test_big_attachments(self): + """ + Ensure big fields (e.g. b64-encoded image data) can be imported and + we're not hitting limits of the default CSV parser config + """ + import csv, cStringIO + from PIL import Image + + im = Image.new('RGB', (1920, 1080)) + fout = cStringIO.StringIO() + + writer = csv.writer(fout, dialect=None) + writer.writerows([ + ['name', 'db_datas'], + ['foo', im.tobytes().encode('base64')] + ]) + + Import = self.env['base_import.import'] + imp = Import.create({ + 'res_model': 'ir.attachment', + 'file': fout.getvalue() + }) + [results] = imp.do( + ['name', 'db_datas'], + {'headers': True, 'separator': ',', 'quoting': '"'}) + self.assertFalse( + results, "results should be empty on successful import") diff --git a/openerp/cli/server.py b/openerp/cli/server.py index db0ab0699a1..f3932b4bf60 100644 --- a/openerp/cli/server.py +++ b/openerp/cli/server.py @@ -30,6 +30,7 @@ GNU Public Licence. """ import atexit +import csv import logging import os import signal @@ -142,6 +143,11 @@ def main(args): config = openerp.tools.config + # the default limit for CSV fields in the module is 128KiB, which is not + # quite sufficient to import images to store in attachment. 500MiB is a + # bit overkill, but better safe than sorry I guess + csv.field_size_limit(500 * 1024 * 1024) + if config["test_file"]: config["test_enable"] = True From 4bd2ee1aaace8e7f6fa5621688e00dc2115fa8a7 Mon Sep 17 00:00:00 2001 From: Xavier Morel Date: Fri, 27 Feb 2015 15:07:15 +0100 Subject: [PATCH 43/87] [FIX] undefined variable Variable was added to an error message, then inlined in only one of the two callsites. Undefined variable error would only appear when an error is triggered in the actual (postgres-level) backup call. inserted in ec9a543 fixes #5241 --- openerp/tools/misc.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index 231496e7b58..9fba582ad30 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -122,7 +122,8 @@ def exec_pg_command(name, *args): prog = find_pg_tool(name) env = exec_pg_environ() with open(os.devnull) as dn: - rc = subprocess.call((prog,) + args, env=env, stdout=dn, stderr=subprocess.STDOUT) + args2 = (prog,) + args + rc = subprocess.call(args2, env=env, stdout=dn, stderr=subprocess.STDOUT) if rc: raise Exception('Postgres subprocess %s error %s' % (args2, rc)) From b11c49d6f606e3d854cf9db2f4f93811b87da138 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Fri, 27 Feb 2015 15:41:28 +0100 Subject: [PATCH 44/87] [FIX] sale: sales team amount invoiced exclude purchases This is possible to assign a sales team to a supplier invoices Supplier invoices must be rejected in the invoiced count of sales team (in the kanban view of sales team). The invoiced amount must be the customer invoices amount minus the customer refund invoices amount opw-629105 --- addons/sale/sales_team.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/sales_team.py b/addons/sale/sales_team.py index c782660d0ea..ef4891de8e8 100644 --- a/addons/sale/sales_team.py +++ b/addons/sale/sales_team.py @@ -36,7 +36,7 @@ class crm_case_section(osv.osv): res = {} for id in ids: - created_domain = [('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)] + created_domain = [('type', 'in', ['out_invoice', 'out_refund']), ('section_id', '=', id), ('state', 'not in', ['draft', 'cancel']), ('date', '>=', date_begin), ('date', '<=', date_end)] values = self.__get_bar_values(cr, uid, obj, created_domain, ['price_total', 'date'], 'price_total', 'date', context=context) for value in values: value['value'] = float_repr(value.get('value', 0), precision_digits=self.pool['decimal.precision'].precision_get(cr, uid, 'Account')) From e6396deb4e93808cfd8e63b7f0d5f460986767a3 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Fri, 27 Feb 2015 15:57:13 +0100 Subject: [PATCH 45/87] [IMP] models: log source when "Comparing apples and oranges" --- openerp/models.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/openerp/models.py b/openerp/models.py index e264fdaeddd..7ba80a12f86 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -49,7 +49,7 @@ import pytz import re import time from collections import defaultdict, MutableMapping -from inspect import getmembers +from inspect import getmembers, currentframe import babel.dates import dateutil.relativedelta @@ -66,6 +66,7 @@ from .osv import fields from .osv.query import Query from .tools import frozendict, lazy_property, ormcache from .tools.config import config +from .tools.func import frame_codeinfo from .tools.misc import CountingStream, DEFAULT_SERVER_DATETIME_FORMAT, DEFAULT_SERVER_DATE_FORMAT from .tools.safe_eval import safe_eval as eval from .tools.translate import _ @@ -5474,7 +5475,9 @@ class BaseModel(object): """ Test whether two recordsets are equivalent (up to reordering). """ if not isinstance(other, BaseModel): if other: - _logger.warning("Comparing apples and oranges: %s == %s", self, other) + filename, lineno = frame_codeinfo(currentframe(), 1) + _logger.warning("Comparing apples and oranges: %r == %r (%s:%s)", + self, other, filename, lineno) return False return self._name == other._name and set(self._ids) == set(other._ids) From fb285d869548a75457bf7fe89a22801de4269c91 Mon Sep 17 00:00:00 2001 From: Samus CTO Date: Fri, 27 Feb 2015 17:16:16 +0100 Subject: [PATCH 46/87] [FIX] prevent unneeded chmod datadir, but check writable --- openerp/tools/config.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/openerp/tools/config.py b/openerp/tools/config.py index b07bbf99c05..21b15132fa3 100644 --- a/openerp/tools/config.py +++ b/openerp/tools/config.py @@ -683,7 +683,8 @@ class configmanager(object): if not os.path.exists(d): os.makedirs(d, 0700) else: - os.chmod(d, 0700) + assert os.access(d, os.W_OK), \ + "%s: directory is not writable" % d return d @property @@ -692,7 +693,8 @@ class configmanager(object): if not os.path.exists(d): os.makedirs(d, 0700) else: - os.chmod(d, 0700) + assert os.access(d, os.W_OK), \ + "%s: directory is not writable" % d return d def filestore(self, dbname): From 128de187a322ec561a194e5dfba6c04761c4c1e5 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Fri, 27 Feb 2015 17:18:06 +0100 Subject: [PATCH 47/87] [FIX] web: KanbanSelection and Priority widgets Use directly datarecord id instead of previously stored value which may not be up to date. --- addons/web/static/src/js/view_form.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/addons/web/static/src/js/view_form.js b/addons/web/static/src/js/view_form.js index df1b0033569..4de993bee3f 100644 --- a/addons/web/static/src/js/view_form.js +++ b/addons/web/static/src/js/view_form.js @@ -2365,7 +2365,6 @@ instance.web.form.KanbanSelection = instance.web.form.FieldChar.extend({ }, render_value: function() { var self = this; - this.record_id = this.view.datarecord.id; this.states = this.prepare_dropdown_selection();; this.$el.html(QWeb.render("KanbanSelection", {'widget': self})); this.$el.find('li').on('click', this.set_kanban_selection.bind(this)); @@ -2383,7 +2382,7 @@ instance.web.form.KanbanSelection = instance.web.form.FieldChar.extend({ write_values[self.name] = value; return this.view.dataset._model.call( 'write', [ - [self.record_id], + [this.view.datarecord.id], write_values, self.view.dataset.get_context() ]).done(self.reload_record.bind(self)); @@ -2421,7 +2420,6 @@ instance.web.form.Priority = instance.web.form.FieldChar.extend({ }, render_value: function() { var self = this; - this.record_id = this.view.datarecord.id; this.priorities = this.prepare_priority(); this.$el.html(QWeb.render("Priority", {'widget': this})); if (!this.get('readonly')){ @@ -2441,7 +2439,7 @@ instance.web.form.Priority = instance.web.form.FieldChar.extend({ write_values[self.name] = value; return this.view.dataset._model.call( 'write', [ - [self.record_id], + [this.view.datarecord.id], write_values, self.view.dataset.get_context() ]).done(self.reload_record.bind(self)); From 1f8c8d6dd52ebb8baa3da98ad7165068accc2ec8 Mon Sep 17 00:00:00 2001 From: Simon Lejeune Date: Fri, 27 Feb 2015 18:00:39 +0100 Subject: [PATCH 48/87] [IMP] doc: install: advised wkhtmltopdf version Also redirect to our builds of wkhtmltopdf 0.12.1 for debian jessie, as the wkhtmltopdf team does not provide one. --- doc/setup/install.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/doc/setup/install.rst b/doc/setup/install.rst index 4221eb15225..701dec1a137 100644 --- a/doc/setup/install.rst +++ b/doc/setup/install.rst @@ -124,8 +124,10 @@ daemon and automatically start it. .. danger:: to print PDF reports, you must install wkhtmltopdf_ yourself: the version of wkhtmltopdf_ available in debian repositories does not support headers and footers so it can not be installed - automatically. Use the version available on - `the wkhtmltopdf download page`_. + automatically. The recommended version is 0.12.1 and is available on + `the wkhtmltopdf download page`_, in the archive section. As there + is no official release for Debian Jessie, you can find ours on + http://nightly.odoo.com/extra/. Configuration ''''''''''''' From e5a94f74aa05bbad04bf930d9c3740f318ae7cec Mon Sep 17 00:00:00 2001 From: Antony Lesuisse Date: Sat, 28 Feb 2015 17:58:22 +0100 Subject: [PATCH 49/87] [CLA] mailmap update --- .mailmap | 173 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 90 insertions(+), 83 deletions(-) diff --git a/.mailmap b/.mailmap index de72af905da..9e806a9ffa2 100644 --- a/.mailmap +++ b/.mailmap @@ -4,6 +4,8 @@ Ajay Javiya Ajay Javiya Akash Balar Akash Balar +Akta Dakwala +Akta Dakwala EDA(Axelor) <> Albert Cervera i Areny Albert Cervera i Areny Albert Cervera i Areny - http://www.NaN-tic.com <> Albert Cervera i Areny Albert Cervera i Areny <> @@ -20,6 +22,7 @@ Alexis de Lattre Alexis de Lattre Alexis de Lattre Alexis de Lattre <> Alexis de Lattre alexis.delattre@akretion.com <> +Aliane Abdelouahab Aline Preillon Aline Preillon Amit Bhavsar @@ -103,6 +106,9 @@ Atul Patel ATP(OpenERP) <> Atul Patel ATUL (OpenERP) <> Atul Patel atp (OpenERP) <> Augustin Cisterne-Kaas Augustin Cisterne-Kaas +Avdhesh Patel +Avdhesh Patel AVP(Axelor) <> +Avdhesh Patel avp Baskhuu Jacara Jacara <> Baskhuu Jacara jacara <> Benoit Guillot @@ -113,6 +119,7 @@ Bharat Devnani Bharat Devnani Bharat Devnani (Open ERP) <> Bharat Devnani Bharat Devnani(OpenERP) <> Bharat Devnani bde@tinyerp.com <> +Bhatt Chintal bch <> Bhavik Bagdiya Bhavik Bagdiya Bhumi Patel @@ -217,6 +224,8 @@ Dhara Shah Dhara (OpenERP) <> Dhara Shah Dhara Dhara Shah dsh (Open ERP) <> Dhara Shah dsh(Open ERP) <> +Dharmesh Rathod +Dharmesh Rathod DRA Axelor <> Dharmraj Jhala Dharmraj Jhala Dharmraj Jhala @@ -320,6 +329,7 @@ Guewen Baconnier Guewen Baconnier <> Guillem Barba Guillem Barba <> Guillem Barba guillem <> Guillermo Bisheimer +Gulamfarid Ghanchi Géry Debongnie Géry Debongnie Géry Debongnie @@ -330,6 +340,7 @@ Hardik Ansodariya Hardik Ansodariya (OpenERP) <> Hardik Ansodariya Hardik Ansodariya <> Hardik R. Shamnani Hardik R. Shamnani hsa-tinyerp <> +Hardik Sanchawat Hardik Sanchawat (OpenERP) <> Harshad Modi Harshad Modi HMO (Open ERP) <> Harshad Modi HMO(OpenERP) <> @@ -345,6 +356,7 @@ Herbert Petersen Conexus.at <> Hiral Patel Hitesh Trivedi Hugo Santos hugo <> +Hugo Santos hugosdsantos Humberto Arocha hbto(humbertoarocha) <> Husen Daudi Husen Daudi @@ -352,6 +364,7 @@ Husen Daudi Husen Daudi Husen Daudi Husen Daudi JVO,HDA <> +Husen Daudi ashish Husen Daudi hda(Open ERP) <> Husen Daudi hda@tinyerp.com <> Husen Daudi (Serpent CS) @@ -412,6 +425,10 @@ Jean-Baptiste Aubort Jean-Christophe Vassort Jean-Christophe Vassort Jean-Sebastien Suzanne Jean-Sebastien SUZANNE <> +Jenil Malavia +Jenil Malavia JMA(OpenEPR) <> +Jenil Malavia JMA(OpenERP) <> +Jenil Malavia Jenil(OpenERP) <> Jeremy Kersten Jeremy Kersten Jeremy Kersten jke-openerp <> @@ -422,6 +439,7 @@ Jigar Amin Jigar Amin Jigar Amin JAM (OpenERP) <> Jigar Amin JAM(OpenERP) <> +Jigar Amin amami <> Jigar Amin jam <> Jignesh Rathod Jignesh Rathod @@ -461,6 +479,9 @@ Kuldeep Joshi Kuldeep Joshi Kuldeep Joshi kjo <> Kunal Chavda +Kunal Mathur +Kunal Mathur +Kunal Mathur KUM(OpenERP) <> Laetitia Gangloff Launchpad Launchpad Launchpad Translations on behalf of openerp <> @@ -497,6 +518,8 @@ Mantavya Gajjar Mantavya Gajjar MGA(OpenERP) <> Mantavya Gajjar Matvaya Gajjar(Openerp) <> Mantavya Gajjar mga@tinyerp.com <> +Manuel Abadia Manu <> +Manuel Abadia Manu, Jigar Amin - OpenERP <> Marcelo Hamra Marcelo Hamra <> Marcelo Hamra marcelo Mario Arias Badila @@ -579,6 +602,7 @@ Nicolas Bessi Nicolas Bessi Nicolas <> Nicolas Bessi Nicolas Bessi(C2C) <> Nicolas Bessi nicolas.bessi@camptocamp.com <> +Nicolas De Smet Zyph0s <> Nicolas Seinlet Nicolas Vanhoren Nicolas Vanhoren @@ -592,6 +616,9 @@ Niels Huylebroeck Niels Huylebroeck <> Niels Huylebroeck Niels(Red15) <> Niels Huylebroeck Niels(Rrd15) <> Niels Huylebroeck Red15 <> +Nikunj Jani +Nikunj Jani +Nikunj Jani Reporting Team(JNI OpenERP) <> Nimesh Contractor Oliver Laurent Olivier Dony @@ -599,24 +626,33 @@ Olivier Dony Olivier Dony Olivier Dony Odowan Kenobi <> Olivier Dony Olivier Dony <> -Olivier Dony Olvier Dony <> +Olivier Dony Olivier Laurent, Olivier Dony <> Olivier Dony OpenERP Framework R&D Team <> Olivier Dony odo@tinyerp.com <> Olivier Dossmann Olivier DOSSMANN Olivier Laurent Olivier Laurent Olivier Laurent -Olivier Laurent Olivier Laurent, Olivier Dony <> Olivier Laurent olt at tinyerp <> Olivier Laurent olt@tinyerp.com <> +Olivier Macchioni Omar Castiñeira Saavedra Omar Castiñeira Saavedra Omar (Pexego) <> Omar Castiñeira Saavedra Omar Omar Castiñeira Saavedra Omar(Pexego),Anup(OpenERP) <> Omar Castiñeira Saavedra omar7r <> +Ostap Yakovenko Pankaj Joshi Pankaj Joshi Pankaj Joshi Pankita Shah +Pankti Parikh +Pankti Parikh +Pankti Parikh PAP (Openerp) <> +Pankti Parikh PAP(OpenERP) <> +Pankti Parikh PAP(Openerp) <> +Pankti Parikh pap (OpenERP) <> +Pankti Parikh pap(openerp) <> +Pankti Parikh pap@tinyerp.com <> Panos Christeas Panos Christeas Panos Christeas @@ -640,8 +676,10 @@ Pariket Trivedi Parth Gajjar Parthiv Patel Parthiv Patel +Patrick Monnerat Patrik <> Patrick Thieuleux Patrick Thieuleux <> Paul J Stevens Paul J Stevens <> +Paul Rad-Dupuy vivisci <> Paulius Sladkevičius Paulius Sladkevičius Paulius Sladkevičius Paulius Sladkevičius Paulius Sladkevičius @@ -654,6 +692,7 @@ Peter Langenberg Peter Langenberg <> Philu philubert@gmail.com <> Phong Nguyen Phong Nguyen Phong Nguyen-Thanh <> +Phuong Lu phu <> Pinakin Nayi Pinakin Nayi Pinakin Nayi Pinakin Nai(OpenERP) <> @@ -732,6 +771,10 @@ Ravi Gohil Ravi Gohil Ravi Gohil Ravi Gohil (OpenERP) <> Ravi Gohil rgo <> +Ravindra Mekhiya +Ravindra Mekhiya +Ravindra Mekhiya RME(OpenERP) <> +Ravindra Mekhiya Ravindra Mekhiya (OpenERP) <> Ravish Murari Ravish Murari Ravish Murari Ravish(OpenERP) <> @@ -767,6 +810,11 @@ Rikenkumar Bhorania RCH(OpenERP). <> Rikenkumar Bhorania rch (Open ERP) <> Rikenkumar Bhorania rch (OpenERP) <> Rikenkumar Bhorania rch(OpenERP) <> +Rima Vora +Rima Vora +Rima Vora RVO <> +Rima Vora RVO(OpenERP) <> +Rima Vora Rvo(Open ERP) <> Robert Sevcik Robajz <> Roberto López López Rohan Nayani @@ -785,8 +833,23 @@ Rucha Patel Rucha Patel RPA(OpenERP) <> Rucha Patel rpa(Open ERP) <> Rucha Patel rpa-openerp <> +Sabrina Romero +Saiyed Azadahmed +Saiyed Azadahmed +Saiyed Azadahmed SAZ (Open ERP) <> +Saiyed Azadahmed SAZ (OpenERP) <> +Saiyed Azadahmed SAZ (Opn ERP) <> +Saiyed Azadahmed SAZ (OprnERP) <> +Saiyed Azadahmed SAZ(OpenERP) <> +Saiyed Azadahmed SAz <> +Saiyed Azadahmed saz <> +Saiyed Azadahmed saz(OpenERP) <> +Saiyed Azadahmed saz(openerp) <> Salvatore Trimarchi Salvi Angjeli Salvi Angjeli <> +Sananaz Mansuri +Sananaz Mansuri SMA(OpenERP) <> +Sananaz Mansuri sma@tinyerp.com <> Sandy Carter Sandy Carter Sanjay Gohel @@ -818,6 +881,7 @@ Sheth Sunny Sheth Sunny Sheth Sunny Sheth Sunny sus +Shyamli Singh Simon Lejeune Simon Lejeune Simone Orsi @@ -897,6 +961,11 @@ Ujjvala Collins uco(OpenERP) <> Vadim Chobanu Vadim - SmartMode LTD <> Vaibhav Darji Vaibhav Darji +Vaishali Thakkar +Vaishali Thakkar VTH(OpenERP) <> +Valencia Rodrigues +Valencia Rodrigues VRO(OpenERP) <> +Vee vitridex <> Vianney da Costa Vianney da Costa Vianney - www.akretion.com.br <> Vianney da Costa Vianney,Anup(OpenERP) <> @@ -941,6 +1010,9 @@ Xavier Morel Xavier Morel Xavier Morel <> Xavier Morel Xavier(OpenERP) <> Xavier Morel xmo <> +Yajushi Yagnik +Yajushi Yagnik YYA(Axelor) <> +Yajushi Yagnik YYA(OpenERP) <> Yann Papouin Yann Papouin Yann Papouin <> Yann Papouin Yann papouin <> @@ -950,6 +1022,7 @@ Yannick Vaucher Yannick Vaucher <> Yannick Vaucher Yannick Vaucher Yannick Vaucher Yannick Vaucher@Camptocamp <> Yogesh Parekh +Yogesh Parekh Yogesh Parekh Yogesh Sakhreliya Yogesh Sakhreliya @@ -968,63 +1041,12 @@ Yury Tello Yury Tello _FIXME ajm _FIXME ajm AJM (OpenERP) <> -_FIXME bch bch <> -_FIXME ggh -_FIXME jma -_FIXME jma JMA(OpenEPR) <> -_FIXME jma JMA(OpenERP) <> -_FIXME jni -_FIXME jni -_FIXME jni Reporting Team(JNI OpenERP) <> -_FIXME kum -_FIXME kum -_FIXME kum KUM(OpenERP) <> -_FIXME pap -_FIXME pap -_FIXME pap PAP (Openerp) <> -_FIXME pap PAP(OpenERP) <> -_FIXME pap PAP(Openerp) <> -_FIXME pap pap (OpenERP) <> -_FIXME pap pap(openerp) <> -_FIXME pap pap@tinyerp.com <> _FIXME rde rde <> -_FIXME rme -_FIXME rme -_FIXME rme RME(OpenERP) <> -_FIXME rvo -_FIXME rvo -_FIXME rvo RVO <> -_FIXME rvo RVO(OpenERP) <> -_FIXME rvo Rvo(Open ERP) <> -_FIXME saz -_FIXME saz -_FIXME saz SAZ (Open ERP) <> -_FIXME saz SAZ (OpenERP) <> -_FIXME saz SAZ (Opn ERP) <> -_FIXME saz SAZ (OprnERP) <> -_FIXME saz SAZ(OpenERP) <> -_FIXME saz SAz <> -_FIXME saz saz <> -_FIXME saz saz(OpenERP) <> -_FIXME saz saz(openerp) <> -_FIXME sma -_FIXME sma SMA(OpenERP) <> -_FIXME sma sma@tinyerp.com <> _FIXME sme _FIXME sme SME(OpenERP) <> _FIXME sme SME(Tiny/Axelor) <> _FIXME sme SME(tiny/Axelor) <> _FIXME sme sme <> -_FIXME ssi -_FIXME vro -_FIXME vro VRO(OpenERP) <> -_FIXME vth -_FIXME vth VTH(OpenERP) <> -_FIXME yya -_FIXME yya EDA(Axelor) <> -_FIXME yya -_FIXME yya YYA(Axelor) <> -_FIXME yya YYA(OpenERP) <> __FIXME bhumi __FIXME tiny __FIXME tiny @@ -1032,25 +1054,22 @@ __FIXME tiny tiny <> # TODO INTERNAL -# 2 AVP(Axelor) <> -# 1 DRA(Axelor) <> -# 1 avp # 2 Priyanka Jani(OpenERP Trainee) <> -# 2 Hardik Sanchawat (OpenERP) <> -# 2 Jenil(OpenERP) <> # 1 Dhruvit Darji(OpenERP Trainee) <> -# 1 Ravindra Mekhiya (OpenERP) <> -# 1 Yogesh Parekh (OpenERP Trainee) # 1 Nikita Parmar (OpenERP Trainee) # 1 Sejal Bhut (OpenERP Trainee) -# 1 Amit (Open ERP) <> -# 1 phu <> # 1 vba <> + +# TODO INTERNAL Conflict + +# 1 Amit (Open ERP) <> # 1 Ajay Patel # 1 Yogesh(Open ERP) <> # 1 Yogesh(OpenERP) <> # 1 Chirag +# TODO INTERNAL Couples + # 1 ACH,Jay <> # 2 JMA,VRA <> # 2 Vir,Amp <> @@ -1088,6 +1107,8 @@ __FIXME tiny tiny <> # 1 Olivier Dony, Jay Vora <> # 1 Christophe Simonis, Olivier Dony <> +# TODO INTERNAL Team Leaders + # 2 Framework Team <> # 2 OpenERP R&D Framework Team <> # 2 Quality Team <> @@ -1095,6 +1116,8 @@ __FIXME tiny tiny <> # 1 Mod2 Team(OpenERP) <> # 1 OnDemand Open Object User +# TODO INTERNAL Leading Space + # 1 Amit Dodiya (OpenERP) <> # 1 Amit Dodiya(OpenERP) <> # 1 Jean-Christophe VASSORT <> @@ -1108,7 +1131,6 @@ __FIXME tiny tiny <> # 2 jkei # 2 richierichrawr # 2 watermelonjuice -# 1 vrenaville # 2 youring # 2 Дмитро Катюха # 1 jbeficent @@ -1117,9 +1139,7 @@ __FIXME tiny tiny <> # 1 jmustonen # 1 celm1990 # 1 cysnake4713 -# 1 dharmesh rathod # 1 driou -# 1 dsabrinarg # 1 eneldoserrata # 1 fdesmottes # 1 g.shipilov @@ -1127,38 +1147,25 @@ __FIXME tiny tiny <> # 1 senthilnathang # 1 the-digital-engineer # 1 yingliu4203 -# 1 hugosdsantos # 1 krnkris # 1 buke -# 1 OstapYakovenko # 1 DEEP SILVER ACCESSORY # 1 neps1192 -# 1 ALIANE Abdelouahab # 1 Invitu # 1 Jorge # 1 Maria G # 1 Mack -# TODO MISSING INFO - -# 1 Olivier Macchioni -# Email not found +# TODO MISSING INFO EMAIL # 2 Denis Seleznyov <> -# Email not found # 1 openerp user # Name: Milan Tribuson -# email not found -# 1 Manu, Jigar Amin - OpenERP <> -# 1 Ferchu <> -# 1 Patrik <> # 1 R.Messier <> -# 1 Manu <> -# 1 Zyph0s <> -# 1 amami <> -# 1 vitridex <> -# 1 vivisci <> -# 1 ashish +# Name: Raphael Messier + +# 1 Ferchu <> +# Launchpad: https://launchpad.net/~fgb From ccf021938d14435c51a835254e980496d9c48440 Mon Sep 17 00:00:00 2001 From: Daniel Reis Date: Sun, 1 Mar 2015 02:01:55 +0100 Subject: [PATCH 50/87] [FIX] auth_ldap login when password is unicode python ldap library api is str based, assume utf-8 encoding for non-ascii chars. Closes #5376 lp:1079476 --- addons/auth_ldap/users_ldap.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/auth_ldap/users_ldap.py b/addons/auth_ldap/users_ldap.py index b2241471c25..5f4f47b34a2 100644 --- a/addons/auth_ldap/users_ldap.py +++ b/addons/auth_ldap/users_ldap.py @@ -105,7 +105,7 @@ class CompanyLDAP(osv.osv): if results and len(results) == 1: dn = results[0][0] conn = self.connect(conf) - conn.simple_bind_s(dn, password) + conn.simple_bind_s(dn, password.encode('utf-8')) conn.unbind() entry = results[0] except ldap.INVALID_CREDENTIALS: @@ -141,7 +141,7 @@ class CompanyLDAP(osv.osv): try: conn = self.connect(conf) conn.simple_bind_s(conf['ldap_binddn'] or '', - conf['ldap_password'] or '') + conf['ldap_password'].encode('utf-8') or '') results = conn.search_st(conf['ldap_base'], ldap.SCOPE_SUBTREE, filter, retrieve_attributes, timeout=60) conn.unbind() From 04de29288559cdb4fe841e80b1f8bf41fb76dbfc Mon Sep 17 00:00:00 2001 From: Ravi Gohil Date: Sun, 1 Mar 2015 02:17:04 +0100 Subject: [PATCH 51/87] [FIX] http: support old version of psutil Add support for older versions of psutil (<1.2.0). Only used when debug_rpc is enabled. Closes #5108, #5163 --- openerp/http.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openerp/http.py b/openerp/http.py index 64023602d9d..d56e2d84649 100644 --- a/openerp/http.py +++ b/openerp/http.py @@ -559,7 +559,7 @@ class JsonRequest(WebRequest): start_time = time.time() _, start_vms = 0, 0 if psutil: - _, start_vms = psutil.Process().get_memory_info() + _, start_vms = psutil.Process(os.getpid()).get_memory_info() if rpc_request and rpc_response_flag: rpc_request.debug('%s: %s %s, %s', endpoint, model, method, pprint.pformat(args)) @@ -570,7 +570,7 @@ class JsonRequest(WebRequest): end_time = time.time() _, end_vms = 0, 0 if psutil: - _, end_vms = psutil.Process().get_memory_info() + _, end_vms = psutil.Process(os.getpid()).get_memory_info() logline = '%s: %s %s: time:%.3fs mem: %sk -> %sk (diff: %sk)' % ( endpoint, model, method, end_time - start_time, start_vms / 1024, end_vms / 1024, (end_vms - start_vms)/1024) if rpc_response_flag: From cf2b1143dc1ef10e8374f6b64181f6f9908f1b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?C=C3=A9dric=20Pigeon?= Date: Thu, 12 Feb 2015 10:29:20 +0100 Subject: [PATCH 52/87] [FIX] hr_timesheet_invoice avoid empty returns in invoice line description Closes #5242 --- addons/hr_timesheet_invoice/hr_timesheet_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py index ec8c89dbe64..6e6393bced2 100644 --- a/addons/hr_timesheet_invoice/hr_timesheet_invoice.py +++ b/addons/hr_timesheet_invoice/hr_timesheet_invoice.py @@ -278,7 +278,8 @@ class account_analytic_line(osv.osv): details.append("%s" % (line['unit_amount'], )) if data.get('name', False): details.append(line['name']) - note.append(u' - '.join(map(lambda x: unicode(x) or '',details))) + if details: + note.append(u' - '.join(map(lambda x: unicode(x) or '',details))) if note: curr_line['name'] += "\n" + ("\n".join(map(lambda x: unicode(x) or '',note))) invoice_line_obj.create(cr, uid, curr_line, context=context) From ddb900c7c5db3b4eb38319024b50f37e70ebd568 Mon Sep 17 00:00:00 2001 From: Leonardo Pistone Date: Fri, 16 Jan 2015 11:47:51 +0100 Subject: [PATCH 53/87] [FIX] product: demo data irrelevant product description Closes #4735 --- addons/product/product_demo.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/addons/product/product_demo.xml b/addons/product/product_demo.xml index f5b06a37375..4d520b19358 100644 --- a/addons/product/product_demo.xml +++ b/addons/product/product_demo.xml @@ -233,7 +233,6 @@ FaceTime HD Camera, 1.2 MP Photos consu - Custom computer assembled on order based on customer's requirement. Bose's smallest portable Bluetooth speaker B3423 From 631d2f71461eda852b41a429c782772c5d97f2fe Mon Sep 17 00:00:00 2001 From: Goffin Simon Date: Fri, 27 Feb 2015 11:28:18 +0100 Subject: [PATCH 54/87] [FIX] purchase: pricelist and related currency mismatch on purchase order (Bid received). pricelist must be readonly when currency is readonly because there 's an onchange on currency which depends on the pricelist. opw:627009 Fixed #4598 --- addons/purchase/purchase.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/addons/purchase/purchase.py b/addons/purchase/purchase.py index 363fdc620bd..a3ae7197f12 100644 --- a/addons/purchase/purchase.py +++ b/addons/purchase/purchase.py @@ -194,6 +194,13 @@ class purchase_order(osv.osv): ('done', 'Done'), ('cancel', 'Cancelled') ] + + READONLY_STATES = { + 'confirmed': [('readonly', True)], + 'approved': [('readonly', True)], + 'done': [('readonly', True)] + } + _track = { 'state': { 'purchase.mt_rfq_confirmed': lambda self, cr, uid, obj, ctx=None: obj.state == 'confirmed', @@ -222,16 +229,16 @@ class purchase_order(osv.osv): copy=False), 'date_approve':fields.date('Date Approved', readonly=1, select=True, copy=False, help="Date on which purchase order has been approved"), - 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, + 'partner_id':fields.many2one('res.partner', 'Supplier', required=True, states=READONLY_STATES, change_default=True, track_visibility='always'), 'dest_address_id':fields.many2one('res.partner', 'Customer Address (Direct Delivery)', - states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, + states=READONLY_STATES, help="Put an address if you want to deliver directly from the supplier to the customer. " \ "Otherwise, keep empty to deliver to your own company." ), - 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]} ), - 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states={'confirmed':[('readonly',True)], 'approved':[('readonly',True)],'done':[('readonly',True)]}, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."), - 'currency_id': fields.many2one('res.currency','Currency', readonly=True, required=True,states={'draft': [('readonly', False)],'sent': [('readonly', False)]}), + 'location_id': fields.many2one('stock.location', 'Destination', required=True, domain=[('usage','<>','view')], states=READONLY_STATES), + 'pricelist_id':fields.many2one('product.pricelist', 'Pricelist', required=True, states=READONLY_STATES, help="The pricelist sets the currency used for this purchase order. It also computes the supplier price for the selected products/quantities."), + 'currency_id': fields.many2one('res.currency','Currency', required=True, states=READONLY_STATES), 'state': fields.selection(STATE_SELECTION, 'Status', readonly=True, help="The status of the purchase order or the quotation request. " "A request for quotation is a purchase order in a 'Draft' status. " From 85ede15793dd2912ad49ad0035bfadcc022125d2 Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Fri, 27 Feb 2015 12:44:08 +0100 Subject: [PATCH 55/87] [FIX] purchase: get taxes when create rfq from bid When creating a request for quotation from a call for bids, the suppliers taxes where not present in the quotation. Since a product `taxes_id` field is a many2many, the unused triplets syntax caused the issue. fixes #5307 --- addons/purchase_requisition/purchase_requisition.py | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/purchase_requisition/purchase_requisition.py b/addons/purchase_requisition/purchase_requisition.py index 1a51dbfe30f..79c14e4037a 100644 --- a/addons/purchase_requisition/purchase_requisition.py +++ b/addons/purchase_requisition/purchase_requisition.py @@ -170,6 +170,7 @@ class purchase_requisition(osv.osv): 'order_id': purchase_id, 'product_id': product.id, 'account_analytic_id': requisition_line.account_analytic_id.id, + 'taxes_id': [(6, 0, vals.get('taxes_ids', []))], }) return vals From 50fc06e2751b9d0c213d10b5e301c93fe6eefa2d Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 27 Feb 2015 18:43:14 +0100 Subject: [PATCH 56/87] [IMP] Fasten up procurement JIT in v8 already --- addons/procurement_jit/procurement_jit.py | 6 ++++-- addons/sale/sale.py | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/addons/procurement_jit/procurement_jit.py b/addons/procurement_jit/procurement_jit.py index 4ad5cca5f20..0a9181ec42d 100644 --- a/addons/procurement_jit/procurement_jit.py +++ b/addons/procurement_jit/procurement_jit.py @@ -26,9 +26,11 @@ class procurement_order(osv.osv): _inherit = "procurement.order" def create(self, cr, uid, vals, context=None): + context = context or {} procurement_id = super(procurement_order, self).create(cr, uid, vals, context=context) - self.run(cr, uid, [procurement_id], context=context) - self.check(cr, uid, [procurement_id], context=context) + if not context.get('procurement_autorun_defer'): + self.run(cr, uid, [procurement_id], context=context) + self.check(cr, uid, [procurement_id], context=context) return procurement_id # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/addons/sale/sale.py b/addons/sale/sale.py index b772bb7511f..c64a55920f5 100644 --- a/addons/sale/sale.py +++ b/addons/sale/sale.py @@ -695,7 +695,7 @@ class sale_order(osv.osv): :return: True """ - context = dict(context) + context = context or {} context['lang'] = self.pool['res.users'].browse(cr, uid, uid).lang procurement_obj = self.pool.get('procurement.order') sale_line_obj = self.pool.get('sale.order.line') @@ -719,7 +719,9 @@ class sale_order(osv.osv): if (line.state == 'done') or not line.product_id: continue vals = self._prepare_order_line_procurement(cr, uid, order, line, group_id=order.procurement_group_id.id, context=context) - proc_id = procurement_obj.create(cr, uid, vals, context=context) + ctx = context.copy() + ctx['procurement_autorun_defer'] = True + proc_id = procurement_obj.create(cr, uid, vals, context=ctx) proc_ids.append(proc_id) #Confirm procurement order such that rules will be applied on it #note that the workflow normally ensure proc_ids isn't an empty list From 9339f1bfb1d6c2c680448d9ffab6c4f187606529 Mon Sep 17 00:00:00 2001 From: Julien Legros Date: Mon, 2 Mar 2015 11:06:32 +0100 Subject: [PATCH 57/87] [IMP] sale: quotation confirmed mail subtype defaults to False This reverts commit 0130ce4e84fdd038aad3b3fc74480bad04122f80. --- addons/sale/sale_data.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/sale/sale_data.xml b/addons/sale/sale_data.xml index e51be69e2f1..4c5347cf1d0 100644 --- a/addons/sale/sale_data.xml +++ b/addons/sale/sale_data.xml @@ -40,7 +40,7 @@ Sales Order Confirmed sale.order - + Quotation confirmed From 2bc75369dcfafc3ae3f4d8eed11945b6de944184 Mon Sep 17 00:00:00 2001 From: Andrius Preimantas Date: Thu, 26 Feb 2015 18:11:49 +0200 Subject: [PATCH 58/87] Versada signing CLA --- doc/cla/corporate/versada.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 doc/cla/corporate/versada.md diff --git a/doc/cla/corporate/versada.md b/doc/cla/corporate/versada.md new file mode 100644 index 00000000000..24610ad24fc --- /dev/null +++ b/doc/cla/corporate/versada.md @@ -0,0 +1,14 @@ +Lithuania, 2015-02-26 + +Versada 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, + +Andrius Preimantas andrius@versada.lt https://github.com/andrius-preimantas + +List of contributors: + +Andrius Preimantas andrius@versada.lt https://github.com/andrius-preimantas + From 19ce5932e270047fab366a838a61dc9d7f6d3eb5 Mon Sep 17 00:00:00 2001 From: Denis Ledoux Date: Mon, 2 Mar 2015 12:08:43 +0100 Subject: [PATCH 59/87] [FIX] warning: stock picking warnings Warnings on pickings no longer worked, while it was possible to set them in the partner form. The problem comes from the fact onchange_partner_in no longer exists on the picking form, by default. It was therefore never called, on the warning never displayed. opw-628956 --- addons/warning/warning.py | 5 +---- addons/warning/warning_view.xml | 11 +++++++++++ 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/addons/warning/warning.py b/addons/warning/warning.py index 8387f49c585..a8b55404e7e 100644 --- a/addons/warning/warning.py +++ b/addons/warning/warning.py @@ -172,10 +172,7 @@ class stock_picking(osv.osv): if partner.picking_warn == 'block': return {'value': {'partner_id': False}, 'warning': warning} - result = super(stock_picking_in, self).onchange_partner_in(cr, uid, ids, partner_id, context) - if result.get('warning',False): - warning['title'] = title and title +' & '+ result['warning']['title'] or result['warning']['title'] - warning['message'] = message and message + ' ' + result['warning']['message'] or result['warning']['message'] + result = {'value': {}} if warning: result['warning'] = warning diff --git a/addons/warning/warning_view.xml b/addons/warning/warning_view.xml index fdb199d0f4c..1415fc84ab1 100644 --- a/addons/warning/warning_view.xml +++ b/addons/warning/warning_view.xml @@ -60,5 +60,16 @@ + + stock.picking.form.warning + stock.picking + + + + onchange_partner_in(partner_id) + + + + From a8a9265340e3948fa3885128e4ea4350cb158826 Mon Sep 17 00:00:00 2001 From: Christophe Simonis Date: Mon, 2 Mar 2015 13:43:52 +0100 Subject: [PATCH 60/87] [FIX] website: backend html widget: only use an iframe if field is not readonly --- .../website/static/src/js/website.backend.js | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/addons/website/static/src/js/website.backend.js b/addons/website/static/src/js/website.backend.js index 158ad12c029..2345ca0fd3a 100644 --- a/addons/website/static/src/js/website.backend.js +++ b/addons/website/static/src/js/website.backend.js @@ -46,15 +46,9 @@ instance.web.form.FieldTextHtml = widget.extend({ // init resize this.resize = function resize() { + if (self.get('effective_readonly')) { return; } if ($("body").hasClass("o_form_FieldTextHtml_fullscreen")) { self.$iframe.css('height', $("body").hasClass('o_form_FieldTextHtml_fullscreen') ? (document.body.clientHeight - self.$iframe.offset().top) + 'px' : ''); - } else if (self.get('effective_readonly')) { - var height = 110; - try { - height += self.$body.height(); - height += parseInt(self.$content.parent().css("margin-top")); - } catch (e) {} - self.$iframe.css("height", height+"px"); } else { self.$iframe.css("height", (self.$body.find("#oe_snippets").length ? 500 : 300) + "px"); } @@ -93,15 +87,17 @@ instance.web.form.FieldTextHtml = widget.extend({ return src; }, initialize_content: function() { - this.$iframe = this.$el.find('iframe'); - this.document = null; - this.$body = $(); - this.$content = $(); - this.dirty = false; - this.editor = false; - window.openerp[this.callback+"_set_value"] = function () {}; + if (!this.get("effective_readonly")) { + this.$iframe = this.$el.find('iframe'); + this.document = null; + this.$body = $(); + this.$content = $(); + this.dirty = false; + this.editor = false; + window.openerp[this.callback+"_set_value"] = function () {}; - this.$iframe.attr("src", this.get_url()); + this.$iframe.attr("src", this.get_url()); + } }, on_content_loaded: function () { var self = this; @@ -134,12 +130,16 @@ instance.web.form.FieldTextHtml = widget.extend({ }, 500); }, render_value: function() { - if (!this.$content) { - return; - } - if(window.openerp[this.callback+"_set_value"]) { - window.openerp[this.callback+"_set_value"](this.get('value') || '', this.view.get_fields_values()); - this.resize(); + if (!this.get("effective_readonly")) { + if (!this.$content) { + return; + } + if(window.openerp[this.callback+"_set_value"]) { + window.openerp[this.callback+"_set_value"](this.get('value') || '', this.view.get_fields_values()); + this.resize(); + } + } else { + this.$el.html(this.get('value')); } }, is_false: function() { @@ -160,4 +160,4 @@ instance.web.form.FieldTextHtml = widget.extend({ } }); -}; \ No newline at end of file +}; From d4152c2a40877fd62815d0a55bce5e64b053db72 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 27 Feb 2015 09:56:06 +0100 Subject: [PATCH 61/87] [IMP] Optimize child_of with parent_left and parent_right [FIX] Make sure it does not give errors on runbot by providing an alternative when parent_left would be zero --- addons/stock/product.py | 35 +++++++++++++++++++++++++++++------ addons/stock/stock.py | 8 +++++--- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/addons/stock/product.py b/addons/stock/product.py index 56770784f9e..1c6769213a0 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -93,11 +93,29 @@ class product_product(osv.osv): operator = context.get('compute_child', True) and 'child_of' or 'in' domain = context.get('force_company', False) and ['&', ('company_id', '=', context['force_company'])] or [] - return ( - domain + [('location_id', operator, location_ids)], - domain + ['&', ('location_dest_id', operator, location_ids), '!', ('location_id', operator, location_ids)], - domain + ['&', ('location_id', operator, location_ids), '!', ('location_dest_id', operator, location_ids)] - ) + locations = location_obj.browse(cr, uid, location_ids, context=context) + if operator == "child_of" and locations and locations[0].parent_left != 0: + loc_domain = [] + dest_loc_domain = [] + for loc in locations: + if loc_domain: + loc_domain = ['|'] + loc_domain + ['&', ('location_id.parent_left', '>=', loc.parent_left), ('location_id.parent_left', '<', loc.parent_right)] + dest_loc_domain = ['|'] + dest_loc_domain + ['&', ('location_dest_id.parent_left', '>=', loc.parent_left), ('location_dest_id.parent_left', '<', loc.parent_right)] + else: + loc_domain += ['&', ('location_id.parent_left', '>=', loc.parent_left), ('location_id.parent_left', '<', loc.parent_right)] + dest_loc_domain += ['&', ('location_dest_id.parent_left', '>=', loc.parent_left), ('location_dest_id.parent_left', '<', loc.parent_right)] + + return ( + domain + loc_domain, + domain + ['&'] + dest_loc_domain + ['!'] + loc_domain, + domain + ['&'] + loc_domain + ['!'] + dest_loc_domain + ) + else: + return ( + domain + [('location_id', operator, location_ids)], + domain + ['&', ('location_dest_id', operator, location_ids), '!', ('location_id', operator, location_ids)], + domain + ['&', ('location_id', operator, location_ids), '!', ('location_dest_id', operator, location_ids)] + ) def _get_domain_dates(self, cr, uid, ids, context): from_date = context.get('from_date', False) @@ -114,7 +132,8 @@ class product_product(osv.osv): field_names = field_names or [] domain_products = [('product_id', 'in', ids)] - domain_quant, domain_move_in, domain_move_out = self._get_domain_locations(cr, uid, ids, context=context) + domain_quant, domain_move_in, domain_move_out = [], [], [] + domain_quant_loc, domain_move_in_loc, domain_move_out_loc = self._get_domain_locations(cr, uid, ids, context=context) domain_move_in += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products domain_move_out += self._get_domain_dates(cr, uid, ids, context=context) + [('state', 'not in', ('done', 'cancel', 'draft'))] + domain_products domain_quant += domain_products @@ -128,9 +147,13 @@ class product_product(osv.osv): moves_in = [] moves_out = [] else: + domain_move_in += domain_move_in_loc + domain_move_out += domain_move_out_loc moves_in = self.pool.get('stock.move').read_group(cr, uid, domain_move_in, ['product_id', 'product_qty'], ['product_id'], context=context) moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context) + domain_quant += domain_quant_loc + print domain_quant quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index 7a694f7cc32..df401cf2a06 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -293,7 +293,7 @@ class stock_quant(osv.osv): _columns = { 'name': fields.function(_get_quant_name, type='char', string='Identifier'), 'product_id': fields.many2one('product.product', 'Product', required=True, ondelete="restrict", readonly=True, select=True), - 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict", readonly=True, select=True), + 'location_id': fields.many2one('stock.location', 'Location', required=True, ondelete="restrict", readonly=True, select=True, auto_join=True), 'qty': fields.float('Quantity', required=True, help="Quantity of products in this quant, in the default unit of measure of the product", readonly=True, select=True), 'package_id': fields.many2one('stock.quant.package', string='Package', help="The package containing this quant", readonly=True, select=True), 'packaging_type_id': fields.related('package_id', 'packaging_id', type='many2one', relation='product.packaging', string='Type of packaging', readonly=True, store=True), @@ -1742,8 +1742,10 @@ class stock_move(osv.osv): 'product_packaging': fields.many2one('product.packaging', 'Prefered Packaging', help="It specifies attributes of packaging like type, quantity of packaging,etc."), - 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, states={'done': [('readonly', True)]}, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."), - 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, states={'done': [('readonly', True)]}, select=True, help="Location where the system will stock the finished products."), + 'location_id': fields.many2one('stock.location', 'Source Location', required=True, select=True, auto_join=True, + states={'done': [('readonly', True)]}, help="Sets a location if you produce at a fixed location. This can be a partner location if you subcontract the manufacturing operations."), + 'location_dest_id': fields.many2one('stock.location', 'Destination Location', required=True, states={'done': [('readonly', True)]}, select=True, + auto_join=True, help="Location where the system will stock the finished products."), 'partner_id': fields.many2one('res.partner', 'Destination Address ', states={'done': [('readonly', True)]}, help="Optional address where goods are to be delivered, specifically used for allotment"), From acbef5552dc05b70e5097b9a9176c87d68e07cab Mon Sep 17 00:00:00 2001 From: Denis Michiels Date: Thu, 26 Feb 2015 15:00:16 +0100 Subject: [PATCH 62/87] [FIX] stock: improvement search on product - The search on qty_available is faster by looking only to the quants. - Less SQL queries --- addons/stock/product.py | 88 +++++++++++++++++++++++------------------ 1 file changed, 49 insertions(+), 39 deletions(-) diff --git a/addons/stock/product.py b/addons/stock/product.py index 1c6769213a0..89029f7ffae 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -185,17 +185,33 @@ class product_product(osv.osv): if operator == '=': operator = '==' - product_ids = self.search(cr, uid, [], context=context) ids = [] - if product_ids: - #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget - #the context that can be set with a location, an owner... - for element in self.browse(cr, uid, product_ids, context=context): - if eval(str(element[field]) + operator + str(value)): - ids.append(element.id) - res.append(('id', 'in', ids)) + if name == 'qty_available': + res.append(('id', 'in', self._search_qty_available(cr, uid, operator, value, context))) + else: + product_ids = self.search(cr, uid, [], context=context) + if product_ids: + #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget + #the context that can be set with a location, an owner... + for element in self.browse(cr, uid, product_ids, context=context): + if eval(str(element[field]) + operator + str(value)): + ids.append(element.id) + res.append(('id', 'in', ids)) return res + def _search_qty_available(self, cr, uid, operator, value, context): + domain_quant = self._get_domain_locations(cr, uid, [], context=context)[0] + if context.get('lot_id'): + domain_quant.append(('lot_id', '=', context['lot_id'])) + if context.get('owner_id'): + domain_quant.append(('owner_id', '=', context['owner_id'])) + if context.get('package_id'): + domain_quant.append(('package_id', '=', context['package_id'])) + quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) + quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) + quants = dict((k, v) for k, v in quants.iteritems() if eval(str(v) + operator + str(value))) + return(list(quants)) + def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None): res = {} for product in self.browse(cr, uid, ids, context=context): @@ -316,41 +332,35 @@ class product_template(osv.osv): _inherit = 'product.template' def _product_available(self, cr, uid, ids, name, arg, context=None): - res = dict.fromkeys(ids, 0) - for product in self.browse(cr, uid, ids, context=context): - res[product.id] = { - # "reception_count": sum([p.reception_count for p in product.product_variant_ids]), - # "delivery_count": sum([p.delivery_count for p in product.product_variant_ids]), - "qty_available": sum([p.qty_available for p in product.product_variant_ids]), - "virtual_available": sum([p.virtual_available for p in product.product_variant_ids]), - "incoming_qty": sum([p.incoming_qty for p in product.product_variant_ids]), - "outgoing_qty": sum([p.outgoing_qty for p in product.product_variant_ids]), + prod_available = dict.fromkeys(ids, 0) + product_ids = self.browse(cr, uid, ids, context=context) + var_ids = [] + for product in product_ids: + var_ids += [p.id for p in product.product_variant_ids] + variant_available= self.pool['product.product']._product_available(cr, uid, var_ids, context=context) + + for product in product_ids: + qty_available = 0 + virtual_available = 0 + incoming_qty = 0 + outgoing_qty = 0 + for p in product.product_variant_ids: + qty_available += variant_available[p.id]["qty_available"] + virtual_available += variant_available[p.id]["virtual_available"] + incoming_qty += variant_available[p.id]["incoming_qty"] + outgoing_qty += variant_available[p.id]["outgoing_qty"] + prod_available[product.id] = { + "qty_available":qty_available, + "virtual_available":virtual_available, + "incoming_qty":incoming_qty, + "outgoing_qty":outgoing_qty, } - return res + return prod_available def _search_product_quantity(self, cr, uid, obj, name, domain, context): prod = self.pool.get("product.product") - res = [] - for field, operator, value in domain: - #to prevent sql injections - assert field in ('qty_available', 'virtual_available', 'incoming_qty', 'outgoing_qty'), 'Invalid domain left operand' - assert operator in ('<', '>', '=', '!=', '<=', '>='), 'Invalid domain operator' - assert isinstance(value, (float, int)), 'Invalid domain right operand' - - if operator == '=': - operator = '==' - - product_ids = prod.search(cr, uid, [], context=context) - ids = [] - if product_ids: - #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget - #the context that can be set with a location, an owner... - for element in prod.browse(cr, uid, product_ids, context=context): - if eval(str(element[field]) + operator + str(value)): - ids.append(element.id) - res.append(('product_variant_ids', 'in', ids)) - return res - + product_variant_ids = prod.search(cr, uid, domain, context=context) + return [('product_variant_ids', 'in', product_variant_ids)] def _product_available_text(self, cr, uid, ids, field_names=None, arg=False, context=None): res = {} From edaec2183595c1d50ad148978359d555a1919995 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Mon, 2 Mar 2015 12:17:15 +0100 Subject: [PATCH 63/87] [IMP] Change order of search terms when creating domain for search qty, clean --- addons/stock/product.py | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/addons/stock/product.py b/addons/stock/product.py index 89029f7ffae..4627f5fabbe 100644 --- a/addons/stock/product.py +++ b/addons/stock/product.py @@ -153,7 +153,6 @@ class product_product(osv.osv): moves_out = self.pool.get('stock.move').read_group(cr, uid, domain_move_out, ['product_id', 'product_qty'], ['product_id'], context=context) domain_quant += domain_quant_loc - print domain_quant quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) @@ -191,8 +190,7 @@ class product_product(osv.osv): else: product_ids = self.search(cr, uid, [], context=context) if product_ids: - #TODO: use a query instead of this browse record which is probably making the too much requests, but don't forget - #the context that can be set with a location, an owner... + #TODO: Still optimization possible when searching virtual quantities for element in self.browse(cr, uid, product_ids, context=context): if eval(str(element[field]) + operator + str(value)): ids.append(element.id) @@ -200,13 +198,14 @@ class product_product(osv.osv): return res def _search_qty_available(self, cr, uid, operator, value, context): - domain_quant = self._get_domain_locations(cr, uid, [], context=context)[0] + domain_quant = [] if context.get('lot_id'): domain_quant.append(('lot_id', '=', context['lot_id'])) if context.get('owner_id'): domain_quant.append(('owner_id', '=', context['owner_id'])) if context.get('package_id'): domain_quant.append(('package_id', '=', context['package_id'])) + domain_quant += self._get_domain_locations(cr, uid, [], context=context)[0] quants = self.pool.get('stock.quant').read_group(cr, uid, domain_quant, ['product_id', 'qty'], ['product_id'], context=context) quants = dict(map(lambda x: (x['product_id'][0], x['qty']), quants)) quants = dict((k, v) for k, v in quants.iteritems() if eval(str(v) + operator + str(value))) @@ -332,7 +331,7 @@ class product_template(osv.osv): _inherit = 'product.template' def _product_available(self, cr, uid, ids, name, arg, context=None): - prod_available = dict.fromkeys(ids, 0) + prod_available = {} product_ids = self.browse(cr, uid, ids, context=context) var_ids = [] for product in product_ids: @@ -350,10 +349,10 @@ class product_template(osv.osv): incoming_qty += variant_available[p.id]["incoming_qty"] outgoing_qty += variant_available[p.id]["outgoing_qty"] prod_available[product.id] = { - "qty_available":qty_available, - "virtual_available":virtual_available, - "incoming_qty":incoming_qty, - "outgoing_qty":outgoing_qty, + "qty_available": qty_available, + "virtual_available": virtual_available, + "incoming_qty": incoming_qty, + "outgoing_qty": outgoing_qty, } return prod_available From 84b47f62eb5fe4123240402b049e2affaeae6bf1 Mon Sep 17 00:00:00 2001 From: Josse Colpaert Date: Fri, 20 Feb 2015 14:26:47 +0100 Subject: [PATCH 64/87] [IMP] Add already the possibility to fill in the products you want in an inventory In order to do that, we change the theoretical quantity into a functional stored field. Therefore the on_change changes, but the old still work. The UoM of the inventory line is also taken into account [IMP] Manual selection, no theoretical qty compute on import, comments --- addons/stock/stock.py | 93 ++++++++++++++++++++++--------------- addons/stock/stock_view.xml | 22 ++++----- 2 files changed, 66 insertions(+), 49 deletions(-) diff --git a/addons/stock/stock.py b/addons/stock/stock.py index df401cf2a06..9816ebb20f4 100644 --- a/addons/stock/stock.py +++ b/addons/stock/stock.py @@ -2573,7 +2573,7 @@ class stock_inventory(osv.osv): :rtype: list of tuple """ #default available choices - res_filter = [('none', _('All products')), ('product', _('One product only'))] + res_filter = [('none', _('All products')), ('partial', _('Manual Selection of Products')), ('product', _('One product only'))] settings_obj = self.pool.get('stock.config.settings') config_ids = settings_obj.search(cr, uid, [], limit=1, order='id DESC', context=context) #If we don't have updated config until now, all fields are by default false and so should be not dipslayed @@ -2616,7 +2616,10 @@ class stock_inventory(osv.osv): 'partner_id': fields.many2one('res.partner', 'Inventoried Owner', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Owner to focus your inventory on a particular Owner."), 'lot_id': fields.many2one('stock.production.lot', 'Inventoried Lot/Serial Number', readonly=True, states={'draft': [('readonly', False)]}, help="Specify Lot/Serial Number to focus your inventory on a particular Lot/Serial Number.", copy=False), 'move_ids_exist': fields.function(_get_move_ids_exist, type='boolean', string=' Stock Move Exists?', help='technical field for attrs in view'), - 'filter': fields.selection(_get_available_filters, 'Selection Filter', required=True), + 'filter': fields.selection(_get_available_filters, 'Inventory of', required=True, + help="If you do an entire inventory, you can choose 'All Products' and it will prefill the inventory with the current stock. If you only do some products "\ + "(e.g. Cycle Counting) you can choose 'Manual Selection of Products' and the system won't propose anything. You can also let the "\ + "system propose for a single product / lot /... "), 'total_qty': fields.function(_get_total_qty, type="float"), } @@ -2728,25 +2731,11 @@ class stock_inventory(osv.osv): for inventory in self.browse(cr, uid, ids, context=context): # If there are inventory lines already (e.g. from import), respect those and set their theoretical qty line_ids = [line.id for line in inventory.line_ids] - if not line_ids: + if not line_ids and inventory.filter != 'partial': #compute the inventory lines and create them vals = self._get_inventory_lines(cr, uid, inventory, context=context) for product_line in vals: inventory_line_obj.create(cr, uid, product_line, context=context) - else: - # On import calculate theoretical quantity - quant_obj = self.pool.get("stock.quant") - for line in inventory.line_ids: - dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), - ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id)] - if line.package_id: - dom += [('package_id', '=', line.package_id.id)] - quants = quant_obj.search(cr, uid, dom, context=context) - tot_qty = 0 - for quant in quant_obj.browse(cr, uid, quants, context=context): - tot_qty += quant.qty - inventory_line_obj.write(cr, uid, [line.id], {'theoretical_qty': tot_qty}, context=context) - return self.write(cr, uid, ids, {'state': 'confirm', 'date': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) def _get_inventory_lines(self, cr, uid, inventory, context=None): @@ -2802,6 +2791,19 @@ class stock_inventory_line(osv.osv): def _get_prodlot_change(self, cr, uid, ids, context=None): return self.pool.get('stock.inventory.line').search(cr, uid, [('prod_lot_id', 'in', ids)], context=context) + def _get_theoretical_qty(self, cr, uid, ids, name, args, context=None): + res = {} + quant_obj = self.pool["stock.quant"] + uom_obj = self.pool["product.uom"] + for line in self.browse(cr, uid, ids, context=context): + quant_ids = self._get_quants(cr, uid, line, context=context) + quants = quant_obj.browse(cr, uid, quant_ids, context=context) + tot_qty = sum([x.qty for x in quants]) + if line.product_uom_id and line.product_id.uom_id.id != line.product_uom_id.id: + tot_qty = uom_obj._compute_qty_obj(cr, uid, line.product_id.uom_id, tot_qty, line.product_uom_id, context=context) + res[line.id] = tot_qty + return res + _columns = { 'inventory_id': fields.many2one('stock.inventory', 'Inventory', ondelete='cascade', select=True), 'location_id': fields.many2one('stock.location', 'Location', required=True, select=True), @@ -2812,7 +2814,8 @@ class stock_inventory_line(osv.osv): 'company_id': fields.related('inventory_id', 'company_id', type='many2one', relation='res.company', string='Company', store=True, select=True, readonly=True), 'prod_lot_id': fields.many2one('stock.production.lot', 'Serial Number', domain="[('product_id','=',product_id)]"), 'state': fields.related('inventory_id', 'state', type='char', string='Status', readonly=True), - 'theoretical_qty': fields.float('Theoretical Quantity', digits_compute=dp.get_precision('Product Unit of Measure'), readonly=True), + 'theoretical_qty': fields.function(_get_theoretical_qty, type='float', digits_compute=dp.get_precision('Product Unit of Measure'), + store=True, readonly=True, string="Theoretical Quantity"), 'partner_id': fields.many2one('res.partner', 'Owner'), 'product_name': fields.related('product_id', 'name', type='char', string='Product Name', store={ 'product.product': (_get_product_name_change, ['name', 'default_code'], 20), @@ -2829,9 +2832,40 @@ class stock_inventory_line(osv.osv): } _defaults = { - 'product_qty': 1, + 'product_qty': 0, } + def _get_quants(self, cr, uid, line, context=None): + quant_obj = self.pool["stock.quant"] + dom = [('company_id', '=', line.company_id.id), ('location_id', 'child_of', line.location_id.id), ('lot_id', '=', line.prod_lot_id.id), + ('product_id','=', line.product_id.id), ('owner_id', '=', line.partner_id.id), ('package_id', '=', line.package_id.id)] + quants = quant_obj.search(cr, uid, dom, context=context) + return quants + + def onchange_createline(self, cr, uid, ids, location_id=False, product_id=False, uom_id=False, package_id=False, prod_lot_id=False, partner_id=False, company_id=False, context=None): + quant_obj = self.pool["stock.quant"] + uom_obj = self.pool["product.uom"] + res = {'value': {}} + # If no UoM already put the default UoM of the product + if product_id and not uom_id: + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + res['value']['product_uom_id'] = product.uom_id.id + res['domain'] = {'product_uom_id': [('category_id','=',product.uom_id.category_id.id)]} + uom_id = product.uom_id.id + # Calculate theoretical quantity by searching the quants as in quants_get + if product_id and location_id: + product = self.pool.get('product.product').browse(cr, uid, product_id, context=context) + if not company_id: + company_id = self.pool.get('res.users').browse(cr, uid, uid, context=context).company_id.id + dom = [('company_id', '=', company_id), ('location_id', 'child_of', location_id), ('lot_id', '=', prod_lot_id), + ('product_id','=', product_id), ('owner_id', '=', partner_id), ('package_id', '=', package_id)] + quants = quant_obj.search(cr, uid, dom, context=context) + th_qty = sum([x.qty for x in quant_obj.browse(cr, uid, quants, context=context)]) + if product_id and uom_id and product.uom_id.id != uom_id: + th_qty = uom_obj._compute_qty(cr, uid, product.uom_id.id, th_qty, uom_id) + res['value']['theoretical_qty'] = th_qty + return res + def _resolve_inventory_line(self, cr, uid, inventory_line, context=None): stock_move_obj = self.pool.get('stock.move') diff = inventory_line.theoretical_qty - inventory_line.product_qty @@ -2862,26 +2896,11 @@ class stock_inventory_line(osv.osv): vals['product_uom_qty'] = diff return stock_move_obj.create(cr, uid, vals, context=context) + # Should be left out in next version def restrict_change(self, cr, uid, ids, theoretical_qty, context=None): - if ids and theoretical_qty: - #if the user try to modify a line prepared by openerp, reject the change and display an error message explaining how he should do - old_value = self.browse(cr, uid, ids[0], context=context) - return { - 'value': { - 'product_id': old_value.product_id.id, - 'product_uom_id': old_value.product_uom_id.id, - 'location_id': old_value.location_id.id, - 'prod_lot_id': old_value.prod_lot_id.id, - 'package_id': old_value.package_id.id, - 'partner_id': old_value.partner_id.id, - }, - 'warning': { - 'title': _('Error'), - 'message': _('You can only change the checked quantity of an existing inventory line. If you want modify a data, please set the checked quantity to 0 and create a new inventory line.') - } - } return {} + # Should be left out in next version def on_change_product_id(self, cr, uid, ids, product, uom, theoretical_qty, context=None): """ Changes UoM @param location_id: Location id @@ -2889,8 +2908,6 @@ class stock_inventory_line(osv.osv): @param uom: UoM product @return: Dictionary of changed values """ - if ids and theoretical_qty: - return self.restrict_change(cr, uid, ids, theoretical_qty, context=context) if not product: return {'value': {'product_uom_id': False}} obj_product = self.pool.get('product.product').browse(cr, uid, product, context=context) diff --git a/addons/stock/stock_view.xml b/addons/stock/stock_view.xml index 41870b3d829..ee751063931 100644 --- a/addons/stock/stock_view.xml +++ b/addons/stock/stock_view.xml @@ -53,7 +53,7 @@ - + stock.inventory.filter stock.inventory @@ -107,10 +107,10 @@ - - - - + + + + @@ -118,12 +118,12 @@
    - +
    + + From f667e0ead71fb6f63fa910c23b72349581deec2c Mon Sep 17 00:00:00 2001 From: Sathors Date: Mon, 2 Mar 2015 09:04:26 -0500 Subject: [PATCH 66/87] Added the individual CLA --- doc/cla/individual/Sathors.md | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 doc/cla/individual/Sathors.md diff --git a/doc/cla/individual/Sathors.md b/doc/cla/individual/Sathors.md new file mode 100644 index 00000000000..8e005313156 --- /dev/null +++ b/doc/cla/individual/Sathors.md @@ -0,0 +1,11 @@ +Ecuador, 2nd of March 2015 + +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, + +Nicolas Schmitt sathors@free.fr https://github.com/Sathors From 1b6345f4f20b5d17f94dc3a170d2a2354088f7af Mon Sep 17 00:00:00 2001 From: Sathors Date: Fri, 20 Feb 2015 11:29:40 -0500 Subject: [PATCH 67/87] [ADD] doc: advanced options category including auto-reload. So far there is no info about the "Advanced Options" category of the --help. Added only the --auto-reload, others can be added later. --- doc/reference/cmdline.rst | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/doc/reference/cmdline.rst b/doc/reference/cmdline.rst index 280cd079622..434b0d9cd9d 100644 --- a/doc/reference/cmdline.rst +++ b/doc/reference/cmdline.rst @@ -278,6 +278,14 @@ customize the amout of logging output In case of conflict between :option:`--log-level` and :option:`--log-handler`, the latter is used +Advanced options +---------------- + +.. option:: --auto-reload + + enable auto-reloading of python files and xml files without having to + restart the server. Requires pyinotify_. + .. _reference/cmdline/scaffold: Scaffolding @@ -347,3 +355,4 @@ to that file. http://www.postgresql.org/docs/9.2/static/libpq-connect.html#AEN38208 .. _Werkzeug's proxy support: http://werkzeug.pocoo.org/docs/0.9/contrib/fixers/#werkzeug.contrib.fixers.ProxyFix +.. _pyinotify: https://github.com/seb-m/pyinotify/wiki From 29cf197cfa118a7e56d309405a6f8dd2cc9dff8c Mon Sep 17 00:00:00 2001 From: Yenthe Date: Fri, 27 Feb 2015 13:21:20 +0100 Subject: [PATCH 68/87] [FIX] website_event: typo compenents to components > Fixed the typo's. --- addons/website_event/data/event_demo.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/addons/website_event/data/event_demo.xml b/addons/website_event/data/event_demo.xml index e536f888783..75c31e67f86 100644 --- a/addons/website_event/data/event_demo.xml +++ b/addons/website_event/data/event_demo.xml @@ -61,7 +61,7 @@
  • Loops
  • Arrays
  • @@ -227,7 +227,7 @@
  • Loops
  • Arrays
  • @@ -405,7 +405,7 @@
  • Loops
  • Arrays
  • From e11ed3f01cfbf6a6123fcb12d586755836543e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Maes?= Date: Fri, 27 Feb 2015 13:46:32 +0100 Subject: [PATCH 69/87] [FIX] project : issue_ids field should contains only the open issues. Closed issues are interesting for reporting, not for today job. A issue is closed when its stage is fold. this implies changes in rating : a search is required to find issue of the current project. This may introduce performance issue in a case of big data. Create a custom request is a too important change in a stable version. --- addons/project_issue/project_issue.py | 4 ++-- addons/rating_project_issue/models/project_issue.py | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py index eb5ce771a95..a7f89ec53a5 100644 --- a/addons/project_issue/project_issue.py +++ b/addons/project_issue/project_issue.py @@ -494,7 +494,7 @@ class project(osv.Model): states={'close': [('readonly', True)], 'cancelled': [('readonly', True)]}), 'issue_count': fields.function(_issue_count, type='integer', string="Issues",), 'issue_ids': fields.one2many('project.issue', 'project_id', string="Issues", - domain=[('date_closed', '!=', False)]), + domain=[('stage_id.fold', '=', False)]), } def _check_escalation(self, cr, uid, ids, context=None): @@ -581,7 +581,7 @@ class res_partner(osv.osv): partner_id: Issue.search_count(cr,uid, [('partner_id', '=', partner_id)]) for partner_id in ids } - + """ Inherits partner and adds Issue information in the partner form """ _inherit = 'res.partner' _columns = { diff --git a/addons/rating_project_issue/models/project_issue.py b/addons/rating_project_issue/models/project_issue.py index 339027d74d7..778000e9418 100644 --- a/addons/rating_project_issue/models/project_issue.py +++ b/addons/rating_project_issue/models/project_issue.py @@ -28,6 +28,7 @@ class Project(models.Model): def _compute_percentage_satisfaction_project(self): super(Project, self)._compute_percentage_satisfaction_project() Rating = self.env['rating.rating'] + Issue = self.env['project.issue'] for record in self.filtered(lambda record: record.use_tasks or record.use_issues): if record.use_tasks or record.use_issues: # built the domain according the project parameters (use tasks and/or issues) @@ -37,8 +38,10 @@ class Project(models.Model): res_models.append('project.task') domain += ['&', ('res_model', '=', 'project.task'), ('res_id', 'in', record.tasks.ids)] if record.use_issues: + # TODO: if performance issue, compute the satisfaction with a custom request joining rating and task/issue. + issues = Issue.search([('project_id', '=', record.id)]) res_models.append('project.issue') - domain += ['&', ('res_model', '=', 'project.issue'), ('res_id', 'in', record.issue_ids.ids)] + domain += ['&', ('res_model', '=', 'project.issue'), ('res_id', 'in', issues.ids)] if len(res_models) == 2: domain = ['|'] + domain domain = ['&', ('rating', '>=', 0)] + domain From bc1bcfd5ff638af91aa93e44b996aeed60bc8273 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=A9rome=20Maes?= Date: Mon, 2 Mar 2015 12:31:42 +0100 Subject: [PATCH 70/87] [FIX] rating_project_issue : fix domain in view for stat button. Add help tooltip on field 'is_visible_happy_customer' --- addons/rating_project/models/project.py | 3 ++- addons/rating_project/views/project_view.xml | 2 +- addons/rating_project_issue/views/project_issue_view.xml | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/addons/rating_project/models/project.py b/addons/rating_project/models/project.py index d2f08cc46cc..68ebd3420dc 100644 --- a/addons/rating_project/models/project.py +++ b/addons/rating_project/models/project.py @@ -52,7 +52,8 @@ class Project(models.Model): compute='_compute_percentage_satisfaction_task', string='% Happy', store=True, default=-1) percentage_satisfaction_project = fields.Integer( compute="_compute_percentage_satisfaction_project", string="% Happy", store=True, default=-1) - is_visible_happy_customer = fields.Boolean(string="Customer Satisfaction", default=False) + is_visible_happy_customer = fields.Boolean(string="Customer Satisfaction", default=False, + help="Display informations about rating of the project on kanban and form view. This buttons will only be displayed if at least a rating exists.") @api.multi diff --git a/addons/rating_project/views/project_view.xml b/addons/rating_project/views/project_view.xml index 17d32a97030..a077f00169f 100644 --- a/addons/rating_project/views/project_view.xml +++ b/addons/rating_project/views/project_view.xml @@ -10,7 +10,7 @@