[MERGE] merge with main branch

bzr revid: psi@tinyerp.com-20120601113816-mmbmhklr588wj5sl
This commit is contained in:
Purnendu Singh (OpenERP) 2012-06-01 17:08:16 +05:30
commit 0255049b09
57 changed files with 2188 additions and 1532 deletions

View File

@ -0,0 +1,25 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2012-today OpenERP SA (<http://openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
import base_state
import base_stage
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2012-today OpenERP SA (<http://openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
{
'name': 'State/Stage Management',
'version': '1.0',
'category': 'Hidden',
'description': """
This module handles state and stage. It is derived from the crm_base and
crm_case classes from crm.
* ``base_state``: state management
* ``base_stage``: stage management
""",
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'depends': ['base'],
'init_xml': [],
'update_xml': [],
'demo_xml': [],
'installable': True,
'auto_install': False,
}
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -0,0 +1,432 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields
from tools.translate import _
class base_stage(object):
""" Base utility mixin class for objects willing to manage their stages.
Object that inherit from this class should inherit from mailgate.thread
to have access to the mail gateway, as well as Chatter. Objects
subclassing this class should define the following colums:
- ``date_open`` (datetime field)
- ``date_closed`` (datetime field)
- ``user_id`` (many2one to res.users)
- ``partner_id`` (many2one to res.partner)
- ``stage_id`` (many2one to a stage definition model)
- ``state`` (selection field, related to the stage_id.state)
"""
def _get_default_partner(self, cr, uid, context=None):
""" Gives id of partner for current user
:param context: if portal not in context returns False
"""
if context is None:
context = {}
if not context or not context.get('portal'):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if hasattr(user, 'partner_address_id') and user.partner_address_id:
return user.partner_address_id
return user.company_id.partner_id.id
def _get_default_email(self, cr, uid, context=None):
""" Gives default email address for current user
:param context: if portal not in context returns False
"""
if context is None:
context = {}
if not context or not context.get('portal'):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.user_email
def _get_default_user(self, cr, uid, context=None):
""" Gives current user id
:param context: if portal not in context returns False
"""
if context is None:
context = {}
if not context or not context.get('portal'):
return False
return uid
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
""" This function returns value of partner email based on Partner Address
:param add: Id of Partner's address
:param email: Partner's email ID
"""
data = {'value': {'email_from': False, 'phone':False}}
if add:
address = self.pool.get('res.partner').browse(cr, uid, add)
data['value'] = {'email_from': address and address.email or False ,
'phone': address and address.phone or False}
if 'phone' not in self._columns:
del data['value']['phone']
return data
def onchange_partner_id(self, cr, uid, ids, part, email=False):
""" This function returns value of partner address based on partner
:param part: Partner's id
:param email: Partner's email ID
"""
data={}
if part:
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
return {'value': data}
def _get_default_section_id(self, cr, uid, context=None):
""" Gives default section """
return False
def _get_default_stage_id(self, cr, uid, context=None):
""" Gives default stage_id """
return self.stage_find(cr, uid, [], None, [('state', '=', 'draft')], context=context)
def stage_find(self, cr, uid, cases, section_id, domain=[], order='sequence', context=None):
""" Find stage, with a given (optional) domain on the search,
ordered by the order parameter. If several stages match the
search criterions, the first one will be returned, according
to the requested search order.
This method is meant to be overriden by subclasses. That way
specific behaviors can be achieved for every class inheriting
from base_stage.
:param cases: browse_record of cases
:param section_id: section limitating the search, given for
a generic search (for example default search).
A section models concepts such as Sales team
(for CRM), ou departments (for HR).
:param domain: a domain on the search of stages
:param order: order of the search
"""
return False
def stage_set_with_state_name(self, cr, uid, cases, state_name, context=None):
""" Set a new stage, with a state_name instead of a stage_id
:param cases: browse_record of cases
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
for case in cases:
stage_id = self.stage_find(cr, uid, [case], None, [('state', '=', state_name)], context=context)
if stage_id:
self.stage_set(cr, uid, [case.id], stage_id, context=context)
return True
def stage_set(self, cr, uid, ids, stage_id, context=None):
""" Set the new stage. This methods is the right method to call
when changing states. It also checks whether an onchange is
defined, and execute it.
"""
value = {}
if hasattr(self, 'onchange_stage_id'):
value = self.onchange_stage_id(cr, uid, ids, stage_id, context=context)['value']
value['stage_id'] = stage_id
self.stage_set_send_note(cr, uid, ids, stage_id, context=context)
return self.write(cr, uid, ids, value, context=context)
def stage_change(self, cr, uid, ids, op, order, context=None):
""" Change the stage and take the next one, based on a condition
writen for the 'sequence' field and an operator. This methods
checks whether the case has a current stage, and takes its
sequence. Otherwise, a default 0 sequence is chosen and this
method will therefore choose the first available stage.
For example if op is '>' and current stage has a sequence of
10, this will call stage_find, with [('sequence', '>', '10')].
"""
for case in self.browse(cr, uid, ids, context=context):
seq = 0
if case.stage_id:
seq = case.stage_id.sequence or 0
section_id = None
next_stage_id = self.stage_find(cr, uid, [case], None, [('sequence', op, seq)],order, context=context)
if next_stage_id:
return self.stage_set(cr, uid, [case.id], next_stage_id, context=context)
return False
def stage_next(self, cr, uid, ids, context=None):
""" This function computes next stage for case from its current stage
using available stage for that case type
"""
return self.stage_change(cr, uid, ids, '>','sequence', context)
def stage_previous(self, cr, uid, ids, context=None):
""" This function computes previous stage for case from its current
stage using available stage for that case type
"""
return self.stage_change(cr, uid, ids, '<', 'sequence desc', context)
def copy(self, cr, uid, id, default=None, context=None):
""" Overrides orm copy method to avoid copying messages,
as well as date_closed and date_open columns if they
exist."""
if default is None:
default = {}
if hasattr(self, '_columns'):
if self._columns.get('date_closed'):
default.update({ 'date_closed': False, })
if self._columns.get('date_open'):
default.update({ 'date_open': False })
return super(base_stage, self).copy(cr, uid, id, default, context=context)
def case_escalate(self, cr, uid, ids, context=None):
""" Escalates case to parent level """
cases = self.browse(cr, uid, ids, context=context)
cases[0].state # fill browse record cache, for _action having old and new values
for case in cases:
data = {'active': True}
if case.section_id.parent_id:
data['section_id'] = case.section_id.parent_id.id
if case.section_id.parent_id.change_responsible:
if case.section_id.parent_id.user_id:
data['user_id'] = case.section_id.parent_id.user_id.id
else:
raise osv.except_osv(_('Error !'), _('You can not escalate, you are already at the top level regarding your sales-team category.'))
self.write(cr, uid, [case.id], data, context=context)
case.case_escalate_send_note(case.section_id.parent_id, context=context)
cases = self.browse(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'escalate', context=context)
return True
def case_open(self, cr, uid, ids, context=None):
""" Opens case """
cases = self.browse(cr, uid, ids, context=context)
for case in cases:
data = {'active': True}
if case.stage_id and case.stage_id.state == 'draft':
data['date_open'] = fields.datetime.now()
if not case.user_id:
data['user_id'] = uid
self.case_set(cr, uid, [case.id], 'open', data, context=context)
self.case_open_send_note(cr, uid, [case.id], context=context)
return True
def case_close(self, cr, uid, ids, context=None):
""" Closes case """
self.case_set(cr, uid, ids, 'done', {'active': True, 'date_closed': fields.datetime.now()}, context=context)
self.case_close_send_note(cr, uid, ids, context=context)
return True
def case_cancel(self, cr, uid, ids, context=None):
""" Cancels case """
self.case_set(cr, uid, ids, 'cancel', {'active': True}, context=context)
self.case_cancel_send_note(cr, uid, ids, context=context)
return True
def case_pending(self, cr, uid, ids, context=None):
""" Set case as pending """
self.case_set(cr, uid, ids, 'pending', {'active': True}, context=context)
self.case_pending_send_note(cr, uid, ids, context=context)
return True
def case_reset(self, cr, uid, ids, context=None):
""" Resets case as draft """
self.case_set(cr, uid, ids, 'draft', {'active': True}, context=context)
self.case_reset_send_note(cr, uid, ids, context=context)
return True
def case_set(self, cr, uid, ids, new_state_name=None, values_to_update=None, new_stage_id=None, context=None):
""" Generic method for setting case. This methods wraps the update
of the record, as well as call to _action and browse_record
case setting to fill the cache.
:params new_state_name: the new state of the record; this method
will call ``stage_set_with_state_name``
that will find the stage matching the
new state, using the ``stage_find`` method.
:params new_stage_id: alternatively, you may directly give the
new stage of the record
:params state_name: the new value of the state, such as
'draft' or 'close'.
:params update_values: values that will be added with the state
update when writing values to the record.
"""
cases = self.browse(cr, uid, ids, context=context)
cases[0].state # fill browse record cache, for _action having old and new values
# 1. update the stage
if new_state_name:
self.stage_set_with_state_name(cr, uid, cases, new_state_name, context=context)
elif not (new_stage_id is None):
self.stage_set(cr, uid, ids, new_stage_id, context=context)
# 2. update values
if values_to_update:
self.write(cr, uid, ids, values_to_update, context=context)
# 3. call _action for base action rule
if new_state_name:
self._action(cr, uid, cases, new_state_name, context=context)
elif not (new_stage_id is None):
new_state_name = self.read(cr, uid, ids, ['state'], context=context)[0]['state']
self._action(cr, uid, cases, new_state_name, context=context)
return True
def _action(self, cr, uid, cases, state_to, scrit=None, context=None):
if context is None:
context = {}
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
if not rule_obj:
return True
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)], context=context)
rule_ids = rule_obj.search(cr, uid, [('model_id','=',model_ids[0])], context=context)
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
def remind_partner(self, cr, uid, ids, context=None, attach=False):
return self.remind_user(cr, uid, ids, context, attach,
destination=False)
def remind_user(self, cr, uid, ids, context=None, attach=False, destination=True):
mail_message = self.pool.get('mail.message')
for case in self.browse(cr, uid, ids, context=context):
if not destination and not case.email_from:
return False
if not case.user_id.user_email:
return False
if destination and case.section_id.user_id:
case_email = case.section_id.user_id.user_email
else:
case_email = case.user_id.user_email
src = case_email
dest = case.user_id.user_email or ""
body = case.description or ""
for message in case.message_ids:
if message.email_from and message.body_text:
body = message.body_text
break
if not destination:
src, dest = dest, case.email_from
if body and case.user_id.signature:
if body:
body += '\n\n%s' % (case.user_id.signature)
else:
body = '\n\n%s' % (case.user_id.signature)
body = self.format_body(body)
attach_to_send = {}
if attach:
attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)])
attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas'])
attach_to_send = dict(map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send))
# Send an email
subject = "Reminder: [%s] %s" % (str(case.id), case.name, )
mail_message.schedule_with_attach(cr, uid,
src,
[dest],
subject,
body,
model=self._name,
reply_to=case.section_id.reply_to,
res_id=case.id,
attachments=attach_to_send,
context=context
)
return True
def _check(self, cr, uid, ids=False, context=None):
""" Function called by the scheduler to process cases for date actions.
Must be overriden by inheriting classes.
"""
return True
def format_body(self, body):
return self.pool.get('base.action.rule').format_body(body)
def format_mail(self, obj, body):
return self.pool.get('base.action.rule').format_mail(obj, body)
def message_thread_followers(self, cr, uid, ids, context=None):
res = {}
for case in self.browse(cr, uid, ids, context=context):
l=[]
if case.email_cc:
l.append(case.email_cc)
if case.user_id and case.user_id.user_email:
l.append(case.user_id.user_email)
res[case.id] = l
return res
# ******************************
# Notifications
# ******************************
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
""" Default prefix for notifications. For example: "%s has been
<b>closed</b>.". As several models will inherit from base_stage,
this method returns a void string. Class using base_stage
will have to override this method to define the prefix they
want to display.
"""
return ''
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Send a notification when the stage changes. This method has
to be overriden, because each document will have its particular
behavior and/or stage model (such as project.task.type or
crm.case.stage).
"""
return True
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 case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None):
for id in ids:
if new_section:
msg = '%s has been <b>escalated</b> to <b>%s</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name)
else:
msg = '%s has been <b>escalated</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], 'System Notification', msg, context=context)
return True

View File

@ -0,0 +1,194 @@
# -*- coding: utf-8 -*-
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
##############################################################################
from osv import fields
from tools.translate import _
class base_state(object):
""" Base utility mixin class for objects willing to manage their state.
Object subclassing this class should define the following colums:
- ``date_open`` (datetime field)
- ``date_closed`` (datetime field)
- ``user_id`` (many2one to res.users)
- ``partner_id`` (many2one to res.partner)
- ``state`` (selection field)
"""
def _get_default_partner(self, cr, uid, context=None):
""" Gives id of partner for current user
:param context: if portal not in context returns False
"""
if context is None:
context = {}
if not context or not context.get('portal'):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if hasattr(user, 'partner_address_id') and user.partner_address_id:
return user.partner_address_id
return user.company_id.partner_id.id
def _get_default_email(self, cr, uid, context=None):
""" Gives default email address for current user
:param context: if portal not in context returns False
"""
if context is None:
context = {}
if not context or not context.get('portal'):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.user_email
def _get_default_user(self, cr, uid, context=None):
""" Gives current user id
:param context: if portal not in context returns False
"""
if context is None:
context = {}
if not context or not context.get('portal'):
return False
return uid
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
""" This function returns value of partner email based on Partner Address
:param add: Id of Partner's address
:param email: Partner's email ID
"""
data = {'value': {'email_from': False, 'phone':False}}
if add:
address = self.pool.get('res.partner').browse(cr, uid, add)
data['value'] = {'email_from': address and address.email or False ,
'phone': address and address.phone or False}
if 'phone' not in self._columns:
del data['value']['phone']
return data
def onchange_partner_id(self, cr, uid, ids, part, email=False):
""" This function returns value of partner address based on partner
:param part: Partner's id
:param email: Partner's email ID
"""
data={}
if part:
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
return {'value': data}
def case_open(self, cr, uid, ids, context=None):
""" Opens case """
cases = self.browse(cr, uid, ids, context=context)
for case in cases:
values = {'active': True}
if case.state == 'draft':
values['date_open'] = fields.datetime.now()
if not case.user_id:
values['user_id'] = uid
self.case_set(cr, uid, [case.id], 'open', values, context=context)
self.case_open_send_note(cr, uid, [case.id], context=context)
return True
def case_close(self, cr, uid, ids, context=None):
""" Closes case """
self.case_set(cr, uid, ids, 'done', {'date_closed': fields.datetime.now()}, context=context)
self.case_close_send_note(cr, uid, ids, context=context)
return True
def case_cancel(self, cr, uid, ids, context=None):
""" Cancels case """
self.case_set(cr, uid, ids, 'cancel', {'active': True}, context=context)
self.case_cancel_send_note(cr, uid, ids, context=context)
return True
def case_pending(self, cr, uid, ids, context=None):
""" Sets case as pending """
self.case_set(cr, uid, ids, 'pending', {'active': True}, context=context)
self.case_pending_send_note(cr, uid, ids, context=context)
return True
def case_reset(self, cr, uid, ids, context=None):
""" Resets case as draft """
self.case_set(cr, uid, ids, 'draft', {'active': True}, context=context)
self.case_reset_send_note(cr, uid, ids, context=context)
return True
def case_set(self, cr, uid, ids, state_name, update_values=None, context=None):
""" Generic method for setting case. This methods wraps the update
of the record, as well as call to _action and browse_record
case setting to fill the cache.
:params: state_name: the new value of the state, such as
'draft' or 'close'.
:params: update_values: values that will be added with the state
update when writing values to the record.
"""
cases = self.browse(cr, uid, ids, context=context)
cases[0].state # fill browse record cache, for _action having old and new values
if update_values is None:
update_values = {}
update_values['state'] = state_name
self.write(cr, uid, ids, update_values, context=context)
self._action(cr, uid, cases, state_name, context=context)
def _action(self, cr, uid, cases, state_to, scrit=None, context=None):
if context is None:
context = {}
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)])
rule_ids = rule_obj.search(cr, uid, [('model_id','=',model_ids[0])])
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
# ******************************
# Notifications
# ******************************
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return ''
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

View File

