[IMP]improve code and add tooltip on task_management
[odoo/odoo.git] / addons / project / project.py
index 16c9a29..ab02cc8 100644 (file)
@@ -19,7 +19,7 @@
 #
 ##############################################################################
 
-from crm import crm
+from base_status.base_stage import base_stage
 from datetime import datetime, date
 from lxml import etree
 from osv import fields, osv
@@ -27,7 +27,7 @@ from openerp.addons.resource.faces import task as Task
 import time
 from tools.translate import _
 
-_TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancel', 'Cancelled')]
+_TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')]
 
 class project_task_type(osv.osv):
     _name = 'project.task.type'
@@ -46,8 +46,8 @@ class project_task_type(osv.osv):
                         help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
     }
     _defaults = {
-        'state': 'draft',
         'sequence': 1,
+        'state': 'draft',
         'fold': False,
     }
     _order = 'sequence'
@@ -165,14 +165,29 @@ class project(osv.osv):
             res[task.project_id.id] += 1
         return res
 
+    def _get_followers(self, cr, uid, ids, name, arg, context=None):
+        '''
+        Functional field that computes the users that are 'following' a thread.
+        '''
+        res = {}
+        for project in self.browse(cr, uid, ids, context=context):
+            l = set()
+            for message in project.message_ids:
+                l.add(message.user_id and message.user_id.id or False)
+            res[project.id] = list(filter(None, l))
+        return res
+
+    def _search_followers(self, cr, uid, obj, name, args, context=None):
+        project_obj = self.pool.get('project.project')
+        project_ids = project_obj.search(cr, uid, [('message_ids.user_id.id', 'in', args[0][2])], context=context)
+        return [('id', 'in', project_ids)]
+
     _columns = {
         'complete_name': fields.function(_complete_name, string="Project Name", type='char', size=250),
         'active': fields.boolean('Active', help="If the active field is set to False, it will allow you to hide the project without removing it."),
         'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of Projects."),
         'analytic_account_id': fields.many2one('account.analytic.account', 'Analytic Account', help="Link this project to an analytic account if you need financial management on projects. It enables you to connect projects with budgets, planning, cost and revenue analysis, timesheets on projects, etc.", ondelete="cascade", required=True),
         'priority': fields.integer('Sequence', help="Gives the sequence order when displaying the list of projects"),
-        'warn_manager': fields.boolean('Notify Manager', help="If you check this field, the project manager will receive an email each time a task is completed by his team.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
-
         'members': fields.many2many('res.users', 'project_user_rel', 'project_id', 'uid', 'Project Members',
             help="Project's members are users who can have an access to the tasks related to this project.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
         'tasks': fields.one2many('project.task', 'project_id', "Task Activities"),
@@ -197,19 +212,18 @@ class project(osv.osv):
                 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
             }),
         'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
-        'warn_customer': fields.boolean('Warn Partner', help="If you check this, the user will have a popup when closing a task that propose a message to send by email to the customer.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
-        'warn_header': fields.text('Mail Header', help="Header added at the beginning of the email for the warning message sent to the customer when a task is closed.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
-        'warn_footer': fields.text('Mail Footer', help="Footer added at the beginning of the email for the warning message sent to the customer when a task is closed.", states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
         'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
-        'use_tasks': fields.boolean('Use Tasks', help="Check this field if this project is aimed at managing tasks"),
         'task_count': fields.function(_task_count, type='integer', string="Open Tasks"),
         'color': fields.integer('Color Index'),
-        'company_uom_id': fields.related('company_id', 'project_time_mode_id', type='many2one', relation='product.uom'),
+        'privacy_visibility': fields.selection([('public','Public'), ('followers','Followers Only')], 'Privacy / Visibility'),
+        'state': fields.selection([('template', 'Template'),('draft','New'),('open','In Progress'), ('cancelled', 'Cancelled'),('pending','Pending'),('close','Closed')], 'Status', required=True,),
+        'followers': fields.function(_get_followers, method=True, fnct_search=_search_followers,
+                        type='many2many', relation='res.users', string='Followers'),
      }
     
     def dummy(self, cr, uid, ids, context):
         return True
-       
+
     def _get_type_common(self, cr, uid, context):
         ids = self.pool.get('project.task.type').search(cr, uid, [('case_default','=',1)], context=context)
         return ids
@@ -217,10 +231,11 @@ class project(osv.osv):
     _order = "sequence"
     _defaults = {
         'active': True,
+        'type': 'contract',
+        'state': 'open',
         'priority': 1,
         'sequence': 10,
         'type_ids': _get_type_common,
-        'use_tasks': True,
     }
 
     # TODO: Why not using a SQL contraints ?
@@ -242,7 +257,7 @@ class project(osv.osv):
     def set_done(self, cr, uid, ids, context=None):
         task_obj = self.pool.get('project.task')
         task_ids = task_obj.search(cr, uid, [('project_id', 'in', ids), ('state', 'not in', ('cancelled', 'done'))])
-        task_obj.write(cr, uid, task_ids, {'state': 'done', 'date_end':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0})
+        task_obj.case_close(cr, uid, task_ids, context=context)
         self.write(cr, uid, ids, {'state':'close'}, context=context)
         self.set_close_send_note(cr, uid, ids, context=context)
         return True
@@ -250,7 +265,7 @@ class project(osv.osv):
     def set_cancel(self, cr, uid, ids, context=None):
         task_obj = self.pool.get('project.task')
         task_ids = task_obj.search(cr, uid, [('project_id', 'in', ids), ('state', '!=', 'done')])
-        task_obj.write(cr, uid, task_ids, {'state': 'cancelled', 'date_end':time.strftime('%Y-%m-%d %H:%M:%S'), 'remaining_hours': 0.0})
+        task_obj.case_cancel(cr, uid, task_ids, context=context)
         self.write(cr, uid, ids, {'state':'cancelled'}, context=context)
         self.set_cancel_send_note(cr, uid, ids, context=context)
         return True
@@ -506,13 +521,21 @@ def Project():
         return self.message_append_note(cr, uid, ids, body=message, context=context)
 
 
-class task(crm.crm_case, osv.osv):
+class task(base_stage, osv.osv):
     _name = "project.task"
     _description = "Task"
-    _log_create = True
     _date_name = "date_start"
     _inherit = ['ir.needaction_mixin', 'mail.thread']
 
+    def _get_default_project_id(self, cr, uid, context=None):
+        """ Gives default section by checking if present in the context """
+        return (self._resolve_project_id_from_context(cr, uid, context=context) or False)
+
+    def _get_default_stage_id(self, cr, uid, context=None):
+        """ Gives default stage_id """
+        project_id = self._get_default_project_id(cr, uid, context=context)
+        return self.stage_find(cr, uid, [], project_id, [('state', '=', 'draft')], context=context)
+
     def _resolve_project_id_from_context(self, cr, uid, context=None):
         """ Returns ID of project based on the value of 'default_project_id'
             context key, or None if it cannot be resolved to a single
@@ -520,8 +543,7 @@ class task(crm.crm_case, osv.osv):
         """
         if context is None: context = {}
         if type(context.get('default_project_id')) in (int, long):
-            project_id = context['default_project_id']
-            return project_id
+            return context['default_project_id']
         if isinstance(context.get('default_project_id'), basestring):
             project_name = context['default_project_id']
             project_ids = self.pool.get('project.project').name_search(cr, uid, name=project_name, context=context)
@@ -537,16 +559,15 @@ class task(crm.crm_case, osv.osv):
         if read_group_order == 'stage_id desc':
             order = '%s desc' % order
         # retrieve section_id from the context and write the domain
+        # - ('id', 'in', 'ids'): add columns that should be present
+        # - OR ('case_default', '=', True), ('fold', '=', False): add default columns that are not folded
+        # - OR ('project_ids', 'in', project_id), ('fold', '=', False) if project_id: add project columns that are not folded
         search_domain = []
         project_id = self._resolve_project_id_from_context(cr, uid, context=context)
         if project_id:
-            search_domain += ['|', '&', ('project_ids', '=', project_id), ('fold', '=', True)]
-        else:
-            domain = ['|', ('id','in',ids), ('case_default','=',1)]
-        search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', 1), ('fold', '=', False)]
-        print search_domain
-        stage_ids = stage_obj._search(cr, uid, domain, order=order, access_rights_uid=access_rights_uid, context=context)
-        print stage_ids
+            search_domain += ['|', '&', ('project_ids', '=', project_id), ('fold', '=', False)]
+        search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)]
+        stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
         result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
         # restore order of the search
         result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
@@ -571,7 +592,7 @@ class task(crm.crm_case, osv.osv):
 
     _group_by_full = {
         'stage_id': _read_group_stage_ids,
-        'user_id': _read_group_user_id
+        'user_id': _read_group_user_id,
     }
 
     def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
@@ -677,7 +698,7 @@ class task(crm.crm_case, osv.osv):
         'priority': fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Important'), ('0','Very important')], 'Priority', select=True),
         'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."),
         'stage_id': fields.many2one('project.task.type', 'Stage',
-                        domain="['&', '|', ('project_ids', '=', project_id), ('case_default', '=', True)]"),
+                        domain="['|', ('project_ids', '=', project_id), ('case_default', '=', True)]"),
         'state': fields.related('stage_id', 'state', type="selection", store=True,
                 selection=_TASK_STATE, string="State", readonly=True,
                 help='The state is set to \'Draft\', when a case is created.\
@@ -733,6 +754,8 @@ class task(crm.crm_case, osv.osv):
     }
 
     _defaults = {
+        'stage_id': _get_default_stage_id,
+        'project_id': _get_default_project_id,
         'state': 'draft',
         'kanban_state': 'normal',
         'priority': '2',
@@ -740,7 +763,7 @@ class task(crm.crm_case, osv.osv):
         'sequence': 10,
         'active': True,
         'user_id': lambda obj, cr, uid, context: uid,
-        'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c)
+        'company_id': lambda self, cr, uid, c: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=c),
     }
 
     _order = "priority, sequence, date_start, name, id"
