Browse Source

[ADD][mail_activity_board] Add new module that insert activities board in boards. (#283)

* [ADD] Module that insert activities board.

* [FIX] Author error in __manifest__ file and style changes.

* [FIX] Fix replace in view, rename files and style changes.

* [FIX] Enumerated list ends without a blank line; unexpected unindent.

* [FIX] Name fail.

* [FIX] Bug in view.

* [FIX]  Add button Activities in mail.thread and readme folder. Others improvements in style of code.

* [FIX] Type 'tree' not found in registry: problem solved.

* [FIX] Dependence change: 'mail' for 'calendar'.

* [FIX] Eliminated unnecessary imports.

* [FIX] Bugs about js and if/else.

* [FIX] Improvements following guide lines and eliminating unnecessary attributes in views.

* [ADD] Added counter in the 'Activities List' button.

* [FIX] Bugs in javascript with 'Activities' button.

* [ADD] Tests folder.

* [FIX] Deleted references to modules not installed.

* [FIX] Formatting javascript.

* [FIX] Bug: added a soft line before a class.

* [FIX] Bug: https://github.com/OCA/social/pull/283#discussion_r204302325

* [FIX] Escaping 'lt' in xml file. Bug: https://github.com/OCA/social/pull/283#discussion_r204302325

* [FIX] The meeting attendees are shown in kanban mode on the meeting board.

* [FIX] Hide in form view of the activity board the assistant field if the activity is not a meeting type or if there are no assistants.

* [FIX] Change to default kanban view for partners.
pull/322/head
Dajuayen 6 years ago
committed by Jairo Llopis
parent
commit
c59f6ffac2
  1. 21
      mail_activity_board/README.rst
  2. 1
      mail_activity_board/__init__.py
  3. 24
      mail_activity_board/__manifest__.py
  4. 2
      mail_activity_board/models/__init__.py
  5. 45
      mail_activity_board/models/mail_activity.py
  6. 32
      mail_activity_board/models/mail_activity_mixin.py
  7. 3
      mail_activity_board/readme/CONTRIBUTORS.rst
  8. 2
      mail_activity_board/readme/DESCRIPTION.rst
  9. 9
      mail_activity_board/readme/USAGE.rst
  10. 33
      mail_activity_board/static/src/js/override_chatter.js
  11. 13
      mail_activity_board/static/src/xml/inherit_chatter.xml
  12. 1
      mail_activity_board/tests/__init__.py
  13. 136
      mail_activity_board/tests/test_mail_activity_board.py
  14. 210
      mail_activity_board/views/mail_activity_view.xml
  15. 8
      mail_activity_board/views/templates.xml

21
mail_activity_board/README.rst

@ -0,0 +1,21 @@
**This file is going to be generated by oca-gen-addon-readme.**
*Manual changes will be overwritten.*
Please provide content in the ``readme`` directory:
* **DESCRIPTION.rst** (required)
* INSTALL.rst (optional)
* CONFIGURE.rst (optional)
* **USAGE.rst** (optional, highly recommended)
* DEVELOP.rst (optional)
* ROADMAP.rst (optional)
* HISTORY.rst (optional, recommended)
* **CONTRIBUTORS.rst** (optional, highly recommended)
* CREDITS.rst (optional)
Content of this README will also be drawn from the addon manifest,
from keys such as name, authors, maintainers, development_status,
and license.
A good, one sentence summary in the manifest is also highly recommended.

1
mail_activity_board/__init__.py

@ -0,0 +1 @@
from . import models

24
mail_activity_board/__manifest__.py

@ -0,0 +1,24 @@
# Copyright 2018 David Juaneda - <djuaneda@sdi.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
'name': 'Activities board',
'summary': 'Add Activity Boards',
'version': '11.0.1.0.0',
'development_status': 'Beta',
'category': 'Social Network',
'website': 'https://github.com/OCA/social',
'author': 'SDi, David Juaneda, Odoo Community Association (OCA)',
'license': 'AGPL-3',
'installable': True,
'depends': [
'calendar',
'board',
],
'data': [
'views/templates.xml',
'views/mail_activity_view.xml',
],
'qweb': [
'static/src/xml/inherit_chatter.xml',
]
}

