summaryrefslogtreecommitdiff
path: root/addons/project
diff options
context:
space:
mode:
Diffstat (limited to 'addons/project')
-rw-r--r--addons/project/__openerp__.py5
-rw-r--r--addons/project/board_project_view.xml3
-rw-r--r--addons/project/doc/changelog.rst16
-rw-r--r--addons/project/doc/index.rst22
-rw-r--r--addons/project/doc/stage_status.rst55
-rw-r--r--addons/project/i18n/zh_CN.po111
-rw-r--r--addons/project/project.py364
-rw-r--r--addons/project/project_data.xml48
-rw-r--r--addons/project/project_demo.xml16
-rw-r--r--addons/project/project_view.xml136
-rw-r--r--addons/project/report/project_cumulative.xml4
-rw-r--r--addons/project/report/project_report.py26
-rw-r--r--addons/project/report/project_report_view.xml37
-rw-r--r--addons/project/res_partner.py8
-rw-r--r--addons/project/res_partner_view.xml26
-rw-r--r--addons/project/security/ir.model.access.csv1
-rw-r--r--addons/project/static/description/Mario-Riva.png (renamed from addons/project/html/Mario-Riva.png)bin10124 -> 10124 bytes
-rw-r--r--addons/project/static/description/Yoshi-Tashiro.png (renamed from addons/project/html/Yoshi-Tashiro.png)bin8214 -> 8214 bytes
-rw-r--r--addons/project/static/description/icon.png (renamed from addons/project/static/src/img/icon.png)bin6187 -> 6187 bytes
-rw-r--r--addons/project/static/description/index.html (renamed from addons/project/html/index.html)2
-rw-r--r--addons/project/static/description/project_01.png (renamed from addons/project/html/project_01.png)bin95876 -> 95876 bytes
-rw-r--r--addons/project/static/description/project_calendar.png (renamed from addons/project/html/project_calendar.png)bin9861 -> 9861 bytes
-rw-r--r--addons/project/static/description/project_chat.png (renamed from addons/project/html/project_chat.png)bin27767 -> 27767 bytes
-rw-r--r--addons/project/static/description/project_easy.png (renamed from addons/project/html/project_easy.png)bin31387 -> 31387 bytes
-rw-r--r--addons/project/static/description/project_etherpad.png (renamed from addons/project/html/project_etherpad.png)bin98815 -> 98815 bytes
-rw-r--r--addons/project/static/description/project_gantt.png (renamed from addons/project/html/project_gantt.png)bin23260 -> 23260 bytes
-rw-r--r--addons/project/static/description/project_illu_01.png (renamed from addons/project/html/project_illu_01.png)bin34110 -> 34110 bytes
-rw-r--r--addons/project/static/description/project_kanban.png (renamed from addons/project/html/project_kanban.png)bin46301 -> 46301 bytes
-rw-r--r--addons/project/static/description/project_kpi.png (renamed from addons/project/html/project_kpi.png)bin38247 -> 38247 bytes
-rw-r--r--addons/project/static/description/project_timesheet.png (renamed from addons/project/html/project_timesheet.png)bin61578 -> 61578 bytes
-rw-r--r--addons/project/test/project_demo.yml12
-rw-r--r--addons/project/test/project_process.yml70
-rw-r--r--addons/project/test/task_process.yml76
-rw-r--r--addons/project/tests/__init__.py28
-rw-r--r--addons/project/tests/test_project_base.py99
-rw-r--r--addons/project/tests/test_project_flow.py163
-rw-r--r--addons/project/wizard/__init__.py2
-rw-r--r--addons/project/wizard/project_task_reevaluate.py84
-rw-r--r--addons/project/wizard/project_task_reevaluate_view.xml32
39 files changed, 704 insertions, 742 deletions
diff --git a/addons/project/__openerp__.py b/addons/project/__openerp__.py
index c0a516cf555..f5ba24c41cb 100644
--- a/addons/project/__openerp__.py
+++ b/addons/project/__openerp__.py
@@ -40,7 +40,6 @@
],
'depends': [
'base_setup',
- 'base_status',
'product',
'analytic',
'board',
@@ -66,7 +65,6 @@ Dashboard / Reports for Project Management will include:
'data': [
'security/project_security.xml',
'wizard/project_task_delegate_view.xml',
- 'wizard/project_task_reevaluate_view.xml',
'security/ir.model.access.csv',
'project_data.xml',
'project_view.xml',
@@ -79,9 +77,6 @@ Dashboard / Reports for Project Management will include:
],
'demo': ['project_demo.xml'],
'test': [
- 'test/project_demo.yml',
- 'test/project_process.yml',
- 'test/task_process.yml',
],
'installable': True,
'auto_install': False,
diff --git a/addons/project/board_project_view.xml b/addons/project/board_project_view.xml
index 931c661d211..3a14f7b6274 100644
--- a/addons/project/board_project_view.xml
+++ b/addons/project/board_project_view.xml
@@ -16,7 +16,6 @@
<field name="effective_hours" widget="float_time"/>
<field name="progress" widget="progressbar"/>
<field name="stage_id" invisible="context.get('set_visible',False)"/>
- <field name="state" invisible="1"/>
</tree>
</field>
</record>
@@ -26,7 +25,7 @@
<field name="res_model">project.task</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
- <field name="domain">[('user_id','=',uid),('state','not in',('cancel','done'))]</field>
+ <field name="domain">[('user_id', '=', uid), ('stage_id.fold', '!=', True)]</field>
<field name="view_id" ref="view_task_tree"/>
</record>
diff --git a/addons/project/doc/changelog.rst b/addons/project/doc/changelog.rst
new file mode 100644
index 00000000000..ba5b242f1c0
--- /dev/null
+++ b/addons/project/doc/changelog.rst
@@ -0,0 +1,16 @@
+.. _changelog:
+
+Changelog
+=========
+
+`trunk (saas-2)`
+----------------
+
+- Stage/state update
+
+ - ``project.task``: removed inheritance from ``base_stage`` class and removed
+ ``state`` field. Added ``date_last_stage_update`` field holding last stage_id
+ modification. Updated reports.
+ - ``project.task.type``: removed ``state`` field.
+
+- Removed ``project.task.reevaluate`` wizard.
diff --git a/addons/project/doc/index.rst b/addons/project/doc/index.rst
new file mode 100644
index 00000000000..118b8e7006b
--- /dev/null
+++ b/addons/project/doc/index.rst
@@ -0,0 +1,22 @@
+=====================
+Project DevDoc
+=====================
+
+Project module documentation
+===================================
+
+Documentation topics
+''''''''''''''''''''
+
+.. toctree::
+ :maxdepth: 1
+
+ stage_status.rst
+
+Changelog
+'''''''''
+
+.. toctree::
+ :maxdepth: 1
+
+ changelog.rst
diff --git a/addons/project/doc/stage_status.rst b/addons/project/doc/stage_status.rst
new file mode 100644
index 00000000000..cc2a64c2fbf
--- /dev/null
+++ b/addons/project/doc/stage_status.rst
@@ -0,0 +1,55 @@
+.. _stage_status:
+
+Stage and Status
+================
+
+.. versionchanged:: 8.0 saas-2 state/stage cleaning
+
+Stage
++++++
+
+This revision removed the concept of state on project.task objects. The ``state``
+field has been totally removed and replaced by stages, using ``stage_id``. The
+following models are impacted:
+
+ - ``project.task`` now use only stages. However a convention still exists about
+ 'New' stage. A task is consdered as ``new`` when it has the following
+ properties:
+
+ - ``stage_id and stage_id.sequence = 1``
+
+ - ``project.task.type`` do not have any ``state`` field anymore.
+ - ``project.task.report`` do not have any ``state`` field anymore.
+
+By default a newly created task is in a new stage. It means that it will
+fetch the stage having ``sequence = 1``. Stage mangement is done using the
+kanban view or the clikable statusbar. It is not done using buttons anymore.
+
+Stage analysis
+++++++++++++++
+
+Stage analysis can be performed using the newly introduced ``date_last_stage_update``
+datetime field. This field is updated everytime ``stage_id`` is updated.
+
+``project.task.report`` model also uses the ``date_last_stage_update`` field.
+This allows to group and analyse the time spend in the various stages.
+
+Open / Assignation date
++++++++++++++++++++++++
+
+The ``date_open`` field meaning has been updated. It is now set when the ``user_id``
+(responsible) is set. It is therefore the assignation date.
+
+Subtypes
+++++++++
+
+The following subtypes are triggered on ``project.task``:
+
+ - ``mt_task_new``: new tasks. Condition: ``obj.stage_id and obj.stage_id.sequence == 1``
+ - ``mt_task_stage``: stage changed. Condition: ``obj.stage_id and obj.stage_id.sequence != 1``
+ - ``mt_task_assigned``: user assigned. condition: ``obj.user_id and obj.user_id.id``
+ - ``mt_task_blocked``: kanban state blocked. Condition: ``obj.kanban_state == 'blocked'``
+
+
+Those subtypes are also available on the ``project.project`` model and are used
+for the auto subscription.
diff --git a/addons/project/i18n/zh_CN.po b/addons/project/i18n/zh_CN.po
index b0618c0233d..242bca1b9c0 100644
--- a/addons/project/i18n/zh_CN.po
+++ b/addons/project/i18n/zh_CN.po
@@ -7,19 +7,19 @@ msgstr ""
"Project-Id-Version: OpenERP Server 6.0dev\n"
"Report-Msgid-Bugs-To: support@openerp.com\n"
"POT-Creation-Date: 2012-12-21 17:04+0000\n"
-"PO-Revision-Date: 2012-12-14 14:12+0000\n"
-"Last-Translator: sum1201 <Unknown>\n"
+"PO-Revision-Date: 2013-08-13 04:04+0000\n"
+"Last-Translator: Wei \"oldrev\" Li <oldrev@gmail.com>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-"X-Launchpad-Export-Date: 2013-03-16 04:57+0000\n"
-"X-Generator: Launchpad (build 16532)\n"
+"X-Launchpad-Export-Date: 2013-08-14 04:54+0000\n"
+"X-Generator: Launchpad (build 16723)\n"
#. module: project
#: view:project.project:0
msgid "Email Interface"
-msgstr "Email 接口"
+msgstr "电子邮件接口"
#. module: project
#: help:account.analytic.account,use_tasks:0
@@ -38,7 +38,7 @@ msgstr "进度"
#. module: project
#: model:process.node,name:project.process_node_taskbydelegate0
msgid "Task by delegate"
-msgstr "委派任务"
+msgstr "委派的任务"
#. module: project
#: view:project.project:0
@@ -58,18 +58,18 @@ msgstr "阶段名称"
#. module: project
#: model:process.transition.action,name:project.process_transition_action_openpendingtask0
msgid "Set pending"
-msgstr "设为未决"
+msgstr "设置为等待状态"
#. module: project
#: view:project.project:0
msgid "New Project Based on Template"
-msgstr "新的基于模板的项目"
+msgstr "基于该模板新建项目"
#. module: project
#: view:report.project.task.user:0
#: field:report.project.task.user,day:0
msgid "Day"
-msgstr "天数"
+msgstr "日"
#. module: project
#: model:project.task.type,name:project.project_tt_merge
@@ -82,7 +82,7 @@ msgid "Start Task"
msgstr "开始任务"
#. module: project
-#: code:addons/project/project.py:932
+#: code:addons/project/project.py:944
#, python-format
msgid "Warning !"
msgstr "警告!"
@@ -91,7 +91,7 @@ msgstr "警告!"
#: help:project.project,message_unread:0
#: help:project.task,message_unread:0
msgid "If checked new messages require your attention."
-msgstr "如果要求你关注新消息,勾选此项"
+msgstr "如果选中此项,你需要注意新的消息。"
#. module: project
#: model:process.node,name:project.process_node_donetask0
@@ -101,12 +101,12 @@ msgstr "完成任务"
#. module: project
#: model:process.node,note:project.process_node_donetask0
msgid "Task is Completed"
-msgstr "任务完成"
+msgstr "任务已经完成"
#. module: project
#: view:res.partner:0
msgid "False"
-msgstr "否"
+msgstr "错误的"
#. module: project
#: model:project.task.type,name:project.project_tt_testing
@@ -121,7 +121,7 @@ msgstr "辅助核算科目"
#. module: project
#: field:project.config.settings,group_time_work_estimation_tasks:0
msgid "Manage time estimation on tasks"
-msgstr "估计管理任务的时间"
+msgstr "管理任务的估计时间"
#. module: project
#: help:project.project,message_summary:0
@@ -129,11 +129,11 @@ msgstr "估计管理任务的时间"
msgid ""
"Holds the Chatter summary (number of messages, ...). This summary is "
"directly in html format in order to be inserted in kanban views."
-msgstr "保存复杂的摘要(消息数量,……等)。为了插入到看板视图,这一摘要直接是是HTML格式。"
+msgstr "保存对话概要信息,这些概要信息将为直接以HTML格式插入到看板视图。"
#. module: project
-#: code:addons/project/project.py:432
-#: code:addons/project/project.py:1318
+#: code:addons/project/project.py:444
+#: code:addons/project/project.py:1332
#, python-format
msgid "Warning!"
msgstr "警告!"
@@ -163,12 +163,12 @@ msgstr "重设项目"
#. module: project
#: view:report.project.task.user:0
msgid "In progress tasks"
-msgstr "进行中任务"
+msgstr "进行中的任务"
#. module: project
#: help:project.project,progress_rate:0
msgid "Percent of tasks closed according to the total of tasks todo."
-msgstr "结束任务和全部要做任务的百分比"
+msgstr "已结束任务和未结束任务的百分比。"
#. module: project
#: model:ir.actions.client,name:project.action_client_project_menu
@@ -224,7 +224,7 @@ msgstr "分配给"
#: model:mail.message.subtype,name:project.mt_project_task_closed
#: model:mail.message.subtype,name:project.mt_task_closed
msgid "Task Done"
-msgstr ""
+msgstr "已完成的任务"
#. module: project
#: view:project.project:0
@@ -265,7 +265,7 @@ msgstr "任务分派"
#: model:mail.message.subtype,name:project.mt_project_task_started
#: model:mail.message.subtype,name:project.mt_task_started
msgid "Task Started"
-msgstr ""
+msgstr "已开始的任务"
#. module: project
#: view:project.task:0
@@ -383,7 +383,7 @@ msgid "Assigned to"
msgstr "指派到"
#. module: project
-#: code:addons/project/project.py:1021
+#: code:addons/project/project.py:1033
#, python-format
msgid "Delegated User should be specified"
msgstr "应该制定委派用户"
@@ -433,7 +433,7 @@ msgstr "使用集成的协作记事簿任务"
#: model:mail.message.subtype,name:project.mt_project_task_blocked
#: model:mail.message.subtype,name:project.mt_task_blocked
msgid "Task Blocked"
-msgstr ""
+msgstr "延期的任务"
#. module: project
#: model:process.node,note:project.process_node_opentask0
@@ -453,12 +453,12 @@ msgstr "oe_kanban_text_red"
#. module: project
#: model:mail.message.subtype,description:project.mt_task_blocked
msgid "Task blocked"
-msgstr ""
+msgstr "延期的任务"
#. module: project
#: view:project.task:0
msgid "Delegation"
-msgstr "代表"
+msgstr "任务委派"
#. module: project
#: field:project.task,create_date:0
@@ -636,6 +636,15 @@ msgid ""
" </p>\n"
" "
msgstr ""
+"<p class=\"oe_view_nocontent_create\">\n"
+" 创建一个新项目\n"
+" </p><p>\n"
+" "
+"项目是组织活动,计划任务,跟踪问题,记录人力资源成本的组织形式。项目可以分为内部项目(研发活动,加快销售进度),私有项目或者客户参与的项目。\n"
+" </p><p>\n"
+" 内部用户以项目成员的方式参与到项目中,也可以邀请客户加入到项目中分享项目活动。\n"
+" </p>\n"
+" "
#. module: project
#: view:project.config.settings:0
@@ -647,7 +656,7 @@ msgstr "计划"
#: field:project.task,date_deadline:0
#: field:report.project.task.user,date_deadline:0
msgid "Deadline"
-msgstr "最后期限"
+msgstr "计划完成日期"
#. module: project
#: view:project.task.history.cumulative:0
@@ -740,7 +749,7 @@ msgstr "待处理任务草稿"
#. module: project
#: field:project.project,alias_model:0
msgid "Alias Model"
-msgstr ""
+msgstr "别名模型"
#. module: project
#: help:report.project.task.user,closing_days:0
@@ -845,7 +854,7 @@ msgstr "增加筛选条件"
#. module: project
#: model:ir.ui.menu,name:project.menu_tasks_config
msgid "GTD"
-msgstr ""
+msgstr "GTD"
#. module: project
#: help:project.task,state:0
@@ -866,7 +875,7 @@ msgstr "公司"
#. module: project
#: field:project.task.type,fold:0
msgid "Folded by Default"
-msgstr ""
+msgstr "默认折叠"
#. module: project
#: field:project.task.history,date:0
@@ -1137,7 +1146,7 @@ msgstr "您的文档的状态将自动更改选定的阶段。例如,如果某
#. module: project
#: view:project.task:0
msgid "Extra Info"
-msgstr "额外信息"
+msgstr "其它信息"
#. module: project
#: view:project.task:0
@@ -1195,9 +1204,9 @@ msgid "Computed as: Time Spent + Remaining Time."
msgstr "计算:花费的时间 + 剩余的时间"
#. module: project
-#: code:addons/project/project.py:356
-#: code:addons/project/project.py:377
-#: code:addons/project/project.py:709
+#: code:addons/project/project.py:368
+#: code:addons/project/project.py:389
+#: code:addons/project/project.py:721
#, python-format
msgid "%s (copy)"
msgstr "%s (副本)"
@@ -1205,7 +1214,7 @@ msgstr "%s (副本)"
#. module: project
#: model:mail.message.subtype,name:project.mt_project_task_stage
msgid "Task Stage Changed"
-msgstr ""
+msgstr "任务阶段已改变"
#. module: project
#: view:project.task:0
@@ -1397,7 +1406,7 @@ msgid ""
msgstr "做任务的估计时间.通常由项目经理在任务草稿阶段设定"
#. module: project
-#: code:addons/project/project.py:220
+#: code:addons/project/project.py:230
#, python-format
msgid "Attachments"
msgstr "附件"
@@ -1447,7 +1456,7 @@ msgstr "剩余的小时数"
#. module: project
#: model:mail.message.subtype,description:project.mt_task_stage
msgid "Stage changed"
-msgstr ""
+msgstr "任务阶段已改变"
#. module: project
#: constraint:project.task:0
@@ -1479,7 +1488,7 @@ msgstr "总小时数"
#. module: project
#: model:ir.model,name:project.model_project_config_settings
msgid "project.config.settings"
-msgstr ""
+msgstr "项目配置"
#. module: project
#: model:project.task.type,name:project.project_tt_development
@@ -1524,7 +1533,7 @@ msgid "Overpassed Tasks"
msgstr "拖期任务"
#. module: project
-#: code:addons/project/project.py:932
+#: code:addons/project/project.py:944
#, python-format
msgid ""
"Child task still open.\n"
@@ -1556,7 +1565,7 @@ msgstr "委派你的任务给其它用户"
#. module: project
#: model:mail.message.subtype,description:project.mt_task_started
msgid "Task started"
-msgstr ""
+msgstr "已开始的任务"
#. module: project
#: help:project.task.reevaluate,remaining_hours:0
@@ -1585,7 +1594,7 @@ msgid "CHECK: "
msgstr "CHECK: "
#. module: project
-#: code:addons/project/project.py:432
+#: code:addons/project/project.py:444
#, python-format
msgid "You must assign members on the project '%s' !"
msgstr "您必须为项目“%s”指定成员!"
@@ -1697,6 +1706,12 @@ msgid ""
" </p>\n"
" "
msgstr ""
+"<p class=\"oe_view_nocontent_create\">\n"
+" 单击创建新任务。\n"
+" </p><p>\n"
+"OpenERP 系统的任务管理允许你为了更高效地完成工作而管理任务管道。你可以跟踪任务进度、与他人讨论任务、附加文档附件等。\n"
+" </p>\n"
+" "
#. module: project
#: field:project.task,date_end:0
@@ -1717,7 +1732,7 @@ msgstr "文档"
#. module: project
#: model:mail.message.subtype,description:project.mt_task_new
msgid "Task created"
-msgstr ""
+msgstr "任务已创建"
#. module: project
#: view:report.project.task.user:0
@@ -1811,7 +1826,7 @@ msgid "Starting Date"
msgstr "开始日期"
#. module: project
-#: code:addons/project/project.py:398
+#: code:addons/project/project.py:410
#: model:ir.actions.act_window,name:project.open_view_project_all
#: model:ir.ui.menu,name:project.menu_projects
#: view:project.project:0
@@ -1972,7 +1987,7 @@ msgid "Reevaluation Task"
msgstr "任务重估"
#. module: project
-#: code:addons/project/project.py:1318
+#: code:addons/project/project.py:1332
#, python-format
msgid "Please delete the project linked with this account first."
msgstr "请删除与此科目关联的项目"
@@ -1981,7 +1996,7 @@ msgstr "请删除与此科目关联的项目"
#: model:mail.message.subtype,name:project.mt_project_task_new
#: model:mail.message.subtype,name:project.mt_task_new
msgid "Task Created"
-msgstr ""
+msgstr "任务已创建"
#. module: project
#: view:report.project.task.user:0
@@ -2004,7 +2019,7 @@ msgstr "准备好下一个阶段"
#. module: project
#: field:project.task.type,case_default:0
msgid "Default for New Projects"
-msgstr ""
+msgstr "默认用于新项目"
#. module: project
#: view:project.task:0
@@ -2042,11 +2057,13 @@ msgid ""
"resource allocation.\n"
" This installs the module project_long_term."
msgstr ""
+"用于长期项目管理的模块,能够跟踪计划、排程和资源控制。\n"
+" 将会安装模块“project_long_term”。"
#. module: project
#: model:mail.message.subtype,description:project.mt_task_closed
msgid "Task closed"
-msgstr ""
+msgstr "已关闭的任务"
#. module: project
#: selection:report.project.task.user,month:0
@@ -2074,6 +2091,8 @@ msgid ""
"Provides timesheet support for the issues/bugs management in project.\n"
" This installs the module project_issue_sheet."
msgstr ""
+"为项目中的问题/错误管理提供时间表支持。\n"
+" 将安装模块“project_issue_sheet”。"
#. module: project
#: selection:project.project,privacy_visibility:0
diff --git a/addons/project/project.py b/addons/project/project.py
index e907e4b3652..db2052224f9 100644
--- a/addons/project/project.py
+++ b/addons/project/project.py
@@ -25,13 +25,10 @@ import time
from openerp import SUPERUSER_ID
from openerp import tools
+from openerp.addons.resource.faces import task as Task
from openerp.osv import fields, osv
from openerp.tools.translate import _
-from openerp.addons.base_status.base_stage import base_stage
-from openerp.addons.resource.faces import task as Task
-
-_TASK_STATE = [('draft', 'New'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done'), ('cancelled', 'Cancelled')]
class project_task_type(osv.osv):
_name = 'project.task.type'
@@ -44,33 +41,18 @@ class project_task_type(osv.osv):
'case_default': fields.boolean('Default for New 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, 'Related Status', required=True,
- help="The status of your document is automatically changed regarding the selected stage. " \
- "For example, if a stage is related to the status 'Close', when your document reaches this stage, it is automatically closed."),
'fold': fields.boolean('Folded by Default',
help="This stage is not visible, for example in status bar or kanban view, when there are no records in that stage to display."),
}
- def _get_default_project_id(self, cr, uid, ctx={}):
- proj = ctx.get('default_project_id', False)
- if type(proj) is int:
- return [proj]
- return proj
+
_defaults = {
'sequence': 1,
- 'state': 'open',
'fold': False,
'case_default': False,
- 'project_ids': _get_default_project_id
+ 'project_ids': lambda self, cr, uid, ctx=None: self.pool['project.task']._get_default_project_id(cr, uid, context=ctx),
}
_order = 'sequence'
-def short_name(name):
- """Keep first word(s) of name to make it small enough
- but distinctive"""
- if not name: return name
- # keep 7 chars + end of the last word
- keep_words = name[:7].strip().split()
- return ' '.join(name.split()[:len(keep_words)])
class project(osv.osv):
_name = "project.project"
@@ -99,9 +81,9 @@ class project(osv.osv):
def onchange_partner_id(self, cr, uid, ids, part=False, context=None):
partner_obj = self.pool.get('res.partner')
- if not part:
- return {'value':{}}
val = {}
+ if not part:
+ return {'value': val}
if 'pricelist_id' in self.fields_get(cr, uid, context=context):
pricelist = partner_obj.read(cr, uid, part, ['property_product_pricelist'], context=context)
pricelist_id = pricelist.get('property_product_pricelist', False) and pricelist.get('property_product_pricelist')[0] or False
@@ -152,11 +134,13 @@ class project(osv.osv):
cr.execute("""
SELECT project_id, COALESCE(SUM(planned_hours), 0.0),
COALESCE(SUM(total_hours), 0.0), COALESCE(SUM(effective_hours), 0.0)
- FROM project_task WHERE project_id IN %s AND state <> 'cancelled'
+ FROM project_task
+ LEFT JOIN project_task_type ON project_task.stage_id = project_task_type.id
+ WHERE project_task.project_id IN %s AND project_task_type.fold = False
GROUP BY project_id
""", (tuple(child_parent.keys()),))
# aggregate results into res
- res = dict([(id, {'planned_hours':0.0,'total_hours':0.0,'effective_hours':0.0}) for id in ids])
+ res = dict([(id, {'planned_hours':0.0, 'total_hours':0.0, 'effective_hours':0.0}) for id in ids])
for id, planned, total, effective in cr.fetchall():
# add the values specific to id to all parent projects of id in the result
while id:
@@ -182,10 +166,10 @@ class project(osv.osv):
_('You cannot delete a project containing tasks. You can either delete all the project\'s tasks and then delete the project or simply deactivate the project.'))
elif proj.alias_id:
alias_ids.append(proj.alias_id.id)
- res = super(project, self).unlink(cr, uid, ids, context=context)
+ res = super(project, self).unlink(cr, uid, ids, context=context)
mail_alias.unlink(cr, uid, alias_ids, context=context)
return res
-
+
def _get_attached_docs(self, cr, uid, ids, field_name, arg, context):
res = {}
attachment = self.pool.get('ir.attachment')
@@ -196,7 +180,7 @@ class project(osv.osv):
task_attachments = attachment.search(cr, uid, [('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)], context=context, count=True)
res[id] = (project_attachments or 0) + (task_attachments or 0)
return res
-
+
def _task_count(self, cr, uid, ids, field_name, arg, context=None):
if context is None:
context = {}
@@ -209,22 +193,21 @@ class project(osv.osv):
return res
def _get_alias_models(self, cr, uid, context=None):
- """Overriden in project_issue to offer more options"""
+ """ Overriden in project_issue to offer more options """
return [('project.task', "Tasks")]
def _get_visibility_selection(self, cr, uid, context=None):
""" Overriden in portal_project to offer more options """
- return [('public', 'Public'),
- ('employees', 'Employees Only'),
- ('followers', 'Followers Only')]
+ return [('public', 'Public project'),
+ ('employees', 'Internal project: all employees can access'),
+ ('followers', 'Private project: followers Only')]
def attachment_tree_view(self, cr, uid, ids, context):
task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)])
domain = [
- '|',
+ '|',
'&', ('res_model', '=', 'project.project'), ('res_id', 'in', ids),
- '&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)
- ]
+ '&', ('res_model', '=', 'project.task'), ('res_id', 'in', task_ids)]
res_id = ids and ids[0] or False
return {
'name': _('Attachments'),
@@ -237,6 +220,7 @@ class project(osv.osv):
'limit': 80,
'context': "{'default_res_model': '%s','default_res_id': %d}" % (self._name, res_id)
}
+
# Lambda indirection method to avoid passing a copy of the overridable method when declaring the field
_alias_models = lambda self, *args, **kwargs: self._get_alias_models(*args, **kwargs)
_visibility_selection = lambda self, *args, **kwargs: self._get_visibility_selection(*args, **kwargs)
@@ -253,22 +237,22 @@ class project(osv.osv):
'planned_hours': fields.function(_progress_rate, multi="progress", string='Planned Time', help="Sum of planned hours of all tasks related to this project and its child projects.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'effective_hours': fields.function(_progress_rate, multi="progress", string='Time Spent', help="Sum of spent hours of all tasks related to this project and its child projects.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'total_hours': fields.function(_progress_rate, multi="progress", string='Total Time', help="Sum of total hours of all tasks related to this project and its child projects.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'progress_rate': fields.function(_progress_rate, multi="progress", string='Progress', type='float', group_operator="avg", help="Percent of tasks closed according to the total of tasks todo.",
store = {
'project.project': (_get_project_and_parents, ['tasks', 'parent_id', 'child_ids'], 10),
- 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'state'], 20),
+ 'project.task': (_get_projects_from_tasks, ['planned_hours', 'remaining_hours', 'work_ids', 'stage_id'], 20),
}),
'resource_calendar_id': fields.many2one('resource.calendar', 'Working Time', help="Timetable working hours to adjust the gantt diagram report", states={'close':[('readonly',True)]} ),
'type_ids': fields.many2many('project.task.type', 'project_task_type_rel', 'project_id', 'type_id', 'Tasks Stages', states={'close':[('readonly',True)], 'cancelled':[('readonly',True)]}),
@@ -323,26 +307,19 @@ class project(osv.osv):
]
def set_template(self, cr, uid, ids, context=None):
- res = self.setActive(cr, uid, ids, value=False, context=context)
- return res
+ return self.setActive(cr, uid, ids, value=False, context=context)
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.case_close(cr, uid, task_ids, context=context)
- return self.write(cr, uid, ids, {'state':'close'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'close'}, context=context)
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.case_cancel(cr, uid, task_ids, context=context)
- return self.write(cr, uid, ids, {'state':'cancelled'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'cancelled'}, context=context)
def set_pending(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state':'pending'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'pending'}, context=context)
def set_open(self, cr, uid, ids, context=None):
- return self.write(cr, uid, ids, {'state':'open'}, context=context)
+ return self.write(cr, uid, ids, {'state': 'open'}, context=context)
def reset_project(self, cr, uid, ids, context=None):
return self.setActive(cr, uid, ids, value=True, context=context)
@@ -370,13 +347,11 @@ class project(osv.osv):
default['state'] = 'open'
default['line_ids'] = []
default['tasks'] = []
- default.pop('alias_name', None)
- default.pop('alias_id', None)
proj = self.browse(cr, uid, id, context=context)
if not default.get('name', False):
default.update(name=_("%s (copy)") % (proj.name))
res = super(project, self).copy(cr, uid, id, default, context)
- self.map_tasks(cr,uid,id,res,context)
+ self.map_tasks(cr, uid, id, res, context=context)
return res
def duplicate_template(self, cr, uid, ids, context=None):
@@ -450,7 +425,7 @@ class project(osv.osv):
for project in projects:
if (not project.members) and force_members:
- raise osv.except_osv(_('Warning!'),_("You must assign members on the project '%s' !") % (project.name,))
+ raise osv.except_osv(_('Warning!'),_("You must assign members on the project '%s'!") % (project.name,))
resource_pool = self.pool.get('resource.resource')
@@ -461,8 +436,6 @@ class project(osv.osv):
if project.user_id and (project.user_id.id not in u_ids):
u_ids.append(project.user_id.id)
for task in project.tasks:
- if task.state in ('done','cancelled'):
- continue
if task.user_id and (task.user_id.id not in u_ids):
u_ids.append(task.user_id.id)
calendar_id = project.resource_calendar_id and project.resource_calendar_id.id or False
@@ -527,7 +500,7 @@ def Project():
for project in projects:
project_gantt = getattr(projects_gantt, 'Project_%d' % (project.id,))
for task in project.tasks:
- if task.state in ('done','cancelled'):
+ if task.stage_id and task.stage_id.fold:
continue
p = getattr(project_gantt, 'Task_%d' % (task.id,))
@@ -542,28 +515,20 @@ def Project():
}, context=context)
return True
- # ------------------------------------------------
- # OpenChatter methods and notifications
- # ------------------------------------------------
-
def create(self, cr, uid, vals, context=None):
- if context is None: context = {}
- # Prevent double project creation when 'use_tasks' is checked!
- context = dict(context, project_creation_in_progress=True)
- mail_alias = self.pool.get('mail.alias')
- if not vals.get('alias_id') and vals.get('name', False):
- alias_name = vals.pop('alias_name', None) # prevent errors during copy()
- alias_id = mail_alias.create_unique_alias(cr, uid,
- # Using '+' allows using subaddressing for those who don't
- # have a catchall domain setup.
- {'alias_name': alias_name or "project+"+short_name(vals['name'])},
- model_name=vals.get('alias_model', 'project.task'),
- context=context)
- vals['alias_id'] = alias_id
- if vals.get('type', False) not in ('template','contract'):
+ if context is None:
+ context = {}
+ # Prevent double project creation when 'use_tasks' is checked + alias management
+ create_context = dict(context, project_creation_in_progress=True,
+ alias_model_name=vals.get('alias_model', 'project.task'),
+ alias_parent_model_name=self._name)
+
+ if vals.get('type', False) not in ('template', 'contract'):
vals['type'] = 'contract'
- project_id = super(project, self).create(cr, uid, vals, context)
- mail_alias.write(cr, uid, [vals['alias_id']], {'alias_defaults': {'project_id': project_id} }, context)
+
+ project_id = super(project, self).create(cr, uid, vals, context=create_context)
+ project_rec = self.browse(cr, uid, project_id, context=context)
+ self.pool.get('mail.alias').write(cr, uid, [project_rec.alias_id.id], {'alias_parent_thread_id': project_id, 'alias_defaults': {'project_id': project_id}}, context)
return project_id
def write(self, cr, uid, ids, vals, context=None):
@@ -573,26 +538,35 @@ def Project():
vals.update(alias_model_id=model_ids[0])
return super(project, self).write(cr, uid, ids, vals, context=context)
-class task(base_stage, osv.osv):
+
+class task(osv.osv):
_name = "project.task"
_description = "Task"
_date_name = "date_start"
_inherit = ['mail.thread', 'ir.needaction_mixin']
+ _mail_post_access = 'read'
_track = {
- 'state': {
- 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj['state'] in ['new', 'draft'],
- 'project.mt_task_started': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'open',
- 'project.mt_task_closed': lambda self, cr, uid, obj, ctx=None: obj['state'] == 'done',
- },
'stage_id': {
- 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj['state'] not in ['new', 'draft', 'done', 'open'],
+ 'project.mt_task_new': lambda self, cr, uid, obj, ctx=None: obj.stage_id and obj.stage_id.sequence == 1,
+ 'project.mt_task_stage': lambda self, cr, uid, obj, ctx=None: obj.stage_id.sequence != 1,
+ },
+ 'user_id': {
+ 'project.mt_task_assigned': lambda self, cr, uid, obj, ctx=None: obj.user_id and obj.user_id.id,
},
- 'kanban_state': { # kanban state: tracked, but only block subtype
- 'project.mt_task_blocked': lambda self, cr, uid, obj, ctx=None: obj['kanban_state'] == 'blocked',
+ 'kanban_state': {
+ 'project.mt_task_blocked': lambda self, cr, uid, obj, ctx=None: obj.kanban_state == 'blocked',
},
}
+ def _get_default_partner(self, cr, uid, context=None):
+ project_id = self._get_default_project_id(cr, uid, context)
+ if project_id:
+ project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
+ if project and project.partner_id:
+ return project.partner_id.id
+ return False
+
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)
@@ -600,14 +574,15 @@ class task(base_stage, osv.osv):
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)
+ return self.stage_find(cr, uid, [], project_id, [('sequence', '=', '1')], 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 context is None:
+ context = {}
if type(context.get('default_project_id')) in (int, long):
return context['default_project_id']
if isinstance(context.get('default_project_id'), basestring):
@@ -676,27 +651,32 @@ class task(base_stage, osv.osv):
res[task.id]['progress'] = 0.0
if (task.remaining_hours + hours.get(task.id, 0.0)):
res[task.id]['progress'] = round(min(100.0 * hours.get(task.id, 0.0) / res[task.id]['total_hours'], 99.99),2)
- if task.state in ('done','cancelled'):
+ # TDE CHECK: if task.state in ('done','cancelled'):
+ if task.stage_id and task.stage_id.fold:
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}}
+ return {'value': {'planned_hours': remaining}}
return {}
def onchange_planned(self, cr, uid, ids, planned=0.0, effective=0.0):
- return {'value':{'remaining_hours': planned - effective}}
-
- def onchange_project(self, cr, uid, id, project_id):
- if not project_id:
- return {}
- data = self.pool.get('project.project').browse(cr, uid, [project_id])
- partner_id=data and data[0].partner_id
- if partner_id:
- return {'value':{'partner_id':partner_id.id}}
+ return {'value': {'remaining_hours': planned - effective}}
+
+ def onchange_project(self, cr, uid, id, project_id, context=None):
+ if project_id:
+ project = self.pool.get('project.project').browse(cr, uid, project_id, context=context)
+ if project and project.partner_id:
+ return {'value': {'partner_id': project.partner_id.id}}
return {}
+ def onchange_user_id(self, cr, uid, ids, user_id, context=None):
+ vals = {}
+ if user_id:
+ vals['date_start'] = fields.datetime.now()
+ return {'value': vals}
+
def duplicate_task(self, cr, uid, map_ids, context=None):
for new in map_ids.values():
task = self.browse(cr, uid, new, context)
@@ -754,13 +734,6 @@ class task(base_stage, osv.osv):
'sequence': fields.integer('Sequence', select=True, help="Gives the sequence order when displaying a list of tasks."),
'stage_id': fields.many2one('project.task.type', 'Stage', track_visibility='onchange',
domain="[('project_ids', '=', project_id)]"),
- 'state': fields.related('stage_id', 'state', type="selection", store=True,
- selection=_TASK_STATE, string="Status", readonly=True,
- help='The status is set to \'Draft\', when a case is created.\
- If the case is in progress the status is set to \'Open\'.\
- When the case is over, the status is set to \'Done\'.\
- If the case needs to be reviewed then the status is \
- set to \'Pending\'.'),
'categ_ids': fields.many2many('project.category', string='Tags'),
'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State',
track_visibility='onchange',
@@ -769,10 +742,12 @@ class task(base_stage, osv.osv):
" * Blocked indicates something is preventing the progress of this task\n"
" * Ready for next stage indicates the task is ready to be pulled to the next stage",
readonly=True, required=False),
- 'create_date': fields.datetime('Create Date', readonly=True,select=True),
+ 'create_date': fields.datetime('Create Date', readonly=True, select=True),
+ 'write_date': fields.datetime('Last Modification Date', readonly=True, select=True), #not displayed in the view but it might be useful with base_action_rule module (and it needs to be defined first for that)
'date_start': fields.datetime('Starting Date',select=True),
'date_end': fields.datetime('Ending Date',select=True),
'date_deadline': fields.date('Deadline',select=True),
+ 'date_last_stage_update': fields.datetime('Last Stage Update', select=True),
'project_id': fields.many2one('project.project', 'Project', ondelete='set null', select="1", track_visibility='onchange'),
'parent_ids': fields.many2many('project.task', 'project_task_parent_rel', 'task_id', 'parent_id', 'Parent Tasks'),
'child_ids': fields.many2many('project.task', 'project_task_parent_rel', 'parent_id', 'task_id', 'Delegated Tasks'),
@@ -789,7 +764,7 @@ class task(base_stage, osv.osv):
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours'], 10),
'project.task.work': (_get_task, ['hours'], 10),
}),
- 'progress': fields.function(_hours_get, string='Progress (%)', multi='hours', group_operator="avg", help="If the task has a progress of 99.99% you should close the task if it's finished or reevaluate the time",
+ 'progress': fields.function(_hours_get, string='Working Time Progress (%)', multi='hours', group_operator="avg", help="If the task has a progress of 99.99% you should close the task if it's finished or reevaluate the time",
store = {
'project.task': (lambda self, cr, uid, ids, c={}: ids, ['work_ids', 'remaining_hours', 'planned_hours','state'], 10),
'project.task.work': (_get_task, ['hours'], 10),
@@ -812,13 +787,15 @@ class task(base_stage, osv.osv):
_defaults = {
'stage_id': _get_default_stage_id,
'project_id': _get_default_project_id,
+ 'date_last_stage_update': lambda *a: fields.datetime.now(),
'kanban_state': 'normal',
'priority': '2',
'progress': 0,
'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),
+ 'user_id': lambda obj, cr, uid, ctx=None: uid,
+ 'company_id': lambda self, cr, uid, ctx=None: self.pool.get('res.company')._company_default_get(cr, uid, 'project.task', context=ctx),
+ 'partner_id': lambda self, cr, uid, ctx=None: self._get_default_partner(cr, uid, context=ctx),
}
_order = "priority, sequence, date_start, name, id"
@@ -938,7 +915,7 @@ class task(base_stage, osv.osv):
section_ids.append(task.project_id.id)
search_domain = []
if section_ids:
- search_domain = [('|')] * (len(section_ids)-1)
+ search_domain = [('|')] * (len(section_ids) - 1)
for section_id in section_ids:
search_domain.append(('project_ids', '=', section_id))
search_domain += list(domain)
@@ -955,82 +932,10 @@ class task(base_stage, osv.osv):
for task in tasks:
if task.child_ids:
for child in task.child_ids:
- if child.state in ['draft', 'open', 'pending']:
- raise osv.except_osv(_("Warning !"), _("Child task still open.\nPlease cancel or complete child task first."))
+ if child.stage_id and not child.stage_id.fold:
+ raise osv.except_osv(_("Warning!"), _("Child task still open.\nPlease cancel or complete child task first."))
return True
- def action_close(self, cr, uid, ids, context=None):
- """ This action closes the task
- """
- task_id = len(ids) and ids[0] or False
- self._check_child_task(cr, uid, ids, context=context)
- if not task_id: return False
- return self.do_close(cr, uid, [task_id], context=context)
-
- def do_close(self, cr, uid, ids, context=None):
- """ Compatibility when changing to case_close. """
- return self.case_close(cr, uid, ids, context=context)
-
- def case_close(self, cr, uid, ids, context=None):
- """ Closes Task """
- if not isinstance(ids, list): ids = [ids]
- for task in self.browse(cr, uid, ids, context=context):
- vals = {}
- project = task.project_id
- for parent_id in task.parent_ids:
- if parent_id.state in ('pending','draft'):
- reopen = True
- for child in parent_id.child_ids:
- if child.id != task.id and child.state not in ('done','cancelled'):
- reopen = False
- if reopen:
- self.do_reopen(cr, uid, [parent_id.id], context=context)
- # close task
- vals['remaining_hours'] = 0.0
- if not task.date_end:
- vals['date_end'] = fields.datetime.now()
- self.case_set(cr, uid, [task.id], 'done', vals, context=context)
- return True
-
- def do_reopen(self, cr, uid, ids, context=None):
- for task in self.browse(cr, uid, ids, context=context):
- project = task.project_id
- self.case_set(cr, uid, [task.id], 'open', {}, context=context)
- return True
-
- def do_cancel(self, cr, uid, ids, context=None):
- """ Compatibility when changing to case_cancel. """
- return self.case_cancel(cr, uid, ids, context=context)
-
- def case_cancel(self, cr, uid, ids, context=None):
- tasks = self.browse(cr, uid, ids, context=context)
- self._check_child_task(cr, uid, ids, context=context)
- for task in tasks:
- self.case_set(cr, uid, [task.id], 'cancelled', {'remaining_hours': 0.0}, context=context)
- return True
-
- 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]
- return self.case_set(cr, uid, ids, 'open', {'date_start': fields.datetime.now()}, context=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):
- return self.case_set(cr, uid, ids, 'draft', {}, context=context)
-
- 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):
- return self.case_set(cr, uid, ids, 'pending', {}, 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)
@@ -1051,6 +956,7 @@ class task(base_stage, osv.osv):
delegated_task_id = self.copy(cr, uid, task.id, {
'name': delegate_data['name'],
'project_id': delegate_data['project_id'] and delegate_data['project_id'][0] or False,
+ 'stage_id': delegate_data.get('stage_id') and delegate_data.get('stage_id')[0] or False,
'user_id': delegate_data['user_id'] and delegate_data['user_id'][0] or False,
'planned_hours': delegate_data['planned_hours'] or 0.0,
'parent_ids': [(6, 0, [task.id])],
@@ -1065,16 +971,12 @@ class task(base_stage, osv.osv):
'planned_hours': delegate_data['planned_hours_me'] + (task.effective_hours or 0.0),
'name': newname,
}, context=context)
- if delegate_data['state'] == 'pending':
- self.do_pending(cr, uid, [task.id], context=context)
- elif delegate_data['state'] == 'done':
- self.do_close(cr, uid, [task.id], context=context)
delegated_tasks[task.id] = delegated_task_id
return delegated_tasks
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):
+ if (task.stage_id and task.stage_id.sequence == 1) or (task.planned_hours == 0.0):
self.write(cr, uid, [task.id], {'planned_hours': remaining_time}, context=context)
self.write(cr, uid, ids, {'remaining_hours': remaining_time}, context=context)
return True
@@ -1109,50 +1011,56 @@ class task(base_stage, osv.osv):
'planned_hours': task.planned_hours,
'kanban_state': task.kanban_state,
'type_id': task.stage_id.id,
- 'state': task.state,
'user_id': task.user_id.id
}, context=context)
return True
+ # ------------------------------------------------
+ # CRUD overrides
+ # ------------------------------------------------
+
def create(self, cr, uid, vals, context=None):
if context is None:
context = {}
- if not vals.get('stage_id'):
- ctx = context.copy()
- if vals.get('project_id'):
- ctx['default_project_id'] = vals['project_id']
- vals['stage_id'] = self._get_default_stage_id(cr, uid, context=ctx)
+
+ # for default stage
+ if vals.get('project_id') and not context.get('default_project_id'):
+ context['default_project_id'] = vals.get('project_id')
+ # user_id change: update date_start
+ if vals.get('user_id'):
+ vals['date_start'] = fields.datetime.now()
+
# context: no_log, because subtype already handle this
create_context = dict(context, mail_create_nolog=True)
task_id = super(task, self).create(cr, uid, vals, context=create_context)
self._store_history(cr, uid, [task_id], context=context)
return task_id
- # Overridden to reset the kanban_state to normal whenever
- # 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.get('project_id'):
- project_id = self.pool.get('project.project').browse(cr, uid, vals.get('project_id'), context=context)
- if project_id:
- vals.setdefault('message_follower_ids', [])
- vals['message_follower_ids'] += [(6, 0,[follower.id]) for follower in project_id.message_follower_ids]
+
+ # stage change: update date_last_stage_update
+ if 'stage_id' in vals:
+ vals['date_last_stage_update'] = fields.datetime.now()
+ # user_id change: update date_start
+ if vals.get('user_id'):
+ vals['date_start'] = fields.datetime.now()
+
+ # Overridden to reset the kanban_state to normal whenever
+ # the stage (stage_id) of the task changes.
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):
- #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 ('stage_id' in vals) or ('remaining_hours' in vals) or ('user_id' in vals) or ('state' in vals) or ('kanban_state' in vals):
+
+ if any(item in vals for item in ['stage_id', 'remaining_hours', 'user_id', 'kanban_state']):
self._store_history(cr, uid, ids, context=context)
return result
@@ -1168,7 +1076,7 @@ class task(base_stage, osv.osv):
result = ""
ident = ' '*ident
for task in tasks:
- if task.state in ('done','cancelled'):
+ if task.stage_id and task.stage_id.fold:
continue
result += '''
%sdef Task_%s():
@@ -1199,17 +1107,6 @@ class task(base_stage, osv.osv):
return [task.project_id.message_get_reply_to()[0] if task.project_id else False
for task in self.browse(cr, uid, ids, context=context)]
- def check_mail_message_access(self, cr, uid, mids, operation, model_obj=None, context=None):
- """ mail.message document permission rule: can post a new message if can read
- because of portal document. """
- if not model_obj:
- model_obj = self
- if operation == 'create':
- model_obj.check_access_rights(cr, uid, 'read')
- model_obj.check_access_rule(cr, uid, mids, 'read', context=context)
- else:
- return super(task, self).check_mail_message_access(cr, uid, mids, operation, model_obj=model_obj, context=context)
-
def message_new(self, cr, uid, msg, custom_values=None, context=None):
""" Override to updates the document according to the email. """
if custom_values is None: custom_values = {}
@@ -1238,24 +1135,10 @@ class task(base_stage, osv.osv):
update_vals[field] = float(res.group(2).lower())
except (ValueError, TypeError):
pass
- elif match.lower() == 'state' \
- and res.group(2).lower() in ['cancel','close','draft','open','pending']:
- act = 'do_%s' % res.group(2).lower()
if act:
getattr(self,act)(cr, uid, ids, context=context)
return super(task,self).message_update(cr, uid, ids, msg, update_vals=update_vals, context=context)
- def project_task_reevaluate(self, cr, uid, ids, context=None):
- if self.pool.get('res.users').has_group(cr, uid, 'project.group_time_work_estimation_tasks'):
- return {
- 'view_type': 'form',
- "view_mode": 'form',
- 'res_model': 'project.task.reevaluate',
- 'type': 'ir.actions.act_window',
- 'target': 'new',
- }
- return self.do_reopen(cr, uid, ids, context=context)
-
class project_work(osv.osv):
_name = "project.task.work"
_description = "Project Task Work"
@@ -1376,7 +1259,7 @@ class project_task_history(osv.osv):
def _get_date(self, cr, uid, ids, name, arg, context=None):
result = {}
for history in self.browse(cr, uid, ids, context=context):
- if history.state in ('done','cancelled'):
+ if history.type_id and history.type_id.fold:
result[history.id] = history.date
continue
cr.execute('''select
@@ -1410,14 +1293,13 @@ class project_task_history(osv.osv):
_columns = {
'task_id': fields.many2one('project.task', 'Task', ondelete='cascade', required=True, select=True),
'type_id': fields.many2one('project.task.type', 'Stage'),
- 'state': fields.selection([('draft', 'New'), ('cancelled', 'Cancelled'),('open', 'In Progress'),('pending', 'Pending'), ('done', 'Done')], 'Status'),
- 'kanban_state': fields.selection([('normal', 'Normal'),('blocked', 'Blocked'),('done', 'Ready for next stage')], 'Kanban State', required=False),
+ 'kanban_state': fields.selection([('normal', 'Normal'), ('blocked', 'Blocked'), ('done', 'Ready for next stage')], 'Kanban State', required=False),
'date': fields.date('Date', select=True),
'end_date': fields.function(_get_date, string='End Date', type="date", store={
'project.task.history': (_get_related_date, None, 20)
}),
- 'remaining_hours': fields.float('Remaining Time', digits=(16,2)),
- 'planned_hours': fields.float('Planned Time', digits=(16,2)),
+ 'remaining_hours': fields.float('Remaining Time', digits=(16, 2)),
+ 'planned_hours': fields.float('Planned Time', digits=(16, 2)),
'user_id': fields.many2one('res.users', 'Responsible'),
}
_defaults = {
@@ -1447,7 +1329,7 @@ class project_task_history_cumulative(osv.osv):
SELECT
h.id AS history_id,
h.date+generate_series(0, CAST((coalesce(h.end_date, DATE 'tomorrow')::date - h.date) AS integer)-1) AS date,
- h.task_id, h.type_id, h.user_id, h.kanban_state, h.state,
+ h.task_id, h.type_id, h.user_id, h.kanban_state,
greatest(h.remaining_hours, 1) AS remaining_hours, greatest(h.planned_hours, 1) AS planned_hours,
t.project_id
FROM
diff --git a/addons/project/project_data.xml b/addons/project/project_data.xml
index 7bf1a623a2c..0f48dfe9a2b 100644
--- a/addons/project/project_data.xml
+++ b/addons/project/project_data.xml
@@ -30,51 +30,43 @@
<record id="project_tt_analysis" model="project.task.type">
<field name="sequence">1</field>
<field name="name">Analysis</field>
- <field name="state">draft</field>
- <field name="case_default" eval="False"/>
+ <field name="case_default" eval="True"/>
</record>
<record id="project_tt_specification" model="project.task.type">
- <field name="sequence">2</field>
+ <field name="sequence">10</field>
<field name="name">Specification</field>
- <field name="state">pending</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_design" model="project.task.type">
- <field name="sequence">2</field>
+ <field name="sequence">11</field>
<field name="name">Design</field>
- <field name="state">open</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_development" model="project.task.type">
- <field name="sequence">3</field>
+ <field name="sequence">12</field>
<field name="name">Development</field>
- <field name="state">open</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_testing" model="project.task.type">
- <field name="sequence">4</field>
+ <field name="sequence">13</field>
<field name="name">Testing</field>
- <field name="state">open</field>
<field name="case_default" eval="True"/>
</record>
<record id="project_tt_merge" model="project.task.type">
- <field name="sequence">5</field>
+ <field name="sequence">14</field>
<field name="name">Merge</field>
- <field name="state">open</field>
<field name="case_default" eval="False"/>
<field name="fold" eval="True"/>
</record>
<record id="project_tt_deployment" model="project.task.type">
- <field name="sequence">100</field>
+ <field name="sequence">20</field>
<field name="name">Done</field>
- <field name="state">done</field>
<field name="case_default" eval="True"/>
<field name="fold" eval="True"/>
</record>
<record id="project_tt_cancel" model="project.task.type">
- <field name="sequence">200</field>
+ <field name="sequence">30</field>
<field name="name">Cancelled</field>
- <field name="state">cancelled</field>
<field name="case_default" eval="True"/>
<field name="fold" eval="True"/>
</record>
@@ -86,11 +78,11 @@
<field name="default" eval="False"/>
<field name="description">Task created</field>
</record>
- <record id="mt_task_started" model="mail.message.subtype">
- <field name="name">Task Started</field>
+ <record id="mt_task_assigned" model="mail.message.subtype">
+ <field name="name">Task Assigned</field>
<field name="res_model">project.task</field>
<field name="default" eval="False"/>
- <field name="description">Task started</field>
+ <field name="description">Task Assigned</field>
</record>
<record id="mt_task_blocked" model="mail.message.subtype">
<field name="name">Task Blocked</field>
@@ -98,12 +90,6 @@
<field name="default" eval="False"/>
<field name="description">Task blocked</field>
</record>
- <record id="mt_task_closed" model="mail.message.subtype">
- <field name="name">Task Done</field>
- <field name="res_model">project.task</field>
- <field name="default" eval="False"/>
- <field name="description">Task closed</field>
- </record>
<record id="mt_task_stage" model="mail.message.subtype">
<field name="name">Stage Changed</field>
<field name="res_model">project.task</field>
@@ -118,11 +104,11 @@
<field name="parent_id" eval="ref('mt_task_new')"/>
<field name="relation_field">project_id</field>
</record>
- <record id="mt_project_task_started" model="mail.message.subtype">
- <field name="name">Task Started</field>
+ <record id="mt_project_task_assigned" model="mail.message.subtype">
+ <field name="name">Task Assigned</field>
<field name="res_model">project.project</field>
<field name="default" eval="False"/>
- <field name="parent_id" eval="ref('mt_task_started')"/>
+ <field name="parent_id" eval="ref('mt_task_assigned')"/>
<field name="relation_field">project_id</field>
</record>
<record id="mt_project_task_blocked" model="mail.message.subtype">
@@ -131,12 +117,6 @@
<field name="parent_id" eval="ref('mt_task_blocked')"/>
<field name="relation_field">project_id</field>
</record>
- <record id="mt_project_task_closed" model="mail.message.subtype">
- <field name="name">Task Done</field>
- <field name="res_model">project.project</field>
- <field name="parent_id" eval="ref('mt_task_closed')"/>
- <field name="relation_field">project_id</field>
- </record>
<record id="mt_project_task_stage" model="mail.message.subtype">
<field name="name">Task Stage Changed</field>
<field name="res_model">project.project</field>
diff --git a/addons/project/project_demo.xml b/addons/project/project_demo.xml
index b7dcc2bdb83..12e373642fd 100644
--- a/addons/project/project_demo.xml
+++ b/addons/project/project_demo.xml
@@ -46,9 +46,12 @@
<record id="project_project_2" model="project.project">
<field name="name">Research &amp; Development</field>
<field name="parent_id" ref="all_projects_account"/>
- <field name="privacy_visibility">public</field>
+ <field name="privacy_visibility">followers</field>
<field name="user_id" ref="base.user_demo"/>
<field name="alias_model">project.task</field>
+ <field name="message_follower_ids" eval="[(6, 0, [
+ ref('base.user_root'),
+ ref('base.user_demo')])]"/>
</record>
<!-- We assign after so that default values applies -->
@@ -75,6 +78,7 @@
<field name="parent_id" ref="all_projects_account"/>
<field name="name">Website Design Templates</field>
<field name="user_id" ref="base.user_root"/>
+ <field name="privacy_visibility">followers</field>
<field name="alias_model">project.task</field>
<field name="privacy_visibility">employees</field>
<field name="members" eval="[(4, ref('base.user_root')), (4,ref('base.user_demo'))]"/>
@@ -221,7 +225,6 @@
ref('project.project_category_04')])]"/>
<field name="stage_id" ref="project_tt_merge"/>
</record>
- <function model="project.task" name="do_close" eval="[ref('project_task_11')], {'install_mode': True}"/>
<record id="project_task_12" model="project.task">
<field name="planned_hours" eval="40.0"/>
@@ -233,7 +236,6 @@
<field name="stage_id" ref="project_tt_merge"/>
<field name="color">6</field>
</record>
- <function model="project.task" name="do_close" eval="[ref('project_task_12')], {'install_mode': True}"/>
<record id="project_task_13" model="project.task">
<field name="planned_hours" eval="12.0"/>
@@ -244,7 +246,6 @@
<field name="name">Design Use Cases</field>
<field name="stage_id" ref="project_tt_analysis"/>
</record>
- <function model="project.task" name="do_pending" eval="[ref('project_task_13')], {'install_mode': True}"/>
<record id="project_task_14" model="project.task">
<field name="planned_hours" eval="12.0"/>
@@ -278,7 +279,6 @@
<field name="name">Set target for all deparments</field>
<field name="stage_id" ref="project_tt_development"/>
</record>
- <function model="project.task" name="do_open" eval="[ref('project_task_16')], {'install_mode': True}"/>
<record id="project_task_17" model="project.task">
<field name="planned_hours" eval="34.0"/>
@@ -289,7 +289,6 @@
<field name="name">Integration of core components</field>
<field name="stage_id" ref="project_tt_testing"/>
</record>
- <function model="project.task" name="do_open" eval="[ref('project_task_17')], {'install_mode': True}"/>
<record id="project_task_18" model="project.task">
<field name="planned_hours" eval="16.0"/>
@@ -311,7 +310,6 @@
<field name="categ_ids" eval="[(6, 0, [
ref('project_category_03')])]"/>
</record>
- <function model="project.task" name="do_open" eval="[ref('project_task_19')], {'install_mode': True}"/>
<record id="project_task_20" model="project.task">
<field name="planned_hours">42.0</field>
@@ -321,7 +319,6 @@
<field name="project_id" ref="project.project_project_4"/>
<field name="name">Create new components</field>
</record>
- <function model="project.task" name="do_open" eval="[ref('project_task_20')], {'install_mode': True}"/>
<record id="project_task_21" model="project.task">
<field name="planned_hours">14.0</field>
@@ -333,7 +330,6 @@
<field name="categ_ids" eval="[(6, 0, [
ref('project_category_04')])]"/>
</record>
- <function model="project.task" name="do_open" eval="[ref('project_task_21')], {'install_mode': True}"/>
<record id="project_task_22" model="project.task">
<field name="planned_hours">12.0</field>
@@ -367,7 +363,6 @@
<field name="categ_ids" eval="[(6, 0, [
ref('project_category_01')])]"/>
</record>
- <function model="project.task" name="do_open" eval="[ref('project_task_24')], {'install_mode': True}"/>
<record id="project_task_25" model="project.task">
<field name="sequence">20</field>
@@ -378,7 +373,6 @@
<field name="name">Data importation + Doc</field>
<field name="stage_id" ref="project_tt_development"/>
</record>
- <function model="project.task" name="do_open" eval="[ref('project_task_25')], {'install_mode': True}"/>
<record id="project_task_26" model="project.task">
<field name="sequence">20</field>
diff --git a/addons/project/project_view.xml b/addons/project/project_view.xml
index eb042e5f0c7..37836b0e07a 100644
--- a/addons/project/project_view.xml
+++ b/addons/project/project_view.xml
@@ -18,28 +18,26 @@
<search string="Tasks">
<field name="name" string="Tasks"/>
<field name="categ_ids"/>
+ <filter string="Unassigned" name="unassigned" domain="[('user_id', '=', False)]"/>
+ <filter string="New" name="draft" domain="[('stage_id.sequence', '=', 1)]"/>
<separator/>
- <filter icon="terp-mail-message-new" string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
- <separator/>
- <filter name="draft" string="New" domain="[('state','=','draft')]" help="New Tasks" icon="terp-check"/>
- <filter name="open" string="In Progress" domain="[('state','=','open')]" help="In Progress Tasks" icon="terp-camera_test"/>
- <filter string="Pending" domain="[('state','=','pending')]" context="{'show_delegated':False}" help="Pending Tasks" icon="terp-gtk-media-pause"/>
+ <filter string="My Tasks" domain="[('user_id','=',uid)]"/>
<separator/>
- <filter name="My project" string="Project" domain="[('project_id.user_id','=',uid)]" help="My Projects" icon="terp-check"/>
- <separator/>
- <filter string="My Tasks" domain="[('user_id','=',uid)]" help="My Tasks" icon="terp-personal"/>
- <filter string="Unassigned Tasks" domain="[('user_id','=',False)]" help="Unassigned Tasks" icon="terp-personal-"/>
+ <filter string="Unread Messages" name="message_unread" domain="[('message_unread','=',True)]"/>
<separator/>
<filter string="Deadlines" context="{'deadline_visible': False}" domain="[('date_deadline','&lt;&gt;',False)]"
- help="Show only tasks having a deadline" icon="terp-gnome-cpu-frequency-applet+"/>
+ help="Show only tasks having a deadline"/>
+ <field name="partner_id"/>
<field name="project_id"/>
<field name="user_id"/>
+ <field name="stage_id" domain="[]"/>
<group expand="0" string="Group By...">
<filter string="Users" name="group_user_id" icon="terp-personal" domain="[]" context="{'group_by':'user_id'}"/>
<filter string="Project" name="group_project_id" icon="terp-folder-violet" domain="[]" context="{'group_by':'project_id'}"/>
<filter string="Stage" name="group_stage_id" icon="terp-stage" domain="[]" context="{'group_by':'stage_id'}"/>
+ <filter string="Last Stage Update" icon="terp-go-month" domain="[]" context="{'group_by':'date_last_stage_update'}"/>
<filter string="Deadline" icon="terp-gnome-cpu-frequency-applet+" domain="[]" context="{'group_by':'date_deadline'}"/>
- <filter string="Start Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}" groups="base.group_no_one"/>
+ <filter string="Start Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_start'}"/>
<filter string="End Date" icon="terp-go-month" domain="[]" context="{'group_by':'date_end'}" groups="base.group_no_one"/>
</group>
</search>
@@ -89,17 +87,6 @@
<h1>
<field name="name" string="Project Name"/>
</h1>
- <div name="group_alias"
- attrs="{'invisible': [('alias_domain', '=', False)]}">
- <label for="alias_id" string="Email Alias"/>
- <field name="alias_id" class="oe_inline oe_read_only" required="0" nolabel="1"/>
- <span name="edit_alias" class="oe_edit_only">
- <field name="alias_name" class="oe_inline"
- attrs="{'required': [('alias_id', '!=', False)]}"/>
- @
- <field name="alias_domain" class="oe_inline" readonly="1"/>
- </span>
- </div>
<div name="options_active">
<field name="use_tasks" class="oe_inline"/>
<label for="use_tasks"/>
@@ -112,17 +99,31 @@
</div>
<group>
<group>
- <field name="privacy_visibility"/>
<field name="user_id" string="Project Manager"
- attrs="{'readonly':[('state','in',['close', 'cancelled'])]}"
- context="{'default_groups_ref': ['base.group_user', 'project.group_project_manager']}"/>
- </group>
- <group>
+ attrs="{'readonly':[('state','in',['close', 'cancelled'])]}"
+ context="{'default_groups_ref': ['base.group_user', 'project.group_project_manager']}"/>
<field name="partner_id" on_change="onchange_partner_id(partner_id)"/>
- <p colspan="2" attrs="{'invisible': [('analytic_account_id','=',False)]}">
- To invoice or setup invoicing and renewal options, go to the related contract: <field name="analytic_account_id" readonly="1" required="0" class="oe_inline"/>.
+ <span></span>
+ <p attrs="{'invisible': [('analytic_account_id','=',False)]}">
+ To invoice or setup invoicing and renewal options, go to the related contract:
+ <field name="analytic_account_id" readonly="1" required="0" class="oe_inline" nolabel="1"/>.
</p>
</group>
+ <group name="group_alias"
+ attrs="{'invisible': [('alias_domain', '=', False)]}">
+ <label for="alias_name" string="Email Alias"/>
+ <div name="alias_def">
+ <field name="alias_id" class="oe_read_only oe_inline"
+ string="Email Alias" required="0"/>
+ <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" >
+ <field name="alias_name" class="oe_inline"/>@<field name="alias_domain" class="oe_inline" readonly="1"/>
+ </div>
+ </div>
+ <label for="alias_model" string="Incoming Emails create"/>
+ <field name="alias_model" class="oe_inline" nolabel="1"/>
+ <field name="alias_contact" class="oe_inline"
+ string="Accept Emails From"/>
+ </group>
</group>
<notebook>
<page string="Team" name="team">
@@ -134,6 +135,7 @@
<div style="position: relative">
<a t-if="! read_only_mode" type="delete" style="position: absolute; right: 0; padding: 4px; diplay: inline-block">X</a>
<div class="oe_module_vignette">
+ <img t-att-src="kanban_image('res.users', 'image_small', record.id.value)" class="oe_avatar oe_kanban_avatar_smallbox"/>
<div class="oe_module_desc">
<field name="name"/>
</div>
@@ -145,20 +147,22 @@
</field>
</page>
<page string="Other Info">
- <group>
- <group string="Administration" groups="project.group_time_work_estimation_tasks">
- <field name="planned_hours" widget="float_time"/>
- <field name="effective_hours" widget="float_time"/>
- <field name="resource_calendar_id"/>
- </group>
- <group string="Miscellaneous" name="misc">
- <field name="date_start"/>
- <field name="date" string="End Date"/>
- <field name="priority" groups="base.group_no_one"/>
- <field name="active" attrs="{'invisible':[('state','in',['open', 'pending', 'template'])]}"/>
- <field name="currency_id" groups="base.group_multi_currency" required="1"/>
- <field name="parent_id" string="Parent" help="Append this project to another one using analytic accounts hierarchy" domain="[('id','!=',analytic_account_id)]" context="{'current_model': 'project.project'}" />
- </group>
+ <group string="Administration">
+ <field name="privacy_visibility" widget="radio"/>
+ <field name="planned_hours" widget="float_time"
+ groups="project.group_time_work_estimation_tasks"/>
+ <field name="effective_hours" widget="float_time"
+ groups="project.group_time_work_estimation_tasks"/>
+ <field name="resource_calendar_id"
+ groups="project.group_time_work_estimation_tasks"/>
+ </group>
+ <group string="Miscellaneous" name="misc">
+ <field name="date_start"/>
+ <field name="date" string="End Date"/>
+ <field name="priority" groups="base.group_no_one"/>
+ <field name="active" attrs="{'invisible':[('state','in',['open', 'pending', 'template'])]}"/>
+ <field name="currency_id" groups="base.group_multi_currency" required="1"/>
+ <field name="parent_id" string="Parent" help="Append this project to another one using analytic accounts hierarchy" domain="[('id','!=',analytic_account_id)]" context="{'current_model': 'project.project'}" />
</group>
</page>
<page string="Project Stages" attrs="{'invisible': [('use_tasks', '=', False)]}" name="project_stages">
@@ -252,7 +256,7 @@
<div class="oe_kanban_content">
<h4><field name="name"/></h4>
<div class="oe_kanban_alias" t-if="record.alias_id.value">
- <span class="oe_e">%%</span><small><field name="alias_id"/></small>
+ <span class="oe_e oe_e_alias">%%</span><small><field name="alias_id"/></small>
</div>
<div class="oe_kanban_project_list">
<a t-if="record.use_tasks.raw_value" name="%(act_project_project_2_project_task_all)d" type="action" style="margin-right: 10px">
@@ -365,18 +369,6 @@
<field name="arch" type="xml">
<form string="Project" version="7.0">
<header>
- <!--
- <button name="do_open" string="Start Task" type="object"
- states="draft,pending" class="oe_highlight"/>
- <button name="do_draft" string="Draft" type="object"
- states="cancel,done"/>
- -->
- <button name="project_task_reevaluate" string="Reactivate" type="object"
- states="cancelled,done" context="{'button_reactivate':True}" groups="base.group_user"/>
- <button name="action_close" string="Done" type="object"
- states="draft,open,pending" groups="base.group_user"/>
- <button name="do_cancel" string="Cancel Task" type="object"
- states="draft,open,pending" groups="base.group_user"/>
<field name="stage_id" widget="statusbar" clickable="True"/>
</header>
<sheet string="Task">
@@ -390,8 +382,7 @@
<group>
<group>
<field name="project_id" on_change="onchange_project(project_id)" context="{'default_use_tasks':1}"/>
- <field name="user_id"
- attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"
+ <field name="user_id"
options='{"no_open": True}'
context="{'default_groups_ref': ['base.group_user', 'project.group_project_user']}"/>
<field name="planned_hours" widget="float_time"
@@ -399,15 +390,15 @@
on_change="onchange_planned(planned_hours, effective_hours)"/>
</group>
<group>
- <field name="date_deadline" attrs="{'readonly':[('state','in',['done', 'cancelled'])]}"/>
+ <field name="date_deadline"/>
<field name="categ_ids" widget="many2many_tags"/>
<field name="progress" widget="progressbar"
- groups="project.group_time_work_estimation_tasks" attrs="{'invisible':[('state','=','cancelled')]}"/>
+ groups="project.group_time_work_estimation_tasks"/>
</group>
</group>
<notebook>
<page string="Description">
- <field name="description" attrs="{'readonly':[('state','=','done')]}" placeholder="Add a Description..."/>
+ <field name="description" placeholder="Add a Description..."/>
<field name="work_ids" groups="project.group_tasks_work_on_tasks">
<tree string="Task Work" editable="top">
<field name="name"/>
@@ -421,7 +412,7 @@
<field name="effective_hours" widget="float_time"/>
<label for="remaining_hours" string="Remaining" groups="project.group_time_work_estimation_tasks"/>
<div>
- <field name="remaining_hours" widget="float_time" attrs="{'readonly':[('state','in',('done','cancelled'))]}" groups="project.group_time_work_estimation_tasks"/>
+ <field name="remaining_hours" widget="float_time" groups="project.group_time_work_estimation_tasks"/>
</div>
<field name="total_hours" widget="float_time" class="oe_subtotal_footer_separator"/>
</group>
@@ -430,7 +421,7 @@
</page>
<page string="Delegation" groups="project.group_delegate_task">
<button name="%(action_project_task_delegate)d" string="Delegate" type="action"
- states="pending,open,draft" groups="project.group_delegate_task"/>
+ groups="project.group_delegate_task"/>
<separator string="Parent Tasks"/>
<field name="parent_ids"/>
<separator string="Delegated tasks"/>
@@ -439,7 +430,6 @@
<field name="name"/>
<field name="user_id"/>
<field name="stage_id"/>
- <field name="state" invisible="1"/>
<field name="effective_hours" widget="float_time"/>
<field name="progress" widget="progressbar"/>
<field name="remaining_hours" widget="float_time"/>
@@ -447,12 +437,11 @@
</tree>
</field>
</page>
- <page string="Extra Info" attrs="{'readonly':[('state','=','done')]}">
+ <page string="Extra Info">
<group col="4">
<field name="priority" groups="base.group_user"/>
<field name="sequence"/>
<field name="partner_id"/>
- <field name="state" invisible="1"/>
<field name="company_id" groups="base.group_multi_company" widget="selection"/>
</group>
<group>
@@ -461,6 +450,7 @@
<field name="date_end"/>
</group>
<group>
+ <field name="date_last_stage_update" groups="base.group_no_one"/>
</group>
</group>
</page>
@@ -487,7 +477,6 @@
<field name="user_email"/>
<field name="description"/>
<field name="sequence"/>
- <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"/>
@@ -507,7 +496,6 @@
<li><a name="set_remaining_time_2" type="object" class="oe_kanban_button">2</a></li>
<li><a name="set_remaining_time_5" type="object" class="oe_kanban_button">5</a></li>
<li><a name="set_remaining_time_10" type="object" class="oe_kanban_button">10</a></li>
- <li><a name="do_open" states="draft" string="Validate planned time" type="object" class="oe_kanban_button oe_kanban_button_active">!</a></li>
</ul>
</li>
<br/>
@@ -556,7 +544,7 @@
<field name="model">project.task</field>
<field eval="2" name="priority"/>
<field name="arch" type="xml">
- <tree fonts="bold:message_unread==True" colors="grey:state in ('cancelled','done');blue:state == 'pending';red:date_deadline and (date_deadline&lt;current_date) and (state in ('draft','pending','open'))" string="Tasks">
+ <tree fonts="bold:message_unread==True" colors="red:date_deadline and (date_deadline&lt;current_date)" string="Tasks">
<field name="message_unread" invisible="1"/>
<field name="sequence" invisible="not context.get('seq_visible', False)"/>
<field name="name"/>
@@ -569,7 +557,6 @@
<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="stage_id" invisible="context.get('set_visible',False)"/>
- <field name="state" invisible="1"/>
<field name="date_start" groups="base.group_no_one"/>
<field name="date_end" groups="base.group_no_one"/>
<field name="progress" widget="progressbar" invisible="context.get('set_visible',False)"/>
@@ -655,7 +642,7 @@
<field name="res_model">project.task</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form,calendar,graph,kanban</field>
- <field name="domain">[('date_deadline','&lt;',time.strftime('%Y-%m-%d')),('state','in',('draft','pending','open'))]</field>
+ <field name="domain">[('date_deadline','&lt;',time.strftime('%Y-%m-%d'))]</field>
<field name="filter" eval="True"/>
<field name="search_view_id" ref="view_task_search_form"/>
</record>
@@ -700,7 +687,6 @@
<field name="case_default"/>
</group>
<group>
- <field name="state"/>
<field name="sequence"/>
<field name="fold"/>
</group>
@@ -717,7 +703,7 @@
<tree string="Task Stage">
<field name="sequence" widget="handle"/>
<field name="name"/>
- <field name="state"/>
+ <field name="fold"/>
</tree>
</field>
</record>
@@ -761,7 +747,9 @@
</record>
<!-- User Form -->
- <act_window context="{'search_default_user_id': [active_id], 'default_user_id': active_id}" domain="[('state', '&lt;&gt;', 'cancelled'),('state', '&lt;&gt;', 'done')]" id="act_res_users_2_project_task_opened" name="Assigned Tasks" res_model="project.task" src_model="res.users" view_mode="tree,form,gantt,calendar,graph" view_type="form"/>
+ <act_window context="{'search_default_user_id': [active_id], 'default_user_id': active_id}"
+ id="act_res_users_2_project_task_opened" name="Assigned Tasks"
+ res_model="project.task" src_model="res.users" view_mode="tree,form,gantt,calendar,graph" view_type="form"/>
<!-- Tags -->
<record model="ir.ui.view" id="project_category_search_view">
diff --git a/addons/project/report/project_cumulative.xml b/addons/project/report/project_cumulative.xml
index 17e059c8de7..9b9490f0b28 100644
--- a/addons/project/report/project_cumulative.xml
+++ b/addons/project/report/project_cumulative.xml
@@ -14,7 +14,6 @@
<field name="user_id"/>
<field name="remaining_hours"/>
<field name="kanban_state"/>
- <field name="state" groups="base.group_no_one"/>
</tree>
</field>
</record>
@@ -40,8 +39,7 @@
<field name="date"/>
<field name="project_id"/>
<field name="user_id"/>
- <filter name="open" string="In Progress Tasks" domain="[('state','in',('open','draft'))]"/>
- <filter string="Pending Tasks" domain="[('state','=','pending')]" context="{'show_delegated':False}"/>
+ <filter name="new" string="New" domain="[('type_id.sequence', '=', 1)]"/>
<separator/>
<filter name="kanban_blocked" string="Blocked" domain="[('kanban_state','=','blocked')]"/>
<filter name="kanban_ready" string="Ready" domain="[('kanban_state','=','done')]"/>
diff --git a/addons/project/report/project_report.py b/addons/project/report/project_report.py
index 006aae1085c..15b26507ff3 100644
--- a/addons/project/report/project_report.py
+++ b/addons/project/report/project_report.py
@@ -19,9 +19,10 @@
#
##############################################################################
-from openerp.osv import fields,osv
+from openerp.osv import fields, osv
from openerp import tools
+
class report_project_task_user(osv.osv):
_name = "report.project.task.user"
_description = "Tasks by user and project"
@@ -31,10 +32,11 @@ class report_project_task_user(osv.osv):
'day': fields.char('Day', size=128, readonly=True),
'year': fields.char('Year', size=64, required=False, readonly=True),
'user_id': fields.many2one('res.users', 'Assigned To', readonly=True),
- 'date_start': fields.date('Starting Date',readonly=True),
+ 'date_start': fields.date('Assignation Date', readonly=True),
'no_of_days': fields.integer('# of Days', size=128, readonly=True),
'date_end': fields.date('Ending Date', readonly=True),
'date_deadline': fields.date('Deadline', readonly=True),
+ 'date_last_stage_update': fields.date('Last Stage Update', readonly=True),
'project_id': fields.many2one('project.project', 'Project', readonly=True),
'hours_planned': fields.float('Planned Hours', readonly=True),
'hours_effective': fields.float('Effective Hours', readonly=True),
@@ -44,16 +46,17 @@ class report_project_task_user(osv.osv):
'total_hours': fields.float('Total Hours', readonly=True),
'closing_days': fields.float('Days to Close', digits=(16,2), readonly=True, group_operator="avg",
help="Number of Days to close the task"),
- 'opening_days': fields.float('Days to Open', digits=(16,2), readonly=True, group_operator="avg",
+ 'opening_days': fields.float('Days to Assign', digits=(16,2), readonly=True, group_operator="avg",
help="Number of Days to Open the task"),
'delay_endings_days': fields.float('Overpassed Deadline', digits=(16,2), readonly=True),
'nbr': fields.integer('# of tasks', readonly=True),
- 'priority' : fields.selection([('4','Very Low'), ('3','Low'), ('2','Medium'), ('1','Urgent'),
-('0','Very urgent')], 'Priority', readonly=True),
- 'month':fields.selection([('01','January'), ('02','February'), ('03','March'), ('04','April'), ('05','May'), ('06','June'), ('07','July'), ('08','August'), ('09','September'), ('10','October'), ('11','November'), ('12','December')], 'Month', readonly=True),
+ 'priority': fields.selection([('4', 'Very Low'), ('3', 'Low'), ('2', 'Medium'), ('1', 'Urgent'), ('0', 'Very urgent')],
+ string='Priority', readonly=True),
+ 'month':fields.selection(fields.date.MONTHS, 'Month', readonly=True),
'state': fields.selection([('draft', 'Draft'), ('open', 'In Progress'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('done', 'Done')],'Status', readonly=True),
'company_id': fields.many2one('res.company', 'Company', readonly=True),
'partner_id': fields.many2one('res.partner', 'Contact', readonly=True),
+ 'stage_id': fields.many2one('project.task.type', 'Stage'),
}
_order = 'name desc, project_id'
@@ -69,19 +72,19 @@ class report_project_task_user(osv.osv):
to_char(date_start, 'YYYY-MM-DD') as day,
date_trunc('day',t.date_start) as date_start,
date_trunc('day',t.date_end) as date_end,
+ date_trunc('day',t.date_last_stage_update) as date_last_stage_update,
to_date(to_char(t.date_deadline, 'dd-MM-YYYY'),'dd-MM-YYYY') as date_deadline,
-- sum(cast(to_char(date_trunc('day',t.date_end) - date_trunc('day',t.date_start),'DD') as int)) as no_of_days,
abs((extract('epoch' from (t.date_end-t.date_start)))/(3600*24)) as no_of_days,
t.user_id,
progress as progress,
t.project_id,
- t.state,
t.effective_hours as hours_effective,
t.priority,
t.name as name,
t.company_id,
t.partner_id,
- t.stage_id,
+ t.stage_id as stage_id,
remaining_hours as remaining_hours,
total_hours as total_hours,
t.delay_hours as hours_delay,
@@ -106,15 +109,12 @@ class report_project_task_user(osv.osv):
date_start,
date_end,
date_deadline,
+ date_last_stage_update,
t.user_id,
t.project_id,
- t.state,
t.priority,
name,
t.company_id,
t.partner_id,
- t.stage_id
-
+ stage_id
""")
-
-
diff --git a/addons/project/report/project_report_view.xml b/addons/project/report/project_report_view.xml
index d9650e165ce..d5638e332a4 100644
--- a/addons/project/report/project_report_view.xml
+++ b/addons/project/report/project_report_view.xml
@@ -13,11 +13,12 @@
<tree string="Tasks Analysis" create="false">
<field name="name" invisible="1"/>
<field name="project_id" invisible="1"/>
+ <field name="stage_id" invisible="1"/>
<field name="user_id" invisible="1"/>
<field name="date_deadline" invisible="1"/>
- <field name="state" invisible="1"/>
<field name="date_start" invisible="1"/>
<field name="date_end" invisible="1"/>
+ <field name="date_last_stage_update" invisible="1"/>
<field name="company_id" invisible="1" groups="base.group_multi_company"/>
<field name="partner_id" invisible="1"/>
<field name="day" invisible="1"/>
@@ -44,7 +45,7 @@
<field name="arch" type="xml">
<graph string="Tasks Analysis" type="bar">
<field name="name"/>
- <field name="state" group="True"/>
+ <field name="stage_id" group="True"/>
<field name="no_of_days" operator="+"/>
</graph>
</field>
@@ -58,31 +59,29 @@
<field name="date_start"/>
<field name="date_end"/>
<field name="date_deadline"/>
- <filter string="New" icon="terp-document-new" domain="[('state','=','draft')]" help = "New tasks"/>
- <filter string="In progress" icon="terp-check" domain="[('state', '=' ,'open')]" help = "In progress tasks"/>
- <filter string="Pending" icon="terp-gtk-media-pause" domain="[('state','=','pending')]" help = "Pending tasks"/>
- <filter string="Done" icon="terp-dialog-close" name="done" domain="[('state','=','done')]"/>
- <separator/>
- <filter icon="terp-folder-violet" string="My Projects" help="My Projects" domain="[('project_id.user_id','=',uid)]"/>
- <separator/>
- <filter icon="terp-personal" string="My Task" help = "My tasks" domain="[('user_id','=',uid)]" />
- <filter icon="terp-personal-" string="Non Assigned Tasks to users" help="Non Assigned Tasks to users" domain="[('user_id','=',False)]"/>
+ <field name="date_last_stage_update"/>
<field name="project_id"/>
<field name="user_id"/>
<field name="partner_id" filter_domain="[('partner_id', 'child_of', self)]"/>
+ <field name="stage_id"/>
+ <filter string="Unassigned" name="unassigned" domain="[('user_id','=',False)]"/>
+ <filter string="New" name="new" domain="[('stage_id.sequence', '=', 1)]"/>
+ <separator/>
+ <filter string="My Task" domain="[('user_id','=',uid)]" />
<group expand="0" string="Extended Filters...">
<field name="priority"/>
<field name="company_id" groups="base.group_multi_company"/>
</group>
<group expand="1" string="Group By...">
- <filter string="Project" name="project" icon="terp-folder-violet" context="{'group_by':'project_id'}"/>
- <filter string="Task" icon="terp-stock_align_left_24" context="{'group_by':'name'}" />
- <filter string="Contact" icon="terp-partner" context="{'group_by':'partner_id'}" />
- <filter string="Assigned to" name="User" icon="terp-personal" context="{'group_by':'user_id'}" />
- <filter string="Company" icon="terp-go-home" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
- <filter string="Day" icon="terp-go-today" context="{'group_by':'day'}" help="Creation Date"/>
- <filter string="Month" icon="terp-go-month" context="{'group_by':'month'}" help="Creation Date"/>
- <filter string="Year" icon="terp-go-year" context="{'group_by':'year'}" help="Creation Date"/>
+ <filter string="Project" name="project" context="{'group_by':'project_id'}"/>
+ <filter string="Task" context="{'group_by':'name'}" />
+ <filter string="Contact" context="{'group_by':'partner_id'}" />
+ <filter string="Assigned to" name="User" context="{'group_by':'user_id'}" />
+ <filter string="Company" context="{'group_by':'company_id'}" groups="base.group_multi_company"/>
+ <filter string="Day" context="{'group_by':'day'}" help="Creation Date"/>
+ <filter string="Month" context="{'group_by':'month'}" help="Creation Date"/>
+ <filter string="Year" context="{'group_by':'year'}" help="Creation Date"/>
+ <filter string="Last Stage Update" context="{'group_by':'date_last_stage_update'}" help="Last Stage Update"/>
</group>
</search>
</field>
diff --git a/addons/project/res_partner.py b/addons/project/res_partner.py
index e4ca63d79d8..e49cacf4f89 100644
--- a/addons/project/res_partner.py
+++ b/addons/project/res_partner.py
@@ -29,6 +29,14 @@ class res_partner(osv.osv):
'task_ids': fields.one2many('project.task', 'partner_id', 'Tasks'),
}
+ def copy(self, cr, uid, record_id, default=None, context=None):
+ if default is None:
+ default = {}
+
+ default['task_ids'] = []
+ return super(res_partner, self).copy(
+ cr, uid, record_id, default=default, context=context)
+
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project/res_partner_view.xml b/addons/project/res_partner_view.xml
index d668e3c7092..f12189b5163 100644
--- a/addons/project/res_partner_view.xml
+++ b/addons/project/res_partner_view.xml
@@ -4,28 +4,20 @@
<!-- Partners inherited form -->
<record id="view_task_partner_info_form" model="ir.ui.view">
- <field name="name">res.partner.task.info.inherit</field>
+ <field name="name">res.partner.task.buttons</field>
<field name="model">res.partner</field>
<field name="inherit_id" ref="base.view_partner_form"/>
+ <field name="priority" eval="50"/>
<field name="arch" type="xml">
- <xpath expr="//page[@name='page_history']" position="attributes">
- <attribute name="invisible">False</attribute>
- </xpath>
- <xpath expr="//page[@name='page_history']" position="inside">
- <group name="grp_task" string="Tasks">
- <field name="task_ids" colspan="4" nolabel="1">
- <tree string="Tasks" editable="bottom">
- <field name="name"/>
- <field name="user_id"/>
- <field name="date_deadline"/>
- <field name="state" invisible="1"/>
- <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_close" states="draft,pending,open" string="Done" type="object" icon="terp-dialog-close" help="For changing to done state"/>
- </tree>
- </field>
- </group>
+ <xpath expr="//div[@name='buttons']" position="inside">
+ <button class="oe_inline" type="action"
+ name="%(action_view_task)d"
+ string="Tasks"
+ context="{'search_default_partner_id': active_id, 'default_partner_id': active_id}"
+ groups="project.group_project_user" />
</xpath>
</field>
</record>
+
</data>
</openerp>
diff --git a/addons/project/security/ir.model.access.csv b/addons/project/security/ir.model.access.csv
index a76faa0fcce..c88fb276fcb 100644
--- a/addons/project/security/ir.model.access.csv
+++ b/addons/project/security/ir.model.access.csv
@@ -19,6 +19,7 @@ access_project_task_history,project.task.history project,project.model_project_t
access_project_task_history_cumulative,project.task.history project,project.model_project_task_history_cumulative,project.group_project_manager,1,0,0,0
access_project_task_history_cumulative,project.task.history project,project.model_project_task_history_cumulative,project.group_project_user,1,0,0,0
access_resource_calendar,project.resource_calendar manager,resource.model_resource_calendar,project.group_project_manager,1,0,0,0
+access_resource_calendar_leaves_user,resource.calendar.leaves user,resource.model_resource_calendar_leaves,project.group_project_user,1,1,1,1
access_project_category,project.project_category,model_project_category,,1,0,0,0
access_project_category_manager,project.project_category,model_project_category,project.group_project_manager,1,1,1,1
access_mail_alias,mail.alias,mail.model_mail_alias,project.group_project_manager,1,1,1,1
diff --git a/addons/project/html/Mario-Riva.png b/addons/project/static/description/Mario-Riva.png
index 35b030a4265..35b030a4265 100644
--- a/addons/project/html/Mario-Riva.png
+++ b/addons/project/static/description/Mario-Riva.png
Binary files differ
diff --git a/addons/project/html/Yoshi-Tashiro.png b/addons/project/static/description/Yoshi-Tashiro.png
index f3457a9088e..f3457a9088e 100644
--- a/addons/project/html/Yoshi-Tashiro.png
+++ b/addons/project/static/description/Yoshi-Tashiro.png
Binary files differ
diff --git a/addons/project/static/src/img/icon.png b/addons/project/static/description/icon.png
index 9a37528ca6e..9a37528ca6e 100644
--- a/addons/project/static/src/img/icon.png
+++ b/addons/project/static/description/icon.png
Binary files differ
diff --git a/addons/project/html/index.html b/addons/project/static/description/index.html
index 93f1be8a41d..2a1574c5d68 100644
--- a/addons/project/html/index.html
+++ b/addons/project/static/description/index.html
@@ -17,7 +17,7 @@ work done. Keep track of everything, from the big picture to the minute
details, from the customer contract to the billing.
</p>
<div class="oe_centeralign oe_websiteonly">
- <a href="http://www.openerp.com/start" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> trial</a>
+ <a href="http://www.openerp.com/start?app=project" class="oe_button oe_big oe_tacky">Start your <span class="oe_emph">free</span> trial</a>
</div>
</div>
</div>
diff --git a/addons/project/html/project_01.png b/addons/project/static/description/project_01.png
index 0c86a2965d5..0c86a2965d5 100644
--- a/addons/project/html/project_01.png
+++ b/addons/project/static/description/project_01.png
Binary files differ
diff --git a/addons/project/html/project_calendar.png b/addons/project/static/description/project_calendar.png
index f8c8fca7662..f8c8fca7662 100644
--- a/addons/project/html/project_calendar.png
+++ b/addons/project/static/description/project_calendar.png
Binary files differ
diff --git a/addons/project/html/project_chat.png b/addons/project/static/description/project_chat.png
index 8cb771e876f..8cb771e876f 100644
--- a/addons/project/html/project_chat.png
+++ b/addons/project/static/description/project_chat.png
Binary files differ
diff --git a/addons/project/html/project_easy.png b/addons/project/static/description/project_easy.png
index 4ffde14b42b..4ffde14b42b 100644
--- a/addons/project/html/project_easy.png
+++ b/addons/project/static/description/project_easy.png
Binary files differ
diff --git a/addons/project/html/project_etherpad.png b/addons/project/static/description/project_etherpad.png
index 038b210ee18..038b210ee18 100644
--- a/addons/project/html/project_etherpad.png
+++ b/addons/project/static/description/project_etherpad.png
Binary files differ
diff --git a/addons/project/html/project_gantt.png b/addons/project/static/description/project_gantt.png
index 34486b98558..34486b98558 100644
--- a/addons/project/html/project_gantt.png
+++ b/addons/project/static/description/project_gantt.png
Binary files differ
diff --git a/addons/project/html/project_illu_01.png b/addons/project/static/description/project_illu_01.png
index 4fff4837faa..4fff4837faa 100644
--- a/addons/project/html/project_illu_01.png
+++ b/addons/project/static/description/project_illu_01.png
Binary files differ
diff --git a/addons/project/html/project_kanban.png b/addons/project/static/description/project_kanban.png
index c541d3b5d08..c541d3b5d08 100644
--- a/addons/project/html/project_kanban.png
+++ b/addons/project/static/description/project_kanban.png
Binary files differ
diff --git a/addons/project/html/project_kpi.png b/addons/project/static/description/project_kpi.png
index c58f8176098..c58f8176098 100644
--- a/addons/project/html/project_kpi.png
+++ b/addons/project/static/description/project_kpi.png
Binary files differ
diff --git a/addons/project/html/project_timesheet.png b/addons/project/static/description/project_timesheet.png
index d06c3a924ec..d06c3a924ec 100644
--- a/addons/project/html/project_timesheet.png
+++ b/addons/project/static/description/project_timesheet.png
Binary files differ
diff --git a/addons/project/test/project_demo.yml b/addons/project/test/project_demo.yml
deleted file mode 100644
index 5f6acbb913e..00000000000
--- a/addons/project/test/project_demo.yml
+++ /dev/null
@@ -1,12 +0,0 @@
--
- !record {model: project.project, id: project_project_1, view: False}:
- partner_id: base.res_partner_2
--
- !record {model: project.task, id: project_task_1, view: False}:
- remaining_hours: 10.00
--
- !record {model: project.task, id: project_task_1, view: False}:
- planned_hours: 10.00
--
- !record {model: project.task, id: project_task_1, view: False}:
- project_id: project_project_1 \ No newline at end of file
diff --git a/addons/project/test/project_process.yml b/addons/project/test/project_process.yml
deleted file mode 100644
index c2a5c072e9d..00000000000
--- a/addons/project/test/project_process.yml
+++ /dev/null
@@ -1,70 +0,0 @@
--
- In order to Test Process of Project Management,
--
- I create duplicate template.
--
- !python {model: project.project}: |
- new_template = self.duplicate_template(cr, uid, [ref("project_project_1")])
- assert new_template, "duplicate template is not created"
- template = self.browse(cr, uid, new_template['res_id'], context=context)
- assert template.state == 'open', "Duplicate template must be in open state."
--
- I convert template into real Project.
--
- !python {model: project.project}: |
- self.reset_project(cr, uid, [ref("project_project_1")])
--
- I check project details after convert from template.
--
- !assert {model: project.project, id: project_project_1, severity: error, string: Project should be active}:
- - state == "open"
--
- I put project in pending.
--
- !python {model: project.project}: |
- self.set_pending(cr, uid, [ref("project_project_1")])
--
- I check state after put in pending.
--
- !assert {model: project.project, id: project_project_1, severity: error, string: Project should be in pending state}:
- - state == "pending"
--
- I re-open the project.
--
- !python {model: project.project}: |
- self.set_open(cr, uid, [ref("project_project_1")])
--
- I check state after reopen.
--
- !assert {model: project.project, id: project_project_1, severity: error, string: Project should be open.}:
- - state == "open"
--
- I close the project.
--
- !python {model: project.project}: |
- self.set_done(cr, uid, [ref("project_project_1")])
--
- I check state after closed.
--
- !assert {model: project.project, id: project_project_1, severity: error, string: Project should be close.}:
- - state == "close"
--
- I set project into template.
--
- !python {model: project.project}: |
- self.set_template(cr, uid, [ref("project_project_1")])
--
- I schedule tasks of project.
--
- !python {model: project.project}: |
- self.schedule_tasks(cr, uid, [ref("project_project_1")], context=context)
--
- I copy the tasks of project.
--
- !python {model: project.project}: |
- self.copy(cr, uid, ref("project_project_1"))
--
- I cancel Project.
--
- !python {model: project.project}: |
- self.set_cancel(cr, uid, [ref("project_project_2")])
diff --git a/addons/project/test/task_process.yml b/addons/project/test/task_process.yml
deleted file mode 100644
index e37b2116714..00000000000
--- a/addons/project/test/task_process.yml
+++ /dev/null
@@ -1,76 +0,0 @@
--
- I put task in pending due to specification is not clear.
--
- !python {model: project.task}: |
- self.do_pending(cr, uid, [ref("project_task_1")])
- context.update({"active_id": ref("project_task_1")})
--
- I check state of task after put in pending.
--
- !assert {model: project.task, id: project_task_1, severity: error, string: task should be in pending state}:
- - state == "pending"
--
- !record {model: project.task.delegate, id: delegate_id}:
- user_id: base.user_demo
- planned_hours: 12.0
- planned_hours_me: 2.0
--
- Now I delegate task to team member.
--
- !python {model: project.task.delegate}: |
- self.delegate(cr, uid, [ref("delegate_id")], {"active_id": ref("project_task_1")})
--
- I check delegated task details.
--
- !python {model: project.task}: |
- task = self.browse(cr, uid, ref("project_task_1"), context=context)
- assert task.planned_hours == 2.0, "Planning hours is not correct after delegated."
- assert task.state == "pending", "Task should be in Pending after delegated."
--
- I re-open the task.
--
- !python {model: project.task}: |
- self.do_reopen(cr, uid, [ref("project_task_1")])
--
- I check reopened task details.
--
- !assert {model: project.task, id: project_task_1, severity: error, string: task should be open.}:
- - state == "open"
--
- I change the stage of task to next stage.
--
- !python {model: project.task}: |
- self.stage_next(cr, uid, [ref("project_task_1")])
--
- !record {model: project.task.reevaluate, id: reevaluate_id}:
- remaining_hours : 120
--
- I reevaluate task with remaining hours.
--
- !python {model: project.task.reevaluate}: |
- self.compute_hours(cr, uid, [ref("reevaluate_id")], {"active_id": ref("project_task_1")})
--
- I check remaining hours after reevaluated task.
--
- !assert {model: project.task, id: project_task_1, severity: error, string: task should be reevaluated}:
- - remaining_hours == 120.0
--
- I close the task.
--
- !python {model: project.task}: |
- self.action_close(cr, uid, [ref("project_task_1")])
--
- I check state after closed.
--
- !assert {model: project.task, id: project_task_1, severity: error, string: task is in open state}:
- - state == "done"
--
- I change the stage of task to previous stage.
--
- !python {model: project.task}: |
- 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")])
diff --git a/addons/project/tests/__init__.py b/addons/project/tests/__init__.py
new file mode 100644
index 00000000000..4d3163d6dc6
--- /dev/null
+++ b/addons/project/tests/__init__.py
@@ -0,0 +1,28 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Business Applications
+# Copyright (c) 2013-TODAY OpenERP S.A. <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 . import test_project_flow
+
+checks = [
+ test_project_flow,
+]
+
+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project/tests/test_project_base.py b/addons/project/tests/test_project_base.py
new file mode 100644
index 00000000000..f82561766fe
--- /dev/null
+++ b/addons/project/tests/test_project_base.py
@@ -0,0 +1,99 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Business Applications
+# Copyright (c) 2013-TODAY OpenERP S.A. <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 openerp.addons.mail.tests.test_mail_base import TestMailBase
+
+
+class TestProjectBase(TestMailBase):
+
+ def setUp(self):
+ super(TestProjectBase, self).setUp()
+ cr, uid = self.cr, self.uid
+
+ # Usefull models
+ self.project_project = self.registry('project.project')
+ self.project_task = self.registry('project.task')
+ self.project_task_delegate = self.registry('project.task.delegate')
+
+ # Find Project User group
+ group_project_user_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_user')
+ self.group_project_user_id = group_project_user_ref and group_project_user_ref[1] or False
+
+ # Find Project Manager group
+ group_project_manager_ref = self.registry('ir.model.data').get_object_reference(cr, uid, 'project', 'group_project_manager')
+ self.group_project_manager_id = group_project_manager_ref and group_project_manager_ref[1] or False
+
+ # Test partners to use through the various tests
+ self.project_partner_id = self.res_partner.create(cr, uid, {
+ 'name': 'Gertrude AgrolaitPartner',
+ 'email': 'gertrude.partner@agrolait.com',
+ })
+ self.email_partner_id = self.res_partner.create(cr, uid, {
+ 'name': 'Patrick Ratatouille',
+ 'email': 'patrick.ratatouille@agrolait.com',
+ })
+
+ # Test users to use through the various tests
+ self.user_projectuser_id = self.res_users.create(cr, uid, {
+ 'name': 'Armande ProjectUser',
+ 'login': 'Armande',
+ 'alias_name': 'armande',
+ 'email': 'armande.projectuser@example.com',
+ 'groups_id': [(6, 0, [self.group_employee_id, self.group_project_user_id])]
+ })
+ self.user_projectmanager_id = self.res_users.create(cr, uid, {
+ 'name': 'Bastien ProjectManager',
+ 'login': 'bastien',
+ 'alias_name': 'bastien',
+ 'email': 'bastien.projectmanager@example.com',
+ 'groups_id': [(6, 0, [self.group_employee_id, self.group_project_manager_id])]
+ })
+ self.user_none_id = self.res_users.create(cr, uid, {
+ 'name': 'Charlie Avotbonkeur',
+ 'login': 'charlie',
+ 'alias_name': 'charlie',
+ 'email': 'charlie.noone@example.com',
+ 'groups_id': [(6, 0, [])]
+ })
+ self.user_projectuser = self.res_users.browse(cr, uid, self.user_projectuser_id)
+ self.user_projectmanager = self.res_users.browse(cr, uid, self.user_projectmanager_id)
+ self.partner_projectuser_id = self.user_projectuser.partner_id.id
+ self.partner_projectmanager_id = self.user_projectmanager.partner_id.id
+
+ # Test 'Pigs' project
+ self.project_pigs_id = self.project_project.create(cr, uid, {
+ 'name': 'Pigs',
+ 'privacy_visibility': 'public',
+ 'alias_name': 'project+pigs',
+ 'partner_id': self.partner_raoul_id,
+ }, {'mail_create_nolog': True})
+
+ # Already-existing tasks in Pigs
+ self.task_1_id = self.project_task.create(cr, uid, {
+ 'name': 'Pigs UserTask',
+ 'user_id': self.user_projectuser_id,
+ 'project_id': self.project_pigs_id,
+ }, {'mail_create_nolog': True})
+ self.task_2_id = self.project_task.create(cr, uid, {
+ 'name': 'Pigs ManagerTask',
+ 'user_id': self.user_projectmanager_id,
+ 'project_id': self.project_pigs_id,
+ }, {'mail_create_nolog': True})
diff --git a/addons/project/tests/test_project_flow.py b/addons/project/tests/test_project_flow.py
new file mode 100644
index 00000000000..a037c92d2ae
--- /dev/null
+++ b/addons/project/tests/test_project_flow.py
@@ -0,0 +1,163 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# OpenERP, Open Source Business Applications
+# Copyright (c) 2013-TODAY OpenERP S.A. <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 openerp.addons.project.tests.test_project_base import TestProjectBase
+from openerp.osv.orm import except_orm
+from openerp.tools import mute_logger
+
+
+EMAIL_TPL = """Return-Path: <whatever-2a840@postmaster.twitter.com>
+X-Original-To: {email_to}
+Delivered-To: {email_to}
+To: {email_to}
+Received: by mail1.openerp.com (Postfix, from userid 10002)
+ id 5DF9ABFB2A; Fri, 10 Aug 2012 16:16:39 +0200 (CEST)
+Message-ID: {msg_id}
+Date: Tue, 29 Nov 2011 12:43:21 +0530
+From: {email_from}
+MIME-Version: 1.0
+Subject: {subject}
+Content-Type: text/plain; charset=ISO-8859-1; format=flowed
+
+Hello,
+
+This email should create a new entry in your module. Please check that it
+effectively works.
+
+Thanks,
+
+--
+Raoul Boitempoils
+Integrator at Agrolait"""
+
+
+class TestProjectFlow(TestProjectBase):
+
+ @mute_logger('openerp.addons.base.ir.ir_model', 'openerp.osv.orm')
+ def test_00_project_process(self):
+ """ Testing project management """
+ cr, uid, user_projectuser_id, user_projectmanager_id, project_pigs_id = self.cr, self.uid, self.user_projectuser_id, self.user_projectmanager_id, self.project_pigs_id
+
+ # ProjectUser: set project as template -> raise
+ self.assertRaises(except_orm, self.project_project.set_template, cr, user_projectuser_id, [project_pigs_id])
+
+ # Other tests are done using a ProjectManager
+ project = self.project_project.browse(cr, user_projectmanager_id, project_pigs_id)
+ self.assertNotEqual(project.state, 'template', 'project: incorrect state, should not be a template')
+
+ # Set test project as template
+ self.project_project.set_template(cr, user_projectmanager_id, [project_pigs_id])
+ project.refresh()
+ self.assertEqual(project.state, 'template', 'project: set_template: project state should be template')
+ self.assertEqual(len(project.tasks), 0, 'project: set_template: project tasks should have been set inactive')
+
+ # Duplicate template
+ new_template_act = self.project_project.duplicate_template(cr, user_projectmanager_id, [project_pigs_id])
+ new_project = self.project_project.browse(cr, user_projectmanager_id, new_template_act['res_id'])
+ self.assertEqual(new_project.state, 'open', 'project: incorrect duplicate_template')
+ self.assertEqual(len(new_project.tasks), 2, 'project: duplicating a project template should duplicate its tasks')
+
+ # Convert into real project
+ self.project_project.reset_project(cr, user_projectmanager_id, [project_pigs_id])
+ project.refresh()
+ self.assertEqual(project.state, 'open', 'project: resetted project should be in open state')
+ self.assertEqual(len(project.tasks), 2, 'project: reset_project: project tasks should have been set active')
+
+ # Put as pending
+ self.project_project.set_pending(cr, user_projectmanager_id, [project_pigs_id])
+ project.refresh()
+ self.assertEqual(project.state, 'pending', 'project: should be in pending state')
+
+ # Re-open
+ self.project_project.set_open(cr, user_projectmanager_id, [project_pigs_id])
+ project.refresh()
+ self.assertEqual(project.state, 'open', 'project: reopened project should be in open state')
+
+ # Close project
+ self.project_project.set_done(cr, user_projectmanager_id, [project_pigs_id])
+ project.refresh()
+ self.assertEqual(project.state, 'close', 'project: closed project should be in close state')
+
+ # Re-open
+ self.project_project.set_open(cr, user_projectmanager_id, [project_pigs_id])
+ project.refresh()
+
+ # Re-convert into a template and schedule tasks
+ self.project_project.set_template(cr, user_projectmanager_id, [project_pigs_id])
+ self.project_project.schedule_tasks(cr, user_projectmanager_id, [project_pigs_id])
+
+ # Copy the project
+ new_project_id = self.project_project.copy(cr, user_projectmanager_id, project_pigs_id)
+ new_project = self.project_project.browse(cr, user_projectmanager_id, new_project_id)
+ self.assertEqual(len(new_project.tasks), 2, 'project: copied project should have copied task')
+
+ # Cancel the project
+ self.project_project.set_cancel(cr, user_projectmanager_id, [project_pigs_id])
+ self.assertEqual(project.state, 'cancelled', 'project: cancelled project should be in cancel state')
+
+ def test_10_task_process(self):
+ """ Testing task creation and management """
+ cr, uid, user_projectuser_id, user_projectmanager_id, project_pigs_id = self.cr, self.uid, self.user_projectuser_id, self.user_projectmanager_id, self.project_pigs_id
+
+ def format_and_process(template, email_to='project+pigs@mydomain.com, other@gmail.com', subject='Frogs',
+ email_from='Patrick Ratatouille <patrick.ratatouille@agrolait.com>',
+ msg_id='<1198923581.41972151344608186760.JavaMail@agrolait.com>'):
+ self.assertEqual(self.project_task.search(cr, uid, [('name', '=', subject)]), [])
+ mail = template.format(email_to=email_to, subject=subject, email_from=email_from, msg_id=msg_id)
+ self.mail_thread.message_process(cr, uid, None, mail)
+ return self.project_task.search(cr, uid, [('name', '=', subject)])
+
+ # Do: incoming mail from an unknown partner on an alias creates a new task 'Frogs'
+ frogs = format_and_process(EMAIL_TPL)
+
+ # Test: one task created by mailgateway administrator
+ self.assertEqual(len(frogs), 1, 'project: message_process: a new project.task should have been created')
+ task = self.project_task.browse(cr, user_projectuser_id, frogs[0])
+ res = self.project_task.perm_read(cr, uid, [task.id], details=False)
+ self.assertEqual(res[0].get('create_uid'), uid,
+ 'project: message_process: task should have been created by uid as alias_user_id is False on the alias')
+ # Test: messages
+ self.assertEqual(len(task.message_ids), 3,
+ 'project: message_process: newly created task should have 2 messages: creation and email')
+ self.assertEqual(task.message_ids[2].subtype_id.name, 'Task Created',
+ 'project: message_process: first message of new task should have Task Created subtype')
+ self.assertEqual(task.message_ids[1].subtype_id.name, 'Task Assigned',
+ 'project: message_process: first message of new task should have Task Created subtype')
+ self.assertEqual(task.message_ids[0].author_id.id, self.email_partner_id,
+ 'project: message_process: second message should be the one from Agrolait (partner failed)')
+ self.assertEqual(task.message_ids[0].subject, 'Frogs',
+ 'project: message_process: second message should be the one from Agrolait (subject failed)')
+ # Test: task content
+ self.assertEqual(task.name, 'Frogs', 'project_task: name should be the email subject')
+ self.assertEqual(task.project_id.id, self.project_pigs_id, 'project_task: incorrect project')
+ self.assertEqual(task.stage_id.sequence, 1, 'project_task: should have a stage with sequence=1')
+
+ # Open the delegation wizard
+ delegate_id = self.project_task_delegate.create(cr, user_projectuser_id, {
+ 'user_id': user_projectuser_id,
+ 'planned_hours': 12.0,
+ 'planned_hours_me': 2.0,
+ }, {'active_id': task.id})
+ self.project_task_delegate.delegate(cr, user_projectuser_id, [delegate_id], {'active_id': task.id})
+
+ # Check delegation details
+ task.refresh()
+ self.assertEqual(task.planned_hours, 2, 'project_task_delegate: planned hours is not correct after delegation')
diff --git a/addons/project/wizard/__init__.py b/addons/project/wizard/__init__.py
index 7a9a734b2bd..24017d9cbe8 100644
--- a/addons/project/wizard/__init__.py
+++ b/addons/project/wizard/__init__.py
@@ -20,7 +20,5 @@
##############################################################################
import project_task_delegate
-import project_task_reevaluate
# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
-
diff --git a/addons/project/wizard/project_task_reevaluate.py b/addons/project/wizard/project_task_reevaluate.py
deleted file mode 100644
index eeb8cccc5d2..00000000000
--- a/addons/project/wizard/project_task_reevaluate.py
+++ /dev/null
@@ -1,84 +0,0 @@
-# -*- coding: utf-8 -*-
-##############################################################################
-#
-# OpenERP, Open Source Management Solution
-# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
-#
-# 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 lxml import etree
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-
-class project_task_reevaluate(osv.osv_memory):
- _name = 'project.task.reevaluate'
-
- def _get_remaining(self, cr, uid, context=None):
- if context is None:
- context = {}
- active_id = context.get('active_id', False)
- res = False
- if active_id:
- res = self.pool.get('project.task').browse(cr, uid, active_id, context=context).remaining_hours
- return res
-
- _columns = {
- 'remaining_hours' : fields.float('Remaining Hours', digits=(16,2), help="Put here the remaining hours required to close the task."),
- }
-
- _defaults = {
- 'remaining_hours': _get_remaining,
- }
-
- def fields_view_get(self, cr, uid, view_id=None, view_type='form', context=None, toolbar=False, submenu=False):
- res = super(project_task_reevaluate, self).fields_view_get(cr, uid, view_id, view_type, context, toolbar, submenu=submenu)
- users_pool = self.pool.get('res.users')
- time_mode = users_pool.browse(cr, uid, uid, context).company_id.project_time_mode_id
- time_mode_name = time_mode and time_mode.name or 'Hours'
- if time_mode_name in ['Hours','Hour']:
- return res
-
- eview = etree.fromstring(res['arch'])
-
- def _check_rec(eview):
- if eview.attrib.get('widget','') == 'float_time':
- eview.set('widget','float')
- for child in eview:
- _check_rec(child)
- return True
-
- _check_rec(eview)
-
- res['arch'] = etree.tostring(eview)
-
- for field in res['fields']:
- if 'Hours' in res['fields'][field]['string']:
- res['fields'][field]['string'] = res['fields'][field]['string'].replace('Hours',time_mode_name)
- return res
-
- def compute_hours(self, cr, uid, ids, context=None):
- if context is None:
- context = {}
- data = self.browse(cr, uid, ids, context=context)[0]
- task_id = context.get('active_id')
- if task_id:
- task_pool = self.pool.get('project.task')
- task_pool.write(cr, uid, task_id, {'remaining_hours': data.remaining_hours})
- if context.get('button_reactivate'):
- task_pool.do_reopen(cr, uid, [task_id], context=context)
- return {'type': 'ir.actions.act_window_close'}
-
-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
diff --git a/addons/project/wizard/project_task_reevaluate_view.xml b/addons/project/wizard/project_task_reevaluate_view.xml
deleted file mode 100644
index 6dfe58e8cfd..00000000000
--- a/addons/project/wizard/project_task_reevaluate_view.xml
+++ /dev/null
@@ -1,32 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<openerp>
- <data>
-
- <record id="view_project_task_reevaluate" model="ir.ui.view">
- <field name="name">Re-evaluate Task</field>
- <field name="model">project.task.reevaluate</field>
- <field name="arch" type="xml">
- <form string="Reevaluate Task" version="7.0">
- <separator string="Reevaluation Task"/>
- <group>
- <field name="remaining_hours" widget="float_time"/>
- </group>
- <footer>
- <button name="compute_hours" string="_Evaluate" type="object" default_focus="1" class="oe_highlight"/>
- or
- <button string="Cancel" class="oe_link" special="cancel" />
- </footer>
- </form>
- </field>
- </record>
-
- <record id="action_project_task_reevaluate" model="ir.actions.act_window">
- <field name="name">Re-evaluate Task</field>
- <field name="type">ir.actions.act_window</field>
- <field name="res_model">project.task.reevaluate</field>
- <field name="view_type">form</field>
- <field name="view_mode">form</field>
- <field name="target">new</field>
- </record>
- </data>
-</openerp>