@@ -810,7 +833,7 @@ class task(crm.crm_case, osv.osv):
     #
     def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
         users_obj = self.pool.get('res.users')
-
+        if context is None: context = {}
         # read uom as admin to avoid access rights issues, e.g. for portal/share users,
         # this should be safe (no context passed to avoid side-effects)
         obj_tm = users_obj.browse(cr, 1, uid, context=context).company_id.project_time_mode_id
@@ -846,22 +869,30 @@ class task(crm.crm_case, osv.osv):
     def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
         """ Override of the base.stage method
             Parameter of the stage search taken from the lead:
-            - type: stage type must be the same or 'both'
             - section_id: if set, stages must belong to this section or
               be a default stage; if not set, stages must be default
               stages
         """
         if isinstance(cases, (int, long)):
             cases = self.browse(cr, uid, cases, context=context)
-        domain = list(domain)
+        # collect all section_ids
+        section_ids = []
         if section_id:
-                domain += ['|', ('project_ids', '=', section_id)]
-        domain.append(('case_default', '=', True))
+            section_ids.append(section_id)
         for task in cases:
-            task_project_id = task.project_id.id if task.project_id else None
-            if task_project_id:
-                domain += ['|', ('project_ids', '=', task_project_id), ('case_default', '=', True)]
-        stage_ids = self.pool.get('project.task.type').search(cr, uid, domain, order=order, context=context)
+            if task.project_id:
+                section_ids.append(task.project_id.id)
+        # OR all section_ids and OR with case_default
+        search_domain = []
+        if section_ids:
+            search_domain += [('|')] * len(section_ids)
+            for section_id in section_ids:
+                search_domain.append(('project_ids', '=', section_id))
+        search_domain.append(('case_default', '=', True))
+        # AND with the domain in parameter
+        search_domain += list(domain)
+        # perform search, return the first found
+        stage_ids = self.pool.get('project.task.type').search(cr, uid, search_domain, order=order, context=context)
         if stage_ids:
             return stage_ids[0]
         return False
