summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThibault Delavallée <tde@openerp.com>2012-06-01 12:14:26 +0200
committerThibault Delavallée <tde@openerp.com>2012-06-01 12:14:26 +0200
commitf9e6b60c9f48c48d7b0d2a9b67a3e2376e032fd2 (patch)
treec602d1e223affb7b04a8c2cc0189fb4405441854
parentb98e0bdf14d4537f338fcee4fe2f14bdf76ac9ce (diff)
parentd988ca61568707ee4c9dca9288ff269dca9c77a6 (diff)
[MERGE] Merged branch holding state/stage cleaning for lead/opportunities, hr_recruitment, project_issue, tasks, crm_claim. Those models now use stage as the main status flow; the state is a related field to the stage, and is not important anymore. Updated views accordingly (buttons, statusbar on stage_id). Also updated view of crm_meeting, crm_helpdesk and crm_phonecall. Old crm_base and crm_case are nos distinct from crm, and are in a new addon called 'base_status'. This module defines most of the methods related to the logic; models inheriting from base_state or base_stage have few methods to override to implement a particular behavior (mainly stage_find for base_stage).
bzr revid: tde@openerp.com-20120601101426-28e6gnsywhkxeber
-rw-r--r--addons/base_status/__init__.py25
-rw-r--r--addons/base_status/__openerp__.py42
-rw-r--r--addons/base_status/base_stage.py432
-rw-r--r--addons/base_status/base_state.py194
-rw-r--r--addons/crm/__openerp__.py2
-rw-r--r--addons/crm/board_crm_view.xml20
-rw-r--r--addons/crm/crm.py460
-rw-r--r--addons/crm/crm_lead.py248
-rw-r--r--addons/crm/crm_lead_data.xml56
-rw-r--r--addons/crm/crm_lead_demo.xml172
-rw-r--r--addons/crm/crm_lead_view.xml353
-rw-r--r--addons/crm/crm_meeting.py75
-rw-r--r--addons/crm/crm_meeting_view.xml35
-rw-r--r--addons/crm/crm_phonecall.py72
-rw-r--r--addons/crm/crm_phonecall_view.xml27
-rw-r--r--addons/crm/crm_view.xml5
-rw-r--r--addons/crm/test/process/cancel_lead.yml34
-rw-r--r--addons/crm/test/process/lead2opportunity2win.yml24
-rw-r--r--addons/crm_claim/crm_claim.py144
-rw-r--r--addons/crm_claim/crm_claim_data.xml34
-rw-r--r--addons/crm_claim/crm_claim_demo.xml7
-rw-r--r--addons/crm_claim/crm_claim_menu.xml5
-rw-r--r--addons/crm_claim/crm_claim_view.xml80
-rw-r--r--addons/crm_claim/security/ir.model.access.csv1
-rw-r--r--addons/crm_claim/test/process/claim.yml2
-rw-r--r--addons/crm_fundraising/crm_fundraising.py82
-rw-r--r--addons/crm_fundraising/crm_fundraising_demo.xml8
-rw-r--r--addons/crm_helpdesk/crm_helpdesk.py22
-rw-r--r--addons/crm_helpdesk/crm_helpdesk_view.xml50
-rw-r--r--addons/hr_recruitment/__openerp__.py9
-rw-r--r--addons/hr_recruitment/board_hr_recruitment_statistical_view.xml2
-rw-r--r--addons/hr_recruitment/hr_recruitment.py183
-rw-r--r--addons/hr_recruitment/hr_recruitment_data.xml12
-rw-r--r--addons/hr_recruitment/hr_recruitment_view.xml45
-rw-r--r--addons/hr_recruitment/test/recruitment_process.yml33
-rw-r--r--addons/project/__openerp__.py2
-rw-r--r--addons/project/board_project_view.xml27
-rw-r--r--addons/project/project.py317
-rw-r--r--addons/project/project_data.xml28
-rw-r--r--addons/project/project_demo.xml54
-rw-r--r--addons/project/project_view.xml93
-rw-r--r--addons/project/report/project_cumulative.xml3
-rw-r--r--addons/project/report/project_report.py4
-rw-r--r--addons/project/report/project_report_view.xml2
-rw-r--r--addons/project/test/task_process.yml6
-rw-r--r--addons/project_issue/__openerp__.py1
-rw-r--r--addons/project_issue/board_project_issue_view.xml26
-rw-r--r--addons/project_issue/project_issue.py190
-rw-r--r--addons/project_issue/project_issue_demo.xml48
-rw-r--r--addons/project_issue/project_issue_view.xml93
-rw-r--r--addons/project_issue/report/project_issue_report.py4
-rw-r--r--addons/project_issue/report/project_issue_report_view.xml4
-rw-r--r--addons/project_issue/test/cancel_issue.yml6
-rw-r--r--addons/project_long_term/project_long_term_view.xml2
-rw-r--r--addons/project_planning/project_planning_view.xml2
-rw-r--r--addons/project_scrum/project_scrum_demo.xml12
-rw-r--r--addons/project_scrum/project_scrum_view.xml4
57 files changed, 2290 insertions, 1633 deletions
diff --git a/addons/base_status/__init__.py b/addons/base_status/__init__.py
new file mode 100644
index 00000000000..e4107f90a37
--- /dev/null
+++ b/addons/base_status/__init__.py
@@ -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:
diff --git a/addons/base_status/__openerp__.py b/addons/base_status/__openerp__.py
new file mode 100644
index 00000000000..fcb8f54a7ee
--- /dev/null
+++ b/addons/base_status/__openerp__.py
@@ -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:
diff --git a/addons/base_status/base_stage.py b/addons/base_status/base_stage.py
new file mode 100644
index 00000000000..cc6dda19cdf
--- /dev/null
+++ b/addons/base_status/base_stage.py
@@ -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
diff --git a/addons/base_status/base_state.py b/addons/base_status/base_state.py
new file mode 100644
index 00000000000..5baf71a01b6
--- /dev/null
+++ b/addons/base_status/base_state.py
@@ -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
diff --git a/addons/crm/__openerp__.py b/addons/crm/__openerp__.py
index 2049b3a8cc9..0c9b75b4d41 100644
--- a/addons/crm/__openerp__.py
+++ b/addons/crm/__openerp__.py
@@ -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',
diff --git a/addons/crm/board_crm_view.xml b/addons/crm/board_crm_view.xml
index b8e05b68cf5..524a49ef025 100644
--- a/addons/crm/board_crm_view.xml
+++ b/addons/crm/board_crm_view.xml
@@ -19,19 +19,13 @@
<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"/>
- <button name="case_open" string="Open"
- states="draft,pending" type="object"
- icon="gtk-go-forward" />
- <button name="case_pending" string="Pending"
- states="open,draft" type="object"
- icon="gtk-media-pause" />
- <button name="case_mark_lost" string="Lost"
- states="open,pending" type="object"
- icon="gtk-cancel" />
- <button name="case_close" string="Won"
- states="open,draft,pending" type="object"
- icon="gtk-apply" />
+ <field name="state" groups="base.group_no_one"/>
+ <button name="case_open" string="Open" type="object"
+ states="draft,pending" icon="gtk-go-forward"/>
+ <button name="case_mark_won" string="Won" type="object"
+ states="open,pending" icon="gtk-apply"/>
+ <button name="case_mark_lost" string="Lost" type="object"
+ states="open,pending" icon="gtk-cancel"/>
</tree>
</field>
</record>
diff --git a/addons/crm/crm.py b/addons/crm/crm.py
index eb6314c5103..65af3f9e530 100644
--- a/addons/crm/crm.py
+++ b/addons/crm/crm.py
@@ -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"""
diff --git a/addons/crm/crm_lead.py b/addons/crm/crm_lead.py
index bf20deb0ce5..ee6b9835f02 100644
--- a/addons/crm/crm_lead.py
+++ b/addons/crm/crm_lead.py
@@ -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 binascii
+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
-from tools.translate import _
-from crm import crm_case
-import binascii
import tools
-from mail.mail_message import to_email
+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
+ """
+ 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 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
"""
- 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)])
-
- if ids:
- return ids[0]
+ 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]
diff --git a/addons/crm/crm_lead_data.xml b/addons/crm/crm_lead_data.xml
index aaa00307bfe..306a35924a9 100644
--- a/addons/crm/crm_lead_data.xml
+++ b/addons/crm/crm_lead_data.xml
@@ -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">Opportunity</field>
+ <field eval="1" name="case_default"/>
+ <field name="state">open</field>
+ <field eval="'20'" name="probability"/>
+ <field eval="'11'" name="sequence"/>
+ <field name="type">lead</field>
+ </record>
+ <record model="crm.case.stage" id="stage_lead3">
<field name="name">Qualification</field>
<field eval="1" name="case_default"/>
+ <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_lead3">
+ <record model="crm.case.stage" id="stage_lead4">
<field name="name">Proposition</field>
<field eval="1" name="case_default"/>
+ <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_lead4">
+ <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_lead5">
+ <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 -->
diff --git a/addons/crm/crm_lead_demo.xml b/addons/crm/crm_lead_demo.xml
index 19987f8f61a..21c7f9e31ab 100644
--- a/addons/crm/crm_lead_demo.xml
+++ b/addons/crm/crm_lead_demo.xml
@@ -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"/>
diff --git a/addons/crm/crm_lead_view.xml b/addons/crm/crm_lead_view.xml
index 3c2b8f48eb6..7f921a35bfe 100644
--- a/addons/crm/crm_lead_view.xml
+++ b/addons/crm/crm_lead_view.xml
@@ -1,35 +1,41 @@
<?xml version="1.0"?>
<openerp>
-<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>
-
- <!--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>
+ <data>
+
+ <!--
+ CRM CASE STAGE
+ -->
+
+ <!-- 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,72 +159,55 @@
</div>
</form>
</field>
- </record>
-
- <!-- 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="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"/>
- <button name="stage_previous" string="Previous Stage"
- states="open,pending" type="object" icon="gtk-go-back" />
- <button name="stage_next" string="Next Stage"
- states="open,pending" type="object"
- icon="gtk-go-forward" />
- <field name="section_id" invisible="context.get('invisible_section', True)" />
- <field name="user_id" />
- <field name="state" />
- <button name="case_open" string="Open"
- states="draft,pending" type="object"
- icon="gtk-go-forward" />
- <button name="case_close" string="Close"
- states="open,draft,pending" type="object"
- icon="gtk-close" />
- <button name="%(crm.action_crm_lead2opportunity_partner)d"
- string="Convert to Opportunity"
- states="draft,open,pending" icon="gtk-index"
- type="action" />
- <button name="case_escalate" string="Escalate"
- states="open,draft,pending" type="object"
- icon="gtk-go-up" />
- </tree>
- </field>
- </record>
+ </record>
- <!-- CRM Lead Calendar 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"/>
+ <button name="%(crm.action_crm_lead2opportunity_partner)d" string="Convert to Opportunity" type="action"
+ states="draft,open,pending" icon="gtk-go-forward"/>
+ <button name="case_escalate" string="Escalate" type="object"
+ states="draft,open,pending" icon="gtk-go-up"/>
+ <button name="case_cancel" string="Dead" type="object"
+ states="open,draft,pending" icon="gtk-cancel"/>
+ </tree>
+ </field>
+ </record>
- <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">
@@ -235,7 +216,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"/>
@@ -316,7 +297,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>
@@ -391,6 +371,10 @@
</record>
+ <!--
+ OPPORTUNITY
+ -->
+
<!-- Opportunities Form View -->
<record model="ir.ui.view" id="crm_case_form_view_oppor">
<field name="name">Opportunities</field>
@@ -400,15 +384,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>
@@ -461,20 +453,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"/>
@@ -534,41 +514,41 @@
</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"/>
- <button name="stage_previous" string="Previous Stage" states="open,pending" type="object" icon="gtk-go-back" />
- <button name="stage_next" string="Next Stage" states="open,pending" type="object" icon="gtk-go-forward" />
- <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"/>
- <button name="case_open" string="Open" states="draft,pending" type="object" icon="gtk-go-forward" />
- <button name="case_pending" string="Pending" states="open,draft" type="object" icon="gtk-media-pause" />
- <button name="case_mark_lost" string="Lost" states="open,pending" type="object" icon="gtk-cancel" />
- <button name="case_mark_won" string="Won" states="open,pending" type="object" icon="gtk-apply" />
- </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"/>
+ <button name="case_open" string="Open" type="object"
+ states="draft,pending" icon="gtk-go-forward"/>
+ <button name="case_mark_won" string="Won" type="object"
+ states="open,pending" icon="gtk-apply"/>
+ <button name="case_mark_lost" string="Lost" type="object"
+ states="open,pending" icon="gtk-cancel"/>
+ </tree>
+ </field>
+ </record>
<!-- Opportunities Search View -->
@@ -599,8 +579,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}"
@@ -630,19 +609,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>
diff --git a/addons/crm/crm_meeting.py b/addons/crm/crm_meeting.py
index a0e4803cde7..740db3d967f 100644
--- a/addons/crm/crm_meeting.py
+++ b/addons/crm/crm_meeting.py
@@ -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_lead(base_stage, osv.osv):
+ """ CRM Leads """
+ _name = 'crm.lead'
-class crm_meeting(crm_base, osv.osv):
- """ CRM Meeting Cases """
-
+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 user’s 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 """
diff --git a/addons/crm/crm_meeting_view.xml b/addons/crm/crm_meeting_view.xml
index 5b0a35a185e..1d1422ee285 100644
--- a/addons/crm/crm_meeting_view.xml
+++ b/addons/crm/crm_meeting_view.xml
@@ -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>
diff --git a/addons/crm/crm_phonecall.py b/addons/crm/crm_phonecall.py
index 309102ec366..8c7c3fe72df 100644
--- a/addons/crm/crm_phonecall.py
+++ b/addons/crm/crm_phonecall.py
@@ -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:
diff --git a/addons/crm/crm_phonecall_view.xml b/addons/crm/crm_phonecall_view.xml
index 55d8d8c9a64..788b69f8747 100644
--- a/addons/crm/crm_phonecall_view.xml
+++ b/addons/crm/crm_phonecall_view.xml
@@ -43,10 +43,14 @@
states="open,pending" icon="gtk-redo"
name="action_make_meeting" type="object" />
<field name="state"/>
- <button name="case_cancel" string="Cancel" states="open,pending" type="object" icon="gtk-cancel"/>
- <button name="case_open" string="Todo" states="pending" type="object" icon="gtk-go-forward"/>
- <button name="case_close" string="Held" states="open,pending" type="object" icon="gtk-jump-to"/>
- <button name="case_pending" string="Not Held" states="open" type="object" icon="gtk-media-pause"/>
+ <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_cancel" string="Cancel" type="object"
+ states="draft,open,pending" icon="gtk-cancel"/>
+ <button name="case_reset" string="Reset to Todo" type="object"
+ states="cancel" icon="gtk-convert"/>
</tree>
</field>
</record>
@@ -60,13 +64,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>
diff --git a/addons/crm/crm_view.xml b/addons/crm/crm_view.xml
index dd24ef38911..c8f019e685b 100644
--- a/addons/crm/crm_view.xml
+++ b/addons/crm/crm_view.xml
@@ -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"/>
diff --git a/addons/crm/test/process/cancel_lead.yml b/addons/crm/test/process/cancel_lead.yml
index ca5f7d0174a..34dd8310022 100644
--- a/addons/crm/test/process/cancel_lead.yml
+++ b/addons/crm/test/process/cancel_lead.yml
@@ -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!'
diff --git a/addons/crm/test/process/lead2opportunity2win.yml b/addons/crm/test/process/lead2opportunity2win.yml
index a5b6c421dbb..c1caf394c2e 100644
--- a/addons/crm/test/process/lead2opportunity2win.yml
+++ b/addons/crm/test/process/lead2opportunity2win.yml
@@ -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.
-
@@ -97,6 +96,19 @@
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.
-
!python {model: crm.meeting}: |
diff --git a/addons/crm_claim/crm_claim.py b/addons/crm_claim/crm_claim.py
index 1b6d386b0cc..c4e19a19dfd 100644
--- a/addons/crm_claim/crm_claim.py
+++ b/addons/crm_claim/crm_claim.py
@@ -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 base_status.base_stage import base_stage
+import binascii
from crm import crm
-import time
from crm import wizard
-import binascii
+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 = {
diff --git a/addons/crm_claim/crm_claim_data.xml b/addons/crm_claim/crm_claim_data.xml
index 5b3a2b0ed2a..33bd280c364 100644
--- a/addons/crm_claim/crm_claim_data.xml
+++ b/addons/crm_claim/crm_claim_data.xml
@@ -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.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 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>
+
</data>
</openerp>
diff --git a/addons/crm_claim/crm_claim_demo.xml b/addons/crm_claim/crm_claim_demo.xml
index 592e655b489..0e368bcdffa 100644
--- a/addons/crm_claim/crm_claim_demo.xml
+++ b/addons/crm_claim/crm_claim_demo.xml
@@ -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"/>
diff --git a/addons/crm_claim/crm_claim_menu.xml b/addons/crm_claim/crm_claim_menu.xml
index 734302fa7f9..75bfd9ce211 100644
--- a/addons/crm_claim/crm_claim_menu.xml
+++ b/addons/crm_claim/crm_claim_menu.xml
@@ -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>
diff --git a/addons/crm_claim/crm_claim_view.xml b/addons/crm_claim/crm_claim_view.xml
index 57a8827d528..c6e7db167a8 100644
--- a/addons/crm_claim/crm_claim_view.xml
+++ b/addons/crm_claim/crm_claim_view.xml
@@ -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,19 +72,19 @@
<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"/>
- <button name="case_open" string="Open"
- states="draft,pending" type="object"
- icon="gtk-go-forward" />
- <button name="case_close" string="Close"
- states="open,draft,pending" type="object"
- icon="gtk-close" />
+ <field name="state" groups="base.group_no_one"/>
+ <button name="case_open" string="Open" type="object"
+ states="draft,pending" icon="gtk-go-forward"/>
+ <button name="case_close" string="Done" type="object"
+ states="draft,open,pending" icon="gtk-close"/>
+ <button name="case_refuse" string="Refuse" type="object"
+ states="draft,open,pending" icon="gtk-cancel"/>
</tree>
</field>
</record>
@@ -68,13 +96,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>
@@ -86,11 +124,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"/>
@@ -227,7 +261,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"
diff --git a/addons/crm_claim/security/ir.model.access.csv b/addons/crm_claim/security/ir.model.access.csv
index 5a05fbd4d87..303624288ce 100644
--- a/addons/crm_claim/security/ir.model.access.csv
+++ b/addons/crm_claim/security/ir.model.access.csv
@@ -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
diff --git a/addons/crm_claim/test/process/claim.yml b/addons/crm_claim/test/process/claim.yml
index e20436cd3c6..45ca7cf2962 100644
--- a/addons/crm_claim/test/process/claim.yml
+++ b/addons/crm_claim/test/process/claim.yml
@@ -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.
-
diff --git a/addons/crm_fundraising/crm_fundraising.py b/addons/crm_fundraising/crm_fundraising.py
index 90014188777..34b5f20203c 100644
--- a/addons/crm_fundraising/crm_fundraising.py
+++ b/addons/crm_fundraising/crm_fundraising.py
@@ -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:
diff --git a/addons/crm_fundraising/crm_fundraising_demo.xml b/addons/crm_fundraising/crm_fundraising_demo.xml
index 52b4094d126..70b32afabf3 100644
--- a/addons/crm_fundraising/crm_fundraising_demo.xml
+++ b/addons/crm_fundraising/crm_fundraising_demo.xml
@@ -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"/>
diff --git a/addons/crm_helpdesk/crm_helpdesk.py b/addons/crm_helpdesk/crm_helpdesk.py
index d5ee8efc17e..c77fe384fbe 100644
--- a/addons/crm_helpdesk/crm_helpdesk.py
+++ b/addons/crm_helpdesk/crm_helpdesk.py
@@ -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:
diff --git a/addons/crm_helpdesk/crm_helpdesk_view.xml b/addons/crm_helpdesk/crm_helpdesk_view.xml
index b1d5a2b96f8..8030237f3da 100644
--- a/addons/crm_helpdesk/crm_helpdesk_view.xml
+++ b/addons/crm_helpdesk/crm_helpdesk_view.xml
@@ -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>
@@ -147,23 +156,18 @@
<field name="section_id"/>
<field name="priority"/>
<field name="state"/>
- <button name="case_cancel" string="Cancel"
- states="draft,open,pending" type="object"
- icon="gtk-cancel" />
- <button name="case_open" string="Open"
- states="draft,pending" type="object"
- icon="gtk-go-forward" />
- <button name="case_pending" string="Pending"
- states="draft,open" type="object"
- icon="gtk-media-pause" />
- <button name="case_escalate" string="Escalate"
- states="open,draft,pending" type="object"
- icon="gtk-go-up" />
- <button name="case_close" string="Done"
- states="open,draft,pending" type="object"
- icon="gtk-close" />
- <button name="case_reset" string="Reset to Draft"
- states="done,cancel" type="object" icon="gtk-convert" />
+ <button name="case_open" string="Open" type="object"
+ states="draft,pending" icon="gtk-go-forward"/>
+ <button name="case_close" string="Done" type="object"
+ states="open,draft,pending" icon="gtk-close"/>
+ <button name="case_pending" string="Pending" type="object"
+ states="draft,open" icon="gtk-media-pause"/>
+ <button name="case_escalate" string="Escalate" type="object"
+ states="open,draft,pending" icon="gtk-go-up"/>
+ <button name="case_cancel" string="Cancel" type="object"
+ states="draft,open,pending" icon="gtk-cancel"/>
+ <button name="case_reset" string="Reset to Draft" type="object"
+ states="cancel,done" icon="gtk-convert" />
</tree>
</field>
</record>
diff --git a/addons/hr_recruitment/__openerp__.py b/addons/hr_recruitment/__openerp__.py
index 4a29befbfaf..b83a482e1c1 100644
--- a/addons/hr_recruitment/__openerp__.py
+++ b/addons/hr_recruitment/__openerp__.py
@@ -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',
diff --git a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml b/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml
index 8af4b0f8553..e9054416eea 100644
--- a/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml
+++ b/addons/hr_recruitment/board_hr_recruitment_statistical_view.xml
@@ -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>
diff --git a/addons/hr_recruitment/hr_recruitment.py b/addons/hr_recruitment/hr_recruitment.py
index 505509058e1..c77236cc456 100644
--- a/addons/hr_recruitment/hr_recruitment.py
+++ b/addons/hr_recruitment/hr_recruitment.py
@@ -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 user’s 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 user’s 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:
diff --git a/addons/hr_recruitment/hr_recruitment_data.xml b/addons/hr_recruitment/hr_recruitment_data.xml
index d6cb5952d38..8cbebda5f1f 100644
--- a/addons/hr_recruitment/hr_recruitment_data.xml
+++ b/addons/hr_recruitment/hr_recruitment_data.xml
@@ -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>
diff --git a/addons/hr_recruitment/hr_recruitment_view.xml b/addons/hr_recruitment/hr_recruitment_view.xml
index 5c8b560b380..16aceee070a 100644
--- a/addons/hr_recruitment/hr_recruitment_view.xml
+++ b/addons/hr_recruitment/hr_recruitment_view.xml
@@ -62,9 +62,9 @@
<field name="availability" invisible="1"/>
<field name="department_id" invisible="context.get('invisible_department', True)"/>
<field name="user_id"/>
- <field name="state"/>
- <button name="case_open" string="In Progress" states="draft,pending" type="object" icon="gtk-go-forward"/>
- <button name="case_pending" string="Pending" states="open" type="object" icon="gtk-media-pause"/>
+ <field name="state" groups="base.group_no_one"/>
+ <button name="case_open" string="In Progress" states="draft,pending" type="object" icon="gtk-go-forward" groups="base.group_no_one"/>
+ <button name="case_pending" string="Pending" states="draft,open" type="object" icon="gtk-media-pause" groups="base.group_no_one"/>
</tree>
</field>
</record>
@@ -76,14 +76,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">
@@ -96,11 +106,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"/>
@@ -139,6 +145,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"/>
@@ -241,7 +254,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"/>
@@ -360,6 +373,7 @@
<field name="sequence" invisible="1"/>
<field name="name"/>
<field name="department_id"/>
+ <field name="state"/>
</tree>
</field>
</record>
@@ -377,6 +391,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"/>
diff --git a/addons/hr_recruitment/test/recruitment_process.yml b/addons/hr_recruitment/test/recruitment_process.yml
index e856747ed99..38b84fccab2 100644
--- a/addons/hr_recruitment/test/recruitment_process.yml
+++ b/addons/hr_recruitment/test/recruitment_process.yml
@@ -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,28 +9,43 @@
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
-
!python {model: hr.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.
diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py
index ef26425d3c9..2c53fc2a28a 100644
--- a/addons/project/__openerp__.py
+++ b/addons/project/__openerp__.py
@@ -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.
======================================================================================
diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml
index fa6816ebbf7..1f06ea6938c 100644
--- a/addons/project/board_project_view.xml
+++ b/addons/project/board_project_view.xml
@@ -17,11 +17,16 @@
<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)"/>
- <button name="do_cancel" states="draft,open,pending" string="Cancel" type="object" icon="gtk-cancel" help="For cancelling the task"/>
- <button name="do_open" states="pending,draft,done,cancel" string="Start Task" type="object" icon="gtk-execute" help="For changing to open state" invisible="context.get('set_visible',False)"/>
- <button name="%(action_project_task_delegate)d" states="pending,open,draft" string="Delegate" type="action" icon="gtk-sort-descending" help="For changing to delegate state"/>
- <button name="action_close" states="draft,pending,open" string="Done" type="object" icon="terp-dialog-close" help="For changing to done state"/>
+ <field name="stage_id" invisible="context.get('set_visible',False)"/>
+ <field name="state" invisible="context.get('set_visible',False)" groups="base.group_no_one"/>
+ <button name="do_open" string="Start Task" type="object"
+ states="draft,pending" icon="gtk-go-forward" invisible="context.get('set_visible',False)"/>
+ <button name="action_close" string="Done" type="object"
+ states="draft,open,pending" icon="gtk-apply" invisible="context.get('set_visible',False)"/>
+ <button name="do_cancel" string="Cancel" type="object"
+ states="open,draft,pending" icon="gtk-cancel" invisible="context.get('set_visible',False)"/>
+ <button name="%(action_project_task_delegate)d" string="Delegate" type="action"
+ states="pending,open,draft" icon="gtk-sort-descending"/>
</tree>
</field>
</record>
@@ -39,10 +44,14 @@
<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)"/>
- <button name="do_cancel" states="draft,open,pending" string="Cancel" type="object" icon="gtk-cancel" help="For cancelling the task"/>
- <button name="do_open" states="pending,draft,done,cancel" string="Start Task" type="object" icon="gtk-execute" help="For changing to open state" invisible="context.get('set_visible',False)"/>
- <button name="action_close" states="draft,pending,open" string="Done" type="object" icon="gtk-apply" help="For changing to done state"/>
+ <field name="stage_id" invisible="context.get('set_visible',False)"/>
+ <field name="state" invisible="context.get('set_visible',False)" groups="base.group_no_one"/>
+ <button name="do_open" string="Start Task" type="object"
+ states="draft,pending" icon="gtk-go-forward" invisible="context.get('set_visible',False)"/>
+ <button name="action_close" string="Done" type="object"
+ states="draft,open,pending" icon="gtk-apply" invisible="context.get('set_visible',False)"/>
+ <button name="do_cancel" string="Cancel" type="object"
+ states="open,draft,pending" icon="gtk-cancel" invisible="context.get('set_visible',False)"/>
</tree>
</field>
</record>
diff --git a/addons/project/project.py b/addons/project/project.py
index 0b23196a67f..fa3e0c33f4d 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -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()
+
diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml
index b7b59911fa4..4c95373d91f 100644
--- a/addons/project/project_data.xml
+++ b/addons/project/project_data.xml
@@ -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_merge" model="project.task.type">
+ <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">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>
diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml
index 2f8c9e9638a..f685f14c85b 100644
--- a/addons/project/project_demo.xml
+++ b/addons/project/project_demo.xml
@@ -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 -->
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index 70cdac1efa0..d001af367c4 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -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,20 +517,21 @@
<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)"/>
- <button name="next_type" invisible="context.get('set_visible',False)"
- states="draft,open,pending"
- string="Change Stage"
- type="object"
- icon="gtk-go-forward"
- help="Change Type"/>
+ <button name="do_open" string="Start Task" type="object"
+ states="draft,pending" icon="gtk-go-forward"/>
+ <button name="action_close" string="Done" type="object"
+ states="draft,open,pending" icon="gtk-apply"/>
+ <button name="%(action_project_task_delegate)d" string="Delegate" type="action"
+ states="pending,open,draft" icon="terp-personal"/>
+ <field name="stage_id" invisible="context.get('set_visible',False)"/>
+ <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)]}"/>
+ <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)"/>
- <button name="do_open" states="pending,draft,done,cancelled" string="Start Task" type="object" icon="gtk-media-play" help="For changing to open state" invisible="context.get('set_visible',False)"/>
- <button name="%(action_project_task_delegate)d" states="pending,open,draft" string="Delegate" type="action" icon="terp-personal" help="For changing to delegate state"/>
- <button name="action_close" states="draft,pending,open" string="Done" type="object" icon="terp-dialog-close" help="For changing to done state"/>
</tree>
</field>
</record>
@@ -589,7 +599,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'}"/>
@@ -660,7 +670,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>
@@ -674,10 +684,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"/>
@@ -693,6 +707,7 @@
<tree string="Task Stage">
<field name="sequence"/>
<field name="name"/>
+ <field name="state"/>
</tree>
</field>
</record>
@@ -707,10 +722,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"/>
diff --git a/addons/project/report/project_cumulative.xml b/addons/project/report/project_cumulative.xml
index 89667992f0a..3c04d36feb6 100644
--- a/addons/project/report/project_cumulative.xml
+++ b/addons/project/report/project_cumulative.xml
@@ -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>
diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py
index 1012ca9d518..58f7739e96d 100644
--- a/addons/project/report/project_report.py
+++ b/addons/project/report/project_report.py
@@ -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
""")
diff --git a/addons/project/report/project_report_view.xml b/addons/project/report/project_report_view.xml
index 3bdec47fcb9..240176f7941 100644
--- a/addons/project/report/project_report_view.xml
+++ b/addons/project/report/project_report_view.xml
@@ -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>
diff --git a/addons/project/test/task_process.yml b/addons/project/test/task_process.yml
index c4d17f783f6..ad4bbe53bb3 100644
--- a/addons/project/test/task_process.yml
+++ b/addons/project/test/task_process.yml
@@ -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")]) \ No newline at end of file
+ self.do_cancel(cr, uid, [ref("project_task_2")])
diff --git a/addons/project_issue/__openerp__.py b/addons/project_issue/__openerp__.py
index fc88372fe90..9037523d41e 100644
--- a/addons/project_issue/__openerp__.py
+++ b/addons/project_issue/__openerp__.py
@@ -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',
],
diff --git a/addons/project_issue/board_project_issue_view.xml b/addons/project_issue/board_project_issue_view.xml
index a070bdbe521..9945f006e43 100644
--- a/addons/project_issue/board_project_issue_view.xml
+++ b/addons/project_issue/board_project_issue_view.xml
@@ -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,18 +96,22 @@
<field name="partner_id"/>
<field name="project_id" />
<field name="priority" string="Priority"/>
- <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"/>
<field name="version_id" widget="selection"/>
<field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/>
- <field name="state"/>
- <button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" icon="gtk-cancel"/>
- <button name="case_close" string="Done" states="open,draft,pending" type="object" icon="gtk-jump-to"/>
- <button name="case_open" string="Open" states="draft,pending" type="object" icon="gtk-go-forward" help="To Do"/>
- <button name="case_pending" string="Pending" states="draft,open" type="object" icon="gtk-media-pause"/>
- <button name="case_escalate" string="Escalate" states="open,draft,pending" type="object" icon="gtk-go-up"/>
- <button name="case_reset" string="Reset to Draft" states="done,cancel" type="object" icon="gtk-convert"/>
+ <field name="stage_id" widget="selection" readonly="1"/>
+ <button name="stage_previous" string="Previous" type="object"
+ icon="gtk-go-back" help="Change to Previous Stage"
+ attrs="{'invisible': [('stage_id','=', False)]}"/>
+ <button name="stage_next" string="Next" type="object"
+ icon="gtk-go-forward" help="Change to Next Stage"
+ attrs="{'invisible': [('stage_id','=', False)]}"/>
+ <field name="state" groups="base.group_no_one"/>
+ <button name="case_open" string="Open" type="object"
+ states="draft,pending" icon="gtk-go-forward"/>
+ <button name="case_close" string="Done" type="object"
+ states="draft,open,pending" icon="gtk-close"/>
+ <button name="case_cancel" string="Cancel" type="object"
+ states="draft,open,pending" icon="gtk-cancel"/>
<field name="categ_id" invisible="1"/>
<field name="task_id" invisible="1"/>
</tree>
diff --git a/addons/project_issue/project_issue.py b/addons/project_issue/project_issue.py
index 176ffa61acd..177c21570cd 100644
--- a/addons/project_issue/project_issue.py
+++ b/addons/project_issue/project_issue.py
@@ -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.")
diff --git a/addons/project_issue/project_issue_demo.xml b/addons/project_issue/project_issue_demo.xml
index 358e9e55aa5..a213cb02c2e 100644
--- a/addons/project_issue/project_issue_demo.xml
+++ b/addons/project_issue/project_issue_demo.xml
@@ -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>
diff --git a/addons/project_issue/project_issue_view.xml b/addons/project_issue/project_issue_view.xml
index 716a02f2f60..c3b4bf184be 100644
--- a/addons/project_issue/project_issue_view.xml
+++ b/addons/project_issue/project_issue_view.xml
@@ -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,19 +146,23 @@
<field name="partner_id"/>
<field name="project_id" />
<field name="priority" string="Priority"/>
- <field name="type_id" widget="selection" readonly="1" 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"/>
<field name="version_id" widget="selection"/>
<field name="user_id"/>
<field name="progress" widget="progressbar" attrs="{'invisible':[('task_id','=',False)]}"/>
- <field name="state"/>
- <button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" icon="gtk-cancel"/>
- <button name="case_close" string="Done" states="open,draft,pending" type="object" icon="gtk-jump-to"/>
- <button name="case_open" string="Open" states="draft,pending" type="object" icon="gtk-go-forward" help="To Do"/>
- <button name="case_pending" string="Pending" states="draft,open" type="object" icon="gtk-media-pause"/>
- <button name="case_escalate" string="Escalate" states="open,draft,pending" type="object" icon="gtk-go-up"/>
- <button name="case_reset" string="Reset to New" states="done,cancel" type="object" icon="gtk-convert"/>
+ <field name="stage_id" widget="selection" readonly="1"/>
+ <button name="stage_previous" string="Previous" type="object"
+ icon="gtk-go-back" help="Change to Previous Stage"
+ attrs="{'invisible': [('stage_id','=', False)]}"/>
+ <button name="stage_next" string="Next" type="object"
+ icon="gtk-go-forward" help="Change to Next Stage"
+ attrs="{'invisible': [('stage_id','=', False)]}"/>
+ <field name="state" groups="base.group_no_one"/>
+ <button name="case_open" string="Open" type="object"
+ icon="gtk-go-forward" states="draft,pending"/>
+ <button name="case_close" string="Done" type="object"
+ icon="gtk-close" states="draft,open,pending"/>
+ <button name="case_cancel" string="Cancel" type="object"
+ icon="gtk-cancel" states="draft,open,pending"/>
<field name="categ_id" invisible="1"/>
<field name="task_id" invisible="1"/>
</tree>
@@ -192,7 +204,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" />
@@ -224,9 +236,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"/>
@@ -270,8 +282,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"/>
@@ -304,18 +315,22 @@
<field name="name" string="Feature description"/>
<field name="partner_id"/>
<field name="priority" string="Priority"/>
- <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"/>
<field name="version_id"/>
<field name="user_id"/>
- <field name="state"/>
- <button name="case_cancel" string="Cancel" states="draft,open,pending" type="object" icon="gtk-cancel"/>
- <button name="case_close" string="Done" states="open,draft,pending" type="object" icon="gtk-jump-to"/>
- <button name="case_open" string="Open" states="draft,pending" type="object" icon="gtk-go-forward"/>
- <button name="case_pending" string="Pending" states="draft,open" type="object" icon="gtk-media-pause"/>
- <button name="case_escalate" string="Escalate" states="open,draft,pending" type="object" icon="gtk-go-up"/>
- <button name="case_reset" string="Reset to New" states="done,cancel" type="object" icon="gtk-convert"/>
+ <field name="stage_id" widget="selection" readonly="1"/>
+ <button name="stage_previous" string="Previous" type="object"
+ icon="gtk-go-back" help="Change to Previous Stage"
+ attrs="{'invisible': [('stage_id','=', False)]}"/>
+ <button name="stage_next" string="Next" type="object"
+ icon="gtk-go-forward" help="Change to Next Stage"
+ attrs="{'invisible': [('stage_id','=', False)]}"/>
+ <field name="state" groups="base.group_no_one"/>
+ <button name="case_open" string="Open" type="object"
+ states="draft,pending" icon="gtk-go-forward"/>
+ <button name="case_close" string="Done" type="object"
+ states="draft,open,pending" icon="gtk-close"/>
+ <button name="case_cancel" string="Cancel" type="object"
+ states="draft,open,pending" icon="gtk-cancel"/>
</tree>
</field>
</record>
@@ -326,13 +341,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>
diff --git a/addons/project_issue/report/project_issue_report.py b/addons/project_issue/report/project_issue_report.py
index 7cc520308e8..be3309d7175 100644
--- a/addons/project_issue/report/project_issue_report.py
+++ b/addons/project_issue/report/project_issue_report.py
@@ -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,
diff --git a/addons/project_issue/report/project_issue_report_view.xml b/addons/project_issue/report/project_issue_report_view.xml
index ee0a4fbd1ed..43a49abf2b8 100644
--- a/addons/project_issue/report/project_issue_report_view.xml
+++ b/addons/project_issue/report/project_issue_report_view.xml
@@ -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"/>
diff --git a/addons/project_issue/test/cancel_issue.yml b/addons/project_issue/test/cancel_issue.yml
index ff05d923ed0..76ffa212638 100644
--- a/addons/project_issue/test/cancel_issue.yml
+++ b/addons/project_issue/test/cancel_issue.yml
@@ -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' \ No newline at end of file
+ - state == 'cancelled'
diff --git a/addons/project_long_term/project_long_term_view.xml b/addons/project_long_term/project_long_term_view.xml
index 1e9f50cdd87..eb2c86a0f1b 100644
--- a/addons/project_long_term/project_long_term_view.xml
+++ b/addons/project_long_term/project_long_term_view.xml
@@ -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>
diff --git a/addons/project_planning/project_planning_view.xml b/addons/project_planning/project_planning_view.xml
index 11e9b057c06..ef230543e9c 100644
--- a/addons/project_planning/project_planning_view.xml
+++ b/addons/project_planning/project_planning_view.xml
@@ -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"/>
diff --git a/addons/project_scrum/project_scrum_demo.xml b/addons/project_scrum/project_scrum_demo.xml
index 50c67146716..6e3de7665bd 100644
--- a/addons/project_scrum/project_scrum_demo.xml
+++ b/addons/project_scrum/project_scrum_demo.xml
@@ -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>
diff --git a/addons/project_scrum/project_scrum_view.xml b/addons/project_scrum/project_scrum_view.xml
index ef8a2681620..168a56f46aa 100644
--- a/addons/project_scrum/project_scrum_view.xml
+++ b/addons/project_scrum/project_scrum_view.xml
@@ -106,7 +106,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"
@@ -578,7 +578,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"/>