@ -57,6 +57,7 @@ Creates a dashboard for CRM that includes:
'depends': [
'base_action_rule',
'base_setup',
'base_status',
'process',
'mail',
'base_calendar',
@ -66,7 +67,6 @@ Creates a dashboard for CRM that includes:
],
'init_xml': [
'crm_data.xml',
'crm_meeting_data.xml',
'crm_lead_data.xml',
'crm_meeting_data.xml',
'crm_phonecall_data.xml',

View File

@ -14,7 +14,7 @@
<field name="planned_revenue" sum="Total of Planned Revenue"/>
<field name="probability" widget="progressbar" avg="Avg. of Probability"/>
<field name="date_deadline" invisible="1"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,12 +19,11 @@
#
##############################################################################
import time
import base64
import tools
import time
from osv import fields
from osv import osv
import tools
from tools.translate import _
MAX_LEVEL = 15
@ -57,8 +56,11 @@ class crm_case_channel(osv.osv):
}
class crm_case_stage(osv.osv):
""" Stage of case """
""" Model for case stages. This models the main stages of a document
management flow. Main CRM objects (leads, opportunities, project
issues, ...) will now use only stages, instead of state and stages.
Stages are for example used to display the kanban view of records.
"""
_name = "crm.case.stage"
_description = "Stage of case"
_rec_name = 'name'
@ -66,22 +68,34 @@ class crm_case_stage(osv.osv):
_columns = {
'name': fields.char('Stage Name', size=64, required=True, translate=True),
'sequence': fields.integer('Sequence', help="Used to order stages."),
'sequence': fields.integer('Sequence', help="Used to order stages. Lower is better."),
'probability': fields.float('Probability (%)', required=True, help="This percentage depicts the default/average probability of the Case for this stage to be a success"),
'on_change': fields.boolean('Change Probability Automatically', help="Setting this stage will change the probability automatically on the opportunity."),
'requirements': fields.text('Requirements'),
'section_ids':fields.many2many('crm.case.section', 'section_stage_rel', 'stage_id', 'section_id', 'Sections'),
'case_default': fields.boolean('Common to All Teams', help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."),
'section_ids':fields.many2many('crm.case.section', 'section_stage_rel', 'stage_id', 'section_id', string='Sections',
help="Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams."),
'state': fields.selection(AVAILABLE_STATES, 'State', required=True, help="The related state for the stage. The state of your document will automatically change regarding the selected stage. For example, if a stage is related to the state 'Close', when your document reaches this stage, it will be automatically have the 'closed' state."),
'case_default': fields.boolean('Common to All Teams',
help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."),
'fold': fields.boolean('Hide in Views when Empty',
help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
'type': fields.selection([ ('lead','Lead'),
('opportunity', 'Opportunity'),
('both', 'Both')],
string='Type', size=16, required=True,
help="This field is used to distinguish stages related to Leads from stages related to Opportunities, or to specify stages available for both types."),
}
_defaults = {
'sequence': lambda *args: 1,
'probability': lambda *args: 0.0,
'state': 'draft',
'fold': False,
'type': 'both',
}
class crm_case_section(osv.osv):
"""Sales Team"""
""" Model for sales teams. """
_name = "crm.case.section"
_description = "Sales Teams"
_order = "complete_name"
@ -107,6 +121,7 @@ class crm_case_section(osv.osv):
'working_hours': fields.float('Working Hours', digits=(16,2 )),
'stage_ids': fields.many2many('crm.case.stage', 'section_stage_rel', 'section_id', 'stage_id', 'Stages'),
}
def _get_stage_common(self, cr, uid, context):
ids = self.pool.get('crm.case.stage').search(cr, uid, [('case_default','=',1)], context=context)
return ids
@ -171,429 +186,6 @@ class crm_case_resource_type(osv.osv):
'section_id': fields.many2one('crm.case.section', 'Sales Team'),
}
class crm_base(object):
""" Base utility mixin class for crm objects,
Object subclassing this should define colums:
date_open
date_closed
user_id
partner_id
"""
def _get_default_partner_address(self, cr, uid, context=None):
"""Gives id of default address for current user
:param context: if portal in context is false return false anyway
"""
if context is None:
context = {}
if not context.get('portal'):
return False
# was user.address_id.id, but address_id has been removed
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if hasattr(user, 'partner_address_id') and user.partner_address_id:
return user.partner_address_id
return False
def _get_default_partner(self, cr, uid, context=None):
"""Gives id of partner for current user
:param context: if portal in context is false return false anyway
"""
if context is None:
context = {}
if not context.get('portal', False):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
if hasattr(user, 'partner_address_id') and user.partner_address_id:
return user.partner_address_id
return user.company_id.partner_id.id
def _get_default_email(self, cr, uid, context=None):
"""Gives default email address for current user
:param context: if portal in context is false return false anyway
"""
if not context.get('portal', False):
return False
user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
return user.user_email
def _get_default_user(self, cr, uid, context=None):
"""Gives current user id
:param context: if portal in context is false return false anyway
"""
if context and context.get('portal', False):
return False
return uid
def _get_section(self, cr, uid, context=None):
return False
def onchange_partner_address_id(self, cr, uid, ids, add, email=False):
"""This function returns value of partner email based on Partner Address
:param ids: List of case IDs
:param add: Id of Partner's address
:param email: Partner's email ID
"""
data = {'value': {'email_from': False, 'phone':False}}
if add:
address = self.pool.get('res.partner').browse(cr, uid, add)
data['value'] = {'email_from': address and address.email or False ,
'phone': address and address.phone or False}
if 'phone' not in self._columns:
del data['value']['phone']
return data
def onchange_partner_id(self, cr, uid, ids, part, email=False):
"""This function returns value of partner address based on partner
:param ids: List of case IDs
:param part: Partner's id
:param email: Partner's email ID
"""
data={}
if part:
addr = self.pool.get('res.partner').address_get(cr, uid, [part], ['contact'])
data.update(self.onchange_partner_address_id(cr, uid, ids, addr['contact'])['value'])
return {'value': data}
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return ''
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 case_open(self, cr, uid, ids, context=None):
"""Opens Case
:param ids: List of case Ids
"""
cases = self.browse(cr, uid, ids)
for case in cases:
data = {'state': 'open', 'active': True}
if not case.user_id:
data['user_id'] = uid
self.write(cr, uid, [case.id], data)
self.case_open_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'open')
return True
def case_close(self, cr, uid, ids, context=None):
"""Closes Case
:param ids: List of case Ids
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': 'done', 'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'), })
# We use the cache of cases to keep the old case state
self.case_close_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'done')
return True
def case_cancel(self, cr, uid, ids, context=None):
"""Cancels Case
:param ids: List of case Ids
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': 'cancel', 'active': True})
# We use the cache of cases to keep the old case state
self.case_cancel_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'cancel')
return True
def case_pending(self, cr, uid, ids, context=None):
"""Marks case as pending
:param ids: List of case Ids
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': 'pending', 'active': True})
self.case_pending_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'pending')
return True
def case_reset(self, cr, uid, ids, context=None):
"""Resets case as draft
:param ids: List of case Ids
"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': 'draft', 'active': True})
self.case_reset_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'draft')
return True
def _action(self, cr, uid, cases, state_to, scrit=None, context=None):
if context is None:
context = {}
context['state_to'] = state_to
rule_obj = self.pool.get('base.action.rule')
model_obj = self.pool.get('ir.model')
model_ids = model_obj.search(cr, uid, [('model','=',self._name)])
rule_ids = rule_obj.search(cr, uid, [('model_id','=',model_ids[0])])
return rule_obj._action(cr, uid, rule_ids, cases, scrit=scrit, context=context)
class crm_case(crm_base):
""" A simple python class to be used for common functions
Object that inherit from this class should inherit from mailgate.thread
And need a stage_id field
And object that inherit (orm inheritance) from a class the overwrite copy
"""
def stage_find(self, cr, uid, section_id, domain=[], order='sequence'):
domain = list(domain)
if section_id:
domain.append(('section_ids', '=', section_id))
stage_ids = self.pool.get('crm.case.stage').search(cr, uid, domain, order=order)
if stage_ids:
return stage_ids[0]
return False
def stage_set(self, cr, uid, ids, stage_id, context=None):
value = {}
if hasattr(self,'onchange_stage_id'):
value = self.onchange_stage_id(cr, uid, ids, stage_id)['value']
value['stage_id'] = stage_id
return self.write(cr, uid, ids, value, context=context)
def stage_change(self, cr, uid, ids, op, order, context=None):
if context is None:
context = {}
for case in self.browse(cr, uid, ids, context=context):
seq = 0
if case.stage_id:
seq = case.stage_id.sequence
section_id = None
if case.section_id:
section_id = case.section_id.id
next_stage_id = self.stage_find(cr, uid, section_id, [('sequence',op,seq)],order)
if next_stage_id:
return self.stage_set(cr, uid, [case.id], next_stage_id, context=context)
return False
def stage_next(self, cr, uid, ids, context=None):
"""This function computes next stage for case from its current stage
using available stage for that case type
"""
return self.stage_change(cr, uid, ids, '>','sequence', context)
def stage_previous(self, cr, uid, ids, context=None):
"""This function computes previous stage for case from its current
stage using available stage for that case type
"""
return self.stage_change(cr, uid, ids, '<', 'sequence desc', context)
def copy(self, cr, uid, id, default=None, context=None):
"""Overrides orm copy method to avoid copying messages,
as well as date_closed and date_open columns if they
exist."""
if default is None:
default = {}
default.update({ 'message_ids': [], })
if hasattr(self, '_columns'):
if self._columns.get('date_closed'):
default.update({ 'date_closed': False, })
if self._columns.get('date_open'):
default.update({ 'date_open': False })
return super(crm_case, self).copy(cr, uid, id, default, context=context)
def case_escalate_send_note(self, cr, uid, ids, new_section=None, context=None):
for id in ids:
if new_section:
msg = '%s has been <b>escalated</b> to <b>%s</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context), new_section.name)
else:
msg = '%s has been <b>escalated</b>.' % (self.case_get_note_msg_prefix(cr, uid, id, context=context))
self.message_append_note(cr, uid, [id], 'System Notification', msg, context=context)
return True
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return ''
def case_open(self, cr, uid, ids, context=None):
"""Opens Case"""
cases = self.browse(cr, uid, ids)
for case in cases:
data = {'state': 'open', 'active': True }
if not case.user_id:
data['user_id'] = uid
self.write(cr, uid, [case.id], data)
self.case_open_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'open')
return True
def case_close(self, cr, uid, ids, context=None):
"""Closes Case"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': 'done',
'date_closed': time.strftime('%Y-%m-%d %H:%M:%S'),
})
#
# We use the cache of cases to keep the old case state
#
self.case_close_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'done')
return True
def case_escalate(self, cr, uid, ids, context=None):
"""Escalates case to parent level"""
cases = self.browse(cr, uid, ids)
for case in cases:
data = {'active': True}
if case.section_id.parent_id:
data['section_id'] = case.section_id.parent_id.id
if case.section_id.parent_id.change_responsible:
if case.section_id.parent_id.user_id:
data['user_id'] = case.section_id.parent_id.user_id.id
else:
raise osv.except_osv(_('Error !'), _('You can not escalate, you are already at the top level regarding your sales-team category.'))
self.write(cr, uid, [case.id], data)
case.case_escalate_send_note(case.section_id.parent_id)
cases = self.browse(cr, uid, ids)
self._action(cr, uid, cases, 'escalate')
return True
def case_cancel(self, cr, uid, ids, context=None):
"""Cancels Case"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': 'cancel',
'active': True})
self.case_cancel_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'cancel')
return True
def case_pending(self, cr, uid, ids, context=None):
"""Marks case as pending"""
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': 'pending', 'active': True})
self.case_pending_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, 'pending')
return True
def case_reset(self, cr, uid, ids, context=None):
"""Resets case as draft"""
state = 'draft'
cases = self.browse(cr, uid, ids)
cases[0].state # to fill the browse record cache
self.write(cr, uid, ids, {'state': state, 'active': True})
self.case_reset_send_note(cr, uid, ids, context=context)
self._action(cr, uid, cases, state)
return True
def remind_partner(self, cr, uid, ids, context=None, attach=False):
return self.remind_user(cr, uid, ids, context, attach,
destination=False)
def remind_user(self, cr, uid, ids, context=None, attach=False, destination=True):
mail_message = self.pool.get('mail.message')
for case in self.browse(cr, uid, ids, context=context):
if not destination and not case.email_from:
return False
if not case.user_id.user_email:
return False
if destination and case.section_id.user_id:
case_email = case.section_id.user_id.user_email
else:
case_email = case.user_id.user_email
src = case_email
dest = case.user_id.user_email or ""
body = case.description or ""
for message in case.message_ids:
if message.email_from and message.body_text:
body = message.body_text
break
if not destination:
src, dest = dest, case.email_from
if body and case.user_id.signature:
if body:
body += '\n\n%s' % (case.user_id.signature)
else:
body = '\n\n%s' % (case.user_id.signature)
body = self.format_body(body)
attach_to_send = {}
if attach:
attach_ids = self.pool.get('ir.attachment').search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', case.id)])
attach_to_send = self.pool.get('ir.attachment').read(cr, uid, attach_ids, ['datas_fname', 'datas'])
attach_to_send = dict(map(lambda x: (x['datas_fname'], base64.decodestring(x['datas'])), attach_to_send))
# Send an email
subject = "Reminder: [%s] %s" % (str(case.id), case.name, )
mail_message.schedule_with_attach(cr, uid,
src,
[dest],
subject,
body,
model=self._name,
reply_to=case.section_id.reply_to,
res_id=case.id,
attachments=attach_to_send,
context=context
)
return True
def _check(self, cr, uid, ids=False, context=None):
"""Function called by the scheduler to process cases for date actions
Only works on not done and cancelled cases
"""
cr.execute('select * from crm_case \
where (date_action_last<%s or date_action_last is null) \
and (date_action_next<=%s or date_action_next is null) \
and state not in (\'cancel\',\'done\')',
(time.strftime("%Y-%m-%d %H:%M:%S"),
time.strftime('%Y-%m-%d %H:%M:%S')))
ids2 = map(lambda x: x[0], cr.fetchall() or [])
cases = self.browse(cr, uid, ids2, context=context)
return self._action(cr, uid, cases, False, context=context)
def format_body(self, body):
return self.pool.get('base.action.rule').format_body(body)
def format_mail(self, obj, body):
return self.pool.get('base.action.rule').format_mail(obj, body)
def message_thread_followers(self, cr, uid, ids, context=None):
res = {}
for case in self.browse(cr, uid, ids, context=context):
l=[]
if case.email_cc:
l.append(case.email_cc)
if case.user_id and case.user_id.user_email:
l.append(case.user_id.user_email)
res[case.id] = l
return res
def _links_get(self, cr, uid, context=None):
"""Gets links value for reference field"""

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,15 +19,15 @@
#
##############################################################################
from osv import fields, osv
from datetime import datetime
import crm
import time
from tools.translate import _
from crm import crm_case
import binascii
import tools
from base_status.base_stage import base_stage
import crm
from datetime import datetime
from mail.mail_message import to_email
from osv import fields, osv
import time
import tools
from tools.translate import _
CRM_LEAD_PENDING_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
@ -35,22 +35,68 @@ CRM_LEAD_PENDING_STATES = (
crm.AVAILABLE_STATES[4][0], # Pending
)
class crm_lead(crm_case, osv.osv):
class crm_lead(base_stage, osv.osv):
""" CRM Lead Case """
_name = "crm.lead"
_description = "Lead/Opportunity"
_order = "priority,date_action,id desc"
_inherit = ['ir.needaction_mixin', 'mail.thread','res.partner']
def _get_default_section_id(self, cr, uid, context=None):
""" Gives default section by checking if present in the context """
return (self._resolve_section_id_from_context(cr, uid, context=context) or False)
def _get_default_stage_id(self, cr, uid, context=None):
""" Gives default stage_id """
section_id = self._get_default_section_id(cr, uid, context=context)
return self.stage_find(cr, uid, [], section_id, [('state', '=', 'draft'), ('type', '=', 'both')], context=context)
def _resolve_section_id_from_context(self, cr, uid, context=None):
""" Returns ID of section based on the value of 'section_id'
context key, or None if it cannot be resolved to a single
Sales Team.
"""
if context is None:
context = {}
if type(context.get('default_section_id')) in (int, long):
return context.get('default_section_id')
if isinstance(context.get('default_section_id'), basestring):
section_name = context['default_section_id']
section_ids = self.pool.get('crm.case.section').name_search(cr, uid, name=section_name, context=context)
if len(section_ids) == 1:
return int(section_ids[0][0])
return None
def _resolve_type_from_context(self, cr, uid, context=None):
""" Returns the type (lead or opportunity) from the type context
key. Returns None if it cannot be resolved.
"""
if context is None:
context = {}
return context.get('default_type')
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('crm.case.stage')
order = stage_obj._order
# lame hack to allow reverting search, should just work in the trivial case
if read_group_order == 'stage_id desc':
# lame hack to allow reverting search, should just work in the trivial case
order = "%s desc" % order
stage_ids = stage_obj._search(cr, uid, ['|', ('id','in',ids),('case_default','=',1)], order=order,
access_rights_uid=access_rights_uid, context=context)
# 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 ('section_ids', '=', section_id), ('fold', '=', False) if section_id: add section columns that are not folded
search_domain = []
section_id = self._resolve_section_id_from_context(cr, uid, context=context)
if section_id:
search_domain += ['|', '&', ('section_ids', '=', section_id), ('fold', '=', False)]
search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)]
# retrieve type from the context (if set: choose 'type' or 'both')
type = self._resolve_type_from_context(cr, uid, context=context)
if type:
search_domain += ['|', ('type', '=', type), ('type', '=', 'both')]
# perform search
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])))
@ -154,7 +200,6 @@ class crm_lead(crm_case, osv.osv):
'email_cc': fields.text('Global CC', size=252 , 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"),
'description': fields.text('Notes'),
'write_date': fields.datetime('Update Date' , readonly=True),
'categ_id': fields.many2one('crm.case.categ', 'Category', \
domain="['|',('section_id','=',section_id),('section_id','=',False), ('object_id.model', '=', 'crm.lead')]"),
'type_id': fields.many2one('crm.case.resource.type', 'Campaign', \
@ -167,7 +212,8 @@ class crm_lead(crm_case, osv.osv):
'type':fields.selection([ ('lead','Lead'), ('opportunity','Opportunity'), ],'Type', help="Type is used to separate Leads and Opportunities"),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
'date_closed': fields.datetime('Closed', readonly=True),
'stage_id': fields.many2one('crm.case.stage', 'Stage', domain="[('section_ids', '=', section_id)]"),
'stage_id': fields.many2one('crm.case.stage', 'Stage',
domain="['&', '|', ('section_ids', '=', section_id), ('case_default', '=', True), '|', ('type', '=', type), ('type', '=', 'both')]"),
'user_id': fields.many2one('res.users', 'Salesperson'),
'referred': fields.char('Referred By', size=64),
'date_open': fields.datetime('Opened', readonly=True),
@ -175,11 +221,13 @@ class crm_lead(crm_case, osv.osv):
multi='day_open', type="float", store=True),
'day_close': fields.function(_compute_day, string='Days to Close', \
multi='day_close', type="float", store=True),
'state': fields.selection(crm.AVAILABLE_STATES, 'Status', 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 \'In progress\'.\
\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\'.'),
'state': fields.related('stage_id', 'state', type="selection", store=True,
selection=crm.AVAILABLE_STATES, string="State", readonly=True,
help='The state is set to \'Draft\', when a case is created.\
If the case is in progress the state is set to \'Open\'.\
When the case is over, the state is set to \'Done\'.\
If the case needs to be reviewed then the state is \
set to \'Pending\'.'),
'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
'subjects': fields.function(_get_email_subject, fnct_search=_history_search, string='Subject of Email', type='char', size=64),
@ -192,23 +240,21 @@ class crm_lead(crm_case, osv.osv):
'date_deadline': fields.date('Expected Closing'),
'date_action': fields.date('Next Action Date', select=True),
'title_action': fields.char('Next Action', size=64),
'stage_id': fields.many2one('crm.case.stage', 'Stage', domain="[('section_ids', '=', section_id)]"),
'color': fields.integer('Color Index'),
'partner_address_name': fields.related('partner_id', 'name', type='char', string='Partner Contact Name', readonly=True),
'partner_address_email': fields.related('partner_id', 'email', type='char', string='Partner Contact Email', readonly=True),
'company_currency': fields.related('company_id', 'currency_id', 'symbol', type='char', string='Company Currency', readonly=True),
'user_email': fields.related('user_id', 'user_email', type='char', string='User Email', readonly=True),
'user_login': fields.related('user_id', 'login', type='char', string='User Login', readonly=True),
}
_defaults = {
'active': lambda *a: 1,
'user_id': crm_case._get_default_user,
'email_from': crm_case._get_default_email,
'state': lambda *a: 'draft',
'type': lambda *a: 'lead',
'section_id': crm_case._get_section,
'active': 1,
'type': 'lead',
'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c),
'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.lead', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'color': 0,
@ -241,95 +287,104 @@ class crm_lead(crm_case, osv.osv):
return {'value':{}}
return {'value':{'probability': stage.probability}}
def stage_find_percent(self, cr, uid, percent, section_id):
""" Return the first stage with a probability == percent
def _check(self, cr, uid, ids=False, context=None):
""" Override of the base.stage method.
Function called by the scheduler to process cases for date actions
Only works on not done and cancelled cases
"""
stage_pool = self.pool.get('crm.case.stage')
if section_id :
ids = stage_pool.search(cr, uid, [("probability", '=', percent), ("section_ids", 'in', [section_id])])
else :
ids = stage_pool.search(cr, uid, [("probability", '=', percent)])
cr.execute('select * from crm_case \
where (date_action_last<%s or date_action_last is null) \
and (date_action_next<=%s or date_action_next is null) \
and state not in (\'cancel\',\'done\')',
(time.strftime("%Y-%m-%d %H:%M:%S"),
time.strftime('%Y-%m-%d %H:%M:%S')))
if ids:
return ids[0]
ids2 = map(lambda x: x[0], cr.fetchall() or [])
cases = self.browse(cr, uid, ids2, context=context)
return self._action(cr, uid, cases, False, context=context)
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)
# collect all section_ids
section_ids = []
types = ['both']
if section_id:
section_ids.append(section_id)
for lead in cases:
if lead.section_id:
section_ids.append(lead.section_id.id)
if lead.type not in types:
types.append(lead.type)
# 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(('section_ids', '=', section_id))
search_domain.append(('case_default', '=', True))
# AND with cases types
search_domain.append(('type', 'in', types))
# AND with the domain in parameter
search_domain += list(domain)
# perform search, return the first found
stage_ids = self.pool.get('crm.case.stage').search(cr, uid, search_domain, order=order, context=context)
if stage_ids:
return stage_ids[0]
return False
def stage_find_lost(self, cr, uid, section_id):
return self.stage_find_percent(cr, uid, 0.0, section_id)
def stage_find_won(self, cr, uid, section_id):
return self.stage_find_percent(cr, uid, 100.0, section_id)
def case_open(self, cr, uid, ids, context=None):
for lead in self.browse(cr, uid, ids, context=context):
if lead.state == 'draft':
value = {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')}
self.write(cr, uid, [lead.id], value)
if lead.type == 'opportunity' and not lead.stage_id:
stage_id = self.stage_find(cr, uid, lead.section_id.id or False, [('sequence','>',0)])
if stage_id:
self.stage_set(cr, uid, [lead.id], stage_id)
res = super(crm_lead, self).case_open(cr, uid, ids, context)
return res
def case_close(self, cr, uid, ids, context=None):
res = super(crm_lead, self).case_close(cr, uid, ids, context)
self.write(cr, uid, ids, {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')})
return res
def case_cancel(self, cr, uid, ids, context=None):
"""Overrides cancel for crm_case for setting probability
"""
res = super(crm_lead, self).case_cancel(cr, uid, ids, context)
self.write(cr, uid, ids, {'probability' : 0.0})
""" Overrides case_cancel from base_stage to set probability """
res = super(crm_lead, self).case_cancel(cr, uid, ids, context=context)
self.write(cr, uid, ids, {'probability' : 0.0}, context=context)
return res
def case_reset(self, cr, uid, ids, context=None):
"""Overrides reset as draft in order to set the stage field as empty
"""
res = super(crm_lead, self).case_reset(cr, uid, ids, context)
self.write(cr, uid, ids, {'stage_id': False, 'probability': 0.0})
""" Overrides case_reset from base_stage to set probability """
res = super(crm_lead, self).case_reset(cr, uid, ids, context=context)
self.write(cr, uid, ids, {'probability': 0.0}, context=context)
return res
def case_mark_lost(self, cr, uid, ids, context=None):
"""Mark the case as lost: state = done and probability = 0%
"""
res = super(crm_lead, self).case_close(cr, uid, ids, context)
self.write(cr, uid, ids, {'probability' : 0.0})
""" Mark the case as lost: state=cancel and probability=0 """
for lead in self.browse(cr, uid, ids):
stage_id = self.stage_find_lost(cr, uid, lead.section_id.id or False)
stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 0.0)], context=context)
if stage_id:
self.stage_set(cr, uid, [lead.id], stage_id)
return res
self.case_set(cr, uid, [lead.id], values_to_update={'probability': 0.0}, new_stage_id=stage_id, context=context)
self.case_mark_lost_send_note(cr, uid, ids, context=context)
return True
def case_mark_won(self, cr, uid, ids, context=None):
"""Mark the case as lost: state = done and probability = 0%
"""
res = super(crm_lead, self).case_close(cr, uid, ids, context=None)
self.write(cr, uid, ids, {'probability' : 100.0})
""" Mark the case as lost: state=done and probability=100 """
for lead in self.browse(cr, uid, ids):
stage_id = self.stage_find_won(cr, uid, lead.section_id.id or False)
stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, [('probability', '=', 100.0)], context=context)
if stage_id:
self.stage_set(cr, uid, [lead.id], stage_id)
self.case_mark_won_send_note(cr, uid, [lead.id], context=context)
return res
self.case_set(cr, uid, [lead.id], values_to_update={'probability': 100.0}, new_stage_id=stage_id, context=context)
self.case_mark_won_send_note(cr, uid, ids, context=context)
return True
def set_priority(self, cr, uid, ids, priority):
"""Set lead priority
""" Set lead priority
"""
return self.write(cr, uid, ids, {'priority' : priority})
def set_high_priority(self, cr, uid, ids, context=None):
"""Set lead priority to high
""" Set lead priority to high
"""
return self.set_priority(cr, uid, ids, '1')
def set_normal_priority(self, cr, uid, ids, context=None):
"""Set lead priority to normal
""" Set lead priority to normal
"""
return self.set_priority(cr, uid, ids, '3')
def _merge_data(self, cr, uid, ids, oldest, fields, context=None):
# prepare opportunity data into dictionary for merging
opportunities = self.browse(cr, uid, ids, context=context)
@ -807,22 +862,12 @@ class crm_lead(crm_case, osv.osv):
"You should better cancel it, instead of deleting it.") % lead.name)
return super(crm_lead, self).unlink(cr, uid, ids, context)
def write(self, cr, uid, ids, vals, context=None):
if not context:
context = {}
if 'date_closed' in vals:
return super(crm_lead,self).write(cr, uid, ids, vals, context=context)
if vals.get('stage_id'):
stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context)
if vals.get('stage_id') and not vals.get('probability'):
# change probability of lead(s) if required by stage
if not vals.get('probability') and stage.on_change:
stage = self.pool.get('crm.case.stage').browse(cr, uid, vals['stage_id'], context=context)
if stage.on_change:
vals['probability'] = stage.probability
for case in self.browse(cr, uid, ids, context=context):
message = _("Stage changed to <b>%s</b>.") % (stage.name)
case.message_append_note(body=message)
return super(crm_lead,self).write(cr, uid, ids, vals, context)
# ----------------------------------------
@ -837,6 +882,11 @@ class crm_lead(crm_case, osv.osv):
sub_ids.append(obj.user_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('crm.case.stage').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 case_get_note_msg_prefix(self, cr, uid, lead, context=None):
if isinstance(lead, (int, long)):
lead = self.browse(cr, uid, [lead], context=context)[0]

View File

@ -3,47 +3,81 @@
<data noupdate="1">
<!-- Crm stages -->
<record model="crm.case.stage" id="stage_lead6">
<field name="name">Lost</field>
<field eval="1" name="case_default"/>
<field eval="'0'" name="probability"/>
<field eval="'0'" name="sequence"/>
</record>
<record model="crm.case.stage" id="stage_lead1">
<field name="name">New</field>
<field eval="1" name="case_default"/>
<field name="state">draft</field>
<field eval="'10'" name="probability"/>
<field eval="'11'" name="sequence"/>
<field eval="'10'" name="sequence"/>
<field name="type">both</field>
</record>
<record model="crm.case.stage" id="stage_lead2">
<field name="name">Qualification</field>
<field name="name">Opportunity</field>
<field eval="1" name="case_default"/>
<field name="state">open</field>
<field eval="'20'" name="probability"/>
<field eval="'12'" name="sequence"/>
<field eval="'11'" name="sequence"/>
<field name="type">lead</field>
</record>
<record model="crm.case.stage" id="stage_lead3">
<field name="name">Proposition</field>
<field name="name">Qualification</field>
<field eval="1" name="case_default"/>
<field eval="'40'" name="probability"/>
<field eval="'13'" name="sequence"/>
<field name="state">open</field>
<field eval="'20'" name="probability"/>
<field eval="'12'" name="sequence"/>
<field name="type">opportunity</field>
</record>
<record model="crm.case.stage" id="stage_lead4">
<field name="name">Negotiation</field>
<field name="name">Proposition</field>
<field eval="1" name="case_default"/>
<field eval="'60'" name="probability"/>
<field eval="'14'" name="sequence"/>
<field name="state">open</field>
<field eval="'40'" name="probability"/>
<field eval="'13'" name="sequence"/>
<field name="type">opportunity</field>
</record>
<record model="crm.case.stage" id="stage_lead5">
<field name="name">Negotiation</field>
<field eval="1" name="case_default"/>
<field name="state">open</field>
<field eval="'60'" name="probability"/>
<field eval="'14'" name="sequence"/>
<field name="type">opportunity</field>
</record>
<record model="crm.case.stage" id="stage_lead6">
<field name="name">Won</field>
<field eval="1" name="case_default"/>
<field name="state">done</field>
<field eval="'100'" name="probability"/>
<field eval="'15'" name="sequence"/>
<field eval="1" name="on_change"/>
<field name="type">opportunity</field>
</record>
<record model="crm.case.stage" id="stage_lead7">
<field name="name">Dead</field>
<field eval="1" name="case_default"/>
<field eval="True" name="fold"/>
<field name="state">cancel</field>
<field eval="'0'" name="probability"/>
<field eval="'16'" name="sequence"/>
<field name="type">lead</field>
</record>
<record model="crm.case.stage" id="stage_lead8">
<field name="name">Lost</field>
<field eval="1" name="case_default"/>
<field eval="True" name="fold"/>
<field name="state">cancel</field>
<field eval="'0'" name="probability"/>
<field eval="'17'" name="sequence"/>
<field name="type">opportunity</field>
</record>
<record model="crm.case.section" id="section_sales_department">
<field name="name">Sales Department</field>
<field name="code">Sales</field>
<field name="stage_ids" eval="[(4, ref('stage_lead1')), (4, ref('stage_lead2')), (4, ref('stage_lead3')), (4, ref('stage_lead4')), (4, ref('stage_lead5')), (4, ref('stage_lead6'))]"/>
<field name="stage_ids" eval="[ (4, ref('stage_lead1')), (4, ref('stage_lead2')),
(4, ref('stage_lead3')), (4, ref('stage_lead4')),
(4, ref('stage_lead5')), (4, ref('stage_lead6')),
(4, ref('stage_lead7')), (4, ref('stage_lead8'))]"/>
</record>
<!-- Crm campain -->

View File