@@ -878,28 +909,12 @@ class task(crm.crm_case, osv.osv):
         return True
 
     def action_close(self, cr, uid, ids, context=None):
-        """ This action closes the task, then opens the wizard to send an 
-            email to the partner or the project manager.
+        """ This action closes the task 
         """
         task_id = len(ids) and ids[0] or False
         self._check_child_task(cr, uid, ids, context=context)
         if not task_id: return False
-        task = self.browse(cr, uid, task_id, context=context)
-        project = task.project_id
-        res = self.case_close(cr, uid, [task_id], context=context)
-        if project.warn_manager or project.warn_customer:
-            return {
-                'name': _('Send Email after close task'),
-                'view_type': 'form',
-                'view_mode': 'form',
-                'res_model': 'mail.compose.message',
-                'type': 'ir.actions.act_window',
-                'target': 'new',
-                'nodestroy': True,
-                'context': {'active_id': task.id,
-                            'active_model': 'project.task'}
-           }
-        return res
+        return self.do_close(cr, uid, [task_id], context=context)
 
     def do_close(self, cr, uid, ids, context=None):
         """ Compatibility when changing to case_close. """
@@ -907,23 +922,10 @@ class task(crm.crm_case, osv.osv):
     
     def case_close(self, cr, uid, ids, context=None):
         """ Closes Task """
-        request = self.pool.get('res.request')
         if not isinstance(ids, list): ids = [ids]
         for task in self.browse(cr, uid, ids, context=context):
             vals = {}
             project = task.project_id
-            if project:
-                # Send request to project manager
-                if project.warn_manager and project.user_id and (project.user_id.id != uid):
-                    request.create(cr, uid, {
-                        'name': _("Task '%s' closed") % task.name,
-                        'state': 'waiting',
-                        'act_from': uid,
-                        'act_to': project.user_id.id,
-                        'ref_partner_id': task.partner_id.id,
-                        'ref_doc1': 'project.task,%d'% (task.id,),
-                        'ref_doc2': 'project.project,%d'% (project.id,),
-                    }, context=context)
             for parent_id in task.parent_ids:
                 if parent_id.state in ('pending','draft'):
                     reopen = True
@@ -937,51 +939,25 @@ class task(crm.crm_case, osv.osv):
             if not task.date_end:
                 vals['date_end'] = fields.datetime.now()
             self.case_set(cr, uid, [task.id], 'done', vals, context=context)
-            #self.case_close_send_note(cr, uid, [task.id], context=context)
+            self.case_close_send_note(cr, uid, [task.id], context=context)
         return True
 
     def do_reopen(self, cr, uid, ids, context=None):
-        request = self.pool.get('res.request')
-
         for task in self.browse(cr, uid, ids, context=context):
             project = task.project_id
-            if project and project.warn_manager and project.user_id.id and (project.user_id.id != uid):
-                request.create(cr, uid, {
-                    'name': _("Task '%s' set in progress") % task.name,
-                    'state': 'waiting',
-                    'act_from': uid,
-                    'act_to': project.user_id.id,
-                    'ref_partner_id': task.partner_id.id,
-                    'ref_doc1': 'project.task,%d' % task.id,
-                    'ref_doc2': 'project.project,%d' % project.id,
-                }, context=context)
-
-            self.write(cr, uid, [task.id], {'state': 'open'}, context=context)
-            #self.do_open_send_note(cr, uid, [task.id], context)
+            self.case_set(cr, uid, [task.id], 'open', {}, context=context)
+            self.case_open_send_note(cr, uid, [task.id], context)
         return True
 
     def do_cancel(self, cr, uid, ids, context=None):
         """ Compatibility when changing to case_cancel. """
         return self.case_cancel(cr, uid, ids, context=context)
     
-    def case_cancel(cr, uid, ids, context=None):
-        request = self.pool.get('res.request')
+    def case_cancel(self, cr, uid, ids, context=None):
         tasks = self.browse(cr, uid, ids, context=context)
         self._check_child_task(cr, uid, ids, context=context)
         for task in tasks:
-            project = task.project_id
-            if project.warn_manager and project.user_id and (project.user_id.id != uid):
-                request.create(cr, uid, {
-                    'name': _("Task '%s' cancelled") % task.name,
-                    'state': 'waiting',
-                    'act_from': uid,
-                    'act_to': project.user_id.id,
-                    'ref_partner_id': task.partner_id.id,
-                    'ref_doc1': 'project.task,%d' % task.id,
-                    'ref_doc2': 'project.project,%d' % project.id,
-                }, context=context)
-            # cancel task
-            self.case_set(cr, uid, [task.id], 'cancel', {'remaining_hours': 0.0}, context=context)
+            self.case_set(cr, uid, [task.id], 'cancelled', {'remaining_hours': 0.0}, context=context)
             self.case_cancel_send_note(cr, uid, [task.id], context=context)
         return True
 
@@ -999,7 +975,7 @@ class task(crm.crm_case, osv.osv):
         """ Compatibility when changing to case_draft. """
         return self.case_draft(cr, uid, ids, context=context)
     
-    def case_draft(cr, uid, ids, context=None):
+    def case_draft(self, cr, uid, ids, context=None):
         self.case_set(cr, uid, ids, 'draft', {}, context=context)
         self.case_draft_send_note(cr, uid, ids, context=context)
         return True
@@ -1081,36 +1057,6 @@ class task(crm.crm_case, osv.osv):
     def set_kanban_state_done(self, cr, uid, ids, context=None):
         self.write(cr, uid, ids, {'kanban_state': 'done'}, context=context)
 
-    def _change_type(self, cr, uid, ids, next, context=None):
-        """
-            go to the next stage
-            if next is False, go to previous stage
-        """
-        for task in self.browse(cr, uid, ids):
-            if  task.project_id.type_ids:
-                typeid = task.stage_id.id
-                types_seq={}
-                for type in task.project_id.type_ids :
-                    types_seq[type.id] = type.sequence
-                if next:
-                    types = sorted(types_seq.items(), lambda x, y: cmp(x[1], y[1]))
-                else:
-                    types = sorted(types_seq.items(), lambda x, y: cmp(y[1], x[1]))
-                sorted_types = [x[0] for x in types]
-                if not typeid:
-                    self.write(cr, uid, task.id, {'stage_id': sorted_types[0]})
-                elif typeid and typeid in sorted_types and sorted_types.index(typeid) != len(sorted_types)-1:
-                    index = sorted_types.index(typeid)
-                    self.write(cr, uid, task.id, {'stage_id': sorted_types[index+1]})
-                self.state_change_send_note(cr, uid, [task.id], context)
-        return True
-
-    def next_type(self, cr, uid, ids, context=None):
-        return self._change_type(cr, uid, ids, True, context=context)
-
-    def prev_type(self, cr, uid, ids, context=None):
-        return self._change_type(cr, uid, ids, False, context=context)
-
     def _store_history(self, cr, uid, ids, context=None):
         for task in self.browse(cr, uid, ids, context=context):
             self.pool.get('project.task.history').create(cr, uid, {
@@ -1141,9 +1087,9 @@ class task(crm.crm_case, osv.osv):
             vals_reset_kstate = dict(vals, kanban_state='normal')
             for t in self.browse(cr, uid, ids, context=context):
                 #TO FIX:Kanban view doesn't raise warning 
-#                stages = [stage.id for stage in t.project_id.type_ids]
-#                if new_stage not in stages:
-#                    raise osv.except_osv(_('Warning !'), _('Stage is not defined in the project.'))
+                #stages = [stage.id for stage in t.project_id.type_ids]
+                #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)
             result = True
@@ -1151,7 +1097,6 @@ class task(crm.crm_case, osv.osv):
             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)
-            self.state_change_send_note(cr, uid, ids, context)
         return result
 
     def unlink(self, cr, uid, ids, context=None):
@@ -1192,8 +1137,9 @@ class task(crm.crm_case, osv.osv):
     # OpenChatter methods and notifications
     # ---------------------------------------------------
 
-       def case_get_note_msg_prefix(self, cr, uid, id, context=None):
-               return 'Task'
+    def case_get_note_msg_prefix(self, cr, uid, id, context=None):
+        """ Override of default prefix for notifications. """
+        return 'Task'
 
     def get_needaction_user_ids(self, cr, uid, ids, context=None):
         result = dict.fromkeys(ids, [])
@@ -1211,6 +1157,11 @@ class task(crm.crm_case, osv.osv):
                 sub_ids.append(obj.manager_id.id)
         return self.pool.get('res.users').read(cr, uid, sub_ids, context=context)
 
+    def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
+        """ Override of the (void) default notification method. """
+        stage_name = self.pool.get('project.task.type').name_get(cr, uid, [stage_id], context=context)[0][1]
+        return self.message_append_note(cr, uid, ids, body= _("Stage changed to <b>%s</b>.") % (stage_name), context=context)
+
     def create_send_note(self, cr, uid, ids, context=None):
         return self.message_append_note(cr, uid, ids, body=_("Task has been <b>created</b>."), context=context)
 
@@ -1218,49 +1169,12 @@ class task(crm.crm_case, osv.osv):
         msg = _('Task has been set as <b>draft</b>.')
         return self.message_append_note(cr, uid, ids, body=msg, context=context)
 
-    def case_open_send_note(self, cr, uid, ids, context=None):
-        #for id in ids:
-            #msg = _('%s has been <b>opened</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
-            #self.message_append_note(cr, uid, [id], body=msg, context=context)
-        return True
-
-    def case_close_send_note(self, cr, uid, ids, context=None):
-        #for id in ids:
-            #msg = _('%s has been <b>closed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
-            #self.message_append_note(cr, uid, [id], body=msg, context=context)
-        return True
-
-    def case_cancel_send_note(self, cr, uid, ids, context=None):
-        #for id in ids:
-            #msg = _('%s has been <b>canceled</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
-            #self.message_append_note(cr, uid, [id], body=msg, context=context)
-        return True
-
-    def case_pending_send_note(self, cr, uid, ids, context=None):
-        #for id in ids:
-            #msg = _('%s is now <b>pending</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
-            #self.message_append_note(cr, uid, [id], body=msg, context=context)
-        return True
-
-    def case_reset_send_note(self, cr, uid, ids, context=None):
-        #for id in ids:
-            #msg = _('%s has been <b>renewed</b>.') % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
-            #self.message_append_note(cr, uid, [id], body=msg, context=context)
-        return True
-
     def do_delegation_send_note(self, cr, uid, ids, context=None):
         for task in self.browse(cr, uid, ids, context=context):
             msg = _('Task has been <b>delegated</b> to <em>%s</em>.') % (task.user_id.name)
             self.message_append_note(cr, uid, [task.id], body=msg, context=context)
         return True
 
-    def state_change_send_note(self, cr, uid, ids, context=None):
-        for task in self.browse(cr, uid, ids, context=context):
-            msg = _('Stage changed to <b>%s</b>') % (task.stage_id.name)
-            self.message_append_note(cr, uid, [task.id], body=msg, context=context)
-        return True
-
-task()
 
 class project_work(osv.osv):
     _name = "project.task.work"
@@ -1299,19 +1213,52 @@ class project_work(osv.osv):
         for work in self.browse(cr, uid, ids):
             cr.execute('update project_task set remaining_hours=remaining_hours + %s where id=%s', (work.hours, work.task_id.id))
         return super(project_work,self).unlink(cr, uid, ids,*args, **kwargs)
-project_work()
 
-class account_analytic_account(osv.osv):
 
+class account_analytic_account(osv.osv):
     _inherit = 'account.analytic.account'
     _description = 'Analytic Account'
+    _columns = {
+        'use_tasks': fields.boolean('Tasks Management',help="If check,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'),
+    }
+
+    def _trigger_project_creation(self, cr, uid, vals, context=None):
+        '''
+        This function is used to decide if a project needs to be automatically created or not when an analytic account is created. It returns True if it needs to be so, False otherwise.
+        '''
+        return vals.get('use_tasks')
+
+    def project_create(self, cr, uid, analytic_account_id, vals, context=None):
+        '''
+        This function is called at the time of analytic account creation and is used to create a project automatically linked to it if the conditions are meet.
+        '''
+        project_pool = self.pool.get('project.project')
+        project_id = project_pool.search(cr, uid, [('analytic_account_id','=', analytic_account_id)])
+        if not project_id and self._trigger_project_creation(cr, uid, vals, context=context):
+            project_values = {
+                'name': vals.get('name'),
+                'analytic_account_id': analytic_account_id,
+            }
+            return project_pool.create(cr, uid, project_values, context=context)
+        return False
 
     def create(self, cr, uid, vals, context=None):
         if context is None:
             context = {}
         if vals.get('child_ids', False) and context.get('analytic_project_copy', False):
             vals['child_ids'] = []
-        return super(account_analytic_account, self).create(cr, uid, vals, context=context)
+        analytic_account_id = super(account_analytic_account, self).create(cr, uid, vals, context=context)
+        self.project_create(cr, uid, analytic_account_id, vals, context=context)
+        return analytic_account_id
+
+    def write(self, cr, uid, ids, vals, context=None):
+        name = vals.get('name')
+        for account in self.browse(cr, uid, ids, context=context):
+            if not name:
+                vals['name'] = account.name
+            self.project_create(cr, uid, account.id, vals, context=context)
+        return super(account_analytic_account, self).write(cr, uid, ids, vals, context=context)
 
     def unlink(self, cr, uid, ids, *args, **kwargs):
         project_obj = self.pool.get('project.project')
@@ -1320,7 +1267,6 @@ class account_analytic_account(osv.osv):
             raise osv.except_osv(_('Warning !'), _('Please delete the project linked with this account first.'))
         return super(account_analytic_account, self).unlink(cr, uid, ids, *args, **kwargs)
 
-account_analytic_account()
 
 #
 # Tasks History, used for cumulative flow charts (Lean/Agile)
@@ -1381,7 +1327,7 @@ class project_task_history(osv.osv):
     _defaults = {
         'date': fields.date.context_today,
     }
-project_task_history()
+
 
 class project_task_history_cumulative(osv.osv):
     _name = 'project.task.history.cumulative'
@@ -1409,4 +1355,4 @@ class project_task_history_cumulative(osv.osv):
             ) as history
         )
         """)
-project_task_history_cumulative()
+