diff --git a/addons/account/models/account_journal_dashboard.py b/addons/account/models/account_journal_dashboard.py index 82828965d52..6379bd87fbc 100644 --- a/addons/account/models/account_journal_dashboard.py +++ b/addons/account/models/account_journal_dashboard.py @@ -179,8 +179,8 @@ class account_journal(models.Model): account_ids = tuple(ac for ac in [self.default_debit_account_id.id, self.default_credit_account_id.id] if ac) if account_ids: amount_field = 'balance' if not self.currency_id else 'amount_currency' - query = """SELECT sum(%s) FROM account_move_line WHERE account_id in %%s;""" % (amount_field,) - self.env.cr.execute(query, (account_ids,)) + query = """SELECT sum(%s) FROM account_move_line WHERE account_id in %%s AND date <= %%s;""" % (amount_field,) + self.env.cr.execute(query, (account_ids, fields.Date.today(),)) query_results = self.env.cr.dictfetchall() if query_results and query_results[0].get('sum') != None: account_sum = query_results[0].get('sum') diff --git a/addons/account/models/account_move.py b/addons/account/models/account_move.py index d0dccab7844..40503c62388 100644 --- a/addons/account/models/account_move.py +++ b/addons/account/models/account_move.py @@ -1399,7 +1399,9 @@ class AccountMoveLine(models.Model): @api.model def _query_get(self, domain=None): context = dict(self._context or {}) - domain = domain and safe_eval(str(domain)) or [] + domain = domain or [] + if not isinstance(domain, (list, tuple)): + domain = safe_eval(domain) date_field = 'date' if context.get('aged_balance'): diff --git a/addons/account/models/partner.py b/addons/account/models/partner.py index de32aaa5f3d..25e6449c7af 100644 --- a/addons/account/models/partner.py +++ b/addons/account/models/partner.py @@ -356,7 +356,7 @@ class ResPartner(models.Model): @api.multi def mark_as_reconciled(self): self.env['account.partial.reconcile'].check_access_rights('write') - return self.sudo().write({'last_time_entries_checked': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) + return self.sudo().with_context(company_id=self.env.user.company_id.id).write({'last_time_entries_checked': time.strftime(DEFAULT_SERVER_DATETIME_FORMAT)}) @api.one def _get_company_currency(self): diff --git a/addons/account/static/tests/reconciliation_tests.js b/addons/account/static/tests/reconciliation_tests.js index cdf32c4010e..27f327febb1 100644 --- a/addons/account/static/tests/reconciliation_tests.js +++ b/addons/account/static/tests/reconciliation_tests.js @@ -22,6 +22,9 @@ var db = { {id: 4, display_name: "partner 4", image: 'DDD', customer: true}, {id: 8, display_name: "Agrolait", image: 'EEE', customer: true}, {id: 12, display_name: "Camptocamp", image: 'FFF', supplier: true, property_account_receivable_id: 287, property_account_payable_id: 287}, + // add more to have 'Search More' option + {id: 98, display_name: "partner 98", image: 'YYY', customer: true}, + {id: 99, display_name: "partner 99", image: 'ZZZ', customer: true}, ], mark_as_reconciled: function () { return $.when(); @@ -738,12 +741,18 @@ QUnit.module('account', { }); QUnit.test('Reconciliation change partner', function (assert) { - assert.expect(10); + assert.expect(13); var clientAction = new ReconciliationClientAction.StatementAction(null, this.params.options); testUtils.addMockEnvironment(clientAction, { data: this.params.data, + archs: { + 'res.partner,false,list': '', + 'res.partner,false,search': '' + + '' + + '', + }, }); clientAction.appendTo($('#qunit-fixture')); @@ -758,6 +767,15 @@ QUnit.module('account', { assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Camptocamp", "the partner many2one should display Camptocamp"); assert.strictEqual(widget.$('.match table tr.mv_line').length, 3, "camptocamp should have 3 propositions for reconciliation"); + // Simulate changing partner with SelectCreateDialog + widget = clientAction.widgets[1]; + assert.strictEqual($('.modal').length, 0, "shouldn't have any opened modal"); + widget.$('.o_input_dropdown input').trigger('click'); + $('.ui-autocomplete .ui-menu-item a:contains(Search More):eq(1)').trigger('mouseenter').trigger('click'); + assert.strictEqual($('.modal').length, 1, "should open a SelectCreateDialog"); + $('.modal table.o_list_view td:contains(Camptocamp)').click(); + assert.strictEqual(widget.$('.o_input_dropdown input').val(), "Camptocamp", "the partner many2one should display Camptocamp"); + widget = clientAction.widgets[2]; widget.$('.accounting_view thead td:first').trigger('click'); widget.$('.accounting_view .mv_line .cell_label').trigger('click'); diff --git a/addons/account_asset/models/account_invoice.py b/addons/account_asset/models/account_invoice.py index d4b65d6b020..192c984b265 100644 --- a/addons/account_asset/models/account_invoice.py +++ b/addons/account_asset/models/account_invoice.py @@ -107,6 +107,7 @@ class AccountInvoiceLine(models.Model): self.asset_category_id = self.product_id.product_tmpl_id.deferred_revenue_category_id.id elif invoice.type == 'in_invoice': self.asset_category_id = self.product_id.product_tmpl_id.asset_category_id.id + self.onchange_asset_category_id() super(AccountInvoiceLine, self)._set_additional_fields(invoice) def get_invoice_line_account(self, type, product, fpos, company): diff --git a/addons/account_tax_python/models/account_tax.py b/addons/account_tax_python/models/account_tax.py index 7ab0c2bbe1f..8b438b82859 100644 --- a/addons/account_tax_python/models/account_tax.py +++ b/addons/account_tax_python/models/account_tax.py @@ -37,9 +37,9 @@ class AccountTaxPython(models.Model): @api.multi def compute_all(self, price_unit, currency=None, quantity=1.0, product=None, partner=None): - taxes = self.env['account.tax'] + taxes = self.filtered(lambda r: r.amount_type != 'code') company = self.env.user.company_id - for tax in self: + for tax in self.filtered(lambda r: r.amount_type == 'code'): localdict = {'price_unit': price_unit, 'quantity': quantity, 'product': product, 'partner': partner, 'company': company} safe_eval(tax.python_applicable, localdict, mode="exec", nocopy=True) if localdict.get('result', False): diff --git a/addons/account_voucher/models/account_voucher.py b/addons/account_voucher/models/account_voucher.py index 02256abaa22..a0f7d79288c 100644 --- a/addons/account_voucher/models/account_voucher.py +++ b/addons/account_voucher/models/account_voucher.py @@ -82,7 +82,7 @@ class AccountVoucher(models.Model): @api.model def _get_currency(self): - journal = self.env['account.journal'].browse(self._context.get('journal_id', False)) + journal = self.env['account.journal'].browse(self.env.context.get('default_journal_id', False)) if journal.currency_id: return journal.currency_id.id return self.env.user.company_id.currency_id.id diff --git a/addons/base_setup/views/base_config_settings_views.xml b/addons/base_setup/views/base_config_settings_views.xml index a0dab58c570..d97c0d421e5 100644 --- a/addons/base_setup/views/base_config_settings_views.xml +++ b/addons/base_setup/views/base_config_settings_views.xml @@ -23,7 +23,7 @@
diff --git a/addons/base_vat/models/res_partner.py b/addons/base_vat/models/res_partner.py index 3b6b648a9bd..b2525706da5 100644 --- a/addons/base_vat/models/res_partner.py +++ b/addons/base_vat/models/res_partner.py @@ -98,7 +98,11 @@ class ResPartner(models.Model): @api.constrains('vat', 'commercial_partner_country_id') def check_vat(self): - if self.env.user.company_id.vat_check_vies: + if self.env.context.get('company_id'): + company = self.env['res.company'].browse(self.env.context['company_id']) + else: + company = self.env.user.company_id + if company.vat_check_vies: # force full VIES online check check_func = self.vies_vat_check else: @@ -121,7 +125,11 @@ class ResPartner(models.Model): self.ensure_one() vat_no = "'CC##' (CC=Country Code, ##=VAT Number)" vat_no = _ref_vat.get(country_code) or vat_no - if self.env.user.company_id.vat_check_vies: + if self.env.context.get('company_id'): + company = self.env['res.company'].browse(self.env.context['company_id']) + else: + company = self.env.user.company_id + if company.vat_check_vies: return '\n' + _('The VAT number [%s] for partner [%s] either failed the VIES VAT validation check or did not respect the expected format %s.') % (self.vat, self.name, vat_no) return '\n' + _('The VAT number [%s] for partner [%s] does not seem to be valid. \nNote: the expected format is %s') % (self.vat, self.name, vat_no) diff --git a/addons/calendar/models/calendar.py b/addons/calendar/models/calendar.py index e80a2727282..7b7275f9a1d 100644 --- a/addons/calendar/models/calendar.py +++ b/addons/calendar/models/calendar.py @@ -1409,7 +1409,8 @@ class Meeting(models.Model): partners_to_notify.append(event_attendees_changes['removed_partners'].ids) self.env['calendar.alarm_manager'].notify_next_alarm(partners_to_notify) - if (values.get('start_date') or values.get('start_datetime')) and values.get('active', True): + if (values.get('start_date') or values.get('start_datetime') or + (values.get('start') and self.env.context.get('from_ui'))) and values.get('active', True): for current_meeting in all_meetings: if attendees_create: attendees_create = attendees_create[current_meeting.id] diff --git a/addons/crm/wizard/base_partner_merge.py b/addons/crm/wizard/base_partner_merge.py index a91b9ec3dc1..44c6b611d74 100644 --- a/addons/crm/wizard/base_partner_merge.py +++ b/addons/crm/wizard/base_partner_merge.py @@ -163,7 +163,7 @@ class MergePartnerAutomatic(models.TransientModel): except psycopg2.Error: # updating fails, most likely due to a violated unique constraint # keeping record with nonexistent partner_id is useless, better delete it - query = 'DELETE FROM %(table)s WHERE %(column)s IN %%s' % query_dic + query = 'DELETE FROM "%(table)s" WHERE "%(column)s" IN %%s' % query_dic self._cr.execute(query, (tuple(src_partners.ids),)) @api.model diff --git a/addons/google_calendar/models/google_calendar.py b/addons/google_calendar/models/google_calendar.py index ac91835f530..590619ae936 100644 --- a/addons/google_calendar/models/google_calendar.py +++ b/addons/google_calendar/models/google_calendar.py @@ -247,7 +247,8 @@ class GoogleCalendar(models.AbstractModel): if not self.get_need_synchro_attendee(): data.pop("attendees") if isCreating: - other_google_ids = [other_att.google_internal_event_id for other_att in event.attendee_ids if other_att.google_internal_event_id] + other_google_ids = [other_att.google_internal_event_id for other_att in event.attendee_ids + if other_att.google_internal_event_id and not other_att.google_internal_event_id.startswith('_')] if other_google_ids: data["id"] = other_google_ids[0] return data @@ -608,7 +609,8 @@ class GoogleCalendar(models.AbstractModel): ('event_id.final_date', '>', fields.Datetime.to_string(self.get_minTime())), ]) for att in my_attendees: - other_google_ids = [other_att.google_internal_event_id for other_att in att.event_id.attendee_ids if other_att.google_internal_event_id and other_att.id != att.id] + other_google_ids = [other_att.google_internal_event_id for other_att in att.event_id.attendee_ids if + other_att.google_internal_event_id and other_att.id != att.id and not other_att.google_internal_event_id.startswith('_')] for other_google_id in other_google_ids: if self.get_one_event_synchro(other_google_id): att.write({'google_internal_event_id': other_google_id}) diff --git a/addons/purchase/models/purchase.py b/addons/purchase/models/purchase.py index e5b95282faf..1d0aec5901b 100644 --- a/addons/purchase/models/purchase.py +++ b/addons/purchase/models/purchase.py @@ -52,7 +52,7 @@ class PurchaseOrder(models.Model): if any(float_compare(line.qty_invoiced, line.product_qty if line.product_id.purchase_method == 'purchase' else line.qty_received, precision_digits=precision) == -1 for line in order.order_line): order.invoice_status = 'to invoice' - elif all(float_compare(line.qty_invoiced, line.product_qty if line.product_id.purchase_method == 'purchase' else line.qty_received, precision_digits=precision) >= 0 for line in order.order_line): + elif all(float_compare(line.qty_invoiced, line.product_qty if line.product_id.purchase_method == 'purchase' else line.qty_received, precision_digits=precision) >= 0 for line in order.order_line) and order.invoice_ids: order.invoice_status = 'invoiced' else: order.invoice_status = 'no' @@ -677,7 +677,9 @@ class PurchaseOrderLine(models.Model): order = line.order_id price_unit = line.price_unit if line.taxes_id: - price_unit = line.taxes_id.with_context(round=False).compute_all(price_unit, currency=line.order_id.currency_id, quantity=1.0)['total_excluded'] + price_unit = line.taxes_id.with_context(round=False).compute_all( + price_unit, currency=line.order_id.currency_id, quantity=1.0, product=line.product_id, partner=line.order_id.partner_id + )['total_excluded'] if line.product_uom.id != line.product_id.uom_id.id: price_unit *= line.product_uom.factor / line.product_id.uom_id.factor if order.currency_id != order.company_id.currency_id: diff --git a/addons/stock/models/procurement.py b/addons/stock/models/procurement.py index ad5b81d9bdf..91a9061fe58 100644 --- a/addons/stock/models/procurement.py +++ b/addons/stock/models/procurement.py @@ -190,17 +190,21 @@ class ProcurementOrder(models.Model): def run(self, autocommit=False): # TDE CLEANME: unused context key procurement_auto_defer remove new_self = self.filtered(lambda order: order.state not in ['running', 'done', 'cancel']) - res = super(ProcurementOrder, new_self).run(autocommit=autocommit) + res = True + if new_self: + res = super(ProcurementOrder, new_self).run(autocommit=autocommit) - # after all the procurements are run, check if some created a draft stock move that needs to be confirmed - # (we do that in batch because it fasts the picking assignation and the picking state computation) - new_self.filtered(lambda order: order.state == 'running' and order.rule_id.action == 'move').mapped('move_ids').filtered(lambda move: move.state == 'draft').action_confirm() + # after all the procurements are run, check if some created a draft stock move that needs to be confirmed + # (we do that in batch because it fasts the picking assignation and the picking state computation) + move_ids = new_self.filtered(lambda order: order.state == 'running' and order.rule_id.action == 'move').mapped('move_ids').filtered(lambda move: move.state == 'draft') + if move_ids: + move_ids.action_confirm() - # TDE FIXME: action_confirm in stock_move already call run() ... necessary ?? - # If procurements created other procurements, run the created in batch - new_procurements = self.search([('move_dest_id.procurement_id', 'in', new_self.ids)], order='id') - if new_procurements: - res = new_procurements.run(autocommit=autocommit) + # TDE FIXME: action_confirm in stock_move already call run() ... necessary ?? + # If procurements created other procurements, run the created in batch + new_procurements = self.search([('move_dest_id.procurement_id', 'in', new_self.ids)], order='id') + if new_procurements: + res = new_procurements.run(autocommit=autocommit) return res @api.multi diff --git a/addons/stock_account/models/stock.py b/addons/stock_account/models/stock.py index cadeaea8bb8..f8a1f8b9dd0 100644 --- a/addons/stock_account/models/stock.py +++ b/addons/stock_account/models/stock.py @@ -82,7 +82,7 @@ class StockMoveLine(models.Model): class StockMove(models.Model): _inherit = "stock.move" - to_refund = fields.Boolean(string="To Refund (update SO/PO)", + to_refund = fields.Boolean(string="To Refund (update SO/PO)", copy=False, help='Trigger a decrease of the delivered/received quantity in the associated Sale Order/Purchase Order') value = fields.Float() remaining_qty = fields.Float() diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js index 9b1e930cef3..c2bd4f5d5b8 100644 --- a/addons/web/static/src/js/fields/relational_fields.js +++ b/addons/web/static/src/js/fields/relational_fields.js @@ -401,8 +401,8 @@ var FieldMany2One = AbstractField.extend({ initial_ids: ids ? _.map(ids, function (x) { return x[0]; }) : undefined, initial_view: view, disable_multiple_selection: true, - on_selected: function (element_ids) { - self.reinitialize({id: element_ids[0]}); + on_selected: function (records) { + self.reinitialize(records[0]); self.activate(); } })).open(); @@ -1150,15 +1150,16 @@ var FieldMany2Many = FieldX2Many.extend({ title: _t("Add: ") + this.string, no_create: this.nodeOptions.no_create || !this.activeActions.create, fields_view: this.attrs.views.form, - on_selected: function (res_ids) { - var new_ids = _.difference(res_ids, self.value.res_ids); - if (new_ids.length) { - var values = _.map(new_ids, function (id) { + on_selected: function (records) { + var resIDs = _.pluck(records, 'id'); + var newIDs = _.difference(resIDs, self.value.res_ids); + if (newIDs.length) { + var values = _.map(newIDs, function (id) { return {id: id}; }); self._setValue({ operation: 'ADD_M2M', - ids: values + ids: values, }); } } diff --git a/addons/web/static/src/js/views/calendar/calendar_model.js b/addons/web/static/src/js/views/calendar/calendar_model.js index eb6d7176884..4374421d7b3 100644 --- a/addons/web/static/src/js/views/calendar/calendar_model.js +++ b/addons/web/static/src/js/views/calendar/calendar_model.js @@ -2,6 +2,7 @@ odoo.define('web.CalendarModel', function (require) { "use strict"; var AbstractModel = require('web.AbstractModel'); +var Context = require('web.Context'); var core = require('web.core'); var fieldUtils = require('web.field_utils'); var session = require('web.session'); @@ -298,11 +299,12 @@ return AbstractModel.extend({ data[k] = dateToServer(data[k]); } } + var context = new Context(this.data.context, {from_ui: true}); return this._rpc({ model: this.modelName, method: 'write', args: [[record.id], data], - context: this.data.context + context: context }); }, diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js index 2465b1ee3c5..7222d792c69 100644 --- a/addons/web/static/src/js/views/list/list_controller.js +++ b/addons/web/static/src/js/views/list/list_controller.js @@ -87,9 +87,20 @@ var ListController = BasicController.extend({ * @returns {number[]} list of res_ids */ getSelectedIds: function () { + return _.map(this.getSelectedRecords(), function (record) { + return record.res_id; + }); + }, + /** + * Returns the list of currently selected records (with the check boxes on + * the left) + * + * @returns {Object[]} list of records + */ + getSelectedRecords: function () { var self = this; return _.map(this.selectedRecords, function (db_id) { - return self.model.get(db_id, {raw: true}).res_id; + return self.model.get(db_id, {raw: true}); }); }, /** diff --git a/addons/web/static/src/js/views/view_dialogs.js b/addons/web/static/src/js/views/view_dialogs.js index 876c37fbd59..645540b7a57 100644 --- a/addons/web/static/src/js/views/view_dialogs.js +++ b/addons/web/static/src/js/views/view_dialogs.js @@ -238,8 +238,11 @@ var SelectCreateListController = ListController.extend({ // row of the list) such that it triggers up 'select_record' with its res_id. custom_events: _.extend({}, ListController.prototype.custom_events, { open_record: function (event) { - var selected_record = this.model.get(event.data.id); - this.trigger_up('select_record', {id: selected_record.res_id}); + var selectedRecord = this.model.get(event.data.id); + this.trigger_up('select_record', { + id: selectedRecord.res_id, + display_name: selectedRecord.data.display_name, + }); }, }), }); @@ -251,7 +254,7 @@ var SelectCreateDialog = ViewDialog.extend({ custom_events: _.extend({}, ViewDialog.prototype.custom_events, { select_record: function (event) { if (!this.options.readonly) { - this.on_selected([event.data.id]); + this.on_selected([event.data]); this.close(); } }, @@ -369,7 +372,14 @@ var SelectCreateDialog = ViewDialog.extend({ disabled: true, close: true, click: function () { - self.on_selected(self.list_controller.getSelectedIds()); + var records = self.list_controller.getSelectedRecords(); + var values = _.map(records, function (record) { + return { + id: record.res_id, + display_name: record.data.display_name, + }; + }); + self.on_selected(values); }, }); } @@ -399,7 +409,11 @@ var SelectCreateDialog = ViewDialog.extend({ var self = this; var dialog = new FormViewDialog(this, _.extend({}, this.options, { on_saved: function (record) { - self.on_selected([record.res_id]); + var values = [{ + id: record.res_id, + display_name: record.data.display_name, + }]; + self.on_selected(values); }, })).open(); dialog.on('closed', this, this.close.bind(this)); diff --git a/addons/web/static/src/js/widgets/debug_manager.js b/addons/web/static/src/js/widgets/debug_manager.js index c74ce95ac1b..a57e452de4b 100644 --- a/addons/web/static/src/js/widgets/debug_manager.js +++ b/addons/web/static/src/js/widgets/debug_manager.js @@ -169,11 +169,11 @@ var DebugManager = Widget.extend({ title: _t('Select a view'), disable_multiple_selection: true, domain: [['type', '!=', 'qweb'], ['type', '!=', 'search']], - on_selected: function (element_ids) { + on_selected: function (records) { self._rpc({ model: 'ir.ui.view', method: 'search_read', - domain: [['id', '=', element_ids[0]]], + domain: [['id', '=', records[0].id]], fields: ['name', 'model', 'type'], limit: 1, }) diff --git a/addons/web/static/tests/fields/relational_fields_tests.js b/addons/web/static/tests/fields/relational_fields_tests.js index 45110c7cf9a..2020e0124f3 100644 --- a/addons/web/static/tests/fields/relational_fields_tests.js +++ b/addons/web/static/tests/fields/relational_fields_tests.js @@ -3463,7 +3463,6 @@ QUnit.module('relational_fields', { '', mockRPC: function (method, args) { if (args.method === 'write') { - // debugger; assert.deepEqual(args.args[1].turtles, [ [1, 3, {display_name: 'coucou', turtle_foo: 'has changed', turtle_int: 42}], ], "correct commands should be sent (only send changed values)"); diff --git a/doc/cla/individual/d4n.md b/doc/cla/individual/d4n.md new file mode 100644 index 00000000000..080d8ba9789 --- /dev/null +++ b/doc/cla/individual/d4n.md @@ -0,0 +1,12 @@ +Germany, 2017-09-12 + +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, + +Dan Čermák dan.cermak@cgc-instruments.com https://github.com/D4N +Dan Čermák dan.cermak@cgc-instruments.de https://github.com/D4N