2
mail_activity_board/models/__init__.py

@ -0,0 +1,2 @@
from . import mail_activity
from . import mail_activity_mixin

45
mail_activity_board/models/mail_activity.py

@ -0,0 +1,45 @@
# Copyright 2018 David Juaneda - <djuaneda@sdi.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import api, models, fields
class MailActivity(models.Model):
_inherit = "mail.activity"
res_model_id_name = fields.Char(
related='res_model_id.name', string="Origin",
readonly=True)
duration = fields.Float(
related='calendar_event_id.duration', readonly=True)
calendar_event_id_start = fields.Datetime(
related='calendar_event_id.start', readonly=True)
calendar_event_id_partner_ids = fields.Many2many(
related='calendar_event_id.partner_ids',
readonly=True)
@api.multi
def open_origin(self):
self.ensure_one()
vid = self.env[self.res_model].browse(self.res_id).get_formview_id()
response = {
'type': 'ir.actions.act_window',
'res_model': self.res_model,
'view_mode': 'form',
'res_id': self.res_id,
'target': 'current',
'flags': {
'form': {
'action_buttons': False
}
},
'views': [
(vid, "form")
]
}
return response
@api.model
def action_activities_board(self):
action = self.env.ref(
'mail_activity_board.open_boards_activities').read()[0]
return action

32
mail_activity_board/models/mail_activity_mixin.py

@ -0,0 +1,32 @@
# Copyright 2018 David Juaneda - <djuaneda@sdi.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models
class MailActivityMixin(models.AbstractModel):
_inherit = 'mail.activity.mixin'
def redirect_to_activities(self, **kwargs):
"""Redirects to the list of activities of the object shown.
Redirects to the activity board and configures the domain so that
only those activities that are related to the object shown are
displayed.
Add to the title of the view the name the class of the object from
which the activities will be displayed.
:param kwargs: contains the id of the object and the model it's about.
:return: action.
"""
id = kwargs.get("id")
action = self.env['mail.activity'].action_activities_board()
views = []
for v in action['views']:
if v[1] == 'tree':
v = (v[0], 'list')
views.append(v)
action['views'] = views
action['domain'] = [('res_id', '=', id)]
return action

3
mail_activity_board/readme/CONTRIBUTORS.rst

@ -0,0 +1,3 @@
* `SDI <https://www.sdi.es>`_:
* David Juaneda

2
mail_activity_board/readme/DESCRIPTION.rst

@ -0,0 +1,2 @@
This module adds an activity board with form, tree, kanban, calendar, pivot, graph and search views.

9
mail_activity_board/readme/USAGE.rst

@ -0,0 +1,9 @@
To use this module, you need to:
#. Access to the views from menu Boards.
A smartButton of activities is added in the mail thread from form view.
From this smartButton is linked to the activity board, to the view tree,
which shows the activities related to the opportunity.
From the form view of the activity you can navigate to the origin of the activity.

33
mail_activity_board/static/src/js/override_chatter.js

@ -0,0 +1,33 @@
/* Copyright 2018 David Juaneda
* License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl). */
odoo.define('mail.Chatter.activity', function (require) {
"use strict";
var chatter = require('mail.Chatter');
chatter.include({
events: _.extend({}, chatter.prototype.events, {
'click .o_chatter_button_list_activity': '_onListActivity',
}),
/**
* Performs the action to redirect to the activities of the object.
*
* @private
*/
_onListActivity: function () {
this._rpc({
model: this.record.model,
method: 'redirect_to_activities',
args: [[]],
kwargs: {
'id':this.record.res_id,
'model':this.record.model,
},
context: this.record.getContext(),
}).then($.proxy(this, "do_action"));
},
});
});

