[MERGE][ADD] mail, various: add activities
This commit introduces generic activities to use in your addons. Activities are actions user have to take on a document like making a phonecall or organizing a meeting. Activities come with the mail module as they are integrated in the Chatter but are not bundled with mail.thread. New models are * mail.activity.type: used to categorize activities. Each type is a different kind of activity e.g. call, mail, meeting. An activity can be generic i.e. available for all models using activities; or specific to a model in which case res_model_id field should be used. * mail.activity: an actual activity to perform. Activities are linked to documents using res_id and res_model_id fields. Activities have a deadline that can be used in kanban view to display a status. Once done activities are unlinked and a message is posted. This message has a new activity_type_id field that indicates the activity linked to the message. This commit introduces a mail.activity mixin to use in various addons that enables the activities feature. It works like the mail.thread mixin. It defines a activity_ids one2many field toward activities using res_id and res_model_id. Various related / computed fields are also added to have a global status of activities on documents. Activities come with a new JS widget for the form view. It is integrated in the Chatter widget although it is a separate widget. It displays activities linked to the current record and allow to schedule, edit and mark done activities. Use widget="mail_activity" on activity_ids field in form view to use it. 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. Next commits will aim at integrating activities inside main Odoo addons. Thanks to R&D India for their work and testing on this task. Thanks to belgian Usabiliteam for reviewing and testing it. Thanks to @jem-odoo for the final review. May his soul lie in peace with the trumpets of paradise.
This commit is contained in:
@@ -37,7 +37,7 @@ MAGIC_COLUMNS = ('id', 'create_uid', 'create_date', 'write_uid', 'write_date')
|
||||
|
||||
class AccountInvoice(models.Model):
|
||||
_name = "account.invoice"
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = "Invoice"
|
||||
_order = "date_invoice desc, number desc, id desc"
|
||||
|
||||
|
||||
@@ -335,6 +335,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>
|
||||
@@ -523,6 +524,18 @@
|
||||
<field name="date" string="Period"/>
|
||||
<separator/>
|
||||
<filter domain="[('user_id','=',uid)]" help="My Invoices"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter name="group_by_partner_id" string="Partner" context="{'group_by':'commercial_partner_id'}"/>
|
||||
<filter string="Salesperson" context="{'group_by':'user_id'}"/>
|
||||
|
||||
@@ -41,7 +41,6 @@ Dashboard for CRM will include:
|
||||
'web_tour',
|
||||
],
|
||||
'data': [
|
||||
'data/crm_activity_data.xml',
|
||||
'data/crm_data.xml',
|
||||
'data/crm_stage_data.xml',
|
||||
'data/sales_config_settings_data.xml',
|
||||
@@ -53,7 +52,6 @@ Dashboard for CRM will include:
|
||||
'security/ir.model.access.csv',
|
||||
|
||||
'wizard/base_partner_merge_views.xml',
|
||||
'wizard/crm_activity_log_views.xml',
|
||||
'wizard/crm_lead_lost_views.xml',
|
||||
'wizard/crm_lead_to_opportunity_views.xml',
|
||||
'wizard/crm_merge_opportunities_views.xml',
|
||||
@@ -64,7 +62,6 @@ Dashboard for CRM will include:
|
||||
|
||||
'views/crm_templates.xml',
|
||||
'views/crm_views.xml',
|
||||
'views/crm_activity_views.xml',
|
||||
'views/crm_stage_views.xml',
|
||||
'views/crm_lead_views.xml',
|
||||
'views/calendar_views.xml',
|
||||
@@ -74,8 +71,8 @@ Dashboard for CRM will include:
|
||||
],
|
||||
'demo': [
|
||||
'data/crm_demo.xml',
|
||||
'data/mail_activity_demo.xml',
|
||||
'data/crm_lead_demo.xml',
|
||||
'data/crm_activity_demo.xml',
|
||||
'data/base_action_rule_demo.xml',
|
||||
],
|
||||
'css': ['static/src/css/crm.css'],
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="0">
|
||||
|
||||
<record id="crm_activity_data_email" model="crm.activity">
|
||||
<field name="name">Email</field>
|
||||
</record>
|
||||
<record id="crm_activity_data_call" model="crm.activity">
|
||||
<field name="name">Call</field>
|
||||
</record>
|
||||
<record id="crm_activity_data_meeting" model="crm.activity">
|
||||
<field name="name">Task</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -1,37 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
|
||||
<record id="crm_activity_demo_followup_quote" model="crm.activity">
|
||||
<field name="name">Follow-up Quote</field>
|
||||
<field name="description">Follow-up quote</field>
|
||||
<field name="days">30</field>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_demo_make_quote" model="crm.activity">
|
||||
<field name="name">Make Quote</field>
|
||||
<field name="description">Send a quotation</field>
|
||||
<field name="days">15</field>
|
||||
<field name="recommended_activity_ids" eval="[(6,0, [ref('crm.crm_activity_demo_followup_quote')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_demo_call_demo" model="crm.activity">
|
||||
<field name="name">Call for Demo</field>
|
||||
<field name="days">10</field>
|
||||
<field name="description">Call for a demo</field>
|
||||
<field name="recommended_activity_ids" eval="[(6, 0, [ref('crm.crm_activity_demo_make_quote')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_data_call" model="crm.activity">
|
||||
<field name="days">5</field>
|
||||
<field name="recommended_activity_ids" eval="[(6, 0, [ref('crm.crm_activity_data_email')])]"/>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_data_meeting" model="crm.activity">
|
||||
<field name="recommended_activity_ids" eval="[(6, 0, [ref('crm_activity_data_call'), ref('crm_activity_data_email')])]"/>
|
||||
<field name="days">15</field>
|
||||
</record>
|
||||
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -374,9 +374,6 @@ Andrew</p>]]></field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor1])]"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_meeting"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=3)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Meeting to go over pricing information.</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||
@@ -402,9 +399,6 @@ Andrew</p>]]></field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor2])]"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_email"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=3)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Send Catalog by Email</field>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead1"/>
|
||||
@@ -427,9 +421,6 @@ Andrew</p>]]></field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor1])]"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_call"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Call to get system requirements</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead2"/>
|
||||
@@ -516,9 +507,6 @@ Andrew</p>]]></field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor5])]"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_meeting"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=2)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Convert to quote</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead2"/>
|
||||
@@ -542,9 +530,6 @@ Andrew</p>]]></field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor3,categ_oppor4])]"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=3)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_email"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=-1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Send our service pricelist</field>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead2"/>
|
||||
@@ -577,9 +562,6 @@ Andrew</p>]]></field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor4,categ_oppor6])]"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=3)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_call"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Call to get training needs</field>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead2"/>
|
||||
@@ -628,9 +610,6 @@ Andrew</p>]]></field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor1])]"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_call"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=-2)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Followup on the proposal</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -653,7 +632,6 @@ Andrew</p>]]></field>
|
||||
<field name="phone">+1 312 349 2324</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor4,categ_oppor8])]"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="next_activity_id" ref="crm_activity_data_call"/>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -672,7 +650,6 @@ Andrew</p>]]></field>
|
||||
<field name="street">Cannon Hill Park</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor7])]"/>
|
||||
<field name="priority">2</field>
|
||||
<field name="next_activity_id" ref="crm_activity_data_meeting"/>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -691,7 +668,6 @@ Andrew</p>]]></field>
|
||||
<field name="partner_id" ref="base.res_partner_12"/>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor3])]"/>
|
||||
<field name="priority">1</field>
|
||||
<field name="next_activity_id" ref="crm_activity_data_email"/>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -708,8 +684,6 @@ Andrew</p>]]></field>
|
||||
<field name="partner_id" ref="base.res_partner_18"/>
|
||||
<field name="priority">0</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor3])]"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_email"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -728,8 +702,6 @@ Andrew</p>]]></field>
|
||||
<field name="city">Lima</field>
|
||||
<field name="priority">0</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor7])]"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_email"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=2)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -753,9 +725,6 @@ Andrew</p>]]></field>
|
||||
<field name="street2">C1414CMS Capital Federal</field>
|
||||
<field name="priority">2</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor4])]"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_call"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=0)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Conf call with technical service</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -778,9 +747,6 @@ Andrew</p>]]></field>
|
||||
<field name="priority">2</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor2])]"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=6)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_email"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=0)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Send Catalog by Email</field>
|
||||
<field name="team_id" ref="sales_team.crm_team_1"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -804,9 +770,6 @@ Andrew</p>]]></field>
|
||||
<field name="priority">2</field>
|
||||
<field name="tag_ids" eval="[(6, 0, [categ_oppor2])]"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=6)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="next_activity_id" ref="crm_activity_data_email"/>
|
||||
<field name="date_action" eval="(DateTime.today() + relativedelta(days=0)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
<field name="title_action">Send Catalog by Email</field>
|
||||
<field name="team_id" ref="sales_team.team_sales_department"/>
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="stage_id" ref="crm.stage_lead3"/>
|
||||
@@ -817,5 +780,100 @@ Andrew</p>]]></field>
|
||||
eval="[ref('crm_case_20'), ref('crm_case_23'), ref('crm_case_27')],
|
||||
{'install_mode': True}"/>
|
||||
|
||||
<!-- Activities -->
|
||||
<record id="activity_1" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_13" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call" />
|
||||
<field name="summary">Meeting to go over pricing information.</field>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=3)).strftime('%Y-%m-%d %H:%M')"/>
|
||||
</record>
|
||||
<record id="activity_2" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_14" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_email"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=3)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Send Catalog by Email</field>
|
||||
</record>
|
||||
<record id="activity_3" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_15" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Call to get system requirements</field>
|
||||
</record>
|
||||
<record id="activity_4" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_16" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=2)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Convert to quote</field>
|
||||
</record>
|
||||
<record id="activity_5" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_17" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_email"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=-1)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Send our service pricelist</field>
|
||||
</record>
|
||||
<record id="activity_6" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_18" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Call to get training needs</field>
|
||||
</record>
|
||||
<record id="activity_7" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_19" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=-2)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Followup on the proposal</field>
|
||||
</record>
|
||||
<record id="activity_8" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_20" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')" />
|
||||
</record>
|
||||
<record id="activity_9" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_21" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=-1)).strftime('%Y-%m-%d %H:%M')" />
|
||||
</record>
|
||||
<record id="activity_10" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_22" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_email"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=-2)).strftime('%Y-%m-%d %H:%M')" />
|
||||
</record>
|
||||
<record id="activity_11" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_23" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_email"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=1)).strftime('%Y-%m-%d %H:%M')" />
|
||||
</record>
|
||||
<record id="activity_12" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_24" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_email"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=2)).strftime('%Y-%m-%d %H:%M')" />
|
||||
</record>
|
||||
<record id="activity_13" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_25" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=0)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Conf call with technical service</field>
|
||||
</record>
|
||||
<record id="activity_14" model="mail.activity">
|
||||
<field name="res_id" ref="crm.crm_case_26" />
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_email"/>
|
||||
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=0)).strftime('%Y-%m-%d %H:%M')" />
|
||||
<field name="summary">Send Catalog by Email</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<record id="mail_activity_demo_followup_quote" model="mail.activity.type">
|
||||
<field name="name">Follow-up Quote</field>
|
||||
<field name="icon">fa-file-text-o</field>
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="summary">Follow-up quote</field>
|
||||
<field name="days">30</field>
|
||||
</record>
|
||||
<record id="mail_activity_demo_make_quote" model="mail.activity.type">
|
||||
<field name="name">Make Quote</field>
|
||||
<field name="icon">fa-file-text-o</field>
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="summary">Send a quotation</field>
|
||||
<field name="days">15</field>
|
||||
</record>
|
||||
<record id="mail_activity_demo_call_demo" model="mail.activity.type">
|
||||
<field name="name">Call for Demo</field>
|
||||
<field name="icon">fa-phone</field>
|
||||
<field name="res_model_id" ref="crm.model_crm_lead"/>
|
||||
<field name="days">10</field>
|
||||
<field name="summary">Call for a demo</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -3,7 +3,6 @@
|
||||
|
||||
import res_users
|
||||
import calendar
|
||||
import crm_activity
|
||||
import crm_lead
|
||||
import crm_stage
|
||||
import crm_team
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class CrmActivity(models.Model):
|
||||
''' CrmActivity is a model introduced in Odoo v9 that models activities
|
||||
performed in CRM, like phone calls, sending emails, making demonstrations,
|
||||
... Users are able to configure their custom activities.
|
||||
|
||||
Each activity can configure recommended next activities. This allows to model
|
||||
light custom workflows. This way sales manager can configure their crm
|
||||
workflow that salepersons will use in their daily job.
|
||||
|
||||
CrmActivity inherits from mail.message.subtype. This allows users to follow
|
||||
some activities through subtypes. Each activity will generate messages with
|
||||
the matching subtypes, allowing reporting and statistics computation based
|
||||
on mail.message.subtype model. '''
|
||||
|
||||
_name = 'crm.activity'
|
||||
_description = 'CRM Activity'
|
||||
_inherits = {'mail.message.subtype': 'subtype_id'}
|
||||
_rec_name = 'name'
|
||||
_order = "sequence"
|
||||
|
||||
days = fields.Integer('Number of days', default=0,
|
||||
help='Number of days before executing the action, allowing you to plan the date of the action.')
|
||||
sequence = fields.Integer('Sequence', default=0)
|
||||
team_id = fields.Many2one('crm.team', string='Sales Team')
|
||||
subtype_id = fields.Many2one('mail.message.subtype', string='Message Subtype', required=True, ondelete='cascade')
|
||||
recommended_activity_ids = fields.Many2many(
|
||||
'crm.activity', 'crm_activity_rel', 'activity_id', 'recommended_id',
|
||||
string='Recommended Next Activities')
|
||||
preceding_activity_ids = fields.Many2many(
|
||||
'crm.activity', 'crm_activity_rel', 'recommended_id', 'activity_id',
|
||||
string='Preceding Activities')
|
||||
|
||||
# setting a default value on inherited fields is a bit involved
|
||||
res_model = fields.Char('Model', related='subtype_id.res_model', inherited=True, default='crm.lead')
|
||||
internal = fields.Boolean('Internal Only', related='subtype_id.internal', inherited=True, default=True)
|
||||
default = fields.Boolean('Default', related='subtype_id.default', inherited=True, default=False)
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
activities = self.search([('subtype_id', '=', self.subtype_id.id)])
|
||||
# to ensure that the subtype is only linked the current activity
|
||||
if len(activities) == 1:
|
||||
self.subtype_id.unlink()
|
||||
return super(CrmActivity, self).unlink()
|
||||
@@ -52,11 +52,10 @@ CRM_LEAD_FIELDS_TO_MERGE = [
|
||||
|
||||
|
||||
class Lead(FormatAddress, models.Model):
|
||||
|
||||
_name = "crm.lead"
|
||||
_description = "Lead/Opportunity"
|
||||
_order = "priority desc,date_action,id desc"
|
||||
_inherit = ['mail.thread', 'utm.mixin']
|
||||
_order = "priority desc,activity_date_deadline,id desc"
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'utm.mixin']
|
||||
_mail_mass_mailing = _('Leads / Opportunities')
|
||||
|
||||
def _default_probability(self):
|
||||
@@ -116,12 +115,6 @@ class Lead(FormatAddress, models.Model):
|
||||
probability = fields.Float('Probability', group_operator="avg", default=lambda self: self._default_probability())
|
||||
planned_revenue = fields.Float('Expected Revenue', track_visibility='always')
|
||||
date_deadline = fields.Date('Expected Closing', help="Estimate of the date on which the opportunity will be won.")
|
||||
|
||||
# CRM Actions
|
||||
next_activity_id = fields.Many2one("crm.activity", string="Next Activity", index=True)
|
||||
date_action = fields.Date('Next Activity Date', index=True)
|
||||
title_action = fields.Char('Next Activity Summary')
|
||||
|
||||
color = fields.Integer('Color Index', default=0)
|
||||
partner_address_name = fields.Char('Partner Contact Name', related='partner_id.name', readonly=True)
|
||||
partner_address_email = fields.Char('Partner Contact Email', related='partner_id.email', readonly=True)
|
||||
@@ -170,8 +163,8 @@ class Lead(FormatAddress, models.Model):
|
||||
today = date.today()
|
||||
for lead in self:
|
||||
kanban_state = 'grey'
|
||||
if lead.date_action:
|
||||
lead_date = fields.Date.from_string(lead.date_action)
|
||||
if lead.activity_date_deadline:
|
||||
lead_date = fields.Date.from_string(lead.activity_date_deadline)
|
||||
if lead_date >= today:
|
||||
kanban_state = 'green'
|
||||
else:
|
||||
@@ -270,18 +263,6 @@ class Lead(FormatAddress, models.Model):
|
||||
if self.state_id:
|
||||
self.country_id = self.state_id.country_id.id
|
||||
|
||||
@api.onchange('next_activity_id')
|
||||
def _onchange_next_activity_id(self):
|
||||
values = {
|
||||
'title_action': False,
|
||||
'date_action': False,
|
||||
}
|
||||
if self.next_activity_id:
|
||||
values['title_action'] = self.next_activity_id.description
|
||||
if self.next_activity_id.days:
|
||||
values['date_action'] = fields.Datetime.to_string(datetime.now() + timedelta(days=self.next_activity_id.days))
|
||||
self.update(values)
|
||||
|
||||
# ----------------------------------------
|
||||
# ORM override (CRUD, fields_view_get, ...)
|
||||
# ----------------------------------------
|
||||
@@ -934,13 +915,13 @@ class Lead(FormatAddress, models.Model):
|
||||
if date_deadline < date.today():
|
||||
result['closing']['overdue'] += 1
|
||||
# Next activities
|
||||
if opp.next_activity_id and opp.date_action:
|
||||
date_action = fields.Date.from_string(opp.date_action)
|
||||
if date_action == date.today():
|
||||
for activity in opp.activity_ids:
|
||||
date_deadline = fields.Date.from_string(activity.date_deadline)
|
||||
if date_deadline == date.today():
|
||||
result['activity']['today'] += 1
|
||||
if date.today() <= date_action <= date.today() + timedelta(days=7):
|
||||
if date.today() <= date_deadline <= date.today() + timedelta(days=7):
|
||||
result['activity']['next_7_days'] += 1
|
||||
if date_action < date.today():
|
||||
if date_deadline < date.today():
|
||||
result['activity']['overdue'] += 1
|
||||
# Won in Opportunities
|
||||
if opp.date_closed:
|
||||
@@ -957,19 +938,19 @@ class Lead(FormatAddress, models.Model):
|
||||
# crm.activity is a very messy model so we need to do that in order to retrieve the actions done.
|
||||
self._cr.execute("""
|
||||
SELECT
|
||||
m.id,
|
||||
m.subtype_id,
|
||||
m.date,
|
||||
l.user_id,
|
||||
l.type
|
||||
FROM mail_message M
|
||||
LEFT JOIN crm_lead L ON (M.res_id = L.id)
|
||||
INNER JOIN crm_activity A ON (M.subtype_id = A.subtype_id)
|
||||
mail_message.id,
|
||||
mail_message.subtype_id,
|
||||
mail_message.mail_activity_type_id,
|
||||
mail_message.date,
|
||||
crm_lead.user_id,
|
||||
crm_lead.type
|
||||
FROM mail_message
|
||||
LEFT JOIN crm_lead ON (mail_message.res_id = crm_lead.id)
|
||||
INNER JOIN mail_activity_type activity_type ON (mail_message.mail_activity_type_id = activity_type.id)
|
||||
WHERE
|
||||
(M.model = 'crm.lead') AND (L.user_id = %s) AND (L.type = 'opportunity')
|
||||
(mail_message.model = 'crm.lead') AND (crm_lead.user_id = %s) AND (crm_lead.type = 'opportunity')
|
||||
""", (self._uid,))
|
||||
activites_done = self._cr.dictfetchall()
|
||||
|
||||
for activity in activites_done:
|
||||
if activity['date']:
|
||||
date_act = fields.Date.from_string(activity['date'])
|
||||
|
||||
@@ -18,7 +18,8 @@ class ActivityReport(models.Model):
|
||||
team_id = fields.Many2one('crm.team', 'Sales Team', readonly=True)
|
||||
lead_id = fields.Many2one('crm.lead', "Lead", readonly=True)
|
||||
subject = fields.Char('Summary', readonly=True)
|
||||
subtype_id = fields.Many2one('mail.message.subtype', 'Activity', readonly=True)
|
||||
subtype_id = fields.Many2one('mail.message.subtype', 'Subtype', readonly=True)
|
||||
mail_activity_type_id = fields.Many2one('mail.activity.type', 'Activity Type', readonly=True)
|
||||
country_id = fields.Many2one('res.country', 'Country', readonly=True)
|
||||
company_id = fields.Many2one('res.company', 'Company', readonly=True)
|
||||
stage_id = fields.Many2one('crm.stage', 'Stage', readonly=True)
|
||||
@@ -37,6 +38,7 @@ class ActivityReport(models.Model):
|
||||
select
|
||||
m.id,
|
||||
m.subtype_id,
|
||||
m.mail_activity_type_id,
|
||||
m.author_id,
|
||||
m.date,
|
||||
m.subject,
|
||||
@@ -57,5 +59,5 @@ class ActivityReport(models.Model):
|
||||
on
|
||||
(m.res_id = l.id)
|
||||
WHERE
|
||||
(m.model = 'crm.lead')
|
||||
(m.model = 'crm.lead' AND m.mail_activity_type_id IS NOT NULL)
|
||||
)""")
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<field name="model">crm.activity.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<graph string="Activities Analysis" stacked="True">
|
||||
<field name="subtype_id" type="col"/>
|
||||
<field name="mail_activity_type_id" type="col"/>
|
||||
<field name="date" interval="month" type="row"/>
|
||||
</graph>
|
||||
</field>
|
||||
@@ -17,7 +17,7 @@
|
||||
<field name="model">crm.activity.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<pivot string="Activities Analysis" disable_linking="True">
|
||||
<field name="subtype_id" type="col"/>
|
||||
<field name="mail_activity_type_id" type="col"/>
|
||||
<field name="date" interval="month" type="row"/>
|
||||
</pivot>
|
||||
</field>
|
||||
@@ -28,7 +28,7 @@
|
||||
<field name="model">crm.activity.report</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Activities Analysis">
|
||||
<filter string="My Activities" name="my_activity" domain="[('user_id', '=', uid)]"/>
|
||||
<filter string="My Activities" name="activities_my" domain="[('user_id', '=', uid)]"/>
|
||||
<separator/>
|
||||
<filter name="lead" string="Leads" domain="[('lead_type','=', 'lead')]" help="Show only lead"/>
|
||||
<filter name="opportunity" string="Opportunities" domain="[('lead_type','=','opportunity')]" help="Show only opportunity"/>
|
||||
@@ -58,7 +58,7 @@
|
||||
<field name="res_model">crm.activity.report</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">pivot,graph</field>
|
||||
<field name="context">{'search_default_my_activity': True}</field>
|
||||
<field name="context">{'search_default_activities_my': True}</field>
|
||||
<field name="domain">[]</field>
|
||||
</record>
|
||||
|
||||
|
||||
@@ -18,9 +18,6 @@ access_crm_lead_tag_manager,crm_lead_tag manager,model_crm_lead_tag,sales_team.g
|
||||
access_crm_lost_reason_manager,crm.lost.reason.manager,model_crm_lost_reason,sales_team.group_sale_manager,1,1,1,1
|
||||
access_crm_lost_reason_salesman,crm.lost.reason.salesman,model_crm_lost_reason,sales_team.group_sale_salesman,1,0,0,0
|
||||
access_crm_lost_reason_user,crm.lost.reason.user,model_crm_lost_reason,base.group_user,1,0,0,0
|
||||
access_crm_activity_user,crm.activity.user,model_crm_activity,base.group_user,1,1,1,0
|
||||
access_crm_activity_manager,crm.activity.manager,model_crm_activity,sales_team.group_sale_manager,1,1,1,1
|
||||
access_crm_activity_salesman,crm.activity.salesman,model_crm_activity,sales_team.group_sale_salesman,1,1,1,0
|
||||
access_crm_activity_report_user,crm.activity.report.user,model_crm_activity_report,base.group_user,1,0,0,0
|
||||
access_calendar_event_manager,calendar.event.manager,calendar.model_calendar_event,sales_team.group_sale_manager,1,1,1,1
|
||||
access_calendar_event,calendar.event,calendar.model_calendar_event,sales_team.group_sale_salesman,1,1,1,0
|
||||
|
||||
|
@@ -35,18 +35,22 @@ tour.register('crm_tour', {
|
||||
position: "right",
|
||||
run: "drag_and_drop .o_opportunity_kanban .o_kanban_group:eq(2) ",
|
||||
}, {
|
||||
trigger: ".o_kanban_record:not(.o_updating) .oe_kanban_status_grey",
|
||||
trigger: ".o_kanban_record:not(.o_updating) .o_activity_color_default",
|
||||
extra_trigger: ".o_opportunity_kanban",
|
||||
content: _t("This opportunity has <b>no next activity scheduled</b>. <i>Click to set one.</i>"),
|
||||
content: _t("This opportunity has <b>no activity planned</b>. <i>Click to check them.</i>"),
|
||||
position: "bottom"
|
||||
}, {
|
||||
trigger: ".o_recommended_activity .o_radio_item",
|
||||
trigger: ".o_schedule_activity",
|
||||
extra_trigger: ".o_opportunity_kanban",
|
||||
content: _t("Schedule an activity by clicking here"),
|
||||
position: "bottom"
|
||||
}, {
|
||||
trigger: ".modal-body .o_form_field_many2one",
|
||||
extra_trigger: ".o_opportunity_kanban",
|
||||
content: _t("<p>You will be able to customize your followup activities. Examples:</p><ol><li>introductory email</li><li>call 10 days after</li><li>second call 3 days after, ...</li></ol><p class='mb0'><i>Select a standard activity for now on.</i></p>"),
|
||||
position: "left",
|
||||
position: "bottom",
|
||||
run: function (actions) {
|
||||
actions.auto(this.$anchor.children("input").first());
|
||||
actions.auto(".modal-footer .btn-primary");
|
||||
actions.auto(".modal-footer .btn-default");
|
||||
},
|
||||
}, {
|
||||
trigger: ".o_kanban_record",
|
||||
|
||||
@@ -3,52 +3,47 @@
|
||||
|
||||
from .common import TestCrmCases
|
||||
from odoo import fields
|
||||
from datetime import date
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
|
||||
class TestCrmActivity(TestCrmCases):
|
||||
class TestCrmMailActivity(TestCrmCases):
|
||||
|
||||
def setUp(self):
|
||||
super(TestCrmActivity, self).setUp()
|
||||
super(TestCrmMailActivity, self).setUp()
|
||||
# Set up activities
|
||||
Activity = self.env['crm.activity']
|
||||
self.activity3 = Activity.create({
|
||||
lead_model_id = self.env['ir.model'].search([('model', '=', 'crm.lead')]).id
|
||||
ActivityType = self.env['mail.activity.type']
|
||||
self.activity3 = ActivityType.create({
|
||||
'name': 'Celebrate the sale',
|
||||
'days': 3,
|
||||
'description': 'ACT 3 : Beers for everyone because I am a good salesman !',
|
||||
'internal': True,
|
||||
'res_model': 'crm.lead',
|
||||
'summary': 'ACT 3 : Beers for everyone because I am a good salesman !',
|
||||
'res_model_id': lead_model_id,
|
||||
})
|
||||
self.activity2 = Activity.create({
|
||||
self.activity2 = ActivityType.create({
|
||||
'name': 'Call for Demo',
|
||||
'days': 6,
|
||||
'description': 'ACT 2 : I want to show you my ERP !',
|
||||
'internal': True,
|
||||
'res_model': 'crm.lead',
|
||||
'recommended_activity_ids': [(6, 0, [self.activity3.id])],
|
||||
'summary': 'ACT 2 : I want to show you my ERP !',
|
||||
'res_model_id': lead_model_id,
|
||||
})
|
||||
self.activity1 = Activity.create({
|
||||
self.activity1 = ActivityType.create({
|
||||
'name': 'Initial Contact',
|
||||
'days': 5,
|
||||
'description': 'ACT 1 : Presentation, barbecue, ... ',
|
||||
'internal': True,
|
||||
'res_model': 'crm.lead',
|
||||
'recommended_activity_ids': [(6, 0, [self.activity2.id])],
|
||||
'summary': 'ACT 1 : Presentation, barbecue, ... ',
|
||||
'res_model_id': lead_model_id,
|
||||
})
|
||||
|
||||
# I create an opportunity, as salesman
|
||||
self.partner_client = self.env.ref("base.res_partner_1")
|
||||
Lead = self.env['crm.lead'].sudo(self.crm_salesman.id)
|
||||
self.lead = Lead.create({
|
||||
self.lead = self.env['crm.lead'].sudo(self.crm_salesman.id).create({
|
||||
'name': 'Test Opp',
|
||||
'type': 'opportunity',
|
||||
'name': 'Test Opportunity Activity Log',
|
||||
'partner_id': self.partner_client.id,
|
||||
'team_id': self.env.ref("sales_team.team_sales_department").id,
|
||||
'user_id': self.crm_salesman.id,
|
||||
})
|
||||
|
||||
def test_crm_activity_recipients(self):
|
||||
""" This test case check :
|
||||
""" This test case checks
|
||||
- no internal subtype followed by client
|
||||
- activity subtype are not default ones
|
||||
- only activity followers are recipients when this kind of activity is logged
|
||||
@@ -60,73 +55,60 @@ class TestCrmActivity(TestCrmCases):
|
||||
self.lead.message_subscribe([self.partner_client.id])
|
||||
|
||||
# Check the client is not follower of any internal subtype
|
||||
is_internal_subtype_for_client = self.lead.message_follower_ids.filtered(lambda fol: fol.partner_id.id == self.partner_client.id).mapped('subtype_ids.internal')
|
||||
self.assertFalse(any(is_internal_subtype_for_client), 'Partner client is following an internal subtype')
|
||||
internal_subtypes = self.lead.message_follower_ids.filtered(lambda fol: fol.partner_id == self.partner_client).mapped('subtype_ids').filtered(lambda subtype: subtype.internal)
|
||||
self.assertFalse(internal_subtypes)
|
||||
|
||||
# Add sale manager as follower of default subtypes
|
||||
self.lead.message_subscribe([self.crm_salemanager.partner_id.id])
|
||||
# Make the sale manager follower of the activity subtype
|
||||
manager_follower = self.env['mail.followers'].sudo().search([('res_model', '=', 'crm.lead'), ('res_id', '=', self.lead.id), ('partner_id', '=', self.crm_salemanager.partner_id.id)])
|
||||
manager_follower.write({
|
||||
'subtype_ids': [(4, activity.subtype_id.id)]
|
||||
})
|
||||
|
||||
# Instanciate wizard, trigger onchange lead and set the next activity
|
||||
ActivityLogWizard = self.env['crm.activity.log'].sudo(self.crm_salesman.id)
|
||||
wizard = ActivityLogWizard.create({
|
||||
activity = self.env['mail.activity'].sudo(self.crm_salesman.id).create({
|
||||
'activity_type_id': self.activity1.id,
|
||||
'note': 'Content of the activity to log',
|
||||
'lead_id': self.lead.id,
|
||||
'res_id': self.lead.id,
|
||||
'res_model_id': self.env.ref('crm.model_crm_lead').id,
|
||||
})
|
||||
wizard.onchange_lead_id()
|
||||
wizard.write({
|
||||
'next_activity_id': activity.id,
|
||||
})
|
||||
wizard.action_log()
|
||||
activity._onchange_activity_type_id()
|
||||
self.assertEqual(self.lead.activity_type_id, self.activity1)
|
||||
self.assertEqual(self.lead.activity_summary, self.activity1.summary)
|
||||
# self.assertEqual(self.lead.activity_date_deadline, self.activity1.summary)
|
||||
|
||||
# Check message recipients
|
||||
# mark as done, check lead and posted message
|
||||
activity.action_done()
|
||||
self.assertFalse(self.lead.activity_type_id.id)
|
||||
self.assertFalse(self.lead.activity_ids)
|
||||
activity_message = self.lead.message_ids[0]
|
||||
self.assertEqual(activity_message.needaction_partner_ids, self.crm_salemanager.partner_id, 'Only the crm manager should be notified by the activity')
|
||||
self.assertEqual(self.lead.next_activity_id.id, False, 'When logging activity, the next activity planned is erased')
|
||||
self.assertEqual(activity_message.needaction_partner_ids, self.crm_salemanager.partner_id)
|
||||
self.assertEqual(activity_message.subtype_id, self.env.ref('mail.mt_activities'))
|
||||
|
||||
def test_crm_activity_next_action(self):
|
||||
""" This test case set the next activity on a lead, log another, and schedule a third. """
|
||||
# Add the next activity (like we set it from a form view)
|
||||
self.lead.write({
|
||||
'next_activity_id': self.activity1.id
|
||||
lead_model_id = self.env['ir.model'].search([('model', '=', 'crm.lead')]).id
|
||||
activity = self.env['mail.activity'].sudo(self.crm_salesman.id).create({
|
||||
'activity_type_id': self.activity1.id,
|
||||
'summary': 'My Own Summary',
|
||||
'res_id': self.lead.id,
|
||||
'res_model_id': lead_model_id,
|
||||
})
|
||||
self.lead._onchange_next_activity_id()
|
||||
activity._onchange_activity_type_id()
|
||||
|
||||
# Check the next activity is correct
|
||||
self.assertEqual(self.lead.title_action, self.activity1.description, 'Activity title should be the same on the lead and on the chosen activity')
|
||||
self.assertEqual(self.lead.activity_summary, activity.summary)
|
||||
self.assertEqual(self.lead.activity_type_id, activity.activity_type_id)
|
||||
# self.assertEqual(fields.Datetime.from_string(self.lead.activity_date_deadline), datetime.now() + timedelta(days=activity.activity_type_id.days))
|
||||
|
||||
# Instanciate the wizard to schedule the next activity
|
||||
wizard = self.env['crm.activity.log'].sudo(self.crm_salesman.id).create({
|
||||
activity.write({
|
||||
'activity_type_id': self.activity2.id,
|
||||
'summary': '',
|
||||
'note': 'Content of the activity to log',
|
||||
'lead_id': self.lead.id,
|
||||
})
|
||||
wizard.onchange_lead_id()
|
||||
wizard.write({
|
||||
'next_activity_id': self.activity2.id,
|
||||
})
|
||||
wizard.action_log()
|
||||
activity._onchange_activity_type_id()
|
||||
|
||||
self.assertEqual(self.lead.activity_summary, activity.activity_type_id.summary)
|
||||
self.assertEqual(self.lead.activity_type_id, activity.activity_type_id)
|
||||
# self.assertEqual(fields.Datetime.from_string(self.lead.activity_date_deadline), datetime.now() + timedelta(days=activity.activity_type_id.days))
|
||||
|
||||
activity.action_done()
|
||||
|
||||
# Check the next activity on the lead has been removed
|
||||
self.assertFalse(self.lead.next_activity_id.id, 'No next activity should be set on lead, since we jsut log another activity')
|
||||
|
||||
# Instanciate the wizard to schedule the next activity
|
||||
self.env['crm.activity.log'].sudo(self.crm_salesman.id).create({
|
||||
'next_activity_id': self.activity3.id,
|
||||
'note': 'Content of the activity to log',
|
||||
'lead_id': self.lead.id,
|
||||
})
|
||||
wizard.onchange_lead_id()
|
||||
wizard.write({
|
||||
'next_activity_id': self.activity3.id,
|
||||
})
|
||||
wizard.onchange_next_activity_id()
|
||||
wizard.action_schedule()
|
||||
|
||||
# Check the activity is well scheldule on lead
|
||||
delta_days = (fields.Date.from_string(self.lead.date_action) - date.today()).days
|
||||
self.assertEqual(self.activity3.days, delta_days, 'The action date should be in the number of days set up on the activity 3')
|
||||
self.assertEqual(self.lead.title_action, self.activity3.description, 'Activity title should be the same on the lead and on the activity 3')
|
||||
self.assertFalse(self.lead.activity_type_id)
|
||||
|
||||
@@ -1,67 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
|
||||
<record id="crm_activity_view_search" model="ir.ui.view">
|
||||
<field name="name">crm.activity.search</field>
|
||||
<field name="model">crm.activity</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Action Search">
|
||||
<field name="name"/>
|
||||
<field name="team_id"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_view_form" model="ir.ui.view">
|
||||
<field name="name">crm.activity.form</field>
|
||||
<field name="model">crm.activity</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Activities">
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="team_id"/>
|
||||
<field name="description"/>
|
||||
<field name="days"/>
|
||||
<field name="default"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="recommended_activity_ids" widget="many2many_tags"/>
|
||||
</group>
|
||||
</group>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_view_tree" model="ir.ui.view">
|
||||
<field name="name">crm.activity.tree</field>
|
||||
<field name="model">crm.activity</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Activities">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="days"/>
|
||||
<field name="team_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_action" model="ir.actions.act_window">
|
||||
<field name="name">Activities</field>
|
||||
<field name="res_model">crm.activity</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
<field name="help" type="html">
|
||||
<p class="oe_view_nocontent_create">Click to create a new activity.</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem
|
||||
id="crm_activity_menu"
|
||||
name="Activities"
|
||||
action="crm_activity_action"
|
||||
parent="menu_crm_config_lead"/>
|
||||
</odoo>
|
||||
@@ -186,7 +186,7 @@
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="priority" eval="2"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Leads Generation" date_start="date_action" color="user_id">
|
||||
<calendar string="Leads Generation" date_start="activity_date_deadline" color="user_id">
|
||||
<field name="name"/>
|
||||
<field name="partner_name"/>
|
||||
</calendar>
|
||||
@@ -240,16 +240,18 @@
|
||||
<field name="priority"/>
|
||||
<field name="planned_revenue"/>
|
||||
<field name="kanban_state"/>
|
||||
<field name="date_action"/>
|
||||
<field name="activity_date_deadline"/>
|
||||
<field name="user_email"/>
|
||||
<field name="user_id"/>
|
||||
<field name="partner_address_email"/>
|
||||
<field name="message_needaction_counter"/>
|
||||
<field name="tag_ids"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="title_action"/>
|
||||
<field name="activity_summary"/>
|
||||
<field name="active"/>
|
||||
<field name="company_currency"/>
|
||||
<field name="activity_state" />
|
||||
<field name="activity_ids" />
|
||||
<templates>
|
||||
<field name="date_deadline"/>
|
||||
<t t-name="kanban-box">
|
||||
@@ -284,16 +286,10 @@
|
||||
<span class='oe_kanban_mail_new' title='Unread Messages'><i class='fa fa-comments'/><t t-raw="record.message_needaction_counter.raw_value"/></span>
|
||||
</t>
|
||||
<div>
|
||||
<a name="%(crm.crm_activity_log_action)d" type="action" t-if="record.kanban_state.raw_value!='grey'">
|
||||
<span
|
||||
t-att-title="record.date_action.value + ': '+ record.title_action.value"
|
||||
t-attf-class="oe_kanban_status oe_kanban_status_#{record.kanban_state.raw_value}"/>
|
||||
</a>
|
||||
<a name="%(crm.crm_activity_schedule_action)d" type="action" t-if="record.kanban_state.raw_value=='grey'">
|
||||
<span
|
||||
class="oe_kanban_status oe_kanban_status_grey"/>
|
||||
</a>
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
|
||||
<div class="o_kanban_inline_block">
|
||||
<field name="activity_ids" widget="kanban_activity"/>
|
||||
</div>
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -332,6 +328,18 @@
|
||||
<filter string="Exclude Opt Out"
|
||||
name='not_opt_out' domain="[('opt_out', '=', False)]"
|
||||
help="Leads that did not ask not to be included in mass mailing campaigns"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Salesperson" name="groupby_salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter string="Sales Team" name="groupby_team" domain="[]" context="{'group_by':'team_id'}"/>
|
||||
@@ -362,8 +370,6 @@
|
||||
type="action" class="oe_highlight"
|
||||
context="{'default_lead_id': active_id}"
|
||||
attrs="{'invisible': [('active', '=', False),('probability', '<', 100)]}"/>
|
||||
<button name="%(crm.crm_activity_log_action)d" string="Log Activity" type="action" class="btn-link"
|
||||
attrs="{'invisible': [('active', '=', False)]}"/>
|
||||
<field name="stage_id" widget="statusbar" clickable="True"
|
||||
options="{'fold_field': 'fold'}"
|
||||
domain="['|', ('team_id', '=', team_id), ('team_id', '=', False)]"
|
||||
@@ -425,21 +431,6 @@
|
||||
</group>
|
||||
|
||||
<group>
|
||||
<label for="next_activity_id"/>
|
||||
<div>
|
||||
<div class="o_row mr16">
|
||||
<field name="next_activity_id"
|
||||
options="{'no_create': True, 'no_open': True}"
|
||||
domain="['|',('team_id','=',False),('team_id','=',team_id)]"/>
|
||||
<span class="oe_form_field"
|
||||
attrs="{'invisible': ['|', ('next_activity_id', '=', False), ('date_action', '=', False)]}"> on </span>
|
||||
<field name="date_action" attrs="{'invisible': [('next_activity_id', '=', False)]}" placeholder="Choose a date..."/>
|
||||
</div>
|
||||
<div>
|
||||
<field name="title_action" placeholder="e.g. Discuss proposal"
|
||||
attrs="{'invisible': [('next_activity_id', '=', False)]}"/>
|
||||
</div>
|
||||
</div>
|
||||
<field name="date_deadline"/>
|
||||
</group>
|
||||
|
||||
@@ -503,6 +494,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>
|
||||
@@ -513,14 +505,14 @@
|
||||
<field name="name">crm.lead.tree.opportunity</field>
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Opportunities" decoration-bf="message_needaction==True" decoration-muted="probability == 100" decoration-danger="date_action and (date_action < current_date)">
|
||||
<tree string="Opportunities" decoration-bf="message_needaction==True" decoration-muted="probability == 100" decoration-danger="activity_date_deadline and (activity_date_deadline < current_date)">
|
||||
<field name="date_deadline" invisible="1"/>
|
||||
<field name="create_date"/>
|
||||
<field name="name" string="Opportunity"/>
|
||||
<field name="partner_id" string="Customer"/>
|
||||
<field name="country_id"/>
|
||||
<field name="date_action"/>
|
||||
<field name="title_action"/>
|
||||
<field name="activity_date_deadline"/>
|
||||
<field name="activity_summary"/>
|
||||
<field name="medium_id" invisible="1"/>
|
||||
<field name="campaign_id" invisible="1"/>
|
||||
<field name="source_id" invisible="1"/>
|
||||
@@ -542,12 +534,12 @@
|
||||
<field name="name">crm.lead.tree.opportunity.next.activity</field>
|
||||
<field name="model">crm.lead</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Next Activity" decoration-danger="date_action and (date_action < current_date)" default_order="date_action">
|
||||
<tree string="Next Activities" decoration-danger="activity_date_deadline and (activity_date_deadline < current_date)" default_order="activity_date_deadline">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="date_action"/>
|
||||
<field name="next_activity_id"/>
|
||||
<field name="title_action"/>
|
||||
<field name="activity_date_deadline"/>
|
||||
<field name="activity_type_id"/>
|
||||
<field name="activity_summary"/>
|
||||
<field name="stage_id"/>
|
||||
<field name="planned_revenue"/>
|
||||
<field name="date_deadline"/>
|
||||
@@ -566,8 +558,8 @@
|
||||
<field name="user_id"/>
|
||||
<field name="partner_id" operator="child_of" string="Customer"/>
|
||||
<field name="stage_id" domain="[]"/>
|
||||
<field name="next_activity_id"/>
|
||||
<field name="title_action"/>
|
||||
<field name="activity_type_id"/>
|
||||
<field name="activity_summary"/>
|
||||
<field name="probability"/>
|
||||
<field name="lost_reason"/>
|
||||
<separator/>
|
||||
@@ -580,14 +572,16 @@
|
||||
<filter string="Unassigned" name="unassigned"
|
||||
domain="[('user_id','=', False)]" help="No salesperson"/>
|
||||
<separator/>
|
||||
<filter string="Today Activities" name="today"
|
||||
domain="[('date_action', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="This Week Activities" name="this_week"
|
||||
domain="['&', ('date_action', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('date_action', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue Activities" name="overdue"
|
||||
domain="[('date_action', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
<filter string="Overdue Activities" name="activities_overdue"
|
||||
domain="[('activity_ids.date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all opportunities for which the next action date is before today"/>
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
@@ -831,7 +825,7 @@ if record:
|
||||
eval="[(5, 0, 0), (0, 0, {'view_mode': 'tree', 'view_id': ref('crm_lead_view_tree_activity')}),
|
||||
(0, 0, {'view_mode': 'form', 'view_id': ref('crm_case_form_view_oppor')}),
|
||||
(0, 0, {'view_mode': 'kanban'}), (0, 0, {'view_mode': 'calendar'}), (0, 0, {'view_mode': 'pivot'}), (0, 0, {'view_mode': 'graph'})]"/>
|
||||
<field name="domain">[('type','=','opportunity'), ('date_action', '!=', False)]</field>
|
||||
<field name="domain">[('type','=','opportunity'), ('activity_date_deadline', '!=', False)]</field>
|
||||
<field name="context">{
|
||||
'default_type': 'opportunity',
|
||||
'default_user_id': uid,
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
import base_partner_merge
|
||||
import crm_activity_log
|
||||
import crm_lead_lost
|
||||
import crm_partner_binding
|
||||
import crm_lead_to_opportunity
|
||||
|
||||
@@ -1,103 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from odoo import api, fields, models, tools, _
|
||||
|
||||
|
||||
class ActivityLog(models.TransientModel):
|
||||
|
||||
_name = "crm.activity.log"
|
||||
_description = "Log an Activity"
|
||||
_rec_name = 'title_action'
|
||||
|
||||
@api.model
|
||||
def _default_lead_id(self):
|
||||
if 'default_lead_id' in self._context:
|
||||
return self._context['default_lead_id']
|
||||
if self._context.get('active_model') == 'crm.lead':
|
||||
return self._context.get('active_id')
|
||||
return False
|
||||
|
||||
next_activity_id = fields.Many2one('crm.activity', 'Activity')
|
||||
last_activity_id = fields.Many2one('crm.activity', 'Previous Activity', default=False)
|
||||
recommended_activity_id = fields.Many2one("crm.activity", "Recommended Activities")
|
||||
title_action = fields.Char('Summary')
|
||||
note = fields.Html('Note')
|
||||
date_action = fields.Date('Next Activity Date')
|
||||
lead_id = fields.Many2one('crm.lead', 'Lead', required=True, default=_default_lead_id)
|
||||
team_id = fields.Many2one('crm.team', 'Sales Team')
|
||||
date_deadline = fields.Date('Expected Closing')
|
||||
planned_revenue = fields.Float('Expected Revenue')
|
||||
|
||||
@api.onchange('lead_id')
|
||||
def onchange_lead_id(self):
|
||||
self.next_activity_id = self.lead_id.next_activity_id
|
||||
self.date_deadline = self.lead_id.date_deadline
|
||||
self.team_id = self.lead_id.team_id
|
||||
self.planned_revenue = self.lead_id.planned_revenue
|
||||
self.title_action = self.lead_id.title_action
|
||||
|
||||
@api.onchange('next_activity_id')
|
||||
def onchange_next_activity_id(self):
|
||||
if not self.title_action:
|
||||
self.title_action = self.next_activity_id.description
|
||||
date_action = False
|
||||
if self.next_activity_id and self.next_activity_id.days:
|
||||
date_action = (datetime.now() + timedelta(days=self.next_activity_id.days)).strftime(tools.DEFAULT_SERVER_DATETIME_FORMAT)
|
||||
self.date_action = date_action
|
||||
|
||||
@api.onchange('recommended_activity_id')
|
||||
def onchange_recommended_activity_id(self):
|
||||
self.next_activity_id = self.recommended_activity_id
|
||||
|
||||
@api.multi
|
||||
def action_log_and_schedule(self):
|
||||
self.ensure_one()
|
||||
self.action_log()
|
||||
view_id = self.env.ref('crm.crm_activity_log_view_form_schedule')
|
||||
return {
|
||||
'name': _('Next activity'),
|
||||
'res_model': 'crm.activity.log',
|
||||
'context': {
|
||||
'default_last_activity_id': self.next_activity_id.id,
|
||||
'default_lead_id': self.lead_id.id
|
||||
},
|
||||
'type': 'ir.actions.act_window',
|
||||
'view_id': False,
|
||||
'views': [(view_id.id, 'form')],
|
||||
'view_mode': 'form',
|
||||
'target': 'new',
|
||||
'view_type': 'form',
|
||||
'res_id': False
|
||||
}
|
||||
|
||||
@api.multi
|
||||
def action_log(self):
|
||||
for log in self:
|
||||
body_html = "<div><b>%(title)s</b>: %(next_activity)s</div>%(description)s%(note)s" % {
|
||||
'title': _('Activity Done'),
|
||||
'next_activity': log.next_activity_id.name,
|
||||
'description': log.title_action and '<p><em>%s</em></p>' % log.title_action or '',
|
||||
'note': log.note or '',
|
||||
}
|
||||
log.lead_id.message_post(body_html, subject=log.title_action, subtype_id=log.next_activity_id.subtype_id.id)
|
||||
log.lead_id.write({
|
||||
'date_deadline': log.date_deadline,
|
||||
'planned_revenue': log.planned_revenue,
|
||||
'title_action': False,
|
||||
'date_action': False,
|
||||
'next_activity_id': False,
|
||||
})
|
||||
return True
|
||||
|
||||
@api.multi
|
||||
def action_schedule(self):
|
||||
for log in self:
|
||||
log.lead_id.write({
|
||||
'title_action': log.title_action,
|
||||
'date_action': log.date_action,
|
||||
'next_activity_id': log.next_activity_id.id,
|
||||
})
|
||||
return True
|
||||
@@ -1,85 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<record id="crm_activity_log_view_form" model="ir.ui.view">
|
||||
<field name="name">Log an Activity</field>
|
||||
<field name="model">crm.activity.log</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Log an Activity">
|
||||
<group>
|
||||
<field name="team_id" invisible="1"/>
|
||||
<field name="lead_id" invisible="1"/>
|
||||
<group>
|
||||
<field name="next_activity_id" required="1"
|
||||
domain="['|', ('team_id', '=', False), ('team_id', '=', team_id)]"
|
||||
options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="title_action" placeholder="e.g. Discuss proposal"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="planned_revenue"/>
|
||||
<field name="date_deadline"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="note" placeholder="Log a note..."/>
|
||||
<footer>
|
||||
<button name="action_log_and_schedule" string="Log & Schedule Next" type="object" class="btn btn-primary"/>
|
||||
<button name="action_log" string="Log Only" type="object" class="btn btn-primary"/>
|
||||
<button string="Cancel" class="btn btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_log_view_form_schedule" model="ir.ui.view">
|
||||
<field name="name">Schedule next Activity</field>
|
||||
<field name="model">crm.activity.log</field>
|
||||
<field name="priority">40</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Schedule Next Activity">
|
||||
<group class="mt0">
|
||||
<field name="team_id" invisible="1"/>
|
||||
<field name="lead_id" invisible="1"/>
|
||||
<field name="last_activity_id" invisible="1"/>
|
||||
<group>
|
||||
<field name="next_activity_id"
|
||||
domain="['|', ('team_id', '=', False), ('team_id', '=', team_id)]"
|
||||
options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="date_action"/>
|
||||
<field name="title_action" placeholder="e.g. Discuss proposal"/>
|
||||
</group>
|
||||
<group>
|
||||
<div>
|
||||
<p><strong>Recommended activities:</strong></p>
|
||||
<field name="recommended_activity_id" widget="radio" class="o_recommended_activity"
|
||||
domain="[('preceding_activity_ids', '=', last_activity_id)]"/>
|
||||
</div>
|
||||
</group>
|
||||
</group>
|
||||
<footer>
|
||||
<button name="action_schedule" string="Schedule Activity" type="object" class="btn btn-primary"/>
|
||||
<button string="No activity" class="btn btn-default" special="cancel"/>
|
||||
</footer>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="crm_activity_log_action" model="ir.actions.act_window">
|
||||
<field name="name">Log an Activity</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">crm.activity.log</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="crm_activity_log_view_form"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
|
||||
<record id="crm_activity_schedule_action" model="ir.actions.act_window">
|
||||
<field name="name">Schedule an Activity</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">crm.activity.log</field>
|
||||
<field name="view_mode">form</field>
|
||||
<field name="view_id" ref="crm_activity_log_view_form_schedule"/>
|
||||
<field name="target">new</field>
|
||||
</record>
|
||||
|
||||
</odoo>
|
||||
@@ -124,7 +124,7 @@ class Lead2OpportunityPartner(models.TransientModel):
|
||||
if self.name == 'merge':
|
||||
leads = self.with_context(active_test=False).opportunity_ids.merge_opportunity()
|
||||
if not leads.active:
|
||||
leads.write({'active': True, 'next_activity_id': False, 'lost_reason': False})
|
||||
leads.write({'active': True, 'activity_type_id': False, 'lost_reason': False})
|
||||
if leads.type == "lead":
|
||||
values.update({'lead_ids': leads.ids, 'user_ids': [self.user_id.id]})
|
||||
self.with_context(active_ids=leads.ids)._convert_opportunity(values)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<record id="view_partner_property_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.carrier.property.form.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="mail.view_emails_partner_info_form" />
|
||||
<field name="inherit_id" ref="mail.res_partner_view_form_inherit_mail" />
|
||||
<field name="arch" type="xml">
|
||||
<field name="opt_out" position="after">
|
||||
<field name="property_delivery_carrier_id"/>
|
||||
|
||||
@@ -39,7 +39,9 @@ You can define the different phases of interviews and easily rate the applicant
|
||||
'views/hr_job_views.xml',
|
||||
'views/tour_views.xml',
|
||||
],
|
||||
'demo': ['data/hr_recruitment_demo.xml'],
|
||||
'demo': [
|
||||
'data/hr_recruitment_demo.xml',
|
||||
],
|
||||
'test': ['test/recruitment_process.yml'],
|
||||
'installable': True,
|
||||
'auto_install': False,
|
||||
|
||||
@@ -58,8 +58,6 @@
|
||||
<field name="partner_name">Enrique Jones</field>
|
||||
<field name="partner_mobile">9963214587</field>
|
||||
<field name="stage_id" ref="stage_job1"/>
|
||||
<field eval="time.strftime('%Y-%m-15 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Send mail regarding our interview</field>
|
||||
</record>
|
||||
<record id="hr_case_traineemca0" model="hr.applicant">
|
||||
<field name="name">Trainee - MCA</field>
|
||||
@@ -73,8 +71,6 @@
|
||||
<field name="partner_mobile">9988774455</field>
|
||||
<field name="stage_id" ref="stage_job4"/>
|
||||
<field name="partner_phone">6633225</field>
|
||||
<field eval="time.strftime('%Y-%m-10 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Call to define real needs about training</field>
|
||||
</record>
|
||||
<record id="hr_case_fresher0" model="hr.applicant">
|
||||
<field name="name">Fresher</field>
|
||||
@@ -87,8 +83,6 @@
|
||||
<field name="partner_name">Jose</field>
|
||||
<field name="stage_id" ref="stage_job3"/>
|
||||
<field name="partner_phone">999666735</field>
|
||||
<field eval="time.strftime('%Y-%m-5 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Send mail regarding our interview</field>
|
||||
</record>
|
||||
<record id="hr_case_yrsexperienceinphp0" model="hr.applicant">
|
||||
<field name="name">Marketing Job</field>
|
||||
@@ -99,8 +93,6 @@
|
||||
<field name="user_id" ref="base.user_root"/>
|
||||
<field name="partner_name">John Bruno</field>
|
||||
<field name="stage_id" ref="stage_job5"/>
|
||||
<field eval="time.strftime('%Y-%m-24 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Call to define real needs about training</field>
|
||||
</record>
|
||||
<record id="hr_case_marketingjob0" model="hr.applicant">
|
||||
<field name="name">More than 5 yrs Experience in PHP</field>
|
||||
@@ -111,8 +103,6 @@
|
||||
<field name="user_id" ref="base.user_demo"/>
|
||||
<field name="partner_name">Sandra Elvis</field>
|
||||
<field name="stage_id" ref="stage_job5"/>
|
||||
<field eval="time.strftime('%Y-%m-20 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Send mail regarding our interview</field>
|
||||
</record>
|
||||
<record id="hr_case_financejob0" model="hr.applicant">
|
||||
<field name="name">Finance Manager</field>
|
||||
@@ -125,8 +115,6 @@
|
||||
<field name="partner_name">David Armstrong</field>
|
||||
<field name="stage_id" ref="stage_job2"/>
|
||||
<field name="partner_phone">33968745</field>
|
||||
<field eval="time.strftime('%Y-%m-18 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Send mail regarding our interview</field>
|
||||
</record>
|
||||
<record id="hr_case_traineemca1" model="hr.applicant">
|
||||
<field name="name">Trainee - MCA</field>
|
||||
@@ -138,8 +126,6 @@
|
||||
<field name="partner_mobile">9898745745</field>
|
||||
<field name="stage_id" ref="stage_job4"/>
|
||||
<field name="partner_phone">6630125</field>
|
||||
<field eval="time.strftime('%Y-%m-15 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Send mail regarding our interview</field>
|
||||
</record>
|
||||
<record id="hr_case_programmer" model="hr.applicant">
|
||||
<field name="name">Programmer</field>
|
||||
@@ -152,8 +138,6 @@
|
||||
<field name="stage_id" ref="stage_job4"/>
|
||||
<field name="partner_phone">6630125</field>
|
||||
<field name="salary_expected">11000.0</field>
|
||||
<field eval="time.strftime('%Y-%m-24 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Send mail regarding our interview</field>
|
||||
</record>
|
||||
<record id="hr_case_advertisement" model="hr.applicant">
|
||||
<field name="name">Advertisement</field>
|
||||
@@ -165,8 +149,6 @@
|
||||
<field name="partner_mobile">9988774455</field>
|
||||
<field name="stage_id" ref="stage_job2"/>
|
||||
<field name="salary_expected">11000.0</field>
|
||||
<field eval="time.strftime('%Y-%m-15 18:15:00')" name="date_action"/>
|
||||
<field name="title_action">Send mail regarding our interview</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_case_salesman0_cv" model="ir.attachment">
|
||||
@@ -235,5 +217,27 @@
|
||||
<field name="author_id" ref="base.partner_demo"/>
|
||||
</record>
|
||||
|
||||
<record id="mail_activity_0" model="mail.activity">
|
||||
<field name="res_id" ref="hr_recruitment.hr_case_salesman0" />
|
||||
<field name="res_model_id" ref="model_hr_applicant"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_email" />
|
||||
<field name="date_deadline" eval="time.strftime('%Y-%m-15 18:15:00')"/>
|
||||
<field name="summary">Send mail regarding our interview</field>
|
||||
</record>
|
||||
<record id="mail_activity_1" model="mail.activity">
|
||||
<field name="res_id" ref="hr_recruitment.hr_case_traineemca0" />
|
||||
<field name="res_model_id" ref="model_hr_applicant"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call" />
|
||||
<field name="date_deadline" eval="time.strftime('%Y-%m-10 18:15:00')"/>
|
||||
<field name="summary">Call to define real needs about training</field>
|
||||
</record>
|
||||
<record id="mail_activity_2" model="mail.activity">
|
||||
<field name="res_id" ref="hr_recruitment.hr_case_yrsexperienceinphp0" />
|
||||
<field name="res_model_id" ref="model_hr_applicant"/>
|
||||
<field name="activity_type_id" ref="mail.mail_activity_data_call" />
|
||||
<field name="date_deadline" eval="time.strftime('%Y-%m-24 18:15:00')"/>
|
||||
<field name="summary">Call to define real needs about training</field>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -88,7 +88,7 @@ class Applicant(models.Model):
|
||||
_name = "hr.applicant"
|
||||
_description = "Applicant"
|
||||
_order = "priority desc, id desc"
|
||||
_inherit = ['mail.thread', 'utm.mixin']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin', 'utm.mixin']
|
||||
_mail_mass_mailing = _('Applicants')
|
||||
|
||||
def _default_stage_id(self):
|
||||
@@ -135,8 +135,6 @@ class Applicant(models.Model):
|
||||
date_closed = fields.Datetime("Closed", readonly=True, index=True)
|
||||
date_open = fields.Datetime("Assigned", readonly=True, index=True)
|
||||
date_last_stage_update = fields.Datetime("Last Stage Update", index=True, default=fields.Datetime.now)
|
||||
date_action = fields.Date("Next Action Date")
|
||||
title_action = fields.Char("Next Action", size=64)
|
||||
priority = fields.Selection(AVAILABLE_PRIORITIES, "Appreciation", default='0')
|
||||
job_id = fields.Many2one('hr.job', "Applied Job")
|
||||
salary_proposed_extra = fields.Char("Proposed Salary Extra", help="Salary Proposed by the Organisation, extra advantages")
|
||||
|
||||
@@ -36,8 +36,6 @@
|
||||
<field name="partner_phone"/>
|
||||
<field name="job_id"/>
|
||||
<field name="stage_id"/>
|
||||
<field name="title_action" invisible="context.get('invisible_next_action', True)"/>
|
||||
<field name="date_action" invisible="context.get('invisible_next_date', True)"/>
|
||||
<field name="medium_id"/>
|
||||
<field name="source_id"/>
|
||||
<field name="priority"/>
|
||||
@@ -49,6 +47,21 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_applicant_view_tree_activity" model="ir.ui.view">
|
||||
<field name="name">hr.applicant.view.tree.activity</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Next Activities" decoration-danger="activity_date_deadline < current_date" default_order="activity_date_deadline">
|
||||
<field name="name"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="activity_date_deadline"/>
|
||||
<field name="activity_type_id"/>
|
||||
<field name="activity_summary"/>
|
||||
<field name="stage_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="crm_case_form_view_job">
|
||||
<field name="name">Jobs - Recruitment Form</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
@@ -113,11 +126,6 @@
|
||||
</group>
|
||||
<group>
|
||||
<field name="user_id"/>
|
||||
<label for="title_action"/>
|
||||
<div>
|
||||
<field name="date_action"/>
|
||||
<field name="title_action" class="oe_inline" placeholder="e.g. Call for interview"/>
|
||||
</div>
|
||||
<field name="priority" widget="priority"/>
|
||||
<field name="medium_id" groups="base.group_no_one" />
|
||||
<field name="source_id"/>
|
||||
@@ -150,6 +158,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>
|
||||
@@ -178,17 +187,13 @@
|
||||
</record>
|
||||
|
||||
<record id="view_crm_case_jobs_filter" model="ir.ui.view">
|
||||
<field name="name">Jobs - Recruitment Search</field>
|
||||
<field name="name">hr.applicant.view.search</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Search Applicants">
|
||||
<field name="partner_name" filter_domain="['|','|',('name','ilike',self),('partner_name','ilike',self),('email_from','ilike',self)]" string="Subject / Applicant"/>
|
||||
<filter string="My" domain="[('user_id', '=', uid)]"/>
|
||||
<filter string="My Applications" domain="[('user_id', '=', uid)]"/>
|
||||
<filter string="Unassigned" domain="[('user_id', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="Next Actions" context="{'invisible_next_action':False, 'invisible_next_date':False}"
|
||||
domain="[('date_action','<>',False)]" help="Filter and view on next actions and date"/>
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<field name="job_id"/>
|
||||
@@ -196,6 +201,17 @@
|
||||
<field name="user_id"/>
|
||||
<field name="stage_id" domain="[]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<field name="categ_ids"/>
|
||||
<field name="attachment_ids" filter_domain="[('attachment_ids.index_content', 'ilike', self)]" string="Attachments"/>
|
||||
<separator/>
|
||||
@@ -211,17 +227,15 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<!-- CRM Lead Calendar View -->
|
||||
<record model="ir.ui.view" id="hr_applicant_calendar_view">
|
||||
<field name="name">Hr Applicants Calendar</field>
|
||||
<field name="model">hr.applicant</field>
|
||||
<field name="priority" eval="2"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Applicants"
|
||||
date_start="date_action" color="user_id">
|
||||
<calendar string="Applicants" date_start="activity_date_deadline" color="user_id">
|
||||
<field name="name"/>
|
||||
<field name="partner_name"/>
|
||||
<field name="title_action"/>
|
||||
<field name="activity_summary"/>
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
@@ -240,12 +254,13 @@
|
||||
<field name="partner_name"/>
|
||||
<field name="type_id"/>
|
||||
<field name="job_id"/>
|
||||
<field name="title_action"/>
|
||||
<field name="department_id"/>
|
||||
<field name="categ_ids"/>
|
||||
<field name="message_needaction_counter"/>
|
||||
<field name="attachment_number"/>
|
||||
<field name="active"/>
|
||||
<field name="activity_ids" />
|
||||
<field name="activity_state" />
|
||||
<templates>
|
||||
<t t-name="kanban-tooltip">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
@@ -273,13 +288,12 @@
|
||||
<i><field name="name"/></i><br/>
|
||||
<field name="job_id"/><br/>
|
||||
<t t-if="record.partner_mobile.raw_value">Mobile: <field name="partner_mobile"/><br/></t>
|
||||
<t t-if="record.date_action.raw_value and record.date_action.raw_value lt (new Date())" t-set="red">oe_kanban_text_red</t>
|
||||
<span t-attf-class="#{red || ''}"><field name="date_action"/></span>
|
||||
<t t-if="record.date_action.raw_value"> : </t>
|
||||
<field name="title_action"/>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<div class="o_kanban_inline_block ml4">
|
||||
<field name="activity_ids" widget="kanban_activity"/>
|
||||
</div>
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
|
||||
<div class="pull-left" groups="base.group_user">
|
||||
<div class="pull-left mt4" groups="base.group_user">
|
||||
<field name="priority" widget="priority"/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -316,6 +330,22 @@
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="hr_applicant_action_activity" model="ir.actions.act_window">
|
||||
<field name="name">My Next Activities</field>
|
||||
<field name="res_model">hr.applicant</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,kanban,calendar</field>
|
||||
<field name="view_id" ref="hr_applicant_view_tree_activity"/>
|
||||
<field name="context">{'search_default_activities_my': 1}</field>
|
||||
<field name="domain">[('activity_date_deadline', '!=', False)]</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Currently there are not any activity scheduled.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="action_hr_job_sources">
|
||||
<field name="name">Jobs Sources</field>
|
||||
<field name="res_model">hr.recruitment.source</field>
|
||||
@@ -499,7 +529,7 @@
|
||||
process and follow up all operations: meetings, interviews, etc.
|
||||
</p><p>
|
||||
Applicants and their attached CV are created automatically when an email is sent.
|
||||
If you install the document management modules, all resumes are indexed automatically,
|
||||
If you install the document management modules, all resumes are indexed automatically,
|
||||
so that you can easily search through their content.
|
||||
</p>
|
||||
</field>
|
||||
@@ -526,7 +556,7 @@
|
||||
<field name="act_window_id" ref="crm_case_categ0_act_job"/>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window.view" id="action_hr_sec_graph_view_act_job">
|
||||
<record id="action_hr_sec_graph_view_act_job" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="4"/>
|
||||
<field name="view_mode">graph</field>
|
||||
<field name="view_id" ref="crm_case_graph_view_job"/>
|
||||
@@ -549,7 +579,18 @@
|
||||
<menuitem
|
||||
name="Applications"
|
||||
parent="menu_hr_recruitment_root"
|
||||
id="menu_crm_case_categ0_act_job" action="crm_case_categ0_act_job" sequence="2" groups="base.group_no_one"/>
|
||||
id="menu_crm_case_categ0_act_job" sequence="2"/>
|
||||
|
||||
<menuitem
|
||||
name="All Applications"
|
||||
parent="menu_crm_case_categ0_act_job"
|
||||
id="menu_crm_case_categ_all_app" action="crm_case_categ0_act_job" sequence="2"/>
|
||||
|
||||
<menuitem id="hr_applicant_nmenu_activity"
|
||||
name="Next Activities"
|
||||
parent="hr_recruitment.menu_crm_case_categ0_act_job"
|
||||
action="hr_applicant_action_activity"
|
||||
sequence="20"/>
|
||||
|
||||
<!-- Resume and Letters -->
|
||||
<record id="ir_attachment_view_search_inherit_hr_recruitment" model="ir.ui.view">
|
||||
|
||||
@@ -36,10 +36,12 @@ Main Features
|
||||
'views/mail_followers_views.xml',
|
||||
'views/mail_channel_views.xml',
|
||||
'views/mail_shortcode_views.xml',
|
||||
'views/mail_activity_views.xml',
|
||||
'views/res_config_views.xml',
|
||||
'data/mail_data.xml',
|
||||
'data/mail_channel_data.xml',
|
||||
'data/mail_shortcode_data.xml',
|
||||
'data/mail_activity_data.xml',
|
||||
'security/mail_security.xml',
|
||||
'security/ir.model.access.csv',
|
||||
'views/mail_alias_views.xml',
|
||||
@@ -57,6 +59,7 @@ Main Features
|
||||
'installable': True,
|
||||
'application': True,
|
||||
'qweb': [
|
||||
'static/src/xml/activity.xml',
|
||||
'static/src/xml/client_action.xml',
|
||||
'static/src/xml/composer.xml',
|
||||
'static/src/xml/chatter.xml',
|
||||
@@ -66,5 +69,6 @@ Main Features
|
||||
'static/src/xml/chat_window.xml',
|
||||
'static/src/xml/extended_chat_window.xml',
|
||||
'static/src/xml/announcement.xml',
|
||||
'static/src/xml/web_kanban_activity.xml',
|
||||
],
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<odoo>
|
||||
<data noupdate="1">
|
||||
<record id="mail_activity_data_email" model="mail.activity.type">
|
||||
<field name="name">Email</field>
|
||||
<field name="icon">fa-envelope</field>
|
||||
<field name="summary">Send an Email</field>
|
||||
</record>
|
||||
<record id="mail_activity_data_call" model="mail.activity.type">
|
||||
<field name="name">Call</field>
|
||||
<field name="icon">fa-phone</field>
|
||||
<field name="summary">Make a Call</field>
|
||||
<field name="days">2</field>
|
||||
</record>
|
||||
<record id="mail_activity_data_meeting" model="mail.activity.type">
|
||||
<field name="name">Meeting</field>
|
||||
<field name="icon">fa-users</field>
|
||||
<field name="summary">Reminder for meeting</field>
|
||||
</record>
|
||||
<record id="mail_activity_data_todo" model="mail.activity.type">
|
||||
<field name="name">Todo</field>
|
||||
<field name="icon">fa-tasks</field>
|
||||
<field name="summary">Task to perform</field>
|
||||
<field name="days">5</field>
|
||||
</record>
|
||||
</data>
|
||||
</odoo>
|
||||
@@ -56,6 +56,12 @@
|
||||
<field name="internal" eval="True"/>
|
||||
<field name="sequence" eval="100"/>
|
||||
</record>
|
||||
<record id="mt_activities" model="mail.message.subtype">
|
||||
<field name="name">Activities</field>
|
||||
<field name="default" eval="True"/>
|
||||
<field name="internal" eval="True"/>
|
||||
<field name="sequence" eval="90"/>
|
||||
</record>
|
||||
|
||||
</data>
|
||||
<data>
|
||||
|
||||
@@ -6,6 +6,7 @@ import mail_alias
|
||||
import mail_followers
|
||||
import mail_notification
|
||||
import mail_message
|
||||
import mail_activity
|
||||
import mail_mail
|
||||
import mail_thread
|
||||
import mail_channel
|
||||
|
||||
@@ -0,0 +1,226 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
||||
|
||||
from datetime import date, datetime, timedelta
|
||||
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class MailActivityType(models.Model):
|
||||
""" Activity Types are used to categorize activities. Each type is a different
|
||||
kind of activity e.g. call, mail, meeting. An activity can be generic i.e.
|
||||
available for all models using activities; or specific to a model in which
|
||||
case res_model_id field should be used. """
|
||||
_name = 'mail.activity.type'
|
||||
_description = 'Activity Type'
|
||||
_rec_name = 'name'
|
||||
_order = 'sequence'
|
||||
|
||||
name = fields.Char('Name', required=True, translate=True)
|
||||
summary = fields.Char('Summary', translate=True)
|
||||
sequence = fields.Integer('Sequence', default=10)
|
||||
days = fields.Integer(
|
||||
'# Days', default=0,
|
||||
help='Number of days before executing the action. It allows to plan the action deadline.')
|
||||
icon = fields.Char('Icon', help="Font awesome icon e.g. fa-tasks")
|
||||
res_model_id = fields.Many2one(
|
||||
'ir.model', 'Model', index=True,
|
||||
help='Specify a model if the activity should be specific to a model'
|
||||
'and not available when managing activities for other models.')
|
||||
|
||||
|
||||
class MailActivity(models.Model):
|
||||
""" An actual activity to perform. Activities are linked to
|
||||
documents using res_id and res_model_id fields. Activities have a deadline
|
||||
that can be used in kanban view to display a status. Once done activities
|
||||
are unlinked and a message is posted. This message has a new activity_type_id
|
||||
field that indicates the activity linked to the message. """
|
||||
_name = 'mail.activity'
|
||||
_description = 'Activity'
|
||||
_order = 'date_deadline ASC'
|
||||
_rec_name = 'summary'
|
||||
|
||||
@api.model
|
||||
def default_get(self, fields):
|
||||
res = super(MailActivity, self).default_get(fields)
|
||||
if not fields or 'res_model_id' in fields and res.get('res_model'):
|
||||
res['res_model_id'] = self.env['ir.model'].sudo().search([('model', '=', res['res_model'])]).id
|
||||
return res
|
||||
|
||||
# owner
|
||||
res_id = fields.Integer('Related Document ID', index=True, required=True)
|
||||
res_model_id = fields.Many2one(
|
||||
'ir.model', 'Related Document Model',
|
||||
index=True, ondelete='cascade', required=True)
|
||||
res_model = fields.Char(
|
||||
'Related Document Model',
|
||||
index=True, related='res_model_id.model', store=True, readonly=True)
|
||||
res_name = fields.Char(
|
||||
'Document Name', compute='_compute_res_name', store=True,
|
||||
help="Display name of the related document.", readonly=True)
|
||||
# activity
|
||||
activity_type_id = fields.Many2one(
|
||||
'mail.activity.type', 'Activity',
|
||||
domain="['|', ('res_model_id', '=', False), ('res_model_id', '=', res_model_id)]")
|
||||
icon = fields.Char('Icon', related='activity_type_id.icon')
|
||||
summary = fields.Char('Summary')
|
||||
note = fields.Html('Note')
|
||||
date_deadline = fields.Date('Due Date', index=True, required=True, default=fields.Date.today)
|
||||
# description
|
||||
user_id = fields.Many2one(
|
||||
'res.users', 'Assigned to',
|
||||
default=lambda self: self.env.user,
|
||||
index=True, required=True)
|
||||
state = fields.Selection([
|
||||
('overdue', 'Overdue'),
|
||||
('today', 'Today'),
|
||||
('planned', 'Planned')], 'State',
|
||||
compute='_compute_state')
|
||||
|
||||
@api.depends('res_model', 'res_id')
|
||||
def _compute_res_name(self):
|
||||
for activity in self:
|
||||
activity.res_name = self.env[activity.res_model].browse(activity.res_id).name_get()[0][1]
|
||||
|
||||
@api.depends('date_deadline')
|
||||
def _compute_state(self):
|
||||
today = date.today()
|
||||
for record in self.filtered(lambda activity: activity.date_deadline):
|
||||
date_deadline = fields.Date.from_string(record.date_deadline)
|
||||
diff = (date_deadline - today)
|
||||
if diff.days == 0:
|
||||
record.state = 'today'
|
||||
elif diff.days < 0:
|
||||
record.state = 'overdue'
|
||||
else:
|
||||
record.state = 'planned'
|
||||
|
||||
@api.onchange('activity_type_id')
|
||||
def _onchange_activity_type_id(self):
|
||||
if self.activity_type_id:
|
||||
if not self.summary:
|
||||
self.summary = self.activity_type_id.summary
|
||||
self.date_deadline = (datetime.now() + timedelta(days=self.activity_type_id.days))
|
||||
|
||||
@api.model
|
||||
def create(self, values):
|
||||
activity = super(MailActivity, self).create(values)
|
||||
activity.post_assignation_message()
|
||||
return activity
|
||||
|
||||
def write(self, values):
|
||||
res = super(MailActivity, self).write(values)
|
||||
if values.get('user_id'):
|
||||
self.post_assignation_message()
|
||||
return res
|
||||
|
||||
def post_assignation_message(self):
|
||||
for activity in self:
|
||||
record = self.env[activity.res_model].browse(activity.res_id)
|
||||
record.with_context(
|
||||
mail_post_autofollow=True
|
||||
).message_post_with_view(
|
||||
'mail.message_activity_assigned',
|
||||
values={'activity': activity},
|
||||
partner_ids=[activity.user_id.partner_id.id],
|
||||
subtype_id=self.env.ref('mail.mt_note').id,
|
||||
)
|
||||
|
||||
@api.multi
|
||||
def action_done(self):
|
||||
message = self.env['mail.message']
|
||||
for activity in self:
|
||||
record = self.env[activity.res_model].browse(activity.res_id)
|
||||
record.message_post_with_view(
|
||||
'mail.message_activity_done',
|
||||
values={'activity': activity},
|
||||
subtype_id=self.env.ref('mail.mt_activities').id,
|
||||
mail_activity_type_id=activity.activity_type_id.id,
|
||||
)
|
||||
message |= record.message_ids[0]
|
||||
|
||||
self.unlink()
|
||||
return message.ids and message.ids[0] or False
|
||||
|
||||
@api.multi
|
||||
def action_close_dialog(self):
|
||||
return {'type': 'ir.actions.act_window_close'}
|
||||
|
||||
|
||||
class MailActivityMixin(models.AbstractModel):
|
||||
""" Mail Activity Mixin is a mixin class to use if you want to add activities
|
||||
management on a model. It works like the mail.thread mixin. It defines
|
||||
an activity_ids one2many field toward activities using res_id and res_model_id.
|
||||
Various related / computed fields are also added to have a global status of
|
||||
activities on documents.
|
||||
|
||||
Activities come with a new JS widget for the form view. It is integrated in the
|
||||
Chatter widget although it is a separate widget. It displays activities linked
|
||||
to the current record and allow to schedule, edit and mark done activities.
|
||||
Use widget="mail_activity" on activity_ids field in form view to use it.
|
||||
|
||||
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."""
|
||||
_name = 'mail.activity.mixin'
|
||||
_description = 'Activity Mixin'
|
||||
|
||||
activity_ids = fields.One2many(
|
||||
'mail.activity', 'res_id', 'Activities',
|
||||
auto_join=True,
|
||||
domain=lambda self: [('res_model', '=', self._name)])
|
||||
activity_state = fields.Selection([
|
||||
('overdue', 'Overdue'),
|
||||
('today', 'Today'),
|
||||
('planned', 'Planned')], string='State',
|
||||
compute='_compute_activity_state',
|
||||
help='Status based on activities\nOverdue: Due date is already passed\n'
|
||||
'Today: Activity date is today\nPlanned: Future activities.')
|
||||
activity_user_id = fields.Many2one(
|
||||
'res.users', 'Responsible',
|
||||
related='activity_ids.user_id',
|
||||
search='_search_activity_user_id')
|
||||
activity_type_id = fields.Many2one(
|
||||
'mail.activity.type', 'Next Activity Type',
|
||||
related='activity_ids.activity_type_id',
|
||||
search='_search_activity_type_id')
|
||||
activity_date_deadline = fields.Date(
|
||||
'Next Activity Deadline', related='activity_ids.date_deadline',
|
||||
readonly=True, store=True) # store to enable ordering + search
|
||||
activity_summary = fields.Char(
|
||||
'Next Activity Summary', related='activity_ids.summary',
|
||||
search='_search_activity_summary')
|
||||
|
||||
@api.depends('activity_ids.state')
|
||||
def _compute_activity_state(self):
|
||||
for record in self:
|
||||
states = record.activity_ids.mapped('state')
|
||||
if 'overdue' in states:
|
||||
record.activity_state = 'overdue'
|
||||
elif 'today' in states:
|
||||
record.activity_state = 'today'
|
||||
elif 'planned' in states:
|
||||
record.activity_state = 'planned'
|
||||
|
||||
@api.model
|
||||
def _search_activity_user_id(self, operator, operand):
|
||||
return [('activity_ids.user_id', operator, operand)]
|
||||
|
||||
@api.model
|
||||
def _search_activity_type_id(self, operator, operand):
|
||||
return [('activity_ids.activity_type_id', operator, operand)]
|
||||
|
||||
@api.model
|
||||
def _search_activity_summary(self, operator, operand):
|
||||
return [('activity_ids.summary', operator, operand)]
|
||||
|
||||
@api.multi
|
||||
def unlink(self):
|
||||
""" Override unlink to delete records activities through (res_model, res_id). """
|
||||
record_ids = self.ids
|
||||
result = super(MailActivityMixin, self).unlink()
|
||||
self.env['mail.activity'].sudo().search(
|
||||
[('res_model', '=', self._name), ('res_id', 'in', record_ids)]
|
||||
).unlink()
|
||||
return result
|
||||
@@ -66,11 +66,16 @@ class Followers(models.Model):
|
||||
default_subtypes = self.env['mail.message.subtype'].search([
|
||||
('default', '=', True),
|
||||
'|', ('res_model', '=', res_model), ('res_model', '=', False)])
|
||||
external_default_subtypes = default_subtypes.filtered(lambda subtype: not subtype.internal)
|
||||
|
||||
if force_mode:
|
||||
employee_pids = self.env['res.users'].sudo().search([('partner_id', 'in', partner_data.keys()), ('share', '=', False)]).mapped('partner_id').ids
|
||||
for pid, data in partner_data.iteritems():
|
||||
if not data:
|
||||
partner_data[pid] = default_subtypes.ids
|
||||
if pid not in employee_pids:
|
||||
partner_data[pid] = external_default_subtypes.ids
|
||||
else:
|
||||
partner_data[pid] = default_subtypes.ids
|
||||
for cid, data in channel_data.iteritems():
|
||||
if not data:
|
||||
channel_data[cid] = default_subtypes.ids
|
||||
|
||||
@@ -61,6 +61,9 @@ class Message(models.Model):
|
||||
"message, comment for other messages such as user replies",
|
||||
oldname='type')
|
||||
subtype_id = fields.Many2one('mail.message.subtype', 'Subtype', ondelete='set null', index=True)
|
||||
mail_activity_type_id = fields.Many2one(
|
||||
'mail.activity.type', 'Mail Activity Type',
|
||||
index=True, ondelete='set null')
|
||||
# origin
|
||||
email_from = fields.Char(
|
||||
'From', default=_get_default_from,
|
||||
|
||||
@@ -14,7 +14,7 @@ class Partner(models.Model):
|
||||
""" Update partner to add a field about notification preferences. Add a generic opt-out field that can be used
|
||||
to restrict usage of automatic email templates. """
|
||||
_name = "res.partner"
|
||||
_inherit = ['res.partner', 'mail.thread']
|
||||
_inherit = ['res.partner', 'mail.thread', 'mail.activity.mixin']
|
||||
_mail_flat_thread = False
|
||||
_mail_mass_mailing = _('Customers')
|
||||
|
||||
|
||||
@@ -33,4 +33,8 @@ access_mail_template,mail.template,model_mail_template,base.group_user,1,1,1,0
|
||||
access_mail_template_system,mail.template_system,model_mail_template,base.group_system,1,1,1,1
|
||||
access_mail_shortcode,mail.shortcode,model_mail_shortcode,base.group_user,1,1,1,1
|
||||
access_mail_shortcode_portal,mail.shortcode.portal,model_mail_shortcode,base.group_portal,1,0,0,0
|
||||
access_mail_test_all,mail.test.all,model_mail_test,base.group_user,1,1,1,1
|
||||
access_mail_test_all,mail.test.all,model_mail_test,base.group_user,1,1,1,1
|
||||
access_mail_activity_all,mail.activity.all,model_mail_activity,,1,0,0,0
|
||||
access_mail_activity_user,mail.activity.user,model_mail_activity,base.group_user,1,1,1,1
|
||||
access_mail_activity_type_all,mail.activity.type.all,model_mail_activity_type,,1,0,0,0
|
||||
access_mail_activity_type_user,mail.activity.type.user,model_mail_activity_type,base.group_user,1,1,1,1
|
||||
|
@@ -0,0 +1,218 @@
|
||||
odoo.define('mail.activity', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var form_common = require('web.form_common');
|
||||
var Model = require("web.Model");
|
||||
var Widget = require('web.Widget');
|
||||
var ChatManager = require('mail.chat_manager');
|
||||
var QWeb = core.qweb;
|
||||
var _t = core._t;
|
||||
|
||||
|
||||
/**
|
||||
* Set the 'label_delay' entry in activity data according to the deadline date
|
||||
* @param {Array} list of activity Object
|
||||
* @return {Array} : list of modified activity Object
|
||||
*/
|
||||
var set_delay_label = function(activities){
|
||||
var today = moment().startOf('day');
|
||||
var today_str = today.format('YYYY-MM-DD');
|
||||
_.each(activities, function(activity){
|
||||
var to_display = '';
|
||||
var deadline = moment(activity.date_deadline + ' 00:00:00');
|
||||
var diff = deadline.diff(today, 'days', true); // true means no rounding
|
||||
if(diff === 0){
|
||||
to_display = _t('Today');
|
||||
}else{
|
||||
if(diff < 0){ // overdue
|
||||
if(diff === -1){
|
||||
to_display = _t('Yesterday');
|
||||
}else{
|
||||
to_display = _.str.sprintf(_t('%d days overdue'), Math.abs(diff));
|
||||
}
|
||||
}else{ // due
|
||||
if(diff === 1){
|
||||
to_display = _t('Tomorrow');
|
||||
}else{
|
||||
to_display = _.str.sprintf(_t('Due in %d days'), Math.abs(diff));
|
||||
}
|
||||
}
|
||||
}
|
||||
activity['label_delay'] = to_display;
|
||||
});
|
||||
return activities;
|
||||
};
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Activities Widget ('mail_activity' widget)
|
||||
//
|
||||
// Since it is displayed on a form view, it extends 'AbstractField' widget.
|
||||
//
|
||||
// Note: the activity widget is moved inside the chatter by the chatter itself
|
||||
// for layout purposes.
|
||||
// -----------------------------------------------------------------------------
|
||||
|
||||
var Activity = form_common.AbstractField.extend({
|
||||
className: 'o_mail_activity',
|
||||
|
||||
events: {
|
||||
"click .o_activity_edit": "on_activity_edit",
|
||||
"click .o_activity_unlink": "on_activity_unlink",
|
||||
"click .o_activity_done": "on_activity_done",
|
||||
},
|
||||
|
||||
init: function () {
|
||||
this._super.apply(this, arguments);
|
||||
|
||||
this.context = this.options.context || {};
|
||||
this.activities = [];
|
||||
this.Activity = new Model('mail.activity');
|
||||
},
|
||||
|
||||
start: function () {
|
||||
var self = this;
|
||||
|
||||
// Hide the chatter in 'create' mode
|
||||
this.view.on("change:actual_mode", this, this.check_visibility);
|
||||
|
||||
// find chatter if any
|
||||
this.chatter = this.field_manager.fields.message_ids;
|
||||
|
||||
return this._super();
|
||||
},
|
||||
|
||||
get_res_id: function () {
|
||||
return this.view.datarecord.id;
|
||||
},
|
||||
|
||||
check_visibility: function () {
|
||||
this.set({"force_invisible": this.view.get("actual_mode") === "create"});
|
||||
},
|
||||
|
||||
set_value: function (_value) {
|
||||
this.value = _value;
|
||||
this._super(_value);
|
||||
},
|
||||
|
||||
fetch_and_render_value: function () {
|
||||
var self = this;
|
||||
|
||||
return new Model(this.view.dataset.model)
|
||||
.call("read", [[this.get_res_id()], ['activity_ids']])
|
||||
.then(function (results) {
|
||||
self.value = results[0]['activity_ids'];
|
||||
return self.render_value();
|
||||
});
|
||||
},
|
||||
|
||||
render_value: function () {
|
||||
var self = this;
|
||||
|
||||
return this.Activity
|
||||
.call("read", [this.value])
|
||||
.then(function (results) {
|
||||
this.activities = set_delay_label(results);
|
||||
self.$el.html(QWeb.render('mail.activity_items', {
|
||||
activities: this.activities,
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
on_activity_schedule: function (event) {
|
||||
event.preventDefault();
|
||||
var self = this;
|
||||
var action = {
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'mail.activity',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [
|
||||
[false, 'form']
|
||||
],
|
||||
target: 'new',
|
||||
context: _.extend({
|
||||
default_res_id: this.get_res_id(),
|
||||
default_res_model: this.view.dataset.model,
|
||||
}, {'mark_done': true}, this.context),
|
||||
res_id: false,
|
||||
};
|
||||
return this.do_action(action, {
|
||||
on_close: function() {
|
||||
self.fetch_and_render_value();
|
||||
self.chatter.refresh_followers();
|
||||
ChatManager.get_messages({model: self.res_model, res_id: self.get_res_id()});
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
on_activity_edit: function (event) {
|
||||
event.preventDefault();
|
||||
var self = this;
|
||||
var activity_id = this.$(event.currentTarget).data('activity-id');
|
||||
var action = {
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'mail.activity',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [
|
||||
[false, 'form']
|
||||
],
|
||||
target: 'new',
|
||||
context: _.extend({
|
||||
default_res_id: this.get_res_id(),
|
||||
default_res_model: this.model,
|
||||
}, {'mark_done': true}, this.context),
|
||||
res_id: activity_id,
|
||||
}
|
||||
this.do_action(action, {
|
||||
on_close: function () {
|
||||
self.fetch_and_render_value();
|
||||
self.chatter.refresh_followers();
|
||||
ChatManager.get_messages({model: self.res_model, res_id: self.get_res_id()});
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
on_activity_unlink: function (event) {
|
||||
event.preventDefault();
|
||||
var self = this;
|
||||
var activity_id = this.$(event.currentTarget).data('activity-id');
|
||||
this.Activity
|
||||
.call("unlink", [[activity_id]])
|
||||
.then(function (res) {
|
||||
self.fetch_and_render_value();
|
||||
});
|
||||
},
|
||||
|
||||
on_activity_done: function (event) {
|
||||
event.preventDefault();
|
||||
var self = this;
|
||||
var activity_id = this.$(event.currentTarget).data('activity-id');
|
||||
this.Activity
|
||||
.call("action_done", [[activity_id]])
|
||||
.then(function (msg_id) {
|
||||
self.fetch_and_render_value();
|
||||
|
||||
self.chatter.msg_ids.unshift(msg_id);
|
||||
|
||||
// to stop scrollbar flickering add min height of the thread and remove after
|
||||
// render. on render thread it will remove and add all it's element which cause flickering
|
||||
var thread = self.chatter.thread;
|
||||
thread.$el.css('min-height', thread.$el.height());
|
||||
self.chatter.fetch_and_render_thread(self.chatter.msg_ids).then(function () {
|
||||
thread.$el.css('min-height', '');
|
||||
});
|
||||
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
core.form_widget_registry.add('mail_activity', Activity);
|
||||
|
||||
return {
|
||||
Activity: Activity,
|
||||
set_delay_label: set_delay_label,
|
||||
};
|
||||
|
||||
});
|
||||
@@ -10,7 +10,9 @@ var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var form_common = require('web.form_common');
|
||||
var framework = require('web.framework');
|
||||
var Model = require("web.Model");
|
||||
var web_utils = require('web.utils');
|
||||
var Widget = require('web.Widget');
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
@@ -278,8 +280,9 @@ var Chatter = form_common.AbstractField.extend({
|
||||
template: 'mail.Chatter',
|
||||
|
||||
events: {
|
||||
"click .o_chatter_button_new_message": "on_open_composer_new_message",
|
||||
"click .o_chatter_button_log_note": "on_open_composer_log_note",
|
||||
'click .o_chatter_button_new_message': 'on_open_composer_new_message',
|
||||
'click .o_chatter_button_log_note': 'on_open_composer_log_note',
|
||||
'click .o_activity_schedule': 'on_activity_schedule',
|
||||
},
|
||||
|
||||
init: function () {
|
||||
@@ -288,6 +291,10 @@ var Chatter = form_common.AbstractField.extend({
|
||||
this.res_id = undefined;
|
||||
this.context = this.options.context || {};
|
||||
this.dp = new web_utils.DropPrevious();
|
||||
// extends display options
|
||||
this.options = _.extend(this.options || {}, {
|
||||
'display_activity_button': this.field_manager.fields.activity_ids
|
||||
});
|
||||
},
|
||||
|
||||
willStart: function () {
|
||||
@@ -315,6 +322,12 @@ var Chatter = form_common.AbstractField.extend({
|
||||
this.followers.on('followers_update', this, this.on_followers_update);
|
||||
}
|
||||
|
||||
// Move the activities widget (if any) inside the chatter
|
||||
this.activities = this.field_manager.fields.activity_ids
|
||||
if (this.activities) {
|
||||
this.$('.o_chatter_topbar').after(this.activities.$el);
|
||||
}
|
||||
|
||||
this.thread = new ChatThread(this, {
|
||||
display_order: ChatThread.ORDER.DESC,
|
||||
display_document_link: false,
|
||||
@@ -476,6 +489,13 @@ var Chatter = form_common.AbstractField.extend({
|
||||
this.followers.read_value();
|
||||
}
|
||||
},
|
||||
|
||||
on_activity_schedule: function (event) {
|
||||
if (this.activities) {
|
||||
this.activities.on_activity_schedule(event);
|
||||
}
|
||||
},
|
||||
|
||||
// composer toggle
|
||||
on_open_composer_new_message: function () {
|
||||
this.open_composer();
|
||||
@@ -501,7 +521,7 @@ var Chatter = form_common.AbstractField.extend({
|
||||
this.composer.on('input_focused', this, function () {
|
||||
this.composer.mention_set_prefetched_partners(this.mention_suggestions || []);
|
||||
});
|
||||
this.composer.insertBefore(this.$('.o_mail_thread')).then(function () {
|
||||
this.composer.insertAfter(this.$('.o_chatter_topbar')).then(function () {
|
||||
// destroy existing composer
|
||||
if (old_composer) {
|
||||
old_composer.destroy();
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
odoo.define('web.kanban.activity', function (require) {
|
||||
"use strict";
|
||||
|
||||
var core = require('web.core');
|
||||
var Model = require("web.Model");
|
||||
var Widget = require('web.Widget');
|
||||
var kanban_widgets = require('web_kanban.widgets');
|
||||
var AbstractField = kanban_widgets.AbstractField;
|
||||
var fields_registry = kanban_widgets.registry;
|
||||
var set_delay_label = require('mail.activity').set_delay_label;
|
||||
|
||||
var QWeb = core.qweb;
|
||||
|
||||
|
||||
var KanbanActivity = AbstractField.extend({
|
||||
template: 'mail.KanbanActivity',
|
||||
|
||||
events: {
|
||||
"click .o_activity_btn": "get_activity_logs",
|
||||
"click .o_schedule_activity": "on_activity_schedule",
|
||||
"click .o_mark_as_done": "on_activity_done",
|
||||
},
|
||||
|
||||
init: function(parent, field, $node) {
|
||||
this._super.apply(this, arguments);
|
||||
this.parent = parent;
|
||||
this.activity_state = parent.record.activity_state.raw_value;
|
||||
this.selection = _.object(parent.fields.activity_state.selection);
|
||||
this.Activity = new Model("mail.activity");
|
||||
},
|
||||
|
||||
get_activity_logs: function(event) {
|
||||
event.preventDefault();
|
||||
var self = this;
|
||||
this.$('.o_activity').html(QWeb.render("mail.KanbanActivityLoading"));
|
||||
this.Activity
|
||||
.call("read", [this.field.raw_value])
|
||||
.then(function (records) {
|
||||
records = set_delay_label(records);
|
||||
self.$('.o_activity').html($(QWeb.render("mail.KanbanActivityDropdown", {
|
||||
selection: self.selection,
|
||||
records: _.groupBy(records, 'state'),
|
||||
uid: self.session.uid,
|
||||
})));
|
||||
});
|
||||
},
|
||||
|
||||
reset_state: function () {
|
||||
var self = this;
|
||||
new Model(self.parent.model).query(['activity_ids', 'activity_state'])
|
||||
.filter([['id', '=', self.parent.id]])
|
||||
.first()
|
||||
.then(function (record) {
|
||||
var state_color_class = 'o_activity_color_' + (record.activity_state || 'default');
|
||||
var current_state_class = 'o_activity_color_' + (self.activity_state || 'default');
|
||||
self.$(".o_activity_btn > span").removeClass(current_state_class).addClass(state_color_class);
|
||||
self.activity_state = record.activity_state;
|
||||
self.parent.record.activity_ids.raw_value = record.activity_ids;
|
||||
});
|
||||
},
|
||||
|
||||
on_activity_schedule: function(event) {
|
||||
var self = this;
|
||||
var activity_id = this.$(event.currentTarget).data('activity-id') || false;
|
||||
var action = {
|
||||
type: 'ir.actions.act_window',
|
||||
res_model: 'mail.activity',
|
||||
view_mode: 'form',
|
||||
view_type: 'form',
|
||||
views: [
|
||||
[false, 'form']
|
||||
],
|
||||
target: 'new',
|
||||
context: _.extend({
|
||||
default_res_id: this.parent.id,
|
||||
default_res_model: this.parent.model,
|
||||
}, {'mark_done': true}, this.context),
|
||||
res_id: activity_id,
|
||||
};
|
||||
this.do_action(action, {
|
||||
on_close: function() {
|
||||
self.reset_state();
|
||||
self.get_activity_logs(event);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
on_activity_done: function (event) {
|
||||
event.stopPropagation();
|
||||
var self = this;
|
||||
var activity_id = this.$(event.currentTarget).data('activity-id');
|
||||
this.Activity
|
||||
.call("action_done", [[activity_id]])
|
||||
.then(function (result) {
|
||||
self.get_activity_logs(event);
|
||||
self.reset_state();
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
fields_registry.add('kanban_activity', KanbanActivity);
|
||||
|
||||
return {
|
||||
KanbanActivity: KanbanActivity
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,105 @@
|
||||
/* Common */
|
||||
.o_mail_activity {
|
||||
&.o_form_field {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.o_thread_date_separator.o_border_dashed {
|
||||
border-bottom: dashed #eeeeee 2px;
|
||||
}
|
||||
|
||||
.o_thread_message_core.o_mail_note {
|
||||
padding: 0px 3px;
|
||||
}
|
||||
|
||||
.o_activity_color_default {
|
||||
color: @gray;
|
||||
}
|
||||
|
||||
.o_activity_color_planned {
|
||||
color: @brand-primary;
|
||||
}
|
||||
.o_activity_color_overdue {
|
||||
color: @brand-danger;
|
||||
}
|
||||
.o_activity_color_today {
|
||||
color: @brand-success;
|
||||
}
|
||||
|
||||
.o_activity_link {
|
||||
color: @gray;
|
||||
&:hover {
|
||||
color: @brand-success;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Kanban View */
|
||||
.o_kanban_record {
|
||||
.o_kanban_inline_block {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.o_mail_activity {
|
||||
.o_activity_btn {
|
||||
span.fa {
|
||||
vertical-align: -35%;
|
||||
}
|
||||
}
|
||||
|
||||
.o_activity_link_kanban {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
ul.o_activity {
|
||||
width: 290px;
|
||||
|
||||
& > li:last-child {
|
||||
background-color: @odoo-brand-lightsecondary;
|
||||
color: @gray-dark;
|
||||
line-height: 3em;
|
||||
}
|
||||
li > ul.o_activity_log {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
color: @text-color;
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
display: inline-block;
|
||||
|
||||
&.o_activity_label {
|
||||
line-height: 2.5em;
|
||||
padding: 0px 5px;
|
||||
background-color: @odoo-brand-lightsecondary;
|
||||
}
|
||||
|
||||
&.o_schedule_activity {
|
||||
padding: 8px 10px 8px 0px;
|
||||
vertical-align: middle;
|
||||
&:hover {
|
||||
background-color: @gray-lighter;
|
||||
.o_activity_link_kanban {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
|
||||
.o_activity_title {
|
||||
width: 85%;
|
||||
padding: 0px 5px 0px 10px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.o_no_activity {
|
||||
padding: 10px;
|
||||
cursor: initial;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
@mail-thread-side-date-opacity: 0.6;
|
||||
@mail-thread-note: @gray-lighter;
|
||||
|
||||
.o_mail_thread {
|
||||
.o_mail_thread, .o_mail_activity {
|
||||
.o_thread_show_more {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
<t t-name="mail.activity_items">
|
||||
<div t-if="activities.length" class="o_thread_date_separator o_border_dashed">
|
||||
<span class="o_thread_date">Planned</span>
|
||||
</div>
|
||||
<t t-foreach="activities" t-as="activity">
|
||||
<div class="o_thread_message">
|
||||
<div class="o_thread_message_sidebar">
|
||||
<img t-attf-src="/web/image/res.users/#{activity.user_id[0]}/image_small"
|
||||
class="o_thread_message_avatar mb8"/>
|
||||
<a href="#" class="o_activity_done o_activity_link" t-att-data-activity-id="activity.id"><span class="fa fa-check-circle fa-2x" /></a>
|
||||
</div>
|
||||
<div class="o_thread_message_core o_mail_note">
|
||||
<div class="o_mail_info">
|
||||
<strong>
|
||||
<span t-attf-class="fa #{activity.icon}" />
|
||||
<t t-esc="activity.activity_type_id[1]" />
|
||||
</strong> planned by <strong><t t-esc="activity.user_id[1]" /></strong>
|
||||
<span class="fa fa-clock-o ml8" />
|
||||
<span t-attf-class="o_activity_date o_activity_color_#{activity.state}"><t t-esc="activity.label_delay" /></span>
|
||||
<div class="pull-right">
|
||||
<a href="#" class="o_activity_edit o_activity_link" t-att-data-activity-id="activity.id">Edit</a> |
|
||||
<a href="#" class="o_activity_unlink o_activity_link" t-att-data-activity-id="activity.id">Remove</a>
|
||||
</div>
|
||||
</div>
|
||||
<div t-if="activity.summary">
|
||||
<strong><i><t t-esc="activity.summary"/></i></strong>
|
||||
</div>
|
||||
<div t-if="activity.note">
|
||||
<t t-raw="activity.note"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
</t>
|
||||
</templates>
|
||||
@@ -59,6 +59,9 @@
|
||||
<button t-if="widget.options.display_log_button" class="btn btn-sm btn-link o_chatter_button_log_note" title="Log a note. Followers will not be notified.">
|
||||
Log an internal note
|
||||
</button>
|
||||
<button t-if="widget.options.display_activity_button" class="btn btn-sm btn-link o_activity_schedule" title="Schedule an activity">
|
||||
Schedule an Activity
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<templates xml:space="preserve">
|
||||
|
||||
<t t-name="mail.KanbanActivity">
|
||||
<div class="o_kanban_inline_block dropdown o_kanban_selection o_mail_activity">
|
||||
<a class="dropdown-toggle o_activity_btn" data-toggle="dropdown">
|
||||
<span t-attf-class="fa o_activity_color_#{widget.activity_state or 'default'} fa-chevron-circle-right fa-lg"/>
|
||||
</a>
|
||||
<ul class="dropdown-menu o_activity" role="menu">
|
||||
</ul>
|
||||
</div>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.KanbanActivityLoading">
|
||||
<li class="text-center o_no_activity">
|
||||
<span class="fa fa-spinner fa-spin fa-2x" />
|
||||
</li>
|
||||
</t>
|
||||
|
||||
<t t-name="mail.KanbanActivityDropdown">
|
||||
<li t-if="_.isEmpty(records)" class="text-center o_no_activity">
|
||||
<span>No activities planned.</span>
|
||||
</li>
|
||||
<li t-if="!_.isEmpty(records)">
|
||||
<ul class="o_activity_log">
|
||||
<t t-foreach="_.keys(records)" t-as="key">
|
||||
<t t-set="logs" t-value="records[key]" />
|
||||
<li class="o_activity_label">
|
||||
<strong t-attf-class="o_activity_color_#{key}">
|
||||
<t t-esc="selection[key]" /> (<span t-esc="logs.length" />)
|
||||
</strong>
|
||||
</li>
|
||||
<li t-foreach="logs" t-as="log" t-att-data-activity-id="log.id" class="o_schedule_activity">
|
||||
<div class="o_activity_title pull-left">
|
||||
<span t-attf-class="fa #{log.icon} fa-fw" />
|
||||
<strong>
|
||||
<t t-esc="log.title_action or log.activity_type_id[1]" />
|
||||
</strong>
|
||||
<div>
|
||||
<span class="fa fa-clock-o fa-fw" />
|
||||
<span t-att-title="log.date_deadline"><t t-esc="log.label_delay" /></span>
|
||||
<t t-if="log.user_id[0] != uid">
|
||||
<span class="ml4 fa fa-user" />
|
||||
<span><t t-esc="log.user_id[1]" /></span>
|
||||
</t>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pull-right">
|
||||
<span class="o_mark_as_done o_activity_link o_activity_link_kanban fa fa-check-circle fa-2x mt4" t-att-data-activity-id="log.id" title="Mark as done" />
|
||||
</div>
|
||||
</li>
|
||||
</t>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="o_schedule_activity text-center">
|
||||
<strong>Schedule an activity</strong>
|
||||
</li>
|
||||
</t>
|
||||
|
||||
</templates>
|
||||
@@ -79,6 +79,22 @@ class TestMailFollowers(TestMail):
|
||||
self.assertEqual(len(follower), 1)
|
||||
self.assertEqual(follower.subtype_ids, self.default_group_subtypes)
|
||||
|
||||
def test_followers_subtypes_default_internal(self):
|
||||
mt_mg_def_int = self.env['mail.message.subtype'].create({'name': 'mt_mg_def', 'default': True, 'res_model': 'mail.channel', 'internal': True})
|
||||
self.group_pigs.message_subscribe_users(user_ids=[self.user_employee.id])
|
||||
follower = self.env['mail.followers'].search([
|
||||
('res_model', '=', 'mail.channel'),
|
||||
('res_id', '=', self.group_pigs.id),
|
||||
('partner_id', '=', self.user_employee.partner_id.id)])
|
||||
self.assertEqual(follower.subtype_ids, self.default_group_subtypes | mt_mg_def_int)
|
||||
|
||||
self.group_pigs.message_subscribe_users(user_ids=[self.user_portal.id])
|
||||
follower = self.env['mail.followers'].search([
|
||||
('res_model', '=', 'mail.channel'),
|
||||
('res_id', '=', self.group_pigs.id),
|
||||
('partner_id', '=', self.user_portal.partner_id.id)])
|
||||
self.assertEqual(follower.subtype_ids, self.default_group_subtypes.filtered(lambda subtype: not subtype.internal))
|
||||
|
||||
def test_followers_subtypes_specified(self):
|
||||
self.group_pigs.sudo(self.user_employee).message_subscribe_users(subtype_ids=[self.mt_mg_nodef.id])
|
||||
self.assertEqual(self.group_pigs.message_follower_ids.mapped('partner_id'), self.user_employee.partner_id)
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
<?xml version="1.0"?>
|
||||
<odoo>
|
||||
<record id="mail_activity_type_view_form" model="ir.ui.view">
|
||||
<field name="name">mail.activity.type.view.form</field>
|
||||
<field name="model">mail.activity.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Activities">
|
||||
<sheet>
|
||||
<div class="oe_title">
|
||||
<label for="name" class="oe_edit_only"/>
|
||||
<h1><field name="name"/></h1>
|
||||
</div>
|
||||
<group>
|
||||
<group>
|
||||
<field name="res_model_id"/>
|
||||
<field name="icon" groups="base.group_no_one"/>
|
||||
<field name="days"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="summary"/>
|
||||
</group>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_activity_type_view_tree" model="ir.ui.view">
|
||||
<field name="name">mail.activity.type.view.tree</field>
|
||||
<field name="model">mail.activity.type</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Activities">
|
||||
<field name="sequence" widget="handle"/>
|
||||
<field name="name"/>
|
||||
<field name="summary"/>
|
||||
<field name="days"/>
|
||||
<field name="res_model_id" groups="base.group_no_one"/>
|
||||
<field name="icon" groups="base.group_no_one"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_activity_type_action" model="ir.actions.act_window">
|
||||
<field name="name">Activities</field>
|
||||
<field name="res_model">mail.activity.type</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_activity_view_form_popup" model="ir.ui.view">
|
||||
<field name="name">mail.activity.view.form.popup</field>
|
||||
<field name="model">mail.activity</field>
|
||||
<field name="priority">20</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Log an Activity" create="false">
|
||||
<sheet string="Activity">
|
||||
<group>
|
||||
<field name="res_model" invisible="1"/>
|
||||
<field name="res_model_id" invisible="1"/>
|
||||
<field name="res_id" invisible="1"/>
|
||||
<group>
|
||||
<field name="activity_type_id" required="1" options="{'no_create': True, 'no_open': True}"/>
|
||||
<field name="summary" placeholder="e.g. Discuss proposal"/>
|
||||
</group>
|
||||
<group>
|
||||
<field name="date_deadline"/>
|
||||
<field name="user_id"/>
|
||||
</group>
|
||||
</group>
|
||||
<field name="note" placeholder="Log a note..."/>
|
||||
<footer>
|
||||
<button string="Schedule" name="action_close_dialog"
|
||||
type="object" class="btn-primary"/>
|
||||
<button string="Mark as Done" name="action_done"
|
||||
type="object" class="btn-primary"/>
|
||||
<button string="Discard" class="btn-default" special="cancel" />
|
||||
</footer>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_activity_view_search" model="ir.ui.view">
|
||||
<field name="name">mail.activity.view.search</field>
|
||||
<field name="model">mail.activity</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Activity">
|
||||
<field name="res_model_id"/>
|
||||
<field name="summary"/>
|
||||
<field name="activity_type_id"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('create_uid', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue Activities" name="activities_overdue"
|
||||
domain="[('date_deadline', '<', context_today().strftime('%Y-%m-%d'))]"
|
||||
help="Show all records which has next action date is before today"/>
|
||||
<separator/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Due Date" context="{'group_by': 'date_deadline'}"/>
|
||||
<filter string="Created By" context="{'group_by': 'create_uid'}"/>
|
||||
<filter string="Activity Type" context="{'group_by': 'activity_type_id'}"/>
|
||||
</group>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_activity_view_tree" model="ir.ui.view">
|
||||
<field name="name">mail.activity.view.tree</field>
|
||||
<field name="model">mail.activity</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Next Activities"
|
||||
decoration-danger="date_deadline < current_date"
|
||||
decoration-success="date_deadline == current_date"
|
||||
default_order="date_deadline" create="false">
|
||||
<field name="res_name"/>
|
||||
<field name="activity_type_id"/>
|
||||
<field name="summary"/>
|
||||
<field name="date_deadline"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="mail_activity_view_calendar" model="ir.ui.view">
|
||||
<field name="name">mail.activity.view.calendar</field>
|
||||
<field name="model">mail.activity</field>
|
||||
<field name="priority" eval="2"/>
|
||||
<field name="arch" type="xml">
|
||||
<calendar string="Activity" date_start="date_deadline" color="activity_type_id">
|
||||
<field name="res_name"/>
|
||||
<field name="create_uid"/>
|
||||
<field name="summary"/>
|
||||
</calendar>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
@@ -17,6 +17,8 @@
|
||||
<script type="text/javascript" src="/mail/static/src/js/tour.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/utils.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/window_manager.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/activity.js"></script>
|
||||
<script type="text/javascript" src="/mail/static/src/js/web_kanban_activity.js"></script>
|
||||
|
||||
<link rel="stylesheet" href="/mail/static/src/less/announcement.less"/>
|
||||
<link rel="stylesheet" href="/mail/static/src/less/client_action.less" type="text/less"/>
|
||||
@@ -27,6 +29,7 @@
|
||||
<link rel="stylesheet" href="/mail/static/src/less/followers.less" type="text/less"/>
|
||||
<link rel="stylesheet" href="/mail/static/src/less/thread.less" type="text/less"/>
|
||||
<link rel="stylesheet" href="/mail/static/src/less/systray.less" type="text/less"/>
|
||||
<link rel="stylesheet" href="/mail/static/src/less/mail_activity.less"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
@@ -50,5 +53,30 @@
|
||||
<p style="color:#9E588B;">Powered by <a href="https://www.odoo.com">Odoo</a>.</p>
|
||||
</template>
|
||||
|
||||
<template id="message_activity_done">
|
||||
<div>
|
||||
<p>
|
||||
<strong><span t-attf-class="fa #{activity.activity_type_id.icon}"/></strong> <span t-field="activity.activity_type_id.name"/> done
|
||||
by <span t-field="activity.user_id.name"/>:
|
||||
<span t-if="activity.summary" t-field="activity.summary"/>
|
||||
</p>
|
||||
<div t-field="activity.note"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<template id="message_activity_assigned">
|
||||
<div>
|
||||
<p>
|
||||
Planned <span t-field="activity.activity_type_id.name"/> related to <t t-esc="object.name_get()[0][1]"/>:
|
||||
<soan t-if="activity.summary" t-field="activity.summary"/>
|
||||
</p>
|
||||
<ul>
|
||||
<li>Scheduled for: <span t-field="activity.date_deadline"/></li>
|
||||
<li>Assigned to: <span t-field="activity.user_id.name"/></li>
|
||||
</ul>
|
||||
<div t-if="activity.note" t-field="activity.note"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</data>
|
||||
</odoo>
|
||||
|
||||
@@ -21,6 +21,18 @@
|
||||
<field name="alias_domain" placeholder="mycompany.odoo.com" class="oe_inline"/>
|
||||
</div>
|
||||
</xpath>
|
||||
<xpath expr="//group[@name='authentication']" position='before'>
|
||||
<group name="activity">
|
||||
<label for="id" string="Activities"/>
|
||||
<div>
|
||||
<div>
|
||||
<button class="oe_link" name="%(mail_activity_type_action)d" type="action">
|
||||
<span>Configure the chatter activities</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</group>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</data>
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<odoo>
|
||||
<data>
|
||||
|
||||
<record id="view_emails_partner_info_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.emails.info.inherit</field>
|
||||
<record id="res_partner_view_form_inherit_mail" model="ir.ui.view">
|
||||
<field name="name">res.partner.view.form.inherit.mail</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||
<field name="arch" type="xml">
|
||||
@@ -15,14 +15,15 @@
|
||||
<xpath expr="//sheet" position="after">
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers"/>
|
||||
<field name="message_ids" widget="mail_thread" options='{"thread_level": 1}'/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="res_partner_opt_out_search">
|
||||
<field name="name">res.partner.opt_out.search</field>
|
||||
<record id="res_partner_view_search_inherit_mail" model="ir.ui.view">
|
||||
<field name="name">res.partner.view.search.inherit.mail</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="base.view_res_partner_filter"/>
|
||||
<field name="arch" type="xml">
|
||||
@@ -31,6 +32,19 @@
|
||||
<filter string="Available for mass mailing"
|
||||
name='not_opt_out' domain="[('opt_out', '=', False)]"
|
||||
help="Partners that did not ask not to be included in mass mailing campaigns" />
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<separator/>
|
||||
</filter>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -293,6 +293,7 @@ class MailComposer(models.TransientModel):
|
||||
'record_name': self.record_name,
|
||||
'no_auto_thread': self.no_auto_thread,
|
||||
'mail_server_id': self.mail_server_id.id,
|
||||
'mail_activity_type_id': self.mail_activity_type_id.id,
|
||||
}
|
||||
# mass mailing: rendering override wizard static values
|
||||
if mass_mail_mode and self.model:
|
||||
|
||||
@@ -89,7 +89,7 @@ class MaintenanceEquipmentCategory(models.Model):
|
||||
|
||||
class MaintenanceEquipment(models.Model):
|
||||
_name = 'maintenance.equipment'
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = 'Equipment'
|
||||
|
||||
@api.multi
|
||||
@@ -205,7 +205,7 @@ class MaintenanceEquipment(models.Model):
|
||||
|
||||
class MaintenanceRequest(models.Model):
|
||||
_name = 'maintenance.request'
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = 'Maintenance Requests'
|
||||
_order = "id desc"
|
||||
|
||||
|
||||
@@ -25,6 +25,18 @@
|
||||
<separator/>
|
||||
<filter string="My Requests" name="my_requests" domain="[('owner_user_id', '=', uid)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<separator/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
@@ -87,6 +99,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>
|
||||
@@ -107,6 +120,8 @@
|
||||
<field name="category_id"/>
|
||||
<field name="kanban_state"/>
|
||||
<field name="message_needaction_counter"/>
|
||||
<field name="activity_ids" />
|
||||
<field name="activity_state" />
|
||||
<templates>
|
||||
<t t-name="kanban-tooltip">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
@@ -140,11 +155,15 @@
|
||||
<span t-if="record.category_id.raw_value"><field name="category_id"/></span>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<div class="o_kanban_inline_block ml4 mr4">
|
||||
<field name="activity_ids" widget="kanban_activity" />
|
||||
</div>
|
||||
<field name="kanban_state" widget="kanban_state_selection"/>
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.owner_user_id.raw_value)" t-att-title="record.owner_user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
|
||||
<div class="pull-left" groups="base.group_user">
|
||||
<field name="kanban_state" widget="kanban_state_selection"/>
|
||||
<div class="pull-left mr4 mt4">
|
||||
<field name="priority" widget="priority"/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="oe_clear"></div>
|
||||
@@ -336,6 +355,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>
|
||||
@@ -358,6 +378,8 @@
|
||||
<field name="maintenance_open_count"/>
|
||||
<field name="next_action_date"/>
|
||||
<field name="technician_user_id"/>
|
||||
<field name="activity_ids" />
|
||||
<field name="activity_state" />
|
||||
<templates>
|
||||
<t t-name="kanban-tooltip">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
@@ -386,6 +408,9 @@
|
||||
<div t-if="record.owner_user_id.raw_value"><field name="owner_user_id"/></div>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<div class="o_kanban_inline_block">
|
||||
<field name="activity_ids" widget="kanban_activity" />
|
||||
</div>
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.owner_user_id.raw_value)" t-att-title="record.owner_user_id.value" width="27" height="27" class="oe_kanban_avatar"/>
|
||||
</div>
|
||||
<div class="oe_kanban_footer_left mt4 label label-danger" t-if="record.maintenance_open_count.raw_value" >
|
||||
@@ -438,6 +463,17 @@
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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="Archived" name="inactive" domain="[('active','=',False)]"/> -->
|
||||
<group expand='0' string='Group by...'>
|
||||
<filter string='Technicians' domain="[]" context="{'group_by': 'technician_user_id'}"/>
|
||||
|
||||
@@ -14,7 +14,7 @@ class MrpProduction(models.Model):
|
||||
_name = 'mrp.production'
|
||||
_description = 'Manufacturing Order'
|
||||
_date_name = 'date_planned_start'
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'date_planned_start asc,id'
|
||||
|
||||
@api.model
|
||||
|
||||
@@ -8,7 +8,7 @@ from odoo.exceptions import UserError
|
||||
class MrpUnbuild(models.Model):
|
||||
_name = "mrp.unbuild"
|
||||
_description = "Unbuild Order"
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = 'id desc'
|
||||
|
||||
def _get_default_location_id(self):
|
||||
|
||||
@@ -162,6 +162,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>
|
||||
@@ -266,6 +267,18 @@
|
||||
<field name="product_id" string="Raw Material" filter_domain="[('move_raw_ids.product_id','ilike',self)]"/>
|
||||
<field name="name" string="Work Center" filter_domain="[('routing_id.operation_ids.workcenter_id','ilike',self)]"/>
|
||||
<field name="routing_id" groups="mrp.group_mrp_routings"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By...">
|
||||
<filter string="Product" domain="[]" context="{'group_by':'product_id'}"/>
|
||||
<filter string="Routing" domain="[]" context="{'group_by':'routing_id'}"/>
|
||||
|
||||
@@ -22,6 +22,18 @@
|
||||
<group expand="0" string="Filters">
|
||||
<filter name="draft" string="Draft" domain="[('state', '=', 'draft')]"/>
|
||||
<filter name="done" string="Done" domain="[('state', '=', 'done')]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
</group>
|
||||
<group expand='0' string='Group by...'>
|
||||
<filter string='Product' name="productgroup" context="{'group_by': 'product_id'}"/>
|
||||
@@ -83,6 +95,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>
|
||||
|
||||
@@ -11,7 +11,7 @@ from odoo.exceptions import UserError
|
||||
class Repair(models.Model):
|
||||
_name = 'mrp.repair'
|
||||
_description = 'Repair Order'
|
||||
_inherit = 'mail.thread'
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
@api.model
|
||||
def _default_stock_location(self):
|
||||
|
||||
@@ -192,6 +192,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>
|
||||
@@ -214,6 +215,18 @@
|
||||
<filter string="Invoiced" domain="[('invoiced','=',True)]"/>
|
||||
<field name="product_id"/>
|
||||
<field name="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Partner" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Product" domain="[]" context="{'group_by':'product_id'}"/>
|
||||
|
||||
@@ -33,7 +33,7 @@ class Tag(models.Model):
|
||||
class Note(models.Model):
|
||||
|
||||
_name = 'note.note'
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = "Note"
|
||||
_order = 'sequence'
|
||||
|
||||
|
||||
@@ -144,6 +144,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>
|
||||
@@ -160,6 +161,18 @@
|
||||
<field name="tag_ids"/>
|
||||
<filter name="open_true" string="Active" domain="[('open', '=', True)]"/>
|
||||
<filter name="open_false" string="Archive" domain="[('open', '=', False)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Stage" help="By sticky note Category" context="{'group_by':'stage_id'}"/>
|
||||
</group>
|
||||
|
||||
@@ -288,7 +288,7 @@ class Task(models.Model):
|
||||
_name = "project.task"
|
||||
_description = "Task"
|
||||
_date_name = "date_start"
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_mail_post_access = 'read'
|
||||
_order = "priority desc, sequence, date_start, name, id"
|
||||
|
||||
|
||||
@@ -34,6 +34,18 @@
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Project" name="project" context="{'group_by':'project_id'}"/>
|
||||
<filter string="Task" context="{'group_by':'name'}"/>
|
||||
@@ -399,6 +411,7 @@ action = model.activate_sample_project()
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -428,6 +441,8 @@ action = model.activate_sample_project()
|
||||
<field name="legend_blocked"/>
|
||||
<field name="legend_normal"/>
|
||||
<field name="legend_done"/>
|
||||
<field name="activity_ids"/>
|
||||
<field name="activity_state"/>
|
||||
<templates>
|
||||
<t t-name="kanban-box">
|
||||
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
|
||||
@@ -463,8 +478,9 @@ action = model.activate_sample_project()
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
|
||||
<div class="pull-left" groups="base.group_user">
|
||||
<field name="kanban_state" widget="kanban_state_selection"/>
|
||||
<div class="pull-left mr4">
|
||||
<field name="activity_ids" widget="kanban_activity"/>
|
||||
<field name="kanban_state" widget="kanban_state_selection" groups="base.group_user"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -496,6 +512,21 @@ action = model.activate_sample_project()
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_task_view_tree_activity" model="ir.ui.view">
|
||||
<field name="name">project.task.tree.activity</field>
|
||||
<field name="model">project.task</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Next Activities" decoration-danger="activity_date_deadline < current_date" default_order="activity_date_deadline">
|
||||
<field name="name"/>
|
||||
<field name="project_id"/>
|
||||
<field name="activity_date_deadline"/>
|
||||
<field name="activity_type_id"/>
|
||||
<field name="activity_summary"/>
|
||||
<field name="stage_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_task_calendar" model="ir.ui.view">
|
||||
<field name="name">project.task.calendar</field>
|
||||
<field name="model">project.task</field>
|
||||
@@ -572,6 +603,21 @@ action = model.activate_sample_project()
|
||||
<field name="act_window_id" ref="action_view_task"/>
|
||||
</record>
|
||||
|
||||
<record id="project_task_action_activity" model="ir.actions.act_window">
|
||||
<field name="name">My Next Task Activities</field>
|
||||
<field name="res_model">project.task</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,kanban,calendar</field>
|
||||
<field name="view_id" ref="project_task_view_tree_activity"/>
|
||||
<field name="context">{'search_default_activities_my':1}</field>
|
||||
<field name="domain">[('activity_date_deadline', '!=', False)]</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Currently there are not any activity scheduled.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem action="action_view_task" id="menu_action_view_task" parent="project.menu_project_management" sequence="5"/>
|
||||
|
||||
<record id="project_task_action_from_partner" model="ir.actions.act_window">
|
||||
@@ -581,6 +627,12 @@ action = model.activate_sample_project()
|
||||
<field name="search_view_id" ref="view_task_search_form"/>
|
||||
</record>
|
||||
|
||||
<menuitem id="project_task_menu_activity"
|
||||
name="Next Task Activities"
|
||||
parent="project.menu_project_management"
|
||||
action="project_task_action_activity"
|
||||
sequence="25"/>
|
||||
|
||||
<record id="action_view_task_overpassed_draft" model="ir.actions.act_window">
|
||||
<field name="name">Overpassed Tasks</field>
|
||||
<field name="res_model">project.task</field>
|
||||
|
||||
@@ -9,7 +9,7 @@ from odoo.tools.safe_eval import safe_eval
|
||||
class ProjectIssue(models.Model):
|
||||
_name = "project.issue"
|
||||
_description = "Project Issue"
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_order = "priority desc, create_date desc"
|
||||
_mail_post_access = 'read'
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@
|
||||
</sheet>
|
||||
<div class="oe_chatter">
|
||||
<field name="message_follower_ids" widget="mail_followers" groups="base.group_user"/>
|
||||
<field name="activity_ids" widget="mail_activity"/>
|
||||
<field name="message_ids" widget="mail_thread"/>
|
||||
</div>
|
||||
</form>
|
||||
@@ -86,6 +87,21 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_issue_view_tree_activity" model="ir.ui.view">
|
||||
<field name="name">project.issue.view.tree.activity</field>
|
||||
<field name="model">project.issue</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree string="Next Activities" decoration-danger="activity_date_deadline < current_date" default_order="activity_date_deadline">
|
||||
<field name="name"/>
|
||||
<field name="project_id"/>
|
||||
<field name="activity_date_deadline"/>
|
||||
<field name="activity_type_id"/>
|
||||
<field name="activity_summary"/>
|
||||
<field name="stage_id"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="view_project_issue_filter" model="ir.ui.view">
|
||||
<field name="name">project.issue.search.view</field>
|
||||
<field name="model">project.issue</field>
|
||||
@@ -105,6 +121,18 @@
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By" >
|
||||
<filter string="Assigned to" name="Responsible" context="{'group_by':'user_id'}" />
|
||||
<filter string="Project" name="project" context="{'group_by':'project_id'}" />
|
||||
@@ -150,6 +178,8 @@
|
||||
<field name="legend_blocked"/>
|
||||
<field name="legend_done"/>
|
||||
<field name="legend_normal"/>
|
||||
<field name="activity_ids" />
|
||||
<field name="activity_state" />
|
||||
<templates>
|
||||
<t t-name="kanban-tooltip">
|
||||
<ul class="oe_kanban_tooltip">
|
||||
@@ -182,6 +212,9 @@
|
||||
</t>
|
||||
</div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<div class="o_kanban_inline_block ml4 mr4">
|
||||
<field name="activity_ids" widget="kanban_activity" />
|
||||
</div>
|
||||
<img t-att-src="kanban_image('res.users', 'image_small', record.user_id.raw_value)" t-att-title="record.user_id.value" width="24" height="24" class="oe_kanban_avatar pull-right"/>
|
||||
<div class="pull-left" groups="base.group_user">
|
||||
<field name="kanban_state" widget="kanban_state_selection"/>
|
||||
@@ -315,6 +348,28 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="project_issue_action_activity" model="ir.actions.act_window">
|
||||
<field name="name">My Next Issue Activities</field>
|
||||
<field name="res_model">project.issue</field>
|
||||
<field name="view_type">form</field>
|
||||
<field name="view_mode">tree,form,kanban,calendar</field>
|
||||
<field name="view_id" ref="project_issue_view_tree_activity"/>
|
||||
<field name="context">{'search_default_activities_my':1}</field>
|
||||
<field name="domain">[('activity_date_deadline', '!=', False)]</field>
|
||||
<field name="help" type="html">
|
||||
<p>
|
||||
Currently there are not any activity scheduled.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<menuitem id="menu_project_issue_track" name="Issues" parent="project.menu_project_management"
|
||||
action="project_issue_categ_act0" sequence="15"/>
|
||||
|
||||
|
||||
<menuitem id="project_issue_menu_activity"
|
||||
name="Next Issue Activities"
|
||||
parent="project.menu_project_management"
|
||||
action="project_issue_action_activity"
|
||||
sequence="25"/>
|
||||
</odoo>
|
||||
|
||||
@@ -15,7 +15,7 @@ import odoo.addons.decimal_precision as dp
|
||||
|
||||
class PurchaseOrder(models.Model):
|
||||
_name = "purchase.order"
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = "Purchase Order"
|
||||
_order = 'date_order desc, id desc'
|
||||
|
||||
|
||||
@@ -298,6 +298,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>
|
||||
@@ -320,6 +321,18 @@
|
||||
<filter name="not_invoiced" string="Waiting Bills" domain="[('invoice_status','=', 'to invoice')]" help="Purchase orders that include lines not invoiced."/>
|
||||
<filter name="invoiced" string="Bills Received" domain="[('invoice_status','=', 'invoiced')]" help="Purchase orders that have been invoiced."/>
|
||||
<filter string="New Mail" name="message_unread" domain="[('message_unread','=',True)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Vendor" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
<filter string="Order Date" domain="[]" context="{'group_by':'date_order'}"/>
|
||||
|
||||
@@ -14,7 +14,7 @@ import odoo.addons.decimal_precision as dp
|
||||
|
||||
class SaleOrder(models.Model):
|
||||
_name = "sale.order"
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = "Sales Order"
|
||||
_order = 'date_order desc, id desc'
|
||||
|
||||
|
||||
@@ -355,6 +355,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>
|
||||
@@ -389,6 +390,18 @@
|
||||
<filter string="My Orders" domain="[('user_id','=',uid)]" name="my_sale_orders_filter"/>
|
||||
<separator/>
|
||||
<filter string="Unread Messages" name="message_needaction" domain="[('message_needaction','=',True)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<group expand="0" string="Group By">
|
||||
<filter string="Salesperson" domain="[]" context="{'group_by':'user_id'}"/>
|
||||
<filter name="customer" string="Customer" domain="[]" context="{'group_by':'partner_id'}"/>
|
||||
|
||||
@@ -27,7 +27,7 @@
|
||||
</a>
|
||||
</td>
|
||||
<td class="o_main" title="To Activities">
|
||||
<a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities" data-context="{'search_default_today':'1'}">
|
||||
<a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities" data-context="{'search_default_activities_today':'1'}">
|
||||
<span class="o_highlight">
|
||||
<t t-if="!show_demo">
|
||||
<t t-esc="values['activity']['today']"/>
|
||||
@@ -59,7 +59,7 @@
|
||||
</a>
|
||||
</td>
|
||||
<td class="o_main" title="To Activities">
|
||||
<a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities" data-context="{'search_default_this_week':'1'}">
|
||||
<a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities" data-context="{'search_default_activities_week':'1'}">
|
||||
<t t-if="!show_demo">
|
||||
<t t-esc="values['activity']['next_7_days']"/>
|
||||
</t>
|
||||
@@ -74,7 +74,7 @@
|
||||
<td></td>
|
||||
<!-- Hide overdue when there is none -->
|
||||
<td t-if="show_demo or (values and values['activity']['overdue'] != 0)" class="o_warning" title="To Activities">
|
||||
<a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities" data-context="{'search_default_overdue':'1'}">
|
||||
<a href="#" class="o_dashboard_action" name="crm.crm_lead_action_activities" data-context="{'search_default_activities_overdue':'1'}">
|
||||
<t t-if="!show_demo">
|
||||
<t t-esc="values['activity']['overdue']"/> overdue
|
||||
</t>
|
||||
@@ -84,7 +84,7 @@
|
||||
</a>
|
||||
</td>
|
||||
<td t-if="show_demo or (values and values['closing']['overdue'] != 0)" class="o_warning" title="To Opportunities">
|
||||
<a href="#" class="o_dashboard_action" name="crm.action_your_pipeline" data-context="{'search_default_overdue':'1'}">
|
||||
<a href="#" class="o_dashboard_action" name="crm.action_your_pipeline" data-context="{'search_default_activities_overdue':'1'}">
|
||||
<t t-if="!show_demo">
|
||||
<t t-esc="values['closing']['overdue']"/> exp. closing
|
||||
</t>
|
||||
|
||||
@@ -157,7 +157,7 @@ class PickingType(models.Model):
|
||||
|
||||
class Picking(models.Model):
|
||||
_name = "stock.picking"
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
_description = "Transfer"
|
||||
_order = "priority desc, date asc, id desc"
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<record id="view_partner_stock_form" model="ir.ui.view">
|
||||
<field name="name">res.partner.stock.property.form.inherit</field>
|
||||
<field name="model">res.partner</field>
|
||||
<field name="inherit_id" ref="mail.view_emails_partner_info_form"/>
|
||||
<field name="inherit_id" ref="mail.res_partner_view_form_inherit_mail"/>
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//group[@name='container_row_2']" position="after">
|
||||
<group name="container_row_stock" groups="base.group_no_one">
|
||||
|
||||
@@ -382,6 +382,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>
|
||||
@@ -403,6 +404,18 @@
|
||||
<filter name="late" string="Late" domain="[('min_date','<', time.strftime('%%Y-%%m-%%d %%H:%%M:%%S'))]" help="Pickings that are late on scheduled time"/>
|
||||
<separator/>
|
||||
<filter name="backorder" string="Backorders" domain="[('backorder_id','<>', False)]" help="Remaining parts of picking partially processed"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
<field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
|
||||
<field name="product_id"/>
|
||||
<field name="picking_type_id"/>
|
||||
|
||||
@@ -6,7 +6,7 @@ from odoo.exceptions import UserError
|
||||
|
||||
|
||||
class StockPickingWave(models.Model):
|
||||
_inherit = "mail.thread"
|
||||
_inherit = ['mail.thread']
|
||||
_name = "stock.picking.wave"
|
||||
_description = "Picking Wave"
|
||||
_order = "name desc"
|
||||
|
||||
@@ -53,7 +53,7 @@ class Survey(models.Model):
|
||||
_name = 'survey.survey'
|
||||
_description = 'Survey'
|
||||
_rec_name = 'title'
|
||||
_inherit = ['mail.thread']
|
||||
_inherit = ['mail.thread', 'mail.activity.mixin']
|
||||
|
||||
def _default_stage(self):
|
||||
return self.env['survey.stage'].search([], limit=1).id
|
||||
|
||||
@@ -161,6 +161,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>
|
||||
@@ -194,6 +195,8 @@
|
||||
<field name="color" />
|
||||
<field name="auth_required" />
|
||||
<field name="public_url" />
|
||||
<field name="activity_ids" />
|
||||
<field name="activity_state" />
|
||||
<templates>
|
||||
<div t-name="kanban-box" t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_global_click">
|
||||
<div class="o_dropdown_kanban dropdown" t-if="widget.editable">
|
||||
@@ -226,6 +229,9 @@
|
||||
<t t-if="record.tot_start_survey.raw_value <= 0"><a style="color: #aaaaaa;">Analyze Answers</a></t>
|
||||
</li>
|
||||
</ul></div>
|
||||
<div class="oe_kanban_bottom_right">
|
||||
<field name="activity_ids" widget="kanban_activity" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</templates>
|
||||
@@ -239,6 +245,18 @@
|
||||
<search string="Survey">
|
||||
<field string="Title" name="title"/>
|
||||
<filter string="Archived" name="inactive" domain="[('active','=',False)]"/>
|
||||
<separator/>
|
||||
<filter string="My Activities" name="activities_my"
|
||||
domain="[('activity_ids.user_id', '=', uid)]"/>
|
||||
<filter string="Today Activities" name="activities_today"
|
||||
domain="[('activity_ids.date_deadline', '=', context_today().strftime('%Y-%m-%d'))]"/>
|
||||
<filter string="Upcoming Activities (7 days)" name="activities_week"
|
||||
domain="['&', ('activity_ids.date_deadline', '>=', context_today().strftime('%Y-%m-%d')),
|
||||
('activity_ids.date_deadline', '<=', (context_today()+datetime.timedelta(days=7)).strftime('%Y-%m-%d'))
|
||||
]"/>
|
||||
<filter string="Overdue 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"/>
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -550,6 +550,7 @@
|
||||
height: 12px;
|
||||
width: 12px;
|
||||
border-radius: 6px;
|
||||
vertical-align: middle;
|
||||
|
||||
background-color: @odoo-brand-secondary;
|
||||
|
||||
|
||||
@@ -94,10 +94,10 @@ class WebsiteAccount(website_account):
|
||||
|
||||
searchbar_filters = {
|
||||
'all': {'label': _('All'), 'domain': []},
|
||||
'today': {'label': _('Today Activities'), 'domain': [('date_action', '=', today)]},
|
||||
'today': {'label': _('Today Activities'), 'domain': [('activity_date_deadline', '=', today)]},
|
||||
'week': {'label': _('This Week Activities'),
|
||||
'domain': [('date_action', '>=', today), ('date_action', '<=', this_week_end_date)]},
|
||||
'overdue': {'label': _('Overdue Activities'), 'domain': [('date_action', '<', today)]},
|
||||
'domain': [('activity_date_deadline', '>=', today), ('activity_date_deadline', '<=', this_week_end_date)]},
|
||||
'overdue': {'label': _('Overdue Activities'), 'domain': [('activity_date_deadline', '<', today)]},
|
||||
'won': {'label': _('Won'), 'domain': [('stage_id.probability', '=', 100), ('stage_id.fold', '=', True)]},
|
||||
'lost': {'label': _('Lost'), 'domain': [('active', '=', False)]},
|
||||
}
|
||||
@@ -158,8 +158,9 @@ class WebsiteAccount(website_account):
|
||||
return request.render(
|
||||
"website_crm_partner_assign.portal_my_opportunity", {
|
||||
'opportunity': lead,
|
||||
'user_activity': lead.activity_ids.filtered(lambda activity: activity.user_id == request.env.user)[:1],
|
||||
'stages': request.env['crm.stage'].search([('probability', '!=', '100')], order='sequence desc'),
|
||||
'activities': request.env['crm.activity'].sudo().search([], order='sequence desc'),
|
||||
'activity_types': request.env['mail.activity.type'].sudo().search([]),
|
||||
'states': request.env['res.country.state'].sudo().search([]),
|
||||
'countries': request.env['res.country'].sudo().search([]),
|
||||
})
|
||||
|
||||
@@ -226,30 +226,33 @@ class CrmLead(models.Model):
|
||||
@api.multi
|
||||
def update_lead_portal(self, values):
|
||||
self.check_access_rights('write')
|
||||
|
||||
for lead in self:
|
||||
if values['date_action'] == '':
|
||||
values['date_action'] = False
|
||||
if lead.next_activity_id.id != values['activity_id'] or lead.title_action != values['title_action']\
|
||||
or lead.date_action != values['date_action']:
|
||||
activity = lead.sudo().next_activity_id
|
||||
body_html = "<div><b>%(title)s</b>: %(next_activity)s</div>%(description)s" % {
|
||||
'title': _('Activity Done'),
|
||||
'next_activity': activity.name,
|
||||
'description': lead.title_action and '<p><em>%s</em></p>' % lead.title_action or '',
|
||||
}
|
||||
lead.message_post(
|
||||
body=body_html,
|
||||
subject=lead.title_action,
|
||||
subtype="mail.mt_note")
|
||||
lead.write({
|
||||
lead_values = {
|
||||
'planned_revenue': values['planned_revenue'],
|
||||
'probability': values['probability'],
|
||||
'next_activity_id': values['activity_id'],
|
||||
'title_action': values['title_action'],
|
||||
'date_action': values['date_action'] if values['date_action'] else False,
|
||||
'priority': values['priority'],
|
||||
'date_deadline': values['date_deadline'] if values['date_deadline'] else False,
|
||||
})
|
||||
}
|
||||
# As activities may belong to several users, only the current portal user activity
|
||||
# will be modified by the portal form. If no activity exist we create a new one instead
|
||||
# that we assign to the portal user.
|
||||
user_activity = lead.activity_ids.filtered(lambda activity: activity.user_id == self.env.user)[:1]
|
||||
if user_activity:
|
||||
user_activity.sudo().write({
|
||||
'activity_type_id': values['activity_type_id'],
|
||||
'summary': values['activity_summary'],
|
||||
'date_deadline': values['activity_date_deadline'],
|
||||
})
|
||||
else:
|
||||
self.env['mail.activity'].sudo().create({
|
||||
'res_model_id': self.env.ref('crm.model_crm_lead').id,
|
||||
'res_id': lead.id,
|
||||
'user_id': self.env.user.id,
|
||||
'activity_type_id': values['activity_type_id'],
|
||||
'summary': values['activity_summary'],
|
||||
'date_deadline': values['activity_date_deadline'],
|
||||
})
|
||||
lead.write(lead_values)
|
||||
|
||||
@api.model
|
||||
def create_opp_portal(self, values):
|
||||
@@ -276,4 +279,4 @@ class CrmLead(models.Model):
|
||||
lead.convert_opportunity(lead.partner_id.id)
|
||||
return {
|
||||
'id': lead.id
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,5 +13,4 @@ partner_access_crm_lead,crm.lead,crm.model_crm_lead,base.group_portal,1,1,0,0
|
||||
access_res_partner_grade_invoicing_payment,res.partner.grade,model_res_partner_grade,account.group_account_invoice,1,0,0,0
|
||||
access_res_partner_grade_portal,res.partner.grade,model_res_partner_grade,base.group_portal,1,0,0,0
|
||||
crm_lead_portal_access,crm.lead.portal,crm.model_crm_lead,base.group_portal,1,1,0,0
|
||||
crm_activity_portal_access,crm.activity.portal,crm.model_crm_activity,base.group_portal,1,1,1,0
|
||||
mail_message_subtype_portal_access,mail.message.subtype.portal,mail.model_mail_message_subtype,base.group_portal,1,1,1,0
|
||||
|
@@ -37,7 +37,7 @@ $('.interested_partner_assign_confirm').on('click',function(e){
|
||||
|
||||
|
||||
$('.desinterested_partner_assign_confirm').on('click',function(){
|
||||
var $btn = this;
|
||||
var $btn = $(this);
|
||||
$btn.prop('disabled', true);
|
||||
new Model('crm.lead')
|
||||
.call("partner_desinterested",
|
||||
@@ -55,7 +55,7 @@ $('.desinterested_partner_assign_confirm').on('click',function(){
|
||||
});
|
||||
|
||||
opp_stage_buttons.on('click',function(e){
|
||||
var $btn = this;
|
||||
var $btn = $(this);
|
||||
$btn.prop('disabled', true);
|
||||
new Model('crm.lead')
|
||||
.call("write", [[parseInt(e.currentTarget.getAttribute('opp'))],{
|
||||
@@ -76,7 +76,7 @@ $('.edit_contact_form .country_id').on('change', function(){
|
||||
});
|
||||
|
||||
$('.edit_contact_confirm').on('click',function(){
|
||||
var $btn = this;
|
||||
var $btn = $(this);
|
||||
$btn.prop('disabled', true);
|
||||
new Model('crm.lead')
|
||||
.call("write", [[parseInt($('.edit_contact_form .opportunity_id').val())],{
|
||||
@@ -120,16 +120,15 @@ $('.new_opp_confirm').on('click',function(e){
|
||||
});
|
||||
|
||||
$('.edit_opp_confirm').on('click',function(){
|
||||
var $btn = this;
|
||||
var $btn = $(this);
|
||||
$btn.prop('disabled', true);
|
||||
new Model('crm.lead')
|
||||
.call("update_lead_portal", [[parseInt($('.edit_opp_form .opportunity_id').val())],{
|
||||
activity_id: parseInt($('.edit_opp_form .next_activity').find(":selected").attr('data')),
|
||||
planned_revenue: parseFloat($('.edit_opp_form .planned_revenue').val()),
|
||||
probability: parseFloat($('.edit_opp_form .probability').val()),
|
||||
date_deadline: $('.edit_opp_form .date_deadline').val(),
|
||||
title_action: $('.edit_opp_form .title_action').val(),
|
||||
date_action: $('.edit_opp_form .date_action').val(),
|
||||
activity_type_id: parseInt($('.edit_opp_form .next_activity').find(":selected").attr('data')),
|
||||
activity_summary: $('.edit_opp_form .activity_summary').val(),
|
||||
activity_date_deadline: $('.edit_opp_form .activity_date_deadline').val(),
|
||||
priority: $('input[name="PriorityRadioOptions"]:checked').val(),
|
||||
}])
|
||||
.fail(function() {
|
||||
@@ -143,14 +142,14 @@ $('.edit_opp_confirm').on('click',function(){
|
||||
|
||||
$('.edit_opp_form .next_activity').on('change', function(){
|
||||
var selected = $('.edit_opp_form .next_activity').find(":selected");
|
||||
if(selected.attr('description')){
|
||||
$('.edit_opp_form .title_action').val(selected.attr('description'));
|
||||
if(selected.attr('activity_summary')){
|
||||
$('.edit_opp_form .activity_summary').val(selected.attr('activity_summary'));
|
||||
}
|
||||
if(selected.attr('days')){
|
||||
var date_now = moment();
|
||||
var days = parseInt(selected.attr('days'));
|
||||
var date = date_now.add(days, 'days');
|
||||
$('.edit_opp_form .date_action').val(date.format('YYYY-MM-DD'));
|
||||
$('.edit_opp_form .activity_date_deadline').val(date.format('YYYY-MM-DD'));
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -757,19 +757,19 @@
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="next_activity">Next Activity</label>
|
||||
<select class="form-control next_activity" name="next_activity">
|
||||
<t t-foreach="activities" t-as="activity">
|
||||
<option t-att-data="activity.id" t-att-selected="activity.id == opportunity.next_activity_id.id" t-att-name="activity.name" t-att-days="activity.days" t-att-description="activity.description"><t t-esc="activity.name"/></option>
|
||||
<t t-foreach="activity_types" t-as="activity_type">
|
||||
<option t-att-data="activity_type.id" t-att-selected="activity_type.id == user_activity.activity_type_id.id" t-att-name="activity_type.name" t-att-days="activity_type.days" t-att-summary="activity_type.summary"><t t-esc="activity_type.name"/></option>
|
||||
</t>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="title_action">Details Next Activity</label>
|
||||
<textarea row="3" name="title_action" class="form-control title_action"><t t-esc="opportunity.title_action"/></textarea>
|
||||
<label class="control-label" for="activity_summary">Details Next Activity</label>
|
||||
<textarea row="3" name="activity_summary" class="form-control activity_summary"><t t-esc="user_activity.summary"/></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="control-label" for="date_action">Next Activity Date</label>
|
||||
<label class="control-label" for="activity_date_deadline">Next Activity Date</label>
|
||||
<div class='input-group date'>
|
||||
<input type="text" name="date_action" t-att-value="opportunity.date_action" class="form-control date_action" data-date-format="YYYY-MM-DD" t-att-name="prefix" placeholder="YYYY-MM-DD" />
|
||||
<input type="text" name="activity_date_deadline" t-att-value="user_activity.date_deadline" class="form-control activity_date_deadline" data-date-format="YYYY-MM-DD" t-att-name="prefix" placeholder="YYYY-MM-DD" />
|
||||
<span class="input-group-addon"><span class="fa fa-calendar"></span></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -782,16 +782,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row" t-if="user_activity">
|
||||
<label class="col-xs-4">Next Activity</label>
|
||||
<span class="col-xs-8">
|
||||
<span t-field="opportunity.next_activity_id"/>
|
||||
<span t-if="opportunity.date_action"> on </span>
|
||||
<span t-field="opportunity.date_action"/>
|
||||
<span t-field="user_activity.activity_type_id"/>
|
||||
<span t-if="user_activity.date_deadline"> on </span>
|
||||
<span t-field="user_activity.date_deadline"/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="col-xs-8 col-xs-offset-4" t-field="opportunity.title_action"/>
|
||||
<div class="row" t-if="user_activity">
|
||||
<span class="col-xs-8 col-xs-offset-4" t-field="user_activity.summary"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user