Merge lp:~openerp-dev/openobject-addons/trunk-kanban-column-vme-new into lp:openobject-addons

Proposed by Vidhin Mehta (OpenERP)
Status: Needs review
Proposed branch: lp:~openerp-dev/openobject-addons/trunk-kanban-column-vme-new
Merge into: lp:openobject-addons
Diff against target: 1727 lines (+991/-114)
38 files modified
crm/__init__.py (+1/-1)
crm/__openerp__.py (+2/-0)
crm/crm.py (+21/-0)
crm/crm_case_section_view.xml (+1/-1)
crm/crm_lead.py (+7/-1)
crm/crm_lead_view.xml (+2/-2)
crm/security/ir.model.access.csv (+2/-0)
crm/tests/__init__.py (+24/-0)
crm/tests/test_crm_stages_case.py (+76/-0)
crm/wizard/__init__.py (+1/-0)
crm/wizard/crm_case_stage_management.py (+100/-0)
crm/wizard/crm_case_stage_management_view.xml (+18/-0)
hr/hr_view.xml (+3/-1)
hr_recruitment/__init__.py (+1/-0)
hr_recruitment/__openerp__.py (+1/-0)
hr_recruitment/hr_recruitment.py (+77/-34)
hr_recruitment/hr_recruitment_demo.xml (+7/-0)
hr_recruitment/hr_recruitment_menu.xml (+30/-21)
hr_recruitment/hr_recruitment_view.xml (+34/-34)
hr_recruitment/security/ir.model.access.csv (+4/-1)
hr_recruitment/tests/__init__.py (+24/-0)
hr_recruitment/tests/test_hr_recruitment_stage.py (+77/-0)
hr_recruitment/wizard/__init__.py (+1/-2)
hr_recruitment/wizard/hr_recruitment_stage_management.py (+104/-0)
hr_recruitment/wizard/hr_recruitment_stage_management_view.xml (+19/-0)
project/__openerp__.py (+1/-0)
project/project.py (+31/-5)
project/project_view.xml (+1/-2)
project/security/ir.model.access.csv (+5/-2)
project/tests/__init__.py (+2/-1)
project/tests/project_stage_management.py (+77/-0)
project/wizard/__init__.py (+1/-0)
project/wizard/project_stage_management.py (+104/-0)
project/wizard/project_stage_management.xml (+18/-0)
project_issue/project_issue.py (+8/-4)
project_issue/project_issue_view.xml (+1/-2)
project_issue/tests/__init__.py (+28/-0)
project_issue/tests/project_stage_management.py (+77/-0)
To merge this branch: bzr merge lp:~openerp-dev/openobject-addons/trunk-kanban-column-vme-new
Reviewer Review Type Date Requested Status
OpenERP Core Team Pending
Review via email: mp+196861@code.launchpad.net
To post a comment you must log in.
8989. By Vidhin Mehta (OpenERP)

[CHANGE]comment

8990. By Vidhin Mehta (OpenERP)

[REM]limit

8991. By Vidhin Mehta (OpenERP)

[IMP]if default_XXX is not found in context than default behaviour of stage given.

8992. By Vidhin Mehta (OpenERP)

[IMP]forgot to add vals

8993. By Vidhin Mehta (OpenERP)

[ERM]jobid from tree view

8994. By Darshan Kalola(OpenERP)

[MERGE]sync with trunk and resolve conflicts.

8995. By Mahendra Barad(OpenERP)

[Merge]with trunk

8996. By Mahendra Barad(OpenERP)

[IMP]job_ids

8997. By Mahendra Barad(OpenERP)

[IMP]stage sequence changed in .sequence object if default projetc_id, section_id or job_id not in stage object

8998. By Mahendra Barad(OpenERP)

[IMP]stage load sequence if context in project_issue

8999. By Mahendra Barad(OpenERP)

[IMP]added access rule for new created sequence object

Unmerged revisions

8999. By Mahendra Barad(OpenERP)

[IMP]added access rule for new created sequence object

8998. By Mahendra Barad(OpenERP)

[IMP]stage load sequence if context in project_issue

8997. By Mahendra Barad(OpenERP)

[IMP]stage sequence changed in .sequence object if default projetc_id, section_id or job_id not in stage object

8996. By Mahendra Barad(OpenERP)

[IMP]job_ids

8995. By Mahendra Barad(OpenERP)

[Merge]with trunk

8994. By Darshan Kalola(OpenERP)

[MERGE]sync with trunk and resolve conflicts.

8993. By Vidhin Mehta (OpenERP)

[ERM]jobid from tree view

8992. By Vidhin Mehta (OpenERP)

[IMP]forgot to add vals

8991. By Vidhin Mehta (OpenERP)

[IMP]if default_XXX is not found in context than default behaviour of stage given.

8990. By Vidhin Mehta (OpenERP)