13
mail_activity_board/static/src/xml/inherit_chatter.xml

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<templates xml:space="preserve">
<t t-extend="mail.Chatter.Buttons">
<t t-jquery="button.o_chatter_button_schedule_activity" t-operation="after">
<button t-if="schedule_activity_btn" class="btn btn-sm btn-link o_chatter_button_list_activity"
title="See activities list" type="button">
<i class="fa fa-list"/> Activities
</button>
</t>
</t>
</templates>

1
mail_activity_board/tests/__init__.py

@ -0,0 +1 @@
from . import test_mail_activity_board

136
mail_activity_board/tests/test_mail_activity_board.py

@ -0,0 +1,136 @@
# Copyright 2018 David Juaneda - <djuaneda@sdi.es>
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo.tests.common import TransactionCase
class TestMailActivityBoardMethods(TransactionCase):
def setUp(self):
super(TestMailActivityBoardMethods, self).setUp()
# Set up activities
# Create a user as 'Crm Salesman' and added few groups
self.employee = self.env['res.users'].create({
'company_id': self.env.ref("base.main_company").id,
'name': "Employee",
'login': "csu",
'email': "crmuser@yourcompany.com",
'groups_id': [(6, 0, [self.env.ref('base.group_user').id])]
})
# lead_model_id = self.env['ir.model']._get('crm.lead').id
partner_model_id = self.env['ir.model']._get('res.partner').id
ActivityType = self.env['mail.activity.type']
self.activity1 = ActivityType.create({
'name': 'Initial Contact',
'days': 5,
'summary': 'ACT 1 : Presentation, barbecue, ... ',
'res_model_id': partner_model_id,
})
self.activity2 = ActivityType.create({
'name': 'Call for Demo',
'days': 6,
'summary': 'ACT 2 : I want to show you my ERP !',
'res_model_id': partner_model_id,
})
self.activity3 = ActivityType.create({
'name': 'Celebrate the sale',
'days': 3,
'summary': 'ACT 3 : '
'Beers for everyone because I am a good salesman !',
'res_model_id': partner_model_id,
})
# I create an opportunity, as employee
self.partner_client = self.env.ref("base.res_partner_1")
self.act1 = self.env['mail.activity'].sudo().create({
'activity_type_id': self.activity3.id,
'note': 'Partner activity 1.',
'res_id': self.partner_client.id,
'res_model_id': partner_model_id,
'user_id': self.employee.id
})
self.act2 = self.env['mail.activity'].sudo().create({
'activity_type_id': self.activity2.id,
'note': 'Partner activity 2.',
'res_id': self.partner_client.id,
'res_model_id': partner_model_id,
'user_id': self.employee.id
})
self.act3 = self.env['mail.activity'].sudo().create({
'activity_type_id': self.activity3.id,
'note': 'Partner activity 3.',
'res_id': self.partner_client.id,
'res_model_id': partner_model_id,
'user_id': self.employee.id
})
def get_view(self, activity):
action = activity.open_origin()
result = self.env[action.get('res_model')]\
.load_views(action.get('views'))
return result.get('fields_views').get(action.get('view_mode'))
def test_open_origin_res_partner(self):
""" This test case checks
- If the method redirects to the form view of the correct one
of an object of the 'res.partner' class to which the activity
belongs.
"""
# Id of the form view for the class 'crm.lead', type 'lead'
form_view_partner_id = self.env.ref('base.view_partner_form').id
# Id of the form view return open_origin()
view = self.get_view(self.act1)
# Check the next view is correct
self.assertEqual(form_view_partner_id, view.get('view_id'))
# Id of the form view return open_origin()
view = self.get_view(self.act2)
# Check the next view is correct
self.assertEqual(form_view_partner_id, view.get('view_id'))
# Id of the form view return open_origin()
view = self.get_view(self.act3)
# Check the next view is correct
self.assertEqual(form_view_partner_id, view.get('view_id'))
def test_redirect_to_activities(self):
""" This test case checks
- if the method returns the correct action,
- if the correct activities are shown.
"""
action_id = self.env.ref(
'mail_activity_board.open_boards_activities').id
action = self.partner_client\
.redirect_to_activities(**{'id': self.partner_client.id})
self.assertEqual(action.get('id'), action_id)
kwargs = {
'groupby': [
"activity_type_id"
]
}
kwargs['domain'] = action.get('domain')
result = self.env[action.get('res_model')]\
.load_views(action.get('views'))
fields = result.get('fields_views').get('kanban').get('fields')
kwargs['fields'] = list(fields.keys())
result = self.env['mail.activity'].read_group(**kwargs)
acts = []
for group in result:
records = self.env['mail.activity'].search_read(
domain=group.get('__domain'), fields=kwargs['fields']
)
acts += [id.get('id') for id in records]
for act in acts:
self.assertIn(act, self.partner_client.activity_ids.ids)

