diff options
| author | Christophe Simonis <chs@odoo.com> | 2018-08-27 11:16:44 +0200 |
|---|---|---|
| committer | Christophe Simonis <chs@odoo.com> | 2018-08-27 11:16:44 +0200 |
| commit | a7a30791def5a033f5e77d42f155bafae0c430cb (patch) | |
| tree | ba668f1a961ccd7455be33dfa50e0b763f7c360b /addons | |
| parent | 0da9da4c447eed7dafc10a17d59000a6cfa5f55f (diff) | |
| parent | a5187cef1078b01a27e05e761d7592b6857f41f9 (diff) | |
[MERGE] forward port branch saas-11.4 up to a5187cef107
Diffstat (limited to 'addons')
27 files changed, 246 insertions, 36 deletions
diff --git a/addons/account/models/account_bank_statement.py b/addons/account/models/account_bank_statement.py index 0a91e072607..9374827096f 100644 --- a/addons/account/models/account_bank_statement.py +++ b/addons/account/models/account_bank_statement.py @@ -4,7 +4,7 @@ from odoo import api, fields, models, _ from odoo.osv import expression from odoo.tools import float_is_zero, pycompat from odoo.tools import float_compare, float_round, float_repr -from odoo.tools.misc import formatLang +from odoo.tools.misc import formatLang, format_date from odoo.exceptions import UserError, ValidationError import time diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index 61b5bcb9c6c..5b83ebfb0af 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -6,7 +6,7 @@ from collections import OrderedDict from odoo import api, fields, models, _ from odoo.osv import expression from odoo.exceptions import RedirectWarning, UserError, ValidationError -from odoo.tools.misc import formatLang +from odoo.tools.misc import formatLang, format_date from odoo.tools import float_is_zero, float_compare from odoo.tools.safe_eval import safe_eval from odoo.addons import decimal_precision as dp diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js index 9bc336af4b8..73b98a2f40b 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_model.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js @@ -443,6 +443,7 @@ var StatementModel = BasicModel.extend({ model: 'account.reconciliation.widget', method: 'get_bank_statement_line_data', args: [ids, excluded_ids], + context: self.context, }) .then(self._formatLine.bind(self)); }, @@ -1175,6 +1176,7 @@ var StatementModel = BasicModel.extend({ model: 'account.reconciliation.widget', method: 'get_move_lines_for_bank_statement_line', args: [line.id, line.st_line.partner_id, excluded_ids, filter, offset, limit], + context: this.context, }) .then(this._formatMoveLine.bind(this, handle)); }, @@ -1240,6 +1242,7 @@ var ManualModel = StatementModel.extend({ */ load: function (context) { var self = this; + this.context = context; var domain_account_id = []; if (context && context.company_ids) { @@ -1550,6 +1553,7 @@ var ManualModel = StatementModel.extend({ model: 'account.reconciliation.widget', method: 'get_move_lines_for_manual_reconciliation', args: args, + context: this.context, }) .then(this._formatMoveLine.bind(this, handle)); }, diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index 67963e8e6d0..43fb9c795aa 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -171,7 +171,7 @@ class Lead(models.Model): @api.depends('date_open') def _compute_day_open(self): """ Compute difference between create date and open date """ - for lead in self.filtered(lambda l: l.date_open): + for lead in self.filtered(lambda l: l.date_open and l.create_date): date_create = fields.Datetime.from_string(lead.create_date) date_open = fields.Datetime.from_string(lead.date_open) lead.day_open = abs((date_open - date_create).days) @@ -179,7 +179,7 @@ class Lead(models.Model): @api.depends('date_closed') def _compute_day_close(self): """ Compute difference between current date and log date """ - for lead in self.filtered(lambda l: l.date_closed): + for lead in self.filtered(lambda l: l.date_closed and l.create_date): date_create = fields.Datetime.from_string(lead.create_date) date_close = fields.Datetime.from_string(lead.date_closed) lead.day_close = abs((date_close - date_create).days) @@ -714,7 +714,7 @@ class Lead(models.Model): partner_match_domain.append(('partner_id', '=', partner_id)) partner_match_domain = ['|'] * (len(partner_match_domain) - 1) + partner_match_domain if not partner_match_domain: - return [] + return self.env['crm.lead'] domain = partner_match_domain if not include_lost: domain += ['&', ('active', '=', True), ('probability', '<', 100)] diff --git a/addons/hr/models/res_users.py b/addons/hr/models/res_users.py index 08c3d4f6d14..9b1777c5084 100644 --- a/addons/hr/models/res_users.py +++ b/addons/hr/models/res_users.py @@ -15,7 +15,7 @@ class User(models.Model): selection=lambda self: self._get_group_selection('base.module_category_human_resources'), string="Human Resources", compute='_compute_groups_id', inverse='_inverse_groups_id', category_xml_id='base.module_category_human_resources', - help='Officer: The user will be able to approve document created by employees.\nManager: The user will have an access to the human resources configuration as well as statistic reports.') + help='Officer: The user will be able to approve document created by employees.\nManager: The user will have access to the human resources configuration as well as statistic reports.') @api.multi def write(self, vals): diff --git a/addons/hr/security/hr_security.xml b/addons/hr/security/hr_security.xml index ddb4f9c5cbe..09e16244846 100644 --- a/addons/hr/security/hr_security.xml +++ b/addons/hr/security/hr_security.xml @@ -10,7 +10,7 @@ <record id="group_hr_manager" model="res.groups"> <field name="name">Manager</field> - <field name="comment">The user will have an access to the human resources configuration as well as statistic reports.</field> + <field name="comment">The user will have access to the human resources configuration as well as statistic reports.</field> <field name="category_id" ref="base.module_category_human_resources"/> <field name="implied_ids" eval="[(4, ref('group_hr_user'))]"/> <field name="users" eval="[(4, ref('base.user_root')), (4, ref('base.user_admin'))]"/> diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index cd5a123c691..b1d1d0297ef 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -722,6 +722,6 @@ class HrExpenseSheet(models.Model): for expense_report in self.filtered(lambda hol: hol.state == 'submit'): self.activity_schedule( 'hr_expense.mail_act_expense_approval', - user_id=expense_report._get_responsible_for_approval().id) + user_id=expense_report.sudo()._get_responsible_for_approval().id) self.filtered(lambda hol: hol.state == 'approve').activity_feedback(['hr_expense.mail_act_expense_approval']) self.filtered(lambda hol: hol.state == 'cancel').activity_unlink(['hr_expense.mail_act_expense_approval']) diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py index ffaa77d624e..3a0fdde5ba1 100644 --- a/addons/hr_holidays/models/hr_leave_allocation.py +++ b/addons/hr_holidays/models/hr_leave_allocation.py @@ -467,12 +467,12 @@ class HolidaysAllocation(models.Model): elif allocation.state == 'confirm': allocation.activity_schedule( 'hr_holidays.mail_act_leave_allocation_approval', - user_id=allocation._get_responsible_for_approval().id) + user_id=allocation.sudo()._get_responsible_for_approval().id) elif allocation.state == 'validate1': allocation.activity_feedback(['hr_holidays.mail_act_leave_allocation_approval']) allocation.activity_schedule( 'hr_holidays.mail_act_leave_allocation_second_approval', - user_id=allocation._get_responsible_for_approval().id) + user_id=allocation.sudo()._get_responsible_for_approval().id) elif allocation.state == 'validate': to_do |= allocation elif allocation.state == 'refuse': diff --git a/addons/hr_holidays/report/hr_leave_reports.xml b/addons/hr_holidays/report/hr_leave_reports.xml index 004433a3c62..51f339ca65b 100644 --- a/addons/hr_holidays/report/hr_leave_reports.xml +++ b/addons/hr_holidays/report/hr_leave_reports.xml @@ -12,8 +12,6 @@ <separator/> <filter name="active_types" string="Active Types" domain="[('holiday_status_id.active', '=', True)]" help="Filters only on requests that belong to an leave type that is 'active' (active field is True)"/> <separator/> - <filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/> - <separator/> <filter string="My Department Leaves" name="department" domain="[('department_id.manager_id.user_id', '=', uid)]" help="My Department Leaves"/> <filter name="my_team_leaves" string="My Team Leaves" domain="[('employee_id.parent_id.user_id', '=', uid)]" groups="hr_holidays.group_hr_holidays_manager" help="Leaves of Your Team Member"/> <separator/> diff --git a/addons/mail/models/mail_channel.py b/addons/mail/models/mail_channel.py index 1c316673935..3f2a655e1a9 100644 --- a/addons/mail/models/mail_channel.py +++ b/addons/mail/models/mail_channel.py @@ -621,8 +621,8 @@ class Channel(models.Model): AND P.partner_id IN %s AND channel_type LIKE 'chat' GROUP BY P.channel_id - HAVING COUNT(P.partner_id) = %s - """, (tuple(partners_to), len(partners_to),)) + HAVING array_agg(P.partner_id ORDER BY P.partner_id) = %s + """, (tuple(partners_to), sorted(list(partners_to)),)) result = self.env.cr.dictfetchall() if result: # get the existing channel between the given partners diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index de36e854077..da0c45051ce 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -1913,6 +1913,7 @@ class MailThread(models.AbstractModel): if not attachment: attachment = fname_mapping.get(node.get('data-filename'), '') if attachment: + attachment.generate_access_token() node.set('src', '/web/image/%s?access_token=%s' % (attachment.id, attachment.access_token)) postprocessed = True if postprocessed: diff --git a/addons/mrp/static/src/scss/mrp.scss b/addons/mrp/static/src/scss/mrp.scss deleted file mode 100644 index a0ce531779f..00000000000 --- a/addons/mrp/static/src/scss/mrp.scss +++ /dev/null @@ -1,8 +0,0 @@ -.o_form_view { - .o_field_pdfviewer { - width: 100%; - height: 450px; - border: 0; - margin-top: 10px; - } -} diff --git a/addons/mrp/views/mrp_templates.xml b/addons/mrp/views/mrp_templates.xml index 361a0d0018a..f51f0427e19 100644 --- a/addons/mrp/views/mrp_templates.xml +++ b/addons/mrp/views/mrp_templates.xml @@ -3,7 +3,6 @@ <template id="assets_backend" name="mrp assets" inherit_id="web.assets_backend"> <xpath expr="." position="inside"> <script type="text/javascript" src="/mrp/static/src/js/mrp.js"></script> - <link rel="stylesheet" type="text/scss" href="/mrp/static/src/scss/mrp.scss"/> <script type="text/javascript" src="/mrp/static/src/js/mrp_bom_report.js"></script> </xpath> </template> diff --git a/addons/point_of_sale/models/pos_order.py b/addons/point_of_sale/models/pos_order.py index 25072ad578d..41b27d4e979 100644 --- a/addons/point_of_sale/models/pos_order.py +++ b/addons/point_of_sale/models/pos_order.py @@ -148,7 +148,7 @@ class PosOrder(models.Model): cash_journal_id = cash_journal[0].id order.add_payment({ 'amount': -pos_order['amount_return'], - 'payment_date': fields.Datetime.now(), + 'payment_date': fields.Date.context_today(self), 'payment_name': _('return'), 'journal': cash_journal_id, }) diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml index 4a8a114e641..da106f5ab71 100644 --- a/addons/point_of_sale/views/pos_config_view.xml +++ b/addons/point_of_sale/views/pos_config_view.xml @@ -419,7 +419,8 @@ <div class="content-group mt16" attrs="{'invisible': [('module_account', '=', False)]}"> <div class="row mt16"> <label string="Invoice Journal" for="invoice_journal_id" class="col-lg-3 o_light_label"/> - <field name="invoice_journal_id"/> + <field name="invoice_journal_id" + attrs="{'required': [('module_account_invoicing', '=', True)]}"/> </div> </div> </div> diff --git a/addons/stock/models/stock_move.py b/addons/stock/models/stock_move.py index b760ff82a45..9758f356375 100644 --- a/addons/stock/models/stock_move.py +++ b/addons/stock/models/stock_move.py @@ -891,11 +891,13 @@ class StockMove(models.Model): assigned_moves = self.env['stock.move'] partially_available_moves = self.env['stock.move'] for move in self.filtered(lambda m: m.state in ['confirmed', 'waiting', 'partially_available']): + missing_reserved_uom_quantity = move.product_uom_qty - move.reserved_availability + missing_reserved_quantity = move.product_uom._compute_quantity(missing_reserved_uom_quantity, move.product_id.uom_id, rounding_method='HALF-UP') if move.location_id.should_bypass_reservation()\ or move.product_id.type == 'consu': # create the move line(s) but do not impact quants if move.product_id.tracking == 'serial' and (move.picking_type_id.use_create_lots or move.picking_type_id.use_existing_lots): - for i in range(0, int(move.product_qty - move.reserved_availability)): + for i in range(0, int(missing_reserved_quantity)): self.env['stock.move.line'].create(move._prepare_move_line_vals(quantity=1)) else: to_update = move.move_line_ids.filtered(lambda ml: ml.product_uom_id == move.product_uom and @@ -906,16 +908,16 @@ class StockMove(models.Model): not ml.package_id and not ml.owner_id) if to_update: - to_update[0].product_uom_qty += move.product_qty - move.reserved_availability + to_update[0].product_uom_qty += missing_reserved_uom_quantity else: - self.env['stock.move.line'].create(move._prepare_move_line_vals(quantity=move.product_qty - move.reserved_availability)) + self.env['stock.move.line'].create(move._prepare_move_line_vals(quantity=missing_reserved_quantity)) assigned_moves |= move else: if not move.move_orig_ids: if move.procure_method == 'make_to_order': continue # If we don't need any quantity, consider the move assigned. - need = move.product_qty - move.reserved_availability + need = missing_reserved_quantity if float_is_zero(need, precision_rounding=move.product_id.uom_id.rounding): assigned_moves |= move continue diff --git a/addons/stock/tests/test_move.py b/addons/stock/tests/test_move.py index 4434f45b9bb..53b9b842cc7 100644 --- a/addons/stock/tests/test_move.py +++ b/addons/stock/tests/test_move.py @@ -1099,6 +1099,27 @@ class StockMove(TransactionCase): move_partial._action_assign() self.assertEqual(move_partial.state, 'assigned') + def test_availability_9(self): + """ Test the assignment mechanism when the product quantity is increase + on a receipt move. + """ + move_receipt = self.env['stock.move'].create({ + 'name': 'test_receipt_edit', + 'location_id': self.supplier_location.id, + 'location_dest_id': self.stock_location.id, + 'product_id': self.product1.id, + 'product_uom': self.uom_dozen.id, + 'product_uom_qty': 1.0, + }) + + move_receipt._action_confirm() + move_receipt._action_assign() + self.assertEqual(move_receipt.state, 'assigned') + move_receipt.product_uom_qty = 3.0 + move_receipt._action_assign() + self.assertEqual(move_receipt.state, 'assigned') + self.assertEqual(move_receipt.move_line_ids.product_uom_qty, 3) + def test_unreserve_1(self): """ Check that unreserving a stock move sets the products reserved as available and set the state back to confirmed. diff --git a/addons/web/models/ir_qweb.py b/addons/web/models/ir_qweb.py index 34d2ecb0d5d..b734c4f193c 100644 --- a/addons/web/models/ir_qweb.py +++ b/addons/web/models/ir_qweb.py @@ -65,6 +65,7 @@ class Image(models.AbstractModel): atts["alt"] = alt atts["data-zoom"] = src_zoom and u'1' or None atts["data-zoom-image"] = src_zoom + atts["data-no-post-process"] = options.get('data-no-post-process') atts = self.env['ir.qweb']._post_processing_att('img', atts, options.get('template_options')) diff --git a/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js b/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js index d0eed176d9c..6fec5795bee 100644 --- a/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js +++ b/addons/web/static/src/js/chrome/keyboard_navigation_mixin.js @@ -103,6 +103,14 @@ odoo.define('web.KeyboardNavigationMixin', function (require) { * return {undefined|false} */ _onKeyDown: function (keyDownEvent) { + if ($('body.o_ui_blocked').length && + (keyDownEvent.altKey || keyDownEvent.key === 'Alt') && + !keyDownEvent.ctrlKey) { + if (keyDownEvent.preventDefault) keyDownEvent.preventDefault(); else keyDownEvent.returnValue = false; + if (keyDownEvent.stopPropagation) keyDownEvent.stopPropagation(); + if (keyDownEvent.cancelBubble) keyDownEvent.cancelBubble = true; + return false; + } if (!this._areAccessKeyVisible && (keyDownEvent.altKey || keyDownEvent.key === 'Alt') && !keyDownEvent.ctrlKey) { diff --git a/addons/web/static/src/js/core/misc.js b/addons/web/static/src/js/core/misc.js index be0f012752c..cd9a73ef22b 100644 --- a/addons/web/static/src/js/core/misc.js +++ b/addons/web/static/src/js/core/misc.js @@ -52,6 +52,29 @@ if ($.blockUI) { } +/** + * Remove the "accesskey" attributes to avoid the use of the access keys + * while the blockUI is enable. + */ + +function blockAccessKeys() { + var elementWithAccessKey = []; + elementWithAccessKey = document.querySelectorAll('[accesskey]'); + _.each(elementWithAccessKey, function (elem) { + elem.setAttribute("data-accesskey",elem.getAttribute('accesskey')); + elem.removeAttribute('accesskey'); + }); +} + +function unblockAccessKeys() { + var elementWithDataAccessKey = []; + elementWithDataAccessKey = document.querySelectorAll('[data-accesskey]'); + _.each(elementWithDataAccessKey, function (elem) { + elem.setAttribute('accesskey', elem.getAttribute('data-accesskey')); + elem.removeAttribute('data-accesskey'); + }); +} + var throbbers = []; function blockUI() { @@ -60,6 +83,7 @@ function blockUI() { throbbers.push(throbber); throbber.appendTo($(".oe_blockui_spin_container")); $(document.body).addClass('o_ui_blocked'); + blockAccessKeys(); return tmp; } @@ -67,6 +91,7 @@ function unblockUI() { _.invoke(throbbers, 'destroy'); throbbers = []; $(document.body).removeClass('o_ui_blocked'); + unblockAccessKeys(); return $.unblockUI.apply($, arguments); } diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 186d0798e8f..c095871b75d 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1006,8 +1006,14 @@ var BasicModel = AbstractModel.extend({ params: params, }) .then(function () { + var offset = options.offset ? options.offset : 0; + var old_data = data.data.slice(); data.data = _.sortBy(data.data, function (d) { - return _.indexOf(resIDs, self.localData[d].res_id); + if (_.contains(resIDs, self.localData[d].res_id)) { + return _.indexOf(resIDs, self.localData[d].res_id) + offset; + } else { + return _.indexOf(old_data, d); + } }); data.res_ids = []; _.each(data.data, function (d) { diff --git a/addons/web/static/src/js/views/graph/graph_renderer.js b/addons/web/static/src/js/views/graph/graph_renderer.js index e801fcc5323..80edb8047ba 100644 --- a/addons/web/static/src/js/views/graph/graph_renderer.js +++ b/addons/web/static/src/js/views/graph/graph_renderer.js @@ -389,7 +389,7 @@ return AbstractRenderer.extend({ }; } data_dict[serie].values.push({ - x: tick, y: graphData[i].value, + x: tick - 1, y: graphData[i].value, }); data = _.map(data_dict, identity); } diff --git a/addons/web/static/src/scss/fields.scss b/addons/web/static/src/scss/fields.scss index 1c6fdf3abb1..3681c38eb47 100644 --- a/addons/web/static/src/scss/fields.scss +++ b/addons/web/static/src/scss/fields.scss @@ -403,6 +403,14 @@ } } + // PDF Viewer + &.o_field_pdfviewer, .o_pdfview_iframe { + width: 100%; + height: 450px; + border: 0; + margin-top: 10px; + } + // Copy to clipboard &.o_field_copy { position: relative; diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index 6839223a903..f05c89c1a7d 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -882,6 +882,23 @@ var MockServer = Class.extend({ return data; }, /** + * Simulates a 'resequence' operation + * + * @private + * @param {string} model + * @param {string} field + * @param {Array} ids + */ + _mockResequence: function (args) { + var offset = args.offset ? Number(args.offset) : 0; + var field = args.field ? args.field : 'sequence'; + var records = this.data[args.model].records; + for (var i in args.ids) { + var record = _.findWhere(this.data[args.model].records, {id: args.ids[i]}); + record[field] = Number(i) + offset; + } + }, + /** * Simulate a 'search_count' operation * * @private @@ -1040,7 +1057,7 @@ var MockServer = Class.extend({ return $.when(this._mockSearchReadController(args)); case '/web/dataset/resequence': - return $.when(); + return $.when(this._mockResequence(args)); } if (route.indexOf('/web/image') >= 0 || _.contains(['.png', '.jpg'], route.substr(route.length - 4))) { return $.when(); diff --git a/addons/web/static/tests/views/graph_tests.js b/addons/web/static/tests/views/graph_tests.js index 0c3b664afc5..2e74e36e5e1 100644 --- a/addons/web/static/tests/views/graph_tests.js +++ b/addons/web/static/tests/views/graph_tests.js @@ -152,6 +152,29 @@ QUnit.module('Views', { }); }); + QUnit.test('displaying line chart data with multiple groupbys', function (assert) { + // this test makes sure the line chart shows all data labels (X axis) when + // it is grouped by several fields + assert.expect(3); + + var graph = createView({ + View: GraphView, + model: 'foo', + data: this.data, + arch: '<graph type="line"><field name="foo" /></graph>', + groupBy: ['product_id', 'bar'], + }); + + assert.strictEqual(graph.$('.nv-x text:contains(xphone)').length, 1, + "should contain a text element with product xphone on X axis"); + assert.strictEqual(graph.$('.nv-x text:contains(xpad)').length, 1, + "should contain a text element with product xpad on X axis"); + assert.strictEqual(graph.$('text:contains(true)').length, 1, + "should have an entry for each value of field 'bar' in the legend"); + + graph.destroy(); + }); + QUnit.test('switching measures', function (assert) { var done = assert.async(); assert.expect(4); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index d389e4ef34c..3fb1ca99ded 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -3156,20 +3156,118 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('result of consecutive resequences is correctly sorted', function (assert) { + assert.expect(9); + this.data = { // we want the data to be minimal to have a minimal test + foo: { + fields: {int_field: {string: "int_field", type: "integer", sortable: true}}, + records: [ + {id: 1, int_field: 0}, + {id: 2, int_field: 1}, + {id: 3, int_field: 2}, + {id: 4, int_field: 3}, + ] + } + }; + var moves = 0; + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '<tree>' + + '<field name="int_field" widget="handle"/>' + + '<field name="id"/>' + + '</tree>', + mockRPC: function (route, args) { + if (route === '/web/dataset/resequence') { + if (moves === 0) { + assert.deepEqual(args, { + model: "foo", + ids: [4, 3], + offset: 2, + field: "int_field", + }); + } + if (moves === 1) { + assert.deepEqual(args, { + model: "foo", + ids: [1, 4, 2, 3], + field: "int_field", + }); + } + if (moves === 2) { + assert.deepEqual(args, { + model: "foo", + ids: [2, 4], + offset: 1, + field: "int_field", + }); + } + if (moves === 3) { + assert.deepEqual(args, { + model: "foo", + ids: [1, 4, 2, 3], + field: "int_field", + }); + } + moves += 1; + } + return this._super.apply(this, arguments); + }, + }); + assert.strictEqual(list.$('tbody tr td').text(), '1234', + "default should be sorted by id"); + testUtils.dragAndDrop( + list.$('.ui-sortable-handle').eq(3), + list.$('tbody tr').eq(2), + {position: 'top'} + ); + assert.strictEqual(list.$('tbody tr td').text(), '1243', + "the int_field (sequence) should have been correctly updated"); + testUtils.dragAndDrop( + list.$('.ui-sortable-handle').eq(2), + list.$('tbody tr').eq(1), + {position: 'top'} + ); + assert.deepEqual(list.$('tbody tr td').text(), '1423', + "the int_field (sequence) should have been correctly updated"); + testUtils.dragAndDrop( + list.$('.ui-sortable-handle').eq(1), + list.$('tbody tr').eq(3), + {position: 'top'} + ); + assert.deepEqual(list.$('tbody tr td').text(), '1243', + "the int_field (sequence) should have been correctly updated"); + testUtils.dragAndDrop( + list.$('.ui-sortable-handle').eq(2), + list.$('tbody tr').eq(1), + {position: 'top'} + ); + assert.deepEqual(list.$('tbody tr td').text(), '1423', + "the int_field (sequence) should have been correctly updated"); + list.destroy(); + }); + QUnit.test('editable list with handle widget', function (assert) { assert.expect(12); + // resequence makes sense on a sequence field, not on arbitrary fields + this.data.foo.records[0].int_field = 0; + this.data.foo.records[1].int_field = 1; + this.data.foo.records[2].int_field = 2; + this.data.foo.records[3].int_field = 3; + var list = createView({ View: ListView, model: 'foo', data: this.data, - arch: '<tree editable="top">' + + arch: '<tree editable="top" default_order="int_field">' + '<field name="int_field" widget="handle"/>' + '<field name="amount" widget="float" digits="[5,0]"/>' + '</tree>', mockRPC: function (route, args) { if (route === '/web/dataset/resequence') { - assert.strictEqual(args.offset, -4, + assert.strictEqual(args.offset, 1, "should write the sequence starting from the lowest current one"); assert.strictEqual(args.field, 'int_field', "should write the right field as sequence"); @@ -3217,6 +3315,12 @@ QUnit.module('Views', { QUnit.test('editable list with handle widget with slow network', function (assert) { assert.expect(15); + // resequence makes sense on a sequence field, not on arbitrary fields + this.data.foo.records[0].int_field = 0; + this.data.foo.records[1].int_field = 1; + this.data.foo.records[2].int_field = 2; + this.data.foo.records[3].int_field = 3; + var def = $.Deferred(); var list = createView({ @@ -3229,7 +3333,7 @@ QUnit.module('Views', { '</tree>', mockRPC: function (route, args) { if (route === '/web/dataset/resequence') { - assert.strictEqual(args.offset, -4, + assert.strictEqual(args.offset, 1, "should write the sequence starting from the lowest current one"); assert.strictEqual(args.field, 'int_field', "should write the right field as sequence"); diff --git a/addons/website_crm_partner_assign/controllers/main.py b/addons/website_crm_partner_assign/controllers/main.py index 52f48d58b2a..67272576845 100644 --- a/addons/website_crm_partner_assign/controllers/main.py +++ b/addons/website_crm_partner_assign/controllers/main.py @@ -235,7 +235,7 @@ class WebsiteCrmPartnerAssign(WebsitePartnerPage): grade_domain += [('country_id', '=', country.id)] grades = partner_obj.sudo().read_group( grade_domain, ["id", "grade_id"], - groupby="grade_id", orderby="grade_id DESC") + groupby="grade_id") grades_partners = partner_obj.sudo().search_count(grade_domain) # flag active grade for grade_dict in grades: |
