summaryrefslogtreecommitdiff
path: root/addons/project
diff options
context:
space:
mode:
authorDenis Ledoux dle@openerp.com <>2012-11-08 10:28:38 +0100
committerDenis Ledoux dle@openerp.com <>2012-11-08 10:28:38 +0100
commit981168dfc6d628c72d8469022c0b51e0f5ec00df (patch)
treee7497e1366dcd0b7759ed6eae9d2fa8b50ff46c5 /addons/project
parent9e56b3b6a8a761e02099146b338225d29dbc8c8a (diff)
parent29fffbc97d8970513114b7b76763f002aa73e395 (diff)
[ADD][MERGE]Add functionnalities, merge with trunk
bzr revid: dle@openerp.com-20121108092838-fe89ohg9za3ln6y1
Diffstat (limited to 'addons/project')
-rw-r--r--addons/project/project.py93
-rw-r--r--addons/project/project_data.xml20
-rw-r--r--addons/project/project_view.xml30
-rw-r--r--addons/project/static/src/css/project.css18
-rw-r--r--addons/project/static/src/js/project.js12
5 files changed, 134 insertions, 39 deletions
diff --git a/addons/project/project.py b/addons/project/project.py
index 3b0057c67b7..62c1b34dbf9 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -178,7 +178,18 @@ class project(osv.osv):
res = super(project, self).unlink(cr, uid, ids, *args, **kwargs)
mail_alias.unlink(cr, uid, alias_ids, *args, **kwargs)
return res
-
+
+ def _get_attached_docs(self, cr, uid, ids, field_name, arg, context):
+ res = {}
+ attachment = self.pool.get('ir.attachment')
+ task = self.pool.get('project.task')
+ for id in ids:
+ project_attachments = attachment.search(cr, uid, [('res_model', '=', 'project.project'), ('res_id', 'in', [id])], context=context, count=True)
+ task_ids = task.search(cr, uid, [('project_id', 'in', [id])], context=context)
+ task_attachments = attachment.search(cr, uid, [('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)], context=context, count=True)
+ res[id] = project_attachments or 0 + task_attachments or 0
+ return res
+
def _task_count(self, cr, uid, ids, field_name, arg, context=None):
res = dict.fromkeys(ids, 0)
task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)])
@@ -189,7 +200,26 @@ class project(osv.osv):
def _get_alias_models(self, cr, uid, context=None):
"""Overriden in project_issue to offer more options"""
return [('project.task', "Tasks")]
-
+
+ def attachment_tree_view(self, cr, uid, ids, context):
+ task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)])
+ domain = [
+ ('|',
+ '&', 'res_model', '=', 'project.project'), ('res_id', 'in', ids),
+ '&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)
+ ]
+ res_id = ids and ids[0] or False
+ return {
+ 'name': _('Attachments'),
+ 'domain': domain,
+ 'res_model': 'ir.attachment',
+ 'type': 'ir.actions.act_window',
+ 'view_id': False,
+ 'view_mode': 'tree,form',
+ 'view_type': 'form',
+ 'limit': 80,
+ 'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, res_id)
+ }
# Lambda indirection method to avoid passing a copy of the overridable method when declaring the field
_alias_models = lambda self, *args, **kwargs: self._get_alias_models(*args, **kwargs)
_columns = {
@@ -232,6 +262,7 @@ class project(osv.osv):
help="The kind of document created when an email is received on this project's email alias"),
'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility', required=True),
'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,),
+ 'doc_count':fields.function(_get_attached_docs, string="Number of documents attached", type='int')
}
def _get_type_common(self, cr, uid, context):
@@ -728,11 +759,11 @@ class task(base_stage, osv.osv):
If the case needs to be reviewed then the status is \
set to \'Pending\'.'),
'categ_ids': fields.many2many('project.category', string='Tags'),
- 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready To Pull')], 'Kanban State',
+ 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
help="A task's kanban state indicates special situations affecting it:\n"
" * Normal is the default situation\n"
" * Blocked indicates something is preventing the progress of this task\n"
- " * Ready To Pull indicates the task is ready to be pulled to the next stage",
+ " * Ready for next stage indicates the task is ready to be pulled to the next stage",
readonly=True, required=False),
'create_date': fields.datetime('Create Date', readonly=True,select=True),
'date_start': fields.datetime('Starting Date',select=True),
@@ -792,6 +823,11 @@ class task(base_stage, osv.osv):
"""
return self.write(cr, uid, ids, {'priority' : priority})
+ def set_very_high_priority(self, cr, uid, ids, *args):
+ """Set task priority to very high
+ """
+ return self.set_priority(cr, uid, ids, '0')
+
def set_high_priority(self, cr, uid, ids, *args):
"""Set task priority to high
"""
@@ -1093,13 +1129,36 @@ class task(base_stage, osv.osv):
}, context=context)
return True
- def create(self, cr, uid, vals, context=None):
- task_id = super(task, self).create(cr, uid, vals, context=context)
+ def _subscribe_project_followers_to_task(self, cr, uid, task_id, context=None):
+ """ TDE note: not the best way to do this, we could override _get_followers
+ of task, and perform a better mapping of subtypes than a mapping
+ based on names.
+ However we will keep this implementation, maybe to be refactored
+ in 7.1 of future versions. """
+ # task followers are project followers, with matching subtypes
task_record = self.browse(cr, uid, task_id, context=context)
+ subtype_obj = self.pool.get('mail.message.subtype')
+ follower_obj = self.pool.get('mail.followers')
if task_record.project_id:
- project_follower_ids = [follower.id for follower in task_record.project_id.message_follower_ids]
- self.message_subscribe(cr, uid, [task_id], project_follower_ids,
- context=context)
+ # create mapping
+ task_subtype_ids = subtype_obj.search(cr, uid, ['|', ('res_model', '=', False), ('res_model', '=', self._name)], context=context)
+ task_subtypes = subtype_obj.browse(cr, uid, task_subtype_ids, context=context)
+ # fetch subscriptions
+ follower_ids = follower_obj.search(cr, uid, [('res_model', '=', 'project.project'), ('res_id', '=', task_record.project_id.id)], context=context)
+ # copy followers
+ for follower in follower_obj.browse(cr, uid, follower_ids, context=context):
+ if not follower.subtype_ids:
+ continue
+ project_subtype_names = [project_subtype.name for project_subtype in follower.subtype_ids]
+ task_subtype_ids = [task_subtype.id for task_subtype in task_subtypes if task_subtype.name in project_subtype_names]
+ self.message_subscribe(cr, uid, [task_id], [follower.partner_id.id],
+ subtype_ids=task_subtype_ids, context=context)
+
+ def create(self, cr, uid, vals, context=None):
+ task_id = super(task, self).create(cr, uid, vals, context=context)
+ # subscribe project followers to the task
+ self._subscribe_project_followers_to_task(cr, uid, task_id, context=context)
+
self._store_history(cr, uid, [task_id], context=context)
self.create_send_note(cr, uid, [task_id], context=context)
return task_id
@@ -1109,9 +1168,6 @@ class task(base_stage, osv.osv):
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
- if vals.get('project_id'):
- project_id = self.pool.get('project.project').browse(cr, uid, vals.get('project_id'), context=context)
- vals['message_follower_ids'] = [(4, follower.id) for follower in project_id.message_follower_ids]
if vals and not 'kanban_state' in vals and 'stage_id' in vals:
new_stage = vals.get('stage_id')
vals_reset_kstate = dict(vals, kanban_state='normal')
@@ -1121,13 +1177,18 @@ class task(base_stage, osv.osv):
#if new_stage not in stages:
#raise osv.except_osv(_('Warning!'), _('Stage is not defined in the project.'))
write_vals = vals_reset_kstate if t.stage_id != new_stage else vals
- super(task,self).write(cr, uid, [t.id], write_vals, context=context)
+ super(task, self).write(cr, uid, [t.id], write_vals, context=context)
self.stage_set_send_note(cr, uid, [t.id], new_stage, context=context)
result = True
else:
- result = super(task,self).write(cr, uid, ids, vals, context=context)
+ result = super(task, self).write(cr, uid, ids, vals, context=context)
if ('stage_id' in vals) or ('remaining_hours' in vals) or ('user_id' in vals) or ('state' in vals) or ('kanban_state' in vals):
self._store_history(cr, uid, ids, context=context)
+
+ # subscribe new project followers to the task
+ if vals.get('project_id'):
+ for id in ids:
+ self._subscribe_project_followers_to_task(cr, uid, id, context=context)
return result
def unlink(self, cr, uid, ids, context=None):
@@ -1289,7 +1350,7 @@ class account_analytic_account(osv.osv):
_inherit = 'account.analytic.account'
_description = 'Analytic Account'
_columns = {
- 'use_tasks': fields.boolean('Tasks',help="If check,this contract will be available in the project menu and you will be able to manage tasks or track issues"),
+ 'use_tasks': fields.boolean('Tasks',help="If checked, this contract will be available in the project menu and you will be able to manage tasks or track issues"),
'company_uom_id': fields.related('company_id', 'project_time_mode_id', type='many2one', relation='product.uom'),
}
@@ -1398,7 +1459,7 @@ class project_task_history(osv.osv):
'task_id': fields.many2one('project.task', 'Task', ondelete='cascade', required=True, select=True),
'type_id': fields.many2one('project.task.type', 'Stage'),
'state': fields.selection([('draft', 'New'), ('cancelled', 'Cancelled'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done')], 'Status'),
- 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready To Pull')], 'Kanban State', required=False),
+ 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State', required=False),
'date': fields.date('Date', select=True),
'end_date': fields.function(_get_date, string='End Date', type="date", store={
'project.task.history': (_get_related_date, None, 20)
diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml
index 560c1c3552a..4f467c89a29 100644
--- a/addons/project/project_data.xml
+++ b/addons/project/project_data.xml
@@ -86,37 +86,37 @@
<!-- mail: subtypes -->
<record id="mt_project_new" model="mail.message.subtype">
- <field name="name">New Task</field>
+ <field name="name">New</field>
<field name="res_model">project.project</field>
<field name="default" eval="False"/>
</record>
<record id="mt_project_closed" model="mail.message.subtype">
- <field name="name">Task Closed</field>
+ <field name="name">Closed</field>
<field name="res_model">project.project</field>
</record>
<record id="mt_project_canceled" model="mail.message.subtype">
- <field name="name">Task Canceled</field>
- <field name="res_model">project.task</field>
+ <field name="name">Canceled</field>
+ <field name="res_model">project.project</field>
</record>
<record id="mt_project_stage" model="mail.message.subtype">
- <field name="name">Task Stage Changed</field>
- <field name="res_model">project.task</field>
+ <field name="name">Stage Changed</field>
+ <field name="res_model">project.project</field>
</record>
<record id="mt_task_new" model="mail.message.subtype">
- <field name="name">New Task</field>
+ <field name="name">New</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_closed" model="mail.message.subtype">
- <field name="name">Task Closed</field>
+ <field name="name">Closed</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_canceled" model="mail.message.subtype">
- <field name="name">Task canceled</field>
+ <field name="name">Canceled</field>
<field name="res_model">project.task</field>
</record>
<record id="mt_task_change" model="mail.message.subtype">
- <field name="name">Task Stage Changed</field>
+ <field name="name">Stage Changed</field>
<field name="res_model">project.task</field>
</record>
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index 29be26bc127..ec29b19e97b 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -63,8 +63,8 @@
<field name="arch" type="xml">
<form string="Project" version="7.0">
<header>
- <button name="set_done" string="Close Project" type="object" states="open,pending"/>
<button name="set_open" string="Re-open project" type="object" states="pending" class="oe_highlight"/>
+ <button name="set_done" string="Close Project" type="object" states="open,pending"/>
<button name="set_open" string="Re-open project" type="object" states="cancelled,close"/>
<button name="set_pending" string="Pending" type="object" states="open"/>
<button name="set_template" string="Set as Template" type="object" states="open"/>
@@ -88,7 +88,7 @@
<div class="oe_right oe_button_box" name="buttons">
<button name="%(act_project_project_2_project_task_all)d" string="Tasks"
type="action" attrs="{'invisible':[('use_tasks','=', 0)]}"/>
- <button name="%(base.action_attachment)d" string="Documents" type="action"/>
+ <button name="attachment_tree_view" string="Documents" type="object"/>
</div>
<group>
<group>
@@ -223,6 +223,7 @@
<field name="task_count"/>
<field name="alias_id"/>
<field name="alias_domain"/>
+ <field name="doc_count"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_color_#{kanban_getcolor(record.color.raw_value)} oe_kanban_card oe_kanban_project oe_kanban_global_click">
@@ -238,7 +239,13 @@
<h4><field name="name"/></h4>
<div class="oe_kanban_project_list">
- <a t-if="record.use_tasks.raw_value" name="%(act_project_project_2_project_task_all)d" type="action" style="margin-right: 10px"> <field name="task_count"/> Tasks</a>
+ <a t-if="record.use_tasks.raw_value" name="%(act_project_project_2_project_task_all)d" type="action" style="margin-right: 10px">
+ <span t-if="record.task_count.raw_value gt 1"><field name="task_count"/> Tasks</span>
+ <span t-if="record.task_count.raw_value lt 2"><field name="task_count"/> Task</span>
+ </a>
+ </div>
+ <div class="oe_kanban_project_list">
+ <a t-if="record.doc_count.raw_value" name="attachment_tree_view" type="object" style="margin-right: 10px"> <field name="doc_count"/> Documents</a>
</div>
<div class="oe_kanban_footer_left">
<span groups="project.group_time_work_estimation_tasks">
@@ -389,7 +396,7 @@
states="cancelled,done" context="{'button_reactivate':True}" groups="base.group_user"/>
<button name="action_close" string="Done" type="object"
states="draft,open,pending" groups="base.group_user"/>
- <button name="do_cancel" string="Cancel" type="object"
+ <button name="do_cancel" string="Cancel Task" type="object"
states="draft,open,pending" groups="base.group_user"/>
<field name="stage_id" widget="statusbar" clickable="True"/>
</header>
@@ -403,12 +410,15 @@
<field name="project_id" on_change="onchange_project(project_id)" context="{'default_use_tasks':1}"/>
<field name="user_id" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}" options='{"no_open": True}'/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
+ <field name="planned_hours" widget="float_time"
+ groups="project.group_time_work_estimation_tasks"
+ on_change="onchange_planned(planned_hours, effective_hours)"/>
</group>
<group>
<field name="date_deadline" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>
<field name="categ_ids" widget="many2many_tags"/>
<field name="progress" widget="progressbar"
- groups="project.group_time_work_estimation_tasks"/>
+ groups="project.group_time_work_estimation_tasks" attrs="{'invisible':[('state','=','cancelled')]}"/>
</group>
</group>
<notebook>
@@ -458,9 +468,6 @@
<field name="priority" groups="base.group_user"/>
<field name="sequence"/>
<field name="partner_id"/>
- <field name="planned_hours" widget="float_time" attrs="{'readonly':[('state','!=','draft')]}"
- groups="project.group_time_work_estimation_tasks"
- on_change="onchange_planned(planned_hours, effective_hours)"/>
<field name="state" invisible="1"/>
</group>
</page>
@@ -525,10 +532,11 @@
</div>
<div class="oe_kanban_bottom_right">
<a t-if="record.kanban_state.raw_value === 'normal'" type="object" string="In Progress" name="set_kanban_state_done" class="oe_kanban_status"> </a>
- <a t-if="record.kanban_state.raw_value === 'done'" type="object" string="Stage Done" name="set_kanban_state_blocked" class="oe_kanban_status oe_kanban_status_green"> </a>
+ <a t-if="record.kanban_state.raw_value === 'done'" type="object" string="Ready for next stage" name="set_kanban_state_blocked" class="oe_kanban_status oe_kanban_status_green"> </a>
<a t-if="record.kanban_state.raw_value === 'blocked'" type="object" string="Blocked" name="set_kanban_state_normal" class="oe_kanban_status oe_kanban_status_red"> </a>
- <a t-if="record.priority.raw_value == 1" type="object" string="Priority" name="set_normal_priority" class="oe_e oe_star_on">7</a>
- <a t-if="record.priority.raw_value != 1" type="object" string="Priority" name="set_high_priority" class="oe_e oe_star_off">7</a>
+ <a t-if="record.priority.raw_value == 2" type="object" string="Important" name="set_very_high_priority" class="oe_e oe_star_on">7</a>
+ <a t-if="record.priority.raw_value == 1 or record.priority.raw_value == 3 or record.priority.raw_value == 4" type="object" string="Normal" name="set_normal_priority" class="oe_e oe_star_off">7</a>
+ <a t-if="record.priority.raw_value == 0" type="object" string="Very Important" name="set_high_priority" class="oe_e oe_star_very_high_priority">7</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"/>
</div>
<div class="oe_kanban_footer_left">
diff --git a/addons/project/static/src/css/project.css b/addons/project/static/src/css/project.css
index 8381e6d68d3..5f7361e2ad7 100644
--- a/addons/project/static/src/css/project.css
+++ b/addons/project/static/src/css/project.css
@@ -2,13 +2,29 @@
margin: 2px;
}
+.openerp .oe_kanban_view .oe_kanban_content .oe_star_very_high_priority {
+ color: #cccccc;
+ text-shadow: 0 0 2px black;
+ vertical-align: top;
+ position: relative;
+ top: -5px;
+}
+.openerp .oe_kanban_view .oe_kanban_content .oe_star_very_high_priority:hover {
+ text-decoration: none;
+}
+
+.openerp .oe_kanban_view .oe_kanban_content .oe_star_very_high_priority {
+ color: red;
+}
+
.oe_kanban_project {
width: 220px;
min-height: 160px;
}
-.oe_kanban_project_list {
+.oe_kanban_project_list > a > span:hover{
margin: 4px 0;
+ text-decoration: underline;
}
.openerp .oe_kanban_content h4 {
diff --git a/addons/project/static/src/js/project.js b/addons/project/static/src/js/project.js
index 194cc186f27..59eeba4d191 100644
--- a/addons/project/static/src/js/project.js
+++ b/addons/project/static/src/js/project.js
@@ -57,6 +57,12 @@ openerp.project = function(openerp) {
} else if (self.dataset.model === 'project.task') {
self.project_display_categ_names();
}
+ },
+ on_record_moved: function(record, old_group, old_index, new_group, new_index){
+ var self = this;
+ this._super.apply(this, arguments);
+ if(new_group.state.folded)
+ new_group.do_action_toggle_fold();
}
});
@@ -67,6 +73,10 @@ openerp.project = function(openerp) {
} else {
this._super.apply(this, arguments);
}
- }
+ },
+ bind_events: function() {
+ this._super();
+ this.view.project_display_categ_names();
+ },
});
};