210
mail_activity_board/views/mail_activity_view.xml

@ -0,0 +1,210 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!--
VIEWS
-->
<!-- FORM VIEW -->
<record id="mail_activity_view_form_board" model="ir.ui.view">
<field name="name">mail.activity.boards.view.form</field>
<field name="model">mail.activity</field>
<field name="priority">30</field>
<field name="arch" type="xml">
<form string="Activity Form" create="false" edit="false" delete="false">
<sheet string="Activity">
<button name="open_origin" type="object" class="centre oe_link" nolabel="1">
<h1><field name="res_name"/></h1>
</button>
<field name="activity_category" invisible="1" />
<field name="res_model" invisible="1"/>
<field name="res_model_id" invisible="1"/>
<field name="res_id" invisible="1"/>
<group>
<group>
<field name="activity_type_id" required="1" options="{'no_create': True, 'no_open': True}"/>
<field name="res_model_id_name"/>
<field name="calendar_event_id" invisible="1"/>
<field name="create_date" invisible="1"/>
</group>
<group>
<field name="date_deadline"
attrs="{'invisible': [('activity_category', '=', 'meeting')]}"/>
<field name="calendar_event_id_start" string="Start meeting"
attrs="{'invisible': [('calendar_event_id','=', False)]}"/>
<field name="duration" widget="float_time"
attrs="{'invisible': ['|',('duration', '=', False),
('calendar_event_id','=', False)]}"/>
<field name="user_id" options="{'no_open': True}"/>
</group>
</group>
<group attrs="{'invisible': ['|',('calendar_event_id','=', False),('calendar_event_id_partner_ids','=', False)]}">
<field name="calendar_event_id_partner_ids" mode="kanban"/>
</group>
<group>
<field name="summary" placeholder="e.g. Discuss proposal"/>
<field name="note" placeholder="Log a note..."/>
</group>
</sheet>
</form>
</field>
</record>
<!-- TREE VIEW -->
<record id="mail_activity_view_tree" model="ir.ui.view">
<field name="name">mail.activity.boards.view.tree</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_tree"/>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="default_order"></attribute>
<attribute name="decoration-danger">(date_deadline &lt; current_date)</attribute>
<attribute name="decoration-warning">(date_deadline == current_date)</attribute>
<attribute name="decoration-success">(date_deadline &gt; current_date)</attribute>
</xpath>
</field>
</record>
<!-- KANBAN VIEW -->
<record id="mail_activity_view_kanban" model="ir.ui.view">
<field name="name">mail.activity.boards.view.kanban</field>
<field name="model">mail.activity</field>
<field name="priority" eval="10"/>
<field name="arch" type="xml">
<kanban default_group_by="activity_type_id" class="_kanban_small_column o_opportunity_kanban" create="0" _order="date_deadline"
group_create="false" group_delete="false" group_edit="false">
<field name="user_id"/>
<field name="res_id"/>
<field name="res_name"/>
<field name="res_model"/>
<field name="summary"/>
<field name="date_deadline"/>
<field name="state"/>
<field name="icon"/>
<field name="activity_type_id"/>
<field name="activity_category"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_content oe_kanban_global_click">
<div class="oe_kanban_content">
<div>
<strong class="o_kanban_record_subtitle">
<span t-attf-class="fa #{record.icon.raw_value}" />
<field name="summary"/>
</strong>
</div>
<div>
<strong class="o_kanban_record_title"><field name="res_name"/></strong>
</div>
<div class="o_kanban_record_bottom">
<div class="oe_kanban_bottom_left">
<t t-set="act_date" t-value="new Date(record.date_deadline.raw_value)"/>
<t t-if="act_date &lt; (new Date())">
<span t-attf-class="text-danger"><i class="fa fa-clock-o"/></span>
<t t-if="record.activity_category.raw_value!='meeting'">
<span t-attf-class="text-danger">
<field name="date_deadline" t-options='{"widget": "date"}'/>
</span>
</t>
<t t-else="">
<span t-attf-class="text-danger">
<field name="calendar_event_id_start" t-options='{"widget": "date"}'/>
</span>
</t>
</t>
<t t-else="">
<span><i class="fa fa-clock-o"/></span>
<t t-if="record.activity_category.raw_value!='meeting'">
<span>
<field name="date_deadline" t-options='{"widget": "date"}'/>
</span>
</t>
<t t-else="">
<field name="calendar_event_id_start" t-options='{"widget": "date"}'/>
</t>
</t>
</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"/>
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
<!-- SEARCH VIEW -->
<record id="mail_activity_view_search" model="ir.ui.view">
<field name="name">mail.activity.boards.view.search</field>
<field name="model">mail.activity</field>
<field name="inherit_id" ref="mail.mail_activity_view_search"/>
<field name="priority" eval="2"/>
<field name="mode">primary</field>
<field name="arch" type="xml">
<xpath expr='//field[@name="res_model_id"]' position='before'>
<field name="user_id"/>
<field name="res_name" string="Origin"/>
</xpath>
<xpath expr='//filter[@name="activities_my"]' position='after'>
<filter string="Act. next month" name="activities_month"
domain="[('date_deadline', '&lt;', (context_today()+datetime.timedelta(days=30)).strftime('%Y-%m-%d'))]"
help="Show activities scheduled for next month."/>
<filter string="Act. next 6 months" name="activities_6_month"
domain="[('date_deadline', '&lt;', (context_today()+datetime.timedelta(days=180)).strftime('%Y-%m-%d'))]"
help="Show activities scheduled for next 6 months."/>
<separator/>
</xpath>
<xpath expr='//search/group' position='inside'>
<filter string="User" context="{'group_by':'user_id'}"/>
<filter string="Origin" context="{'group_by': 'res_model_id'}"/>
</xpath>
</field>
</record>
<!--
ACTION
-->
<record model="ir.actions.act_window" id="open_boards_activities">
<field name="name">Activities</field>
<field name="res_model">mail.activity</field>
<field name="view_type">form</field>
<field name="view_mode">kanban,form</field>
<field name="domain">[]</field>
<field name="context">{}</field>
<field name="view_ids"
eval="[(5, 0, 0),
(0, 0, {'view_mode': 'kanban', 'view_id': ref('mail_activity_view_kanban')}),
(0, 0, {'view_mode': 'tree', 'view_id': ref('mail_activity_view_tree')}),
(0, 0, {'view_mode': 'form', 'view_id': ref('mail_activity_view_form_board')}),
(0, 0, {'view_mode': 'calendar'}),
(0, 0, {'view_mode': 'pivot'}),
(0, 0, {'view_mode': 'graph'})]"/>
<field name="search_view_id" ref="mail_activity_view_search"/>
</record>
<!--
Menus
-->
<menuitem
id="board_menu_activities"
name="Activities"
parent="base.menu_board_root"
action="open_boards_activities"
sequence="1"/>
</odoo>

8
mail_activity_board/views/templates.xml

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="assets_backend" name="mail_activity_board assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/mail_activity_board/static/src/js/override_chatter.js"></script>
</xpath>
</template>
</odoo>
Loading…
Cancel
Save