diff --git a/addons/account/models/account.py b/addons/account/models/account.py index 16d1bf22015..b003051e499 100644 --- a/addons/account/models/account.py +++ b/addons/account/models/account.py @@ -266,7 +266,7 @@ class AccountAccount(models.Model): elif self.internal_type == 'receivable': action_context = {'show_mode_selector': False, 'mode': 'customers'} else: - action_context = {'show_mode_selector': False, 'account_ids': [self.id,]} + action_context = {'show_mode_selector': False, 'mode': 'accounts', 'account_ids': [self.id,]} return { 'type': 'ir.actions.client', 'tag': 'manual_reconciliation_view', diff --git a/addons/account/static/src/js/reconciliation/reconciliation_model.js b/addons/account/static/src/js/reconciliation/reconciliation_model.js index 864037a65ef..b136af9a4ed 100644 --- a/addons/account/static/src/js/reconciliation/reconciliation_model.js +++ b/addons/account/static/src/js/reconciliation/reconciliation_model.js @@ -1073,7 +1073,7 @@ var ManualModel = StatementModel.extend({ return self._rpc({ model: 'account.move.line', method: 'get_data_for_manual_reconciliation', - args: ['account', self.account_ids], + args: ['account', model.account_ids || self.account_ids], context: context, }) .then(function (result) { diff --git a/addons/base_import/models/base_import.py b/addons/base_import/models/base_import.py index c86db44e26c..40324f194dc 100644 --- a/addons/base_import/models/base_import.py +++ b/addons/base_import/models/base_import.py @@ -445,7 +445,7 @@ class Import(models.TransientModel): :rtype: (None, None) | (list(str), dict(int: list(str))) """ if not options.get('headers'): - return None, None + return [], {} headers = next(rows) return headers, { diff --git a/addons/base_import/tests/test_base_import.py b/addons/base_import/tests/test_base_import.py index cf324b44abd..a59cfa9205c 100644 --- a/addons/base_import/tests/test_base_import.py +++ b/addons/base_import/tests/test_base_import.py @@ -150,7 +150,7 @@ class TestMatchHeadersMultiple(TransactionCase): def test_noheaders(self): self.assertEqual( - self.env['base_import.import']._match_headers([], [], {}), (None, None) + self.env['base_import.import']._match_headers([], [], {}), ([], {}) ) def test_nomatch(self): diff --git a/addons/crm/models/crm_lead.py b/addons/crm/models/crm_lead.py index f06cea3f434..90d24275051 100644 --- a/addons/crm/models/crm_lead.py +++ b/addons/crm/models/crm_lead.py @@ -978,7 +978,7 @@ class Lead(models.Model): result['activity']['today'] += 1 if date.today() <= date_deadline <= date.today() + timedelta(days=7): result['activity']['next_7_days'] += 1 - if date_deadline < date.today(): + if date_deadline < date.today() and not opp.date_closed: result['activity']['overdue'] += 1 # Won in Opportunities if opp.date_closed and opp.stage_id.probability == 100: diff --git a/addons/hr_attendance/models/hr_attendance.py b/addons/hr_attendance/models/hr_attendance.py index c373bbe23f4..76d442a4b67 100644 --- a/addons/hr_attendance/models/hr_attendance.py +++ b/addons/hr_attendance/models/hr_attendance.py @@ -68,7 +68,7 @@ class HrAttendance(models.Model): ('check_in', '<=', attendance.check_in), ('id', '!=', attendance.id), ], order='check_in desc', limit=1) - if last_attendance_before_check_in and last_attendance_before_check_in.check_out and last_attendance_before_check_in.check_out >= attendance.check_in: + if last_attendance_before_check_in and last_attendance_before_check_in.check_out and last_attendance_before_check_in.check_out > attendance.check_in: raise exceptions.ValidationError(_("Cannot create new attendance record for %(empl_name)s, the employee was already checked in on %(datetime)s") % { 'empl_name': attendance.employee_id.name, 'datetime': fields.Datetime.to_string(fields.Datetime.context_timestamp(self, fields.Datetime.from_string(attendance.check_in))), diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py index d30810eeb1e..c7c07bcee62 100644 --- a/addons/hr_expense/models/hr_expense.py +++ b/addons/hr_expense/models/hr_expense.py @@ -300,6 +300,13 @@ class HrExpense(models.Model): }) return account_move + @api.multi + def unlink(self): + for expense in self: + if expense.state in ['done']: + raise UserError(_('You cannot delete a posted expense.')) + super(HrExpense, self).unlink() + @api.multi def action_get_attachment_view(self): self.ensure_one() @@ -450,8 +457,8 @@ class HrExpenseSheet(models.Model): @api.multi def unlink(self): for expense in self: - if expense.state == "post": - raise UserError(_("You cannot delete a posted expense.")) + if expense.state in ['post', 'done']: + raise UserError(_('You cannot delete a posted or paid expense.')) super(HrExpenseSheet, self).unlink() @api.multi diff --git a/addons/hr_expense/security/ir_rule.xml b/addons/hr_expense/security/ir_rule.xml index 7148e828231..eba7fe52e1a 100644 --- a/addons/hr_expense/security/ir_rule.xml +++ b/addons/hr_expense/security/ir_rule.xml @@ -29,26 +29,6 @@ ['|',('company_id', '=', False), ('company_id', 'child_of', [user.company_id.id])] - - Expense Report Prevent Delete Employee - - [('state', '=', 'submit')] - - - - - - - - Expense Report Authorize Delete Officer - - [(1, '=', 1)] - - - - - - diff --git a/addons/hr_timesheet/security/hr_timesheet_security.xml b/addons/hr_timesheet/security/hr_timesheet_security.xml index 2a8b9743d2f..9a75f999347 100644 --- a/addons/hr_timesheet/security/hr_timesheet_security.xml +++ b/addons/hr_timesheet/security/hr_timesheet_security.xml @@ -6,14 +6,14 @@ User - + Manager - + diff --git a/addons/hr_timesheet/views/hr_views.xml b/addons/hr_timesheet/views/hr_views.xml index d87abf0fb60..21c645d8159 100644 --- a/addons/hr_timesheet/views/hr_views.xml +++ b/addons/hr_timesheet/views/hr_views.xml @@ -37,10 +37,9 @@ hr.department.kanban.inherit hr.department - + -
diff --git a/addons/l10n_fr/migrations/9.0.1.1/post-migrate_tags_on_taxes.py b/addons/l10n_fr/migrations/9.0.1.1/post-migrate_tags_on_taxes.py new file mode 100644 index 00000000000..6f933404f1d --- /dev/null +++ b/addons/l10n_fr/migrations/9.0.1.1/post-migrate_tags_on_taxes.py @@ -0,0 +1,7 @@ +from openerp.modules.registry import RegistryManager + +def migrate(cr, version): + registry = RegistryManager.get(cr.dbname) + from openerp.addons.account.models.chart_template import migrate_tags_on_taxes + migrate_tags_on_taxes(cr, registry) + diff --git a/addons/l10n_fr/migrations/9.0.1.1/pre-set_tags_and_taxes_updatable.py b/addons/l10n_fr/migrations/9.0.1.1/pre-set_tags_and_taxes_updatable.py new file mode 100644 index 00000000000..586ae984b8c --- /dev/null +++ b/addons/l10n_fr/migrations/9.0.1.1/pre-set_tags_and_taxes_updatable.py @@ -0,0 +1,7 @@ +from openerp.modules.registry import RegistryManager + +def migrate(cr, version): + registry = RegistryManager.get(cr.dbname) + from openerp.addons.account.models.chart_template import migrate_set_tags_and_taxes_updatable + migrate_set_tags_and_taxes_updatable(cr, registry, 'l10n_fr') + diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py index 6146b5b37e5..ef1f09c8edc 100644 --- a/addons/mail/controllers/main.py +++ b/addons/mail/controllers/main.py @@ -76,7 +76,10 @@ class MailController(http.Controller): record_action = record_sudo.get_access_action(access_uid=uid) else: record_action = record_sudo.get_access_action() + if record_action['type'] == 'ir.actions.act_url' and record_action.get('target_type') != 'public': + return cls._redirect_to_messaging() + record_action.pop('target_type', None) # the record has an URL redirection: use it directly if record_action['type'] == 'ir.actions.act_url': return werkzeug.utils.redirect(record_action['url']) diff --git a/addons/mail/static/src/js/many2many_tags_email.js b/addons/mail/static/src/js/many2many_tags_email.js index 382efabbd35..c566d2a4706 100644 --- a/addons/mail/static/src/js/many2many_tags_email.js +++ b/addons/mail/static/src/js/many2many_tags_email.js @@ -28,7 +28,7 @@ BasicModel.include({ var localID = (record._changes && fieldName in record._changes) ? record._changes[fieldName] : record.data[fieldName]; - var list = this.localData[localID]; + var list = this._applyX2ManyOperations(this.localData[localID]); var invalidPartnerIds = []; _.each(list.data, function (id) { var record = self.localData[id]; diff --git a/addons/mail/static/tests/chatter_tests.js b/addons/mail/static/tests/chatter_tests.js index 337f3e9f9d1..77d1cca435a 100644 --- a/addons/mail/static/tests/chatter_tests.js +++ b/addons/mail/static/tests/chatter_tests.js @@ -1150,5 +1150,65 @@ QUnit.test('fieldmany2many tags email', function (assert) { $('.modal-footer .btn-primary').click(); }); +QUnit.test('fieldmany2many tags email (edition)', function (assert) { + assert.expect(15); + + this.data.partner.records[0].timmy = [12]; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + res_id: 1, + arch:'
' + + '' + + '' + + '' + + '' + + '
', + viewOptions: { + mode: 'edit', + }, + mockRPC: function (route, args) { + if (args.method ==='read' && args.model === 'partner_type') { + assert.step(args.args[0]); + assert.deepEqual(args.args[1] , ['display_name', 'email'], "should read the email"); + } + return this._super.apply(this, arguments); + }, + archs: { + 'partner_type,false,form': '
', + }, + }); + + assert.verifySteps([[12]]); + assert.strictEqual(form.$('.o_field_many2manytags[name="timmy"] span.o_tag_color_10').length, 1, + "should contain one tag"); + + // add an other existing tag + var $input = form.$('.o_field_many2manytags input'); + $input.click(); // opens the dropdown + $input.autocomplete('widget').find('li:first').click(); // add 'silver' + + assert.strictEqual($('.modal-body.o_act_window').length, 1, + "there should be one modal opened to edit the empty email"); + assert.strictEqual($('.modal-body.o_act_window input[name="display_name"]').val(), "silver", + "the opened modal should be a form view dialog with the partner_type 14"); + assert.strictEqual($('.modal-body.o_act_window input[name="email"]').length, 1, + "there should be an email field in the modal"); + + // set the email and save the modal (will rerender the form view) + $('.modal-body.o_act_window input[name="email"]').val('coucou@petite.perruche').trigger('input'); + $('.modal-footer .btn-primary').click(); + + assert.strictEqual(form.$('.o_field_many2manytags[name="timmy"] span.o_tag_color_10').length, 2, + "should contain the second tag"); + // should have read [14] three times: when opening the dropdown, when opening the modal, and + // after the save + assert.verifySteps([[12], [14], [14], [14]]); + + form.destroy(); +}); + }); }); diff --git a/addons/mrp/models/mrp_production.py b/addons/mrp/models/mrp_production.py index 69ad489da17..1125ed663af 100644 --- a/addons/mrp/models/mrp_production.py +++ b/addons/mrp/models/mrp_production.py @@ -338,6 +338,7 @@ class MrpProduction(models.Model): source_location = self.location_src_id original_quantity = self.product_qty - self.qty_produced data = { + 'sequence': bom_line.sequence, 'name': self.name, 'date': self.date_planned_start, 'date_expected': self.date_planned_start, diff --git a/addons/mrp/report/mrp_production_templates.xml b/addons/mrp/report/mrp_production_templates.xml index 979eff2ccf9..d6869e71b33 100644 --- a/addons/mrp/report/mrp_production_templates.xml +++ b/addons/mrp/report/mrp_production_templates.xml @@ -7,14 +7,9 @@
-
+

Production Order # :

-
- - - -
@@ -76,15 +71,6 @@ Product Quantity - - Source Location - - - Destination Location - - - Barcode - @@ -101,17 +87,6 @@ - - - - - - - - - - - diff --git a/addons/mrp/views/mrp_production_views.xml b/addons/mrp/views/mrp_production_views.xml index cc4774192d4..803340e4696 100644 --- a/addons/mrp/views/mrp_production_views.xml +++ b/addons/mrp/views/mrp_production_views.xml @@ -114,8 +114,8 @@ - + -
-