diff options
| author | Denis Ledoux dle@openerp.com <> | 2012-11-08 10:28:38 +0100 |
|---|---|---|
| committer | Denis Ledoux dle@openerp.com <> | 2012-11-08 10:28:38 +0100 |
| commit | 981168dfc6d628c72d8469022c0b51e0f5ec00df (patch) | |
| tree | e7497e1366dcd0b7759ed6eae9d2fa8b50ff46c5 /addons/project | |
| parent | 9e56b3b6a8a761e02099146b338225d29dbc8c8a (diff) | |
| parent | 29fffbc97d8970513114b7b76763f002aa73e395 (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.py | 93 | ||||
| -rw-r--r-- | addons/project/project_data.xml | 20 | ||||
| -rw-r--r-- | addons/project/project_view.xml | 30 | ||||
| -rw-r--r-- | addons/project/static/src/css/project.css | 18 | ||||
| -rw-r--r-- | addons/project/static/src/js/project.js | 12 |
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(); + }, }); }; |
