[MERGE][IMP] mail, various: add automatic activities generation to ease business flows
This merge make several application automatically generate activities based on business requirements in order to ease the daily job of people using it. In this merge * methods allowing to create, reschedule, mark as done or unlink activites are added in mail.activity.mixin; * automated activities are flagged using the newly-added automated field to distinguish them from other activities; * mail.activity.mixin is added to some models to ensure activities are activated; * several main applications use this API to integrate activities automation in their flow: hr_holidays (leave and allocation), hr_expense (expense report), fleet (vehicle contract), maintenance (maintenance request), sale (sale order); Please see sub-commits for more details about the mixin update as well as the integration in the various applications. This merge is linked to task ID 39578. Closes #20799 .
This commit is contained in:
@@ -35,8 +35,10 @@ Main Features
|
||||
'views/fleet_vehicle_views.xml',
|
||||
'views/fleet_vehicle_cost_views.xml',
|
||||
'views/fleet_board_view.xml',
|
||||
'views/mail_activity_views.xml',
|
||||
'data/fleet_cars_data.xml',
|
||||
'data/fleet_data.xml',
|
||||
'data/mail_data.xml',
|
||||
],
|
||||
|
||||
'demo': ['data/fleet_demo.xml'],
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo><data noupdate="1">
|
||||
|
||||
<record id="mail_act_fleet_contract_to_renew" model="mail.activity.type">
|
||||
<field name="name">Contract to Renew</field>
|
||||
<field name="icon">fa-car</field>
|
||||
<field name="res_model_id" ref="fleet.model_fleet_vehicle_log_contract"/>
|
||||
</record>
|
||||
|
||||
</data></odoo>
|
||||
@@ -71,7 +71,7 @@ class FleetVehicleCost(models.Model):
|
||||
|
||||
|
||||
class FleetVehicleLogContract(models.Model):
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_inherits = {'fleet.vehicle.cost': 'cost_id'}
|
||||
_name = 'fleet.vehicle.log.contract'
|
||||
_description = 'Contract information on a vehicle'
|
||||
@@ -95,7 +95,8 @@ class FleetVehicleLogContract(models.Model):
|
||||
|
||||
name = fields.Text(compute='_compute_contract_name', store=True)
|
||||
active = fields.Boolean(default=True)
|
||||
start_date = fields.Date('Contract Start Date', default=fields.Date.context_today,
|
||||
user_id = fields.Many2one('res.users', 'Responsible', default=lambda self: self.env.user, index=True)
|
||||
start_date = fields.Date('Contract Start Date', default=fields.Date.context_today,
|
||||
help='Date when the coverage of the contract begins')
|
||||
expiration_date = fields.Date('Contract Expiration Date', default=lambda self:
|
||||
self.compute_next_year_date(fields.Date.context_today(self)),
|
||||
@@ -262,17 +263,12 @@ class FleetVehicleLogContract(models.Model):
|
||||
date_today = fields.Date.from_string(fields.Date.today())
|
||||
in_fifteen_days = fields.Date.to_string(date_today + relativedelta(days=+15))
|
||||
nearly_expired_contracts = self.search([('state', '=', 'open'), ('expiration_date', '<', in_fifteen_days)])
|
||||
res = {}
|
||||
for contract in nearly_expired_contracts:
|
||||
if contract.vehicle_id.id in res:
|
||||
res[contract.vehicle_id.id] += 1
|
||||
else:
|
||||
res[contract.vehicle_id.id] = 1
|
||||
|
||||
Vehicle = self.env['fleet.vehicle']
|
||||
for vehicle, value in res.items():
|
||||
Vehicle.browse(vehicle).message_post(body=_('%s contract(s) will expire soon and should be renewed and/or closed!') % value)
|
||||
nearly_expired_contracts.write({'state': 'diesoon'})
|
||||
for contract in nearly_expired_contracts.filtered(lambda contract: contract.user_id):
|
||||
contract.activity_schedule(
|
||||
'fleet.mail_act_fleet_contract_to_renew', contract.expiration_date,
|
||||
user_id=contract.user_id.id)
|
||||
|
||||
expired_contracts = self.search([('state', '!=', 'expired'), ('expiration_date', '<',fields.Date.today() )])
|
||||
expired_contracts.write({'state': 'expired'})
|
||||
@@ -283,7 +279,6 @@ class FleetVehicleLogContract(models.Model):
|
||||
now_running_contracts = self.search([('state', '=', 'futur'), ('start_date', '<=', fields.Date.today())])
|
||||
now_running_contracts.write({'state': 'open'})
|
||||
|
||||
@api.model
|
||||
def run_scheduler(self):
|
||||
self.scheduler_manage_auto_costs()
|
||||
self.scheduler_manage_contract_expiration()
|
||||
|
||||
@@ -19,3 +19,4 @@ fleet_vehicle_log_services_access_right,fleet_vehicle_log_services_access_right,
|
||||
fleet_vehicle_log_contract_access_right,fleet_vehicle_log_contract_access_right,model_fleet_vehicle_log_contract,fleet_group_manager,1,1,1,1
|
||||
fleet_service_type_access_right,fleet_service_type_access_right,model_fleet_service_type,fleet_group_manager,1,1,1,1
|
||||
access_fleet_vehicle_cost,access_fleet_vehicle_cost,model_fleet_vehicle_cost,fleet_group_manager,1,1,1,1
|
||||
access_mail_activity_type_fleet_manager,mail.activity.type.fleet.manager,mail.model_mail_activity_type,fleet.fleet_group_manager,1,1,1,1
|
||||
|
||||
|
Can't render this file because it contains an unexpected character in line 6 and column 59.
|
@@ -187,6 +187,7 @@
|
||||
<group string="Contract details">
|
||||
<field name="vehicle_id"/>
|
||||
<field name="cost_subtype_id" required="1" domain="['|',('category','=','contract'),('category','=','both')]"/>
|
||||
<field name="user_id"/>
|
||||
<field name="amount" string="Activation Cost" help="Cost that is paid only once at the creation of the contract"/>
|
||||
<label for="cost_generated"/>
|
||||
<div>
|
||||
@@ -250,6 +251,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -336,6 +338,15 @@
|
||||
<filter string="In Progress" name="open" domain="[('state', '=', 'open')]"/>
|
||||
<filter string="Expired" name="expired" domain="[('state', '=', 'expired')]"/>
|
||||
<filter string="Expiring Soon" name="expire_soon" domain="[('state', '=', 'diesoon')]"/>
|
||||
<separator/>
|
||||
<filter string="Late Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Vehicle" name="vehicle" context="{'group_by':'vehicle_id'}"/>
|
||||
</group>
|
||||
|
||||
@@ -434,6 +434,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="fleet_vehicle_tag_menu" parent="fleet_configuration" action="fleet_vehicle_tag_action" sequence="112"/>
|
||||
<menuitem id="fleet_vehicle_tag_menu" parent="fleet_configuration" action="fleet_vehicle_tag_action" sequence="89"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<!-- Activity types config -->
|
||||
<record id="mail_activity_type_action_config_fleet" model="ir.actions.act_window">
|
||||
<field name="name">Activity Types</field>
|
||||
<field name="res_model">mail.activity.type</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'fleet.vehicle.log.contract')]</field>
|
||||
<field name="context">{'default_res_model': 'fleet.vehicle.log.contract'}</field>
|
||||
</record>
|
||||
<menuitem id="fleet_menu_config_activity_type"
|
||||
action="mail_activity_type_action_config_fleet"
|
||||
parent="fleet_configuration"
|
||||
sequence="99"
|
||||
groups="base.group_no_one"/>
|
||||
</odoo>
|
||||
@@ -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',
|
||||
|
||||
@@ -2,7 +2,13 @@
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<!--default alias for leads-->
|
||||
<record id="mail_act_expense_approval" model="mail.activity.type">
|
||||
<field name="name">Expense Approval</field>
|
||||
<field name="icon">fa-dollar</field>
|
||||
<field name="res_model_id" ref="hr_expense.model_hr_expense_sheet"/>
|
||||
</record>
|
||||
|
||||
<!-- default alias for expenses -->
|
||||
<record id="mail_alias_expense" model="mail.alias">
|
||||
<field name="alias_name">expense</field>
|
||||
<field name="alias_model_id" ref="model_hr_expense"/>
|
||||
@@ -23,27 +29,12 @@
|
||||
<field name="default" eval="True"/>
|
||||
<field name="description">Expense report refused</field>
|
||||
</record>
|
||||
<record id="mt_expense_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Submitted</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="description">Expense report submitted, waiting approval</field>
|
||||
<field name="default" eval="True"/>
|
||||
</record>
|
||||
<record id="mt_expense_paid" model="mail.message.subtype">
|
||||
<field name="name">Paid</field>
|
||||
<field name="res_model">hr.expense.sheet</field>
|
||||
<field name="description">Expense report paid</field>
|
||||
<field name="default" eval="False"/>
|
||||
</record>
|
||||
<!-- Department (Parent) related subtypes for messaging / Chatter -->
|
||||
<record id="mt_department_expense_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Expenses To Approve</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="parent_id" eval="ref('mt_expense_confirmed')"/>
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="10"/>
|
||||
</record>
|
||||
|
||||
<!-- Email Templates -->
|
||||
<template id="hr_expense_template_refuse_reason">
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -203,6 +203,15 @@
|
||||
<filter string="My Expenses" name="my_expenses" domain="[('employee_id.user_id', '=', uid)]"/>
|
||||
<separator />
|
||||
<filter string="New Mail" name="message_unread" domain="[('message_unread', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Late Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Report" name="report" domain="[]" context="{'group_by': 'sheet_id'}"/>
|
||||
<filter string="Employee" name="employee" domain="[]" context="{'group_by': 'employee_id'}"/>
|
||||
@@ -487,6 +496,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -576,6 +586,15 @@
|
||||
<filter string="My Reports" name="my_reports" domain="[('employee_id.user_id', '=', uid)]"/>
|
||||
<separator />
|
||||
<filter string="New Mail" name="message_unread" domain="[('message_unread', '=', True)]"/>
|
||||
<separator/>
|
||||
<filter string="Late Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Employee" name="employee" domain="[]" context="{'group_by': 'employee_id'}"/>
|
||||
<filter string="Department" name="department" domain="[]" context="{'group_by': 'department_id'}"/>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<!-- Activity types config -->
|
||||
<record id="mail_activity_type_action_config_hr_expense" model="ir.actions.act_window">
|
||||
<field name="name">Activity Types</field>
|
||||
<field name="res_model">mail.activity.type</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'hr.expense.sheet')]</field>
|
||||
<field name="context">{'default_res_model': 'hr.expense.sheet'}</field>
|
||||
</record>
|
||||
<menuitem id="hr_expense_menu_config_activity_type"
|
||||
action="mail_activity_type_action_config_hr_expense"
|
||||
parent="menu_hr_expense_configuration"
|
||||
groups="base.group_no_one"/>
|
||||
</odoo>
|
||||
@@ -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',
|
||||
|
||||
@@ -1,26 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Leave specific activities -->
|
||||
<record id="mail_act_leave_approval" model="mail.activity.type">
|
||||
<field name="name">Leaves Approval</field>
|
||||
<field name="icon">fa-sun-o</field>
|
||||
<field name="res_model_id" ref="hr_holidays.model_hr_leave"/>
|
||||
</record>
|
||||
<record id="mail_act_leave_second_approval" model="mail.activity.type">
|
||||
<field name="name">Leaves Second Approve</field>
|
||||
<field name="icon">fa-sun-o</field>
|
||||
<field name="res_model_id" ref="hr_holidays.model_hr_leave"/>
|
||||
</record>
|
||||
|
||||
<!-- Leave specific activities -->
|
||||
<record id="mail_act_leave_allocation_approval" model="mail.activity.type">
|
||||
<field name="name">Allocation Approval</field>
|
||||
<field name="icon">fa-sun-o</field>
|
||||
<field name="res_model_id" ref="hr_holidays.model_hr_leave_allocation"/>
|
||||
</record>
|
||||
<record id="mail_act_leave_allocation_second_approval" model="mail.activity.type">
|
||||
<field name="name">Allocation Second Approve</field>
|
||||
<field name="icon">fa-sun-o</field>
|
||||
<field name="res_model_id" ref="hr_holidays.model_hr_leave_allocation"/>
|
||||
</record>
|
||||
|
||||
<!-- Holidays-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_leave_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Confirmed</field>
|
||||
<field name="res_model">hr.leave</field>
|
||||
<field name="description">Request created and waiting confirmation</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_leave_first_validated" model="mail.message.subtype">
|
||||
<field name="name">Waiting Second Validation</field>
|
||||
<field name="res_model">hr.leave</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="description">Request validated, waiting second validation</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_leave_approved" model="mail.message.subtype">
|
||||
<field name="name">Approved</field>
|
||||
<field name="res_model">hr.leave</field>
|
||||
<field name="description">Request approved</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_leave_refused" model="mail.message.subtype">
|
||||
<field name="name">Refused</field>
|
||||
<field name="res_model">hr.leave</field>
|
||||
@@ -29,25 +39,11 @@
|
||||
</record>
|
||||
|
||||
<!-- Allocation-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_leave_allocation_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Confirmed</field>
|
||||
<field name="res_model">hr.leave.allocation</field>
|
||||
<field name="description">Request created and waiting confirmation</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_leave_allocation_first_validated" model="mail.message.subtype">
|
||||
<field name="name">Waiting Second Validation</field>
|
||||
<field name="res_model">hr.leave.allocation</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="description">Request validated, waiting second validation</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_leave_allocation_approved" model="mail.message.subtype">
|
||||
<field name="name">Approved</field>
|
||||
<field name="res_model">hr.leave.allocation</field>
|
||||
<field name="description">Request approved</field>
|
||||
</record>
|
||||
|
||||
<record id="mt_leave_allocation_refused" model="mail.message.subtype">
|
||||
<field name="name">Refused</field>
|
||||
<field name="res_model">hr.leave.allocation</field>
|
||||
@@ -56,24 +52,6 @@
|
||||
</record>
|
||||
|
||||
<!-- Department related subtypes for messaging / Chatter -->
|
||||
<record id="mt_department_leave_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Leaves Confirmed</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="parent_id" eval="ref('mt_leave_confirmed')"/>
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="6"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_department_leave_first_validated" model="mail.message.subtype">
|
||||
<field name="name">Leaves First Approval</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="parent_id" eval="ref('mt_leave_first_validated')"/>
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="7"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_department_leave_approved" model="mail.message.subtype">
|
||||
<field name="name">Leaves Approved</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
@@ -82,7 +60,6 @@
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="8"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_department_leave_refused" model="mail.message.subtype">
|
||||
<field name="name">Leaves Refused</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
@@ -91,25 +68,6 @@
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="9"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_department_leave_allocation_confirmed" model="mail.message.subtype">
|
||||
<field name="name">Allocations Confirmed</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="parent_id" eval="ref('mt_leave_allocation_confirmed')"/>
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="6"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_department_leave_allocation_first_validated" model="mail.message.subtype">
|
||||
<field name="name">Allocations First Approval</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
<field name="default" eval="False"/>
|
||||
<field name="parent_id" eval="ref('mt_leave_allocation_first_validated')"/>
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="7"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_department_leave_allocation_approved" model="mail.message.subtype">
|
||||
<field name="name">Allocation Approved</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
@@ -118,7 +76,6 @@
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="8"/>
|
||||
</record>
|
||||
|
||||
<record id="mt_department_leave_allocation_refused" model="mail.message.subtype">
|
||||
<field name="name">Allocation Refused</field>
|
||||
<field name="res_model">hr.department</field>
|
||||
@@ -127,6 +84,5 @@
|
||||
<field name="relation_field">department_id</field>
|
||||
<field name="sequence" eval="9"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
@@ -25,6 +25,15 @@
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id"/>
|
||||
<field name="holiday_status_id"/>
|
||||
<separator/>
|
||||
<filter string="Late Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter name="group_name" string="Description" context="{'group_by':'name'}"/>
|
||||
<filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
|
||||
@@ -77,6 +86,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -116,21 +126,6 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_allocation_tree_customize" model="ir.ui.view">
|
||||
<field name="name">hr.leave.allocation.tree.customize</field>
|
||||
<field name="model">hr.leave.allocation</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Allocation Requests" editable="top">
|
||||
<field name="employee_id"/>
|
||||
<field name="holiday_type"/>
|
||||
<field name="holiday_status_id"/>
|
||||
<field name="name" readonly="1"/>
|
||||
<field name="number_of_days_temp" string="Allocated Days" sum="Remaining Days"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="open_allocation" model="ir.actions.act_window">
|
||||
<field name="name">Allocation Request</field>
|
||||
<field name="res_model">hr.leave.allocation</field>
|
||||
@@ -212,4 +207,42 @@
|
||||
<field name="act_window_id" ref="request_approve_allocation"/>
|
||||
</record>
|
||||
|
||||
<record id="request_approve_holidays" model="ir.actions.act_window">
|
||||
<field name="name">Requests to Approve</field>
|
||||
<field name="res_model">hr.leave</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="context">{
|
||||
'search_default_approve':1,
|
||||
}</field>
|
||||
<field name="view_id" ref="edit_new_allocation"/>
|
||||
<field name="search_view_id" ref="view_hr_holidays_filter"/>
|
||||
</record>
|
||||
|
||||
<record id="action_request_approve_holidays_tree" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_holiday"/>
|
||||
<field name="act_window_id" ref="request_approve_holidays"/>
|
||||
</record>
|
||||
|
||||
<record id="action_request_approve_holidays_form" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="edit_new_allocation"/>
|
||||
<field name="act_window_id" ref="request_approve_holidays"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_open_department_leave_allocation_approve" groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
|
||||
name="Allocations to Approve"
|
||||
parent="menu_hr_holidays_approvals"
|
||||
action="open_department_holidays_allocation_approve"
|
||||
sequence="2"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_open_allocation"
|
||||
parent="menu_hr_holidays_my_leaves"
|
||||
action="open_allocation"
|
||||
sequence="30"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -128,4 +128,17 @@
|
||||
<field name="search_view_id" ref="view_hr_holidays_status_search"/>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_holidays_configuration"
|
||||
name="Configuration"
|
||||
parent="menu_hr_holidays_root"
|
||||
sequence="100"/>
|
||||
|
||||
<menuitem
|
||||
id="hr_holidays_status_menu_configuration"
|
||||
action="open_view_holiday_status"
|
||||
name="Leave Types"
|
||||
parent="menu_hr_holidays_configuration"
|
||||
sequence="1"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -43,6 +43,15 @@
|
||||
<field name="employee_id"/>
|
||||
<field name="department_id"/>
|
||||
<field name="holiday_status_id"/>
|
||||
<separator/>
|
||||
<filter string="Late Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Future Activities" name="activities_upcoming_all"
|
||||
domain="[('activity_ids.date_deadline', '>', context_today().strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter name="group_name" string="Description" context="{'group_by':'name'}"/>
|
||||
<filter name="group_employee" string="Employee" context="{'group_by':'employee_id'}"/>
|
||||
@@ -119,6 +128,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -240,31 +250,6 @@
|
||||
action="open_ask_holidays"
|
||||
sequence="22"/>
|
||||
|
||||
<record model="ir.actions.act_window" id="request_approve_holidays">
|
||||
<field name="name">Requests to Approve</field>
|
||||
<field name="res_model">hr.leave</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="context">{
|
||||
'search_default_approve':1,
|
||||
}</field>
|
||||
<field name="view_id" ref="edit_new_allocation"/>
|
||||
<field name="search_view_id" ref="view_hr_holidays_filter"/>
|
||||
</record>
|
||||
|
||||
<record id="action_request_approve_holidays_tree" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
<field name="view_id" ref="view_holiday"/>
|
||||
<field name="act_window_id" ref="request_approve_holidays"/>
|
||||
</record>
|
||||
|
||||
<record id="action_request_approve_holidays_form" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="2"/>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="edit_new_allocation"/>
|
||||
<field name="act_window_id" ref="request_approve_holidays"/>
|
||||
</record>
|
||||
|
||||
<record id="open_employee_leaves" model="ir.actions.act_window">
|
||||
<field name="name">Leaves Overview</field>
|
||||
<field name="res_model">hr.leave</field>
|
||||
@@ -306,13 +291,6 @@
|
||||
action="open_department_holidays_approve"
|
||||
sequence="1"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_open_department_leave_allocation_approve" groups="hr_holidays.group_hr_holidays_manager,hr_holidays.group_hr_holidays_user"
|
||||
name="Allocations to Approve"
|
||||
parent="menu_hr_holidays_approvals"
|
||||
action="open_department_holidays_allocation_approve"
|
||||
sequence="2"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_open_employee_leave"
|
||||
parent="menu_hr_holidays_approvals"
|
||||
@@ -320,15 +298,22 @@
|
||||
action="open_employee_leaves"
|
||||
sequence="4"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_hr_holidays_configuration"
|
||||
name="Configuration"
|
||||
parent="menu_hr_holidays_root"
|
||||
sequence="100"
|
||||
groups="hr_holidays.group_hr_holidays_manager"
|
||||
action="open_view_holiday_status"/>
|
||||
|
||||
<!-- Shortcuts -->
|
||||
<record id="view_allocation_tree_customize" model="ir.ui.view">
|
||||
<field name="name">hr.leave.allocation.tree.customize</field>
|
||||
<field name="model">hr.leave.allocation</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Allocation Requests" editable="top">
|
||||
<field name="employee_id"/>
|
||||
<field name="holiday_type"/>
|
||||
<field name="holiday_status_id"/>
|
||||
<field name="name" readonly="1"/>
|
||||
<field name="number_of_days_temp" string="Allocated Days" sum="Remaining Days"/>
|
||||
<field name="state"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="act_hr_employee_holiday_request_approved" model="ir.actions.act_window">
|
||||
<field name="name">Leaves to be reported in Payslip</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
@@ -432,11 +417,4 @@
|
||||
id="menu_hr_available_holidays_report_tree"
|
||||
action="action_hr_available_holidays_report"
|
||||
parent="menu_hr_holidays_report" sequence="5"/>
|
||||
|
||||
<menuitem
|
||||
id="menu_open_allocation"
|
||||
parent="menu_hr_holidays_my_leaves"
|
||||
action="open_allocation"
|
||||
sequence="30"/>
|
||||
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<!-- Activity types config -->
|
||||
<record id="mail_activity_type_action_config_hr_holidays" model="ir.actions.act_window">
|
||||
<field name="name">Activity Types</field>
|
||||
<field name="res_model">mail.activity.type</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|', ('res_model_id', '=', False), ('res_model_id.model', 'in', ['hr.leave', 'hr.leave.allocation'])]</field>
|
||||
<field name="context">{'default_res_model': 'hr.leave'}</field>
|
||||
</record>
|
||||
<menuitem id="hr_holidays_menu_config_activity_type"
|
||||
action="mail_activity_type_action_config_hr_holidays"
|
||||
parent="menu_hr_holidays_configuration"
|
||||
groups="base.group_no_one"/>
|
||||
</odoo>
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data>
|
||||
<!-- Maintenance-specific activities, for automatic generation mainly -->
|
||||
<record id="mail_act_maintenance_request" model="mail.activity.type">
|
||||
<field name="name">Maintenance Request</field>
|
||||
<field name="icon">fa-wrench</field>
|
||||
<field name="res_model_id" ref="maintenance.model_maintenance_request"/>
|
||||
</record>
|
||||
|
||||
<!-- email alias for maintenance requests -->
|
||||
<record id="mail_alias_equipment" model="mail.alias">
|
||||
<field name="alias_name">helpdesk</field>
|
||||
|
||||
@@ -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 <a href="#" data-oe-model="%s" data-oe-id="%s">%s</a>') % (
|
||||
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
|
||||
|
||||
@@ -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
|
||||
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
|
||||
|
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<!-- Activity types config -->
|
||||
<record id="mail_activity_type_action_config_maintenance" model="ir.actions.act_window">
|
||||
<field name="name">Activity Types</field>
|
||||
<field name="res_model">mail.activity.type</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'maintenance.request')]</field>
|
||||
<field name="context">{'default_res_model': 'maintenance.request'}</field>
|
||||
</record>
|
||||
<menuitem id="maintenance_menu_config_activity_type"
|
||||
action="mail_activity_type_action_config_maintenance"
|
||||
parent="menu_maintenance_configuration"
|
||||
sequence="20"
|
||||
groups="base.group_no_one"/>
|
||||
</odoo>
|
||||
@@ -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',
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<!-- Activities -->
|
||||
<record id="mail_act_sale_upsell" model="mail.activity.type">
|
||||
<field name="name">Order Upsell</field>
|
||||
<field name="icon">fa-line-chart</field>
|
||||
<field name="res_model_id" ref="sale.model_sale_order"/>
|
||||
</record>
|
||||
|
||||
<!-- Sale-related subtypes for messaging / Chatter -->
|
||||
<record id="mt_order_sent" model="mail.message.subtype">
|
||||
<field name="name">Quotation sent</field>
|
||||
|
||||
@@ -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 <a href='#' data-oe-model='%s' data-oe-id='%d'>%s</a> for customer <a href='#' data-oe-model='%s' data-oe-id='%s'>%s</a>") % (
|
||||
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:
|
||||
|
||||
@@ -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
|
||||
|
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<!-- Activity types config -->
|
||||
<record id="mail_activity_type_action_config_sale" model="ir.actions.act_window">
|
||||
<field name="name">Activity Types</field>
|
||||
<field name="res_model">mail.activity.type</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="domain">['|', ('res_model_id', '=', False), ('res_model_id.model', '=', 'sale.order')]</field>
|
||||
<field name="context">{'default_res_model': 'sale.order'}</field>
|
||||
</record>
|
||||
<menuitem id="sale_menu_config_activity_type"
|
||||
action="mail_activity_type_action_config_sale"
|
||||
parent="menu_sale_config"
|
||||
groups="base.group_no_one"/>
|
||||
</odoo>
|
||||
@@ -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',
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
|
||||
<record id="mail_act_test_todo" model="mail.activity.type">
|
||||
<field name="name">Do Stuff</field>
|
||||
<field name="summary">Really?! Wow! A superpowers drug you can just rub onto your skin?</field>
|
||||
<field name="category">default</field>
|
||||
<field name="res_model_id" ref="test_mail.model_mail_test_activity"/>
|
||||
</record>
|
||||
<record id="mail_act_test_meeting" model="mail.activity.type">
|
||||
<field name="name">Meet People</field>
|
||||
<field name="summary">You'd think it would be something you'd have to freebase. Noooooo!</field>
|
||||
<field name="category">default</field>
|
||||
<field name="res_model_id" ref="test_mail.model_mail_test_activity"/>
|
||||
</record>
|
||||
<record id="mail_act_test_call" model="mail.activity.type">
|
||||
<field name="name">Call People</field>
|
||||
<field name="summary">Then throw her in the laundry room, which will hereafter be referred to as "the brig".</field>
|
||||
<field name="category">default</field>
|
||||
<field name="res_model_id" ref="test_mail.model_mail_test_activity"/>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -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
|
||||
|
||||
@@ -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, '<p>Holidays</p>')
|
||||
#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': '<p>Holidays</p>'})
|
||||
self.assertEqual(demo_reminder.note, '<p>Holidays</p>')
|
||||
|
||||
# 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)
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user