[FIX] Propagated ir.needaction class name change.
[odoo/odoo.git] / addons / project_issue / project_issue.py
index 196d436..e61af23 100644 (file)
@@ -26,7 +26,9 @@ from tools.translate import _
 import binascii
 import time
 import tools
+from crm import wizard
 
+wizard.mail_compose_message.SUPPORTED_MODELS.append('project.issue')
 
 class project_issue_version(osv.osv):
     _name = "project.issue.version"
@@ -43,43 +45,10 @@ project_issue_version()
 class project_issue(crm.crm_case, osv.osv):
     _name = "project.issue"
     _description = "Project Issue"
-    _order = "priority, id desc"
-    _inherit = ['mailgate.thread']
-
-    def case_open(self, cr, uid, ids, *args):
-        """
-        @param self: The object pointer
-        @param cr: the current row, from the database cursor,
-        @param uid: the current user’s ID for security checks,
-        @param ids: List of case's Ids
-        @param *args: Give Tuple Value
-        """
-
-        res = super(project_issue, self).case_open(cr, uid, ids, *args)
-        self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
-        for (id, name) in self.name_get(cr, uid, ids):
-            message = _('Issue ') + " '" + name + "' "+ _("is Open.")
-            self.log(cr, uid, id, message)
-        return res
-
-    def case_close(self, cr, uid, ids, *args):
-        """
-        @param self: The object pointer
-        @param cr: the current row, from the database cursor,
-        @param uid: the current user’s ID for security checks,
-        @param ids: List of case's Ids
-        @param *args: Give Tuple Value
-        """
-
-        res = super(project_issue, self).case_close(cr, uid, ids, *args)
-        for (id, name) in self.name_get(cr, uid, ids):
-            message = _('Issue ') + " '" + name + "' "+ _("is Closed.")
-            self.log(cr, uid, id, message)
-        return res
+    _order = "priority, create_date desc"
+    _inherit = ['ir.needaction_mixin', 'mail.thread']
 
     def _compute_day(self, cr, uid, ids, fields, args, context=None):