[REM]limit

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'crm/__init__.py'
2--- crm/__init__.py 2014-01-15 09:38:05 +0000
3+++ crm/__init__.py 2014-02-27 12:12:58 +0000
4@@ -29,6 +29,6 @@
5 import res_partner
6 import res_config
7 import base_partner_merge
8-
9+import tests
10 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
11
12
13=== modified file 'crm/__openerp__.py'
14--- crm/__openerp__.py 2014-01-15 09:38:05 +0000
15+++ crm/__openerp__.py 2014-02-27 12:12:58 +0000
16@@ -75,6 +75,8 @@
17 'wizard/crm_opportunity_to_phonecall_view.xml',
18
19 'wizard/crm_merge_opportunities_view.xml',
20+
21+ 'wizard/crm_case_stage_management_view.xml',
22
23 'crm_view.xml',
24
25
26=== modified file 'crm/crm.py'
27--- crm/crm.py 2014-02-11 11:09:36 +0000
28+++ crm/crm.py 2014-02-27 12:12:58 +0000
29@@ -47,6 +47,17 @@
30 'active': lambda *a: 1,
31 }
32
33+class crm_case_stage_sequence(osv.osv):
34+ _name = 'crm.case.stage.sequence'
35+ _table = 'section_stage_rel'
36+ _description = 'Crm Case Stage Sequence'
37+ _order = 'sequence'
38+ _columns = {
39+ 'section_id': fields.many2one('crm.case.section', 'Sequence/Section'),
40+ 'stage_id': fields.many2one('crm.case.stage', 'Sequence/Stage'),
41+ 'sequence': fields.integer('Sequence')
42+ }
43+
44 class crm_case_stage(osv.osv):
45 """ Model for case stages. This models the main stages of a document
46 management flow. Main CRM objects (leads, opportunities, project
47@@ -87,6 +98,16 @@
48 'case_default': True,
49 }
50
51+ def write(self, cr, uid, ids, vals, context=None):
52+ section_id = context.get('default_section_id')
53+ crm_case_stage_sequence = self.pool.get('crm.case.stage.sequence')
54+
55+ if section_id:
56+ lead_ids = crm_case_stage_sequence.search(cr, uid, [('stage_id', '=', ids),('section_id', '=', section_id)], context=context)
57+ crm_case_stage_sequence.write(cr, uid, lead_ids, vals , context=context)
58+ else:
59+ return super(crm_case_stage, self).write(cr, uid, ids, vals, context=context)
60+ return True
61
62 class crm_case_section(osv.osv):
63 """ Model for sales teams. """
64
65=== modified file 'crm/crm_case_section_view.xml'
66--- crm/crm_case_section_view.xml 2014-02-20 16:38:15 +0000
67+++ crm/crm_case_section_view.xml 2014-02-27 12:12:58 +0000
68@@ -42,7 +42,7 @@
69 <field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/>
70 <field name="context">{
71 'search_default_section_id': [active_id],
72- 'default_section_id': active_id,
73+ 'search_default_assigned_to_me': 1,
74 'stage_type': 'opportunity',
75 'default_type': 'opportunity',
76 'default_user_id': uid,
77
78=== modified file 'crm/crm_lead.py'
79--- crm/crm_lead.py 2014-02-20 16:38:15 +0000
80+++ crm/crm_lead.py 2014-02-27 12:12:58 +0000
81@@ -143,9 +143,15 @@
82 search_domain += ['|', ('type', '=', type), ('type', '=', 'both')]
83 # perform search
84 stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
85+ if section_id:
86+ sequence_ids = self.pool.get('crm.case.stage.sequence').search_read(cr, uid, [('section_id', '=', section_id), ('stage_id', 'in', stage_ids)], ['stage_id'], order=order, context=context)
87+ type_ids = map(lambda type_ids: type_ids['stage_id'][0], sequence_ids)
88 result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
89 # restore order of the search
90- result.sort(lambda x, y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
91+ if section_id:
92+ result.sort(lambda x,y: cmp(type_ids.index(x[0]), type_ids.index(y[0])))
93+ else:
94+ result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
95
96 fold = {}
97 for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
98
99=== modified file 'crm/crm_lead_view.xml'
100--- crm/crm_lead_view.xml 2014-02-12 18:08:45 +0000
101+++ crm/crm_lead_view.xml 2014-02-27 12:12:58 +0000
102@@ -259,7 +259,7 @@
103 <field name="name">CRM - Leads Kanban</field>
104 <field name="model">crm.lead</field>
105 <field name="arch" type="xml">
106- <kanban default_group_by="stage_id">
107+ <kanban default_group_by="stage_id" stage_management="crm.case.stage.management" stage_options = "{'group_by':'stage_id'}">
108 <field name="stage_id"/>
109 <field name="color"/>
110 <field name="priority"/>
111@@ -545,7 +545,7 @@
112 <search string="Search Opportunities">
113 <field name="name" string="Opportunity" filter_domain="['|','|','|',('partner_id','ilike',self),('partner_name','ilike',self),('email_from','ilike',self),('name', 'ilike', self)]"/>
114 <field name="categ_ids" string="Category" filter_domain="[('categ_ids','ilike', self)]"/>
115- <field name="section_id" context="{'invisible_section': False}" groups="base.group_multi_salesteams"/>
116+ <field name="section_id" groups="base.group_multi_salesteams"/>
117 <field name="user_id"/>
118 <field name="partner_id" filter_domain="[('partner_id','child_of',self)]"/>
119 <field name="stage_id" domain="[]"/>
120
121=== modified file 'crm/security/ir.model.access.csv'
122--- crm/security/ir.model.access.csv 2013-11-27 15:32:57 +0000
123+++ crm/security/ir.model.access.csv 2014-02-27 12:12:58 +0000
124@@ -16,6 +16,8 @@
125 access_crm_case_section_manager,crm.case.section.manager,model_crm_case_section,base.group_sale_manager,1,1,1,1
126 access_crm_case_stage,crm.case.stage,model_crm_case_stage,,1,0,0,0
127 access_crm_case_stage_manager,crm.case.stage,model_crm_case_stage,base.group_sale_manager,1,1,1,1
128+access_crm_case_stage_sequence,crm.case.stage.sequence,model_crm_case_stage_sequence,,1,0,0,0
129+access_crm_case_stage_sequence_manager,crm.case.stage.sequence,model_crm_case_stage_sequence,base.group_sale_manager,1,1,1,1
130 access_crm_case_resource_type_user,crm_case_resource_type user,model_crm_case_resource_type,base.group_sale_salesman,1,1,1,0
131 access_crm_case_resource_type_manager,crm_case_resource_type manager,model_crm_case_resource_type,base.group_sale_manager,1,1,1,1
132 access_crm_phonecall_report_user,crm.phonecall.report.user,model_crm_phonecall_report,base.group_sale_salesman,1,0,0,0
133
134=== added directory 'crm/tests'
135=== added file 'crm/tests/__init__.py'
136--- crm/tests/__init__.py 1970-01-01 00:00:00 +0000
137+++ crm/tests/__init__.py 2014-02-27 12:12:58 +0000
138@@ -0,0 +1,24 @@
139+# -*- coding: utf-8 -*-
140+##############################################################################
141+#
142+# OpenERP, Open Source Business Applications
143+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
144+#
145+# This program is free software: you can redistribute it and/or modify
146+# it under the terms of the GNU Affero General Public License as
147+# published by the Free Software Foundation, either version 3 of the
148+# License, or (at your option) any later version.
149+#
150+# This program is distributed in the hope that it will be useful,
151+# but WITHOUT ANY WARRANTY; without even the implied warranty of
152+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
153+# GNU Affero General Public License for more details.
154+#
155+# You should have received a copy of the GNU Affero General Public License
156+# along with this program. If not, see <http://www.gnu.org/licenses/>.
157+#
158+##############################################################################
159+from . import test_crm_stages_case
160+checks = [
161+ test_crm_stages_case,
162+ ]
163\ No newline at end of file
164
165=== added file 'crm/tests/test_crm_stages_case.py'
166--- crm/tests/test_crm_stages_case.py 1970-01-01 00:00:00 +0000
167+++ crm/tests/test_crm_stages_case.py 2014-02-27 12:12:58 +0000
168@@ -0,0 +1,76 @@
169+# -*- coding: utf-8 -*-
170+##############################################################################
171+#
172+# OpenERP, Open Source Business Applications
173+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
174+#
175+# This program is free software: you can redistribute it and/or modify
176+# it under the terms of the GNU Affero General Public License as
177+# published by the Free Software Foundation, either version 3 of the
178+# License, or (at your option) any later version.
179+#
180+# This program is distributed in the hope that it will be useful,
181+# but WITHOUT ANY WARRANTY; without even the implied warranty of
182+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
183+# GNU Affero General Public License for more details.
184+#
185+# You should have received a copy of the GNU Affero General Public License
186+# along with this program. If not, see <http://www.gnu.org/licenses/>.
187+#
188+##############################################################################
189+from openerp.osv.orm import except_orm
190+from openerp.tests import common
191+
192+
193+class TestCrmStagesCase(common.TransactionCase):
194+ def setUp(self):
195+ super(TestCrmStagesCase, self).setUp()
196+
197+ def test_00_stage_management(self):
198+ cr, uid = self.cr, self.uid
199+ crm_stage_management = self.registry('crm.case.stage.management')
200+ crm_case_section = self.registry('crm.case.section')
201+ crm_case_stage= self.registry('crm.case.stage')
202+ crm_lead = self.registry('crm.lead');
203+ crm_case_section_id = crm_case_section.create(cr, uid, {'name':'test_section'},context={})
204+ context = {'default_section_id': crm_case_section_id, 'default_action':'create'}
205+
206+ ''' For create when default_section_id pass in context
207+ If same name is present than returns its id otherwise create new.
208+ and link with given section.
209+ '''
210+
211+ stage_m2m_oldlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
212+ crm_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
213+ crm_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
214+ check_len_id = crm_case_stage.search(cr, uid, [('name','=', 'First')], context=context)
215+ self.assertEqual(len(check_len_id), 1)
216+ opportunity_id = crm_lead.create(cr, uid, {'name':'Test1','stage_id': check_len_id[0],'section_id':crm_case_section_id})
217+ test1 = check_len_id
218+ stage_m2m_newlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
219+ self.assertEqual(len(stage_m2m_oldlist) + 1, len(stage_m2m_newlist))
220+
221+ ''' For edit when default_section_id pass in context
222+ If same name is present than returns its id otherwise create new.
223+ and link with given section and remove old link with section and crm_lead.
224+ '''
225+
226+ context.update({'active_id':test1[0], 'default_action':'write'})
227+ crm_stage_management.modify_stage(cr, uid, {'name':'Second'},context=context)
228+ test2 = crm_case_stage.search(cr, uid, [('name','=', 'Second')], context=context)
229+ stage_m2m_newlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
230+
231+ check_crm_case_stage_id = crm_lead.browse(cr, uid, opportunity_id, context).stage_id.id
232+ self.assertEqual(check_crm_case_stage_id, test2[0])
233+ self.assertIn(test2[0], [x.id for x in stage_m2m_newlist])
234+
235+ ''' For unlink when default_section_id pass in context
236+ It will unlink relation, not delete.
237+ '''
238+
239+ stage_m2m_oldlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
240+ crm_stage_management.unlink(cr, uid, test2, context=context)
241+ stage_m2m_newlist = crm_case_section.browse(cr, uid, crm_case_section_id, context=context).stage_ids
242+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
243+ unlink_id = crm_case_stage.search(cr, uid, [('name','=', 'Second')], context=context)
244+ self.assertEqual(unlink_id, test2)
245
246=== modified file 'crm/wizard/__init__.py'
247--- crm/wizard/__init__.py 2012-12-05 13:03:21 +0000
248+++ crm/wizard/__init__.py 2014-02-27 12:12:58 +0000
249@@ -24,5 +24,6 @@
250 import crm_opportunity_to_phonecall
251 import crm_lead_to_opportunity
252 import crm_merge_opportunities
253+import crm_case_stage_management
254
255 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
256
257=== added file 'crm/wizard/crm_case_stage_management.py'
258--- crm/wizard/crm_case_stage_management.py 1970-01-01 00:00:00 +0000
259+++ crm/wizard/crm_case_stage_management.py 2014-02-27 12:12:58 +0000
260@@ -0,0 +1,100 @@
261+# -*- coding: utf-8 -*-
262+##############################################################################
263+#
264+# OpenERP, Open Source Management Solution
265+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
266+#
267+# This program is free software: you can redistribute it and/or modify
268+# it under the terms of the GNU Affero General Public License as
269+# published by the Free Software Foundation, either version 3 of the
270+# License, or (at your option) any later version.
271+#
272+# This program is distributed in the hope that it will be useful,
273+# but WITHOUT ANY WARRANTY; without even the implied warranty of
274+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
275+# GNU Affero General Public License for more details.
276+#
277+# You should have received a copy of the GNU Affero General Public License
278+# along with this program. If not, see <http://www.gnu.org/licenses/>.
279+#
280+##############################################################################
281+from openerp.osv import osv,fields
282+
283+class crm_case_stage_management(osv.TransientModel):
284+
285+ _name = "crm.case.stage.management"
286+ _description = "Stage of opportunity"
287+ _columns = {
288+ 'name': fields.char('Stage Name', required= True, size=64),
289+ }
290+ def default_get(self, cr, uid, fields, context=None):
291+ if context is None:context = {}
292+ old_stage_id = context.get('active_id')
293+ res = super(crm_case_stage_management, self).default_get(cr, uid, fields, context)
294+
295+ if(context.get('default_action') == "create"):return res
296+ crm_case_type = self.pool.get("crm.case.stage").browse(cr , uid, old_stage_id, context)
297+ res.update({'name': crm_case_type.name})
298+ return res
299+
300+ def create(self, cr, uid, vals, context):
301+ if context is None: context = {}
302+ action = context.get('default_action')
303+ if action == 'create': self.create_stage(cr, uid, vals.get('name'), context)
304+ if action == 'write': self.modify_stage(cr, uid, vals, context)
305+ return None
306+
307+ def create_stage(self, cr, uid, name, context):
308+ if context is None:context = {}
309+ section_id = context.get('default_section_id')
310+
311+ crm_case_section = self.pool.get('crm.case.section')
312+ crm_case_stage = self.pool.get("crm.case.stage")
313+
314+ if section_id:
315+ new_id = crm_case_stage.search(cr, uid, [('name','=', name)], context=context, limit=1)
316+ if not new_id:
317+ new_id = [crm_case_stage.create(cr, uid, {'name': name}, context=context)]
318+ crm_case_section.write(cr, uid, [section_id], {'stage_ids': [(4, new_id[0])]}, context=context)
319+ else:
320+ new_id = [crm_case_stage.create(cr, uid, {'name': name}, context=context)]
321+ return new_id
322+
323+ def unlink(self, cr, uid, ids, context):
324+ if context is None: context = {}
325+ section_id = context.get('default_section_id')
326+
327+ crm_case_section = self.pool.get('crm.case.section')
328+ crm_lead = self.pool.get('crm.lead')
329+ crm_case_stage = self.pool.get("crm.case.stage")
330+
331+ if section_id:
332+ for stage_id in ids:
333+ crm_lead_ids = crm_lead.search(cr, uid, [('stage_id','=',stage_id),('section_id','=', section_id)])
334+ crm_lead.write(cr, uid, crm_lead_ids, {'stage_id': False} , context=context)
335+ crm_case_section.write(cr, uid, section_id, {'stage_ids': [(3, stage_id)]}, context=context)
336+ else:
337+ crm_case_stage.unlink(cr, uid, ids, context=context)
338+ return True
339+
340+ def modify_stage(self,cr, uid, vals, context=None):
341+ old_stage_id = context.get('active_id')
342+ section_id = context.get('default_section_id')
343+
344+ crm_case_section = self.pool.get('crm.case.section')
345+ crm_lead = self.pool.get('crm.lead')
346+ crm_case_stage = self.pool.get("crm.case.stage")
347+ if section_id:
348+ new_case_type_id = crm_case_stage.search(cr, uid, [('name','=', vals.get('name'))], context=context, limit=1)
349+ if not new_case_type_id:
350+ vals['section_ids'] = [(6, 0, [section_id])]
351+ new_case_type_id = [crm_case_stage.copy(cr, uid, old_stage_id, default=vals, context=context)]
352+ new_stage_id = new_case_type_id[0]
353+
354+ opportunity_ids = crm_lead.search(cr, uid, [('stage_id','=', old_stage_id),('section_id','=', section_id)], context=context)
355+ crm_lead.write(cr, uid, opportunity_ids, {'stage_id': new_stage_id} , context=context)
356+ if section_id:
357+ crm_case_section.write(cr, uid, [section_id], {'stage_ids': [(3, old_stage_id),(4, new_stage_id),]}, context=context)
358+ else:
359+ crm_case_stage.write(cr, uid, old_stage_id, vals, context=context)
360+ return True
361
362=== added file 'crm/wizard/crm_case_stage_management_view.xml'
363--- crm/wizard/crm_case_stage_management_view.xml 1970-01-01 00:00:00 +0000
364+++ crm/wizard/crm_case_stage_management_view.xml 2014-02-27 12:12:58 +0000
365@@ -0,0 +1,18 @@
366+<?xml version="1.0" encoding="utf-8"?>
367+<openerp>
368+ <data>
369+ <record model="ir.ui.view" id="crm_stage_form_wizard">
370+ <field name="name">crm.case.stage.management.form</field>
371+ <field name="model">crm.case.stage.management</field>
372+ <field name="arch" type="xml">
373+ <form string="Stage" version="7.0">
374+ <group>
375+ <group>
376+ <field name="name"/>
377+ </group>
378+ </group>
379+ </form>
380+ </field>
381+ </record>
382+ </data>
383+</openerp>
384\ No newline at end of file
385
386=== modified file 'hr/hr_view.xml'
387--- hr/hr_view.xml 2014-02-11 12:04:33 +0000
388+++ hr/hr_view.xml 2014-02-27 12:12:58 +0000
389@@ -338,6 +338,8 @@
390 <field name="state" widget="statusbar" statusbar_visible="recruit,open"/>
391 </header>
392 <sheet>
393+ <div name="hr_applicant_button" class="oe_right oe_button_box">
394+ </div>
395 <div class="oe_title">
396 <label for="name" class="oe_edit_only"/>
397 <h1><field name="name" class="oe_inline"/></h1>
398@@ -484,4 +486,4 @@
399 <menuitem action="open_module_tree_department" id="menu_hr_department_tree" parent="hr.menu_hr_configuration" sequence="5"/>
400
401 </data>
402-</openerp>
403+</openerp>
404\ No newline at end of file
405
406=== modified file 'hr_recruitment/__init__.py'
407--- hr_recruitment/__init__.py 2012-04-02 05:44:18 +0000
408+++ hr_recruitment/__init__.py 2014-02-27 12:12:58 +0000
409@@ -23,5 +23,6 @@
410 import report
411 import wizard
412 import res_config
413+import tests
414
415 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
416
417=== modified file 'hr_recruitment/__openerp__.py'
418--- hr_recruitment/__openerp__.py 2014-02-10 07:21:00 +0000
419+++ hr_recruitment/__openerp__.py 2014-02-27 12:12:58 +0000
420@@ -48,6 +48,7 @@
421 ],
422 'data': [
423 'wizard/hr_recruitment_create_partner_job_view.xml',
424+ 'wizard/hr_recruitment_stage_management_view.xml',
425 'hr_recruitment_view.xml',
426 'hr_recruitment_menu.xml',
427 'security/hr_recruitment_security.xml',
428
429=== modified file 'hr_recruitment/hr_recruitment.py'
430--- hr_recruitment/hr_recruitment.py 2014-02-12 10:43:40 +0000
431+++ hr_recruitment/hr_recruitment.py 2014-02-27 12:12:58 +0000
432@@ -41,6 +41,17 @@
433 'name': fields.char('Source Name', size=64, required=True, translate=True),
434 }
435
436+class hr_recruitment_stage_sequence(osv.osv):
437+ _name = 'hr.recruitment.stage.sequence'
438+ _table = 'job_stage_rel'
439+ _description = 'Hr Recruitment Stage Sequence'
440+ _order = 'sequence'
441+ _columns = {
442+ 'job_id': fields.many2one('hr.job', 'Sequence/Job'),
443+ 'stage_id': fields.many2one('hr.recruitment.stage', 'Sequence/Stage'),
444+ 'sequence': fields.integer('Sequence')
445+ }
446+
447 class hr_recruitment_stage(osv.osv):
448 """ Stage of HR Recruitment """
449 _name = "hr.recruitment.stage"
450@@ -49,17 +60,38 @@
451 _columns = {
452 'name': fields.char('Name', size=64, required=True, translate=True),
453 'sequence': fields.integer('Sequence', help="Gives the sequence order when displaying a list of stages."),
454- 'department_id':fields.many2one('hr.department', 'Specific to a Department', help="Stages of the recruitment process may be different per department. If this stage is common to all departments, keep this field empty."),
455+ 'job_ids': fields.many2many('hr.job', 'job_stage_rel', 'stage_id','job_id', 'Stages'),
456 'requirements': fields.text('Requirements'),
457+ 'case_default': fields.boolean('Default for New Job'),
458 'template_id': fields.many2one('email.template', 'Use template', help="If set, a message is posted on the applicant using the template when the applicant is set to the stage."),
459 'fold': fields.boolean('Folded in Kanban View',
460 help='This stage is folded in the kanban view when'
461 'there are no records in that stage to display.'),
462 }
463+
464+ def _get_default_job_id(self, cr, uid, ctx={}):
465+ job = ctx.get('default_job_id', False)
466+ if type(job) in (int, long):
467+ return [job]
468+ return job
469+
470 _defaults = {
471 'sequence': 1,
472+ 'job_ids': _get_default_job_id,
473+ 'case_default': True,
474 }
475
476+ def write(self, cr, uid, ids, vals, context=None):
477+ job_id = context.get('default_job_id')
478+ hr_recruitment_stage_sequence = self.pool.get('hr.recruitment.stage.sequence')
479+
480+ if job_id:
481+ recruitment_ids = hr_recruitment_stage_sequence.search(cr, uid, [('stage_id', '=', ids),('job_id', '=', job_id)], context=context)
482+ hr_recruitment_stage_sequence.write(cr, uid, recruitment_ids, vals , context=context)
483+ else:
484+ return super(hr_recruitment_stage, self).write(cr, uid, ids, vals, context=context)
485+ return True
486+
487 class hr_recruitment_degree(osv.osv):
488 """ Degree of HR Recruitment """
489 _name = "hr.recruitment.degree"
490@@ -88,29 +120,29 @@
491 },
492 }
493
494- def _get_default_department_id(self, cr, uid, context=None):
495+ def _get_default_job_id(self, cr, uid, context=None):
496 """ Gives default department by checking if present in the context """
497- return (self._resolve_department_id_from_context(cr, uid, context=context) or False)
498-
499+ return (self._resolve_job_id_from_context(cr, uid, context=context) or False)
500+
501 def _get_default_stage_id(self, cr, uid, context=None):
502 """ Gives default stage_id """
503- department_id = self._get_default_department_id(cr, uid, context=context)
504- return self.stage_find(cr, uid, [], department_id, [('fold', '=', False)], context=context)
505+ job_id = self._get_default_job_id(cr, uid, context=context)
506+ return self.stage_find(cr, uid, [], job_id, [('sequence', '=', '1')], context=context)
507
508- def _resolve_department_id_from_context(self, cr, uid, context=None):
509- """ Returns ID of department based on the value of 'default_department_id'
510- context key, or None if it cannot be resolved to a single
511+ def _resolve_job_id_from_context(self, cr, uid, context=None):
512+ """ Returns ID of job based on the value of 'default_job_id'
513+ context key, or None if it cannot be resolved to a single
514 department.
515 """
516 if context is None:
517 context = {}
518- if type(context.get('default_department_id')) in (int, long):
519- return context.get('default_department_id')
520- if isinstance(context.get('default_department_id'), basestring):
521- department_name = context['default_department_id']
522- department_ids = self.pool.get('hr.department').name_search(cr, uid, name=department_name, context=context)
523- if len(department_ids) == 1:
524- return int(department_ids[0][0])
525+ if type(context.get('default_job_id')) in (int, long):
526+ return context.get('default_job_id')
527+ if isinstance(context.get('default_job_id'), basestring):
528+ job_name = context['default_job_id']
529+ job_ids = self.pool.get('hr.job').name_search(cr, uid, name=job_name, context=context)
530+ if len(job_ids) == 1:
531+ return int(job_ids[0][0])
532 return None
533
534 def _read_group_stage_ids(self, cr, uid, ids, domain, read_group_order=None, access_rights_uid=None, context=None):
535@@ -122,18 +154,23 @@
536 order = "%s desc" % order
537 # retrieve section_id from the context and write the domain
538 # - ('id', 'in', 'ids'): add columns that should be present
539- # - OR ('department_id', '=', False), ('fold', '=', False): add default columns that are not folded
540- # - OR ('department_id', 'in', department_id), ('fold', '=', False) if department_id: add department columns that are not folded
541- department_id = self._resolve_department_id_from_context(cr, uid, context=context)
542+ # - OR ('job_id', '=', False), ('fold', '=', False): add default columns that are not folded
543+ # - OR ('job_id', 'in', job_id), ('fold', '=', False) if job_id: add department columns that are not folded
544+ job_id = self._resolve_job_id_from_context(cr, uid, context=context)
545 search_domain = []
546- if department_id:
547- search_domain += ['|', ('department_id', '=', department_id)]
548- search_domain += ['|', ('id', 'in', ids), ('department_id', '=', False)]
549+ if job_id:
550+ search_domain += [('job_ids', '=', job_id)]
551+ search_domain += ['|', '|', ('id', 'in', ids), ('job_ids', '=', False), ('case_default', '=', True)]
552 stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
553+ if job_id:
554+ sequence_ids = self.pool.get('hr.recruitment.stage.sequence').search_read(cr, uid, [('job_id', '=', job_id), ('stage_id', 'in', stage_ids)], ['stage_id'], order=order, context=context)
555+ type_ids = map(lambda type_ids: type_ids['stage_id'][0], sequence_ids)
556 result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
557 # restore order of the search
558- result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
559-
560+ if job_id:
561+ result.sort(lambda x,y: cmp(type_ids.index(x[0]), type_ids.index(y[0])))
562+ else:
563+ result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
564 fold = {}
565 for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
566 fold[stage.id] = stage.fold or False
567@@ -188,7 +225,7 @@
568 'create_date': fields.datetime('Creation Date', readonly=True, select=True),
569 'write_date': fields.datetime('Update Date', readonly=True),
570 'stage_id': fields.many2one ('hr.recruitment.stage', 'Stage', track_visibility='onchange',
571- domain="['|', ('department_id', '=', department_id), ('department_id', '=', False)]"),
572+ domain="['|', ('job_ids', '=', job_id), ('job_ids', '=', False)]"),
573 'last_stage_id': fields.many2one('hr.recruitment.stage', 'Last Stage',
574 help='Stage of the applicant before being in the current stage. Used for lost cases analysis.'),
575 'categ_ids': fields.many2many('hr.applicant_category', string='Tags'),
576@@ -229,7 +266,7 @@
577 'active': lambda *a: 1,
578 'user_id': lambda s, cr, uid, c: uid,
579 'stage_id': lambda s, cr, uid, c: s._get_default_stage_id(cr, uid, c),
580- 'department_id': lambda s, cr, uid, c: s._get_default_department_id(cr, uid, c),
581+ 'job_id': lambda s, cr, uid, c: s._get_default_job_id(cr, uid, c),
582 'company_id': lambda s, cr, uid, c: s.pool.get('res.company')._company_default_get(cr, uid, 'hr.applicant', context=c),
583 'color': 0,
584 'date_last_stage_update': fields.datetime.now,
585@@ -272,17 +309,16 @@
586 if isinstance(cases, (int, long)):
587 cases = self.browse(cr, uid, cases, context=context)
588 # collect all section_ids
589- department_ids = []
590+ job_ids = []
591 if section_id:
592- department_ids.append(section_id)
593+ job_ids.append(section_id)
594 for case in cases:
595- if case.department_id:
596- department_ids.append(case.department_id.id)
597+ if case.job_id:
598+ job_ids.append(case.job_id.id)
599 # OR all section_ids and OR with case_default
600 search_domain = []
601- if department_ids:
602- search_domain += ['|', ('department_id', 'in', department_ids)]
603- search_domain.append(('department_id', '=', False))
604+ if job_ids:
605+ search_domain += ['|', ('job_ids', 'in', job_ids)]
606 # AND with the domain in parameter
607 search_domain += list(domain)
608 # perform search, return the first found
609@@ -395,6 +431,7 @@
610 if vals.get('user_id'):
611 vals['date_start'] = fields.datetime.now()
612 # stage_id: track last stage before update
613+ res = True
614 if 'stage_id' in vals:
615 vals['date_last_stage_update'] = fields.datetime.now()
616 for applicant in self.browse(cr, uid, ids, context=None):
617@@ -522,6 +559,7 @@
618 'alias_id': fields.many2one('mail.alias', 'Alias', ondelete="restrict", required=True,
619 help="Email alias for this job position. New emails will automatically "
620 "create new applicants for this job position."),
621+ 'stage_ids':fields.many2many('hr.recruitment.stage', 'job_stage_rel','job_id', 'stage_id', 'Stages'),
622 'address_id': fields.many2one('res.partner', 'Job Location', help="Address where employees are working"),
623 'application_ids': fields.one2many('hr.applicant', 'job_id', 'Applications'),
624 'manager_id': fields.related('department_id', 'manager_id', type='many2one', string='Department Manager', relation='hr.employee', readonly=True, store=True),
625@@ -534,7 +572,12 @@
626 user = self.pool.get('res.users').browse(cr, uid, uid, context=context)
627 return user.company_id.partner_id.id
628
629+ def _get_stage_common(self, cr, uid, context):
630+ return self.pool.get('hr.recruitment.stage').search(cr, uid, [('case_default','=',1)], context=context)
631+
632 _defaults = {
633+ 'alias_domain': False, # always hide alias during creation
634+ 'stage_ids': _get_stage_common,
635 'address_id': _address_get
636 }
637
638@@ -596,4 +639,4 @@
639 'name': fields.char('Name', size=64, required=True, translate=True),
640 }
641
642-# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
643+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
644\ No newline at end of file
645
646=== modified file 'hr_recruitment/hr_recruitment_demo.xml'
647--- hr_recruitment/hr_recruitment_demo.xml 2013-12-24 05:37:04 +0000
648+++ hr_recruitment/hr_recruitment_demo.xml 2014-02-27 12:12:58 +0000
649@@ -127,32 +127,39 @@
650 <field name="state">recruit</field>
651 <field name="no_of_recruitment">4</field>
652 <field name="survey_id" ref="survey_job_0"/>
653+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4),(4, stage_job5),(4, stage_job6)]" />
654 </record>
655 <record id="hr.job_ceo" model="hr.job">
656 <field name="survey_id" ref="survey_job_0"/>
657+ <field name= "stage_ids" eval="[(4, stage_job3),(4, stage_job4),(4, stage_job5),(4, stage_job6)]" />
658 </record>
659 <record id="hr.job_cto" model="hr.job">
660 <field name="survey_id" ref="survey_job_0"/>
661+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job4),(4, stage_job5),(4, stage_job6)]" />
662 </record>
663 <record id="hr.job_consultant" model="hr.job">
664 <field name="state">recruit</field>
665 <field name="no_of_recruitment">1</field>
666 <field name="survey_id" ref="survey_job_0"/>
667+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4)]" />
668 </record>
669 <record id="hr.job_hrm" model="hr.job">
670 <field name="no_of_recruitment">1</field>
671 <field name="state">recruit</field>
672 <field name="survey_id" ref="survey_job_0"/>
673+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4)]" />
674 </record>
675 <record id="hr.job_marketing" model="hr.job">
676 <field name="state">recruit</field>
677 <field name="no_of_recruitment">3</field>
678 <field name="survey_id" ref="survey_job_0"/>
679+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4)]" />
680 </record>
681 <record id="hr.job_trainee" model="hr.job">
682 <field name="state">recruit</field>
683 <field name="no_of_recruitment">6</field>
684 <field name="survey_id" ref="survey_job_0"/>
685+ <field name= "stage_ids" eval="[(4, stage_job1),(4, stage_job2),(4, stage_job3),(4, stage_job4)]" />
686 </record>
687
688 <record id="message_application_demo" model="mail.message">
689
690=== modified file 'hr_recruitment/hr_recruitment_menu.xml'
691--- hr_recruitment/hr_recruitment_menu.xml 2014-02-11 11:41:21 +0000
692+++ hr_recruitment/hr_recruitment_menu.xml 2014-02-27 12:12:58 +0000
693@@ -1,26 +1,6 @@
694 <?xml version="1.0"?>
695 <openerp>
696 <data>
697- ######################## JOB OPPORTUNITIES (menu) ###########################
698- <record model="ir.actions.act_window" id="crm_case_categ0_act_job">
699- <field name="name">Applications</field>
700- <field name="res_model">hr.applicant</field>
701- <field name="view_mode">kanban,tree,form,graph,calendar</field>
702- <field name="view_id" eval="False"/>
703- <field name="search_view_id" ref="view_crm_case_jobs_filter"/>
704- <field name="context">{'empty_list_help_model': 'hr.job'}</field>
705- <field name="help" type="html">
706- <p>
707- OpenERP helps you track applicants in the recruitment
708- process and follow up all operations: meetings, interviews, etc.
709- </p><p>
710- Applicants and their attached CV are created automatically when an email is sent.
711- If you install the document management modules, all resumes are indexed automatically,
712- so that you can easily search through their content.
713- </p>
714- </field>
715- </record>
716-
717 <record model="ir.actions.act_window.view" id="action_hr_sec_kanban_view_act_job">
718 <field name="sequence" eval="0"/>
719 <field name="view_mode">kanban</field>
720@@ -61,5 +41,34 @@
721 name="Applications"
722 parent="base.menu_crm_case_job_req_main"
723 id="menu_crm_case_categ0_act_job" action="crm_case_categ0_act_job" sequence="2"/>
724+
725+ <menuitem parent="hr.menu_hr_configuration" id="hr.menu_hr_job" action="hr.action_hr_job" sequence="2"/>
726+
727+ <menuitem name="Recruitment"
728+ id="menu_hr_recruitment_recruitment"
729+ parent="hr.menu_hr_configuration"
730+ sequence="40"/>
731+
732+ <menuitem
733+ id="menu_hr_recruitment_stage"
734+ name="Stages"
735+ parent="menu_hr_recruitment_recruitment"
736+ action="hr_recruitment_stage_act"
737+ sequence="1" groups="base.group_no_one"/>
738+
739+ <menuitem
740+ id="menu_hr_recruitment_degree"
741+ name="Degrees"
742+ parent="menu_hr_recruitment_recruitment"
743+ action="hr_recruitment_degree_action"
744+ sequence="5" groups="base.group_no_one"/>
745+
746+ <menuitem
747+ id="menu_hr_recruitment_source"
748+ parent="menu_hr_recruitment_recruitment"
749+ action="hr_recruitment_source_action"
750+ groups="base.group_no_one"
751+ sequence="10"/>
752+
753 </data>
754-</openerp>
755+</openerp>
756\ No newline at end of file
757
758=== modified file 'hr_recruitment/hr_recruitment_view.xml'
759--- hr_recruitment/hr_recruitment_view.xml 2014-02-11 12:23:08 +0000
760+++ hr_recruitment/hr_recruitment_view.xml 2014-02-27 12:12:58 +0000
761@@ -2,11 +2,6 @@
762 <openerp>
763 <data>
764
765- <menuitem name="Recruitment"
766- id="menu_hr_recruitment_recruitment"
767- parent="hr.menu_hr_configuration"
768- sequence="40"/>
769-
770 <act_window
771 id="act_hr_applicant_to_meeting"
772 name="Meetings"
773@@ -33,7 +28,6 @@
774 </field>
775 </record>
776
777-
778 <!-- Applicants -->
779 <record model="ir.ui.view" id="crm_case_tree_view_job">
780 <field name="name">Applicants</field>
781@@ -121,7 +115,7 @@
782 <field name="survey" invisible="1"/>
783 <field name="response" invisible="1"/>
784 <field name="job_id" on_change="onchange_job(job_id)"/>
785- <field name="department_id" on_change="onchange_department_id(department_id, stage_id)"/>
786+ <field name="department_id"/>
787 <field name="company_id" />
788 <label for="availability"/>
789 <div>
790@@ -207,6 +201,25 @@
791 </search>
792 </field>
793 </record>
794+
795+ <!-- Applications Action-->
796+ <record model="ir.actions.act_window" id="crm_case_categ0_act_job">
797+ <field name="name">Applications</field>
798+ <field name="res_model">hr.applicant</field>
799+ <field name="view_mode">kanban,tree,form,graph,calendar</field>
800+ <field name="view_id" eval="False"/>
801+ <field name="search_view_id" ref="view_crm_case_jobs_filter"/>
802+ <field name="help" type="html">
803+ <p>
804+ OpenERP helps you track applicants in the recruitment
805+ process and follow up all operations: meetings, interviews, etc.
806+ </p><p>
807+ Applicants and their attached CV are created automatically when an email is sent.
808+ If you install the document management modules, all resumes are indexed automatically,
809+ so that you can easily search through their content.
810+ </p>
811+ </field>
812+ </record>
813
814 <!-- CRM Lead Calendar View -->
815 <record model="ir.ui.view" id="hr_applicant_calendar_view">
816@@ -228,7 +241,7 @@
817 <field name="name">Hr Applicants kanban</field>
818 <field name="model">hr.applicant</field>
819 <field name="arch" type="xml">
820- <kanban default_group_by="stage_id">
821+ <kanban default_group_by="stage_id" stage_management="hr.recruitment.stage.management" stage_options = "{'group_by':'stage_id'}">
822 <field name="stage_id"/>
823 <field name="color"/>
824 <field name="priority"/>
825@@ -364,6 +377,14 @@
826 </div>
827 </div>
828 </xpath>
829+ <xpath expr="//div[@name='hr_applicant_button']" position="inside">
830+ <button name="%(crm_case_categ0_act_job)d" string="Applications" type="action"
831+ context="{'search_default_job_id': active_id, 'stage_m2m_field':'stage_ids'}"/>
832+ </xpath>
833+ <xpath expr="//field[@name='requirements']" position="after">
834+ <separator string="Recruitment Stages" />
835+ <field name="stage_ids" nolabel="1"/>
836+ </xpath>
837 <xpath expr="//field[@name='department_id']" position="after">
838 <field name="user_id" class="oe_inline"/>
839 </xpath>
840@@ -499,7 +520,6 @@
841 <tree string="Stages">
842 <field name="sequence" invisible="1"/>
843 <field name="name"/>
844- <field name="department_id"/>
845 <field name="fold"/>
846 </tree>
847 </field>
848@@ -515,16 +535,17 @@
849 <group string="Stage Definition">
850 <group>
851 <field name="name"/>
852- <field name="department_id"/>
853+ <field name="fold"/>
854+ <field name="case_default"/>
855 </group>
856 <group>
857 <field name="sequence"/>
858- <field name="fold"/>
859 <field name="template_id" domain= "[('model_id.model', '=', 'hr.applicant')]"/>
860 </group>
861 </group>
862 <separator string="Requirements"/>
863 <field name="requirements"/>
864+ <field name="job_ids" invisible="1"/>
865 </sheet>
866 </form>
867 </field>
868@@ -545,14 +566,7 @@
869 </p>
870 </field>
871 </record>
872-
873- <menuitem
874- id="menu_hr_recruitment_stage"
875- name="Stages"
876- parent="menu_hr_recruitment_recruitment"
877- action="hr_recruitment_stage_act"
878- sequence="1" groups="base.group_no_one"/>
879-
880+
881 <!-- Degree Tree View -->
882 <record model="ir.ui.view" id="hr_recruitment_degree_tree">
883 <field name="name">hr.recruitment.degree.tree</field>
884@@ -589,13 +603,6 @@
885 <field name="view_id" ref="hr_recruitment_degree_tree"/>
886 </record>
887
888- <menuitem
889- id="menu_hr_recruitment_degree"
890- name="Degrees"
891- parent="menu_hr_recruitment_recruitment"
892- action="hr_recruitment_degree_action"
893- sequence="5" groups="base.group_no_one"/>
894-
895 <!-- Source Tree View -->
896 <record model="ir.ui.view" id="hr_recruitment_source_tree">
897 <field name="name">hr.recruitment.source.tree</field>
898@@ -625,12 +632,5 @@
899 <field name="view_type">form</field>
900 </record>
901
902- <menuitem
903- id="menu_hr_recruitment_source"
904- parent="menu_hr_recruitment_recruitment"
905- action="hr_recruitment_source_action"
906- groups="base.group_no_one"
907- sequence="10"/>
908-
909 </data>
910-</openerp>
911+</openerp>
912\ No newline at end of file
913
914=== modified file 'hr_recruitment/security/ir.model.access.csv'
915--- hr_recruitment/security/ir.model.access.csv 2014-01-15 09:38:05 +0000
916+++ hr_recruitment/security/ir.model.access.csv 2014-02-27 12:12:58 +0000
917@@ -1,7 +1,10 @@
918 id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
919 access_hr_applicant_user,hr.applicant.user,model_hr_applicant,base.group_hr_user,1,1,1,1
920 access_hr_recruitment_report,hr.recruitment.report,model_hr_recruitment_report,base.group_hr_manager,1,1,1,1
921-access_hr_recruitment_stage_user,hr.recruitment.stage.user,model_hr_recruitment_stage,base.group_hr_user,1,1,1,1
922+access_hr_recruitment_stage_user,hr.recruitment.stage.user,model_hr_recruitment_stage,base.group_hr_user,1,0,0,0
923+access_hr_recruitment_stage_manager,hr.recruitment.stage.manager,model_hr_recruitment_stage,base.group_hr_manager,1,1,1,1
924+access_hr_recruitment_stage_sequence_user,hr.recruitment.stage.sequence.user,model_hr_recruitment_stage_sequence,base.group_hr_user,1,0,0,0
925+access_hr_recruitment_stage_sequence_manager,hr.recruitment.stage.sequence.manager,model_hr_recruitment_stage_sequence,base.group_hr_manager,1,1,1,1
926 access_hr_recruitment_degree,hr.recruitment.degree,model_hr_recruitment_degree,base.group_hr_user,1,1,1,1
927 access_res_partner_hr_user,res.partner.user,base.model_res_partner,base.group_hr_user,1,1,1,1
928 access_survey_hr_user,survey.hr.user,survey.model_survey,base.group_hr_user,1,1,1,0
929
930=== added directory 'hr_recruitment/tests'
931=== added file 'hr_recruitment/tests/__init__.py'
932--- hr_recruitment/tests/__init__.py 1970-01-01 00:00:00 +0000
933+++ hr_recruitment/tests/__init__.py 2014-02-27 12:12:58 +0000
934@@ -0,0 +1,24 @@
935+# -*- coding: utf-8 -*-
936+##############################################################################
937+#
938+# OpenERP, Open Source Business Applications
939+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
940+#
941+# This program is free software: you can redistribute it and/or modify
942+# it under the terms of the GNU Affero General Public License as
943+# published by the Free Software Foundation, either version 3 of the
944+# License, or (at your option) any later version.
945+#
946+# This program is distributed in the hope that it will be useful,
947+# but WITHOUT ANY WARRANTY; without even the implied warranty of
948+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
949+# GNU Affero General Public License for more details.
950+#
951+# You should have received a copy of the GNU Affero General Public License
952+# along with this program. If not, see <http://www.gnu.org/licenses/>.
953+#
954+##############################################################################
955+from . import test_hr_recruitment_stage
956+checks = [
957+ test_hr_recruitment_stage,
958+ ]
959\ No newline at end of file
960
961=== added file 'hr_recruitment/tests/test_hr_recruitment_stage.py'
962--- hr_recruitment/tests/test_hr_recruitment_stage.py 1970-01-01 00:00:00 +0000
963+++ hr_recruitment/tests/test_hr_recruitment_stage.py 2014-02-27 12:12:58 +0000
964@@ -0,0 +1,77 @@
965+# -*- coding: utf-8 -*-
966+##############################################################################
967+#
968+# OpenERP, Open Source Business Applications
969+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
970+#
971+# This program is free software: you can redistribute it and/or modify
972+# it under the terms of the GNU Affero General Public License as
973+# published by the Free Software Foundation, either version 3 of the
974+# License, or (at your option) any later version.
975+#
976+# This program is distributed in the hope that it will be useful,
977+# but WITHOUT ANY WARRANTY; without even the implied warranty of
978+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
979+# GNU Affero General Public License for more details.
980+#
981+# You should have received a copy of the GNU Affero General Public License
982+# along with this program. If not, see <http://www.gnu.org/licenses/>.
983+#
984+##############################################################################
985+from openerp.osv.orm import except_orm
986+from openerp.tests import common
987+
988+
989+class TestHrRecruitmentStage(common.TransactionCase):
990+ def setUp(self):
991+ super(TestHrRecruitmentStage, self).setUp()
992+
993+ def test_00_stage_management(self):
994+ cr, uid = self.cr, self.uid
995+ recruitment_stage_management = self.registry('hr.recruitment.stage.management')
996+ hr_job = self.registry('hr.job')
997+ hr_recruitment_stage= self.registry('hr.recruitment.stage')
998+ hr_applicant = self.registry('hr.applicant');
999+ hr_job_id = hr_job.create(cr, uid, {'name':'test_section'},context={})
1000+ context = {'default_job_id': hr_job_id, 'default_action':'create'}
1001+
1002+ ''' For create when default_job_id pass in context
1003+ If same name is present than returns its id otherwise create new.
1004+ and link with given job.
1005+ '''
1006+
1007+ stage_m2m_oldlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
1008+ recruitment_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
1009+ recruitment_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
1010+ check_len_id = hr_recruitment_stage.search(cr, uid, [('name','=', 'First')], context=context)
1011+ self.assertEqual(len(check_len_id), 1)
1012+ hr_applicant_id = hr_applicant.create(cr, uid, {'name':'Test1','stage_id': check_len_id[0],'job_id':hr_job_id})
1013+ test1 = check_len_id
1014+ stage_m2m_newlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
1015+ self.assertEqual(len(stage_m2m_oldlist) + 1, len(stage_m2m_newlist))
1016+
1017+ ''' For edit when default_job_id pass in context
1018+ If same name is present than returns its id otherwise create new.
1019+ and link with given job and remove old link with job and project_task.
1020+ '''
1021+
1022+ context.update({'active_id':test1[0], 'default_action':'create'})
1023+ recruitment_stage_management.modify_stage(cr, uid, {'name':'Second'}, context=context)
1024+ test2 = hr_recruitment_stage.search(cr, uid, [('name','=', 'Second')], context=context)
1025+ stage_m2m_newlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
1026+
1027+ check_hr_recruitment_stage_id = hr_applicant.browse(cr, uid, hr_applicant_id, context).stage_id.id
1028+ self.assertEqual(check_hr_recruitment_stage_id, test2[0])
1029+ self.assertIn(test2[0], [x.id for x in stage_m2m_newlist])
1030+
1031+ ''' For unlink when default_job_id pass in context
1032+ It will unlink relation, not delete.
1033+ '''
1034+
1035+ stage_m2m_oldlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
1036+ recruitment_stage_management.unlink(cr, uid, test2, context=context)
1037+ stage_m2m_newlist = hr_job.browse(cr, uid, hr_job_id, context=context).stage_ids
1038+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
1039+ unlink_id = hr_recruitment_stage.search(cr, uid, [('name','=', 'Second')], context=context)
1040+ self.assertEqual(unlink_id, test2)
1041+
1042
1043=== modified file 'hr_recruitment/wizard/__init__.py'
1044--- hr_recruitment/wizard/__init__.py 2013-10-27 12:31:04 +0000
1045+++ hr_recruitment/wizard/__init__.py 2014-02-27 12:12:58 +0000
1046@@ -20,6 +20,5 @@
1047 ##############################################################################
1048
1049 import hr_recruitment_create_partner_job
1050-
1051+import hr_recruitment_stage_management
1052 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1053-
1054
1055=== added file 'hr_recruitment/wizard/hr_recruitment_stage_management.py'
1056--- hr_recruitment/wizard/hr_recruitment_stage_management.py 1970-01-01 00:00:00 +0000
1057+++ hr_recruitment/wizard/hr_recruitment_stage_management.py 2014-02-27 12:12:58 +0000
1058@@ -0,0 +1,104 @@
1059+# -*- coding: utf-8 -*-
1060+##############################################################################
1061+#
1062+# OpenERP, Open Source Management Solution
1063+# Copyright (C) 2004-2009 Tiny SPRL (<http://tiny.be>). All Rights Reserved
1064+# $Id$
1065+#
1066+# This program is free software: you can redistribute it and/or modify
1067+# it under the terms of the GNU Affero General Public License as published by
1068+# the Free Software Foundation, either version 3 of the License, or
1069+# (at your option) any later version.
1070+#
1071+# This program is distributed in the hope that it will be useful,
1072+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1073+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1074+# GNU Affero General Public License for more details.
1075+#
1076+# You should have received a copy of the GNU Affero General Public License
1077+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1078+#
1079+##############################################################################
1080+from openerp.osv import fields, osv
1081+from openerp.tools.translate import _
1082+
1083+class hr_recruitment_stage_management(osv.TransientModel):
1084+
1085+ _name = "hr.recruitment.stage.management"
1086+ _description = "Stages of application"
1087+
1088+ _columns = {
1089+ 'name': fields.char('Stage Name', required= True, size=64),
1090+ }
1091+ def default_get(self, cr, uid, fields, context=None):
1092+ if context is None:context = {}
1093+ old_stage_id = context.get('active_id')
1094+ res = super(hr_recruitment_stage_management, self).default_get(cr, uid, fields, context)
1095+
1096+ if(context.get('default_action') == "create"):return res
1097+ hr_rec_stage = self.pool.get("hr.recruitment.stage").browse(cr , uid, old_stage_id, context)
1098+ res.update({'name': hr_rec_stage.name})
1099+ return res
1100+
1101+
1102+ def create(self, cr, uid, vals, context):
1103+ if context is None: context = {}
1104+ action = context.get('default_action')
1105+ if action == 'create': self.create_stage(cr, uid, vals.get('name'), context)
1106+ if action == 'write': self.modify_stage(cr, uid, vals, context)
1107+ return None
1108+
1109+ def create_stage(self, cr, uid, name, context):
1110+ if context is None:context = {}
1111+ job_id = context.get('default_job_id')
1112+
1113+ hr_job = self.pool.get('hr.job')
1114+ hr_rec_stage = self.pool.get("hr.recruitment.stage")
1115+
1116+ if job_id:
1117+ new_id = hr_rec_stage.search(cr, uid, [('name','=', name)], context=context, limit=1)
1118+ if not new_id:
1119+ new_id = [hr_rec_stage.create(cr, uid, {'name': name}, context = context)]
1120+ hr_job.write(cr, uid, [job_id], {'stage_ids': [(4, new_id[0])]}, context=context)
1121+ else:
1122+ new_id = [hr_rec_stage.create(cr, uid, {'name': name}, context = context)]
1123+ return new_id
1124+
1125+ def unlink(self, cr, uid, ids, context=None):
1126+ if context is None: context = {}
1127+ job_id = context.get('default_job_id')
1128+
1129+ hr_applicant = self.pool.get('hr.applicant')
1130+ hr_job = self.pool.get('hr.job')
1131+ hr_rec_stage = self.pool.get("hr.recruitment.stage")
1132+
1133+ if job_id:
1134+ for stage_id in ids:
1135+ hr_applicant_ids = hr_applicant.search(cr, uid, [('stage_id','=',stage_id),('job_id','=', job_id)])
1136+ hr_applicant.write(cr, uid, hr_applicant_ids, {'stage_id': False} , context=context)
1137+ hr_job.write(cr, uid, job_id, {'stage_ids': [(3, stage_id)]}, context=context)
1138+ else:
1139+ hr_rec_stage.unlink(cr, uid, ids, context=context)
1140+ return True
1141+
1142+ def modify_stage(self,cr, uid, vals, context=None):
1143+ old_stage_id = context.get('active_id')
1144+ job_id = context.get('default_job_id')
1145+
1146+ hr_job = self.pool.get('hr.job')
1147+ hr_applicant = self.pool.get('hr.applicant')
1148+ hr_rec_stage = self.pool.get("hr.recruitment.stage")
1149+
1150+ if job_id:
1151+ hr_rec_stage_id = hr_rec_stage.search(cr, uid, [('name','=', vals.get('name'))], context=context, limit=1)
1152+ if not hr_rec_stage_id:
1153+ vals['job_ids'] = [(6, 0, [job_id])]
1154+ hr_rec_stage_id = [hr_rec_stage.copy(cr, uid, old_stage_id, default=vals, context=context)]
1155+ new_stage_id = hr_rec_stage_id[0]
1156+
1157+ applicant_ids = hr_applicant.search(cr, uid, [('stage_id','=', old_stage_id),('job_id','=', job_id)], context=context)
1158+ hr_applicant.write(cr, uid, applicant_ids, {'stage_id': new_stage_id} , context=context)
1159+ hr_job.write(cr, uid, [job_id], {'stage_ids': [(3, old_stage_id),(4, new_stage_id),]}, context=context)
1160+ else:
1161+ hr_rec_stage.write(cr, uid, old_stage_id, vals, context=context)
1162+ return True
1163\ No newline at end of file
1164
1165=== added file 'hr_recruitment/wizard/hr_recruitment_stage_management_view.xml'
1166--- hr_recruitment/wizard/hr_recruitment_stage_management_view.xml 1970-01-01 00:00:00 +0000
1167+++ hr_recruitment/wizard/hr_recruitment_stage_management_view.xml 2014-02-27 12:12:58 +0000
1168@@ -0,0 +1,19 @@
1169+<?xml version="1.0"?>
1170+<openerp>
1171+ <data>
1172+ <record model="ir.ui.view" id="crm_case_stage_form_wizard">
1173+ <field name="name">hr.recruitment.stage.management.form</field>
1174+ <field name="model">hr.recruitment.stage.management</field>
1175+ <field name="arch" type="xml">
1176+ <form string="Stage" version="7.0">
1177+ <group>
1178+ <group>
1179+ <field name="name"/>
1180+ </group>
1181+ </group>
1182+ </form>
1183+ </field>
1184+ </record>
1185+
1186+ </data>
1187+</openerp>
1188\ No newline at end of file
1189
1190=== modified file 'project/__openerp__.py'
1191--- project/__openerp__.py 2013-10-27 12:31:04 +0000
1192+++ project/__openerp__.py 2014-02-27 12:12:58 +0000
1193@@ -65,6 +65,7 @@
1194 'data': [
1195 'security/project_security.xml',
1196 'wizard/project_task_delegate_view.xml',
1197+ 'wizard/project_stage_management.xml',
1198 'security/ir.model.access.csv',
1199 'project_data.xml',
1200 'project_view.xml',
1201
1202=== modified file 'project/project.py'
1203--- project/project.py 2014-02-12 17:51:41 +0000
1204+++ project/project.py 2014-02-27 12:12:58 +0000
1205@@ -29,6 +29,16 @@
1206 from openerp.osv import fields, osv
1207 from openerp.tools.translate import _
1208
1209+class project_task_type_sequence(osv.osv):
1210+ _name = 'project.task.type.sequence'
1211+ _table = 'project_task_type_rel'
1212+ _description = 'Task Stage Sequence'
1213+ _order = 'sequence'
1214+ _columns = {
1215+ 'project_id': fields.many2one('project.project', 'Sequence/Project'),
1216+ 'type_id': fields.many2one('project.task.type', 'Sequence/Stage'),
1217+ 'sequence': fields.integer('Sequence')
1218+ }
1219
1220 class project_task_type(osv.osv):
1221 _name = 'project.task.type'
1222@@ -56,8 +66,19 @@
1223 'sequence': 1,
1224 'project_ids': _get_default_project_ids,
1225 }
1226+
1227 _order = 'sequence'
1228
1229+ def write(self, cr, uid, ids, vals, context=None):
1230+ project_id = context.get('default_project_id')
1231+ project_task_type_sequence = self.pool.get('project.task.type.sequence')
1232+
1233+ if project_id:
1234+ task_ids = project_task_type_sequence.search(cr, uid, [('type_id', '=', ids),('project_id', '=', project_id)], context=context)
1235+ project_task_type_sequence.write(cr, uid, task_ids, vals , context=context)
1236+ else:
1237+ return super(project_task_type, self).write(cr, uid, ids, vals, context=context)
1238+ return True
1239
1240 class project(osv.osv):
1241 _name = "project.project"
1242@@ -197,7 +218,8 @@
1243 ctx['active_test'] = False
1244 task_ids = self.pool.get('project.task').search(cr, uid, [('project_id', 'in', ids)], context=ctx)
1245 for task in self.pool.get('project.task').browse(cr, uid, task_ids, context):
1246- res[task.project_id.id] += 1
1247+ if task.stage_id.fold == False:
1248+ res[task.project_id.id] += 1
1249 return res
1250
1251 def _get_alias_models(self, cr, uid, context=None):
1252@@ -618,13 +640,17 @@
1253 search_domain = []
1254 project_id = self._resolve_project_id_from_context(cr, uid, context=context)
1255 if project_id:
1256- search_domain += ['|', ('project_ids', '=', project_id)]
1257- search_domain += [('id', 'in', ids)]
1258+ search_domain += [('project_ids', '=', project_id)]
1259 stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
1260+ if project_id:
1261+ sequence_ids = self.pool.get('project.task.type.sequence').search_read(cr, uid, [('project_id', '=', project_id), ('type_id', 'in', stage_ids)], ['type_id'], order=order, context=context)
1262+ type_ids = map(lambda type_ids: type_ids['type_id'][0], sequence_ids)
1263 result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
1264 # restore order of the search
1265- result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
1266-
1267+ if project_id:
1268+ result.sort(lambda x,y: cmp(type_ids.index(x[0]), type_ids.index(y[0])))
1269+ else:
1270+ result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
1271 fold = {}
1272 for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
1273 fold[stage.id] = stage.fold or False
1274
1275=== modified file 'project/project_view.xml'
1276--- project/project_view.xml 2013-12-24 10:29:22 +0000
1277+++ project/project_view.xml 2014-02-27 12:12:58 +0000
1278@@ -50,7 +50,6 @@
1279 <field name="view_mode">kanban,tree,form,calendar,gantt,graph</field>
1280 <field name="context">{
1281 'search_default_project_id': [active_id],
1282- 'default_project_id': active_id,
1283 'active_test': False,
1284 }</field>
1285 <field name="search_view_id" ref="view_task_search_form"/>
1286@@ -469,7 +468,7 @@
1287 <field name="name">project.task.kanban</field>
1288 <field name="model">project.task</field>
1289 <field name="arch" type="xml">
1290- <kanban default_group_by="stage_id" >
1291+ <kanban default_group_by="stage_id" stage_management="project.stage.management" stage_options = "{'group_by':'stage_id'}">
1292 <field name="color"/>
1293 <field name="priority"/>
1294 <field name="stage_id"/>
1295
1296=== modified file 'project/security/ir.model.access.csv'
1297--- project/security/ir.model.access.csv 2013-10-27 12:31:04 +0000
1298+++ project/security/ir.model.access.csv 2014-02-27 12:12:58 +0000
1299@@ -3,7 +3,10 @@
1300 access_project_project_manager,project.project,model_project_project,project.group_project_manager,1,1,1,1
1301 access_account_analytic_account_user,account.analytic.account,analytic.model_account_analytic_account,project.group_project_user,1,0,0,0
1302 access_account_analytic_account,account.analytic.account,analytic.model_account_analytic_account,project.group_project_manager,1,1,1,1
1303-access_project_task_type_user,project.task.type user,model_project_task_type,project.group_project_user,1,1,1,1
1304+access_project_task_type_user,project.task.type user,model_project_task_type,project.group_project_user,1,0,0,0
1305+access_project_task_type_manager,project.task.type manager,model_project_task_type,project.group_project_manager,1,1,1,1
1306+access_project_task_type_sequence_user,project.task.type.sequence user,model_project_task_type_sequence,project.group_project_user,1,0,0,0
1307+access_project_task_type_sequence_manager,project.task.type.sequence manager,model_project_task_type_sequence,project.group_project_manager,1,1,1,1
1308 access_project_task,project.task,model_project_task,project.group_project_user,1,1,1,1
1309 access_project_task_work,project.task.work,model_project_task_work,project.group_project_user,1,1,1,1
1310 access_report_project_task_user,report.project.task.user,model_report_project_task_user,project.group_project_manager,1,1,1,1
1311@@ -11,7 +14,7 @@
1312 access_task_on_partner,project.task on partners,model_project_task,base.group_user,1,0,0,0
1313 access_project_on_partner,project.project on partners,model_project_project,base.group_user,1,0,0,0
1314 access_project_task_sale_user,project.task salesman,model_project_task,base.group_sale_salesman,1,1,1,1
1315-access_project_task_type_sale_user,project.task.type salesman,project.model_project_task_type,base.group_sale_salesman,1,1,1,1
1316+access_project_task_type_sale_user,project.task.type salesman,project.model_project_task_type,base.group_sale_salesman,1,0,0,0
1317 access_project_task_history_sale_user,project.task.history salesman,project.model_project_task_history,base.group_sale_salesman,1,1,1,1
1318 access_project_project_sale_user,project.project salesman,model_project_project,base.group_sale_salesman,1,0,0,0
1319 access_account_analytic_line_project,account.analytic.line project,analytic.model_account_analytic_line,project.group_project_manager,1,1,1,1
1320
1321=== modified file 'project/tests/__init__.py'
1322--- project/tests/__init__.py 2013-07-10 10:15:08 +0000
1323+++ project/tests/__init__.py 2014-02-27 12:12:58 +0000
1324@@ -19,10 +19,11 @@
1325 #
1326 ##############################################################################
1327
1328-from . import test_project_flow
1329+from . import test_project_flow,project_stage_management
1330
1331 checks = [
1332 test_project_flow,
1333+ project_stage_management,
1334 ]
1335
1336 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1337
1338=== added file 'project/tests/project_stage_management.py'
1339--- project/tests/project_stage_management.py 1970-01-01 00:00:00 +0000
1340+++ project/tests/project_stage_management.py 2014-02-27 12:12:58 +0000
1341@@ -0,0 +1,77 @@
1342+# -*- coding: utf-8 -*-
1343+##############################################################################
1344+#
1345+# OpenERP, Open Source Business Applications
1346+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
1347+#
1348+# This program is free software: you can redistribute it and/or modify
1349+# it under the terms of the GNU Affero General Public License as
1350+# published by the Free Software Foundation, either version 3 of the
1351+# License, or (at your option) any later version.
1352+#
1353+# This program is distributed in the hope that it will be useful,
1354+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1355+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1356+# GNU Affero General Public License for more details.
1357+#
1358+# You should have received a copy of the GNU Affero General Public License
1359+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1360+#
1361+##############################################################################
1362+from openerp.osv.orm import except_orm
1363+from openerp.tests import common
1364+
1365+
1366+class TestCrmStagesCase(common.TransactionCase):
1367+ def setUp(self):
1368+ super(TestCrmStagesCase, self).setUp()
1369+
1370+ def test_00_stage_management(self):
1371+ cr, uid = self.cr, self.uid
1372+ project_stage_management = self.registry('project.stage.management')
1373+ project_project = self.registry('project.project')
1374+ project_task_type= self.registry('project.task.type')
1375+ project_task = self.registry('project.task');
1376+ project_project_id = project_project.create(cr, uid, {'name':'test_section'},context={})
1377+ context = {'default_project_id': project_project_id, 'default_action':'create'}
1378+
1379+ ''' For create when default_project_id pass in context
1380+ If same name is present than returns its id otherwise create new.
1381+ and link with given project
1382+ '''
1383+
1384+ stage_m2m_oldlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1385+ project_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
1386+ project_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
1387+ check_len_id = project_task_type.search(cr, uid, [('name','=', 'First')], context=context)
1388+ self.assertEqual(len(check_len_id), 1)
1389+ task_id = project_task.create(cr, uid, {'name':'Test1','stage_id': check_len_id[0],'project_id':project_project_id})
1390+ test1 = check_len_id
1391+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1392+
1393+ self.assertEqual(len(stage_m2m_oldlist) + 1, len(stage_m2m_newlist))
1394+
1395+ ''' For edit when default_project_id pass in context
1396+ If same name is present than returns its id otherwise create new.
1397+ and link with given project and remove old link with project and project_task.
1398+ '''
1399+ context.update({'active_id':test1[0], 'current_model':'project.task','default_action':'write'})
1400+ project_stage_management.modify_stage(cr, uid, {'name':'Second'}, context=context)
1401+ test2 = project_task_type.search(cr, uid, [('name','=', 'Second')], context=context)
1402+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1403+
1404+ self.assertIn(test2[0], [x.id for x in stage_m2m_newlist])
1405+ check_project_task_stage_id = project_task.browse(cr, uid, task_id, context).stage_id.id
1406+ self.assertEqual(check_project_task_stage_id, test2[0])
1407+
1408+ ''' For unlink when default_project_id pass in context
1409+ It will unlink relation, not delete.
1410+ '''
1411+
1412+ stage_m2m_oldlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1413+ project_stage_management.unlink(cr, uid, test2, context=context)
1414+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1415+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
1416+ unlink_id = project_task_type.search(cr, uid, [('name','=', 'Second')], context=context)
1417+ self.assertEqual(unlink_id, test2)
1418+
1419\ No newline at end of file
1420
1421=== modified file 'project/wizard/__init__.py'
1422--- project/wizard/__init__.py 2013-10-27 12:31:04 +0000
1423+++ project/wizard/__init__.py 2014-02-27 12:12:58 +0000
1424@@ -20,5 +20,6 @@
1425 ##############################################################################
1426
1427 import project_task_delegate
1428+import project_stage_management
1429
1430 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1431
1432=== added file 'project/wizard/project_stage_management.py'
1433--- project/wizard/project_stage_management.py 1970-01-01 00:00:00 +0000
1434+++ project/wizard/project_stage_management.py 2014-02-27 12:12:58 +0000
1435@@ -0,0 +1,104 @@
1436+# -*- coding: utf-8 -*-
1437+##############################################################################
1438+#
1439+# OpenERP, Open Source Management Solution
1440+# Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
1441+#
1442+# This program is free software: you can redistribute it and/or modify
1443+# it under the terms of the GNU Affero General Public License as
1444+# published by the Free Software Foundation, either version 3 of the
1445+# License, or (at your option) any later version.
1446+#
1447+# This program is distributed in the hope that it will be useful,
1448+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1449+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1450+# GNU Affero General Public License for more details.
1451+#
1452+# You should have received a copy of the GNU Affero General Public License
1453+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1454+#
1455+##############################################################################
1456+from openerp.osv import osv,fields
1457+
1458+class project_stage_management(osv.TransientModel):
1459+
1460+ _name = "project.stage.management"
1461+ _description = "Management of stages for project"
1462+ _columns = {
1463+ 'name': fields.char('Stage Name', required= True, size=64),
1464+ }
1465+ def default_get(self, cr, uid, fields, context=None):
1466+ if context is None:context = {}
1467+ old_stage_id = context.get('active_id')
1468+ res = super(project_stage_management, self).default_get(cr, uid, fields, context)
1469+
1470+ if(context.get('default_action') == "create"):return res
1471+ project_task_type = self.pool.get('project.task.type').browse(cr , uid, old_stage_id, context)
1472+ res.update({'name': project_task_type.name})
1473+ return res
1474+
1475+ def create(self, cr, uid, vals, context):
1476+ if context is None:context = {}
1477+ action = context.get('default_action')
1478+ if action == 'create':self.create_stage(cr, uid, vals.get('name'), context)
1479+ if action == 'write':self.modify_stage(cr, uid, vals, context)
1480+ return None
1481+
1482+ def create_stage(self, cr, uid, name, context):
1483+ if context is None:context = {}
1484+ project_id = context.get('default_project_id')
1485+
1486+ project = self.pool.get('project.project')
1487+ project_task_type = self.pool.get('project.task.type')
1488+
1489+ if project_id:
1490+ task_type_id = project_task_type.search(cr, uid, [('name','=', name)], context=context, limit=1)
1491+ if not task_type_id:
1492+ task_type_id = [project_task_type.create(cr, uid, {'name': name}, context=context)]
1493+ project.write(cr, uid, [project_id], {'type_ids': [(4, task_type_id[0]),]}, context=context)
1494+ else:
1495+ task_type_id = [project_task_type.create(cr, uid, {'name': name}, context=context)]
1496+ return task_type_id
1497+
1498+ def unlink(self, cr, uid, ids, context=None):
1499+ if context is None:context = {}
1500+ project_id = context.get('default_project_id')
1501+ model = context.get('current_model')
1502+ if not model: return False
1503+
1504+ project_task_or_issue = self.pool.get(model)
1505+ project_project = self.pool.get('project.project')
1506+ project_task_type = self.pool.get('project.task.type')
1507+
1508+ if project_id:
1509+ for stage_id in ids:
1510+ task_ids = project_task_or_issue.search(cr, uid, [('stage_id','=', stage_id),('project_id','=', project_id)])
1511+ project_task_or_issue.write(cr, uid, task_ids, {'stage_id': False} , context=context)
1512+ project_project.write(cr, uid, project_id, {'type_ids': [(3, stage_id)]}, context=context)
1513+ else:
1514+ project_task_type.unlink(cr, uid, ids, context=context)
1515+ return True
1516+
1517+ def modify_stage(self,cr, uid, vals, context=None):
1518+ old_stage_id = context.get('active_id')
1519+ project_id = context.get('default_project_id')
1520+ model = context.get('current_model')
1521+ if not model: return False
1522+
1523+ project_task_type = self.pool.get('project.task.type')
1524+ project_project = self.pool.get('project.project')
1525+ project_task_or_issue = self.pool.get(model)
1526+
1527+ if project_id:
1528+ new_task_type_id = project_task_type.search(cr, uid, [('name','=', vals.get('name'))], context=context, limit=1)
1529+ if not new_task_type_id:
1530+ vals['project_ids'] = [(6, 0, [project_id])]
1531+ new_task_type_id = [project_task_type.copy(cr, uid, old_stage_id, default=vals, context=context)]
1532+ new_stage_id = new_task_type_id[0]
1533+
1534+ task_ids = project_task_or_issue.search(cr, uid, [('stage_id','=', old_stage_id),('project_id','=', project_id)], context=context)
1535+ project_task_or_issue.write(cr, uid, task_ids, {'stage_id': new_stage_id} , context=context)
1536+ project_project.write(cr, uid, [project_id], {'type_ids': [(3, old_stage_id),(4, new_stage_id),]}, context=context)
1537+ else:
1538+ project_task_type.write(cr, uid, old_stage_id, vals, context=context)
1539+ return True
1540\ No newline at end of file
1541
1542=== added file 'project/wizard/project_stage_management.xml'
1543--- project/wizard/project_stage_management.xml 1970-01-01 00:00:00 +0000
1544+++ project/wizard/project_stage_management.xml 2014-02-27 12:12:58 +0000
1545@@ -0,0 +1,18 @@
1546+<?xml version="1.0" encoding="utf-8"?>
1547+<openerp>
1548+ <data>
1549+ <record model="ir.ui.view" id="project_stage_form_wizard">
1550+ <field name="name">project.stage.management.form</field>
1551+ <field name="model">project.stage.management</field>
1552+ <field name="arch" type="xml">
1553+ <form string="Stage" version="7.0">
1554+ <group>
1555+ <group>
1556+ <field name="name"/>
1557+ </group>
1558+ </group>
1559+ </form>
1560+ </field>
1561+ </record>
1562+ </data>
1563+</openerp>
1564
1565=== modified file 'project_issue/project_issue.py'
1566--- project_issue/project_issue.py 2013-12-02 15:09:25 +0000
1567+++ project_issue/project_issue.py 2014-02-27 12:12:58 +0000
1568@@ -106,14 +106,18 @@
1569 search_domain = []
1570 project_id = self._resolve_project_id_from_context(cr, uid, context=context)
1571 if project_id:
1572- search_domain += ['|', ('project_ids', '=', project_id)]
1573- search_domain += [('id', 'in', ids)]
1574+ search_domain += [('project_ids', '=', project_id)]
1575 # perform search
1576 stage_ids = stage_obj._search(cr, uid, search_domain, order=order, access_rights_uid=access_rights_uid, context=context)
1577+ if project_id:
1578+ sequence_ids = self.pool.get('project.task.type.sequence').search_read(cr, uid, [('project_id', '=', project_id), ('type_id', 'in', stage_ids)], ['type_id'], order=order, context=context)
1579+ type_ids = map(lambda type_ids: type_ids['type_id'][0], sequence_ids)
1580 result = stage_obj.name_get(cr, access_rights_uid, stage_ids, context=context)
1581 # restore order of the search
1582- result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
1583-
1584+ if project_id:
1585+ result.sort(lambda x,y: cmp(type_ids.index(x[0]), type_ids.index(y[0])))
1586+ else:
1587+ result.sort(lambda x,y: cmp(stage_ids.index(x[0]), stage_ids.index(y[0])))
1588 fold = {}
1589 for stage in stage_obj.browse(cr, access_rights_uid, stage_ids, context=context):
1590 fold[stage.id] = stage.fold or False
1591
1592=== modified file 'project_issue/project_issue_view.xml'
1593--- project_issue/project_issue_view.xml 2013-11-29 09:46:10 +0000
1594+++ project_issue/project_issue_view.xml 2014-02-27 12:12:58 +0000
1595@@ -184,7 +184,7 @@
1596 <field name="name">Project Issue Kanban</field>
1597 <field name="model">project.issue</field>
1598 <field name="arch" type="xml">
1599- <kanban default_group_by="stage_id">
1600+ <kanban default_group_by="stage_id" stage_management="project.stage.management" stage_options = "{'group_by':'stage_id','default_context_key':'default_project_id'}">
1601 <field name="stage_id"/>
1602 <field name="color"/>
1603 <field name="priority"/>
1604@@ -281,7 +281,6 @@
1605 <field name="view_mode">kanban,tree,form,calendar,graph</field>
1606 <field name="context">{
1607 'search_default_project_id': [active_id],
1608- 'default_project_id': active_id,
1609 }
1610 </field>
1611 <field name="help" type="html">
1612
1613=== added directory 'project_issue/tests'
1614=== added file 'project_issue/tests/__init__.py'
1615--- project_issue/tests/__init__.py 1970-01-01 00:00:00 +0000
1616+++ project_issue/tests/__init__.py 2014-02-27 12:12:58 +0000
1617@@ -0,0 +1,28 @@
1618+# -*- coding: utf-8 -*-
1619+##############################################################################
1620+#
1621+# OpenERP, Open Source Business Applications
1622+# Copyright (c) 2013-TODAY OpenERP S.A. <http://www.openerp.com>
1623+#
1624+# This program is free software: you can redistribute it and/or modify
1625+# it under the terms of the GNU Affero General Public License as
1626+# published by the Free Software Foundation, either version 3 of the
1627+# License, or (at your option) any later version.
1628+#
1629+# This program is distributed in the hope that it will be useful,
1630+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1631+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1632+# GNU Affero General Public License for more details.
1633+#
1634+# You should have received a copy of the GNU Affero General Public License
1635+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1636+#
1637+##############################################################################
1638+
1639+from . import project_stage_management
1640+
1641+checks = [
1642+ project_stage_management,
1643+]
1644+
1645+# vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4:
1646
1647=== added file 'project_issue/tests/project_stage_management.py'
1648--- project_issue/tests/project_stage_management.py 1970-01-01 00:00:00 +0000
1649+++ project_issue/tests/project_stage_management.py 2014-02-27 12:12:58 +0000
1650@@ -0,0 +1,77 @@
1651+# -*- coding: utf-8 -*-
1652+##############################################################################
1653+#
1654+# OpenERP, Open Source Business Applications
1655+# Copyright (c) 2012-TODAY OpenERP S.A. <http://openerp.com>
1656+#
1657+# This program is free software: you can redistribute it and/or modify
1658+# it under the terms of the GNU Affero General Public License as
1659+# published by the Free Software Foundation, either version 3 of the
1660+# License, or (at your option) any later version.
1661+#
1662+# This program is distributed in the hope that it will be useful,
1663+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1664+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1665+# GNU Affero General Public License for more details.
1666+#
1667+# You should have received a copy of the GNU Affero General Public License
1668+# along with this program. If not, see <http://www.gnu.org/licenses/>.
1669+#
1670+##############################################################################
1671+from openerp.osv.orm import except_orm
1672+from openerp.tests import common
1673+
1674+
1675+class TestProjectStagesCase(common.TransactionCase):
1676+ def setUp(self):
1677+ super(TestProjectStagesCase, self).setUp()
1678+
1679+ def test_00_stage_management(self):
1680+ cr, uid = self.cr, self.uid
1681+ project_issue_stage_management = self.registry('project.stage.management')
1682+ project_project = self.registry('project.project')
1683+ project_issue_type= self.registry('project.task.type')
1684+ project_issue = self.registry('project.issue')
1685+ project_project_id = project_project.create(cr, uid, {'name':'test_section'},context={})
1686+ context = {'default_project_id': project_project_id, 'default_action':'create'}
1687+
1688+ ''' For create when default_project_id pass in context
1689+ If same name is present than returns its id otherwise create new.
1690+ and link with given project.
1691+ '''
1692+
1693+ stage_m2m_oldlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1694+ project_issue_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
1695+ project_issue_stage_management.create(cr, uid, {'name':'First','action':'create'},context)
1696+ check_len_id = project_issue_type.search(cr, uid, [('name','=', 'First')], context=context)
1697+ self.assertEqual(len(check_len_id), 1)
1698+ issue_id = project_issue.create(cr, uid, {'name':'Test1','stage_id': check_len_id[0],'project_id':project_project_id})
1699+ test1 = check_len_id
1700+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1701+
1702+ self.assertEqual(len(stage_m2m_oldlist) + 1, len(stage_m2m_newlist))
1703+
1704+ ''' For edit when default_project_id pass in context
1705+ If same name is present than returns its id otherwise create new.
1706+ and link with given project and remove old link with project and project_issue.
1707+ '''
1708+
1709+ context.update({'active_id':test1[0],'current_model':'project.issue', 'default_action':'write'})
1710+ project_issue_stage_management.modify_stage(cr, uid, {'name':'Second'}, context=context)
1711+ test2 = project_issue_type.search(cr, uid, [('name','=', 'Second')], context=context)
1712+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1713+
1714+ self.assertIn(test2[0], [x.id for x in stage_m2m_newlist])
1715+ check_project_task_stage_id = project_issue.browse(cr, uid, issue_id, context).stage_id.id
1716+ self.assertEqual(check_project_task_stage_id, test2[0])
1717+
1718+ ''' For unlink when default_project_id pass in context
1719+ It will unlink relation, not delete.
1720+ '''
1721+
1722+ stage_m2m_oldlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1723+ project_issue_stage_management.unlink(cr, uid, test2, context=context)
1724+ stage_m2m_newlist = project_project.browse(cr, uid, project_project_id, context=context).type_ids
1725+ self.assertEqual(len(stage_m2m_oldlist) -1, len(stage_m2m_newlist))
1726+ unlink_id = project_issue_type.search(cr, uid, [('name','=', 'Second')], context=context)
1727+ self.assertEqual(unlink_id, test2)

Subscribers

People subscribed via source and target branches

to all changes: