@@ -250,6 +251,7 @@
+
@@ -336,6 +338,15 @@
+
+
+
+
diff --git a/addons/fleet/views/fleet_vehicle_views.xml b/addons/fleet/views/fleet_vehicle_views.xml
index 6ae5fc92316..0dc0463c778 100644
--- a/addons/fleet/views/fleet_vehicle_views.xml
+++ b/addons/fleet/views/fleet_vehicle_views.xml
@@ -434,6 +434,6 @@
-
+
diff --git a/addons/fleet/views/mail_activity_views.xml b/addons/fleet/views/mail_activity_views.xml
new file mode 100644
index 00000000000..98819009f1d
--- /dev/null
+++ b/addons/fleet/views/mail_activity_views.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ Activity Types
+ mail.activity.type
+ form
+ tree,form
+ ['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'fleet.vehicle.log.contract')]
+ {'default_res_model': 'fleet.vehicle.log.contract'}
+
+
+
\ No newline at end of file
diff --git a/addons/hr_expense/__manifest__.py b/addons/hr_expense/__manifest__.py
index 09096c0ec21..11ae6223152 100644
--- a/addons/hr_expense/__manifest__.py
+++ b/addons/hr_expense/__manifest__.py
@@ -36,6 +36,7 @@ This module also uses analytic accounting and is compatible with the invoice on
'wizard/hr_expense_refuse_reason_views.xml',
'wizard/hr_expense_sheet_register_payment.xml',
'views/hr_expense_views.xml',
+ 'views/mail_activity_views.xml',
'security/ir_rule.xml',
'report/hr_expense_report.xml',
'views/hr_department_views.xml',
diff --git a/addons/hr_expense/data/mail_data.xml b/addons/hr_expense/data/mail_data.xml
index be825bac3db..02d662ecff5 100644
--- a/addons/hr_expense/data/mail_data.xml
+++ b/addons/hr_expense/data/mail_data.xml
@@ -2,7 +2,13 @@
-
+
+ Expense Approval
+ fa-dollar
+
+
+
+
expense
@@ -23,27 +29,12 @@
Expense report refused
-
- Submitted
- hr.expense.sheet
- Expense report submitted, waiting approval
-
-
Paid
hr.expense.sheet
Expense report paid
-
-
- Expenses To Approve
- hr.department
-
-
- department_id
-
-
diff --git a/addons/hr_expense/models/hr_expense.py b/addons/hr_expense/models/hr_expense.py
index cb1d4b6a93f..f021eb4d10f 100644
--- a/addons/hr_expense/models/hr_expense.py
+++ b/addons/hr_expense/models/hr_expense.py
@@ -450,7 +450,7 @@ class HrExpense(models.Model):
class HrExpenseSheet(models.Model):
_name = "hr.expense.sheet"
- _inherit = ['mail.thread']
+ _inherit = ['mail.thread', 'mail.activity.mixin']
_description = "Expense Report"
_order = "accounting_date desc, id desc"
@@ -529,6 +529,7 @@ class HrExpenseSheet(models.Model):
def create(self, vals):
self._create_set_followers(vals)
sheet = super(HrExpenseSheet, self).create(vals)
+ sheet.activity_update()
return sheet
@api.multi
@@ -554,8 +555,6 @@ class HrExpenseSheet(models.Model):
self.ensure_one()
if 'state' in init_values and self.state == 'approve':
return 'hr_expense.mt_expense_approved'
- elif 'state' in init_values and self.state == 'submit':
- return 'hr_expense.mt_expense_confirmed'
elif 'state' in init_values and self.state == 'cancel':
return 'hr_expense.mt_expense_refused'
elif 'state' in init_values and self.state == 'done':
@@ -621,6 +620,7 @@ class HrExpenseSheet(models.Model):
self.write({'state': 'post'})
else:
self.write({'state': 'done'})
+ self.activity_update()
return res
@api.multi
@@ -649,6 +649,7 @@ class HrExpenseSheet(models.Model):
raise UserError(_("Only HR Officers can approve expenses"))
responsible_id = self.user_id.id or self.env.user.id
self.write({'state': 'approve', 'user_id': responsible_id})
+ self.activity_update()
@api.multi
def paid_expense_sheets(self):
@@ -661,8 +662,28 @@ class HrExpenseSheet(models.Model):
self.write({'state': 'cancel'})
for sheet in self:
sheet.message_post_with_view('hr_expense.hr_expense_template_refuse_reason', values={'reason': reason, 'is_sheet': True, 'name': self.name})
+ self.activity_update()
@api.multi
def reset_expense_sheets(self):
self.mapped('expense_line_ids').write({'is_refused': False})
- return self.write({'state': 'submit'})
+ self.write({'state': 'submit'})
+ self.activity_update()
+ return True
+
+ def _get_responsible_for_approval(self):
+ if self.user_id:
+ return self.user_id
+ elif self.employee_id.parent_id.user_id:
+ return self.employee_id.parent_id.user_id
+ elif self.employee_id.department_id.manager_id.user_id:
+ return self.employee_id.department_id.manager_id.user_id
+ return self.env.user
+
+ def activity_update(self):
+ for expense_report in self.filtered(lambda hol: hol.state == 'submit'):
+ self.activity_schedule(
+ 'hr_expense.mail_act_expense_approval', fields.Date.today(),
+ user_id=expense_report._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_expense/security/ir.model.access.csv b/addons/hr_expense/security/ir.model.access.csv
index 10ccbc4d729..c059ed5d393 100644
--- a/addons/hr_expense/security/ir.model.access.csv
+++ b/addons/hr_expense/security/ir.model.access.csv
@@ -1,5 +1,4 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
-
access_hr_expense_user,hr.expense.user,model_hr_expense,hr_expense.group_hr_expense_user,1,1,1,1
access_hr_expense_employee,hr.expense.employee,model_hr_expense,base.group_user,1,1,1,1
access_hr_expense_sheet_user,hr.expense.sheet.user,model_hr_expense_sheet,hr_expense.group_hr_expense_user,1,1,1,1
@@ -15,3 +14,4 @@ access_account_invoice_tax_user,account.invoice.tax.user,account.model_account_i
access_account_move_user,account.move.user,account.model_account_move,hr_expense.group_hr_expense_user,1,0,0,0
access_account_move_line_user,account.move.line.user,account.model_account_move_line,hr_expense.group_hr_expense_user,1,0,0,0
access_account_analytic_line_user,account.analytic.line.user,account.model_account_analytic_line,hr_expense.group_hr_expense_user,1,1,1,1
+access_mail_activity_type_expense_user,mail.activity.type.expense.user,mail.model_mail_activity_type,hr_expense.group_hr_expense_user,1,1,1,1
diff --git a/addons/hr_expense/views/hr_expense_views.xml b/addons/hr_expense/views/hr_expense_views.xml
index 3d5343ca5ec..44458ae1962 100644
--- a/addons/hr_expense/views/hr_expense_views.xml
+++ b/addons/hr_expense/views/hr_expense_views.xml
@@ -203,6 +203,15 @@
+
+
+
+
@@ -487,6 +496,7 @@
+
@@ -576,6 +586,15 @@
+
+
+
+
diff --git a/addons/hr_expense/views/mail_activity_views.xml b/addons/hr_expense/views/mail_activity_views.xml
new file mode 100644
index 00000000000..9c1a04203cd
--- /dev/null
+++ b/addons/hr_expense/views/mail_activity_views.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ Activity Types
+ mail.activity.type
+ form
+ tree,form
+ ['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'hr.expense.sheet')]
+ {'default_res_model': 'hr.expense.sheet'}
+
+
+
\ No newline at end of file
diff --git a/addons/hr_holidays/__manifest__.py b/addons/hr_holidays/__manifest__.py
index a1908e6aaf8..da9238ad82a 100644
--- a/addons/hr_holidays/__manifest__.py
+++ b/addons/hr_holidays/__manifest__.py
@@ -34,10 +34,11 @@ A synchronization with an internal agenda (Meetings of the CRM module) is also p
'security/ir.model.access.csv',
'views/resource_views.xml',
+ 'views/hr_leave_views.xml',
'views/hr_leave_type_views.xml',
'views/hr_leave_allocation_views.xml',
- 'views/hr_leave_views.xml',
'views/hr_views.xml',
+ 'views/mail_activity_views.xml',
'wizard/hr_holidays_summary_department_views.xml',
'wizard/hr_holidays_summary_employees_views.xml',
diff --git a/addons/hr_holidays/data/mail_data.xml b/addons/hr_holidays/data/mail_data.xml
index 57c92f06581..2dc75bcb8c8 100644
--- a/addons/hr_holidays/data/mail_data.xml
+++ b/addons/hr_holidays/data/mail_data.xml
@@ -1,26 +1,36 @@
+
+
+ Leaves Approval
+ fa-sun-o
+
+
+
+ Leaves Second Approve
+ fa-sun-o
+
+
+
+
+
+ Allocation Approval
+ fa-sun-o
+
+
+
+ Allocation Second Approve
+ fa-sun-o
+
+
+
-
- Confirmed
- hr.leave
- Request created and waiting confirmation
-
-
-
- Waiting Second Validation
- hr.leave
-
- Request validated, waiting second validation
-
-
Approved
hr.leave
Request approved
-
Refused
hr.leave
@@ -29,25 +39,11 @@
-
- Confirmed
- hr.leave.allocation
- Request created and waiting confirmation
-
-
-
- Waiting Second Validation
- hr.leave.allocation
-
- Request validated, waiting second validation
-
-
Approved
hr.leave.allocation
Request approved
-
Refused
hr.leave.allocation
@@ -56,24 +52,6 @@
-
- Leaves Confirmed
- hr.department
-
-
- department_id
-
-
-
-
- Leaves First Approval
- hr.department
-
-
- department_id
-
-
-
Leaves Approved
hr.department
@@ -82,7 +60,6 @@
department_id
-
Leaves Refused
hr.department
@@ -91,25 +68,6 @@
department_id
-
-
- Allocations Confirmed
- hr.department
-
-
- department_id
-
-
-
-
- Allocations First Approval
- hr.department
-
-
- department_id
-
-
-
Allocation Approved
hr.department
@@ -118,7 +76,6 @@
department_id
-
Allocation Refused
hr.department
@@ -127,6 +84,5 @@
department_id
-
\ No newline at end of file
diff --git a/addons/hr_holidays/models/hr_leave.py b/addons/hr_holidays/models/hr_leave.py
index 4a58d151f55..03c90176363 100644
--- a/addons/hr_holidays/models/hr_leave.py
+++ b/addons/hr_holidays/models/hr_leave.py
@@ -20,7 +20,7 @@ class HolidaysRequest(models.Model):
_name = "hr.leave"
_description = "Leave"
_order = "date_from desc"
- _inherit = ['mail.thread']
+ _inherit = ['mail.thread', 'mail.activity.mixin']
@api.model
def default_get(self, fields_list):
@@ -249,6 +249,7 @@ class HolidaysRequest(models.Model):
holiday.add_follower(employee_id)
if 'employee_id' in values:
holiday._onchange_employee_id()
+ holiday.activity_update()
return holiday
@api.multi
@@ -357,13 +358,16 @@ class HolidaysRequest(models.Model):
for linked_request in linked_requests:
linked_request.action_draft()
linked_requests.unlink()
+ self.activity_update()
return True
@api.multi
def action_confirm(self):
if self.filtered(lambda holiday: holiday.state != 'draft'):
raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.'))
- return self.write({'state': 'confirm'})
+ self.write({'state': 'confirm'})
+ self.activity_update()
+ return True
@api.multi
def action_approve(self):
@@ -387,6 +391,7 @@ class HolidaysRequest(models.Model):
self.filtered(lambda hol: hol.validation_type == 'both').write({'state': 'validate1', 'first_approver_id': current_employee.id})
self.filtered(lambda hol: not hol.validation_type == 'both').action_validate()
+ self.activity_update()
return True
@api.multi
@@ -418,6 +423,7 @@ class HolidaysRequest(models.Model):
leaves.action_approve()
if leaves and leaves[0].validation_type == 'both':
leaves.action_validate()
+ self.activity_update()
return True
@api.multi
@@ -440,8 +446,45 @@ class HolidaysRequest(models.Model):
# If a category that created several holidays, cancel all related
holiday.linked_request_ids.action_refuse()
self._remove_resource_leave()
+ self.activity_update()
return True
+ # ------------------------------------------------------------
+ # Activity methods
+ # ------------------------------------------------------------
+
+ def _get_responsible_for_approval(self):
+ if self.state == 'confirm' and self.manager_id.user_id:
+ return self.manager_id.user_id
+ elif self.state == 'confirm' and self.employee_id.parent_id.user_id:
+ return self.employee_id.parent_id.user_id
+ elif self.department_id.manager_id.user_id:
+ return self.department_id.manager_id.user_id
+ return self.env.user
+
+ def activity_update(self):
+ to_clean, to_do = self.env['hr.leave'], self.env['hr.leave']
+ for holiday in self:
+ if holiday.state == 'draft':
+ to_clean |= holiday
+ elif holiday.state == 'confirm':
+ holiday.activity_schedule(
+ 'hr_holidays.mail_act_leave_approval', fields.Date.today(),
+ user_id=holiday._get_responsible_for_approval().id)
+ elif holiday.state == 'validate1':
+ holiday.activity_feedback(['hr_holidays.mail_act_leave_approval'])
+ holiday.activity_schedule(
+ 'hr_holidays.mail_act_leave_second_approval', fields.Date.today(),
+ user_id=holiday._get_responsible_for_approval().id)
+ elif holiday.state == 'validate':
+ to_do |= holiday
+ elif holiday.state == 'refuse':
+ to_clean |= holiday
+ if to_clean:
+ to_clean.activity_unlink(['hr_holidays.mail_act_leave_approval', 'hr_holidays.mail_act_leave_second_approval'])
+ if to_do:
+ to_do.activity_feedback(['hr_holidays.mail_act_leave_approval', 'hr_holidays.mail_act_leave_second_approval'])
+
####################################################
# Messaging methods
####################################################
@@ -450,10 +493,6 @@ class HolidaysRequest(models.Model):
def _track_subtype(self, init_values):
if 'state' in init_values and self.state == 'validate':
return 'hr_holidays.mt_leave_approved'
- elif 'state' in init_values and self.state == 'validate1':
- return 'hr_holidays.mt_leave_first_validated'
- elif 'state' in init_values and self.state == 'confirm':
- return 'hr_holidays.mt_leave_confirmed'
elif 'state' in init_values and self.state == 'refuse':
return 'hr_holidays.mt_leave_refused'
return super(HolidaysRequest, self)._track_subtype(init_values)
diff --git a/addons/hr_holidays/models/hr_leave_allocation.py b/addons/hr_holidays/models/hr_leave_allocation.py
index d36d89dd2e4..fd98213e091 100644
--- a/addons/hr_holidays/models/hr_leave_allocation.py
+++ b/addons/hr_holidays/models/hr_leave_allocation.py
@@ -14,8 +14,8 @@ _logger = logging.getLogger(__name__)
class HolidaysAllocation(models.Model):
_name = "hr.leave.allocation"
- _description = "Allocation"
- _inherit = ['mail.thread']
+ _description = "Leaves Allocation"
+ _inherit = ['mail.thread', 'mail.activity.mixin']
def _default_employee(self):
return self.env.context.get('default_employee_id') or self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
@@ -147,6 +147,7 @@ class HolidaysAllocation(models.Model):
values.update({'department_id': self.env['hr.employee'].browse(employee_id).department_id.id})
holiday = super(HolidaysAllocation, self.with_context(mail_create_nolog=True, mail_create_nosubscribe=True)).create(values)
holiday.add_follower(employee_id)
+ holiday.activity_update()
return holiday
@api.multi
@@ -200,13 +201,16 @@ class HolidaysAllocation(models.Model):
for linked_request in linked_requests:
linked_request.action_draft()
linked_requests.unlink()
+ self.activity_update()
return True
@api.multi
def action_confirm(self):
if self.filtered(lambda holiday: holiday.state != 'draft'):
raise UserError(_('Leave request must be in Draft state ("To Submit") in order to confirm it.'))
- return self.write({'state': 'confirm'})
+ res = self.write({'state': 'confirm'})
+ self.activity_update()
+ return res
@api.multi
def action_approve(self):
@@ -230,6 +234,7 @@ class HolidaysAllocation(models.Model):
self.filtered(lambda hol: hol.validation_type == 'both').write({'state': 'validate1', 'first_approver_id': current_employee.id})
self.filtered(lambda hol: not hol.validation_type == 'both').action_validate()
+ self.activity_update()
return True
@api.multi
@@ -259,6 +264,7 @@ class HolidaysAllocation(models.Model):
leaves.action_approve()
if leaves and leaves[0].validation_type == 'both':
leaves.action_validate()
+ self.activity_update()
return True
@api.multi
@@ -277,8 +283,43 @@ class HolidaysAllocation(models.Model):
holiday.write({'state': 'refuse', 'second_approver_id': current_employee.id})
# If a category that created several holidays, cancel all related
holiday.linked_request_ids.action_refuse()
+ self.activity_update()
return True
+ # ------------------------------------------------------------
+ # Activity methods
+ # ------------------------------------------------------------
+
+ def _get_responsible_for_approval(self):
+ if self.state == 'confirm' and self.employee_id.parent_id.user_id:
+ return self.employee_id.parent_id.user_id
+ elif self.department_id.manager_id.user_id:
+ return self.department_id.manager_id.user_id
+ return self.env.user
+
+ def activity_update(self):
+ to_clean, to_do = self.env['hr.leave.allocation'], self.env['hr.leave.allocation']
+ for allocation in self:
+ if allocation.state == 'draft':
+ to_clean |= allocation
+ elif allocation.state == 'confirm':
+ allocation.activity_schedule(
+ 'hr_holidays.mail_act_leave_allocation_approval', fields.Date.today(),
+ user_id=allocation._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', fields.Date.today(),
+ user_id=allocation._get_responsible_for_approval().id)
+ elif allocation.state == 'validate':
+ to_do |= allocation
+ elif allocation.state == 'refuse':
+ to_clean |= allocation
+ if to_clean:
+ to_clean.activity_unlink(['hr_holidays.mail_act_leave_allocation_approval', 'hr_holidays.mail_act_leave_allocation_second_approval'])
+ if to_do:
+ to_do.activity_feedback(['hr_holidays.mail_act_leave_allocation_approval', 'hr_holidays.mail_act_leave_allocation_second_approval'])
+
####################################################
# Messaging methods
####################################################
@@ -287,10 +328,6 @@ class HolidaysAllocation(models.Model):
def _track_subtype(self, init_values):
if 'state' in init_values and self.state == 'validate':
return 'hr_holidays.mt_leave_allocation_approved'
- elif 'state' in init_values and self.state == 'validate1':
- return 'hr_holidays.mt_leave_allocation_first_validated'
- elif 'state' in init_values and self.state == 'confirm':
- return 'hr_holidays.mt_leave_allocation_confirmed'
elif 'state' in init_values and self.state == 'refuse':
return 'hr_holidays.mt_leave_allocation_refused'
return super(HolidaysAllocation, self)._track_subtype(init_values)
diff --git a/addons/hr_holidays/security/ir.model.access.csv b/addons/hr_holidays/security/ir.model.access.csv
index 6238cdf051a..f06aa9a96f1 100644
--- a/addons/hr_holidays/security/ir.model.access.csv
+++ b/addons/hr_holidays/security/ir.model.access.csv
@@ -10,3 +10,4 @@ access_resource_calendar_leaves_user,resource_calendar_leaves_user,resource.mode
access_calendar_event_hr_user,calendar.event.hr.user,calendar.model_calendar_event,hr_holidays.group_hr_holidays_user,1,1,1,1
access_calendar_event_type_manager,calendar.event.type.manager,calendar.model_calendar_event_type,hr_holidays.group_hr_holidays_manager,1,1,1,1
access_calendar_attendee_hr_user,calendar.attendee.hr.user,calendar.model_calendar_attendee,hr_holidays.group_hr_holidays_user,1,1,1,1
+access_mail_activity_type_holidays_manager,mail.activity.type.holidays.manager,mail.model_mail_activity_type,hr_holidays.group_hr_holidays_manager,1,1,1,1
diff --git a/addons/hr_holidays/views/hr_leave_allocation_views.xml b/addons/hr_holidays/views/hr_leave_allocation_views.xml
index 933a4cbebba..45887ea0843 100644
--- a/addons/hr_holidays/views/hr_leave_allocation_views.xml
+++ b/addons/hr_holidays/views/hr_leave_allocation_views.xml
@@ -25,6 +25,15 @@
+
+
+
+
@@ -77,6 +86,7 @@
+
@@ -116,21 +126,6 @@
-
- hr.leave.allocation.tree.customize
- hr.leave.allocation
-
-
-
-
-
-
-
-
-
-
-
-
Allocation Request
hr.leave.allocation
@@ -212,4 +207,42 @@
+
+ Requests to Approve
+ hr.leave
+ form
+ {
+ 'search_default_approve':1,
+ }
+
+
+
+
+
+
+ tree
+
+
+
+
+
+
+ form
+
+
+
+
+
+
+
+
diff --git a/addons/hr_holidays/views/hr_leave_type_views.xml b/addons/hr_holidays/views/hr_leave_type_views.xml
index c100347b534..d9b556106d6 100644
--- a/addons/hr_holidays/views/hr_leave_type_views.xml
+++ b/addons/hr_holidays/views/hr_leave_type_views.xml
@@ -128,4 +128,17 @@
+
+
+
+
diff --git a/addons/hr_holidays/views/hr_leave_views.xml b/addons/hr_holidays/views/hr_leave_views.xml
index c985ee037e0..9ed26df82d0 100644
--- a/addons/hr_holidays/views/hr_leave_views.xml
+++ b/addons/hr_holidays/views/hr_leave_views.xml
@@ -43,6 +43,15 @@
+
+
+
+
@@ -119,6 +128,7 @@
+
@@ -240,31 +250,6 @@
action="open_ask_holidays"
sequence="22"/>
-
- Requests to Approve
- hr.leave
- form
- {
- 'search_default_approve':1,
- }
-
-
-
-
-
-
- tree
-
-
-
-
-
-
- form
-
-
-
-
Leaves Overview
hr.leave
@@ -306,13 +291,6 @@
action="open_department_holidays_approve"
sequence="1"/>
-
-
-
-
+
+ hr.leave.allocation.tree.customize
+ hr.leave.allocation
+
+
+
+
+
+
+
+
+
+
+
+
Leaves to be reported in Payslip
ir.actions.act_window
@@ -432,11 +417,4 @@
id="menu_hr_available_holidays_report_tree"
action="action_hr_available_holidays_report"
parent="menu_hr_holidays_report" sequence="5"/>
-
-
-
diff --git a/addons/hr_holidays/views/mail_activity_views.xml b/addons/hr_holidays/views/mail_activity_views.xml
new file mode 100644
index 00000000000..952a81e7b26
--- /dev/null
+++ b/addons/hr_holidays/views/mail_activity_views.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ Activity Types
+ mail.activity.type
+ form
+ tree,form
+ ['|', ('res_model_id', '=', False), ('res_model_id.model', 'in', ['hr.leave', 'hr.leave.allocation'])]
+ {'default_res_model': 'hr.leave'}
+
+
+
\ No newline at end of file
diff --git a/addons/mail/controllers/main.py b/addons/mail/controllers/main.py
index 9682cbe1125..3445691f601 100644
--- a/addons/mail/controllers/main.py
+++ b/addons/mail/controllers/main.py
@@ -237,7 +237,7 @@ class MailController(http.Controller):
if date_deadline:
values['date_deadline'] = date_deadline
if not activity_type_id:
- activity_type_id = request.env['mail.activity.type'].sudo().search([('category', '=', 'reminder')]).id
+ activity_type_id = request.env['mail.activity.type'].sudo().search([('category', '=', 'reminder')], limit=1).id
if activity_type_id:
values['activity_type_id'] = activity_type_id
activity = request.env['mail.activity'].create(values)
diff --git a/addons/mail/models/mail_activity.py b/addons/mail/models/mail_activity.py
index 1259e7395f8..0f849526b90 100644
--- a/addons/mail/models/mail_activity.py
+++ b/addons/mail/models/mail_activity.py
@@ -93,6 +93,9 @@ class MailActivity(models.Model):
active = fields.Boolean(
'Open', default=True,
help='Done reminders should be archived instead of marked as done.')
+ automated = fields.Boolean(
+ 'Automated activity', readonly=True,
+ help='Indicates this activity has been created automatically and not by any user.')
# description
user_id = fields.Many2one(
'res.users', 'Assigned to',
@@ -345,7 +348,15 @@ class MailActivityMixin(models.AbstractModel):
There is also a kanban widget defined. It defines a small widget to integrate
in kanban vignettes. It allow to manage activities directly from the kanban
view. Use widget="kanban_activity" on activitiy_ids field in kanban view to
- use it."""
+ use it.
+
+ Some context keys allow to control the mixin behavior. Use those in some
+ specific cases like import
+
+ * ``mail_activity_automation_skip``: skip activities automation; it means
+ no automated activities will be generated, updated or unlinked, allowing
+ to save computation and avoid generating unwanted activities;
+ """
_name = 'mail.activity.mixin'
_description = 'Activity Mixin'
@@ -432,3 +443,103 @@ class MailActivityMixin(models.AbstractModel):
[('res_model', '=', self._name), ('res_id', 'in', record_ids)]
).unlink()
return result
+
+ def activity_schedule(self, act_type_xmlid='', date_deadline=None, summary='', note='', **act_values):
+ """ Schedule an activity on each record of the current record set.
+ This method allow to provide as parameter act_type_xmlid. This is an
+ xml_id of activity type instead of directly giving an activity_type_id.
+ It is useful to avoid having various "env.ref" in the code and allow
+ to let the mixin handle access rights.
+ """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ if not date_deadline:
+ date_deadline = fields.Date.today()
+ if act_type_xmlid:
+ activity_type = self.sudo().env.ref(act_type_xmlid)
+ else:
+ activity_type = self.env['mail.activity.type'].sudo().browse(act_values['activity_type_id'])
+
+ model_id = self.env['ir.model']._get(self._name).id
+ activities = self.env['mail.activity']
+ for record in self:
+ create_vals = {
+ 'activity_type_id': activity_type.id,
+ 'summary': summary or activity_type.summary,
+ 'automated': True,
+ 'note': note,
+ 'date_deadline': date_deadline,
+ 'res_model_id': model_id,
+ 'res_id': record.id,
+ }
+ create_vals.update(act_values)
+ activities |= self.env['mail.activity'].create(create_vals)
+ return activities
+
+ def activity_reschedule(self, act_type_xmlids, user_id=None, date_deadline=None):
+ """ Reschedule some automated activities. Activities to reschedule are
+ selected based on type xml ids and optionally by user. Purpose is to be
+ able to change the deadline, not anything else currently. """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ Data = self.env['ir.model.data'].sudo()
+ activity_types_ids = [Data.xmlid_to_res_id(xmlid) for xmlid in act_type_xmlids]
+ domain = [
+ '&', '&', '&',
+ ('res_model', '=', self._name),
+ ('res_id', 'in', self.ids),
+ ('automated', '=', True),
+ ('activity_type_id', 'in', activity_types_ids)
+ ]
+ if user_id:
+ domain = ['&'] + domain + [('user_id', '=', user_id)]
+ activities = self.env['mail.activity'].search(domain)
+ if activities:
+ activities.write({
+ 'date_deadline': date_deadline,
+ })
+ return activities
+
+ def activity_feedback(self, act_type_xmlids, user_id=None, feedback=None):
+ """ Set activities as done, limiting to some activity types and
+ optionally to a given user. """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ Data = self.env['ir.model.data'].sudo()
+ activity_types_ids = [Data.xmlid_to_res_id(xmlid) for xmlid in act_type_xmlids]
+ domain = [
+ '&', '&', '&',
+ ('res_model', '=', self._name),
+ ('res_id', 'in', self.ids),
+ ('automated', '=', True),
+ ('activity_type_id', 'in', activity_types_ids)
+ ]
+ if user_id:
+ domain = ['&'] + domain + [('user_id', '=', user_id)]
+ activities = self.env['mail.activity'].search(domain)
+ if activities:
+ activities.action_feedback(feedback=feedback)
+ return True
+
+ def activity_unlink(self, act_type_xmlids, user_id=None):
+ """ Unlink activities, limiting to some activity types and optionally
+ to a given user. """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return False
+
+ Data = self.env['ir.model.data'].sudo()
+ activity_types_ids = [Data.xmlid_to_res_id(xmlid) for xmlid in act_type_xmlids]
+ domain = [
+ '&', '&', '&',
+ ('res_model', '=', self._name),
+ ('res_id', 'in', self.ids),
+ ('automated', '=', True),
+ ('activity_type_id', 'in', activity_types_ids)
+ ]
+ if user_id:
+ domain = ['&'] + domain + [('user_id', '=', user_id)]
+ self.env['mail.activity'].search(domain).unlink()
+ return True
diff --git a/addons/maintenance/__manifest__.py b/addons/maintenance/__manifest__.py
index 80925151e65..d40bdbe3d9e 100644
--- a/addons/maintenance/__manifest__.py
+++ b/addons/maintenance/__manifest__.py
@@ -16,6 +16,7 @@
'data/mail_data.xml',
'views/maintenance_views.xml',
'views/maintenance_templates.xml',
+ 'views/mail_activity_views.xml',
'data/maintenance_cron.xml',
],
'demo': ['data/maintenance_demo.xml'],
diff --git a/addons/maintenance/data/mail_data.xml b/addons/maintenance/data/mail_data.xml
index 74a3a6abe9d..c7b3945b0c7 100644
--- a/addons/maintenance/data/mail_data.xml
+++ b/addons/maintenance/data/mail_data.xml
@@ -1,6 +1,13 @@
+
+
+ Maintenance Request
+ fa-wrench
+
+
+
helpdesk
diff --git a/addons/maintenance/models/maintenance.py b/addons/maintenance/models/maintenance.py
index 47bcf0a95b3..e324123595f 100644
--- a/addons/maintenance/models/maintenance.py
+++ b/addons/maintenance/models/maintenance.py
@@ -336,6 +336,7 @@ class MaintenanceRequest(models.Model):
request._add_followers()
if request.equipment_id and not request.maintenance_team_id:
request.maintenance_team_id = request.equipment_id.maintenance_team_id
+ request.activity_update()
return request
@api.multi
@@ -349,8 +350,31 @@ class MaintenanceRequest(models.Model):
self._add_followers()
if self.stage_id.done and 'stage_id' in vals:
self.write({'close_date': fields.Date.today()})
+ self.activity_feedback(['maintenance.mail_act_maintenance_request'])
+ if 'schedule_date' in vals:
+ self.activity_update()
return res
+ def activity_update(self):
+ """ Update maintenance activities based on current record set state.
+ It reschedule, unlink or create maintenance request activities. """
+ self.filtered(lambda request: not request.schedule_date).activity_unlink(['maintenance.mail_act_maintenance_request'])
+ for request in self.filtered(lambda request: request.schedule_date):
+ date_dl = fields.Datetime.from_string(request.schedule_date).date()
+ updated = request.activity_reschedule(
+ ['maintenance.mail_act_maintenance_request'],
+ date_deadline=date_dl)
+ if not updated:
+ if request.equipment_id:
+ note = _('Request planned for %s') % (
+ request.equipment_id._name, request.equipment_id.id, request.equipment_id.display_name)
+ else:
+ note = False
+ request.activity_schedule(
+ 'maintenance.mail_act_maintenance_request',
+ fields.Datetime.from_string(request.schedule_date).date(),
+ note=note, user_id=request.technician_user_id.id or request.owner_user_id.id or self.env.uid)
+
def _add_followers(self):
for request in self:
user_ids = (request.owner_user_id + request.technician_user_id).ids
diff --git a/addons/maintenance/security/ir.model.access.csv b/addons/maintenance/security/ir.model.access.csv
index 264be46b68e..a4609810350 100644
--- a/addons/maintenance/security/ir.model.access.csv
+++ b/addons/maintenance/security/ir.model.access.csv
@@ -7,4 +7,5 @@ access_equipment_category_admin_user,equipment.category system user,model_mainte
access_maintenance_stage_user,maintenance.stage.user,model_maintenance_stage,base.group_user,1,0,0,0
access_maintenance_stage_admin_user,equipment.request.stage system user,model_maintenance_stage,group_equipment_manager,1,1,1,1
access_maintenance_team_user,maintenance.team.user,model_maintenance_team,base.group_user,1,0,0,0
-access_maintenance_team_admin_user,maintenance.team.admin.user,model_maintenance_team,group_equipment_manager,1,1,1,1
\ No newline at end of file
+access_maintenance_team_admin_user,maintenance.team.admin.user,model_maintenance_team,group_equipment_manager,1,1,1,1
+access_mail_activity_type_equipment_manager,mail.activity.type.equipment.manager,mail.model_mail_activity_type,maintenance.group_equipment_manager,1,1,1,1
\ No newline at end of file
diff --git a/addons/maintenance/views/mail_activity_views.xml b/addons/maintenance/views/mail_activity_views.xml
new file mode 100644
index 00000000000..1768df708a3
--- /dev/null
+++ b/addons/maintenance/views/mail_activity_views.xml
@@ -0,0 +1,17 @@
+
+
+
+
+ Activity Types
+ mail.activity.type
+ form
+ tree,form
+ ['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'maintenance.request')]
+ {'default_res_model': 'maintenance.request'}
+
+
+
\ No newline at end of file
diff --git a/addons/sale/__manifest__.py b/addons/sale/__manifest__.py
index ca5786aefe9..9a84cb0564a 100644
--- a/addons/sale/__manifest__.py
+++ b/addons/sale/__manifest__.py
@@ -26,6 +26,7 @@ This module contains all the common features of Sales Management and eCommerce.
'views/account_invoice_views.xml',
'views/sales_team_views.xml',
'views/res_partner_views.xml',
+ 'views/mail_activity_views.xml',
'views/assets.xml',
'views/sale_portal_templates.xml',
'views/sale_layout_category_view.xml',
diff --git a/addons/sale/data/mail_data.xml b/addons/sale/data/mail_data.xml
index 238bb55d2f2..5c9d0b6a2e5 100644
--- a/addons/sale/data/mail_data.xml
+++ b/addons/sale/data/mail_data.xml
@@ -1,6 +1,13 @@
+
+
+ Order Upsell
+ fa-line-chart
+
+
+
Quotation sent
diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py
index 5758e50f72b..0461dbadfdd 100644
--- a/addons/sale/models/sale.py
+++ b/addons/sale/models/sale.py
@@ -289,6 +289,31 @@ class SaleOrder(models.Model):
result = super(SaleOrder, self).create(vals)
return result
+ @api.multi
+ def _write(self, values):
+ """ Override of private write method in order to generate activities
+ based in the invoice status. As the invoice status is a computed field
+ triggered notably when its lines and linked invoice status changes the
+ flow does not necessarily goes through write if the action was not done
+ on the SO itself. We hence override the _write to catch the computation
+ of invoice_status field. """
+ if self.env.context.get('mail_activity_automation_skip'):
+ return super(SaleOrder, self)._write(values)
+
+ res = super(SaleOrder, self)._write(values)
+ if 'invoice_status' in values:
+ self.activity_unlink(['sale.mail_act_sale_upsell'])
+ if values['invoice_status'] == 'upselling':
+ for order in self:
+ order.activity_schedule(
+ 'sale.mail_act_sale_upsell', fields.Date.today(),
+ user_id=order.user_id.id,
+ note=_("Upsell %s for customer %s") % (
+ order._name, order.id, order.name,
+ order.partner_id._name, order.partner_id.id, order.partner_id.display_name))
+
+ return res
+
@api.multi
def copy_data(self, default=None):
if default is None:
diff --git a/addons/sale/security/ir.model.access.csv b/addons/sale/security/ir.model.access.csv
index 1a559ba8f4c..61ff1649721 100644
--- a/addons/sale/security/ir.model.access.csv
+++ b/addons/sale/security/ir.model.access.csv
@@ -55,3 +55,4 @@ access_account_tax_group_sale_manager,account.tax.group sale manager,account.mod
access_account_account_sale_manager,account.account sale manager,account.model_account_account,sales_team.group_sale_manager,1,0,0,0
access_report_all_channels_sales_salesman,report.all.channels.sales,model_report_all_channels_sales,sales_team.group_sale_salesman,1,1,1,0
access_report_all_channels_sales_manager,report.all.channels.sales,model_report_all_channels_sales,sales_team.group_sale_manager,1,1,1,1
+access_mail_activity_type_sale_manager,mail.activity.type.sale.manager,mail.model_mail_activity_type,sales_team.group_sale_manager,1,1,1,1
\ No newline at end of file
diff --git a/addons/sale/views/mail_activity_views.xml b/addons/sale/views/mail_activity_views.xml
new file mode 100644
index 00000000000..6c0eede2515
--- /dev/null
+++ b/addons/sale/views/mail_activity_views.xml
@@ -0,0 +1,16 @@
+
+
+
+
+ Activity Types
+ mail.activity.type
+ form
+ tree,form
+ ['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'sale.order')]
+ {'default_res_model': 'sale.order'}
+
+
+
\ No newline at end of file
diff --git a/addons/test_mail/__manifest__.py b/addons/test_mail/__manifest__.py
index 51d9cc7fe42..40d43883d4b 100644
--- a/addons/test_mail/__manifest__.py
+++ b/addons/test_mail/__manifest__.py
@@ -12,6 +12,7 @@ tests independently to functional aspects of other models. """,
'depends': ['test_performance', 'mail'],
'data': [
'security/ir.model.access.csv',
+ 'data/data.xml',
],
'demo': [
'data/demo.xml',
diff --git a/addons/test_mail/data/data.xml b/addons/test_mail/data/data.xml
new file mode 100644
index 00000000000..19be18eb2d6
--- /dev/null
+++ b/addons/test_mail/data/data.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ Do Stuff
+ Really?! Wow! A superpowers drug you can just rub onto your skin?
+ default
+
+
+
+ Meet People
+ You'd think it would be something you'd have to freebase. Noooooo!
+ default
+
+
+
+ Call People
+ Then throw her in the laundry room, which will hereafter be referred to as "the brig".
+ default
+
+
+
+
diff --git a/addons/test_mail/models/test_mail_models.py b/addons/test_mail/models/test_mail_models.py
index 4c1fd408ddf..06d84986fe8 100644
--- a/addons/test_mail/models/test_mail_models.py
+++ b/addons/test_mail/models/test_mail_models.py
@@ -38,6 +38,15 @@ class MailTestActivity(models.Model):
name = fields.Char()
email_from = fields.Char()
+ def action_start(self, action_summary):
+ self.activity_schedule(
+ 'test_mail.mail_act_test_todo',
+ summary=action_summary
+ )
+
+ def action_close(self, action_feedback):
+ self.activity_feedback(['test_mail.mail_act_test_todo'], feedback=action_feedback)
+
class MailTestFull(models.Model):
""" This model can be used in tests when complex chatter features are
diff --git a/addons/test_mail/tests/test_mail_activity.py b/addons/test_mail/tests/test_mail_activity.py
index cb1b0173d9f..e340358a5ee 100644
--- a/addons/test_mail/tests/test_mail_activity.py
+++ b/addons/test_mail/tests/test_mail_activity.py
@@ -14,7 +14,13 @@ class TestMailActivity(BaseFunctionalTest):
@classmethod
def setUpClass(cls):
super(TestMailActivity, cls).setUpClass()
- cls.test_record = cls.env['mail.test.activity'].create({'name': 'Test'})
+ cls.test_record = cls.env['mail.test.activity'].with_context(cls._quick_create_ctx).create({'name': 'Test'})
+ # reset ctx
+ cls.test_record = cls.test_record.with_context(
+ mail_create_nolog=False,
+ mail_create_nosubscribe=False,
+ mail_notrack=False
+ )
def test_activity_flow_employee(self):
with self.sudoAs('ernest'):
@@ -55,8 +61,6 @@ class TestMailActivity(BaseFunctionalTest):
with self.sudoAs('chell'):
test_record = self.env['mail.test.activity'].browse(self.test_record.id)
- # test_record.name
- # test_record.read()
with self.assertRaises(exceptions.AccessError):
self.env['mail.activity'].create({
'summary': 'Test Activity',
@@ -64,52 +68,111 @@ class TestMailActivity(BaseFunctionalTest):
'res_model_id': self.env['ir.model']._get(test_record._name).id,
'res_id': test_record.id,
})
- # self.assertEqual(test_record.activity_ids, self.env['mail.activity'])
+
def test_reminder_security(self):
- admin = self.env.ref('base.user_root')
- demo = self.env.ref('base.user_demo')
-
- reminder = self.env['mail.activity'].sudo(admin).create({
- 'note': 'Test Reminder',
- 'date_deadline': date.today() + relativedelta(days=1),
- }) #try to delete record with demo user
- with self.assertRaises(exceptions.AccessError):
- #try to delete admin record with demo user
- reminder.sudo(demo).unlink()
-
- with self.assertRaises(exceptions.AccessError):
- #try to update admin record with demo user
- reminder.sudo(demo).write({'note': 'Give money to demo user'})
-
- #but demo should be able to edit and delete its own reminder
- demo_reminder = self.env['mail.activity'].sudo(demo).create({
- 'note': 'Test Reminder demo',
- 'date_deadline': date.today() + relativedelta(days=2),
+ reminder = self.env['mail.activity'].sudo(self.user_admin).create({
+ 'note': 'Test Reminder',
+ 'date_deadline': date.today() + relativedelta(days=1),
})
- demo_reminder.sudo(demo).write({'note': 'Holidays'})
- self.assertEqual(demo_reminder.note, 'Holidays
')
- #but edit or res_id and res_model should be impossible
with self.assertRaises(exceptions.AccessError):
- demo_reminder.sudo(demo).write({'res_id': 1})
- with self.assertRaises(exceptions.AccessError):
- demo_reminder.sudo(demo).write({'res_model_id': 1})
- #user can unlink own reminder
- demo_reminder.sudo(demo).unlink()
+ # try to delete admin record with demo user
+ reminder.sudo(self.user_employee).unlink()
- def test_reminder_summary(self):
- reminder = self.env['mail.activity'].create({
+ with self.assertRaises(exceptions.AccessError):
+ # try to update admin record with demo user
+ reminder.sudo(self.user_employee).write({'note': 'Give money to demo user'})
+
+ # but demo should be able to edit and delete its own reminder
+ demo_reminder = self.env['mail.activity'].sudo(self.user_employee).create({
+ 'note': 'Test Reminder demo',
+ 'date_deadline': date.today() + relativedelta(days=2),
+ })
+ demo_reminder.write({'note': 'Holidays
'})
+ self.assertEqual(demo_reminder.note, 'Holidays
')
+
+ # edit of res_id and res_model should be impossible
+ with self.assertRaises(exceptions.AccessError):
+ demo_reminder.write({'res_id': 1})
+ with self.assertRaises(exceptions.AccessError):
+ demo_reminder.write({'res_model_id': 1})
+
+ demo_reminder.unlink()
+
+ def test_reminder_flow(self):
+ with self.sudoAs('ernest'):
+ reminder = self.env['mail.activity'].create({
'note': 'Test Reminder',
'date_deadline': date.today(),
- })
- self.assertEqual(reminder.summary, 'Test Reminder', "Summary should be first line of note by default")
- reminder.write({'note': 'Holidays\nDestination: Pescara'})
- self.assertEqual(reminder.summary, 'Holidays', "Summary should be first line of note by default")
- reminder.write({'note': ''})
- self.assertEqual(reminder.summary, 'Reminder', "If note is empty, summary should be Reminder")
- reminder.write({'note': 'Holidays', 'summary': 'Summary'})
- self.assertEqual(reminder.summary, 'Summary', "If summary is set, shouldn't be changed")
+ })
+ self.assertEqual(reminder.summary, 'Test Reminder')
+ reminder.write({'note': 'Holidays\nDestination: Pescara'})
+ self.assertEqual(reminder.summary, 'Holidays')
+ reminder.write({'note': ''})
+ self.assertEqual(reminder.summary, 'Reminder')
+ reminder.write({'note': 'Holidays', 'summary': 'Summary'})
+ self.assertEqual(reminder.summary, 'Summary')
-
+ def test_activity_mixin(self):
+ today = date.today()
+ with self.sudoAs('ernest'):
+ self.assertEqual(self.test_record.env.user, self.user_employee)
+ # Test various scheduling of activities
+ act1 = self.test_record.activity_schedule(
+ 'test_mail.mail_act_test_todo',
+ today + relativedelta(days=1),
+ user_id=self.user_admin.id)
+ self.assertEqual(act1.automated, True)
+ act_type = self.env.ref('test_mail.mail_act_test_todo')
+ self.assertEqual(self.test_record.activity_summary, act_type.summary)
+ self.assertEqual(self.test_record.activity_state, 'planned')
+ self.assertEqual(self.test_record.activity_user_id, self.user_admin)
+
+ act2 = self.test_record.activity_schedule(
+ 'test_mail.mail_act_test_meeting',
+ today + relativedelta(days=-1))
+ self.assertEqual(self.test_record.activity_state, 'overdue')
+ self.assertEqual(self.test_record.activity_user_id, self.user_employee)
+
+ act3 = self.test_record.activity_schedule(
+ 'test_mail.mail_act_test_todo',
+ today + relativedelta(days=3),
+ user_id=self.user_employee.id)
+ self.assertEqual(self.test_record.activity_state, 'overdue')
+ self.assertEqual(self.test_record.activity_user_id, self.user_employee)
+
+ self.assertEqual(self.test_record.activity_ids, act1 | act2 | act3)
+
+ # Perform todo activities for admin
+ self.test_record.activity_feedback(
+ ['test_mail.mail_act_test_todo'],
+ user_id=self.user_admin.id,
+ feedback='Test feedback',)
+ self.assertEqual(self.test_record.activity_ids, act2 | act3)
+
+ # Reschedule all activities, should update the record state
+ self.assertEqual(self.test_record.activity_state, 'overdue')
+ self.test_record.activity_reschedule(
+ ['test_mail.mail_act_test_meeting', 'test_mail.mail_act_test_todo'],
+ date_deadline=today + relativedelta(days=3)
+ )
+ self.assertEqual(self.test_record.activity_state, 'planned')
+
+ # Perform todo activities for remaining people
+ self.test_record.activity_feedback(
+ ['test_mail.mail_act_test_todo'],
+ feedback='Test feedback')
+
+ # Setting activities as done should delete them and post messages
+ self.assertEqual(self.test_record.activity_ids, act2)
+ self.assertEqual(len(self.test_record.message_ids), 2)
+ self.assertEqual(self.test_record.message_ids.mapped('subtype_id'), self.env.ref('mail.mt_activities'))
+
+ # Perform meeting activities
+ self.test_record.activity_unlink(['test_mail.mail_act_test_meeting'])
+
+ # Canceling activities should simply remove them
+ self.assertEqual(self.test_record.activity_ids, self.env['mail.activity'])
+ self.assertEqual(len(self.test_record.message_ids), 2)
diff --git a/addons/test_mail/tests/test_performance.py b/addons/test_mail/tests/test_performance.py
index 183165f5e41..be887fc0173 100644
--- a/addons/test_mail/tests/test_performance.py
+++ b/addons/test_mail/tests/test_performance.py
@@ -160,16 +160,32 @@ class TestAdvMailPerformance(TransactionCase):
'default_res_model': 'mail.test.activity',
})
- with self.assertQueryCount(admin=26, emp=32): # test_mail only: 26 - 32
+ with self.assertQueryCount(admin=26, emp=33): # test_mail only: 26 - 32
activity = MailActivity.create({
'summary': 'Test Activity',
'res_id': record.id,
'activity_type_id': self.env.ref('mail.mail_activity_data_todo').id,
})
- with self.assertQueryCount(admin=59, emp=88): # test_mail only: 58 - 86
+ with self.assertQueryCount(admin=59, emp=88): # test_mail only: 58 - 87
activity.action_feedback(feedback='Zizisse Done !')
+ @users('admin', 'emp')
+ @warmup
+ @mute_logger('odoo.models.unlink')
+ def test_adv_activity_mixin(self):
+ record = self.env['mail.test.activity'].create({'name': 'Test'})
+
+ with self.assertQueryCount(admin=26, emp=32): # test_mail only: 26 - 32
+ record.action_start('Test Start')
+
+ record.write({'name': 'Dupe write'})
+
+ with self.assertQueryCount(admin=58, emp=88): # test_mail only: 58 - 88
+ record.action_close('Dupe feedback')
+
+ self.assertEqual(record.activity_ids, self.env['mail.activity'])
+
@mute_logger('odoo.addons.mail.models.mail_mail', 'odoo.models.unlink')
@users('admin', 'emp')
@warmup