-        if context is None:
-            context = {}
         """
         @param cr: the current row, from the database cursor,
         @param uid: the current user’s ID for security checks,
@@ -92,32 +61,45 @@ class project_issue(crm.crm_case, osv.osv):
 
         res = {}
         for issue in self.browse(cr, uid, ids, context=context):
+            res[issue.id] = {}
             for field in fields:
-                res[issue.id] = {}
                 duration = 0
                 ans = False
                 hours = 0
 
+                date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
                 if field in ['working_hours_open','day_open']:
                     if issue.date_open:
-                        date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
                         date_open = datetime.strptime(issue.date_open, "%Y-%m-%d %H:%M:%S")
                         ans = date_open - date_create
                         date_until = issue.date_open
                         #Calculating no. of working hours to open the issue
-                        hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id,
-                                 datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'),
-                                 datetime.strptime(issue.date_open, '%Y-%m-%d %H:%M:%S'))
+                        if issue.project_id.resource_calendar_id:
+                            hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id,
+                                                           date_create,
+                                                           date_open)
                 elif field in ['working_hours_close','day_close']:
                     if issue.date_closed:
-                        date_create = datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
                         date_close = datetime.strptime(issue.date_closed, "%Y-%m-%d %H:%M:%S")
                         date_until = issue.date_closed
                         ans = date_close - date_create
                         #Calculating no. of working hours to close the issue
-                        hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id,
-                                datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'),
-                                datetime.strptime(issue.date_closed, '%Y-%m-%d %H:%M:%S'))
+                        if issue.project_id.resource_calendar_id:
+                            hours = cal_obj.interval_hours_get(cr, uid, issue.project_id.resource_calendar_id.id,
+                               date_create,
+                               date_close)
+                elif field in ['days_since_creation']:
+                    if issue.create_date:
+                        days_since_creation = datetime.today() - datetime.strptime(issue.create_date, "%Y-%m-%d %H:%M:%S")
+                        res[issue.id][field] = days_since_creation.days
+                    continue
+
+                elif field in ['inactivity_days']:
+                    res[issue.id][field] = 0
+                    if issue.date_action_last:
+                        inactive_days = datetime.today() - datetime.strptime(issue.date_action_last, '%Y-%m-%d %H:%M:%S')
+                        res[issue.id][field] = inactive_days.days
+                    continue
                 if ans:
                     resource_id = False
                     if issue.user_id:
@@ -127,7 +109,11 @@ class project_issue(crm.crm_case, osv.osv):
                     duration = float(ans.days)
                     if issue.project_id and issue.project_id.resource_calendar_id:
                         duration = float(ans.days) * 24
-                        new_dates = cal_obj.interval_min_get(cr, uid, issue.project_id.resource_calendar_id.id, datetime.strptime(issue.create_date, '%Y-%m-%d %H:%M:%S'), duration, resource=resource_id)
+
+                        new_dates = cal_obj.interval_min_get(cr, uid,
+                                                             issue.project_id.resource_calendar_id.id,
+                                                             date_create,
+                                                             duration, resource=resource_id)
                         no_days = []
                         date_until = datetime.strptime(date_until, '%Y-%m-%d %H:%M:%S')
                         for in_time, out_time in new_dates:
@@ -136,24 +122,41 @@ class project_issue(crm.crm_case, osv.osv):
                             if out_time > date_until:
                                 break
                         duration = len(no_days)
+
                 if field in ['working_hours_open','working_hours_close']:
                     res[issue.id][field] = hours
                 else:
                     res[issue.id][field] = abs(float(duration))
+
+        return res
+
+    def _hours_get(self, cr, uid, ids, field_names, args, context=None):
+        task_pool = self.pool.get('project.task')
+        res = {}
+        for issue in self.browse(cr, uid, ids, context=context):
+            progress = 0.0
+            if issue.task_id:
+                progress = task_pool._hours_get(cr, uid, [issue.task_id.id], field_names, args, context=context)[issue.task_id.id]['progress']
+            res[issue.id] = {'progress' : progress}
         return res
 
+    def _get_project(self, cr, uid, context=None):
+        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
+        if user.context_project_id:
+            return user.context_project_id.id
+        return False
+
+    def on_change_project(self, cr, uid, ids, project_id, context=None):
+        return {}
+
     def _get_issue_task(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
         issues = []
         issue_pool = self.pool.get('project.issue')
         for task in self.pool.get('project.task').browse(cr, uid, ids, context=context):
-            issues += issue_pool.search(cr, uid, [('task_id','=',task.id)])            
+            issues += issue_pool.search(cr, uid, [('task_id','=',task.id)])
         return issues
 
     def _get_issue_work(self, cr, uid, ids, context=None):
-        if context is None:
-            context = {}
         issues = []
         issue_pool = self.pool.get('project.issue')
         for work in self.pool.get('project.task.work').browse(cr, uid, ids, context=context):
@@ -161,68 +164,57 @@ class project_issue(crm.crm_case, osv.osv):
                 issues += issue_pool.search(cr, uid, [('task_id','=',work.task_id.id)])
         return issues
 
-    def _hours_get(self, cr, uid, ids, field_names, args, context=None):
-        task_pool = self.pool.get('project.task')
-        res = {}
-        for issue in self.browse(cr, uid, ids, context=context):
-            progress = 0.0
-            if issue.task_id:
-                progress = task_pool._hours_get(cr, uid, [issue.task_id.id], field_names, args, context=context)[issue.task_id.id]['progress']
-            res[issue.id] = {'progress' : progress}     
-        return res        
-
     _columns = {
-        'id': fields.integer('ID'),
+        'id': fields.integer('ID', readonly=True),
         'name': fields.char('Issue', size=128, required=True),
         'active': fields.boolean('Active', required=False),
-        'create_date': fields.datetime('Creation Date', readonly=True),
+        'create_date': fields.datetime('Creation Date', readonly=True,select=True),
         'write_date': fields.datetime('Update Date', readonly=True),
+        'days_since_creation': fields.function(_compute_day, string='Days since creation date', \
+                                               multi='compute_day', type="integer", help="Difference in days between creation date and current date"),
         'date_deadline': fields.date('Deadline'),
         'section_id': fields.many2one('crm.case.section', 'Sales Team', \
                         select=True, help='Sales team to which Case belongs to.\
                              Define Responsible user and Email account for mail gateway.'),
-        'user_id': fields.many2one('res.users', 'Responsible'),
-        'partner_id': fields.many2one('res.partner', 'Partner'),
-        'partner_address_id': fields.many2one('res.partner.address', 'Partner Contact', \
-                                 domain="[('partner_id','=',partner_id)]"),
+        'partner_id': fields.many2one('res.partner', 'Partner', select=1),
         'company_id': fields.many2one('res.company', 'Company'),
         'description': fields.text('Description'),
-        'state': fields.selection([('draft', 'Draft'), ('open', 'To Do'), ('cancel', 'Cancelled'), ('done', 'Closed'),('pending', 'Pending'), ], 'State', size=16, readonly=True,
+        'state': fields.selection([('draft', 'New'), ('open', 'In Progress'), ('cancel', 'Cancelled'), ('done', 'Done'),('pending', 'Pending'), ], 'State', size=16, readonly=True,
                                   help='The state is set to \'Draft\', when a case is created.\
                                   \nIf the case is in progress the state is set to \'Open\'.\
                                   \nWhen the case is over, the state is set to \'Done\'.\
                                   \nIf the case needs to be reviewed then the state is set to \'Pending\'.'),
-        'email_from': fields.char('Email', size=128, help="These people will receive email."),
+        'email_from': fields.char('Email', size=128, help="These people will receive email.", select=1),
         'email_cc': fields.char('Watchers Emails', size=256, help="These email addresses will be added to the CC field of all inbound and outbound emails for this record before being sent. Separate multiple email addresses with a comma"),
-        'date_open': fields.datetime('Opened', readonly=True),
+        'date_open': fields.datetime('Opened', readonly=True,select=True),
         # Project Issue fields
-        'date_closed': fields.datetime('Closed', readonly=True),
+        'date_closed': fields.datetime('Closed', readonly=True,select=True),
         'date': fields.datetime('Date'),
-        'canal_id': fields.many2one('res.partner.canal', 'Channel', help="The channels represent the different communication modes available with the customer." \
-                                                                        " With each commercial opportunity, you can indicate the canall which is this opportunity source."),
+        'channel_id': fields.many2one('crm.case.channel', 'Channel', help="Communication channel."),
         'categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('object_id.model', '=', 'crm.project.bug')]"),
-        'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority'),
+        'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
         'version_id': fields.many2one('project.issue.version', 'Version'),
-        'partner_name': fields.char("Employee's Name", size=64),
-        'partner_mobile': fields.char('Mobile', size=32),
-        'partner_phone': fields.char('Phone', size=32),
-        'type_id': fields.many2one ('project.task.type', 'Resolution'),
+        'type_id': fields.many2one ('project.task.type', 'Stages', domain="[('project_ids', '=', project_id)]"),
         'project_id':fields.many2one('project.project', 'Project'),
         'duration': fields.float('Duration'),
         'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"),
         'day_open': fields.function(_compute_day, string='Days to Open', \
-                                method=True, multi='day_open', type="float", store=True),
+                                multi='compute_day', type="float", store=True),
         'day_close': fields.function(_compute_day, string='Days to Close', \
-                                method=True, multi='day_close', type="float", store=True),
-        'assigned_to': fields.related('task_id', 'user_id', string = 'Assigned to', type="many2one", relation="res.users", store=True, help='This is the current user to whom the related task have been assigned'),
+                                multi='compute_day', type="float", store=True),
+        'user_id': fields.many2one('res.users', 'Assigned to', required=False, select=1),
         'working_hours_open': fields.function(_compute_day, string='Working Hours to Open the Issue', \
-                                method=True, multi='working_days_open', type="float", store=True),
+                                multi='compute_day', type="float", store=True),
         'working_hours_close': fields.function(_compute_day, string='Working Hours to Close the Issue', \
-                                method=True, multi='working_days_close', type="float", store=True),
-        'message_ids': fields.one2many('mailgate.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
+                                multi='compute_day', type="float", store=True),
+        'inactivity_days': fields.function(_compute_day, string='Days since last action', \
+                                multi='compute_day', type="integer", help="Difference in days between last action and current date"),
+        'color': fields.integer('Color Index'),
+        'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
+        'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
         'date_action_last': fields.datetime('Last Action', readonly=1),
         'date_action_next': fields.datetime('Next Action', readonly=1),
-        'progress': fields.function(_hours_get, method=True, string='Progress (%)', multi='hours', group_operator="avg", help="Computed as: Time Spent / Total Time.",
+        'progress': fields.function(_hours_get, string='Progress (%)', multi='hours', group_operator="avg", help="Computed as: Time Spent / Total Time.",
             store = {
                 'project.issue': (lambda self, cr, uid, ids, c={}: ids, ['task_id'], 10),
                 'project.task': (_get_issue_task, ['progress'], 10),
@@ -230,33 +222,42 @@ class project_issue(crm.crm_case, osv.osv):
             }),
     }
 
-    def _get_project(self, cr, uid, context):
-        user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
-        if user.context_project_id:
-            return user.context_project_id.id
-        return False
 
     _defaults = {
         'active': 1,
-        'user_id': crm.crm_case._get_default_user,
         'partner_id': crm.crm_case._get_default_partner,
-        'partner_address_id': crm.crm_case._get_default_partner_address,
-        'email_from': crm.crm_case. _get_default_email,
+        'email_from': crm.crm_case._get_default_email,
         'state': 'draft',
-        'section_id': crm.crm_case. _get_section,
+        'section_id': crm.crm_case._get_section,
         'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
         'priority': crm.AVAILABLE_PRIORITIES[2][0],
         'project_id':_get_project,
-    }
+        'categ_id' : lambda *a: False,
+         }
+
+    def set_priority(self, cr, uid, ids, priority):
+        """Set lead priority
+        """
+        return self.write(cr, uid, ids, {'priority' : priority})
+
+    def set_high_priority(self, cr, uid, ids, *args):
+        """Set lead priority to high
+        """
+        return self.set_priority(cr, uid, ids, '1')
+
+    def set_normal_priority(self, cr, uid, ids, *args):
+        """Set lead priority to normal
+        """
+        return self.set_priority(cr, uid, ids, '3')
 
     def convert_issue_task(self, cr, uid, ids, context=None):
+        if context is None:
+            context = {}
+        
         case_obj = self.pool.get('project.issue')
         data_obj = self.pool.get('ir.model.data')
         task_obj = self.pool.get('project.task')
-
-        if context is None:
-            context = {}
-
+        
         result = data_obj._get_id(cr, uid, 'project', 'view_task_search_form')
         res = data_obj.read(cr, uid, result, ['res_id'])
         id2 = data_obj._get_id(cr, uid, 'project', 'view_task_form2')
@@ -271,18 +272,20 @@ class project_issue(crm.crm_case, osv.osv):
                 'name': bug.name,
                 'partner_id': bug.partner_id.id,
                 'description':bug.description,
-                'date': bug.date,
+                'date_deadline': bug.date,
                 'project_id': bug.project_id.id,
-                'priority': bug.priority,
-                'user_id': bug.assigned_to.id,
+                # priority must be in ['0','1','2','3','4'], while bug.priority is in ['1','2','3','4','5']
+                'priority': str(int(bug.priority) - 1),
+                'user_id': bug.user_id.id,
                 'planned_hours': 0.0,
             })
-
             vals = {
                 'task_id': new_task_id,
-                'state':'open'
+                'state':'pending'
             }
-            case_obj.write(cr, uid, [bug.id], vals)
+            self.convert_to_task_send_note(cr, uid, [bug.id], context=context)
+            case_obj.write(cr, uid, [bug.id], vals, context=context)
+            self.case_pending_send_note(cr, uid, [bug.id], context=context)
 
         return  {
             'name': _('Tasks'),
@@ -297,6 +300,7 @@ class project_issue(crm.crm_case, osv.osv):
             'nodestroy': True
         }
 
+
     def _convert(self, cr, uid, ids, xml_id, context=None):
         data_obj = self.pool.get('ir.model.data')
         id2 = data_obj._get_id(cr, uid, 'project_issue', xml_id)
@@ -313,48 +317,66 @@ class project_issue(crm.crm_case, osv.osv):
     def convert_to_bug(self, cr, uid, ids, context=None):
         return self._convert(cr, uid, ids, 'bug_categ', context=context)
 
-    def next_type(self, cr, uid, ids, *args):
+    def next_type(self, cr, uid, ids, context=None):
         for task in self.browse(cr, uid, ids):
             typeid = task.type_id.id
             types = map(lambda x:x.id, task.project_id.type_ids or [])
             if types:
                 if not typeid:
-                    self.write(cr, uid, task.id, {'type_id': types[0]})
+                    self.write(cr, uid, [task.id], {'type_id': types[0]})
                 elif typeid and typeid in types and types.index(typeid) != len(types)-1 :
                     index = types.index(typeid)
-                    self.write(cr, uid, task.id, {'type_id': types[index+1]})
+                    self.write(cr, uid, [task.id], {'type_id': types[index+1]})
         return True
 
-    def prev_type(self, cr, uid, ids, *args):
+    def prev_type(self, cr, uid, ids, context=None):
         for task in self.browse(cr, uid, ids):
             typeid = task.type_id.id
             types = map(lambda x:x.id, task.project_id and task.project_id.type_ids or [])
             if types:
                 if typeid and typeid in types:
                     index = types.index(typeid)
-                    self.write(cr, uid, task.id, {'type_id': index and types[index-1] or False})
+                    self.write(cr, uid, [task.id], {'type_id': index and types[index-1] or False})
         return True
 
+    def write(self, cr, uid, ids, vals, context=None):
+        #Update last action date every time the user change the stage, the state or send a new email
+        logged_fields = ['type_id', 'state', 'message_ids']
+        if any([field in vals for field in logged_fields]):
+            vals['date_action_last'] = time.strftime('%Y-%m-%d %H:%M:%S')
+        if vals.get('type_id', False):
+            stage = self.pool.get('project.task.type').browse(cr, uid, vals['type_id'], context=context)
+            self.message_append_note(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % stage.name, context=context)
+        return super(project_issue, self).write(cr, uid, ids, vals, context)
+
     def onchange_task_id(self, cr, uid, ids, task_id, context=None):
-        if context is None:
-            context = {}
         result = {}
         if not task_id:
             return {'value':{}}
-        task = self.pool.get('project.task').browse(cr, uid, task_id, context)
-        return {'value':{'assigned_to': task.user_id.id,}}
+        task = self.pool.get('project.task').browse(cr, uid, task_id, context=context)
+        return {'value':{'user_id': task.user_id.id,}}
 
-    def case_escalate(self, cr, uid, ids, *args):
-        """Escalates case to top level
-        @param self: The object pointer
-        @param cr: the current row, from the database cursor,
-        @param uid: the current user’s ID for security checks,
-        @param ids: List of case Ids
-        @param *args: Tuple Value for additional Params
+    def case_reset(self, cr, uid, ids, context=None):
+        """Resets case as draft
         """