@ -8,14 +8,13 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_root"/>
<field eval="'The Oil Company'" name="partner_name"/>
<field eval="'draft'" name="state"/>
<field name="section_id" ref="crm.section_sales_marketing_department"/>
<field eval="'Luc Latour'" name="contact_name"/>
<field name="title" ref="base.res_partner_title_sir"/>
<field eval="'Training Manager'" name="function"/>
<field eval="'Paris'" name="city"/>
<field name="country_id" ref="base.fr"/>
<field eval="'luc.latour@oilcompany.fr'" name="email_from"/>
<field eval="'Training Manager'" name="function"/>
<field eval="'Paris'" name="city"/>
<field name="country_id" ref="base.fr"/>
<field eval="'luc.latour@oilcompany.fr'" name="email_from"/>
<field eval="'0033 621 782-0636'" name="mobile"/>
<field eval="1" name="active"/>
<field name="stage_id" ref="crm.stage_lead1"/>
@ -29,18 +28,17 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_root"/>
<field eval="'Le Club SARL'" name="partner_name"/>
<field eval="'Marc Dufour'" name="contact_name"/>
<field eval="'Marc Dufour'" name="contact_name"/>
<field name="title" ref="base.res_partner_title_sir"/>
<field eval="'Purchase Manager'" name="function"/>
<field eval="'Bordeaux'" name="city"/>
<field name="country_id" ref="base.fr"/>
<field eval="'md@leclub.fr'" name="email_from"/>
<field eval="'draft'" name="state"/>
<field eval="'Purchase Manager'" name="function"/>
<field eval="'Bordeaux'" name="city"/>
<field name="country_id" ref="base.fr"/>
<field eval="'md@leclub.fr'" name="email_from"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="'(392) 895-7917'" name="mobile"/>
<field eval="1" name="active"/>
<field name="categ_id" ref="crm.categ_oppor2"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Interest in Your New Product'" name="name"/>
<field eval="'(956) 293-2595'" name="phone"/>
</record>
@ -50,15 +48,14 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_demo"/>
<field eval="'The Kompany'" name="partner_name"/>
<field eval="'John Miller'" name="contact_name"/>
<field eval="'New-York'" name="city"/>
<field name="country_id" ref="base.us"/>
<field eval="'draft'" name="state"/>
<field eval="'John Miller'" name="contact_name"/>
<field eval="'New-York'" name="city"/>
<field name="country_id" ref="base.us"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="'(820) 167-3208'" name="mobile"/>
<field eval="1" name="active"/>
<field name="categ_id" ref="crm.categ_oppor4"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Need Info about Web Design'" name="name"/>
<field eval="'(079) 681-2139'" name="phone"/>
<field eval="'contact@thkompany.com'" name="email_from"/>
@ -68,10 +65,9 @@
<field eval="'3'" name="priority"/>
<field name="type">lead</field>
<field eval="'The Gas Company'" name="partner_name"/>
<field eval="'Henry Mc Coy'" name="contact_name"/>
<field eval="'London'" name="city"/>
<field name="country_id" ref="base.uk"/>
<field eval="'draft'" name="state"/>
<field eval="'Henry Mc Coy'" name="contact_name"/>
<field eval="'London'" name="city"/>
<field name="country_id" ref="base.uk"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="'(077) 582-4035'" name="mobile"/>
<field eval="1" name="active"/>
@ -87,16 +83,15 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_root"/>
<field eval="'Stonage IT'" name="partner_name"/>
<field eval="'Carrie Helle'" name="contact_name"/>
<field eval="'Purchase Manager'" name="function"/>
<field eval="'Bruxelles'" name="city"/>
<field name="country_id" ref="base.be"/>
<field eval="'draft'" name="state"/>
<field eval="'Carrie Helle'" name="contact_name"/>
<field eval="'Purchase Manager'" name="function"/>
<field eval="'Bruxelles'" name="city"/>
<field name="country_id" ref="base.be"/>
<field name="section_id" ref="crm.section_sales_marketing_department"/>
<field eval="'(333) 715-1450'" name="mobile"/>
<field eval="1" name="active"/>
<field name="categ_id" ref="crm.categ_oppor1"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Need a Quotation for PC1'" name="name"/>
<field eval="'(855) 924-4364'" name="phone"/>
<field eval="'helle@stonageit.be'" name="email_from"/>
@ -110,15 +105,14 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_root"/>
<field eval="'Opensides'" name="partner_name"/>
<field eval="'Tina Pinero'" name="contact_name"/>
<field eval="'Consultant'" name="function"/>
<field eval="'Roma'" name="city"/>
<field name="country_id" ref="base.it"/>
<field eval="'draft'" name="state"/>
<field eval="'Tina Pinero'" name="contact_name"/>
<field eval="'Consultant'" name="function"/>
<field eval="'Roma'" name="city"/>
<field name="country_id" ref="base.it"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="'(468) 017-2684'" name="mobile"/>
<field name="categ_id" ref="crm.categ_oppor8"/>
<field name="stage_id" ref="crm.stage_lead6"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Need Info about your Services'" name="name"/>
<field eval="'(373) 907-1009'" name="phone"/>
<field eval="'info@opensides.be'" name="email_from"/>
@ -130,15 +124,14 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_demo"/>
<field eval="'Gardner Group'" name="partner_name"/>
<field eval="'Wendi Baltz'" name="contact_name"/>
<field eval="'Journalist'" name="function"/>
<field eval="'Kiev'" name="city"/>
<field name="country_id" ref="base.ua"/>
<field eval="'draft'" name="state"/>
<field eval="'Wendi Baltz'" name="contact_name"/>
<field eval="'Journalist'" name="function"/>
<field eval="'Kiev'" name="city"/>
<field name="country_id" ref="base.ua"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="'(463) 014-1208'" name="mobile"/>
<field name="categ_id" ref="crm.categ_oppor4"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Info about Your Company ?'" name="name"/>
<field eval="'(282) 603-7489'" name="phone"/>
</record>
@ -149,17 +142,16 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_demo"/>
<field eval="'Survey'" name="name"/>
<field eval="'open'" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor6"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Survey Expert'" name="partner_name"/>
<field eval="'John Smith'" name="contact_name"/>
<field eval="'Sales'" name="function"/>
<field eval="'Cambridge'" name="city"/>
<field name="country_id" ref="base.uk"/>
<field eval="'John Smith'" name="contact_name"/>
<field eval="'Sales'" name="function"/>
<field eval="'Cambridge'" name="city"/>
<field name="country_id" ref="base.uk"/>
<field eval="'smith_john@gmail.com'" name="email_from"/>
<field eval="'(282) 596-8584'" name="phone"/>
<field eval="'(282) 596-8584'" name="phone"/>
</record>
<record id="crm_case_business_card0" model="crm.lead">
<field eval="1" name="active"/>
@ -168,17 +160,16 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_root"/>
<field eval="'Partnership'" name="name"/>
<field eval="'open'" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor5"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Marketing Business'" name="partner_name"/>
<field eval="'Laure Smith'" name="contact_name"/>
<field eval="'Sales'" name="function"/>
<field eval="'Oxford'" name="city"/>
<field name="country_id" ref="base.uk"/>
<field eval="'Laure Smith'" name="contact_name"/>
<field eval="'Sales'" name="function"/>
<field eval="'Oxford'" name="city"/>
<field name="country_id" ref="base.uk"/>
<field eval="'l.smith@marketing-business.com'" name="email_from"/>
<field eval="'(252) 578-7894'" name="phone"/>
<field eval="'(252) 578-7894'" name="phone"/>
</record>
<record id="crm_case_imported_contact0" model="crm.lead">
<field eval="1" name="active"/>
@ -186,9 +177,8 @@
<field eval="'2'" name="priority"/>
<field name="type">lead</field>
<field eval="'Info'" name="name"/>
<field eval="'cancel'" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Business Group'" name="partner_name"/>
</record>
<record id="crm_case_employee0" model="crm.lead">
@ -198,12 +188,11 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_demo"/>
<field eval="'Need Info about Onsite Intervention'" name="name"/>
<field eval="'draft'" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor3"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Agrolait'" name="partner_name"/>
<field eval="'Sylvie Lelitre'" name="contact_name"/>
<field eval="'Sylvie Lelitre'" name="contact_name"/>
</record>
<record id="crm_case_company_partnership0" model="crm.lead">
<field eval="1" name="active"/>
@ -212,28 +201,15 @@
<field name="type">lead</field>
<field name="user_id" ref="base.user_demo"/>
<field eval="'Need Quotation for 100 PC and 100 Keyboards'" name="name"/>
<field eval="'done'" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor1"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Centrale d\'achats 1'" name="partner_name"/>
</record>
<!-- Call Function to Open the leads-->
<function model="crm.lead" name="case_open"
eval="[ref('crm_case_electonicgoodsdealer0'), ref('crm_case_company_partnership0'), ref('crm_case_webvisitor0'), ref('crm_case_business_card0'), ref('crm.crm_case_employee0')], {'install_mode': True}"
/>
<!-- Call Function to mark the lead as Pending-->
<function model="crm.lead" name="case_pending"
eval="[ref('crm_case_itdeveloper0')], {'install_mode': True}"
/>
<!-- Call Function to Close the leads-->
<function model="crm.lead" name="case_close"
eval="[ref('crm_case_vpoperations0'), ref('crm_case_developingwebapplications0'), ref('crm_case_webvisitor0')], {'install_mode': True}"
/>
<!-- Call Function to Cancel the leads-->
<!-- Call Function to Cancel the leads (set as Dead) -->
<function model="crm.lead" name="case_cancel"
eval="[ref('crm_case_mgroperations0'), ref('crm_case_imported_contact0')], {'install_mode': True}"
eval="[ref('crm_case_company_partnership0'), ref('crm_case_vpoperations0'), ref('crm_case_developingwebapplications0'), ref('crm_case_webvisitor0')], {'install_mode': True}"
/>
<!-- Demo Opportunities -->
@ -245,11 +221,10 @@
<field name="partner_id" ref="base.res_partner_3"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'open'" name="state"/>
<field eval="'150000'" name="planned_revenue"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor1"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field eval="'Plan to buy 200 PC2'" name="name"/>
<field eval="'Conf call with purchase manager'" name="title_action"/>
</record>
@ -260,12 +235,11 @@
<field name="partner_id" ref="base.res_partner_9"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="'draft'" name="state"/>
<field eval="45000.0" name="planned_revenue"/>
<field eval="35" name="probability"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor3"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="'Pricing Information of Onsite Intervention'" name="name"/>
<field eval="'Send price list regarding our interventions'" name="title_action"/>
<field eval="time.strftime('%Y-%m-03')" name="date_action"/>
@ -283,12 +257,11 @@
<field name="partner_id" ref="base.res_partner_lucievonck0"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="'draft'" name="state"/>
<field eval="30000.0" name="planned_revenue"/>
<field eval="30" name="probability"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor3"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="'Interest in your Kitchen Design Project'" name="name"/>
<field eval="'Send Catalogue by E-Mail'" name="title_action"/>
<field eval="time.strftime('%Y-01-10')" name="date_action"/>
@ -299,19 +272,18 @@
<field eval="1367" name="zip"/>
<field name="country_id" ref="base.be"/>
</record>
<record id="crm_case_unifliege" model="crm.lead">
<record id="crm_case_unifliege" model="crm.lead">
<field eval="1" name="active"/>
<field name="type">opportunity</field>
<field name="type_id" ref="crm.type_lead2"/>
<field name="partner_id" ref="base.res_partner_accent"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'open'" name="state"/>
<field eval="2500.0" name="planned_revenue"/>
<field eval="25" name="probability"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor6"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="'Plan train our students on your product'" name="name"/>
<field eval="'Call to define real needs about training'" name="title_action"/>
<field eval="time.strftime('%Y-%m-04')" name="date_action"/>
@ -323,19 +295,18 @@
<field eval="75016" name="zip"/>
<field name="country_id" ref="base.be"/>
</record>
<record id="crm_case_bankwealthy2" model="crm.lead">
<record id="crm_case_bankwealthy2" model="crm.lead">
<field eval="1" name="active"/>
<field name="type">opportunity</field>
<field name="type_id" ref="crm.type_lead2"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'open'" name="state"/>
<field eval="462.0" name="planned_revenue"/>
<field eval="40" name="probability"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor2"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="'Plan to buy 66 keyboards and 66 mouses'" name="name"/>
<field eval="'Propose the kit keyboard+mouse'" name="title_action"/>
<field eval="time.strftime('%Y-04-12')" name="date_action"/>
@ -354,12 +325,11 @@
<field name="partner_id" ref="base.res_partner_8"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'done'" name="state"/>
<field eval="55000.0" name="planned_revenue"/>
<field eval="90" name="probability"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor7"/>
<field name="stage_id" ref="crm.stage_lead5"/>
<field name="stage_id" ref="crm.stage_lead6"/>
<field eval="'Need 20 Days of Consultancy'" name="name"/>
<field eval="time.strftime('%Y-%m-%d')" name="date_deadline"/>
<field eval="'info@mycompany.net'" name="email_from"/>
@ -372,11 +342,10 @@
<field name="partner_id" ref="base.res_partner_duboissprl0"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="'open'" name="state"/>
<field eval="45000.0" name="planned_revenue"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor5"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field name="stage_id" ref="crm.stage_lead5"/>
<field eval="'Need new design for my website'" name="name"/>
<field eval="time.strftime('%Y-05-01')" name="date_action"/>
<field eval="time.strftime('%Y-06-30')" name="date_deadline"/>
@ -395,40 +364,37 @@
<field name="partner_id" ref="base.res_partner_maxtor"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="'done'" name="state"/>
<field eval="42000.0" name="planned_revenue"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor2"/>
<field name="stage_id" ref="crm.stage_lead6"/>
<field name="stage_id" ref="crm.stage_lead8"/>
<field eval="'Want to subscribe to your online solution'" name="name"/>
</record>
<record id="crm_case_dirtminingltdunits0" model="crm.lead">
<record id="crm_case_dirtminingltdunits0" model="crm.lead">
<field eval="30" name="probability"/>
<field eval="1" name="active"/>
<field name="type">opportunity</field>
<field name="partner_id" ref="base.res_partner_tinyatwork"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'done'" name="state"/>
<field eval="25000.0" name="planned_revenue"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor6"/>
<field name="stage_id" ref="crm.stage_lead6"/>
<field name="stage_id" ref="crm.stage_lead8"/>
<field eval="'Interest in your Partnership Contract'" name="name"/>
</record>
<record id="crm_case_dirtminingltdunits10" model="crm.lead">
<record id="crm_case_dirtminingltdunits10" model="crm.lead">
<field eval="30" name="probability"/>
<field eval="1" name="active"/>
<field name="type">opportunity</field>
<field name="partner_id" ref="base.res_partner_desertic_hispafuentes"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'open'" name="state"/>
<field eval="5000" name="planned_revenue"/>
<field eval="30" name="probability"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor2"/>
<field name="stage_id" ref="crm.stage_lead2"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field eval="'Plan to attend a training'" name="name"/>
<field eval="time.strftime('%Y-04-10')" name="date_action"/>
<field eval="time.strftime('%Y-06-12')" name="date_deadline"/>
@ -439,7 +405,7 @@
<field eval="77420" name="zip"/>
<field name="country_id" ref="base.fr"/>
</record>
<record id="crm_case_construstazunits0" model="crm.lead">
<record id="crm_case_construstazunits0" model="crm.lead">
<field eval="60" name="probability"/>
<field eval="1" name="active"/>
<field name="type">opportunity</field>
@ -447,11 +413,10 @@
<field name="partner_id" ref="base.res_partner_thymbra"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'open'" name="state"/>
<field eval="'150000'" name="planned_revenue"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor1"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field eval="'Need customize the solution'" name="name"/>
<field eval="'Conf call with technical service'" name="title_action"/>
<field name="partner_name">Thymbra</field>
@ -461,7 +426,7 @@
<field eval="1659" name="zip"/>
<field name="country_id" ref="base.ar"/>
</record>
<record id="crm_case_ericdubois4" model="crm.lead">
<record id="crm_case_ericdubois4" model="crm.lead">
<field eval="65" name="probability"/>
<field eval="1" name="active"/>
<field name="type">opportunity</field>
@ -469,11 +434,10 @@
<field name="partner_id" ref="base.res_partner_ericdubois0"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'open'" name="state"/>
<field eval="'1200'" name="planned_revenue"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm.categ_oppor1"/>
<field name="stage_id" ref="crm.stage_lead3"/>
<field name="stage_id" ref="crm.stage_lead4"/>
<field eval="'Interest in your customizable PC'" name="name"/>
<field eval="time.strftime('%Y-08-05')" name="date_action"/>
<field eval="time.strftime('%Y-10-10')" name="date_deadline"/>
@ -484,26 +448,24 @@
<field eval="7000" name="zip"/>
<field name="country_id" ref="base.be"/>
</record>
<record id="crm_case_fabiendupont" model="crm.lead">
<record id="crm_case_fabiendupont" model="crm.lead">
<field eval="1" name="active"/>
<field name="type">opportunity</field>
<field name="type_id" ref="crm.type_lead1"/>
<field name="partner_id" ref="base.res_partner_fabiendupont0"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="'draft'" name="state"/>
<field name="categ_id" ref="crm.categ_oppor4"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Need more info about the onsite intervention'" name="name"/>
</record>
<record id="crm_case_shelvehouse" model="crm.lead">
<record id="crm_case_shelvehouse" model="crm.lead">
<field eval="1" name="active"/>
<field name="type">opportunity</field>
<field name="type_id" ref="crm.type_lead1"/>
<field name="partner_id" ref="base.res_partner_theshelvehouse0"/>
<field eval="'3'" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="'draft'" name="state"/>
<field name="categ_id" ref="crm.categ_oppor4"/>
<field name="stage_id" ref="crm.stage_lead1"/>
<field eval="'Need more info about your pc2'" name="name"/>

View File

