X-Git-Url: http://git.inspyration.org/?a=blobdiff_plain;f=addons%2Fproject%2Fproject.py;h=7b848af06a37196e89b61100c3bfb6590815c607;hb=0a22166f88ec8415dbdff8a4e31961e6355d5b9c;hp=5304bd11c24af4c56f27b43673c0f930a2830a63;hpb=f0873dcb32a673c83a7d9e5c3e35f4226d9051a8;p=odoo%2Fodoo.git diff --git a/addons/project/project.py b/addons/project/project.py index 5304bd1..7b848af 100644 --- a/addons/project/project.py +++ b/addons/project/project.py @@ -52,10 +52,20 @@ class project_task_type(osv.osv): } _order = 'sequence' + +def short_name(name): + """Keep first word(s) of name to make it small enough + but distinctive""" + if not name: return name + # keep 7 chars + end of the last word + keep_words = name[:7].strip().split() + return ' '.join(name.split()[:len(keep_words)]) + class project(osv.osv): _name = "project.project" _description = "Project" - _inherits = {'account.analytic.account': "analytic_account_id","mail.alias":"alias_id"} + _inherits = {'account.analytic.account': "analytic_account_id", + "mail.alias": "alias_id"} _inherit = ['ir.needaction_mixin', 'mail.thread'] def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False): @@ -157,8 +167,8 @@ class project(osv.osv): mail_alias = self.pool.get('mail.alias') for proj in self.browse(cr, uid, ids): if proj.tasks: - raise osv.except_osv(_('Operation Not Permitted !'), - _('You cannot delete a project containing tasks. You may disable it instead by unticking the Active checkbox.')) + raise osv.except_osv(_('Invalid Action!'), + _('You cannot delete a project containing tasks. You can either delete all the project\'s tasks and then delete the project or simply deactivate the project.')) elif proj.alias_id: alias_ids.append(proj.alias_id.id) res = super(project, self).unlink(cr, uid, ids, *args, **kwargs) @@ -193,7 +203,7 @@ class project(osv.osv): project_ids = project_obj.search(cr, uid, [('message_ids.user_id.id', 'in', args[0][2])], context=context) return [('id', 'in', project_ids)] - # Lambda indirection method to avoid passing a copy of the overridable method when declaring the field + # 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 = { @@ -229,11 +239,11 @@ class project(osv.osv): 'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}), 'task_count': fields.function(_task_count, type='integer', string="Open Tasks"), 'color': fields.integer('Color Index'), - 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, - help="The email address associated with this project. New emails received will automatically " - "create project tasks (optionally project issues if the Project Issue module is installed)."), - 'alias_model': fields.selection(_alias_models, "Alias Model", select=True, required=True, - help="The kind of document created when an email is received by this project's email alias"), + 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="cascade", required=True, + help="Internal email associated with this project. Incoming emails are automatically synchronized" + "with Tasks (or optionally Issues if the Issue Tracker module is installed)."), + 'alias_model': fields.selection(_alias_models, "Alias Model", select=True, required=True, + 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,), 'followers': fields.function(_get_followers, method=True, fnct_search=_search_followers, @@ -257,6 +267,7 @@ class project(osv.osv): 'type_ids': _get_type_common, 'alias_model': 'project.task', 'privacy_visibility': 'public', + 'alias_domain': False, # always hide alias during creation } # TODO: Why not using a SQL contraints ? @@ -327,11 +338,11 @@ class project(osv.osv): context['active_test'] = False default['state'] = 'open' default['tasks'] = [] - default['alias_id'] = False + default.pop('alias_name', None) + default.pop('alias_id', None) proj = self.browse(cr, uid, id, context=context) if not default.get('name', False): default['name'] = proj.name + _(' (copy)') - default['alias_name'] = default['name'] res = super(project, self).copy(cr, uid, id, default, context) self.map_tasks(cr,uid,id,res,context) return res @@ -407,7 +418,7 @@ class project(osv.osv): for project in projects: if (not project.members) and force_members: - raise osv.except_osv(_('Warning !'),_("You must assign members on the project '%s' !") % (project.name,)) + raise osv.except_osv(_('Warning!'),_("You must assign members on the project '%s' !") % (project.name,)) resource_pool = self.pool.get('resource.resource') @@ -512,18 +523,23 @@ def Project(): return user_ids def create(self, cr, uid, vals, context=None): - alias_pool = self.pool.get('mail.alias') + if context is None: context = {} + # Prevent double project creation when 'use_tasks' is checked! + context = dict(context, project_creation_in_progress=True) + mail_alias = self.pool.get('mail.alias') if not vals.get('alias_id'): - name = vals.get('alias_name') or vals['name'] - alias_id = alias_pool.create_unique_alias(cr, uid, - {'alias_name': "project_"+name, - 'alias_model_id': vals.get('alias_model', 'project.task')}, context=context) - alias = alias_pool.read(cr, uid, alias_id, ['alias_name'],context) - vals.update({'alias_id': alias_id, 'alias_name': alias['alias_name']}) - res = super( project, self).create(cr, uid, vals, context) - alias_pool.write(cr, uid, [vals['alias_id']], {'alias_defaults':{'project_id': res}}, context) - self.create_send_note(cr, uid, [res], context=context) - return res + vals.pop('alias_name', None) # prevent errors during copy() + alias_id = mail_alias.create_unique_alias(cr, uid, + # Using '+' allows using subaddressing for those who don't + # have a catchall domain setup. + {'alias_name': "project+"+short_name(vals['name'])}, + model_name=vals.get('alias_model', 'project.task'), + context=context) + vals['alias_id'] = alias_id + project_id = super(project, self).create(cr, uid, vals, context) + mail_alias.write(cr, uid, [vals['alias_id']], {'alias_defaults': {'project_id': project_id} }, context) + self.create_send_note(cr, uid, [project_id], context=context) + return project_id def create_send_note(self, cr, uid, ids, context=None): return self.message_append_note(cr, uid, ids, body=_("Project has been created."), context=context) @@ -545,10 +561,9 @@ def Project(): return self.message_append_note(cr, uid, ids, body=message, context=context) def write(self, cr, uid, ids, vals, context=None): - model_pool = self.pool.get('ir.model') - # if alias_model has been changed, update alias_model_id of alias accordingly + # if alias_model has been changed, update alias_model_id accordingly if vals.get('alias_model'): - model_ids = model_pool.search(cr, uid, [('model', '=', vals.get('alias_model','project.task'))]) + model_ids = self.pool.get('ir.model').search(cr, uid, [('model', '=', vals.get('alias_model', 'project.task'))]) vals.update(alias_model_id=model_ids[0]) return super(project, self).write(cr, uid, ids, vals, context=context) @@ -776,7 +791,7 @@ class task(base_stage, osv.osv): }), 'user_id': fields.many2one('res.users', 'Assigned to'), 'delegated_user_id': fields.related('child_ids', 'user_id', type='many2one', relation='res.users', string='Delegated To'), - 'partner_id': fields.many2one('res.partner', 'Partner'), + 'partner_id': fields.many2one('res.partner', 'Contact'), 'work_ids': fields.one2many('project.task.work', 'task_id', 'Work done'), 'manager_id': fields.related('project_id', 'analytic_account_id', 'user_id', type='many2one', relation='res.users', string='Project Manager'), 'company_id': fields.many2one('res.company', 'Company'), @@ -1124,7 +1139,7 @@ class task(base_stage, osv.osv): #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.')) + #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) self.stage_set_send_note(cr, uid, [t.id], new_stage, context=context) @@ -1264,7 +1279,7 @@ class account_analytic_account(osv.osv): 'use_tasks': fields.boolean('Tasks Mgmt.',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 on_change_template(self, cr, uid, ids, template_id, context=None): res = super(account_analytic_account, self).on_change_template(cr, uid, ids, template_id, context=context) if template_id and 'value' in res: @@ -1276,7 +1291,8 @@ class account_analytic_account(osv.osv): ''' 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') + if context is None: context = {} + return vals.get('use_tasks') and not 'project_creation_in_progress' in context def project_create(self, cr, uid, analytic_account_id, vals, context=None): ''' @@ -1313,9 +1329,15 @@ class account_analytic_account(osv.osv): project_obj = self.pool.get('project.project') analytic_ids = project_obj.search(cr, uid, [('analytic_account_id','in',ids)]) if analytic_ids: - raise osv.except_osv(_('Warning !'), _('Please delete the project linked with this account first.')) + 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) +class project_project(osv.osv): + _inherit = 'project.project' + _defaults = { + 'use_tasks': True + } + # # Tasks History, used for cumulative flow charts (Lean/Agile)