+        res = super(project_issue, self).case_reset(cr, uid, ids, context)
+        self.write(cr, uid, ids, {'date_open': False, 'date_closed': False})
+        return res
+
+    def create(self, cr, uid, vals, context=None):
+        obj_id = super(project_issue, self).create(cr, uid, vals, context=context)
+        self.create_send_note(cr, uid, [obj_id], context=context)
+        return obj_id
+
+    def case_open(self, cr, uid, ids, context=None):
+        res = super(project_issue, self).case_open(cr, uid, ids, context)
+        self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S'), 'user_id' : uid})
+        return res
+
+    def case_escalate(self, cr, uid, ids, context=None):
         cases = self.browse(cr, uid, ids)
         for case in cases:
-            data = {}
+            data = {'state' : 'draft'}
             if case.project_id.project_escalation_id:
                 data['project_id'] = case.project_id.project_escalation_id.id
                 if case.project_id.project_escalation_id.user_id:
@@ -364,25 +386,17 @@ class project_issue(crm.crm_case, osv.osv):
             else:
                 raise osv.except_osv(_('Warning !'), _('You cannot escalate this issue.\nThe relevant Project has not configured the Escalation Project!'))
             self.write(cr, uid, [case.id], data)
-        self._history(cr, uid, cases, _('Escalate'))
+            self.case_escalate_send_note(cr, uid, [case.id], context)
         return True
 