@ -1,35 +1,41 @@
<?xml version="1.0"?>
<openerp>
<data>
<data>
<!-- Stage Search view -->
<record id="crm_lead_stage_search" model="ir.ui.view">
<field name="name">Stage - Search</field>
<field name="model">crm.case.stage</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Stage Search">
<field name="name"/>
</search>
</field>
</record>
<!--
CRM CASE STAGE
-->
<!--Lead Stage Form view -->
<record id="crm_lead_stage_act" model="ir.actions.act_window">
<field name="name">Stages</field>
<field name="res_model">crm.case.stage</field>
<field name="view_type">form</field>
<field name="view_id" ref="crm.crm_case_stage_tree"/>
<field name="help">Add specific stages to leads and opportunities allowing your sales to better organise their sales pipeline. Stages will allow them to easily track how a specific lead or opportunity is positioned in the sales cycle.</field>
</record>
<!-- Stage Search view -->
<record id="crm_lead_stage_search" model="ir.ui.view">
<field name="name">Stage - Search</field>
<field name="model">crm.case.stage</field>
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Stage Search">
<field name="name"/>
<field name="state"/>
<field name="type"/>
</search>
</field>
</record>
<menuitem action="crm_lead_stage_act" id="menu_crm_lead_stage_act" name="Stages"
groups="base.group_no_one" sequence="0"
parent="base.menu_crm_config_lead" />
<!-- Stage Form view -->
<record id="crm_lead_stage_act" model="ir.actions.act_window">
<field name="name">Stages</field>
<field name="res_model">crm.case.stage</field>
<field name="view_type">form</field>
<field name="view_id" ref="crm.crm_case_stage_tree"/>
<field name="help">Add specific stages to leads and opportunities allowing your sales to better organise their sales pipeline. Stages will allow them to easily track how a specific lead or opportunity is positioned in the sales cycle.</field>
</record>
<menuitem action="crm_lead_stage_act" id="menu_crm_lead_stage_act" name="Stages" sequence="0" parent="base.menu_crm_config_lead" />
<!-- Lead/Opportunity Categories Action -->
<!--
LEADS/OPPORTUNITIES CATEGORIES
-->
<!-- Categories Form View -->
<record id="crm_lead_categ_action" model="ir.actions.act_window">
<field name="name">Categories</field>
<field name="res_model">crm.case.categ</field>
@ -44,6 +50,9 @@
id="menu_crm_lead_categ" name="Categories"
parent="base.menu_crm_config_lead" sequence="1" groups="base.group_no_one"/>
<!--
LEADS
-->
<!-- CRM Lead Form View -->
<record model="ir.ui.view" id="crm_case_form_view_leads">
@ -53,14 +62,17 @@
<field name="arch" type="xml">
<form string="Leads Form" layout="manual">
<div class="oe_form_topbar">
<button name="case_open" string="Open" states="draft,pending" type="object" />
<button name="case_mark_lost" string="Close" states="open,pending" type="object" />
<button name="case_pending" string="Pending" states="open" type="object" />
<button name="case_escalate" string="Escalate" states="open,pending" type="object" />
<button name="case_reset" string="Reset to Draft" states="done,cancel" type="object" />
<button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" />
<button name="%(crm.action_crm_lead2opportunity_partner)d" string="Convert to Opportunity" type="action"
states="draft,open,pending" help="Convert to Opportunity"/>
<button name="case_escalate" string="Escalate" type="object"
states="draft,open,pending"/>
<button name="case_reset" string="Reset" type="object"
states="cancel"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending"/>
<div class="oe_right">
<field name="state" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}'/>
<field name="stage_id" nolabel="1" widget="statusbar"
on_change="onchange_stage_id(stage_id)"/>
</div>
<div class="oe_clear"/>
</div>
@ -71,24 +83,9 @@
<field name="categ_id"
widget="selection"
domain="[('object_id.model','=','crm.lead')]"/>
<button
name="%(crm.action_crm_lead2opportunity_partner)d"
string="Convert to Opportunity"
help="Convert to Opportunity" icon="gtk-go-forward"
type="action"
/>
<newline />
<field name="user_id" />
<field name="section_id" widget="selection" />
<field name="stage_id" domain="section_id and [('section_ids', '=', section_id)] or []" />
<group col="2" colspan="1">
<button name="stage_previous" string=""
states="open,pending,draft" type="object"
icon="gtk-go-back" context="{'stage_type': 'lead'}" />
<button name="stage_next" string=""
states="open,pending,draft" type="object"
icon="gtk-go-forward" context="{'stage_type': 'lead'}" />
</group>
<field name="type" invisible="1"/>
</group>
<notebook colspan="4">
@ -142,6 +139,7 @@
<field name="type_id" select="1" widget="selection"/>
<field name="channel_id" select="1" widget="selection"/>
<field name="referred"/>
<field name="state" groups="base.group_no_one"/>
</group>
<group colspan="2" col="2">
<separator string="Mailings" colspan="2" col="2"/>
@ -161,54 +159,49 @@
</div>
</form>
</field>
</record>
</record>
<!-- CRM Lead Tree View -->
<!-- CRM Lead Tree View -->
<record model="ir.ui.view" id="crm_case_tree_view_leads">
<field name="name">Leads</field>
<field name="model">crm.lead</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Leads" fonts="bold:needaction_pending==True" colors="blue:state=='pending';grey:state in ('cancel', 'done')">
<field name="needaction_pending" invisible="1"/>
<field name="date_deadline" invisible="1"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="name" string="Subject"/>
<field name="contact_name"/>
<field name="country_id" invisible="context.get('invisible_country', True)" />
<field name="email_from"/>
<field name="phone"/>
<field name="stage_id"/>
<field name="categ_id" invisible="1"/>
<field name="section_id" invisible="context.get('invisible_section', True)" />
<field name="state" groups="base.group_no_one"/>
<field name="type_id" invisible="1"/>
<field name="referred" invisible="1"/>
<field name="channel_id" invisible="1"/>
<field name="subjects" invisible="1"/>
</tree>
</field>
</record>
<record model="ir.ui.view" id="crm_case_tree_view_leads">
<field name="name">Leads</field>
<field name="model">crm.lead</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Leads" fonts="bold:needaction_pending==True" colors="blue:state=='pending';grey:state in ('cancel', 'done')">
<field name="needaction_pending" invisible="1"/>
<field name="date_deadline" invisible="1"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="name" string="Subject"/>
<field name="contact_name"/>
<field name="country_id" invisible="context.get('invisible_country', True)" />
<field name="email_from"/>
<field name="phone"/>
<field name="categ_id" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="referred" invisible="1"/>
<field name="channel_id" invisible="1"/>
<field name="subjects" invisible="1"/>
<field name="stage_id"/>
<field name="section_id" invisible="context.get('invisible_section', True)" />
<field name="user_id" />
<field name="state" />
</tree>
</field>
</record>
<!-- CRM Lead Calendar View -->
<record model="ir.ui.view" id="crm_case_calendar_view_leads">
<field name="name">CRM - Leads Calendar</field>
<field name="model">crm.lead</field>
<field name="type">calendar</field>
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<calendar string="Leads Generation"
date_start="date_action" color="user_id">
<field name="name" />
<field name="partner_name" />
</calendar>
</field>
</record>
<!-- CRM Lead Calendar View -->
<record model="ir.ui.view" id="crm_case_calendar_view_leads">
<field name="name">CRM - Leads Calendar</field>
<field name="model">crm.lead</field>
<field name="type">calendar</field>
<field name="priority" eval="2"/>
<field name="arch" type="xml">
<calendar string="Leads Generation"
date_start="date_action" color="user_id">
<field name="name" />
<field name="partner_name" />
</calendar>
</field>
</record>
<!-- CRM Lead Kanban View -->
<record model="ir.ui.view" id="crm_case_kanban_view_leads">
@ -217,7 +210,7 @@
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id">
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
<field name="color"/>
<field name="priority"/>
<field name="planned_revenue" sum="Expected Revenues"/>
@ -298,7 +291,6 @@
</record>
<!-- CRM Lead Search View -->
<record id="view_crm_case_leads_filter" model="ir.ui.view">
<field name="name">CRM - Leads Search</field>
<field name="model">crm.lead</field>
@ -373,6 +365,10 @@
</record>
<!--
OPPORTUNITY
-->
<!-- Opportunities Form View -->
<record model="ir.ui.view" id="crm_case_form_view_oppor">
<field name="name">Opportunities</field>
@ -382,15 +378,23 @@
<field name="arch" type="xml">
<form string="Opportunities" layout="manual">
<div class="oe_form_topbar oe_form_topbar_hifirst">
<button name="case_mark_won" string="Mark Won" states="open,pending" type="object" />
<button name="case_open" string="Open" states="draft,pending" type="object" />
<button name="case_pending" string="Pending" states="draft,open" type="object" />
<button name="case_escalate" string="Escalate" states="open,pending" type="object" />
<button name="case_mark_lost" string="Mark Lost" states="open,pending" type="object" />
<button name="case_reset" string="Reset to Draft" states="done,cancel" type="object" />
<button name="case_cancel" string="Cancel" states="draft" type="object" />
<button name="case_open" string="Open" type="object"
states="draft"/>
<button name="case_mark_won" string="Mark Won" type="object"
states="draft,open"/>
<button name="case_mark_lost" string="Mark Lost" type="object"
states="draft,open"/>
<button name="case_reset" string="Reset to Draft" type="object"
states="cancel"/>
<button name="case_escalate" string="Escalate" type="object"
states="open"/>
<button name="stage_previous" string="Previous" type="object"
states="open" icon="gtk-go-back" context="{'stage_type': 'opportunity'}"/>
<button name="stage_next" string="Next" type="object"
states="open" icon="gtk-go-forward" context="{'stage_type': 'opportunity'}"/>
<div class="oe_right">
<field name="state" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}'/>
<field name="stage_id" nolabel="1" widget="statusbar"
on_change="onchange_stage_id(stage_id)"/>
</div>
<div class="oe_clear"/>
</div>
@ -443,20 +447,8 @@
<field name="categ_id" select="1"
string="Category" widget="selection"
domain="[('object_id.model', '=', 'crm.lead')]" />
<label string="Stage" for="stage_id" align="1.0"/>
<group colspan="1" col="3">
<field name="stage_id" nolabel="1"
on_change="onchange_stage_id(stage_id)"
domain="section_id and [('section_ids', '=', section_id)] or []" width="60%%"/>
<button name="stage_previous"
states="draft,open,pending" type="object"
icon="gtk-go-back" string="" context="{'stage_type': 'opportunity'}"/>
<button name="stage_next" states="draft,open,pending"
type="object" icon="gtk-go-forward" string="" context="{'stage_type': 'opportunity'}"/>
</group>
<field name="priority" string="Priority"/>
<field name="state"/>
<field name="priority"/>
</group>
<separator colspan="4" string="Details"/>
@ -516,35 +508,35 @@
</field>
</record>
<!-- Opportunities Tree View -->
<record model="ir.ui.view" id="crm_case_tree_view_oppor">
<field name="name">Opportunities Tree</field>
<field name="model">crm.lead</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Opportunities" fonts="bold:needaction_pending==True" colors="blue:state=='pending' and not(date_deadline and (date_deadline &lt; current_date));gray:state in ('cancel', 'done');red:date_deadline and (date_deadline &lt; current_date)">
<field name="needaction_pending" invisible="1"/>
<field name="date_deadline" invisible="1"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="name" string="Opportunity"/>
<field name="partner_id" string="Customer"/>
<field name="country_id" invisible="context.get('invisible_country', True)" />
<field name="date_action"/>
<field name="title_action" />
<field name="channel_id" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="subjects" invisible="1"/>
<field name="stage_id"/>
<field name="planned_revenue" sum="Expected Revenues"/>
<field name="probability" widget="progressbar" avg="Avg. of Probability"/>
<field name="section_id" invisible="context.get('invisible_section', True)" />
<field name="user_id"/>
<field name="priority" invisible="1"/>
<field name="categ_id" invisible="1"/>
<field name="state"/>
</tree>
</field>
</record>
<!-- Opportunities Tree View -->
<record model="ir.ui.view" id="crm_case_tree_view_oppor">
<field name="name">Opportunities Tree</field>
<field name="model">crm.lead</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Opportunities" fonts="bold:needaction_pending==True" colors="blue:state=='pending' and not(date_deadline and (date_deadline &lt; current_date));gray:state in ('cancel', 'done');red:date_deadline and (date_deadline &lt; current_date)">
<field name="needaction_pending" invisible="1"/>
<field name="date_deadline" invisible="1"/>
<field name="create_date" groups="base.group_no_one"/>
<field name="name" string="Opportunity"/>
<field name="partner_id" string="Customer"/>
<field name="country_id" invisible="context.get('invisible_country', True)" />
<field name="date_action"/>
<field name="title_action" />
<field name="channel_id" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="subjects" invisible="1"/>
<field name="stage_id"/>
<field name="planned_revenue" sum="Expected Revenues"/>
<field name="probability" widget="progressbar" avg="Avg. of Probability"/>
<field name="section_id" invisible="context.get('invisible_section', True)" />
<field name="user_id"/>
<field name="priority" invisible="1"/>
<field name="categ_id" invisible="1"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>
<!-- Opportunities Search View -->
@ -575,8 +567,7 @@
help="Unassigned Opportunities" />
</field>
<field name="section_id"
context="{'invisible_section': False}"
widget="selection">
context="{'invisible_section': False, 'default_section_id': self}">
<filter icon="terp-personal+"
domain="['|', ('section_id.user_id','=',uid), ('section_id.member_ids', 'in', [uid])]"
context="{'invisible_section': False}"
@ -606,19 +597,19 @@
</field>
</record>
<!-- crm.lead Opportunities Graph View -->
<record model="ir.ui.view" id="crm_case_graph_view_opportunity">
<field name="name">CRM - Opportunity Graph</field>
<field name="model">crm.lead</field>
<field name="type">graph</field>
<field name="arch" type="xml">
<graph string="Opportunity by Categories" type="bar" orientation="horizontal">
<field name="categ_id"/>
<field name="planned_revenue" operator="+"/>
<field name="state" group="True"/>
</graph>
</field>
</record>
<!-- crm.lead Opportunities Graph View -->
<record model="ir.ui.view" id="crm_case_graph_view_opportunity">
<field name="name">CRM - Opportunity Graph</field>
<field name="model">crm.lead</field>
<field name="type">graph</field>
<field name="arch" type="xml">
<graph string="Opportunity by Categories" type="bar" orientation="horizontal">
<field name="categ_id"/>
<field name="planned_revenue" operator="+"/>
<field name="state" group="True"/>
</graph>
</field>
</record>
</data>
</data>
</openerp>

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -20,43 +20,35 @@
##############################################################################
from base_calendar import base_calendar
from crm import crm_base, crm_case
from base_status.base_state import base_state
from base_status.base_stage import base_stage
import logging
from osv import fields, osv
import tools
from tools.translate import _
import logging
class crm_lead(crm_case, osv.osv):
""" CRM Leads """
_name = 'crm.lead'
crm_lead()
class crm_phonecall(crm_case, osv.osv):
""" CRM Phonecall """
_name = 'crm.phonecall'
crm_phonecall()
class crm_meeting(crm_base, osv.osv):
""" CRM Meeting Cases """
class crm_lead(base_stage, osv.osv):
""" CRM Leads """
_name = 'crm.lead'
class crm_meeting(base_state, osv.Model):
""" Model for CRM meetings """
_name = 'crm.meeting'
_description = "Meeting"
_order = "id desc"
_inherit = ["calendar.event", 'ir.needaction_mixin', "mail.thread"]
_columns = {
# From crm.case
'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}),
# base_state required fields
'partner_id': fields.many2one('res.partner', 'Partner', states={'done': [('readonly', True)]}),
'section_id': fields.many2one('crm.case.section', 'Sales Team', states={'done': [('readonly', True)]}, \
select=True, help='Sales team to which Case belongs to.'),
'email_from': fields.char('Email', size=128, states={'done': [('readonly', True)]}, help="These people will receive email."),
'id': fields.integer('ID', readonly=True),
'create_date': fields.datetime('Creation Date' , readonly=True),
'write_date': fields.datetime('Write Date' , readonly=True),
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
# Meeting fields
'name': fields.char('Summary', size=124, required=True, states={'done': [('readonly', True)]}),
'categ_id': fields.many2one('crm.case.categ', 'Meeting Type', \
domain="[('object_id.model', '=', 'crm.meeting')]", \
),
@ -67,11 +59,11 @@ class crm_meeting(crm_base, osv.osv):
'date_closed': fields.datetime('Closed', readonly=True),
'date_deadline': fields.datetime('Deadline', states={'done': [('readonly', True)]}),
'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
'state': fields.selection([('open', 'Confirmed'),
('draft', 'Unconfirmed'),
'state': fields.selection([ ('draft', 'Unconfirmed'),
('open', 'Confirmed'),
('cancel', 'Cancelled'),
('done', 'Done')], 'Status', \
size=16, readonly=True),
('done', 'Done')],
string='Status', size=16, readonly=True),
}
_defaults = {
'state': 'draft',
@ -91,6 +83,17 @@ class crm_meeting(crm_base, osv.osv):
result[obj.id] = [obj.user_id.id]
return result
def case_open(self, cr, uid, ids, context=None):
""" Confirms meeting """
res = super(crm_meeting, self).case_open(cr, uid, ids, context)
for (id, name) in self.name_get(cr, uid, ids):
id=base_calendar.base_calendar_id2real_id(id)
return res
# ----------------------------------------
# OpenChatter
# ----------------------------------------
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Meeting'
@ -119,32 +122,12 @@ class crm_meeting(crm_base, osv.osv):
meeting.message_append_note(body=parent_message)
return True
def case_close_send_note(self, cr, uid, ids, context=None):
message = _("Meeting has been <b>done</b>.")
return self.message_append_note(cr, uid, ids, body=message, context=context)
def case_open_send_note(self, cr, uid, ids, context=None):
for meeting in self.browse(cr, uid, ids, context=context):
if meeting.state != 'draft':
return False
message = _("Meeting has been <b>confirmed</b>.")
meeting.message_append_note(body=message)
return True
return self.message_append_note(cr, uid, ids, body=_("Meeting has been <b>confirmed</b>."), context=context)
def case_open(self, cr, uid, ids, context=None):
"""Confirms meeting
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of Meeting Ids
@param *args: Tuple Value for additional Params
"""
res = super(crm_meeting, self).case_open(cr, uid, ids, context)
for (id, name) in self.name_get(cr, uid, ids):
id=base_calendar.base_calendar_id2real_id(id)
return res
def case_close_send_note(self, cr, uid, ids, context=None):
return self.message_append_note(cr, uid, ids, body=_("Meeting has been <b>done</b>."), context=context)
crm_meeting()
class calendar_attendee(osv.osv):
""" Calendar Attendee """

View File

@ -25,7 +25,22 @@
<field name="model">crm.meeting</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Meetings">
<form string="Meetings" layout="manual">
<div class="oe_form_topbar oe_form_topbar_hifirst">
<button name="case_open" string="Confirm" type="object"
states="draft"/>
<button name="case_close" string="Done" type="object"
states="open"/>
<button name="case_reset" string="Reset to Unconfirmed" type="object"
states="cancel,done"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open"/>
<div class="oe_right">
<field name="state" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done"/>
</div>
<div class="oe_clear"/>
</div>
<sheet layout="auto">
<group col="6" colspan="4">
<field name="name" select="1" string="Title"
required="1" />
@ -71,19 +86,6 @@
</group>
<separator string="Description" colspan="4" />
<field name="description" nolabel="1" colspan="4" />
<separator colspan="4"/>
<group col="8" colspan="4" groups="base.group_no_one">
<field name="state" />
<button name="case_close" string="Done"
states="open" type="object"
icon="gtk-jump-to" />
<button name="case_reset" string="Reset to Unconfirmed"
states="open,done" type="object"
icon="gtk-convert" />
<button name="case_open" string="Confirm"
states="draft" type="object"
icon="gtk-go-forward" />
</group>
</page>
<page string="Invitation Detail">
<button string="Invite People"
@ -209,7 +211,10 @@
</page>
</notebook>
<field name="message_ids_social" colspan="4" widget="ThreadView" nolabel="1"/>
</sheet>
<div class="oe_form_bottom">
<field name="message_ids_social" colspan="4" widget="ThreadView" nolabel="1"/>
</div>
</form>
</field>
</record>

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,26 +19,22 @@
#
##############################################################################
from crm import crm_base
from osv import fields, osv
from tools.translate import _
from base_status.base_state import base_state
import crm
from datetime import datetime
from osv import fields, osv
import time
from tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT, DATETIME_FORMATS_MAP
from datetime import datetime
class crm_phonecall(crm_base, osv.osv):
""" Phonecall Cases """
from tools.translate import _
class crm_phonecall(base_state, osv.osv):
""" Model for CRM phonecalls """
_name = "crm.phonecall"
_description = "Phonecall"
_order = "id desc"
_inherit = ['ir.needaction_mixin', 'mail.thread']
_columns = {
# From crm.case
'id': fields.integer('ID', readonly=True),
'name': fields.char('Call Summary', size=64, required=True),
'active': fields.boolean('Active', required=False),
# base_state required fields
'date_action_last': fields.datetime('Last Action', readonly=1),
'date_action_next': fields.datetime('Next Action', readonly=1),
'create_date': fields.datetime('Creation Date' , readonly=True),
@ -48,20 +44,21 @@ class crm_phonecall(crm_base, osv.osv):
'partner_id': fields.many2one('res.partner', 'Partner'),
'company_id': fields.many2one('res.company', 'Company'),
'description': fields.text('Description'),
'state': fields.selection([
('draft', 'Draft'),
('open', 'Todo'),
'state': fields.selection([ ('draft', 'Draft'),
('open', 'Confirmed'),
('pending', 'Not Held'),
('cancel', 'Cancelled'),
('done', 'Held'),
], 'Status', size=16, readonly=True,
help='The state is set to \'Todo\', when a case is created.\
\nIf the case is in progress the state is set to \'Open\'.\
\nWhen the call is over, the state is set to \'Held\'.\
\nIf the call needs to be done then the state is set to \'Not Held\'.'),
('done', 'Held'),],
string='Status', size=16, readonly=True,
help='The state is set to \'Todo\', when a case is created.\
If the case is in progress the state is set to \'Open\'.\
When the call is over, the state is set to \'Held\'.\
If the call needs to be done then the state is set to \'Not Held\'.'),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'date_open': fields.datetime('Opened', readonly=True),
# phonecall fields
'name': fields.char('Call Summary', size=64, required=True),
'active': fields.boolean('Active', required=False),
'duration': fields.float('Duration', help="Duration in Minutes"),
'categ_id': fields.many2one('crm.case.categ', 'Category', \
domain="['|',('section_id','=',section_id),('section_id','=',False),\
@ -81,11 +78,10 @@ class crm_phonecall(crm_base, osv.osv):
return 'open'
_defaults = {
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'date': fields.datetime.now,
'priority': crm.AVAILABLE_PRIORITIES[2][0],
'state': _get_default_state,
'user_id': lambda self,cr,uid,ctx: uid,
'active': 1,
}
def create(self, cr, uid, vals, context=None):
@ -96,32 +92,23 @@ class crm_phonecall(crm_base, osv.osv):
return obj_id
def case_close(self, cr, uid, ids, context=None):
"""Overrides close for crm_case for setting close date
"""
""" Overrides close for crm_case for setting duration """
res = True
for phone in self.browse(cr, uid, ids):
for phone in self.browse(cr, uid, ids, context=context):
phone_id = phone.id
data = {'date_closed': time.strftime('%Y-%m-%d %H:%M:%S')}
data = {}
if phone.duration <=0:
duration = datetime.now() - datetime.strptime(phone.date, '%Y-%m-%d %H:%M:%S')
data.update({'duration': duration.seconds/float(60)})
res = super(crm_phonecall, self).case_close(cr, uid, [phone_id], context)
self.write(cr, uid, [phone_id], data)
duration = datetime.now() - datetime.strptime(phone.date, DEFAULT_SERVER_DATETIME_FORMAT)
data['duration'] = duration.seconds/float(60)
res = super(crm_phonecall, self).case_close(cr, uid, [phone_id], context=context)
self.write(cr, uid, [phone_id], data, context=context)
return res
def case_reset(self, cr, uid, ids, context=None):
"""Resets case as Todo
"""
res = super(crm_phonecall, self).case_reset(cr, uid, ids, context)
self.write(cr, uid, ids, {'duration': 0.0, 'state':'open'})
return res
def case_open(self, cr, uid, ids, context=None):
"""Overrides cancel for crm_case for setting Open Date
"""
res = super(crm_phonecall, self).case_open(cr, uid, ids, context)
self.write(cr, uid, ids, {'date_open': time.strftime('%Y-%m-%d %H:%M:%S')})
self.write(cr, uid, ids, {'duration': 0.0, 'state':'open'}, context=context)
return res
def schedule_another_phonecall(self, cr, uid, ids, schedule_time, call_summary, \
@ -300,7 +287,7 @@ class crm_phonecall(crm_base, osv.osv):
return value
# ----------------------------------------
# OpenChatter methods and notifications
# OpenChatter
# ----------------------------------------
def get_needaction_user_ids(self, cr, uid, ids, context=None):
@ -336,9 +323,6 @@ class crm_phonecall(crm_base, osv.osv):
def _call_set_partner_send_note(self, cr, uid, ids, context=None):
return self.message_append_note(cr, uid, ids, body=_("Partner has been <b>created</b>"), context=context)
crm_phonecall()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -48,13 +48,16 @@
<field name="arch" type="xml">
<form layout="manual">
<div class="oe_form_topbar">
<button name="case_close" string="Held" states="open,pending" type="object" />
<button name="case_open" string="Todo" states="pending" type="object" />
<button name="case_pending" string="Not Held" states="open" type="object" />
<button name="case_reset" string="Reset to Todo" states="cancel" type="object" />
<button name="case_cancel" string="Cancel" states="open,pending" type="object" />
<button name="case_open" string="Confirm" type="object"
states="draft,pending" icon="gtk-go-forward"/>
<button name="case_close" string="Held" type="object"
states="open,pending" icon="gtk-jump-to"/>
<button name="case_reset" string="Reset to Todo" type="object"
states="cancel" icon="gtk-convert"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending" icon="gtk-cancel"/>
<div class="oe_right">
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="open,done" statusbar_colors='{"pending":"red"}' select="1"/>
<field name="state" widget="statusbar" nolabel="1" statusbar_visible="draft,open,done" select="1"/>
</div>
<div class="oe_clear"/>
</div>

View File

@ -143,6 +143,8 @@
<field name="sequence"/>
<field name="name"/>
<field name="probability"/>
<field name="state"/>
<field name="type"/>
</tree>
</field>
</record>
@ -162,6 +164,9 @@
<field name="on_change"/>
<field name="case_default"/>
<field name="sequence"/>
<field name="state"/>
<field name="fold"/>
<field name="type"/>
</group>
<separator string="Requirements" colspan="4"/>
<field name="requirements" nolabel="1" colspan="4"/>

View File

@ -6,8 +6,11 @@
-
I check cancelled lead.
-
!assert {model: crm.lead, id: crm.crm_case_itisatelesalescampaign0, string: Lead is in cancel state}:
- state == "cancel"
!python {model: crm.lead}: |
lead = self.browse(cr, uid, ref('crm_case_itisatelesalescampaign0'))
assert lead.stage_id.id == ref('crm.stage_lead7'), "Stage should be 'Dead' and is %s." % (lead.stage_id.name)
assert lead.state == 'cancel', "Opportunity is not in 'cancel' state."
assert lead.probability == 0.0, 'Opportunity probability is wrong and should be 0.0.'
-
I reset cancelled lead into unqualified lead.
-
@ -19,35 +22,24 @@
!assert {model: crm.lead, id: crm.crm_case_itisatelesalescampaign0, string: Lead is in draft state}:
- state == "draft"
-
I put unqualified lead into pending.
I re-open the lead
-
!python {model: crm.lead}: |
self.case_pending(cr, uid, [ref("crm_case_itisatelesalescampaign0")])
self.case_open(cr, uid, [ref("crm_case_itisatelesalescampaign0")])
-
I check status of pending lead.
I check stage and state of the re-opened lead
-
!assert {model: crm.lead, id: crm.crm_case_itisatelesalescampaign0, string: Lead is in pending state}:
- state == "pending"
!python {model: crm.lead}: |
lead = self.browse(cr, uid, ref('crm.crm_case_itisatelesalescampaign0'))
assert lead.stage_id.id == ref('crm.stage_lead2'), "Opportunity stage should be 'Qualification'."
assert lead.state == 'open', "Opportunity should be in 'open' state."
-
I escalate the lead to parent team.
-
!python {model: crm.lead}: |
self.case_escalate(cr, uid, [ref("crm_case_itisatelesalescampaign0")])
-
I check lead escalate to parent team.
I check the lead is correctly escalated to the parent team.
-
!assert {model: crm.lead, id: crm.crm_case_itisatelesalescampaign0, string: Escalate lead to parent team}:
- section_id.name == "Sales Department"
-
I mark as lost the opportunity.
-
!python {model: crm.lead}: |
self.case_mark_lost(cr, uid, [ref("crm_case_itisatelesalescampaign0")])
-
I check opportunity after lost.
-
!python {model: crm.lead}: |
lead = self.browse(cr, uid, ref('crm_case_itisatelesalescampaign0'))
assert lead.state == 'done', "lead is not done state"
assert lead.stage_id.id == ref('crm.stage_lead6'), 'Stage is not changed!'
assert lead.probability == 0.0, 'Probability is wrong!'

View File

@ -51,7 +51,6 @@
-
!python {model: crm.lead}: |
self.action_makeMeeting(cr, uid, [ref('crm_case_qrecorp0')])
-
After communicated with customer, I put some notes with contract details.
-
@ -60,18 +59,18 @@
note_id = self.create(cr, uid, {'body': "ces détails envoyés par le client sur le FAX pour la qualité"})
self.action_add(cr, uid, [note_id], context=context)
-
Finally, I won this opportunity, so I close this opportunity.
I win this opportunity
-
!python {model: crm.lead}: |
self.case_mark_won(cr, uid, [ref("crm_case_qrecorp0")])
-
I check details of the opportunity after won the opportunity.
I check details of the opportunity after having won the opportunity.
-
!python {model: crm.lead}: |
lead = self.browse(cr, uid, ref('crm_case_qrecorp0'))
assert lead.state == 'done', 'Opportunity is not in done state!'
assert lead.stage_id.name == 'Won', ' Stage of Opportunity is not win!'
assert lead.probability == 100.0, 'probability revenue should not be 100.0!'
assert lead.stage_id.id == ref('crm.stage_lead6'), "Opportunity stage should be 'Won'."
assert lead.state == 'done', "Opportunity is not in 'done' state!"
assert lead.probability == 100.0, "Revenue probability should be 100.0!"
-
I convert mass lead into opportunity customer.
-
@ -96,6 +95,19 @@
assert opp.name == "Interest in Your New Product", "Opportunity name not correct"
assert opp.type == 'opportunity', 'Lead is not converted to opportunity!'
assert opp.stage_id.id == ref("stage_lead1"), 'Stage of probability is incorrect!'
-
I loose the second opportunity
-
!python {model: crm.lead}: |
self.case_mark_lost(cr, uid, [ref("crm_case_electonicgoodsdealer0")])
-
I check details of the opportunity after the loose
-
!python {model: crm.lead}: |
lead = self.browse(cr, uid, ref('crm_case_electonicgoodsdealer0'))
assert lead.stage_id.id == ref('crm.stage_lead8'), "Opportunity stage should be 'Lost'."
assert lead.state == 'cancel', "Lost opportunity is not in 'cancel' state!"
assert lead.probability == 0.0, "Revenue probability should be 0.0!"
-
I confirm review needs meeting.
-

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,25 +19,57 @@
#
##############################################################################
from osv import fields, osv
from crm import crm
import time
from crm import wizard
from base_status.base_stage import base_stage
import binascii
from crm import crm
from crm import wizard
from osv import fields, osv
import time
import tools
from tools.translate import _
wizard.mail_compose_message.SUPPORTED_MODELS.append('crm.claim')
CRM_CLAIM_PENDING_STATES = (
crm.AVAILABLE_STATES[2][0], # Cancelled
crm.AVAILABLE_STATES[3][0], # Done
crm.AVAILABLE_STATES[4][0], # Pending
)
class crm_claim(crm.crm_case, osv.osv):
class crm_claim_stage(osv.osv):
""" Model for claim stages. This models the main stages of a claim
management flow. Main CRM objects (leads, opportunities, project
issues, ...) will now use only stages, instead of state and stages.
Stages are for example used to display the kanban view of records.
"""
Crm claim
_name = "crm.claim.stage"
_description = "Claim stages"
_rec_name = 'name'
_order = "sequence"
_columns = {
'name': fields.char('Stage Name', size=64, required=True, translate=True),
'sequence': fields.integer('Sequence', help="Used to order stages. Lower is better."),
'section_ids':fields.many2many('crm.case.section', 'section_claim_stage_rel', 'stage_id', 'section_id', string='Sections',
help="Link between stages and sales teams. When set, this limitate the current stage to the selected sales teams."),
'state': fields.selection(crm.AVAILABLE_STATES, 'State', required=True, help="The related state for the stage. The state of your document will automatically change regarding the selected stage. For example, if a stage is related to the state 'Close', when your document reaches this stage, it will be automatically have the 'closed' state."),
'case_refused': fields.boolean('Refused stage',
help='Refused stages are specific stages for done.'),
'case_default': fields.boolean('Common to All Teams',
help="If you check this field, this stage will be proposed by default on each sales team. It will not assign this stage to existing teams."),
'fold': fields.boolean('Hide in Views when Empty',
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 = {
'sequence': lambda *args: 1,
'state': 'draft',
'fold': False,
'case_refused': False,
}
class crm_claim(base_stage, osv.osv):
""" Crm claim
"""
_name = "crm.claim"
_description = "Claim"
@ -73,31 +105,68 @@ class crm_claim(crm.crm_case, osv.osv):
'email_cc': fields.text('Watchers Emails', size=252, 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"),
'email_from': fields.char('Email', size=128, help="These people will receive email."),
'partner_phone': fields.char('Phone', size=32),
'stage_id': fields.many2one ('crm.case.stage', 'Stage', domain="[('section_ids','=',section_id)]"),
'stage_id': fields.many2one ('crm.claim.stage', 'Stage',
domain="['|', ('section_ids', '=', section_id), ('case_default', '=', True)]"),
'cause': fields.text('Root Cause'),
'state': fields.selection(crm.AVAILABLE_STATES, 'Status', 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\'.'),
'state': fields.related('stage_id', 'state', type="selection", store=True,
selection=crm.AVAILABLE_STATES, string="State", readonly=True,
help='The state is set to \'Draft\', when a case is created.\
If the case is in progress the state is set to \'Open\'.\
When the case is over, the state is set to \'Done\'.\
If the case needs to be reviewed then the state is \
set to \'Pending\'.'),
'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
}
_defaults = {
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'email_from':crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'section_id':crm.crm_case. _get_section,
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c),
'partner_id': lambda s, cr, uid, c: s._get_default_partner(cr, uid, c),
'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
'date': fields.datetime.now,
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
'active': lambda *a: 1
}
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Claim'
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:
- section_id: if set, stages must belong to this section or
be a default case
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
# collect all section_ids
section_ids = []
if section_id:
section_ids.append(section_id)
for claim in cases:
if claim.section_id:
section_ids.append(claim.section_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(('section_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('crm.claim.stage').search(cr, uid, search_domain, order=order, context=context)
if stage_ids:
return stage_ids[0]
return False
def case_refuse(self, cr, uid, ids, context=None):
""" Mark the case as refused: state=done and case_refused=True """
for lead in self.browse(cr, uid, ids):
stage_id = self.stage_find(cr, uid, [lead], lead.section_id.id or False, ['&', ('state', '=', 'done'), ('case_refused', '=', True)], context=context)
if stage_id:
self.case_set(cr, uid, [lead.id], values_to_update={}, new_stage_id=stage_id, context=context)
return self.case_refuse_send_note(cr, uid, ids, context=context)
def onchange_partner_id(self, cr, uid, ids, part, email=False):
"""This function returns value of partner address based on partner
:param part: Partner's id
@ -110,19 +179,6 @@ class crm_claim(crm.crm_case, osv.osv):
}
address = self.pool.get('res.partner').browse(cr, uid, part)
return {'value': {'email_from': address.email, 'partner_phone': address.phone}}
def case_open(self, cr, uid, ids, *args):
"""Opens Claim"""
for l in self.browse(cr, uid, ids):
# When coming from draft override date and stage otherwise just set state
if l.state == 'draft':
message = _("The claim '%s' has been opened.") % l.name
self.log(cr, uid, l.id, message)
stage_id = self.stage_find(cr, uid, l.section_id.id or False, [('sequence','>',0)])
if stage_id:
self.stage_set(cr, uid, [l.id], stage_id)
res = super(crm_claim, self).case_open(cr, uid, ids, *args)
return res
def message_new(self, cr, uid, msg, custom_values=None, context=None):
"""Automatically called when new email message arrives"""
@ -177,6 +233,26 @@ class crm_claim(crm.crm_case, osv.osv):
res = self.write(cr, uid, [case.id], values, context=context)
return res
# ---------------------------------------------------
# OpenChatter methods and notifications
# ---------------------------------------------------
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
""" Override of default prefix for notifications. """
return 'Claim'
def case_refuse_send_note(self, cr, uid, ids, context=None):
for id in ids:
msg = _('%s has been <b>refused</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 stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """
stage_name = self.pool.get('crm.claim.stage').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)
class res_partner(osv.osv):
_inherit = 'res.partner'
_columns = {

View File

@ -42,27 +42,39 @@
Case Stage
-->
<record model="crm.case.stage" id="stage_claim1">
<field name="name">Accepted as Claim</field>
<record model="crm.claim.stage" id="stage_claim1">
<field name="name">Draft claim</field>
<field name="state">draft</field>
<field name="sequence">26</field>
<field name="case_default" eval="True"/>
</record>
<record model="crm.case.stage" id="stage_claim5">
<record model="crm.claim.stage" id="stage_claim5">
<field name="name">Actions Defined</field>
<field name="state">open</field>
<field name="sequence">27</field>
<field name="case_default" eval="True"/>
</record>
<record model="crm.case.stage" id="stage_claim2">
<record model="crm.claim.stage" id="stage_claim2">
<field name="name">Actions Done</field>
<field name="state">done</field>
<field name="sequence">28</field>
<field name="case_default" eval="True"/>
</record>
<record model="crm.case.stage" id="stage_claim3">
<field name="name">Won't fix</field>
<record model="crm.claim.stage" id="stage_claim3">
<field name="name">Refused</field>
<field name="state">done</field>
<field name="sequence">29</field>
<field name="case_default" eval="True"/>
<field name="case_refused" eval="True"/>
<field name="fold" eval="True"/>
</record>
<record model="crm.claim.stage" id="stage_claim3">
<field name="name">Cancelled</field>
<field name="state">cancel</field>
<field name="sequence">30</field>
<field name="case_default" eval="True"/>
<field name="fold" eval="True"/>
</record>
<record model="crm.case.section" id="crm.section_sales_department">
<field name="name">Sales Department</field>
<field name="code">Sales</field>
<field name="stage_ids" eval="[(4, ref('stage_claim1')), (4, ref('stage_claim2')), (4, ref('stage_claim3')), (4, ref('stage_claim5'))]"/>
</record>
</data>
</openerp>

View File

@ -12,7 +12,6 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;Problem with the delivery of goods&quot;" name="name"/>
<field eval="&quot;open&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm_claim.categ_claim1"/>
<field name="stage_id" ref="crm_claim.stage_claim1"/>
@ -25,7 +24,6 @@
<field eval="&quot;4&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;Damaged Products&quot;" name="name"/>
<field eval="&quot;open&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm_claim.categ_claim2"/>
<field name="stage_id" ref="crm_claim.stage_claim5"/>
@ -38,7 +36,6 @@
<field eval="&quot;2&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="&quot;Document related problems&quot;" name="name"/>
<field eval="&quot;done&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm_claim.categ_claim3"/>
<field name="stage_id" ref="crm_claim.stage_claim2"/>
@ -52,7 +49,6 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;Product quality not maintained&quot;" name="name"/>
<field eval="&quot;draft&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm_claim.categ_claim1"/>
<field name="stage_id" ref="crm_claim.stage_claim5"/>
@ -65,7 +61,6 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;Some products missing&quot;" name="name"/>
<field eval="&quot;pending&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field name="categ_id" ref="crm_claim.categ_claim3"/>
<field name="stage_id" ref="crm_claim.stage_claim3"/>
@ -77,7 +72,6 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;Problem with the delivery of assignments&quot;" name="name"/>
<field eval="&quot;cancel&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-28 14:15:30')" name="date"/>
<field name="categ_id" ref="crm_claim.categ_claim1"/>
@ -91,7 +85,6 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;Documents unclear&quot;" name="name"/>
<field eval="&quot;done&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-19 13:01:05')" name="date"/>
<field name="categ_id" ref="crm_claim.categ_claim3"/>

View File

@ -50,5 +50,10 @@
<menuitem name="Claims" id="menu_crm_case_claims"
parent="base.menu_aftersale" action="crm_case_categ_claim0" sequence="1"/>
<!-- Claim Stages -->
<menuitem id="menu_definitions" name="Configuration" parent="base.menu_main_pm" sequence="60"/>
<menuitem id="menu_project_config_project" name="Stages" parent="menu_definitions" sequence="1"/>
<menuitem id="menu_claim_stage_view" name="Claim Stages" action="crm_claim_stage_act" parent="menu_project_config_project" sequence="20"/>
</data>
</openerp>

View File

@ -23,12 +23,40 @@
<!-- Claim Stages -->
<record id="crm_claim_stage_tree" model="ir.ui.view">
<field name="name">crm.claim.stage.tree</field>
<field name="model">crm.claim.stage</field>
<field name="type">tree</field>
<field name="arch" type="xml">
<tree string="Claim Stages">
<field name="sequence"/>
<field name="name"/>
<field name="state"/>
</tree>
</field>
</record>
<record id="crm_claim_stage_form" model="ir.ui.view">
<field name="name">crm.claim.stage.form</field>
<field name="model">crm.claim.stage</field>
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Claim Stage">
<field name="name"/>
<field name="case_default"/>
<field name="sequence"/>
<field name="state"/>
<field name="case_refused"/>
<field name="fold"/>
</form>
</field>
</record>
<record id="crm_claim_stage_act" model="ir.actions.act_window">
<field name="name">Claim Stages</field>
<field name="res_model">crm.case.stage</field>
<field name="res_model">crm.claim.stage</field>
<field name="view_type">form</field>
<field name="view_id" ref="crm.crm_case_stage_tree"/>
<field name="context">{'search_default_claim':1}</field>
<field name="view_id" ref="crm_claim_stage_tree"/>
<field name="help">You can create claim stages to categorize the status of every claim entered in the system. The stages define all the steps required for the resolution of a claim.</field>
</record>
@ -44,13 +72,13 @@
<field name="partner_id"/>
<field name="user_id" />
<field name="date"/>
<field name="stage_id"/>
<field name="date_action_next"/>
<field name="action_next"/>
<field name="categ_id" string="Type" select="1"/>
<field name="stage_id" invisible="1"/>
<field name="date_deadline" invisible="1"/>
<field name="date_closed" invisible="1"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>
@ -62,13 +90,23 @@
<field name="arch" type="xml">
<form layout="manual">
<div class="oe_form_topbar">
<button name="case_close" string="Done" states="open,pending" type="object" />
<button name="case_open" string="Open" states="draft,pending" type="object" />
<button name="case_pending" string="Pending" states="draft,open" type="object" />
<button name="case_reset" string="Reset to Draft" states="done,cancel" type="object" />
<button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" />
<button name="case_open" string="Open" type="object"
states="draft,pending"/>
<button name="case_close" string="Done" type="object"
states="open,pending"/>
<button name="case_refuse" string="Refuse" type="object"
states="draft,open,pending"/>
<button name="stage_previous" string="Previous Stage" type="object"
states="open,pending" icon="gtk-go-back" attrs="{'invisible': [('stage_id','=', False)]}"/>
<button name="stage_next" string="Next Stage" type="object"
states="open,pending" icon="gtk-go-forward" attrs="{'invisible': [('stage_id','=', False)]}"/>
<button name="case_reset" string="Reset to Draft" type="object"
states="cancel,done"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending"/>
<div class="oe_right">
<field name="state" select="1" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}'/>
<field name="stage_id" nolabel="1" widget="statusbar"
on_change="onchange_stage_id(stage_id)"/>
</div>
<div class="oe_clear"/>
</div>
@ -80,11 +118,7 @@
<group colspan="4" col="6">
<field name="user_id"/>
<field name="section_id" widget="selection" />
<group colspan="2" col="4">
<field name="stage_id" domain="[('section_ids','=',section_id)]"/>
<button name="stage_previous" string="" type="object" icon="gtk-go-back" />
<button name="stage_next" string="" type="object" icon="gtk-go-forward" />
</group>
<field name="state" groups="base.group_no_one"/>
<newline />
<field name="priority"/>
<field name="date_deadline"/>
@ -221,7 +255,7 @@
domain="[]" context="{'group_by':'categ_id'}" />
<filter string="Status"
icon="terp-stock_effects-object-colorize"
domain="[]" context="{'group_by':'state'}" />
domain="[]" context="{'group_by':'state'}" groups="base.group_no_one"/>
<separator orientation="vertical"/>
<filter string="Claim Date" icon="terp-go-month"
domain="[]" help="Claim Date"

View File

@ -1,5 +1,6 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
access_crm_claim_manager,crm.claim.manager,model_crm_claim,base.group_sale_manager,1,1,1,1
access_crm_claim_user,crm.claim.user,model_crm_claim,base.group_sale_salesman,1,1,1,0
access_crm_claim_stage_user,crm.claim.stage.user,model_crm_claim_stage,base.group_sale_salesman,1,1,1,1
access_crm_claim_report_manager,crm.claim.report.manager,model_crm_claim_report,base.group_sale_manager,1,1,1,1
access_crm_claim_partner_manager,crm.claim.partner.manager,model_crm_claim,base.group_partner_manager,1,0,0,0

1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 access_crm_claim_manager crm.claim.manager model_crm_claim base.group_sale_manager 1 1 1 1
3 access_crm_claim_user crm.claim.user model_crm_claim base.group_sale_salesman 1 1 1 0
4 access_crm_claim_stage_user crm.claim.stage.user model_crm_claim_stage base.group_sale_salesman 1 1 1 1
5 access_crm_claim_report_manager crm.claim.report.manager model_crm_claim_report base.group_sale_manager 1 1 1 1
6 access_crm_claim_partner_manager crm.claim.partner.manager model_crm_claim base.group_partner_manager 1 0 0 0

View File

@ -30,7 +30,7 @@
claim_ids = self.search(cr, uid, [('email_from','=', 'Mr. John Right <info@customer.com>')])
claim = self.browse(cr, uid, claim_ids[0])
assert claim.state == "open", "Claim is not in Open state"
assert claim.stage_id.id == ref("crm.stage_lead1"), "Claim is not in New stage"
assert claim.stage_id.id == ref("crm.stage_lead2"), "Claim is not in Qualification stage"
-
After complete all service from our side, I close this claim.
-

View File

@ -19,13 +19,14 @@
#
##############################################################################
from osv import fields, osv
from base_status.base_stage import base_stage
from crm import crm
from crm import wizard
from osv import fields, osv
wizard.mail_compose_message.SUPPORTED_MODELS.append('crm.fundraising')
class crm_fundraising(crm.crm_case, osv.osv):
class crm_fundraising(base_stage, osv.osv):
""" Fund Raising Cases """
_name = "crm.fundraising"
@ -68,14 +69,58 @@ class crm_fundraising(crm.crm_case, osv.osv):
'duration': fields.float('Duration'),
'ref': fields.reference('Reference', selection=crm._links_get, size=128),
'ref2': fields.reference('Reference 2', selection=crm._links_get, size=128),
'state': fields.selection(crm.AVAILABLE_STATES, 'Status', 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\'.'),
'state': fields.related('stage_id', 'state', type="selection", store=True,
selection=crm.AVAILABLE_STATES, string="State", readonly=True,
help='The state is set to \'Draft\', when a case is created.\
If the case is in progress the state is set to \'Open\'.\
When the case is over, the state is set to \'Done\'.\
If the case needs to be reviewed then the state is \
set to \'Pending\'.'),
'message_ids': fields.one2many('mail.message', 'res_id', 'Messages', domain=[('model','=',_name)]),
}
_defaults = {
'active': 1,
'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c),
'partner_id': lambda s, cr, uid, c: s._get_default_partner(cr, uid, c),
'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.case', context=c),
'priority': crm.AVAILABLE_PRIORITIES[2][0],
'probability': 0.0,
'planned_cost': 0.0,
'planned_revenue': 0.0,
}
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:
- section_id: if set, stages must belong to this section or
be a default case
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
# collect all section_ids
section_ids = []
if section_id:
section_ids.append(section_id)
for case in cases:
if case.section_id:
section_ids.append(case.section_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(('section_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('crm.case.stage').search(cr, uid, search_domain, order=order, context=context)
if stage_ids:
return stage_ids[0]
return False
def message_new(self, cr, uid, msg, custom_values=None, context=None):
"""Automatically called when new email message arrives"""
@ -93,21 +138,18 @@ class crm_fundraising(crm.crm_case, osv.osv):
self.write(cr, uid, [res_id], vals, context=context)
return res_id
# ---------------------------------------------------
# OpenChatter methods and notifications
# ---------------------------------------------------
_defaults = {
'active': 1,
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'email_from': crm.crm_case. _get_default_email,
'state': 'draft',
'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.case', context=c),
'priority': crm.AVAILABLE_PRIORITIES[2][0],
'probability': 0.0,
'planned_cost': 0.0,
'planned_revenue': 0.0,
}
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
""" Override of default prefix for notifications. """
return 'Fundraising'
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('crm.case.stage').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)
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -9,7 +9,6 @@
<field name="partner_id" ref="base.res_partner_9"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="&quot;open&quot;" name="state"/>
<field eval="250000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-01 10:35:50')" name="date"/>
@ -26,7 +25,6 @@
<field name="partner_id" ref="base.main_partner"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;draft&quot;" name="state"/>
<field eval="2000000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-05 12:35:50')" name="date"/>
@ -43,7 +41,6 @@
<field name="partner_id" ref="base.res_partner_3"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;open&quot;" name="state"/>
<field eval="500000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-07 13:50:50')" name="date"/>
@ -59,7 +56,6 @@
<field name="partner_id" ref="base.res_partner_4"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="&quot;draft&quot;" name="state"/>
<field eval="1000000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-12 15:10:50')" name="date"/>
@ -75,7 +71,6 @@
<field name="partner_id" ref="base.res_partner_14"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;open&quot;" name="state"/>
<field eval="5000000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-17 19:00:15')" name="date"/>
@ -91,7 +86,6 @@
<field name="partner_id" ref="base.res_partner_10"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;done&quot;" name="state"/>
<field eval="10000000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-27 09:00:15')" name="date"/>
@ -109,7 +103,6 @@
<field name="partner_id" ref="base.res_partner_15"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="&quot;draft&quot;" name="state"/>
<field eval="10000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-01 10:00:15')" name="date"/>
@ -126,7 +119,6 @@
<field name="partner_id" ref="base.res_partner_9"/>
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field eval="&quot;open&quot;" name="state"/>
<field eval="800000.0" name="planned_cost"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="time.strftime('%Y-%m-24 22:00:15')" name="date"/>

View File

@ -19,10 +19,10 @@
#
##############################################################################
from base_status.base_state import base_state
from crm import crm
from osv import fields, osv
import time
from crm import wizard
from osv import fields, osv
import tools
from tools.translate import _
@ -34,7 +34,7 @@ CRM_HELPDESK_STATES = (
wizard.mail_compose_message.SUPPORTED_MODELS.append('crm.helpdesk')
class crm_helpdesk(crm.crm_case, osv.osv):
class crm_helpdesk(base_state, osv.osv):
""" Helpdesk Cases """
_name = "crm.helpdesk"
@ -82,12 +82,11 @@ class crm_helpdesk(crm.crm_case, osv.osv):
_defaults = {
'active': lambda *a: 1,
'user_id': crm.crm_case._get_default_user,
'partner_id': crm.crm_case._get_default_partner,
'email_from': crm.crm_case. _get_default_email,
'user_id': lambda s, cr, uid, c: s._get_default_user(cr, uid, c),
'partner_id': lambda s, cr, uid, c: s._get_default_partner(cr, uid, c),
'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
'state': lambda *a: 'draft',
'date': lambda *a: time.strftime('%Y-%m-%d %H:%M:%S'),
'section_id': crm.crm_case. _get_section,
'date': lambda *a: fields.datetime.now(),
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
'priority': lambda *a: crm.AVAILABLE_PRIORITIES[2][0],
}
@ -142,5 +141,12 @@ class crm_helpdesk(crm.crm_case, osv.osv):
res = self.write(cr, uid, [case.id], values, context=context)
return res
# ******************************
# OpenChatter
# ******************************
def case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Helpdesk'
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -30,12 +30,18 @@
<field name="arch" type="xml">
<form layout="manual">
<div class="oe_form_topbar">
<button name="case_close" states="open,pending" string="Close" type="object" />
<button name="case_open" states="draft,pending" string="Open" type="object" />
<button name="case_pending" states="draft,open" string="Pending" type="object" />
<button name="case_reset" states="done,cancel" string="Reset to Draft" type="object" />
<button name="case_escalate" states="open,draft,pending" string="Escalate" type="object" />
<button name="case_cancel" states="draft,open,pending" string="Cancel" type="object" />
<button name="case_open" string="Open" type="object"
states="draft,pending"/>
<button name="case_close" string="Close" type="object"
states="open,draft,pending"/>
<button name="case_pending" string="Pending" type="object"
states="draft,open"/>
<button name="case_reset" string="Reset to Draft" type="object"
states="cancel,done"/>
<button name="case_escalate" string="Escalate" type="object"
states="open,draft,pending"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending"/>
<div class="oe_right">
<field name="state" select="1" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}'/>
</div>
@ -126,6 +132,9 @@
</page>
</notebook>
</sheet>
<div class="oe_form_bottom">
<field name="message_ids_social" colspan="4" widget="ThreadView" nolabel="1"/>
</div>
</form>
</field>
</record>

View File

@ -37,7 +37,14 @@ system to store and search in your CV base.
'author': 'OpenERP SA',
'website': 'http://www.openerp.com',
'images': ['images/hr_recruitment_analysis.jpeg','images/hr_recruitment_applicants.jpeg'],
'depends': ['decimal_precision', 'hr', 'survey', 'crm', 'fetchmail'],
'depends': [
'base_status',
'decimal_precision',
'hr',
'survey',
'crm',
'fetchmail',
],
'update_xml': [
'wizard/hr_recruitment_phonecall_view.xml',
'wizard/hr_recruitment_employee_hired.xml',

View File

@ -12,7 +12,7 @@
<field name="job_id"/>
<field name="partner_name"/>
<field name="stage_id"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>

View File

@ -19,6 +19,7 @@
#
##############################################################################
from base_status.base_stage import base_stage
import time
from datetime import datetime, timedelta
@ -57,8 +58,6 @@ class hr_recruitment_source(osv.osv):
_columns = {
'name': fields.char('Source Name', size=64, required=True, translate=True),
}
hr_recruitment_source()
class hr_recruitment_stage(osv.osv):
""" Stage of HR Recruitment """
@ -69,12 +68,15 @@ class hr_recruitment_stage(osv.osv):
'name': fields.char('Name', size=64, required=True, translate=True),
'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."),
'department_id':fields.many2one('hr.department', 'Specific to a Department', help="Stages of the recruitment process may be different per department. If this stage is common to all departments, keep tempy this field."),
'requirements': fields.text('Requirements')
'state': fields.selection(AVAILABLE_STATES, 'State', required=True, help="The related state for the stage. The state of your document will automatically change regarding the selected stage. Example, a stage is related to the state 'Close', when your document reach this stage, it will be automatically closed."),
'fold': fields.boolean('Hide in views if empty', help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
'requirements': fields.text('Requirements'),
}
_defaults = {
'sequence': 1,
'state': 'draft',
'fold': False,
}
hr_recruitment_stage()
class hr_recruitment_degree(osv.osv):
""" Degree of HR Recruitment """
@ -90,14 +92,60 @@ class hr_recruitment_degree(osv.osv):
_sql_constraints = [
('name_uniq', 'unique (name)', 'The name of the Degree of Recruitment must be unique!')
]
hr_recruitment_degree()
class hr_applicant(crm.crm_case, osv.osv):
class hr_applicant(base_stage, osv.Model):
_name = "hr.applicant"
_description = "Applicant"
_order = "id desc"
_inherit = ['ir.needaction_mixin', 'mail.thread']
def _get_default_department_id(self, cr, uid, context=None):
""" Gives default department by checking if present in the context """
return (self._resolve_department_id_from_context(cr, uid, context=context) or False)
def _get_default_stage_id(self, cr, uid, context=None):
""" Gives default stage_id """
department_id = self._get_default_department_id(cr, uid, context=context)
return self.stage_find(cr, uid, [], department_id, [('state', '=', 'draft')], context=context)
def _resolve_department_id_from_context(self, cr, uid, context=None):
""" Returns ID of department based on the value of 'default_department_id'
context key, or None if it cannot be resolved to a single
department.
"""
if context is None:
context = {}
if type(context.get('default_department_id')) in (int, long):
return context.get('default_department_id')
if isinstance(context.get('default_department_id'), basestring):
department_name = context['default_department_id']
department_ids = self.pool.get('hr.department').name_search(cr, uid, name=department_name, context=context)
if len(department_ids) == 1:
return int(department_ids[0][0])
return None
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('hr.recruitment.stage')
order = stage_obj._order
# lame hack to allow reverting search, should just work in the trivial case
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 ('department_id', '=', False), ('fold', '=', False): add default columns that are not folded
# - OR ('department_id', 'in', department_id), ('fold', '=', False) if department_id: add department columns that are not folded
department_id = self._resolve_department_id_from_context(cr, uid, context=context)
search_domain = []
if department_id:
search_domain += ['|', '&', ('department_id', '=', department_id), ('fold', '=', False)]
search_domain += ['|', ('id', 'in', ids), '&', ('department_id', '=', False), ('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])))
return result
def _compute_day(self, cr, uid, ids, fields, args, context=None):
"""
@param cr: the current row, from the database cursor,
@ -143,12 +191,15 @@ class hr_applicant(crm.crm_case, osv.osv):
'partner_id': fields.many2one('res.partner', 'Partner'),
'create_date': fields.datetime('Creation Date', readonly=True, select=True),
'write_date': fields.datetime('Update Date', readonly=True),
'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage'),
'state': fields.selection(AVAILABLE_STATES, 'Status', 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\'.'),
'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage',
domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"),
'state': fields.related('stage_id', 'state', type="selection", store=True,
selection=AVAILABLE_STATES, string="State", readonly=True,
help='The state is set to \'Draft\', when a case is created.\
If the case is in progress the state is set to \'Open\'.\
When the case is over, the state is set to \'Done\'.\
If the case needs to be reviewed then the state is \
set to \'Pending\'.'),
'company_id': fields.many2one('res.company', 'Company'),
'user_id': fields.many2one('res.users', 'Responsible'),
# Applicant Columns
@ -169,7 +220,6 @@ class hr_applicant(crm.crm_case, osv.osv):
'partner_mobile': fields.char('Mobile', size=32),
'type_id': fields.many2one('hr.recruitment.degree', 'Degree'),
'department_id': fields.many2one('hr.department', 'Department'),
'state': fields.selection(AVAILABLE_STATES, 'Status', size=16, readonly=True),
'survey': fields.related('job_id', 'survey_id', type='many2one', relation='survey', string='Survey'),
'response': fields.integer("Response"),
'reference': fields.char('Refered By', size=128),
@ -185,33 +235,19 @@ class hr_applicant(crm.crm_case, osv.osv):
_defaults = {
'active': lambda *a: 1,
'user_id': lambda self, cr, uid, context: uid,
'email_from': crm.crm_case. _get_default_email,
'state': lambda *a: 'draft',
'user_id': lambda s, cr, uid, c: uid,
'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
'priority': lambda *a: '',
'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'crm.helpdesk', context=c),
'color': 0,
}
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('hr.recruitment.stage')
order = stage_obj._order
if read_group_order == 'stage_id desc':
# lame hack to allow reverting search, should just work in the trivial case
order = "%s desc" % order
stage_ids = stage_obj._search(cr, uid, ['|',('id','in',ids),('department_id','=',False)], 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])))
return result
_group_by_full = {
'stage_id': _read_group_stage_ids
}
def onchange_job(self,cr, uid, ids, job, context=None):
result = {}
@ -229,47 +265,33 @@ class hr_applicant(crm.crm_case, osv.osv):
stage_id = stage_ids and stage_ids[0] or False
return {'value': {'stage_id': stage_id}}
def stage_previous(self, cr, uid, ids, context=None):
"""This function computes previous stage for case from its current stage
using available stage for that case type
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case IDs
@param context: A standard dictionary for contextual values"""
stage_obj = self.pool.get('hr.recruitment.stage')
for case in self.browse(cr, uid, ids, context=context):
department = (case.department_id.id or False)
st = case.stage_id.id or False
stage_ids = stage_obj.search(cr, uid, ['|',('department_id','=',department),('department_id','=',False)], context=context)
if st and stage_ids.index(st):
self.write(cr, uid, [case.id], {'stage_id': stage_ids[stage_ids.index(st)-1]}, context=context)
else:
self.write(cr, uid, [case.id], {'stage_id': False}, context=context)
return True
def stage_next(self, cr, uid, ids, context=None):
"""This function computes next stage for case from its current stage
using available stage for that case type
@param self: The object pointer
@param cr: the current row, from the database cursor,
@param uid: the current users ID for security checks,
@param ids: List of case IDs
@param context: A standard dictionary for contextual values"""
stage_obj = self.pool.get('hr.recruitment.stage')
for case in self.browse(cr, uid, ids, context=context):
department = (case.department_id.id or False)
st = case.stage_id.id or False
stage_ids = stage_obj.search(cr, uid, ['|',('department_id','=',department),('department_id','=',False)], context=context)
val = False
if st and len(stage_ids) != stage_ids.index(st)+1:
val = stage_ids[stage_ids.index(st)+1]
elif (not st) and stage_ids:
val = stage_ids[0]
else:
val = False
self.write(cr, uid, [case.id], {'stage_id': val}, context=context)
return True
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:
- department_id: if set, stages must belong to this section or
be a default case
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
# collect all section_ids
department_ids = []
if section_id:
department_ids.append(section_id)
for case in cases:
if case.department_id:
department_ids.append(case.department_id.id)
# OR all section_ids and OR with case_default
search_domain = []
if department_ids:
search_domain += ['|', ('department_id', 'in', department_ids)]
search_domain.append(('department_id', '=', False))
# AND with the domain in parameter
search_domain += list(domain)
# perform search, return the first found
stage_ids = self.pool.get('hr.recruitment.stage').search(cr, uid, search_domain, order=order, context=context)
if stage_ids:
return stage_ids[0]
return False
def action_makeMeeting(self, cr, uid, ids, context=None):
"""
@ -474,12 +496,6 @@ class hr_applicant(crm.crm_case, osv.osv):
"""
return self.set_priority(cr, uid, ids, '3')
def write(self, cr, uid, ids, vals, context=None):
if 'stage_id' in vals and vals['stage_id']:
stage = self.pool.get('hr.recruitment.stage').browse(cr, uid, vals['stage_id'], context=context)
self.message_append_note(cr, uid, ids, body=_("Stage changed to <b>%s</b>.") % stage.name, context=context)
return super(hr_applicant,self).write(cr, uid, ids, vals, context=context)
# -------------------------------------------------------
# OpenChatter methods and notifications
# -------------------------------------------------------
@ -497,7 +513,13 @@ class hr_applicant(crm.crm_case, osv.osv):
if obj.state == 'draft' and obj.user_id:
result[obj.id] = [obj.user_id.id]
return result
def stage_set_send_note(self, cr, uid, ids, stage_id, context=None):
""" Override of the (void) default notification method. """
if not stage_id: return True
stage_name = self.pool.get('hr.recruitment.stage').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 case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Applicant'
@ -529,7 +551,6 @@ class hr_applicant(crm.crm_case, osv.osv):
message = _("Applicant has been <b>created</b>.")
return self.message_append_note(cr, uid, ids, body=message, context=context)
hr_applicant()
class hr_job(osv.osv):
_inherit = "hr.job"
@ -537,6 +558,6 @@ class hr_job(osv.osv):
_columns = {
'survey_id': fields.many2one('survey', 'Interview Form', help="Choose an interview form for this job position and you will be able to print/answer this interview from all applicants who apply for this job"),
}
hr_job()
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:

View File

@ -1,7 +1,9 @@
<?xml version="1.0"?>
<openerp>
<!--
<data noupdate="1">
-->
<data>
<!-- HR Recruitment Source -->
@ -37,30 +39,36 @@
<field name="name">Doctoral Degree</field>
<field name="sequence">4</field>
</record>
<record model="hr.recruitment.stage" id="stage_job1">
<field name="name">Initial Qualification</field>
<field name="state">draft</field>
<field name="sequence">1</field>
</record>
<record model="hr.recruitment.stage" id="stage_job2">
<field name="name">First Interview</field>
<field name="state">open</field>
<field name="sequence">2</field>
</record>
<record model="hr.recruitment.stage" id="stage_job3">
<field name="name">Second Interview</field>
<field name="state">open</field>
<field name="sequence">3</field>
</record>
<record model="hr.recruitment.stage" id="stage_job4">
<field name="name">Contract Proposed</field>
<field name="state">pending</field>
<field name="sequence">4</field>
</record>
<record model="hr.recruitment.stage" id="stage_job5">
<field name="name">Contract Signed</field>
<field name="state">done</field>
<field name="sequence">5</field>
</record>
<record model="hr.recruitment.stage" id="stage_job6">
<field name="name">Refused</field>
<field name="state">cancel</field>
<field name="sequence">6</field>
<field name="fold" eval="True"/>
</record>
<record id="survey_job_0" model="survey">
<field name="title">Job Survey</field>

View File

@ -60,7 +60,7 @@
<field name="availability" invisible="1"/>
<field name="department_id" invisible="context.get('invisible_department', True)"/>
<field name="user_id"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>
@ -72,14 +72,24 @@
<field name="arch" type="xml">
<form layout="manual">
<div class="oe_form_topbar">
<button name="%(action_hr_recruitment_hired_employee)d" string="Hire" states="open,pending" type="action"/>
<button name="case_open" string="In Progress" states="draft,pending" type="object"/>
<button name="case_pending" string="Pending" states="open" type="object"/>
<button name="case_reset" string="Reset to New" states="done,cancel" type="object"/>
<button name="case_cancel" string="Refuse" states="draft,open,pending" type="object"/>
<button name="%(action_hr_recruitment_hired_employee)d" string="Hire" type="action"
states="open,pending"/>
<button name="case_open" string="Open" type="object"
states="draft,pending"/>
<button name="case_pending" string="Pending" type="object"
states="open"/>
<button name="case_reset" string="Reset to New" type="object"
states="done,cancel"/>
<button name="case_cancel" string="Refuse" type="object"
states="draft,open,pending"/>
<button name="stage_previous" string="Previous" type="object"
states="open" icon="gtk-go-back"/>
<button name="stage_next" string="Next" type="object"
states="open" icon="gtk-go-forward"/>
<div class="oe_right">
<field name="state" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}'/>
<field name="stage_id" nolabel="1" widget="statusbar"/>
</div>
<div class="oe_clear"/>
</div>
<sheet string="Jobs - Recruitment Form" layout="auto">
<group colspan="4" col="4">
@ -92,11 +102,7 @@
<field name="user_id"/>
<field name="job_id" on_change="onchange_job(job_id)"/>
<field name="department_id" widget="selection" on_change="onchange_department_id(department_id)"/>
<group colspan="2" col="4">
<field name="stage_id" domain="['|',('department_id','=',department_id),('department_id','=',False)]"/>
<button name="stage_previous" string="" type="object" icon="gtk-go-back"/>
<button icon="gtk-go-forward" string="" name="stage_next" type="object"/>
</group>
<field name="state" groups="base.group_no_one"/>
<field name="date_action"/>
<group colspan="2" col="8">
<field name="title_action"/>
@ -135,6 +141,13 @@
<field name="source_id"/>
<field name="reference"/>
</group>
<group col="2" colspan="2">
<separator colspan="2" string="Dates"/>
<field name="create_date"/>
<field name="write_date"/>
<field name="date_closed"/>
<field name="date_open"/>
</group>
</page>
<page string="Notes">
<field name="description" nolabel="1" colspan="4"/>
@ -237,7 +250,7 @@
<field name="arch" type="xml">
<kanban default_group_by="stage_id">
<field name="color"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
<field name="priority"/>
<field name="survey"/>
<field name="user_id"/>
@ -356,6 +369,7 @@
<field name="sequence" invisible="1"/>
<field name="name"/>
<field name="department_id"/>
<field name="state"/>
</tree>
</field>
</record>
@ -373,6 +387,7 @@
<field name="name" select="1"/>
<field name="department_id"/>
<field name="sequence"/>
<field name="state"/>
</group>
<separator string="Requirements" colspan="4"/>
<field name="requirements" nolabel="1" colspan="4"/>

View File

@ -1,7 +1,7 @@
-
In Order to test process of Recruitment,
-
Applicant interested in job position. so He send resume by email.
An applicant is interested in the job position. So he sends a resume by email.
-
!python {model: mail.thread}: |
import addons
@ -9,27 +9,42 @@
request_message = request_file.read()
self.message_process(cr, uid, 'hr.applicant', request_message)
-
After getting the mail, I check details of new applicant.
After getting the mail, I check the details of the new applicant.
-
!python {model: hr.applicant}: |
applicant_ids = self.search(cr, uid, [('email_from','=', 'Mr. Richard Anderson <Richard_Anderson@yahoo.com>')])
assert applicant_ids, "Applicant is not created after getting the mail"
applicant = self.browse(cr, uid, applicant_ids[0], context=context)
resume_ids = self.pool.get('ir.attachment').search(cr, uid, [('datas_fname','=','resume.doc'),('res_model','=',self._name),('res_id','=',applicant.id)])
assert applicant.name == "Application for the post of Jr.application Programmer.", "Subject does not match"
assert applicant.state == "draft"
assert len(resume_ids), "Resume does not attached."
assert applicant.name == "Application for the post of Jr.application Programmer.", "Applicant name does not match."
assert applicant.stage_id.id == ref('hr_recruitment.stage_job1'), "Stage should be 'Initial qualification' and is '%s'." % (applicant.stage_id.name)
assert applicant.state == "draft", "Applicant state should be 'draft'."
assert len(resume_ids), "Resume is not attached."
-
I refuse applicant for the Recruitment.
I refuse the applicant (hr_case_programmer)
-
!python {model: hr.applicant}: |
self.case_close(cr, uid, [ref("hr_case_programmer")])
self.case_cancel(cr, uid, [ref("hr_case_programmer")])
-
I open applicant for the Recruitment.
I check the details of the refused applicant.
-
!python {model: hr.applicant}: |
applicant = self.browse(cr, uid, ref("hr_case_programmer"), context=context)
assert applicant.stage_id.id == ref('hr_recruitment.stage_job6'), "Stage should be 'Refused' and is %s." % (applicant.stage_id.name)
assert applicant.state == 'cancel', "Applicant is not in 'cancel' state."
-
I reset and re-open the previously refused applicant.
-
!python {model: hr.applicant}: |
self.case_reset(cr, uid, [ref("hr_case_programmer")])
self.case_open(cr, uid, [ref("hr_case_programmer")])
-
I check the details of the re-opened applicant.
-
!python {model: hr.applicant}: |
applicant = self.browse(cr, uid, ref("hr_case_programmer"), context=context)
assert applicant.stage_id.id == ref('hr_recruitment.stage_job2'), "Stage should be 'First interview' and is '%s'." % (applicant.stage_id.name)
assert applicant.state == "open", "Applicant state should be 'open'."
-
I assign the Job position to the applicant
-
@ -91,7 +106,7 @@
-
I check that applicant is "Hired".
-
!assert {model: hr.applicant, id: hr_case_programmer}:
!assert {model: hr.applicant, id: hr_case_programmer, string: Applicant state is done}:
- state == 'done'
-
I do not give employment to the hired the applicant.

View File

@ -28,7 +28,7 @@
"category": "Project Management",
"sequence": 8,
"images": ["images/gantt.png", "images/project_dashboard.jpeg","images/project_task_tree.jpeg","images/project_task.jpeg","images/project.jpeg","images/task_analysis.jpeg"],
"depends": ["base_setup", "product", "analytic", "board", "mail", "resource","web_kanban"],
"depends": ["base_setup", "base_status", "product", "analytic", "board", "mail", "resource","web_kanban"],
"description": """
Project management module tracks multi-level projects, tasks, work done on tasks, eso.
======================================================================================

View File

@ -17,7 +17,8 @@
<field name="planned_hours" widget="float_time"/>
<field name="effective_hours" widget="float_time"/>
<field name="progress" widget="progressbar"/>
<field name="state" invisible="context.get('set_visible',False)"/>
<field name="stage_id" invisible="context.get('set_visible',False)"/>
<field name="state" invisible="context.get('set_visible',False)" groups="base.group_no_one"/>
</tree>
</field>
</record>
@ -35,7 +36,8 @@
<field name="date_deadline"/>
<field name="total_hours" widget="float_time"/>
<field name="progress" widget="progressbar"/>
<field name="state" invisible="context.get('set_visible',False)"/>
<field name="stage_id" invisible="context.get('set_visible',False)"/>
<field name="state" invisible="context.get('set_visible',False)" groups="base.group_no_one"/>
</tree>
</field>
</record>

View File

@ -2,7 +2,7 @@
##############################################################################
#
# OpenERP, Open Source Management Solution
# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
# Copyright (C) 2004-today OpenERP SA (<http://www.openerp.com>)
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
@ -19,18 +19,15 @@
#
##############################################################################
from lxml import etree
import time
from base_status.base_stage import base_stage
from datetime import datetime, date
from tools.translate import _
from lxml import etree
from osv import fields, osv
from openerp.addons.resource.faces import task as Task
import time
from tools.translate import _
# I think we can remove this in v6.1 since VMT's improvements in the framework ?
#class project_project(osv.osv):
# _name = 'project.project'
#project_project()
_TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')]
class project_task_type(osv.osv):
_name = 'project.task.type'
@ -40,14 +37,20 @@ class project_task_type(osv.osv):
'name': fields.char('Stage Name', required=True, size=64, translate=True),
'description': fields.text('Description'),
'sequence': fields.integer('Sequence'),
'project_default': fields.boolean('Common to All Projects', help="If you check this field, this stage will be proposed by default on each new project. It will not assign this stage to existing projects."),
'case_default': fields.boolean('Common to All Projects',
help="If you check this field, this stage will be proposed by default on each new project. It will not assign this stage to existing projects."),
'project_ids': fields.many2many('project.project', 'project_task_type_rel', 'type_id', 'project_id', 'Projects'),
'state': fields.selection(_TASK_STATE, 'State', required=True,
help="The related state for the stage. The state of your document will automatically change regarding the selected stage. Example, a stage is related to the state 'Close', when your document reach this stage, it will be automatically closed."),
'fold': fields.boolean('Hide in views if empty',
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 = {
'sequence': 1
'sequence': 1,
'state': 'draft',
'fold': False,
}
_order = 'sequence'
project_task_type()
class project(osv.osv):
_name = "project.project"
@ -208,7 +211,7 @@ class project(osv.osv):
return True
def _get_type_common(self, cr, uid, context):
ids = self.pool.get('project.task.type').search(cr, uid, [('project_default','=',1)], context=context)
ids = self.pool.get('project.task.type').search(cr, uid, [('case_default','=',1)], context=context)
return ids
_order = "sequence"
@ -239,7 +242,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
@ -247,7 +250,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
@ -501,49 +504,61 @@ def Project():
def set_close_send_note(self, cr, uid, ids, context=None):
message = _("Project has been <b>closed</b>.")
return self.message_append_note(cr, uid, ids, body=message, context=context)
project()
class task(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):
"""Return ID of project based on the value of 'project_id'
context key, or None if it cannot be resolved to a single project.
""" Returns ID of project based on the value of 'default_project_id'
context key, or None if it cannot be resolved to a single
project.
"""
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)
project_ids = self.pool.get('project.project').name_search(cr, uid, name=project_name, context=context)
if len(project_ids) == 1:
return project_ids[0][0]
return None
def _read_group_type_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
stage_obj = self.pool.get('project.task.type')
project_id = self._resolve_project_id_from_context(cr, uid, context=context)
order = stage_obj._order
access_rights_uid = access_rights_uid or uid
if read_group_order == 'type_id desc':
# lame way to allow reverting search, should just work in the trivial case
# lame way to allow reverting search, should just work in the trivial case
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:
domain = ['|', ('id','in',ids), ('project_ids','in',project_id)]
else:
domain = ['|', ('id','in',ids), ('project_default','=',1)]
stage_ids = stage_obj._search(cr, uid, domain, order=order, access_rights_uid=access_rights_uid, context=context)
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])))
return result
def _read_group_user_id(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
res_users = self.pool.get('res.users')
project_id = self._resolve_project_id_from_context(cr, uid, context=context)
@ -562,11 +577,10 @@ class task(osv.osv):
return result
_group_by_full = {
'type_id': _read_group_type_id,
'user_id': _read_group_user_id
'stage_id': _read_group_stage_ids,
'user_id': _read_group_user_id,
}
def search(self, cr, user, args, offset=0, limit=None, order=None, context=None, count=False):
obj_project = self.pool.get('project.project')
for domain in args:
@ -597,7 +611,6 @@ class task(osv.osv):
res[task.id]['progress'] = 100.0
return res
def onchange_remaining(self, cr, uid, ids, remaining=0.0, planned = 0.0):
if remaining and not planned:
return {'value':{'planned_hours': remaining}}
@ -640,7 +653,7 @@ class task(osv.osv):
if not default.get('remaining_hours', False):
default['remaining_hours'] = float(self.read(cr, uid, id, ['planned_hours'])['planned_hours'])
default['active'] = True
default['type_id'] = False
default['stage_id'] = False
if not default.get('name', False):
default['name'] = self.browse(cr, uid, id, context=context).name or ''
if not context.get('copy',False):
@ -670,10 +683,15 @@ class task(osv.osv):
'description': fields.text('Description'),
'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."),
'type_id': fields.many2one('project.task.type', 'Stage'),
'state': fields.selection([('draft', 'New'),('cancelled', 'Cancelled'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done')], 'Status', readonly=True, required=True,
help='If the task is created the state is \'Draft\'.\n If the task is started, the state becomes \'In Progress\'.\n If review is needed the task is in \'Pending\' state.\
\n If the task is over, the states is set to \'Done\'.'),
'stage_id': fields.many2one('project.task.type', 'Stage',
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.\
If the case is in progress the state is set to \'Open\'.\
When the case is over, the state is set to \'Done\'.\
If the case needs to be reviewed then the state is \
set to \'Pending\'.'),
'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready To Pull')], 'Kanban State',
help="A task's kanban state indicates special situations affecting it:\n"
" * Normal is the default situation\n"
@ -722,6 +740,8 @@ class task(osv.osv):
}
_defaults = {
'stage_id': _get_default_stage_id,
'project_id': _get_default_project_id,
'state': 'draft',
'kanban_state': 'normal',
'priority': '2',
@ -729,7 +749,7 @@ class task(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"
@ -828,6 +848,41 @@ class task(osv.osv):
res['fields'][f]['string'] = res['fields'][f]['string'].replace('Hours',tm)
return res
# ****************************************
# Case management
# ****************************************
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:
- 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)
# collect all section_ids
section_ids = []
if section_id:
section_ids.append(section_id)
for task in cases:
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
def _check_child_task(self, cr, uid, ids, context=None):
if context == None:
context = {}
@ -840,9 +895,9 @@ class task(osv.osv):
return True
def action_close(self, cr, uid, ids, context=None):
# This action open wizard to send email to partner or project manager after close task.
if context == None:
context = {}
""" This action closes the task, then opens the wizard to send an
email to the partner or the project manager.
"""
task_id = len(ids) and ids[0] or False
self._check_child_task(cr, uid, ids, context=context)
if not task_id: return False
@ -863,12 +918,14 @@ class task(osv.osv):
}
return res
def do_close(self, cr, uid, ids, context={}):
"""
Close Task
"""
def do_close(self, cr, uid, ids, context=None):
""" Compatibility when changing to case_close. """
return self.case_close(cr, uid, ids, context=context)
def case_close(self, cr, uid, ids, context=None):
""" Closes Task """
request = self.pool.get('res.request')
if not isinstance(ids,list): ids = [ids]
if not isinstance(ids, list): ids = [ids]
for task in self.browse(cr, uid, ids, context=context):
vals = {}
project = task.project_id
@ -884,7 +941,6 @@ class task(osv.osv):
'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
@ -893,12 +949,12 @@ class task(osv.osv):
reopen = False
if reopen:
self.do_reopen(cr, uid, [parent_id.id], context=context)
vals.update({'state': 'done'})
vals.update({'remaining_hours': 0.0})
# close task
vals['remaining_hours'] = 0.0
if not task.date_end:
vals.update({ 'date_end':time.strftime('%Y-%m-%d %H:%M:%S')})
self.write(cr, uid, [task.id],vals, context=context)
self.do_close_send_note(cr, uid, [task.id], context)
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)
return True
def do_reopen(self, cr, uid, ids, context=None):
@ -916,12 +972,15 @@ class task(osv.osv):
'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={}):
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(self, cr, uid, ids, context=None):
request = self.pool.get('res.request')
tasks = self.browse(cr, uid, ids, context=context)
self._check_child_task(cr, uid, ids, context=context)
@ -937,27 +996,38 @@ class task(osv.osv):
'ref_doc1': 'project.task,%d' % task.id,
'ref_doc2': 'project.project,%d' % project.id,
}, context=context)
self.write(cr, uid, [task.id], {'state': 'cancelled', 'remaining_hours':0.0}, context=context)
self.do_cancel_send_note(cr, uid, [task.id], context)
# cancel task
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
def do_open(self, cr, uid, ids, context={}):
def do_open(self, cr, uid, ids, context=None):
""" Compatibility when changing to case_open. """
return self.case_open(cr, uid, ids, context=context)
def case_open(self, cr, uid, ids, context=None):
if not isinstance(ids,list): ids = [ids]
tasks= self.browse(cr, uid, ids, context=context)
for t in tasks:
data = {'state': 'open'}
if not t.date_start:
data['date_start'] = time.strftime('%Y-%m-%d %H:%M:%S')
self.write(cr, uid, [t.id], data, context=context)
self.do_open_send_note(cr, uid, [t.id], context)
self.case_set(cr, uid, ids, 'open', {'date_start': fields.datetime.now()}, context=context)
self.case_open_send_note(cr, uid, ids, context)
return True
def do_draft(self, cr, uid, ids, context={}):
self.write(cr, uid, ids, {'state': 'draft'}, context=context)
self.do_draft_send_note(cr, uid, ids, context)
def do_draft(self, cr, uid, ids, context=None):
""" Compatibility when changing to case_draft. """
return self.case_draft(cr, uid, ids, context=context)
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
def do_pending(self, cr, uid, ids, context=None):
""" Compatibility when changing to case_pending. """
return self.case_pending(cr, uid, ids, context=context)
def case_pending(self, cr, uid, ids, context=None):
self.case_set(cr, uid, ids, 'pending', {}, context=context)
return self.case_pending_send_note(cr, uid, ids, context=context)
def _delegate_task_attachments(self, cr, uid, task_id, delegated_task_id, context=None):
attachment = self.pool.get('ir.attachment')
attachment_ids = attachment.search(cr, uid, [('res_model', '=', self._name), ('res_id', '=', task_id)], context=context)
@ -966,7 +1036,6 @@ class task(osv.osv):
new_attachment_ids.append(attachment.copy(cr, uid, attachment_id, default={'res_id': delegated_task_id}, context=context))
return new_attachment_ids
def do_delegate(self, cr, uid, ids, delegate_data={}, context=None):
"""
Delegate Task to another users.
@ -1000,11 +1069,6 @@ class task(osv.osv):
delegated_tasks[task.id] = delegated_task_id
return delegated_tasks
def do_pending(self, cr, uid, ids, context={}):
self.write(cr, uid, ids, {'state': 'pending'}, context=context)
self.do_pending_send_note(cr, uid, ids, context)
return True
def set_remaining_time(self, cr, uid, ids, remaining_time=1.0, context=None):
for task in self.browse(cr, uid, ids, context=context):
if (task.state=='draft') or (task.planned_hours==0.0):
@ -1033,36 +1097,6 @@ class task(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.type_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, {'type_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, {'type_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, {
@ -1070,7 +1104,7 @@ class task(osv.osv):
'remaining_hours': task.remaining_hours,
'planned_hours': task.planned_hours,
'kanban_state': task.kanban_state,
'type_id': task.type_id.id,
'type_id': task.stage_id.id,
'state': task.state,
'user_id': task.user_id.id
@ -1084,22 +1118,25 @@ class task(osv.osv):
return task_id
# Overridden to reset the kanban_state to normal whenever
# the stage (type_id) of the task changes.
# the stage (stage_id) of the task changes.
def write(self, cr, uid, ids, vals, context=None):
if isinstance(ids, (int, long)):
ids = [ids]
if vals and not 'kanban_state' in vals and 'type_id' in vals:
new_stage = vals.get('type_id')
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')
for t in self.browse(cr, uid, ids, context=context):
write_vals = vals_reset_kstate if t.type_id != new_stage else vals
#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.'))
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
else:
result = super(task,self).write(cr, uid, ids, vals, context=context)
if ('type_id' in vals) or ('remaining_hours' in vals) or ('user_id' in vals) or ('state' in vals) or ('kanban_state' in vals):
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):
@ -1139,7 +1176,11 @@ class task(osv.osv):
# ---------------------------------------------------
# OpenChatter methods and notifications
# ---------------------------------------------------
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, [])
for obj in self.browse(cr, uid, ids, context=context):
@ -1156,28 +1197,16 @@ class task(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)
def do_pending_send_note(self, cr, uid, ids, context=None):
if not isinstance(ids,list): ids = [ids]
msg = _('Task is now <b>pending</b>.')
return self.message_append_note(cr, uid, ids, body=msg, context=context)
def do_open_send_note(self, cr, uid, ids, context=None):
msg = _('Task has been <b>opened</b>.')
return self.message_append_note(cr, uid, ids, body=msg, context=context)
def do_cancel_send_note(self, cr, uid, ids, context=None):
msg = _('Task has been <b>canceled</b>.')
return self.message_append_note(cr, uid, ids, body=msg, context=context)
def do_close_send_note(self, cr, uid, ids, context=None):
msg = _('Task has been <b>closed</b>.')
return self.message_append_note(cr, uid, ids, body=msg, context=context)
def do_draft_send_note(self, cr, uid, ids, context=None):
msg = _('Task has been <b>renewed</b>.')
def case_draft_send_note(self, cr, uid, ids, context=None):
msg = _('Task has been set as <b>draft</b>.')
return self.message_append_note(cr, uid, ids, body=msg, context=context)
def do_delegation_send_note(self, cr, uid, ids, context=None):
@ -1186,13 +1215,6 @@ class task(osv.osv):
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.type_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"
@ -1231,7 +1253,7 @@ 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):
@ -1252,7 +1274,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)
@ -1313,7 +1334,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'
@ -1341,4 +1362,4 @@ class project_task_history_cumulative(osv.osv):
) as history
)
""")
project_task_history_cumulative()

View File

@ -24,22 +24,40 @@
<record id="project_tt_specification" model="project.task.type">
<field name="sequence">1</field>
<field name="name">Design</field>
<field name="project_default" eval="1"/>
<field name="state">draft</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_development" model="project.task.type">
<field name="sequence">2</field>
<field name="name">Development</field>
<field name="project_default" eval="1"/>
<field name="state">open</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_testing" model="project.task.type">
<field name="sequence">3</field>
<field name="name">Testing</field>
<field name="project_default" eval="1"/>
<field name="state">open</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_pending" model="project.task.type">
<field name="sequence">4</field>
<field name="name">Pending</field>
<field name="state">pending</field>
<field name="case_default" eval="True"/>
<field name="fold" eval="True"/>
</record>
<record id="project_tt_merge" model="project.task.type">
<field name="sequence">4</field>
<field name="sequence">5</field>
<field name="name">Deployment</field>
<field name="project_default" eval="1"/>
<field name="state">done</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_cancel" model="project.task.type">
<field name="sequence">6</field>
<field name="name">Cancelled</field>
<field name="state">cancelled</field>
<field name="case_default" eval="True"/>
<field name="fold" eval="True"/>
</record>
</data>
</openerp>

View File

@ -77,7 +77,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Prepare Requirements Document</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
<field name="color">3</field>
</record>
@ -88,7 +88,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Make SRS</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_3" model="project.task">
@ -99,7 +99,7 @@
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Design Use Cases</field>
<field name="date_deadline" eval="time.strftime('%Y-%m-24')"/>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_4" model="project.task">
@ -112,7 +112,7 @@
<field name="description">Use the account_budget module</field>
<field name="date_deadline" eval="time.strftime('%Y-%m-19')"/>
<field name="color">3</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_5" model="project.task">
@ -125,7 +125,7 @@
<field name="kanban_state">done</field>
<field name="priority">0</field>
<field name="date_deadline" eval="time.strftime('%Y-%m-%d')"/>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_6" model="project.task">
@ -135,7 +135,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Risk Management Planning</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_7" model="project.task">
@ -145,7 +145,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Create Project Schedules</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_8" model="project.task">
@ -156,7 +156,7 @@
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Dataflow Design</field>
<field name="priority">0</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_9" model="project.task">
@ -166,7 +166,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">User Interface Design</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<record id="project_task_10" model="project.task">
@ -177,7 +177,7 @@
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Develop Module in Sale Management</field>
<field name="kanban_state">blocked</field>
<field name="type_id" ref="project_tt_development"/>
<field name="stage_id" ref="project_tt_development"/>
</record>
<record id="project_task_11" model="project.task">
@ -187,7 +187,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Develop module in Warehouse</field>
<field name="type_id" ref="project_tt_merge"/>
<field name="stage_id" ref="project_tt_merge"/>
</record>
<function model="project.task" name="do_close" eval="[ref('project_task_11')], {'install_mode': True}"/>
@ -199,7 +199,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Integrate Modules</field>
<field name="type_id" ref="project_tt_development"/>
<field name="stage_id" ref="project_tt_development"/>
</record>
<function model="project.task" name="do_close" eval="[ref('project_task_12')], {'install_mode': True}"/>
@ -211,7 +211,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Unit Testing</field>
<field name="type_id" ref="project_tt_development"/>
<field name="stage_id" ref="project_tt_development"/>
</record>
<function model="project.task" name="do_pending" eval="[ref('project_task_13')], {'install_mode': True}"/>
@ -223,8 +223,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Regression Test</field>
<field name="type_id" ref="project_tt_development"/>
<field name="state">pending</field>
<field name="stage_id" ref="project_tt_development"/>
</record>
<record id="project_task_15" model="project.task">
@ -234,7 +233,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Documentation</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
<field name="date_start">2011-02-06</field>
</record>
@ -247,7 +246,7 @@
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Performance Tuning</field>
<field name="description">Test on Runbot</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<function model="project.task" name="do_open" eval="[ref('project_task_16')], {'install_mode': True}"/>
@ -258,7 +257,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Deploy and Review on Customer System</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<function model="project.task" name="do_open" eval="[ref('project_task_17')], {'install_mode': True}"/>
@ -269,7 +268,7 @@
<field name="priority">2</field>
<field name="project_id" ref="project.project_integrate_openerp"/>
<field name="name">Training and Presentation</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<!--
@ -322,7 +321,7 @@
<record id="project_task_116" model="project.task">
<field name="planned_hours">38.0</field>
<field name="remaining_hours">38.0</field>
<field name="type_id" ref="project_tt_development"/>
<field name="stage_id" ref="project_tt_development"/>
<field name="user_id" eval="False"/>
<field name="project_id" ref="project_project_22"/>
<field name="description">BoM, After sales returns, interventions. Traceability.</field>
@ -334,7 +333,7 @@
<field name="planned_hours">16.0</field>
<field name="remaining_hours">16.0</field>
<field name="user_id" eval="False"/>
<field name="type_id" ref="project_tt_development"/>
<field name="stage_id" ref="project_tt_development"/>
<field name="project_id" ref="project_project_23"/>
<field name="name">Data importation + Doc</field>
</record>
@ -344,7 +343,7 @@
<field name="planned_hours">16.0</field>
<field name="remaining_hours">16.0</field>
<field name="user_id" eval="False"/>
<field name="type_id" ref="project_tt_development"/>
<field name="stage_id" ref="project_tt_development"/>
<field name="project_id" ref="project_project_23"/>
<field name="name">Modifications asked by the customer.</field>
</record>
@ -353,7 +352,7 @@
<record id="project_task_184" model="project.task">
<field name="planned_hours">16.0</field>
<field name="remaining_hours">16.0</field>
<field name="type_id" ref="project_tt_testing"/>
<field name="stage_id" ref="project_tt_testing"/>
<field name="user_id" eval="False"/>
<field name="priority">0</field>
<field name="project_id" ref="project_project_21"/>
@ -364,7 +363,7 @@
<field name="sequence">15</field>
<field name="planned_hours">8.0</field>
<field name="remaining_hours">8.0</field>
<field name="type_id" ref="project_tt_testing"/>
<field name="stage_id" ref="project_tt_testing"/>
<field name="user_id" eval="False"/>
<field name="project_id" ref="project_project_21"/>
<field name="name">Internal testing + Software Install</field>
@ -374,7 +373,7 @@
<field name="sequence">17</field>
<field name="planned_hours">16.0</field>
<field name="remaining_hours">16.0</field>
<field name="type_id" ref="project_tt_development"/>
<field name="stage_id" ref="project_tt_development"/>
<field name="user_id" eval="False"/>
<field name="priority">2</field>
<field name="project_id" ref="project_project_21"/>
@ -389,7 +388,7 @@
<field name="user_id" eval="False"/>
<field name="project_id" ref="project_project_23"/>
<field name="name">Parameters</field>
<field name="type_id" ref="project_tt_specification"/>
<field name="stage_id" ref="project_tt_specification"/>
</record>
<function model="project.task" name="do_open" eval="[ref('project_task_189')], {'install_mode': True}"/>
@ -398,10 +397,9 @@
<field name="planned_hours">32.0</field>
<field name="remaining_hours">32.0</field>
<field name="user_id" eval="False"/>
<field name="state">open</field>
<field name="project_id" ref="project_project_21"/>
<field name="name">Start of the doc redaction + MRP</field>
<field name="type_id" ref="project_tt_testing"/>
<field name="stage_id" ref="project_tt_testing"/>
</record>
<!-- Schedule tasks to assign users and dates -->

View File

@ -161,7 +161,7 @@
</tree>
</field>
</record>
<act_window
context="{'search_default_project_id': [active_id], 'default_project_id': active_id}"
id="act_project_project_2_project_task_all"
@ -305,15 +305,26 @@
<field name="arch" type="xml">
<form string="Project" layout="manual">
<div class="oe_form_topbar">
<button name="action_close" states="pending,open" string="Done" type="object"/>
<button name="do_open" states="pending,draft" string="Start Task" type="object"/>
<button name="%(action_project_task_reevaluate)d" states="done,cancelled" string="Reactivate" type="action" context="{'button_reactivate':True}" />
<button name="do_pending" states="open" string="Pending" type="object"/>
<button name="do_draft" states="open" string="Draft" type="object"/>
<button name="%(action_project_task_delegate)d" states="pending,open,draft" string="Delegate" type="action"/>
<button name="do_cancel" states="draft,open,pending" string="Cancel" type="object"/>
<button name="do_open" string="Start Task" type="object"
states="draft,pending"/>
<button name="%(action_project_task_reevaluate)d" string="Reactivate" type="action"
states="done,cancelled" context="{'button_reactivate':True}"/>
<button name="do_pending" string="Pending" type="object"
states="open"/>
<button name="action_close" string="Done" type="object"
states="draft,open,pending"/>
<button name="do_draft" string="Draft" type="object"
states="cancel,done"/>
<button name="%(action_project_task_delegate)d" string="Delegate" type="action"
states="pending,open,draft"/>
<button name="do_cancel" string="Cancel" type="object"
states="draft,open,pending" />
<button name="stage_previous" string="Previous Stage" type="object"
states="open,pending" icon="gtk-go-back" attrs="{'invisible': [('stage_id','=', False)]}"/>
<button name="stage_next" string="Next Stage" type="object"
states="open,pending" icon="gtk-go-forward" attrs="{'invisible': [('stage_id','=', False)]}"/>
<div class="oe_right">
<field name="state" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}' select="1"/>
<field name="stage_id" nolabel="1" widget="statusbar"/>
</div>
<div class="oe_clear"/>
</div>
@ -362,7 +373,8 @@
<tree string="Delegated tasks">
<field name="name"/>
<field name="user_id"/>
<field name="state"/>
<field name="stage_id"/>
<field name="state" groups="base.group_no_one"/>
<field name="effective_hours" widget="float_time"/>
<field name="progress" widget="progressbar"/>
<field name="remaining_hours" widget="float_time"/>
@ -376,16 +388,13 @@
<separator string="Planning" colspan="2"/>
<field name="priority"/>
<field name="sequence"/>
<field name="state" groups="base.group_no_one"/>
</group>
<separator string="Miscellaneous" colspan="4"/>
<field name="partner_id" />
<field name="company_id" select="1" groups="base.group_multi_company" widget="selection"/>
<group col="4" colspan="2">
<field name="type_id" widget="selection" readonly="1"/>
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/>
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/>
</group>
<field name="state" groups="base.group_no_one"/>
<separator colspan="4" string="Notes"/>
<field colspan="4" name="notes" nolabel="1"/>
</page>
@ -397,22 +406,22 @@
</form>
</field>
</record>
<!-- Project Task Kanban View -->
<record model="ir.ui.view" id="view_task_kanban">
<field name="name">project.task.kanban</field>
<field name="model">project.task</field>
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="type_id" >
<kanban default_group_by="stage_id" >
<field name="color"/>
<field name="priority"/>
<field name="type_id"/>
<field name="stage_id"/>
<field name="user_id"/>
<field name="user_email"/>
<field name="description"/>
<field name="sequence"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
<field name="kanban_state"/>
<field name="remaining_hours" sum="Remaining Time" groups="project.group_time_work_estimation_tasks"/>
<field name="date_deadline"/>
@ -508,11 +517,11 @@
<field name="effective_hours" widget="float_time" sum="Spent Hours" invisible="1"/>
<field name="remaining_hours" widget="float_time" sum="Remaining Hours" on_change="onchange_remaining(remaining_hours,planned_hours)" invisible="context.get('set_visible',False)" groups="project.group_time_work_estimation_tasks"/>
<field name="date_deadline" invisible="context.get('deadline_visible',True)"/>
<field name="type_id" invisible="context.get('set_visible',False)"/>
<field name="stage_id" invisible="context.get('set_visible',False)"/>
<field name="state" invisible="context.get('set_visible',False)" groups="base.group_no_one"/>
<field name="date_start" invisible="1" groups="base.group_no_one"/>
<field name="date_end" invisible="1" groups="base.group_no_one"/>
<field name="progress" widget="progressbar" invisible="context.get('set_visible',False)"/>
<field name="state" invisible="context.get('set_visible',False)"/>
</tree>
</field>
</record>
@ -580,7 +589,7 @@
<separator orientation="vertical"/>
<filter string="Project" name="group_project_id" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/>
<separator orientation="vertical"/>
<filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'type_id'}"/>
<filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Status" name="group_state" icon="terp-stock_effects-object-colorize" domain="[]" context="{'group_by':'state'}"/>
<separator orientation="vertical"/>
<filter string="Deadline" icon="terp-gnome-cpu-frequency-applet+" domain="[]" context="{'group_by':'date_deadline'}"/>
@ -651,7 +660,7 @@
<field name="arch" type="xml">
<search string="Tasks Stages">
<group>
<filter icon="terp-check" string="Common" name="common" domain="[('project_default', '=', 1)]" help="Stages common to all projects"/>
<filter icon="terp-check" string="Common" name="common" domain="[('case_default', '=', 1)]" help="Stages common to all projects"/>
<separator orientation="vertical"/>
<field name="name"/>
</group>
@ -665,10 +674,14 @@
<field name="type">form</field>
<field name="arch" type="xml">
<form string="Task Stage">
<group colspan="4" col="6">
<field name="name"/>
<field name="project_default"/>
<field name="sequence"/>
<group colspan="2" col="2">
<field name="name"/>
<field name="state"/>
</group>
<group colspan="2" col="4">
<field name="case_default"/>
<field name="sequence"/>
<field name="fold"/>
</group>
<separator string="Description" colspan="4"/>
<field colspan="4" name="description" nolabel="1"/>
@ -684,6 +697,7 @@
<tree string="Task Stage">
<field name="sequence"/>
<field name="name"/>
<field name="state"/>
</tree>
</field>
</record>
@ -698,10 +712,10 @@
<menuitem id="menu_tasks_config" name="GTD" parent="project.menu_definitions" sequence="1"/>
<menuitem id="menu_project_config_project" name="Stages" parent="menu_definitions" sequence="1"/>
<menuitem id="menu_project_config_project" name="Stages" parent="project.menu_definitions" sequence="1"/>
<menuitem action="open_task_type_form" id="menu_task_types_view" parent="menu_project_config_project" sequence="2" groups="base.group_no_one"/>
<menuitem action="open_view_project_all" id="menu_projects" name="Projects" parent="menu_project_management" sequence="1"/>
<menuitem action="open_task_type_form" name="Task Stages" id="menu_task_types_view" parent="menu_project_config_project" sequence="2"/>
<menuitem action="open_view_project_all" id="menu_projects" name="Projects" parent="menu_project_management" sequence="1" groups="base.group_no_one"/>
<act_window context="{'search_default_user_id': [active_id], 'default_user_id': active_id}" id="act_res_users_2_project_project" name="User's projects" res_model="project.project" src_model="res.users" view_mode="tree,form" view_type="form"/>

View File

@ -15,7 +15,8 @@
<field name="user_id"/>
<field name="remaining_hours"/>
<field name="kanban_state"/>
<field name="state"/>
<field name="stage_id"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>

View File

@ -82,7 +82,7 @@ class report_project_task_user(osv.osv):
t.name as name,
t.company_id,
t.partner_id,
t.type_id,
t.stage_id,
remaining_hours as remaining_hours,
total_hours as total_hours,
t.delay_hours as hours_delay,
@ -114,7 +114,7 @@ class report_project_task_user(osv.osv):
name,
t.company_id,
t.partner_id,
t.type_id
t.stage_id
""")

View File

@ -214,7 +214,7 @@
<tree string="Task By Days" >
<field name="day"/>
<field name="total_task"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>

View File

@ -40,7 +40,7 @@
I change the stage of task to next stage.
-
!python {model: project.task}: |
self.next_type(cr, uid, [ref("project_task_1")])
self.stage_next(cr, uid, [ref("project_task_1")])
-
!record {model: project.task.reevaluate, id: reevaluate_id}:
remaining_hours : 120
@ -68,9 +68,9 @@
I change the stage of task to previous stage.
-
!python {model: project.task}: |
self.prev_type(cr, uid, [ref("project_task_1")])
self.stage_previous(cr, uid, [ref("project_task_1")])
-
I cancel Task.
-
!python {model: project.task}: |
self.do_cancel(cr, uid, [ref("project_task_2")])
self.do_cancel(cr, uid, [ref("project_task_2")])

View File

@ -38,6 +38,7 @@ and decide on their status as they evolve.
'website': 'http://www.openerp.com',
'images': ['images/issue_analysis.jpeg','images/project_issue.jpeg'],
'depends': [
'base_status',
'crm',
'project',
],

View File

@ -35,7 +35,7 @@
<field name="type">graph</field>
<field name="arch" type="xml">
<graph orientation="vertical" string="Project Issue" type="bar">
<field name="type_id"/>
<field name="stage_id"/>
<field name="nbr" operator="+"/>
<field group="True" name="user_id"/>
</graph>
@ -96,10 +96,10 @@
<field name="partner_id"/>
<field name="project_id" />
<field name="priority" string="Priority"/>
<field name="type_id" widget="selection" readonly="1"/>
<field name="version_id" widget="selection"/>
<field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/>
<field name="state"/>
<field name="stage_id" widget="selection" readonly="1"/>
<field name="state" groups="base.group_no_one"/>
<field name="categ_id" invisible="1"/>
<field name="task_id" invisible="1"/>
</tree>

View File

@ -19,6 +19,7 @@
#
##############################################################################
from base_status.base_stage import base_stage
from crm import crm
from datetime import datetime
from osv import fields,osv
@ -42,12 +43,62 @@ class project_issue_version(osv.osv):
}
project_issue_version()
class project_issue(crm.crm_case, osv.osv):
_ISSUE_STATE= [('draft', 'New'), ('open', 'In Progress'), ('cancel', 'Cancelled'), ('done', 'Done'),('pending', 'Pending')]
class project_issue(base_stage, osv.osv):
_name = "project.issue"
_description = "Project Issue"
_order = "priority, create_date desc"
_inherit = ['ir.needaction_mixin', 'mail.thread']
def _get_default_project_id(self, cr, uid, context=None):
""" Gives default project by checking if present in the context """
return self._resolve_project_id_from_context(cr, uid, context=context)
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
project.
"""
if context is None:
context = {}
if type(context.get('default_project_id')) in (int, long):
return context.get('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)
if len(project_ids) == 1:
return int(project_ids[0][0])
return None
def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
access_rights_uid = access_rights_uid or uid
stage_obj = self.pool.get('project.task.type')
order = stage_obj._order
# lame hack to allow reverting search, should just work in the trivial case
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', '=', False)]
search_domain += ['|', ('id', 'in', ids), '&', ('case_default', '=', True), ('fold', '=', False)]
# perform search
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])))
return result
def _compute_day(self, cr, uid, ids, fields, args, context=None):
"""
@param cr: the current row, from the database cursor,
@ -173,11 +224,13 @@ class project_issue(crm.crm_case, osv.osv):
'partner_id': fields.many2one('res.partner', 'Partner', select=1),
'company_id': fields.many2one('res.company', 'Company'),
'description': fields.text('Description'),
'state': fields.selection([('draft', 'New'), ('cancel', 'Cancelled'), ('open', 'In Progress'),('pending', 'Pending'),('done', 'Done') ], 'Status', 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\'.'),
'state': fields.related('stage_id', 'state', type="selection", store=True,
selection=_ISSUE_STATE, string="State", readonly=True,
help='The state is set to \'Draft\', when a case is created.\
If the case is in progress the state is set to \'Open\'.\
When the case is over, the state is set to \'Done\'.\
If 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.", 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,select=True),
@ -188,7 +241,8 @@ class project_issue(crm.crm_case, osv.osv):
'categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('object_id.model', '=', 'crm.project.bug')]"),
'priority': fields.selection(crm.AVAILABLE_PRIORITIES, 'Priority', select=True),
'version_id': fields.many2one('project.issue.version', 'Version'),
'type_id': fields.many2one ('project.task.type', 'Stages', domain="[('project_ids', '=', project_id)]"),
'stage_id': fields.many2one ('project.task.type', 'Stages',
domain="['|', ('project_ids', '=', project_id), ('case_default', '=', True)]"),
'project_id':fields.many2one('project.project', 'Project'),
'duration': fields.float('Duration'),
'task_id': fields.many2one('project.task', 'Task', domain="[('project_id','=',project_id)]"),
@ -221,15 +275,20 @@ class project_issue(crm.crm_case, osv.osv):
_defaults = {
'active': 1,
'partner_id': crm.crm_case._get_default_partner,
'email_from': crm.crm_case._get_default_email,
'partner_id': lambda s, cr, uid, c: s._get_default_partner(cr, uid, c),
'email_from': lambda s, cr, uid, c: s._get_default_email(cr, uid, c),
'state': 'draft',
'section_id': crm.crm_case._get_section,
'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
'section_id': lambda s, cr, uid, c: s._get_default_section_id(cr, uid, c),
'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],
'categ_id' : lambda *a: False,
}
_group_by_full = {
'stage_id': _read_group_stage_ids
}
def set_priority(self, cr, uid, ids, priority):
"""Set lead priority
"""
@ -312,36 +371,20 @@ 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, 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]})
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]})
return True
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})
return True
def copy(self, cr, uid, id, default=None, context=None):
issue = self.read(cr, uid, id, ['name'], context=context)
if not default:
default = {}
default = default.copy()
default['name'] = issue['name'] + _(' (copy)')
return super(project_issue, self).copy(cr, uid, id, default=default,
context=context)
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']
logged_fields = ['stage_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):
@ -363,15 +406,51 @@ class project_issue(crm.crm_case, osv.osv):
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
# -------------------------------------------------------
# Stage management
# -------------------------------------------------------
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 issue:
- type: stage type must be the same or 'both'
- section_id: if set, stages must belong to this section or
be a default case
"""
if isinstance(cases, (int, long)):
cases = self.browse(cr, uid, cases, context=context)
# collect all section_ids
section_ids = []
if section_id:
section_ids.append(section_id)
for task in cases:
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
def case_cancel(self, cr, uid, ids, context=None):
""" Cancels case """
self.case_set(cr, uid, ids, 'cancelled', {'active': True}, context=context)
self.case_cancel_send_note(cr, uid, ids, context=context)
return True
def case_escalate(self, cr, uid, ids, context=None):
cases = self.browse(cr, uid, ids)
for case in cases:
data = {'state' : 'draft'}
data = {}
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:
@ -380,10 +459,14 @@ class project_issue(crm.crm_case, osv.osv):
self.pool.get('project.task').write(cr, uid, [case.task_id.id], {'project_id': data['project_id'], 'user_id': False})
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.case_escalate_send_note(cr, uid, [case.id], context)
self.case_set(cr, uid, ids, 'draft', data, context=context)
self.case_escalate_send_note(cr, uid, [case.id], context=context)
return True
# -------------------------------------------------------
# Mail gateway
# -------------------------------------------------------
def message_new(self, cr, uid, msg, custom_values=None, context=None):
"""Automatically called when new email message arrives"""
if context is None:
@ -434,7 +517,7 @@ class project_issue(crm.crm_case, osv.osv):
# 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'})
self.case_set(cr, uid, ids, 'open', {}, context=context)
vls = { }
for line in msg['body_text'].split('\n'):
@ -448,15 +531,6 @@ class project_issue(crm.crm_case, osv.osv):
res = self.write(cr, uid, ids, vals)
self.message_append_dict(cr, uid, ids, msg, context=context)
return res
def copy(self, cr, uid, id, default=None, context=None):
issue = self.read(cr, uid, id, ['name'], context=context)
if not default:
default = {}
default = default.copy()
default['name'] = issue['name'] + _(' (copy)')
return super(project_issue, self).copy(cr, uid, id, default=default,
context=context)
# -------------------------------------------------------
# OpenChatter methods and notifications
@ -475,9 +549,15 @@ class project_issue(crm.crm_case, osv.osv):
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 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 case_get_note_msg_prefix(self, cr, uid, id, context=None):
return 'Project issue '
""" Override of default prefix for notifications. """
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.")

View File

@ -7,7 +7,6 @@
<field eval="&quot;5&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_agrolait"/>
<field eval="&quot;open&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field name="categ_id" ref="bug_categ"/>
@ -15,7 +14,7 @@
<field eval="15.0" name="duration"/>
<field eval="&quot;Bug in Accounts module&quot;" name="name"/>
<field eval="&quot;agr@agrolait.com&quot;" name="email_from"/>
<field name="type_id" ref="project.project_tt_specification"/>
<field name="stage_id" ref="project.project_tt_specification"/>
</record>
<record id="crm_case_programnotgivingproperoutput0" model="project.issue">
@ -23,28 +22,26 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_asus"/>
<field eval="&quot;done&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="3.5" name="duration"/>
<field name="categ_id" ref="bug_categ"/>
<field eval="&quot;Program not giving proper output&quot;" name="name"/>
<field name="project_id" ref="project.project_project_22"/>
<field name="type_id" ref="project.project_tt_specification"/>
<field name="stage_id" ref="project.project_tt_specification"/>
</record>
<record id="crm_case_outputincorrect0" model="project.issue">
<field eval="time.strftime('%Y-%m-18 14:30:00')" name="date"/>
<field eval="&quot;4&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field eval="&quot;cancel&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="2.3" name="duration"/>
<field name="categ_id" ref="bug_categ"/>
<field name="project_id" ref="project.project_project_23"/>
<field eval="&quot;Output incorrect&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_development"/>
<field name="stage_id" ref="project.project_tt_development"/>
</record>
<record id="crm_case_problemloadingpage0" model="project.issue">
@ -52,14 +49,13 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_14"/>
<field eval="&quot;cancel&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="4.0" name="duration"/>
<field name="categ_id" ref="bug_categ"/>
<field name="project_id" ref="project.project_project_22"/>
<field eval="&quot;Problem loading page&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_testing"/>
<field name="stage_id" ref="project.project_tt_testing"/>
</record>
<record id="crm_case_pagenotfound0" model="project.issue">
@ -67,14 +63,13 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_desertic_hispafuentes"/>
<field eval="&quot;draft&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="1.0" name="duration"/>
<field name="categ_id" ref="bug_categ"/>
<field name="project_id" ref="project.project_project_22"/>
<field eval="&quot;Page not Found&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_development"/>
<field name="stage_id" ref="project.project_tt_development"/>
</record>
<record id="crm_case_programmingerror0" model="project.issue">
@ -82,14 +77,13 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_5"/>
<field eval="&quot;pending&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="4.0" name="duration"/>
<field name="categ_id" ref="bug_categ"/>
<field name="project_id" ref="project.project_project_22"/>
<field eval="&quot;Programming Error&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_testing"/>
<field name="stage_id" ref="project.project_tt_testing"/>
</record>
<record id="crm_case_logicalerrorinprogram0" model="project.issue">
@ -97,14 +91,13 @@
<field eval="&quot;2&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_6"/>
<field eval="&quot;pending&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="2.0" name="duration"/>
<field name="categ_id" ref="bug_categ"/>
<field name="project_id" ref="project.project_project_9"/>
<field eval="&quot;Logical Error in Program&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_testing"/>
<field name="stage_id" ref="project.project_tt_testing"/>
</record>
<record id="crm_case_constrainterror0" model="project.issue">
@ -112,14 +105,13 @@
<field eval="&quot;2&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_6"/>
<field eval="&quot;pending&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="7.3" name="duration"/>
<field name="categ_id" ref="bug_categ"/>
<field name="project_id" ref="project.project_project_9"/>
<field eval="&quot;Constraint Error&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_testing"/>
<field name="stage_id" ref="project.project_tt_testing"/>
</record>
<record id="crm_case_errorinprogram0" model="project.issue">
@ -127,13 +119,12 @@
<field eval="&quot;2&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field name="partner_id" ref="base.res_partner_5"/>
<field eval="&quot;open&quot;" name="state"/>
<field eval="1" name="active"/>
<field eval="1.3" name="duration"/>
<field name="categ_id" ref="feature_request_categ"/>
<field name="project_id" ref="project.project_project_22"/>
<field eval="&quot;Error in Program&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_testing"/>
<field name="stage_id" ref="project.project_tt_testing"/>
</record>
<record id="crm_case_patcheserrorinprogram0" model="project.issue">
@ -141,14 +132,13 @@
<field eval="&quot;2&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_2"/>
<field eval="&quot;open&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="13.0" name="duration"/>
<field name="categ_id" ref="feature_request_categ"/>
<field name="project_id" ref="project.project_project_9"/>
<field eval="&quot;Patches Error in Program&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_testing"/>
<field name="stage_id" ref="project.project_tt_testing"/>
</record>
<record id="crm_case_newfeaturestobeadded0" model="project.issue">
@ -156,14 +146,13 @@
<field eval="&quot;4&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_maxtor"/>
<field eval="&quot;open&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="3.2" name="duration"/>
<field name="categ_id" ref="feature_request_categ"/>
<field name="project_id" ref="project.project_project_21"/>
<field eval="&quot;New Features To Be Added&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_merge"/>
<field name="stage_id" ref="project.project_tt_merge"/>
</record>
<record id="crm_case_addmenustothemodule0" model="project.issue">
@ -171,7 +160,6 @@
<field eval="&quot;1&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field name="partner_id" ref="base.res_partner_9"/>
<field eval="&quot;done&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="3.0" name="duration"/>
@ -179,7 +167,7 @@
<field name="project_id" ref="project.project_project_21"/>
<field eval="&quot;Add menus to the module&quot;" name="name"/>
<field eval="&quot;info@opensides.be&quot;" name="email_from"/>
<field name="type_id" ref="project.project_tt_development"/>
<field name="stage_id" ref="project.project_tt_development"/>
</record>
<record id="crm_case_includeattendancesheetinproject0" model="project.issue">
@ -187,7 +175,6 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_10"/>
<field eval="&quot;cancel&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="2.0" name="duration"/>
@ -195,7 +182,7 @@
<field name="project_id" ref="project.project_project_9"/>
<field eval="&quot;Include Attendance sheet in Project&quot;" name="name"/>
<field eval="&quot;contact@tecsas.fr&quot;" name="email_from"/>
<field name="type_id" ref="project.project_tt_development"/>
<field name="stage_id" ref="project.project_tt_development"/>
</record>
<record id="crm_case_createnewobject0" model="project.issue">
@ -203,14 +190,13 @@
<field eval="&quot;3&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_6"/>
<field eval="&quot;draft&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="2.45" name="duration"/>
<field name="categ_id" ref="feature_request_categ"/>
<field name="project_id" ref="project.project_project_22"/>
<field eval="&quot;Create new object&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_specification"/>
<field name="stage_id" ref="project.project_tt_specification"/>
</record>
<record id="crm_case_improvereportsinhrms0" model="project.issue">
@ -218,14 +204,13 @@
<field eval="&quot;4&quot;" name="priority"/>
<field name="user_id" ref="base.user_root"/>
<field name="partner_id" ref="base.res_partner_11"/>
<field eval="&quot;pending&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="15.0" name="duration"/>
<field name="categ_id" ref="feature_request_categ"/>
<field name="project_id" ref="project.project_project_22"/>
<field eval="&quot;Improve Reports in HRMS&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_development"/>
<field name="stage_id" ref="project.project_tt_development"/>
</record>
<record id="crm_case_improvereportsinpms0" model="project.issue">
@ -233,14 +218,13 @@
<field eval="&quot;2&quot;" name="priority"/>
<field name="user_id" ref="base.user_demo"/>
<field name="partner_id" ref="base.res_partner_11"/>
<field eval="&quot;pending&quot;" name="state"/>
<field name="section_id" ref="crm.section_sales_department"/>
<field eval="1" name="active"/>
<field eval="06.15" name="duration"/>
<field name="categ_id" ref="feature_request_categ"/>
<field name="project_id" ref="project.project_project_22"/>
<field eval="&quot;Improve Reports in PMS&quot;" name="name"/>
<field name="type_id" ref="project.project_tt_specification"/>
<field name="stage_id" ref="project.project_tt_specification"/>
</record>
</data>

View File

@ -52,14 +52,26 @@
<field name="arch" type="xml">
<form layout="manual">
<div class="oe_form_topbar">
<button name="case_open" string="Open" states="draft,pending" type="object"/>
<button name="case_close" string="Done" states="open,draft,pending" type="object"/>
<button name="case_pending" string="Pending" states="draft,open" type="object"/>
<button name="case_reset" string="Reset to New" states="done,cancel" type="object"/>
<button name="case_escalate" string="Escalate" states="open,draft,pending" type="object"/>
<button name="case_cancel" string="Cancel" states="draft,open,pending" type="object"/>
<button name="case_open" string="Open" type="object"
states="draft,pending"/>
<button name="case_close" string="Done" type="object"
states="draft,open,pending"/>
<button name="case_pending" string="Pending" type="object"
states="draft,open"/>
<button name="case_escalate" string="Escalate" type="object"
states="draft,open,pending"/>
<button name="case_reset" string="Reset to New" type="object"
states="cancelled,done"/>
<button name="stage_previous" string="Previous Stage" type="object"
states="open,pending" icon="gtk-go-back"
attrs="{'invisible': [('stage_id','=', False)]}"/>
<button name="stage_next" string="Next Stage" type="object"
states="open,pending" icon="gtk-go-forward"
attrs="{'invisible': [('stage_id','=', False)]}"/>
<button name="case_cancel" string="Cancel" type="object"
states="draft,open,pending"/>
<div class="oe_right">
<field name="state" nolabel="1" widget="statusbar" statusbar_visible="draft,open,done" statusbar_colors='{"pending":"blue"}'/>
<field name="stage_id" nolabel="1" widget="statusbar"/>
</div>
<div class="oe_clear"/>
</div>
@ -75,11 +87,7 @@
</group>
<field name="user_id"/>
<field name="version_id" colspan="2" widget="selection"/>
<group colspan="2" col="4">
<field name="type_id" string="Stages" />
<button name="prev_type" string="Previous" type="object" icon="gtk-go-back" help="Change to Previous Stage"/>
<button name="next_type" string="Next" type="object" icon="gtk-go-forward" help="Change to Next Stage"/>
</group>
<field name="state" groups="base.group_no_one"/>
</group>
<notebook colspan="4">
<page string="General">
@ -138,11 +146,11 @@
<field name="partner_id"/>
<field name="project_id" />
<field name="priority" string="Priority"/>
<field name="type_id" widget="selection" readonly="1" string="Stages" />
<field name="version_id" widget="selection"/>
<field name="user_id"/>
<field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/>
<field name="state"/>
<field name="stage_id" widget="selection" readonly="1"/>
<field name="state" groups="base.group_no_one"/>
<field name="categ_id" invisible="1"/>
<field name="task_id" invisible="1"/>
</tree>
@ -184,7 +192,7 @@
<filter string="Priority" icon="terp-rating-rated" domain="[]"
context="{'group_by':'priority'}" />
<filter string="Stage" icon="terp-stage" domain="[]"
context="{'group_by':'type_id'}" />
context="{'group_by':'stage_id'}" />
<filter string="Status" icon="terp-stock_effects-object-colorize" domain="[]"
context="{'group_by':'state'}" />
<separator orientation="vertical" />
@ -216,9 +224,9 @@
<field name="model">project.issue</field>
<field name="type">kanban</field>
<field name="arch" type="xml">
<kanban default_group_by="type_id">
<kanban default_group_by="stage_id">
<field name="color"/>
<field name="state"/>
<field name="state" groups="base.group_no_one"/>
<field name="priority"/>
<field name="user_email"/>
<field name="user_id"/>
@ -262,8 +270,7 @@
<a string="Convert To Task" name="convert_issue_task" icon="gtk-index" type="object"/>
</div>
<div class="oe_kanban_right">
<a name="case_open" string="Open" states="draft,pending" type="object" icon="kanban-apply" />
<a name="case_pending" string="Pending" states="draft,open" type="object" icon="kanban-pause"/>
<a name="case_open" string="Open" states="draft,pending" type="object" icon="kanban-apply"/>
<a name="case_close" string="Close" states="open,draft,pending" type="object" icon="kanban-stop"/>
</div>
<div class="oe_kanban_clear"/>
@ -296,10 +303,10 @@
<field name="name" string="Feature description"/>
<field name="partner_id"/>
<field name="priority" string="Priority"/>
<field name="type_id" widget="selection" readonly="1"/>
<field name="version_id"/>
<field name="user_id"/>
<field name="state"/>
<field name="stage_id" widget="selection" readonly="1"/>
<field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>
@ -310,13 +317,13 @@
<field name="type">search</field>
<field name="arch" type="xml">
<search string="Feature Tracker Search">
<filter icon="terp-go-today" string="Today"
<filter icon="terp-go-today" string="Today"
domain="[('date','=',time.strftime('%%Y-%%m-%%d'))]" help="Today's features"/>
<separator orientation="vertical"/>
<group>
<field name="name" string="Feature description"/>
<field name="user_id"/>
<field name="state">
<field name="state" groups="base.group_no_one">
<filter icon="terp-check" domain="[('state','in',('open','draft'))]" help="Current Features" name="current_feature"/>
<filter icon="terp-camera_test" domain="[('state','=','open')]" help="Open Features"/>
</field>

View File

@ -51,7 +51,7 @@ class project_issue_report(osv.osv):
'creation_date': fields.date('Creation Date', readonly=True),
'date_closed': fields.date('Date of Closing', readonly=True),
'categ_id': fields.many2one('crm.case.categ', 'Category', domain="[('section_id','=',section_id),('object_id.model', '=', 'project.issue')]"),
'type_id': fields.many2one('project.task.type', 'Stage'),
'stage_id': fields.many2one('project.task.type', 'Stage'),
'nbr': fields.integer('# of Issues', readonly=True),
'working_hours_open': fields.float('Avg. Working Hours to Open', readonly=True),
'working_hours_close': fields.float('Avg. Working Hours to Close', readonly=True),
@ -87,7 +87,7 @@ class project_issue_report(osv.osv):
c.working_hours_close,
c.section_id,
c.categ_id,
c.type_id,
c.stage_id,
to_char(c.date_closed, 'YYYY-mm-dd') as date_closed,
c.company_id as company_id,
c.priority as priority,

View File

@ -11,7 +11,7 @@
<field name="name" invisible="1"/>
<field name="month" invisible="1"/>
<field name="project_id" invisible="1"/>
<field name="type_id" invisible="1"/>
<field name="stage_id" invisible="1"/>
<field name="version_id" string="Version" invisible="1"/>
<field name="priority" invisible="1"/>
<field name="company_id" invisible="1" groups="base.group_multi_company"/>
@ -103,7 +103,7 @@
<filter string="Version" icon="terp-stock_symbol-selection" domain="[]" context="{'group_by':'version_id'}"/>
<separator orientation="vertical" />
<filter string="Priority" icon="terp-rating-rated" domain="[]" context="{'group_by':'priority'}" />
<filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'type_id'}"/>
<filter string="Stage" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
<filter string="Status" icon="terp-stock_effects-object-colorize"
domain="[]" context="{'group_by':'state'}" />
<separator orientation="vertical"/>

View File

@ -7,7 +7,7 @@
I check the issue is in cancel state.
-
!assert {model: project.issue, id: crm_case_buginaccountsmodule0, severity: error, string: Issue is in cancel state}:
- state == 'cancel'
- state == 'cancelled'
-
I re-open the Issue.
-
@ -37,7 +37,7 @@
I check the issue is in cancel state.
-
!assert {model: project.issue, id: crm_case_buginaccountsmodule0, severity: error, string: Issue is in cancel state}:
- state == 'cancel'
- state == 'cancelled'
-
I close Issue.
-
@ -57,4 +57,4 @@
I check the issue is in cancel state.
-
!assert {model: project.issue, id: crm_case_buginaccountsmodule0, severity: error, string: Issue is in cancel state}:
- state == 'cancel'
- state == 'cancelled'

View File

@ -330,7 +330,7 @@
<field name="type">search</field>
<field name="inherit_id" ref="project.view_task_search_form"/>
<field name="arch" type="xml">
<field name="project_id" position="after">
<field name="user_id" position="before">
<field name="phase_id" select="1"/>
</field>
</field>

View File

@ -129,7 +129,7 @@
<field name="sequence"/>
</group>
<separator string="Miscellaneous" colspan="4"/>
<field name="type_id"/>
<field name="stage_id"/>
<field name="active"/>
<field name="partner_id"/>
<separator colspan="4" string="Notes"/>

View File

@ -265,7 +265,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Review all English Terms</field>
<field name="state">done</field>
<field eval="time.strftime('%Y-%m-%d')" name="date_end"/>
<field name="product_backlog_id" ref="scrum_product_backlog_3"/>
</record>
@ -274,7 +273,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Review all french terms</field>
<field name="state">done</field>
<field eval="time.strftime('%Y-%m-%d')" name="date_end"/>
<field name="product_backlog_id" ref="scrum_product_backlog_3"/>
</record>
@ -283,7 +281,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Analytic Accounting features</field>
<field name="state">done</field>
<field eval="time.strftime('%Y-%m-%d')" name="date_end"/>
<field name="product_backlog_id" ref="scrum_product_backlog_1"/>
</record>
@ -292,7 +289,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Test and improve automatic migration system</field>
<field name="state">open</field>
<field name="product_backlog_id" ref="scrum_product_backlog_0"/>
</record>
<record id="scrum_task_7" model="project.task">
@ -300,7 +296,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">General accounting features</field>
<field name="state">open</field>
<field name="product_backlog_id" ref="scrum_product_backlog_1"/>
</record>
<record id="scrum_task_8" model="project.task">
@ -316,7 +311,6 @@
<field name="project_id" ref="project.project_project_9"/>
<field name="description">Cash book, general ledger, accounts list, aged trial balance</field>
<field name="name">Accounting Report General</field>
<field name="state">open</field>
<field eval="[(6,0,[ref('scrum_task_8')])]" name="child_ids"/>
<field name="product_backlog_id" ref="scrum_product_backlog_1"/>
</record>
@ -332,7 +326,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Accounting Report Analytic</field>
<field name="state">open</field>
<field name="product_backlog_id" ref="scrum_product_backlog_1"/>
</record>
<record id="scrum_task_12" model="project.task">
@ -340,7 +333,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Bugfix - memory leak</field>
<field name="state">done</field>
<field eval="time.strftime('%Y-%m-%d')" name="date_end"/>
<field name="product_backlog_id" ref="scrum_product_backlog_4"/>
</record>
@ -349,7 +341,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Bugfix - Translations</field>
<field name="state">open</field>
<field eval="[(6,0,[ref('scrum_task_12')])]" name="child_ids"/>
<field name="product_backlog_id" ref="scrum_product_backlog_4"/>
</record>
@ -358,7 +349,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Finish the automated plugin system</field>
<field name="state">pending</field>
<field name="product_backlog_id" ref="scrum_product_backlog_2"/>
</record>
<record id="scrum_task_15" model="project.task">
@ -373,7 +363,6 @@
<field model="res.users" name="user_id" search="[('login','=','demo')]"/>
<field name="project_id" ref="project.project_project_9"/>
<field name="name">Test 3.2.0 before releasing</field>
<field name="state">open</field>
<field name="product_backlog_id" ref="scrum_product_backlog_5"/>
</record>
<record id="scrum_task_17" model="project.task">
@ -382,7 +371,6 @@
<field name="project_id" ref="project.project_project_9"/>
<field name="description">default values, onchange, required, add on top or bottom and shortcuts (Ctrl S, Ctrl X, ...)</field>
<field name="name">Editable trees</field>
<field name="state">open</field>
<field name="product_backlog_id" ref="scrum_product_backlog_7"/>
</record>

View File

@ -102,7 +102,7 @@
<field name="type_id" invisible="context.get('set_visible',False)"/>
<field name="date_start" invisible="1"/>
<field name="date_end" invisible="1"/>
<button name="next_type" invisible="context.get('set_visible',False)"
<button name="stage_next" invisible="context.get('set_visible',False)"
states="draft,open,pending"
string="Change Stage"
type="object"
@ -568,7 +568,7 @@
<field name="type">search</field>
<field name="inherit_id" ref="project.view_task_search_form"/>
<field name="arch" type="xml">
<field name="project_id" position="after">
<field name="user_id" position="before">
<field name="sprint_id" context="{'sprint_invisible':False}">
<filter icon="terp-check" context="{'sprint_invisible':False}" domain="[('sprint_id.state','in',('draft','open'))]" help="Current Sprints"/>
<filter icon="gtk-find" context="{'sprint_invisible':False}" domain="[]" help="View Sprints"/>