-    def message_new(self, cr, uid, msg, context):
-        """
-        Automatically calls when new email message arrives
-
-        @param self: The object pointer
-        @param cr: the current row, from the database cursor,
-        @param uid: the current user’s ID for security checks
-        """
-
-        mailgate_pool = self.pool.get('email.server.tools')
-
+    def message_new(self, cr, uid, msg, custom_values=None, context=None):
+        """Automatically called when new email message arrives"""
+        if context is None:
+            context = {}
         subject = msg.get('subject') or _('No Title')
-        body = msg.get('body')
+        body = msg.get('body_text')
         msg_from = msg.get('from')
         priority = msg.get('priority')
-
         vals = {
             'name': subject,
             'email_from': msg_from,
@@ -390,47 +404,28 @@ class project_issue(crm.crm_case, osv.osv):
             'description': body,
             'user_id': False,
         }
-        if msg.get('priority', False):
+        if priority:
             vals['priority'] = priority
-
-        res = mailgate_pool.get_partner(cr, uid, msg.get('from'))
-        if res:
-            vals.update(res)
+        vals.update(self.message_partner_by_email(cr, uid, msg_from))
         context.update({'state_to' : 'draft'})
-        res = self.create(cr, uid, vals, context)
-        message = _('An Issue created') + " '" + subject + "' " + _("from Mailgate.")
-        self.log(cr, uid, res, message)
-        self.convert_to_bug(cr, uid, [res], context=context)
-
-        attachents = msg.get('attachments', [])
-        for attactment in attachents or []:
-            data_attach = {
-                'name': attactment,
-                'datas': binascii.b2a_base64(str(attachents.get(attactment))),
-                'datas_fname': attactment,
-                'description': 'Mail attachment',
-                'res_model': self._name,
-                'res_id': res,
-            }
-            self.pool.get('ir.attachment').create(cr, uid, data_attach)
 
-        return res
+        if custom_values and isinstance(custom_values, dict):
+            vals.update(custom_values)
 
-    def message_update(self, cr, uid, ids, vals={}, msg="", default_act='pending', context=None):
-        if context is None:
-            context = {}
-        """
-        @param self: The object pointer
-        @param cr: the current row, from the database cursor,
-        @param uid: the current user’s ID for security checks,
-        @param ids: List of update mail’s IDs
-        """
+        res_id = self.create(cr, uid, vals, context)
+        self.message_append_dict(cr, uid, [res_id], msg, context=context)
+        self.convert_to_bug(cr, uid, [res_id], context=context)
+        return res_id
+
+    def message_update(self, cr, uid, ids, msg, vals=None, default_act='pending', context=None):
+        if vals is None:
+            vals = {}
 
         if isinstance(ids, (str, int, long)):
             ids = [ids]
 
         vals.update({
-            'description': msg['body']
+            'description': msg['body_text']
         })
         if msg.get('priority', False):
             vals['priority'] = msg.get('priority')
@@ -440,8 +435,14 @@ class project_issue(crm.crm_case, osv.osv):
             'revenue': 'planned_revenue',
             'probability': 'probability'
         }
+
+        # Reassign the 'open' state to the case if this one is in pending or done
+        for record in self.browse(cr, uid, ids, context=context):
+            if record.state in ('pending', 'done'):
+                record.write({'state' : 'open'})
+
         vls = { }
-        for line in msg['body'].split('\n'):
+        for line in msg['body_text'].split('\n'):
             line = line.strip()
             res = tools.misc.command_re.match(line)
             if res and maps.get(res.group(1).lower(), False):
@@ -450,23 +451,10 @@ class project_issue(crm.crm_case, osv.osv):
 
         vals.update(vls)
         res = self.write(cr, uid, ids, vals)
+        self.message_append_dict(cr, uid, ids, msg, context=context)
         return res
 
-    def msg_send(self, cr, uid, id, *args, **argv):
-
-        """ Send The Message
-            @param self: The object pointer
-            @param cr: the current row, from the database cursor,
-            @param uid: the current user’s ID for security checks,
-            @param ids: List of email’s IDs
-            @param *args: Return Tuple Value
-            @param **args: Return Dictionary of Keyword Value
-        """
-        return True
-
     def copy(self, cr, uid, id, default=None, context=None):
-        if not context:
-            context={}
         issue = self.read(cr, uid, id, ['name'], context=context)
         if not default:
             default = {}
@@ -474,23 +462,61 @@ class project_issue(crm.crm_case, osv.osv):
         default['name'] = issue['name'] + _(' (copy)')
         return super(project_issue, self).copy(cr, uid, id, default=default,
                 context=context)
+    
+    # -------------------------------------------------------
+    # OpenChatter methods and notifications
+    # -------------------------------------------------------
+    
+    def get_needaction_user_ids(self, cr, uid, ids, context=None):
+        result = dict.fromkeys(ids, [])
+        for obj in self.browse(cr, uid, ids, context=context):
+            if obj.state == 'draft' and obj.user_id:
+                result[obj.id] = [obj.user_id.id]
+        return result
+    
+    def message_get_subscribers(self, cr, uid, ids, context=None):
+        sub_ids = self.message_get_subscribers_ids(cr, uid, ids, context=context);
+        for obj in self.browse(cr, uid, ids, context=context):
+            if obj.user_id:
+                sub_ids.append(obj.user_id.id)
+        return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
+    
+    def case_get_note_msg_prefix(self, cr, uid, id, context=None):
+        return 'Project issue '
+
+    def convert_to_task_send_note(self, cr, uid, ids, context=None):
+        message = _("Project issue has been <b>converted</b> in to task.")
+        return self.message_append_note(cr, uid, ids, body=message, context=context)
+
+    def create_send_note(self, cr, uid, ids, context=None):
+        message = _("Project issue has been <b>created</b>.")
+        return self.message_append_note(cr, uid, ids, body=message, context=context)
+
+    def case_escalate_send_note(self, cr, uid, ids, context=None):
+        for obj in self.browse(cr, uid, ids, context=context):
+            if obj.project_id:
+                message = _("has been <b>escalated</b> to <em>'%s'</em>.") % (obj.project_id.name)
+                obj.message_append_note(body=message, context=context)
+            else:
+                message = _("has been <b>escalated</b>.")
+                obj.message_append_note(body=message, context=context)
+        return True
 
 project_issue()
 
 class project(osv.osv):
     _inherit = "project.project"
     _columns = {
-        'resource_calendar_id' : fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
         'project_escalation_id' : fields.many2one('project.project','Project Escalation', help='If any issue is escalated from the current Project, it will be listed under the project selected here.', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
         'reply_to' : fields.char('Reply-To Email Address', size=256)
     }
 
-    def _check_escalation(self, cr, uid, ids):
-         project_obj = self.browse(cr, uid, ids[0])
-         if project_obj.project_escalation_id:
-             if project_obj.project_escalation_id.id == project_obj.id:
-                 return False
-         return True
+    def _check_escalation(self, cr, uid, ids, context=None):
+        project_obj = self.browse(cr, uid, ids[0], context=context)
+        if project_obj.project_escalation_id:
+            if project_obj.project_escalation_id.id == project_obj.id:
+                return False
+        return True
 
     _constraints = [
         (_check_escalation, 'Error! You cannot assign escalation to the same project!', ['project_escalation_id'])